Browse Source

DRACOLoader: Simplify decoder loading.

Don McCurdy 7 năm trước cách đây
mục cha
commit
e77144b7f3

+ 195 - 249
examples/js/loaders/draco/DRACOLoader.js

@@ -14,23 +14,25 @@
 //
 //
 'use strict';
 '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) {
+/**
+ * @param {THREE.LoadingManager} manager
+ */
+THREE.DRACOLoader = function(manager) {
     this.timeLoaded = 0;
     this.timeLoaded = 0;
-    this.manager = (manager !== undefined) ? manager :
-        THREE.DefaultLoadingManager;
+    this.manager = manager || THREE.DefaultLoadingManager;
     this.materials = null;
     this.materials = null;
     this.verbosity = 0;
     this.verbosity = 0;
     this.attributeOptions = {};
     this.attributeOptions = {};
-    this.dracoDecoderType =
-        (dracoDecoderType !== undefined) ? dracoDecoderType : {};
     this.drawMode = THREE.TrianglesDrawMode;
     this.drawMode = THREE.TrianglesDrawMode;
-    this.dracoSrcPath = (dracoPath !== undefined) ? dracoPath : './';
-    if (typeof DracoDecoderModule === 'undefined') {
-      THREE.DRACOLoader.loadDracoDecoder(this);
-    }
+    // User defined unique id for attributes.
+    this.attributeUniqueIdMap = {};
+    // Native Draco attribute type to Three.JS attribute type.
+    this.nativeAttributeMap = {
+      'position' : 'POSITION',
+      'normal' : 'NORMAL',
+      'color' : 'COLOR',
+      'uv' : 'TEX_COORD'
+    };
 };
 };
 
 
 THREE.DRACOLoader.prototype = {
 THREE.DRACOLoader.prototype = {
@@ -86,15 +88,30 @@ THREE.DRACOLoader.prototype = {
             skipDequantization;
             skipDequantization;
     },
     },
 
 
-    decodeDracoFile: function(rawBuffer, callback) {
+    /**
+     * |attributeUniqueIdMap| specifies attribute unique id for an attribute in
+     * the geometry to be decoded. The name of the attribute must be one of the
+     * supported attribute type in Three.JS, including:
+     *     'position',
+     *     'color',
+     *     'normal',
+     *     'uv',
+     *     'uv2',
+     *     'skinIndex',
+     *     'skinWeight'.
+     * The format is:
+     *     attributeUniqueIdMap[attributeName] = attributeId
+     */
+    decodeDracoFile: function(rawBuffer, callback, attributeUniqueIdMap) {
       var scope = this;
       var scope = this;
-      THREE.DRACOLoader.getDecoder(this,
-          function(dracoDecoder) {
-            scope.decodeDracoFileInternal(rawBuffer, dracoDecoder, callback);
-      });
+      this.attributeUniqueIdMap = attributeUniqueIdMap || {};
+      THREE.DRACOLoader.getDecoderModule()
+          .then( function ( module ) {
+            scope.decodeDracoFileInternal( rawBuffer, module.decoder, callback );
+          });
     },
     },
 
 
-    decodeDracoFileInternal : function(rawBuffer, dracoDecoder, callback) {
+    decodeDracoFileInternal: function(rawBuffer, dracoDecoder, callback) {
       /*
       /*
        * Here is how to use Draco Javascript decoder and get the geometry.
        * Here is how to use Draco Javascript decoder and get the geometry.
        */
        */
@@ -123,6 +140,33 @@ THREE.DRACOLoader.prototype = {
           geometryType, buffer));
           geometryType, buffer));
     },
     },
 
 
+    addAttributeToGeometry: function(dracoDecoder, decoder, dracoGeometry,
+                                     attributeName, 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 attributeData = new dracoDecoder.DracoFloat32Array();
+      decoder.GetAttributeFloatForAllPoints(
+          dracoGeometry, attribute, attributeData);
+      var numPoints = dracoGeometry.num_points();
+      var numValues = numPoints * numComponents;
+      // Allocate space for attribute.
+      geometryBuffer[attributeName] = new Float32Array(numValues);
+      // Copy data from decoder.
+      for (var i = 0; i < numValues; i++) {
+        geometryBuffer[attributeName][i] = attributeData.GetValue(i);
+      }
+      // Add attribute to THREEJS geometry for rendering.
+      geometry.addAttribute(attributeName,
+          new THREE.Float32BufferAttribute(geometryBuffer[attributeName],
+            numComponents));
+      dracoDecoder.destroy(attributeData);
+    },
+
     convertDracoGeometryTo3JS: function(dracoDecoder, decoder, geometryType,
     convertDracoGeometryTo3JS: function(dracoDecoder, decoder, geometryType,
                                         buffer) {
                                         buffer) {
         if (this.getAttributeOptions('position').skipDequantization === true) {
         if (this.getAttributeOptions('position').skipDequantization === true) {
@@ -153,12 +197,7 @@ THREE.DRACOLoader.prototype = {
         /*
         /*
          * Example on how to retrieve mesh and attributes.
          * 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;
+        var numFaces;
         if (geometryType == dracoDecoder.TRIANGULAR_MESH) {
         if (geometryType == dracoDecoder.TRIANGULAR_MESH) {
           numFaces = dracoGeometry.num_faces();
           numFaces = dracoGeometry.num_faces();
           if (this.verbosity > 0) {
           if (this.verbosity > 0) {
@@ -167,20 +206,18 @@ THREE.DRACOLoader.prototype = {
         } else {
         } else {
           numFaces = 0;
           numFaces = 0;
         }
         }
-        numPoints = dracoGeometry.num_points();
-        numVertexCoordinates = numPoints * 3;
-        numTextureCoordinates = numPoints * 2;
-        numColorCoordinates = numPoints * 3;
-        numAttributes = dracoGeometry.num_attributes();
+
+        var numPoints = dracoGeometry.num_points();
+        var numAttributes = dracoGeometry.num_attributes();
         if (this.verbosity > 0) {
         if (this.verbosity > 0) {
           console.log('Number of points loaded: ' + numPoints.toString());
           console.log('Number of points loaded: ' + numPoints.toString());
           console.log('Number of attributes loaded: ' +
           console.log('Number of attributes loaded: ' +
               numAttributes.toString());
               numAttributes.toString());
         }
         }
 
 
-        // Get position attribute. Must exists.
+        // Verify if there is position attribute.
         var posAttId = decoder.GetAttributeId(dracoGeometry,
         var posAttId = decoder.GetAttributeId(dracoGeometry,
-                                                dracoDecoder.POSITION);
+                                              dracoDecoder.POSITION);
         if (posAttId == -1) {
         if (posAttId == -1) {
           var errorMsg = 'THREE.DRACOLoader: No position attribute found.';
           var errorMsg = 'THREE.DRACOLoader: No position attribute found.';
           console.error(errorMsg);
           console.error(errorMsg);
@@ -189,104 +226,39 @@ THREE.DRACOLoader.prototype = {
           throw new Error(errorMsg);
           throw new Error(errorMsg);
         }
         }
         var posAttribute = decoder.GetAttribute(dracoGeometry, posAttId);
         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.
         // 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);
-            }
-        }
+        var geometryBuffer = {};
+        // Import data to Three JS geometry.
+        var geometry = new THREE.BufferGeometry();
 
 
-        // 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 native Draco attribute type to geometry.
+        for (var attributeName in this.nativeAttributeMap) {
+          // The native attribute type is only used when no unique Id is
+          // provided. For example, loading .drc files.
+          if (this.attributeUniqueIdMap[attributeName] === undefined) {
+            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, attribute, geometry, geometryBuffer);
+            }
           }
           }
         }
         }
 
 
-        // 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);
-          }
+        // Add attributes of user specified unique id. E.g. GLTF models.
+        for (var attributeName in this.attributeUniqueIdMap) {
+          var attributeId = this.attributeUniqueIdMap[attributeName];
+          var attribute = decoder.GetAttributeByUniqueId(dracoGeometry,
+                                                         attributeId);
+          this.addAttributeToGeometry(dracoDecoder, decoder, dracoGeometry,
+              attributeName, attribute, geometry, geometryBuffer);
         }
         }
 
 
-        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.
         // For mesh, we need to generate the faces.
         if (geometryType == dracoDecoder.TRIANGULAR_MESH) {
         if (geometryType == dracoDecoder.TRIANGULAR_MESH) {
           if (this.drawMode === THREE.TriangleStripDrawMode) {
           if (this.drawMode === THREE.TriangleStripDrawMode) {
@@ -313,16 +285,12 @@ THREE.DRACOLoader.prototype = {
          }
          }
         }
         }
 
 
-        // Import data to Three JS geometry.
-        var geometry = new THREE.BufferGeometry();
         geometry.drawMode = this.drawMode;
         geometry.drawMode = this.drawMode;
         if (geometryType == dracoDecoder.TRIANGULAR_MESH) {
         if (geometryType == dracoDecoder.TRIANGULAR_MESH) {
           geometry.setIndex(new(geometryBuffer.indices.length > 65535 ?
           geometry.setIndex(new(geometryBuffer.indices.length > 65535 ?
                 THREE.Uint32BufferAttribute : THREE.Uint16BufferAttribute)
                 THREE.Uint32BufferAttribute : THREE.Uint16BufferAttribute)
               (geometryBuffer.indices, 1));
               (geometryBuffer.indices, 1));
         }
         }
-        geometry.addAttribute('position',
-            new THREE.Float32BufferAttribute(geometryBuffer.vertices, 3));
         var posTransform = new dracoDecoder.AttributeQuantizationTransform();
         var posTransform = new dracoDecoder.AttributeQuantizationTransform();
         if (posTransform.InitFromAttribute(posAttribute)) {
         if (posTransform.InitFromAttribute(posAttribute)) {
           // Quantized attribute. Store the quantization parameters into the
           // Quantized attribute. Store the quantization parameters into the
@@ -338,18 +306,6 @@ THREE.DRACOLoader.prototype = {
           }
           }
         }
         }
         dracoDecoder.destroy(posTransform);
         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(decoder);
         dracoDecoder.destroy(dracoGeometry);
         dracoDecoder.destroy(dracoGeometry);
 
 
@@ -364,9 +320,9 @@ THREE.DRACOLoader.prototype = {
     },
     },
 
 
     isVersionSupported: function(version, callback) {
     isVersionSupported: function(version, callback) {
-        THREE.DRACOLoader.getDecoder(this,
-            function(decoder) {
-              callback(decoder.isVersionSupported(version));
+        THREE.DRACOLoader.getDecoderModule()
+            .then( function ( module ) {
+              callback( module.decoder.isVersionSupported( version ) );
             });
             });
     },
     },
 
 
@@ -377,119 +333,109 @@ THREE.DRACOLoader.prototype = {
     }
     }
 };
 };
 
 
-// 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();
-    };
-  }
+THREE.DRACOLoader.decoderPath = './';
+THREE.DRACOLoader.decoderConfig = {};
+THREE.DRACOLoader.decoderModulePromise = null;
 
 
-  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);
+/**
+ * Sets the base path for decoder source files.
+ * @param {string} path
+ */
+THREE.DRACOLoader.setDecoderPath = function ( path ) {
+  THREE.DRACOLoader.decoderPath = path;
+};
+
+/**
+ * Sets decoder configuration and releases singleton decoder module. Module
+ * will be recreated with the next decoding call.
+ * @param {Object} config
+ */
+THREE.DRACOLoader.setDecoderConfig = function ( config ) {
+  var wasmBinary = THREE.DRACOLoader.decoderConfig.wasmBinary;
+  THREE.DRACOLoader.decoderConfig = config || {};
+  THREE.DRACOLoader.decoderModulePromise = null;
+
+  // Reuse WASM binary.
+  if ( wasmBinary ) THREE.DRACOLoader.decoderConfig.wasmBinary = wasmBinary;
+};
+
+ /**
+  * Gets WebAssembly or asm.js singleton instance of DracoDecoderModule
+  * after testing for browser support. Returns Promise that resolves when
+  * module is available.
+  * @return {Promise<{decoder: DracoDecoderModule}>}
+  */
+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;
+
+  // Load source files.
+  if ( typeof DracoDecoderModule !== 'undefined' ) {
+    // Loaded externally.
+    promise = Promise.resolve();
+  } else if ( typeof WebAssembly !== 'object' || config.type === 'js' ) {
+    // Load with asm.js.
+    promise = THREE.DRACOLoader._loadScript( path + 'draco_decoder.js' );
   } else {
   } else {
-    THREE.DRACOLoader.loadJavaScriptFile(dracoDecoder.dracoSrcPath +
-        'draco_wasm_wrapper.js',
-        function (dracoDecoder) {
-          THREE.DRACOLoader.loadWebAssemblyDecoder(dracoDecoder);
-        }, dracoDecoder);
+    // Load with WebAssembly.
+    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;
+        } );
   }
   }
-}
+
+  // Wait for source files, then create and return a decoder.
+  promise = promise.then( function () {
+    return new Promise( function ( resolve ) {
+      config.onModuleLoaded = function ( decoder ) {
+        scope.timeLoaded = performance.now();
+        // Module is Promise-like. Wrap before resolving to avoid loop.
+        resolve( { decoder: decoder } );
+      };
+      DracoDecoderModule( config );
+    } );
+  } );
+
+  THREE.DRACOLoader.decoderModulePromise = promise;
+  return promise;
+};
 
 
 /**
 /**
- * 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|.
+ * @param {string} src
+ * @return {Promise}
  */
  */
-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);
-        }
-    };
+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 );
+  });
+};
 
 
-})();
+/**
+ * @param {string} src
+ * @return {Promise}
+ */
+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 );
+  });
+};

+ 4 - 2
examples/webgl_loader_draco.html

@@ -43,8 +43,10 @@
 
 
 		var camera, scene, renderer;
 		var camera, scene, renderer;
 
 
-                // Global Draco decoder type.
-                var dracoLoader = new THREE.DRACOLoader('js/loaders/draco/');
+		// Configure and create Draco decoder.
+		THREE.DRACOLoader.setDecoderPath('js/loaders/draco/');
+		THREE.DRACOLoader.setDecoderConfig({type: 'js'});
+		var dracoLoader = new THREE.DRACOLoader();
 		init();
 		init();
 		animate();
 		animate();