| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 'use strict'; |
|
|
| |
| |
| |
| THREE.DRACOLoader = function(manager) { |
| this.timeLoaded = 0; |
| this.manager = manager || THREE.DefaultLoadingManager; |
| this.materials = null; |
| this.verbosity = 0; |
| this.attributeOptions = {}; |
| this.drawMode = THREE.TrianglesDrawMode; |
| |
| this.nativeAttributeMap = { |
| 'position' : 'POSITION', |
| 'normal' : 'NORMAL', |
| 'color' : 'COLOR', |
| 'uv' : 'TEX_COORD' |
| }; |
| }; |
|
|
| THREE.DRACOLoader.prototype = { |
|
|
| constructor: THREE.DRACOLoader, |
|
|
| load: function(url, onLoad, onProgress, onError) { |
| var scope = this; |
| var loader = new THREE.FileLoader(scope.manager); |
| loader.setPath(this.path); |
| loader.setResponseType('arraybuffer'); |
| loader.load(url, function(blob) { |
| scope.decodeDracoFile(blob, onLoad); |
| }, onProgress, onError); |
| }, |
|
|
| setPath: function(value) { |
| this.path = value; |
| return this; |
| }, |
|
|
| setVerbosity: function(level) { |
| this.verbosity = level; |
| return this; |
| }, |
|
|
| |
| |
| |
| |
| |
| |
| setDrawMode: function(drawMode) { |
| this.drawMode = drawMode; |
| return this; |
| }, |
|
|
| |
| |
| |
| |
| |
| |
| setSkipDequantization: function(attributeName, skip) { |
| var skipDequantization = true; |
| if (typeof skip !== 'undefined') |
| skipDequantization = skip; |
| this.getAttributeOptions(attributeName).skipDequantization = |
| skipDequantization; |
| return this; |
| }, |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| decodeDracoFile: function(rawBuffer, callback, attributeUniqueIdMap, |
| attributeTypeMap) { |
| var scope = this; |
| THREE.DRACOLoader.getDecoderModule() |
| .then( function ( module ) { |
| scope.decodeDracoFileInternal( rawBuffer, module.decoder, callback, |
| attributeUniqueIdMap, attributeTypeMap); |
| }); |
| }, |
|
|
| decodeDracoFileInternal: function(rawBuffer, dracoDecoder, callback, |
| attributeUniqueIdMap, attributeTypeMap) { |
| |
| |
| |
| var buffer = new dracoDecoder.DecoderBuffer(); |
| buffer.Init(new Int8Array(rawBuffer), rawBuffer.byteLength); |
| var decoder = new dracoDecoder.Decoder(); |
|
|
| |
| |
| |
| var geometryType = decoder.GetEncodedGeometryType(buffer); |
| if (geometryType == dracoDecoder.TRIANGULAR_MESH) { |
| if (this.verbosity > 0) { |
| console.log('Loaded a mesh.'); |
| } |
| } else if (geometryType == dracoDecoder.POINT_CLOUD) { |
| if (this.verbosity > 0) { |
| console.log('Loaded a point cloud.'); |
| } |
| } else { |
| var errorMsg = 'THREE.DRACOLoader: Unknown geometry type.'; |
| console.error(errorMsg); |
| throw new Error(errorMsg); |
| } |
| callback(this.convertDracoGeometryTo3JS(dracoDecoder, decoder, |
| geometryType, buffer, attributeUniqueIdMap, attributeTypeMap)); |
| }, |
|
|
| addAttributeToGeometry: function(dracoDecoder, decoder, dracoGeometry, |
| attributeName, attributeType, attribute, |
| geometry, geometryBuffer) { |
| if (attribute.ptr === 0) { |
| var errorMsg = 'THREE.DRACOLoader: No attribute ' + attributeName; |
| console.error(errorMsg); |
| throw new Error(errorMsg); |
| } |
|
|
| var numComponents = attribute.num_components(); |
| var numPoints = dracoGeometry.num_points(); |
| var numValues = numPoints * numComponents; |
| var attributeData; |
| var TypedBufferAttribute; |
|
|
| switch ( attributeType ) { |
|
|
| case Float32Array: |
| attributeData = new dracoDecoder.DracoFloat32Array(); |
| decoder.GetAttributeFloatForAllPoints( |
| dracoGeometry, attribute, attributeData); |
| geometryBuffer[ attributeName ] = new Float32Array( numValues ); |
| TypedBufferAttribute = THREE.Float32BufferAttribute; |
| break; |
|
|
| case Int8Array: |
| attributeData = new dracoDecoder.DracoInt8Array(); |
| decoder.GetAttributeInt8ForAllPoints( |
| dracoGeometry, attribute, attributeData ); |
| geometryBuffer[ attributeName ] = new Int8Array( numValues ); |
| TypedBufferAttribute = THREE.Int8BufferAttribute; |
| break; |
|
|
| case Int16Array: |
| attributeData = new dracoDecoder.DracoInt16Array(); |
| decoder.GetAttributeInt16ForAllPoints( |
| dracoGeometry, attribute, attributeData); |
| geometryBuffer[ attributeName ] = new Int16Array( numValues ); |
| TypedBufferAttribute = THREE.Int16BufferAttribute; |
| break; |
|
|
| case Int32Array: |
| attributeData = new dracoDecoder.DracoInt32Array(); |
| decoder.GetAttributeInt32ForAllPoints( |
| dracoGeometry, attribute, attributeData); |
| geometryBuffer[ attributeName ] = new Int32Array( numValues ); |
| TypedBufferAttribute = THREE.Int32BufferAttribute; |
| break; |
|
|
| case Uint8Array: |
| attributeData = new dracoDecoder.DracoUInt8Array(); |
| decoder.GetAttributeUInt8ForAllPoints( |
| dracoGeometry, attribute, attributeData); |
| geometryBuffer[ attributeName ] = new Uint8Array( numValues ); |
| TypedBufferAttribute = THREE.Uint8BufferAttribute; |
| break; |
|
|
| case Uint16Array: |
| attributeData = new dracoDecoder.DracoUInt16Array(); |
| decoder.GetAttributeUInt16ForAllPoints( |
| dracoGeometry, attribute, attributeData); |
| geometryBuffer[ attributeName ] = new Uint16Array( numValues ); |
| TypedBufferAttribute = THREE.Uint16BufferAttribute; |
| break; |
|
|
| case Uint32Array: |
| attributeData = new dracoDecoder.DracoUInt32Array(); |
| decoder.GetAttributeUInt32ForAllPoints( |
| dracoGeometry, attribute, attributeData); |
| geometryBuffer[ attributeName ] = new Uint32Array( numValues ); |
| TypedBufferAttribute = THREE.Uint32BufferAttribute; |
| break; |
|
|
| default: |
| var errorMsg = 'THREE.DRACOLoader: Unexpected attribute type.'; |
| console.error( errorMsg ); |
| throw new Error( errorMsg ); |
|
|
| } |
|
|
| |
| for (var i = 0; i < numValues; i++) { |
| geometryBuffer[attributeName][i] = attributeData.GetValue(i); |
| } |
| |
| geometry.addAttribute(attributeName, |
| new TypedBufferAttribute(geometryBuffer[attributeName], |
| numComponents)); |
| dracoDecoder.destroy(attributeData); |
| }, |
|
|
| convertDracoGeometryTo3JS: function(dracoDecoder, decoder, geometryType, |
| buffer, attributeUniqueIdMap, |
| attributeTypeMap) { |
| |
| if (this.getAttributeOptions('position').skipDequantization === true) { |
| decoder.SkipAttributeTransform(dracoDecoder.POSITION); |
| } |
| var dracoGeometry; |
| var decodingStatus; |
| var start_time = performance.now(); |
| if (geometryType === dracoDecoder.TRIANGULAR_MESH) { |
| dracoGeometry = new dracoDecoder.Mesh(); |
| decodingStatus = decoder.DecodeBufferToMesh(buffer, dracoGeometry); |
| } else { |
| dracoGeometry = new dracoDecoder.PointCloud(); |
| decodingStatus = |
| decoder.DecodeBufferToPointCloud(buffer, dracoGeometry); |
| } |
| if (!decodingStatus.ok() || dracoGeometry.ptr == 0) { |
| var errorMsg = 'THREE.DRACOLoader: Decoding failed: '; |
| errorMsg += decodingStatus.error_msg(); |
| console.error(errorMsg); |
| dracoDecoder.destroy(decoder); |
| dracoDecoder.destroy(dracoGeometry); |
| throw new Error(errorMsg); |
| } |
|
|
| var decode_end = performance.now(); |
| dracoDecoder.destroy(buffer); |
| |
| |
| |
| var numFaces; |
| if (geometryType == dracoDecoder.TRIANGULAR_MESH) { |
| numFaces = dracoGeometry.num_faces(); |
| if (this.verbosity > 0) { |
| console.log('Number of faces loaded: ' + numFaces.toString()); |
| } |
| } else { |
| numFaces = 0; |
| } |
|
|
| var numPoints = dracoGeometry.num_points(); |
| var numAttributes = dracoGeometry.num_attributes(); |
| if (this.verbosity > 0) { |
| console.log('Number of points loaded: ' + numPoints.toString()); |
| console.log('Number of attributes loaded: ' + |
| numAttributes.toString()); |
| } |
|
|
| |
| |
| var posAttId = decoder.GetAttributeId(dracoGeometry, |
| dracoDecoder.POSITION); |
| if (posAttId == -1) { |
| var errorMsg = 'THREE.DRACOLoader: No position attribute found.'; |
| console.error(errorMsg); |
| dracoDecoder.destroy(decoder); |
| dracoDecoder.destroy(dracoGeometry); |
| throw new Error(errorMsg); |
| } |
| var posAttribute = decoder.GetAttribute(dracoGeometry, posAttId); |
|
|
| |
| var geometryBuffer = {}; |
| |
| var geometry = new THREE.BufferGeometry(); |
|
|
| |
| if ( attributeUniqueIdMap ) { |
|
|
| |
| for (var attributeName in attributeUniqueIdMap) { |
| var attributeType = attributeTypeMap[attributeName]; |
| var attributeId = attributeUniqueIdMap[attributeName]; |
| var attribute = decoder.GetAttributeByUniqueId(dracoGeometry, |
| attributeId); |
| this.addAttributeToGeometry(dracoDecoder, decoder, dracoGeometry, |
| attributeName, attributeType, attribute, geometry, geometryBuffer); |
| } |
|
|
| } else { |
|
|
| |
| for (var attributeName in this.nativeAttributeMap) { |
| var attId = decoder.GetAttributeId(dracoGeometry, |
| dracoDecoder[this.nativeAttributeMap[attributeName]]); |
| if (attId !== -1) { |
| if (this.verbosity > 0) { |
| console.log('Loaded ' + attributeName + ' attribute.'); |
| } |
| var attribute = decoder.GetAttribute(dracoGeometry, attId); |
| this.addAttributeToGeometry(dracoDecoder, decoder, dracoGeometry, |
| attributeName, Float32Array, attribute, geometry, geometryBuffer); |
| } |
| } |
|
|
| } |
|
|
| |
| if (geometryType == dracoDecoder.TRIANGULAR_MESH) { |
| if (this.drawMode === THREE.TriangleStripDrawMode) { |
| var stripsArray = new dracoDecoder.DracoInt32Array(); |
| var numStrips = decoder.GetTriangleStripsFromMesh( |
| dracoGeometry, stripsArray); |
| geometryBuffer.indices = new Uint32Array(stripsArray.size()); |
| for (var i = 0; i < stripsArray.size(); ++i) { |
| geometryBuffer.indices[i] = stripsArray.GetValue(i); |
| } |
| dracoDecoder.destroy(stripsArray); |
| } else { |
| var numIndices = numFaces * 3; |
| geometryBuffer.indices = new Uint32Array(numIndices); |
| var ia = new dracoDecoder.DracoInt32Array(); |
| for (var i = 0; i < numFaces; ++i) { |
| decoder.GetFaceFromMesh(dracoGeometry, i, ia); |
| var index = i * 3; |
| geometryBuffer.indices[index] = ia.GetValue(0); |
| geometryBuffer.indices[index + 1] = ia.GetValue(1); |
| geometryBuffer.indices[index + 2] = ia.GetValue(2); |
| } |
| dracoDecoder.destroy(ia); |
| } |
| } |
|
|
| geometry.drawMode = this.drawMode; |
| if (geometryType == dracoDecoder.TRIANGULAR_MESH) { |
| geometry.setIndex(new(geometryBuffer.indices.length > 65535 ? |
| THREE.Uint32BufferAttribute : THREE.Uint16BufferAttribute) |
| (geometryBuffer.indices, 1)); |
| } |
|
|
| |
| |
| var posTransform = new dracoDecoder.AttributeQuantizationTransform(); |
| if (posTransform.InitFromAttribute(posAttribute)) { |
| |
| |
| geometry.attributes['position'].isQuantized = true; |
| geometry.attributes['position'].maxRange = posTransform.range(); |
| geometry.attributes['position'].numQuantizationBits = |
| posTransform.quantization_bits(); |
| geometry.attributes['position'].minValues = new Float32Array(3); |
| for (var i = 0; i < 3; ++i) { |
| geometry.attributes['position'].minValues[i] = |
| posTransform.min_value(i); |
| } |
| } |
| dracoDecoder.destroy(posTransform); |
| dracoDecoder.destroy(decoder); |
| dracoDecoder.destroy(dracoGeometry); |
|
|
| this.decode_time = decode_end - start_time; |
| this.import_time = performance.now() - decode_end; |
|
|
| if (this.verbosity > 0) { |
| console.log('Decode time: ' + this.decode_time); |
| console.log('Import time: ' + this.import_time); |
| } |
| return geometry; |
| }, |
|
|
| isVersionSupported: function(version, callback) { |
| THREE.DRACOLoader.getDecoderModule() |
| .then( function ( module ) { |
| callback( module.decoder.isVersionSupported( version ) ); |
| }); |
| }, |
|
|
| getAttributeOptions: function(attributeName) { |
| if (typeof this.attributeOptions[attributeName] === 'undefined') |
| this.attributeOptions[attributeName] = {}; |
| return this.attributeOptions[attributeName]; |
| } |
| }; |
|
|
| THREE.DRACOLoader.decoderPath = './'; |
| THREE.DRACOLoader.decoderConfig = {}; |
| THREE.DRACOLoader.decoderModulePromise = null; |
|
|
| |
| |
| |
| |
| THREE.DRACOLoader.setDecoderPath = function ( path ) { |
| THREE.DRACOLoader.decoderPath = path; |
| }; |
|
|
| |
| |
| |
| |
| |
| THREE.DRACOLoader.setDecoderConfig = function ( config ) { |
| var wasmBinary = THREE.DRACOLoader.decoderConfig.wasmBinary; |
| THREE.DRACOLoader.decoderConfig = config || {}; |
| THREE.DRACOLoader.releaseDecoderModule(); |
|
|
| |
| if ( wasmBinary ) THREE.DRACOLoader.decoderConfig.wasmBinary = wasmBinary; |
| }; |
|
|
| |
| |
| |
| |
| THREE.DRACOLoader.releaseDecoderModule = function () { |
| THREE.DRACOLoader.decoderModulePromise = null; |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| THREE.DRACOLoader.getDecoderModule = function () { |
| var scope = this; |
| var path = THREE.DRACOLoader.decoderPath; |
| var config = THREE.DRACOLoader.decoderConfig; |
| var promise = THREE.DRACOLoader.decoderModulePromise; |
|
|
| if ( promise ) return promise; |
|
|
| |
| if ( typeof DracoDecoderModule !== 'undefined' ) { |
| |
| promise = Promise.resolve(); |
| } else if ( typeof WebAssembly !== 'object' || config.type === 'js' ) { |
| |
| promise = THREE.DRACOLoader._loadScript( path + 'draco_decoder.js' ); |
| } else { |
| |
| config.wasmBinaryFile = path + 'draco_decoder.wasm'; |
| promise = THREE.DRACOLoader._loadScript( path + 'draco_wasm_wrapper.js' ) |
| .then( function () { |
| return THREE.DRACOLoader._loadArrayBuffer( config.wasmBinaryFile ); |
| } ) |
| .then( function ( wasmBinary ) { |
| config.wasmBinary = wasmBinary; |
| } ); |
| } |
|
|
| |
| promise = promise.then( function () { |
| return new Promise( function ( resolve ) { |
| config.onModuleLoaded = function ( decoder ) { |
| scope.timeLoaded = performance.now(); |
| |
| resolve( { decoder: decoder } ); |
| }; |
| DracoDecoderModule( config ); |
| } ); |
| } ); |
|
|
| THREE.DRACOLoader.decoderModulePromise = promise; |
| return promise; |
| }; |
|
|
| |
| |
| |
| |
| THREE.DRACOLoader._loadScript = function ( src ) { |
| var prevScript = document.getElementById( 'decoder_script' ); |
| if ( prevScript !== null ) { |
| prevScript.parentNode.removeChild( prevScript ); |
| } |
| var head = document.getElementsByTagName( 'head' )[ 0 ]; |
| var script = document.createElement( 'script' ); |
| script.id = 'decoder_script'; |
| script.type = 'text/javascript'; |
| script.src = src; |
| return new Promise( function ( resolve ) { |
| script.onload = resolve; |
| head.appendChild( script ); |
| }); |
| }; |
|
|
| |
| |
| |
| |
| THREE.DRACOLoader._loadArrayBuffer = function ( src ) { |
| var loader = new THREE.FileLoader(); |
| loader.setResponseType( 'arraybuffer' ); |
| return new Promise( function( resolve, reject ) { |
| loader.load( src, resolve, undefined, reject ); |
| }); |
| }; |
|
|