123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495 |
- // Copyright 2016 The Draco Authors.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- //
- 'use strict';
- // |dracoPath| sets the path for the Draco decoder source files. The default
- // path is "./". If |dracoDecoderType|.type is set to "js", then DRACOLoader
- // will load the Draco JavaScript decoder.
- THREE.DRACOLoader = function(dracoPath, dracoDecoderType, manager) {
- this.timeLoaded = 0;
- this.manager = (manager !== undefined) ? manager :
- THREE.DefaultLoadingManager;
- this.materials = null;
- this.verbosity = 0;
- this.attributeOptions = {};
- this.dracoDecoderType =
- (dracoDecoderType !== undefined) ? dracoDecoderType : {};
- this.drawMode = THREE.TrianglesDrawMode;
- this.dracoSrcPath = (dracoPath !== undefined) ? dracoPath : './';
- if (typeof DracoDecoderModule === 'undefined') {
- THREE.DRACOLoader.loadDracoDecoder(this);
- }
- };
- 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');
- if (this.crossOrigin !== undefined) {
- loader.crossOrigin = this.crossOrigin;
- }
- loader.load(url, function(blob) {
- scope.decodeDracoFile(blob, onLoad);
- }, onProgress, onError);
- },
- setPath: function(value) {
- this.path = value;
- },
- setCrossOrigin: function(value) {
- this.crossOrigin = value;
- },
- setVerbosity: function(level) {
- this.verbosity = level;
- },
- /**
- * Sets desired mode for generated geometry indices.
- * Can be either:
- * THREE.TrianglesDrawMode
- * THREE.TriangleStripDrawMode
- */
- setDrawMode: function(drawMode) {
- this.drawMode = drawMode;
- },
- /**
- * Skips dequantization for a specific attribute.
- * |attributeName| is the THREE.js name of the given attribute type.
- * The only currently supported |attributeName| is 'position', more may be
- * added in future.
- */
- setSkipDequantization: function(attributeName, skip) {
- var skipDequantization = true;
- if (typeof skip !== 'undefined')
- skipDequantization = skip;
- this.getAttributeOptions(attributeName).skipDequantization =
- skipDequantization;
- },
- decodeDracoFile: function(rawBuffer, callback) {
- var scope = this;
- THREE.DRACOLoader.getDecoder(this,
- function(dracoDecoder) {
- scope.decodeDracoFileInternal(rawBuffer, dracoDecoder, callback);
- });
- },
- decodeDracoFileInternal : function(rawBuffer, dracoDecoder, callback) {
- /*
- * Here is how to use Draco Javascript decoder and get the geometry.
- */
- var buffer = new dracoDecoder.DecoderBuffer();
- buffer.Init(new Int8Array(rawBuffer), rawBuffer.byteLength);
- var decoder = new dracoDecoder.Decoder();
- /*
- * Determine what type is this file: mesh or point cloud.
- */
- 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));
- },
- convertDracoGeometryTo3JS: function(dracoDecoder, decoder, geometryType,
- buffer) {
- if (this.getAttributeOptions('position').skipDequantization === true) {
- decoder.SkipAttributeTransform(dracoDecoder.POSITION);
- }
- var dracoGeometry;
- var decodingStatus;
- const 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);
- /*
- * Example on how to retrieve mesh and attributes.
- */
- var numFaces, numPoints;
- var numVertexCoordinates, numTextureCoordinates, numColorCoordinates;
- var numAttributes;
- var numColorCoordinateComponents = 3;
- // For output basic geometry information.
- var geometryInfoStr;
- if (geometryType == dracoDecoder.TRIANGULAR_MESH) {
- numFaces = dracoGeometry.num_faces();
- if (this.verbosity > 0) {
- console.log('Number of faces loaded: ' + numFaces.toString());
- }
- } else {
- numFaces = 0;
- }
- numPoints = dracoGeometry.num_points();
- numVertexCoordinates = numPoints * 3;
- numTextureCoordinates = numPoints * 2;
- numColorCoordinates = numPoints * 3;
- numAttributes = dracoGeometry.num_attributes();
- if (this.verbosity > 0) {
- console.log('Number of points loaded: ' + numPoints.toString());
- console.log('Number of attributes loaded: ' +
- numAttributes.toString());
- }
- // Get position attribute. Must exists.
- 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 posAttributeData = new dracoDecoder.DracoFloat32Array();
- decoder.GetAttributeFloatForAllPoints(
- dracoGeometry, posAttribute, posAttributeData);
- // Get color attributes if exists.
- var colorAttId = decoder.GetAttributeId(dracoGeometry,
- dracoDecoder.COLOR);
- var colAttributeData;
- if (colorAttId != -1) {
- if (this.verbosity > 0) {
- console.log('Loaded color attribute.');
- }
- var colAttribute = decoder.GetAttribute(dracoGeometry, colorAttId);
- if (colAttribute.num_components() === 4) {
- numColorCoordinates = numPoints * 4;
- numColorCoordinateComponents = 4;
- }
- colAttributeData = new dracoDecoder.DracoFloat32Array();
- decoder.GetAttributeFloatForAllPoints(dracoGeometry, colAttribute,
- colAttributeData);
- }
- // Get normal attributes if exists.
- var normalAttId =
- decoder.GetAttributeId(dracoGeometry, dracoDecoder.NORMAL);
- var norAttributeData;
- if (normalAttId != -1) {
- if (this.verbosity > 0) {
- console.log('Loaded normal attribute.');
- }
- var norAttribute = decoder.GetAttribute(dracoGeometry, normalAttId);
- norAttributeData = new dracoDecoder.DracoFloat32Array();
- decoder.GetAttributeFloatForAllPoints(dracoGeometry, norAttribute,
- norAttributeData);
- }
- // Get texture coord attributes if exists.
- var texCoordAttId =
- decoder.GetAttributeId(dracoGeometry, dracoDecoder.TEX_COORD);
- var textCoordAttributeData;
- if (texCoordAttId != -1) {
- if (this.verbosity > 0) {
- console.log('Loaded texture coordinate attribute.');
- }
- var texCoordAttribute = decoder.GetAttribute(dracoGeometry,
- texCoordAttId);
- textCoordAttributeData = new dracoDecoder.DracoFloat32Array();
- decoder.GetAttributeFloatForAllPoints(dracoGeometry,
- texCoordAttribute,
- textCoordAttributeData);
- }
- // Structure for converting to THREEJS geometry later.
- var geometryBuffer = {
- vertices: new Float32Array(numVertexCoordinates),
- normals: new Float32Array(numVertexCoordinates),
- uvs: new Float32Array(numTextureCoordinates),
- colors: new Float32Array(numColorCoordinates)
- };
- for (var i = 0; i < numVertexCoordinates; i += 3) {
- geometryBuffer.vertices[i] = posAttributeData.GetValue(i);
- geometryBuffer.vertices[i + 1] = posAttributeData.GetValue(i + 1);
- geometryBuffer.vertices[i + 2] = posAttributeData.GetValue(i + 2);
- // Add normal.
- if (normalAttId != -1) {
- geometryBuffer.normals[i] = norAttributeData.GetValue(i);
- geometryBuffer.normals[i + 1] = norAttributeData.GetValue(i + 1);
- geometryBuffer.normals[i + 2] = norAttributeData.GetValue(i + 2);
- }
- }
- // Add color.
- for (var i = 0; i < numColorCoordinates; i += 1) {
- if (colorAttId != -1) {
- // Draco colors are already normalized.
- geometryBuffer.colors[i] = colAttributeData.GetValue(i);
- } else {
- // Default is white. This is faster than TypedArray.fill().
- geometryBuffer.colors[i] = 1.0;
- }
- }
- // Add texture coordinates.
- if (texCoordAttId != -1) {
- for (var i = 0; i < numTextureCoordinates; i += 2) {
- geometryBuffer.uvs[i] = textCoordAttributeData.GetValue(i);
- geometryBuffer.uvs[i + 1] = textCoordAttributeData.GetValue(i + 1);
- }
- }
- dracoDecoder.destroy(posAttributeData);
- if (colorAttId != -1)
- dracoDecoder.destroy(colAttributeData);
- if (normalAttId != -1)
- dracoDecoder.destroy(norAttributeData);
- if (texCoordAttId != -1)
- dracoDecoder.destroy(textCoordAttributeData);
- // For mesh, we need to generate the faces.
- 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);
- }
- }
- // Import data to Three JS geometry.
- var geometry = new THREE.BufferGeometry();
- geometry.drawMode = this.drawMode;
- if (geometryType == dracoDecoder.TRIANGULAR_MESH) {
- geometry.setIndex(new(geometryBuffer.indices.length > 65535 ?
- THREE.Uint32BufferAttribute : THREE.Uint16BufferAttribute)
- (geometryBuffer.indices, 1));
- }
- geometry.addAttribute('position',
- new THREE.Float32BufferAttribute(geometryBuffer.vertices, 3));
- var posTransform = new dracoDecoder.AttributeQuantizationTransform();
- if (posTransform.InitFromAttribute(posAttribute)) {
- // Quantized attribute. Store the quantization parameters into the
- // THREE.js attribute.
- 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);
- geometry.addAttribute('color',
- new THREE.Float32BufferAttribute(geometryBuffer.colors,
- numColorCoordinateComponents));
- if (normalAttId != -1) {
- geometry.addAttribute('normal',
- new THREE.Float32BufferAttribute(geometryBuffer.normals, 3));
- }
- if (texCoordAttId != -1) {
- geometry.addAttribute('uv',
- new THREE.Float32BufferAttribute(geometryBuffer.uvs, 2));
- }
- 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.getDecoder(this,
- function(decoder) {
- callback(decoder.isVersionSupported(version));
- });
- },
- getAttributeOptions: function(attributeName) {
- if (typeof this.attributeOptions[attributeName] === 'undefined')
- this.attributeOptions[attributeName] = {};
- return this.attributeOptions[attributeName];
- }
- };
- // This function loads a JavaScript file and adds it to the page. "path"
- // is the path to the JavaScript file. "onLoadFunc" is the function to be
- // called when the JavaScript file has been loaded.
- THREE.DRACOLoader.loadJavaScriptFile = function(path, onLoadFunc,
- dracoDecoder) {
- var head = document.getElementsByTagName('head')[0];
- var element = document.createElement('script');
- element.id = "decoder_script";
- element.type = 'text/javascript';
- element.src = path;
- if (onLoadFunc !== null) {
- element.onload = onLoadFunc(dracoDecoder);
- } else {
- element.onload = function(dracoDecoder) {
- dracoDecoder.timeLoaded = performance.now();
- };
- }
- var previous_decoder_script = document.getElementById("decoder_script");
- if (previous_decoder_script !== null) {
- previous_decoder_script.parentNode.removeChild(previous_decoder_script);
- }
- head.appendChild(element);
- }
- THREE.DRACOLoader.loadWebAssemblyDecoder = function(dracoDecoder) {
- dracoDecoder.dracoDecoderType['wasmBinaryFile'] =
- dracoDecoder.dracoSrcPath + 'draco_decoder.wasm';
- var xhr = new XMLHttpRequest();
- xhr.open('GET', dracoDecoder.dracoSrcPath + 'draco_decoder.wasm', true);
- xhr.responseType = 'arraybuffer';
- xhr.onload = function() {
- // draco_wasm_wrapper.js must be loaded before DracoDecoderModule is
- // created. The object passed into DracoDecoderModule() must contain a
- // property with the name of wasmBinary and the value must be an
- // ArrayBuffer containing the contents of the .wasm file.
- dracoDecoder.dracoDecoderType['wasmBinary'] = xhr.response;
- dracoDecoder.timeLoaded = performance.now();
- };
- xhr.send(null)
- }
- // This function will test if the browser has support for WebAssembly. If
- // it does it will download the WebAssembly Draco decoder, if not it will
- // download the asmjs Draco decoder.
- THREE.DRACOLoader.loadDracoDecoder = function(dracoDecoder) {
- if (typeof WebAssembly !== 'object' ||
- dracoDecoder.dracoDecoderType.type === 'js') {
- // No WebAssembly support
- THREE.DRACOLoader.loadJavaScriptFile(dracoDecoder.dracoSrcPath +
- 'draco_decoder.js', null, dracoDecoder);
- } else {
- THREE.DRACOLoader.loadJavaScriptFile(dracoDecoder.dracoSrcPath +
- 'draco_wasm_wrapper.js',
- function (dracoDecoder) {
- THREE.DRACOLoader.loadWebAssemblyDecoder(dracoDecoder);
- }, dracoDecoder);
- }
- }
- /**
- * Creates and returns a singleton instance of the DracoDecoderModule.
- * The module loading is done asynchronously for WebAssembly. Initialized module
- * can be accessed through the callback function
- * |onDracoDecoderModuleLoadedCallback|.
- */
- THREE.DRACOLoader.getDecoder = (function() {
- var decoder;
- var decoderCreationCalled = false;
- return function(dracoDecoder, onDracoDecoderModuleLoadedCallback) {
- if (typeof decoder !== 'undefined') {
- // Module already initialized.
- if (typeof onDracoDecoderModuleLoadedCallback !== 'undefined') {
- onDracoDecoderModuleLoadedCallback(decoder);
- }
- } else {
- if (typeof DracoDecoderModule === 'undefined') {
- // Wait until the Draco decoder is loaded before starting the error
- // timer.
- if (dracoDecoder.timeLoaded > 0) {
- var waitMs = performance.now() - dracoDecoder.timeLoaded;
- // After loading the Draco JavaScript decoder file, there is still
- // some time before the DracoDecoderModule is defined. So start a
- // loop to check when the DracoDecoderModule gets defined. If the
- // time is hit throw an error.
- if (waitMs > 5000) {
- throw new Error(
- 'THREE.DRACOLoader: DracoDecoderModule not found.');
- }
- }
- } else {
- if (!decoderCreationCalled) {
- decoderCreationCalled = true;
- dracoDecoder.dracoDecoderType['onModuleLoaded'] =
- function(module) {
- if (typeof onDracoDecoderModuleLoadedCallback ===
- 'function') {
- decoder = module;
- }
- };
- DracoDecoderModule(dracoDecoder.dracoDecoderType);
- }
- }
- // Either the DracoDecoderModule has not been defined or the decoder
- // has not been created yet. Call getDecoder() again.
- setTimeout(function() {
- THREE.DRACOLoader.getDecoder(dracoDecoder,
- onDracoDecoderModuleLoadedCallback);
- }, 10);
- }
- };
- })();
|