Просмотр исходного кода

GLTFExporter: Add support for WebGPURenderer (#29509)

* GLTFExporter: Add .setTextureUtils

* clean up

* Update misc_exporter_gltf.html

Fix import.

* fix missing await

* clean up

* GLTFExporter: Convert uncompatible int32 and uint32 attributes

---------

Co-authored-by: Michael Herzog <michael.herzog@human-interactive.org>
Don McCurdy 1 год назад
Родитель
Сommit
cef0049107
2 измененных файлов с 138 добавлено и 70 удалено
  1. 136 69
      examples/jsm/exporters/GLTFExporter.js
  2. 2 1
      examples/misc_exporter_gltf.html

+ 136 - 69
examples/jsm/exporters/GLTFExporter.js

@@ -26,8 +26,6 @@ import {
 	Quaternion,
 	REVISION
 } from 'three';
-import { decompress } from './../utils/WebGLTextureUtils.js';
-
 
 /**
  * The KHR_mesh_quantization extension allows these extra attribute component types
@@ -68,6 +66,8 @@ class GLTFExporter {
 
 	constructor() {
 
+		this.textureUtils = null;
+
 		this.pluginCallbacks = [];
 
 		this.register( function ( writer ) {
@@ -180,6 +180,14 @@ class GLTFExporter {
 
 	}
 
+	setTextureUtils( utils ) {
+
+		this.textureUtils = utils;
+
+		return this;
+
+	}
+
 	/**
 	 * Parse scenes and generate GLTF output
 	 * @param  {Scene or [THREE.Scenes]} input   Scene or Array of THREE.Scenes
@@ -199,7 +207,8 @@ class GLTFExporter {
 		}
 
 		writer.setPlugins( plugins );
-		writer.write( input, onDone, options ).catch( onError );
+		writer.setTextureUtils( this.textureUtils );
+		writer.writeAsync( input, onDone, options ).catch( onError );
 
 	}
 
@@ -516,6 +525,8 @@ class GLTFWriter {
 			images: new Map()
 		};
 
+		this.textureUtils = null;
+
 	}
 
 	setPlugins( plugins ) {
@@ -524,13 +535,19 @@ class GLTFWriter {
 
 	}
 
+	setTextureUtils( utils ) {
+
+		this.textureUtils = utils;
+
+	}
+
 	/**
 	 * Parse scenes and generate GLTF output
 	 * @param  {Scene or [THREE.Scenes]} input   Scene or Array of THREE.Scenes
 	 * @param  {Function} onDone  Callback on completed
 	 * @param  {Object} options options
 	 */
-	async write( input, onDone, options = {} ) {
+	async writeAsync( input, onDone, options = {} ) {
 
 		this.options = Object.assign( {
 			// default options
@@ -549,7 +566,7 @@ class GLTFWriter {
 
 		}
 
-		this.processInput( input );
+		await this.processInputAsync( input );
 
 		await Promise.all( this.pending );
 
@@ -823,7 +840,7 @@ class GLTFWriter {
 
 	}
 
-	buildMetalRoughTexture( metalnessMap, roughnessMap ) {
+	async buildMetalRoughTextureAsync( metalnessMap, roughnessMap ) {
 
 		if ( metalnessMap === roughnessMap ) return metalnessMap;
 
@@ -847,17 +864,15 @@ class GLTFWriter {
 
 		}
 
-		console.warn( 'THREE.GLTFExporter: Merged metalnessMap and roughnessMap textures.' );
-
 		if ( metalnessMap instanceof CompressedTexture ) {
 
-			metalnessMap = decompress( metalnessMap );
+			metalnessMap = await this.decompressTextureAsync( metalnessMap );
 
 		}
 
 		if ( roughnessMap instanceof CompressedTexture ) {
 
-			roughnessMap = decompress( roughnessMap );
+			roughnessMap = await this.decompressTextureAsync( roughnessMap );
 
 		}
 
@@ -927,10 +942,25 @@ class GLTFWriter {
 
 		}
 
+		console.warn( 'THREE.GLTFExporter: Merged metalnessMap and roughnessMap textures.' );
+
 		return texture;
 
 	}
 
+
+	async decompressTextureAsync( texture, maxTextureSize = Infinity ) {
+
+		if ( this.textureUtils === null ) {
+
+			throw new Error( 'THREE.GLTFExporter: setTextureUtils() must be called to process compressed textures.' );
+
+		}
+
+		return await this.textureUtils.decompress( texture, maxTextureSize );
+
+	}
+
 	/**
 	 * Process a buffer to append to the default one.
 	 * @param  {ArrayBuffer} buffer
@@ -1407,7 +1437,7 @@ class GLTFWriter {
 	 * @param  {Texture} map Map to process
 	 * @return {Integer} Index of the processed texture in the "textures" array
 	 */
-	processTexture( map ) {
+	async processTextureAsync( map ) {
 
 		const writer = this;
 		const options = writer.options;
@@ -1421,7 +1451,7 @@ class GLTFWriter {
 		// make non-readable textures (e.g. CompressedTexture) readable by blitting them into a new texture
 		if ( map instanceof CompressedTexture ) {
 
-			map = decompress( map, options.maxTextureSize );
+			map = await this.decompressTextureAsync( map, options.maxTextureSize );
 
 		}
 
@@ -1436,9 +1466,9 @@ class GLTFWriter {
 
 		if ( map.name ) textureDef.name = map.name;
 
-		this._invokeAll( function ( ext ) {
+		await this._invokeAllAsync( async function ( ext ) {
 
-			ext.writeTexture && ext.writeTexture( map, textureDef );
+			ext.writeTexture && await ext.writeTexture( map, textureDef );
 
 		} );
 
@@ -1453,7 +1483,7 @@ class GLTFWriter {
 	 * @param  {THREE.Material} material Material to process
 	 * @return {Integer|null} Index of the processed material in the "materials" array
 	 */
-	processMaterial( material ) {
+	async processMaterialAsync( material ) {
 
 		const cache = this.cache;
 		const json = this.json;
@@ -1502,11 +1532,11 @@ class GLTFWriter {
 		// pbrMetallicRoughness.metallicRoughnessTexture
 		if ( material.metalnessMap || material.roughnessMap ) {
 
-			const metalRoughTexture = this.buildMetalRoughTexture( material.metalnessMap, material.roughnessMap );
+			const metalRoughTexture = await this.buildMetalRoughTextureAsync( material.metalnessMap, material.roughnessMap );
 
 			const metalRoughMapDef = {
-				index: this.processTexture( metalRoughTexture ),
-				channel: metalRoughTexture.channel
+				index: await this.processTextureAsync( metalRoughTexture ),
+				texCoord: metalRoughTexture.channel
 			};
 			this.applyTextureTransform( metalRoughMapDef, metalRoughTexture );
 			materialDef.pbrMetallicRoughness.metallicRoughnessTexture = metalRoughMapDef;
@@ -1517,7 +1547,7 @@ class GLTFWriter {
 		if ( material.map ) {
 
 			const baseColorMapDef = {
-				index: this.processTexture( material.map ),
+				index: await this.processTextureAsync( material.map ),
 				texCoord: material.map.channel
 			};
 			this.applyTextureTransform( baseColorMapDef, material.map );
@@ -1540,7 +1570,7 @@ class GLTFWriter {
 			if ( material.emissiveMap ) {
 
 				const emissiveMapDef = {
-					index: this.processTexture( material.emissiveMap ),
+					index: await this.processTextureAsync( material.emissiveMap ),
 					texCoord: material.emissiveMap.channel
 				};
 				this.applyTextureTransform( emissiveMapDef, material.emissiveMap );
@@ -1554,7 +1584,7 @@ class GLTFWriter {
 		if ( material.normalMap ) {
 
 			const normalMapDef = {
-				index: this.processTexture( material.normalMap ),
+				index: await this.processTextureAsync( material.normalMap ),
 				texCoord: material.normalMap.channel
 			};
 
@@ -1575,7 +1605,7 @@ class GLTFWriter {
 		if ( material.aoMap ) {
 
 			const occlusionMapDef = {
-				index: this.processTexture( material.aoMap ),
+				index: await this.processTextureAsync( material.aoMap ),
 				texCoord: material.aoMap.channel
 			};
 
@@ -1612,9 +1642,9 @@ class GLTFWriter {
 
 		this.serializeUserData( material, materialDef );
 
-		this._invokeAll( function ( ext ) {
+		await this._invokeAllAsync( async function ( ext ) {
 
-			ext.writeMaterial && ext.writeMaterial( material, materialDef );
+			ext.writeMaterialAsync && await ext.writeMaterialAsync( material, materialDef );
 
 		} );
 
@@ -1629,7 +1659,7 @@ class GLTFWriter {
 	 * @param  {THREE.Mesh} mesh Mesh to process
 	 * @return {Integer|null} Index of the processed mesh in the "meshes" array
 	 */
-	processMesh( mesh ) {
+	async processMeshAsync( mesh ) {
 
 		const cache = this.cache;
 		const json = this.json;
@@ -1733,7 +1763,9 @@ class GLTFWriter {
 
 			}
 
-			// JOINTS_0 must be UNSIGNED_BYTE or UNSIGNED_SHORT.
+			// Enforce glTF vertex attribute requirements:
+			// - JOINTS_0 must be UNSIGNED_BYTE or UNSIGNED_SHORT
+			// - Only custom attributes may be INT or UNSIGNED_INT
 			modifiedAttribute = null;
 			const array = attribute.array;
 
@@ -1744,6 +1776,11 @@ class GLTFWriter {
 				console.warn( 'GLTFExporter: Attribute "skinIndex" converted to type UNSIGNED_SHORT.' );
 				modifiedAttribute = new BufferAttribute( new Uint16Array( array ), attribute.itemSize, attribute.normalized );
 
+			} else if ( ( array instanceof Uint32Array || array instanceof Int32Array ) && ! attributeName.startsWith( '_' ) ) {
+
+				console.warn( `GLTFExporter: Attribute "${ attributeName }" converted to type FLOAT.` );
+				modifiedAttribute = GLTFExporter.Utils.toFloat32BufferAttribute( attribute );
+
 			}
 
 			const accessor = this.processAccessor( modifiedAttribute || attribute, geometry );
@@ -1930,7 +1967,7 @@ class GLTFWriter {
 
 			}
 
-			const material = this.processMaterial( materials[ groups[ i ].materialIndex ] );
+			const material = await this.processMaterialAsync( materials[ groups[ i ].materialIndex ] );
 
 			if ( material !== null ) primitive.material = material;
 
@@ -1948,7 +1985,7 @@ class GLTFWriter {
 
 		if ( ! json.meshes ) json.meshes = [];
 
-		this._invokeAll( function ( ext ) {
+		await this._invokeAllAsync( function ( ext ) {
 
 			ext.writeMesh && ext.writeMesh( mesh, meshDef );
 
@@ -2225,10 +2262,10 @@ class GLTFWriter {
 
 	/**
 	 * Process Object3D node
-	 * @param  {THREE.Object3D} node Object3D to processNode
+	 * @param  {THREE.Object3D} node Object3D to processNodeAsync
 	 * @return {Integer} Index of the node in the nodes list
 	 */
-	processNode( object ) {
+	async processNodeAsync( object ) {
 
 		const json = this.json;
 		const options = this.options;
@@ -2285,7 +2322,7 @@ class GLTFWriter {
 
 		if ( object.isMesh || object.isLine || object.isPoints ) {
 
-			const meshIndex = this.processMesh( object );
+			const meshIndex = await this.processMeshAsync( object );
 
 			if ( meshIndex !== null ) nodeDef.mesh = meshIndex;
 
@@ -2307,7 +2344,7 @@ class GLTFWriter {
 
 				if ( child.visible || options.onlyVisible === false ) {
 
-					const nodeIndex = this.processNode( child );
+					const nodeIndex = await this.processNodeAsync( child );
 
 					if ( nodeIndex !== null ) children.push( nodeIndex );
 
@@ -2319,7 +2356,7 @@ class GLTFWriter {
 
 		}
 
-		this._invokeAll( function ( ext ) {
+		await this._invokeAllAsync( function ( ext ) {
 
 			ext.writeNode && ext.writeNode( object, nodeDef );
 
@@ -2335,7 +2372,7 @@ class GLTFWriter {
 	 * Process Scene
 	 * @param  {Scene} node Scene to process
 	 */
-	processScene( scene ) {
+	async processSceneAsync( scene ) {
 
 		const json = this.json;
 		const options = this.options;
@@ -2361,7 +2398,7 @@ class GLTFWriter {
 
 			if ( child.visible || options.onlyVisible === false ) {
 
-				const nodeIndex = this.processNode( child );
+				const nodeIndex = await this.processNodeAsync( child );
 
 				if ( nodeIndex !== null ) nodes.push( nodeIndex );
 
@@ -2379,7 +2416,7 @@ class GLTFWriter {
 	 * Creates a Scene to hold a list of objects and parse it
 	 * @param  {Array} objects List of objects to process
 	 */
-	processObjects( objects ) {
+	async processObjectsAsync( objects ) {
 
 		const scene = new Scene();
 		scene.name = 'AuxScene';
@@ -2392,20 +2429,20 @@ class GLTFWriter {
 
 		}
 
-		this.processScene( scene );
+		await this.processSceneAsync( scene );
 
 	}
 
 	/**
 	 * @param {THREE.Object3D|Array<THREE.Object3D>} input
 	 */
-	processInput( input ) {
+	async processInputAsync( input ) {
 
 		const options = this.options;
 
 		input = input instanceof Array ? input : [ input ];
 
-		this._invokeAll( function ( ext ) {
+		await this._invokeAllAsync( function ( ext ) {
 
 			ext.beforeParse && ext.beforeParse( input );
 
@@ -2417,7 +2454,7 @@ class GLTFWriter {
 
 			if ( input[ i ] instanceof Scene ) {
 
-				this.processScene( input[ i ] );
+				await this.processSceneAsync( input[ i ] );
 
 			} else {
 
@@ -2427,7 +2464,11 @@ class GLTFWriter {
 
 		}
 
-		if ( objectsWithoutScene.length > 0 ) this.processObjects( objectsWithoutScene );
+		if ( objectsWithoutScene.length > 0 ) {
+
+			await this.processObjectsAsync( objectsWithoutScene );
+
+		}
 
 		for ( let i = 0; i < this.skins.length; ++ i ) {
 
@@ -2441,7 +2482,7 @@ class GLTFWriter {
 
 		}
 
-		this._invokeAll( function ( ext ) {
+		await this._invokeAllAsync( function ( ext ) {
 
 			ext.afterParse && ext.afterParse( input );
 
@@ -2449,11 +2490,11 @@ class GLTFWriter {
 
 	}
 
-	_invokeAll( func ) {
+	async _invokeAllAsync( func ) {
 
 		for ( let i = 0, il = this.plugins.length; i < il; i ++ ) {
 
-			func( this.plugins[ i ] );
+			await func( this.plugins[ i ] );
 
 		}
 
@@ -2570,7 +2611,7 @@ class GLTFMaterialsUnlitExtension {
 
 	}
 
-	writeMaterial( material, materialDef ) {
+	async writeMaterialAsync( material, materialDef ) {
 
 		if ( ! material.isMeshBasicMaterial ) return;
 
@@ -2603,7 +2644,7 @@ class GLTFMaterialsClearcoatExtension {
 
 	}
 
-	writeMaterial( material, materialDef ) {
+	async writeMaterialAsync( material, materialDef ) {
 
 		if ( ! material.isMeshPhysicalMaterial || material.clearcoat === 0 ) return;
 
@@ -2617,7 +2658,7 @@ class GLTFMaterialsClearcoatExtension {
 		if ( material.clearcoatMap ) {
 
 			const clearcoatMapDef = {
-				index: writer.processTexture( material.clearcoatMap ),
+				index: await writer.processTextureAsync( material.clearcoatMap ),
 				texCoord: material.clearcoatMap.channel
 			};
 			writer.applyTextureTransform( clearcoatMapDef, material.clearcoatMap );
@@ -2630,7 +2671,7 @@ class GLTFMaterialsClearcoatExtension {
 		if ( material.clearcoatRoughnessMap ) {
 
 			const clearcoatRoughnessMapDef = {
-				index: writer.processTexture( material.clearcoatRoughnessMap ),
+				index: await writer.processTextureAsync( material.clearcoatRoughnessMap ),
 				texCoord: material.clearcoatRoughnessMap.channel
 			};
 			writer.applyTextureTransform( clearcoatRoughnessMapDef, material.clearcoatRoughnessMap );
@@ -2641,7 +2682,7 @@ class GLTFMaterialsClearcoatExtension {
 		if ( material.clearcoatNormalMap ) {
 
 			const clearcoatNormalMapDef = {
-				index: writer.processTexture( material.clearcoatNormalMap ),
+				index: await writer.processTextureAsync( material.clearcoatNormalMap ),
 				texCoord: material.clearcoatNormalMap.channel
 			};
 
@@ -2676,7 +2717,7 @@ class GLTFMaterialsDispersionExtension {
 
 	}
 
-	writeMaterial( material, materialDef ) {
+	async writeMaterialAsync( material, materialDef ) {
 
 		if ( ! material.isMeshPhysicalMaterial || material.dispersion === 0 ) return;
 
@@ -2710,7 +2751,7 @@ class GLTFMaterialsIridescenceExtension {
 
 	}
 
-	writeMaterial( material, materialDef ) {
+	async writeMaterialAsync( material, materialDef ) {
 
 		if ( ! material.isMeshPhysicalMaterial || material.iridescence === 0 ) return;
 
@@ -2724,7 +2765,7 @@ class GLTFMaterialsIridescenceExtension {
 		if ( material.iridescenceMap ) {
 
 			const iridescenceMapDef = {
-				index: writer.processTexture( material.iridescenceMap ),
+				index: await writer.processTextureAsync( material.iridescenceMap ),
 				texCoord: material.iridescenceMap.channel
 			};
 			writer.applyTextureTransform( iridescenceMapDef, material.iridescenceMap );
@@ -2739,7 +2780,7 @@ class GLTFMaterialsIridescenceExtension {
 		if ( material.iridescenceThicknessMap ) {
 
 			const iridescenceThicknessMapDef = {
-				index: writer.processTexture( material.iridescenceThicknessMap ),
+				index: await writer.processTextureAsync( material.iridescenceThicknessMap ),
 				texCoord: material.iridescenceThicknessMap.channel
 			};
 			writer.applyTextureTransform( iridescenceThicknessMapDef, material.iridescenceThicknessMap );
@@ -2770,7 +2811,7 @@ class GLTFMaterialsTransmissionExtension {
 
 	}
 
-	writeMaterial( material, materialDef ) {
+	async writeMaterialAsync( material, materialDef ) {
 
 		if ( ! material.isMeshPhysicalMaterial || material.transmission === 0 ) return;
 
@@ -2784,7 +2825,7 @@ class GLTFMaterialsTransmissionExtension {
 		if ( material.transmissionMap ) {
 
 			const transmissionMapDef = {
-				index: writer.processTexture( material.transmissionMap ),
+				index: await writer.processTextureAsync( material.transmissionMap ),
 				texCoord: material.transmissionMap.channel
 			};
 			writer.applyTextureTransform( transmissionMapDef, material.transmissionMap );
@@ -2815,7 +2856,7 @@ class GLTFMaterialsVolumeExtension {
 
 	}
 
-	writeMaterial( material, materialDef ) {
+	async writeMaterialAsync( material, materialDef ) {
 
 		if ( ! material.isMeshPhysicalMaterial || material.transmission === 0 ) return;
 
@@ -2829,7 +2870,7 @@ class GLTFMaterialsVolumeExtension {
 		if ( material.thicknessMap ) {
 
 			const thicknessMapDef = {
-				index: writer.processTexture( material.thicknessMap ),
+				index: await writer.processTextureAsync( material.thicknessMap ),
 				texCoord: material.thicknessMap.channel
 			};
 			writer.applyTextureTransform( thicknessMapDef, material.thicknessMap );
@@ -2868,7 +2909,7 @@ class GLTFMaterialsIorExtension {
 
 	}
 
-	writeMaterial( material, materialDef ) {
+	async writeMaterialAsync( material, materialDef ) {
 
 		if ( ! material.isMeshPhysicalMaterial || material.ior === 1.5 ) return;
 
@@ -2902,7 +2943,7 @@ class GLTFMaterialsSpecularExtension {
 
 	}
 
-	writeMaterial( material, materialDef ) {
+	async writeMaterialAsync( material, materialDef ) {
 
 		if ( ! material.isMeshPhysicalMaterial || ( material.specularIntensity === 1.0 &&
 		       material.specularColor.equals( DEFAULT_SPECULAR_COLOR ) &&
@@ -2916,7 +2957,7 @@ class GLTFMaterialsSpecularExtension {
 		if ( material.specularIntensityMap ) {
 
 			const specularIntensityMapDef = {
-				index: writer.processTexture( material.specularIntensityMap ),
+				index: await writer.processTextureAsync( material.specularIntensityMap ),
 				texCoord: material.specularIntensityMap.channel
 			};
 			writer.applyTextureTransform( specularIntensityMapDef, material.specularIntensityMap );
@@ -2927,7 +2968,7 @@ class GLTFMaterialsSpecularExtension {
 		if ( material.specularColorMap ) {
 
 			const specularColorMapDef = {
-				index: writer.processTexture( material.specularColorMap ),
+				index: await writer.processTextureAsync( material.specularColorMap ),
 				texCoord: material.specularColorMap.channel
 			};
 			writer.applyTextureTransform( specularColorMapDef, material.specularColorMap );
@@ -2961,7 +3002,7 @@ class GLTFMaterialsSheenExtension {
 
 	}
 
-	writeMaterial( material, materialDef ) {
+	async writeMaterialAsync( material, materialDef ) {
 
 		if ( ! material.isMeshPhysicalMaterial || material.sheen == 0.0 ) return;
 
@@ -2973,7 +3014,7 @@ class GLTFMaterialsSheenExtension {
 		if ( material.sheenRoughnessMap ) {
 
 			const sheenRoughnessMapDef = {
-				index: writer.processTexture( material.sheenRoughnessMap ),
+				index: await writer.processTextureAsync( material.sheenRoughnessMap ),
 				texCoord: material.sheenRoughnessMap.channel
 			};
 			writer.applyTextureTransform( sheenRoughnessMapDef, material.sheenRoughnessMap );
@@ -2984,7 +3025,7 @@ class GLTFMaterialsSheenExtension {
 		if ( material.sheenColorMap ) {
 
 			const sheenColorMapDef = {
-				index: writer.processTexture( material.sheenColorMap ),
+				index: await writer.processTextureAsync( material.sheenColorMap ),
 				texCoord: material.sheenColorMap.channel
 			};
 			writer.applyTextureTransform( sheenColorMapDef, material.sheenColorMap );
@@ -3018,7 +3059,7 @@ class GLTFMaterialsAnisotropyExtension {
 
 	}
 
-	writeMaterial( material, materialDef ) {
+	async writeMaterialAsync( material, materialDef ) {
 
 		if ( ! material.isMeshPhysicalMaterial || material.anisotropy == 0.0 ) return;
 
@@ -3029,7 +3070,7 @@ class GLTFMaterialsAnisotropyExtension {
 
 		if ( material.anisotropyMap ) {
 
-			const anisotropyMapDef = { index: writer.processTexture( material.anisotropyMap ) };
+			const anisotropyMapDef = { index: await writer.processTextureAsync( material.anisotropyMap ) };
 			writer.applyTextureTransform( anisotropyMapDef, material.anisotropyMap );
 			extensionDef.anisotropyTexture = anisotropyMapDef;
 
@@ -3061,7 +3102,7 @@ class GLTFMaterialsEmissiveStrengthExtension {
 
 	}
 
-	writeMaterial( material, materialDef ) {
+	async writeMaterialAsync( material, materialDef ) {
 
 		if ( ! material.isMeshStandardMaterial || material.emissiveIntensity === 1.0 ) return;
 
@@ -3096,7 +3137,7 @@ class GLTFMaterialsBumpExtension {
 
 	}
 
-	writeMaterial( material, materialDef ) {
+	async writeMaterialAsync( material, materialDef ) {
 
 		if ( ! material.isMeshStandardMaterial || (
 		       material.bumpScale === 1 &&
@@ -3110,7 +3151,7 @@ class GLTFMaterialsBumpExtension {
 		if ( material.bumpMap ) {
 
 			const bumpMapDef = {
-				index: writer.processTexture( material.bumpMap ),
+				index: await writer.processTextureAsync( material.bumpMap ),
 				texCoord: material.bumpMap.channel
 			};
 			writer.applyTextureTransform( bumpMapDef, material.bumpMap );
@@ -3382,6 +3423,32 @@ GLTFExporter.Utils = {
 
 		return clip;
 
+	},
+
+	toFloat32BufferAttribute: function ( srcAttribute ) {
+
+		const dstAttribute = new BufferAttribute( new Float32Array( srcAttribute.count * srcAttribute.itemSize ), srcAttribute.itemSize, false );
+
+		if ( ! srcAttribute.normalized && ! srcAttribute.isInterleavedBufferAttribute ) {
+
+			dstAttribute.array.set( srcAttribute.array );
+
+			return dstAttribute;
+
+		}
+
+		for ( let i = 0, il = srcAttribute.count; i < il; i ++ ) {
+
+			for ( let j = 0; j < srcAttribute.itemSize; j ++ ) {
+
+				dstAttribute.setComponent( i, j, srcAttribute.getComponent( i, j ) );
+
+			}
+
+		}
+
+		return dstAttribute;
+
 	}
 
 };

+ 2 - 1
examples/misc_exporter_gltf.html

@@ -29,10 +29,11 @@
 			import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js';
 			import { MeshoptDecoder } from 'three/addons/libs/meshopt_decoder.module.js';
 			import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
+			import * as TextureUtils from 'three/addons/utils/WebGLTextureUtils.js';
 
 			function exportGLTF( input ) {
 
-				const gltfExporter = new GLTFExporter();
+				const gltfExporter = new GLTFExporter().setTextureUtils( TextureUtils );
 
 				const options = {
 					trs: params.trs,

粤ICP备19079148号