Browse Source

Add GLTFDRACOLoader.

Don McCurdy 6 years ago
parent
commit
3b1467c5c4
1 changed files with 366 additions and 0 deletions
  1. 366 0
      examples/js/loaders/GLTFDRACOLoader.js

+ 366 - 0
examples/js/loaders/GLTFDRACOLoader.js

@@ -0,0 +1,366 @@
+/**
+ * @author Don McCurdy / https://www.donmccurdy.com
+ */
+
+THREE.GLTFDRACOLoader = function(manager) {
+
+  this.manager = manager || THREE.DefaultLoadingManager;
+
+  this.decoderPath = '';
+  this.decoderConfig = {};
+
+  this.workerLimit = 4;
+  this.workerPool = [];
+  this.workerNextTaskID = 1;
+  this.workerSourceURL = '';
+
+};
+
+THREE.GLTFDRACOLoader.prototype = {
+
+  constructor: THREE.GLTFDRACOLoader,
+
+  decodeDracoFile: function ( rawBuffer, callback, attributeIDs, attributeTypes ) {
+
+    var taskID = this.workerNextTaskID++;
+    var worker = this.getWorker();
+    worker._callbacks[ taskID ] = callback;
+    worker._taskCosts[ taskID ] = rawBuffer.byteLength;
+    worker._taskLoad += worker._taskCosts[ taskID ];
+    worker._taskCount++;
+
+    var attributeTypesSerialized = {};
+
+    for ( var name in attributeTypes ) {
+
+      attributeTypesSerialized[ name ] = attributeTypes[ name ].name;
+
+    }
+
+    worker.postMessage( {
+      type: 'decode',
+      id: taskID,
+      rawBuffer: rawBuffer,
+      attributeIDs: attributeIDs,
+      attributeTypes: attributeTypesSerialized
+    } );
+
+  },
+
+  setDecoderPath: function ( path ) {
+
+    this.decoderPath = path;
+
+  },
+
+  setDecoderConfig: function ( config ) {
+
+    this.decoderConfig = config || {};
+
+  },
+
+  setWorkerLimit: function ( count ) {
+
+    this.workerLimit = count;
+
+  },
+
+  getWorker: function () {
+
+    if ( this.workerPool.length < this.workerLimit ) {
+
+      if ( !this.workerSourceURL ) {
+
+        var fn = GLTFDRACOWorker.toString(); // this part may fail!
+        var body = fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) );
+        this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) );
+
+      }
+
+      var worker = new Worker( this.workerSourceURL );
+
+      worker._callbacks = {};
+      worker._taskCosts = {};
+      worker._taskLoad = 0;
+      worker._taskCount = 0;
+
+      worker.postMessage( {
+        type: 'init',
+        decoderPath: new URL( this.decoderPath, window.location.href ).href, // TODO: Fails in IE11.
+        decoderConfig: this.decoderConfig
+      } );
+
+      worker.onmessage = function ( e ) {
+
+        var message = e.data;
+
+        switch ( message.type ) {
+
+          case 'decode': 
+            var geometry = new THREE.BufferGeometry();
+            geometry.setIndex( new THREE.BufferAttribute( message.mesh.index.buffer, 1 ) );
+            for ( var i = 0; i < message.mesh.attributes.length; i++ ) {
+              var attribute = message.mesh.attributes[ i ];
+              geometry.addAttribute( attribute.name, new THREE.BufferAttribute( attribute.buffer, attribute.itemSize ) );
+            }
+            worker._callbacks[ message.id ]( geometry );
+            worker._taskLoad -= worker._taskCosts[ message.id ];
+            delete worker._callbacks[ message.id ];
+            delete worker._taskCosts[ message.id ];
+            break;
+
+          default:
+            throw new Error( 'THREE.GLTFDRACOLoader: Unexpected message, "' + message.type + '"' );
+
+        }
+
+      }
+
+      this.workerPool.push( worker );
+
+    } else {
+
+      this.workerPool.sort( function ( a, b ) { return a._taskLoad > b._taskLoad ? -1 : 1; } );
+
+    }
+
+    return this.workerPool[ this.workerPool.length - 1 ];
+
+  },
+
+  dispose: function () {
+
+    for ( var i = 0; i < this.workerPool.length; i++ ) {
+
+      this.workerPool[ i ].terminate();
+
+    }
+
+    this.workerPool.length = 0;
+
+  }
+
+};
+
+function GLTFDRACOWorker () {
+
+  var decoderPath;
+  var decoderConfig;
+  var decoderModulePromise;
+
+  onmessage = function ( e ) {
+
+    var message = e.data;
+
+    switch ( message.type ) {
+
+      case 'init':
+        console.log('worker:init');
+        decoderPath = message.decoderPath;
+        decoderConfig = message.decoderConfig;
+        getDecoderModule();
+        break;
+
+      case 'decode':
+        console.log('worker:decode');
+        decode( message.rawBuffer, message.attributeIDs, message.attributeTypes )
+          .then( function ( mesh ) {
+
+            var buffers = [ mesh.index.buffer.buffer ];
+
+            for ( var i = 0; i < mesh.attributes.length; i++ ) {
+
+              buffers.push( mesh.attributes[ i ].buffer.buffer );
+
+            }
+
+            self.postMessage( { type: 'decode', id: message.id, mesh: mesh }, buffers );
+
+          } );
+        break;
+
+      default:
+        throw new Error( 'THREE.GLTFDRACOLoader: Unexpected message type.' );
+
+    }
+
+  };
+
+  function getDecoderModule () {
+    if ( decoderModulePromise ) return decoderModulePromise;
+
+    var promise;
+
+    if ( typeof WebAssembly !== 'object' || decoderConfig.type === 'js' ) {
+      // Load with asm.js.
+      self.importScripts( decoderPath + 'draco_decoder.js' );
+      promise = Promise.resolve();
+    } else {
+      // Load with WebAssembly.
+      self.importScripts( decoderPath + 'draco_wasm_wrapper.js' );
+      decoderConfig.wasmBinaryFile = decoderPath + 'draco_decoder.wasm';
+      promise = fetch( decoderConfig.wasmBinaryFile )
+        .then( function ( wasmResponse ) { return wasmResponse.arrayBuffer(); } )
+        .then( function ( wasmBinary ) { decoderConfig.wasmBinary = wasmBinary; } );
+    }
+
+    // Wait for source files, then create and return a decoder.
+    promise = promise.then( function () {
+      return new Promise( function ( resolve ) {
+        decoderConfig.onModuleLoaded = function ( draco ) {
+          // Module is Promise-like. Wrap before resolving to avoid loop.
+          resolve( { draco: draco } );
+        };
+        DracoDecoderModule( decoderConfig );
+      } );
+    } );
+
+    decoderModulePromise = promise;
+    return promise;
+
+  }
+
+  function decode ( rawBuffer, attributeIDs, attributeTypes ) {
+
+    return getDecoderModule().then( function ( module ) {
+
+      var draco = module.draco;
+      var decoder = new draco.Decoder();
+
+      var buffer = new draco.DecoderBuffer();
+      buffer.Init( new Int8Array( rawBuffer ), rawBuffer.byteLength );
+
+      try {
+
+        return this.decodeGeometry( draco, decoder, buffer, attributeIDs, attributeTypes );
+
+      } finally {
+
+        draco.destroy( buffer );
+        draco.destroy( decoder );
+
+      }
+
+    } );
+
+  }
+
+  function decodeGeometry ( draco, decoder, buffer, attributeIDs, attributeTypes ) {
+
+    var dracoGeometry = new draco.Mesh();
+    var decodingStatus = decoder.DecodeBufferToMesh( buffer, dracoGeometry );
+
+    if ( !decodingStatus.ok() || dracoGeometry.ptr == 0 ) {
+
+      throw new Error( 'THREE.GLTFDRACOLoader: Decoding failed: ' + decodingStatus.error_msg() );
+
+    }
+
+    var geometry = { index: null, attributes: [] };
+    
+    var numFaces = dracoGeometry.num_faces();
+    var numPoints = dracoGeometry.num_points();
+    var numAttributes = dracoGeometry.num_attributes();
+
+    // Add attributes of user specified unique id.
+    for (var attributeName in attributeIDs) {
+      var attributeType = self[ attributeTypes[ attributeName ] ];
+      var attributeId = attributeIDs[attributeName];
+      var attribute = decoder.GetAttributeByUniqueId( dracoGeometry, attributeId );
+      geometry.attributes.push( this.decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) );
+    }
+
+    // Generate mesh faces.
+    var numIndices = numFaces * 3;
+    var index = new Uint32Array(numIndices);
+    var indexArray = new draco.DracoInt32Array();
+    for (var i = 0; i < numFaces; ++i) {
+      decoder.GetFaceFromMesh(dracoGeometry, i, indexArray);
+      index[i * 3] = indexArray.GetValue(0);
+      index[i * 3 + 1] = indexArray.GetValue(1);
+      index[i * 3 + 2] = indexArray.GetValue(2);
+    }
+    geometry.index = { buffer: index, itemSize: 1 };
+
+    draco.destroy( indexArray );
+    draco.destroy( dracoGeometry );
+
+    return geometry;
+
+  }
+
+  function decodeAttribute ( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) {
+
+    var numComponents = attribute.num_components();
+    var numPoints = dracoGeometry.num_points();
+    var numValues = numPoints * numComponents;
+    var dracoArray;
+
+    var buffer;
+
+    switch ( attributeType ) {
+
+      case Float32Array:
+        dracoArray = new draco.DracoFloat32Array();
+        decoder.GetAttributeFloatForAllPoints( dracoGeometry, attribute, dracoArray );
+        buffer = new Float32Array( numValues );
+        break;
+
+      case Int8Array:
+        dracoArray = new draco.DracoInt8Array();
+        decoder.GetAttributeInt8ForAllPoints( dracoGeometry, attribute, dracoArray  );
+        buffer = new Int8Array( numValues );
+        break;
+
+      case Int16Array:
+        dracoArray = new draco.DracoInt16Array();
+        decoder.GetAttributeInt16ForAllPoints( dracoGeometry, attribute, dracoArray );
+        buffer = new Int16Array( numValues );
+        break;
+
+      case Int32Array:
+        dracoArray = new draco.DracoInt32Array();
+        decoder.GetAttributeInt32ForAllPoints( dracoGeometry, attribute, dracoArray );
+        buffer = new Int32Array( numValues );
+        break;
+
+      case Uint8Array:
+        dracoArray = new draco.DracoUInt8Array();
+        decoder.GetAttributeUInt8ForAllPoints( dracoGeometry, attribute, dracoArray );
+        buffer = new Uint8Array( numValues );
+        break;
+
+      case Uint16Array:
+        dracoArray = new draco.DracoUInt16Array();
+        decoder.GetAttributeUInt16ForAllPoints( dracoGeometry, attribute, dracoArray );
+        buffer = new Uint16Array( numValues );
+        break;
+
+      case Uint32Array:
+        dracoArray = new draco.DracoUInt32Array();
+        decoder.GetAttributeUInt32ForAllPoints( dracoGeometry, attribute, dracoArray );
+        buffer = new Uint32Array( numValues );
+        break;
+
+      default:
+        throw new Error( 'THREE.GLTFDRACOLoader: Unexpected attribute type.' );
+
+    }
+
+    for ( var i = 0; i < numValues; i++ ) {
+
+      buffer[ i ] = dracoArray.GetValue( i );
+
+    }
+
+    draco.destroy( dracoArray );
+
+    return {
+      name: attributeName,
+      buffer: buffer,
+      itemSize: numComponents
+    };
+    
+  };
+
+};