| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571 | ( function () {	const _taskCache = new WeakMap();	class DRACOLoader extends THREE.Loader {		constructor( manager ) {			super( manager );			this.decoderPath = '';			this.decoderConfig = {};			this.decoderBinary = null;			this.decoderPending = null;			this.workerLimit = 4;			this.workerPool = [];			this.workerNextTaskID = 1;			this.workerSourceURL = '';			this.defaultAttributeIDs = {				position: 'POSITION',				normal: 'NORMAL',				color: 'COLOR',				uv: 'TEX_COORD'			};			this.defaultAttributeTypes = {				position: 'Float32Array',				normal: 'Float32Array',				color: 'Float32Array',				uv: 'Float32Array'			};		}		setDecoderPath( path ) {			this.decoderPath = path;			return this;		}		setDecoderConfig( config ) {			this.decoderConfig = config;			return this;		}		setWorkerLimit( workerLimit ) {			this.workerLimit = workerLimit;			return this;		}		load( url, onLoad, onProgress, onError ) {			const loader = new THREE.FileLoader( this.manager );			loader.setPath( this.path );			loader.setResponseType( 'arraybuffer' );			loader.setRequestHeader( this.requestHeader );			loader.setWithCredentials( this.withCredentials );			loader.load( url, buffer => {				const taskConfig = {					attributeIDs: this.defaultAttributeIDs,					attributeTypes: this.defaultAttributeTypes,					useUniqueIDs: false				};				this.decodeGeometry( buffer, taskConfig ).then( onLoad ).catch( onError );			}, onProgress, onError );		}		/** @deprecated Kept for backward-compatibility with previous DRACOLoader versions. */		decodeDracoFile( buffer, callback, attributeIDs, attributeTypes ) {			const taskConfig = {				attributeIDs: attributeIDs || this.defaultAttributeIDs,				attributeTypes: attributeTypes || this.defaultAttributeTypes,				useUniqueIDs: !! attributeIDs			};			this.decodeGeometry( buffer, taskConfig ).then( callback );		}		decodeGeometry( buffer, taskConfig ) {			// TODO: For backward-compatibility, support 'attributeTypes' objects containing			// references (rather than names) to typed array constructors. These must be			// serialized before sending them to the worker.			for ( const attribute in taskConfig.attributeTypes ) {				const type = taskConfig.attributeTypes[ attribute ];				if ( type.BYTES_PER_ELEMENT !== undefined ) {					taskConfig.attributeTypes[ attribute ] = type.name;				}			} //			const taskKey = JSON.stringify( taskConfig ); // Check for an existing task using this buffer. A transferred buffer cannot be transferred			// again from this thread.			if ( _taskCache.has( buffer ) ) {				const cachedTask = _taskCache.get( buffer );				if ( cachedTask.key === taskKey ) {					return cachedTask.promise;				} else if ( buffer.byteLength === 0 ) {					// Technically, it would be possible to wait for the previous task to complete,					// transfer the buffer back, and decode again with the second configuration. That					// is complex, and I don't know of any reason to decode a Draco buffer twice in					// different ways, so this is left unimplemented.					throw new Error( 'THREE.DRACOLoader: Unable to re-decode a buffer with different ' + 'settings. Buffer has already been transferred.' );				}			} //			let worker;			const taskID = this.workerNextTaskID ++;			const taskCost = buffer.byteLength; // Obtain a worker and assign a task, and construct a geometry instance			// when the task completes.			const geometryPending = this._getWorker( taskID, taskCost ).then( _worker => {				worker = _worker;				return new Promise( ( resolve, reject ) => {					worker._callbacks[ taskID ] = {						resolve,						reject					};					worker.postMessage( {						type: 'decode',						id: taskID,						taskConfig,						buffer					}, [ buffer ] ); // this.debug();				} );			} ).then( message => this._createGeometry( message.geometry ) ); // Remove task from the task list.			// Note: replaced '.finally()' with '.catch().then()' block - iOS 11 support (#19416)			geometryPending.catch( () => true ).then( () => {				if ( worker && taskID ) {					this._releaseTask( worker, taskID ); // this.debug();				}			} ); // Cache the task result.			_taskCache.set( buffer, {				key: taskKey,				promise: geometryPending			} );			return geometryPending;		}		_createGeometry( geometryData ) {			const geometry = new THREE.BufferGeometry();			if ( geometryData.index ) {				geometry.setIndex( new THREE.BufferAttribute( geometryData.index.array, 1 ) );			}			for ( let i = 0; i < geometryData.attributes.length; i ++ ) {				const attribute = geometryData.attributes[ i ];				const name = attribute.name;				const array = attribute.array;				const itemSize = attribute.itemSize;				geometry.setAttribute( name, new THREE.BufferAttribute( array, itemSize ) );			}			return geometry;		}		_loadLibrary( url, responseType ) {			const loader = new THREE.FileLoader( this.manager );			loader.setPath( this.decoderPath );			loader.setResponseType( responseType );			loader.setWithCredentials( this.withCredentials );			return new Promise( ( resolve, reject ) => {				loader.load( url, resolve, undefined, reject );			} );		}		preload() {			this._initDecoder();			return this;		}		_initDecoder() {			if ( this.decoderPending ) return this.decoderPending;			const useJS = typeof WebAssembly !== 'object' || this.decoderConfig.type === 'js';			const librariesPending = [];			if ( useJS ) {				librariesPending.push( this._loadLibrary( 'draco_decoder.js', 'text' ) );			} else {				librariesPending.push( this._loadLibrary( 'draco_wasm_wrapper.js', 'text' ) );				librariesPending.push( this._loadLibrary( 'draco_decoder.wasm', 'arraybuffer' ) );			}			this.decoderPending = Promise.all( librariesPending ).then( libraries => {				const jsContent = libraries[ 0 ];				if ( ! useJS ) {					this.decoderConfig.wasmBinary = libraries[ 1 ];				}				const fn = DRACOWorker.toString();				const body = [ '/* draco decoder */', jsContent, '', '/* worker */', fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) ) ].join( '\n' );				this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) );			} );			return this.decoderPending;		}		_getWorker( taskID, taskCost ) {			return this._initDecoder().then( () => {				if ( this.workerPool.length < this.workerLimit ) {					const worker = new Worker( this.workerSourceURL );					worker._callbacks = {};					worker._taskCosts = {};					worker._taskLoad = 0;					worker.postMessage( {						type: 'init',						decoderConfig: this.decoderConfig					} );					worker.onmessage = function ( e ) {						const message = e.data;						switch ( message.type ) {							case 'decode':								worker._callbacks[ message.id ].resolve( message );								break;							case 'error':								worker._callbacks[ message.id ].reject( message );								break;							default:								console.error( 'THREE.DRACOLoader: Unexpected message, "' + message.type + '"' );						}					};					this.workerPool.push( worker );				} else {					this.workerPool.sort( function ( a, b ) {						return a._taskLoad > b._taskLoad ? - 1 : 1;					} );				}				const worker = this.workerPool[ this.workerPool.length - 1 ];				worker._taskCosts[ taskID ] = taskCost;				worker._taskLoad += taskCost;				return worker;			} );		}		_releaseTask( worker, taskID ) {			worker._taskLoad -= worker._taskCosts[ taskID ];			delete worker._callbacks[ taskID ];			delete worker._taskCosts[ taskID ];		}		debug() {			console.log( 'Task load: ', this.workerPool.map( worker => worker._taskLoad ) );		}		dispose() {			for ( let i = 0; i < this.workerPool.length; ++ i ) {				this.workerPool[ i ].terminate();			}			this.workerPool.length = 0;			return this;		}	}	/* WEB WORKER */	function DRACOWorker() {		let decoderConfig;		let decoderPending;		onmessage = function ( e ) {			const message = e.data;			switch ( message.type ) {				case 'init':					decoderConfig = message.decoderConfig;					decoderPending = new Promise( function ( resolve						/*, reject*/					) {						decoderConfig.onModuleLoaded = function ( draco ) {							// Module is Promise-like. Wrap before resolving to avoid loop.							resolve( {								draco: draco							} );						};						DracoDecoderModule( decoderConfig ); // eslint-disable-line no-undef					} );					break;				case 'decode':					const buffer = message.buffer;					const taskConfig = message.taskConfig;					decoderPending.then( module => {						const draco = module.draco;						const decoder = new draco.Decoder();						const decoderBuffer = new draco.DecoderBuffer();						decoderBuffer.Init( new Int8Array( buffer ), buffer.byteLength );						try {							const geometry = decodeGeometry( draco, decoder, decoderBuffer, taskConfig );							const buffers = geometry.attributes.map( attr => attr.array.buffer );							if ( geometry.index ) buffers.push( geometry.index.array.buffer );							self.postMessage( {								type: 'decode',								id: message.id,								geometry							}, buffers );						} catch ( error ) {							console.error( error );							self.postMessage( {								type: 'error',								id: message.id,								error: error.message							} );						} finally {							draco.destroy( decoderBuffer );							draco.destroy( decoder );						}					} );					break;			}		};		function decodeGeometry( draco, decoder, decoderBuffer, taskConfig ) {			const attributeIDs = taskConfig.attributeIDs;			const attributeTypes = taskConfig.attributeTypes;			let dracoGeometry;			let decodingStatus;			const geometryType = decoder.GetEncodedGeometryType( decoderBuffer );			if ( geometryType === draco.TRIANGULAR_MESH ) {				dracoGeometry = new draco.Mesh();				decodingStatus = decoder.DecodeBufferToMesh( decoderBuffer, dracoGeometry );			} else if ( geometryType === draco.POINT_CLOUD ) {				dracoGeometry = new draco.PointCloud();				decodingStatus = decoder.DecodeBufferToPointCloud( decoderBuffer, dracoGeometry );			} else {				throw new Error( 'THREE.DRACOLoader: Unexpected geometry type.' );			}			if ( ! decodingStatus.ok() || dracoGeometry.ptr === 0 ) {				throw new Error( 'THREE.DRACOLoader: Decoding failed: ' + decodingStatus.error_msg() );			}			const geometry = {				index: null,				attributes: []			}; // Gather all vertex attributes.			for ( const attributeName in attributeIDs ) {				const attributeType = self[ attributeTypes[ attributeName ] ];				let attribute;				let attributeID; // A Draco file may be created with default vertex attributes, whose attribute IDs				// are mapped 1:1 from their semantic name (POSITION, NORMAL, ...). Alternatively,				// a Draco file may contain a custom set of attributes, identified by known unique				// IDs. glTF files always do the latter, and `.drc` files typically do the former.				if ( taskConfig.useUniqueIDs ) {					attributeID = attributeIDs[ attributeName ];					attribute = decoder.GetAttributeByUniqueId( dracoGeometry, attributeID );				} else {					attributeID = decoder.GetAttributeId( dracoGeometry, draco[ attributeIDs[ attributeName ] ] );					if ( attributeID === - 1 ) continue;					attribute = decoder.GetAttribute( dracoGeometry, attributeID );				}				geometry.attributes.push( decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) );			} // Add index.			if ( geometryType === draco.TRIANGULAR_MESH ) {				geometry.index = decodeIndex( draco, decoder, dracoGeometry );			}			draco.destroy( dracoGeometry );			return geometry;		}		function decodeIndex( draco, decoder, dracoGeometry ) {			const numFaces = dracoGeometry.num_faces();			const numIndices = numFaces * 3;			const byteLength = numIndices * 4;			const ptr = draco._malloc( byteLength );			decoder.GetTrianglesUInt32Array( dracoGeometry, byteLength, ptr );			const index = new Uint32Array( draco.HEAPF32.buffer, ptr, numIndices ).slice();			draco._free( ptr );			return {				array: index,				itemSize: 1			};		}		function decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) {			const numComponents = attribute.num_components();			const numPoints = dracoGeometry.num_points();			const numValues = numPoints * numComponents;			const byteLength = numValues * attributeType.BYTES_PER_ELEMENT;			const dataType = getDracoDataType( draco, attributeType );			const ptr = draco._malloc( byteLength );			decoder.GetAttributeDataArrayForAllPoints( dracoGeometry, attribute, dataType, byteLength, ptr );			const array = new attributeType( draco.HEAPF32.buffer, ptr, numValues ).slice();			draco._free( ptr );			return {				name: attributeName,				array: array,				itemSize: numComponents			};		}		function getDracoDataType( draco, attributeType ) {			switch ( attributeType ) {				case Float32Array:					return draco.DT_FLOAT32;				case Int8Array:					return draco.DT_INT8;				case Int16Array:					return draco.DT_INT16;				case Int32Array:					return draco.DT_INT32;				case Uint8Array:					return draco.DT_UINT8;				case Uint16Array:					return draco.DT_UINT16;				case Uint32Array:					return draco.DT_UINT32;			}		}	}	THREE.DRACOLoader = DRACOLoader;} )();
 |