GLTFDRACOLoader.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. /**
  2. * @author Don McCurdy / https://www.donmccurdy.com
  3. */
  4. THREE.GLTFDRACOLoader = function(manager) {
  5. this.manager = manager || THREE.DefaultLoadingManager;
  6. this.decoderPath = '';
  7. this.decoderConfig = {};
  8. this.workerLimit = 4;
  9. this.workerPool = [];
  10. this.workerNextTaskID = 1;
  11. this.workerSourceURL = '';
  12. };
  13. THREE.GLTFDRACOLoader.prototype = {
  14. constructor: THREE.GLTFDRACOLoader,
  15. decodeDracoFile: function ( rawBuffer, callback, attributeIDs, attributeTypes ) {
  16. var taskID = this.workerNextTaskID++;
  17. var worker = this.getWorker();
  18. worker._callbacks[ taskID ] = callback;
  19. worker._taskCosts[ taskID ] = rawBuffer.byteLength;
  20. worker._taskLoad += worker._taskCosts[ taskID ];
  21. worker._taskCount++;
  22. var attributeTypesSerialized = {};
  23. for ( var name in attributeTypes ) {
  24. attributeTypesSerialized[ name ] = attributeTypes[ name ].name;
  25. }
  26. worker.postMessage( {
  27. type: 'decode',
  28. id: taskID,
  29. rawBuffer: rawBuffer,
  30. attributeIDs: attributeIDs,
  31. attributeTypes: attributeTypesSerialized
  32. } );
  33. },
  34. setDecoderPath: function ( path ) {
  35. this.decoderPath = path;
  36. },
  37. setDecoderConfig: function ( config ) {
  38. this.decoderConfig = config || {};
  39. },
  40. setWorkerLimit: function ( count ) {
  41. this.workerLimit = count;
  42. },
  43. getWorker: function () {
  44. if ( this.workerPool.length < this.workerLimit ) {
  45. if ( !this.workerSourceURL ) {
  46. var fn = GLTFDRACOWorker.toString(); // this part may fail!
  47. var body = fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) );
  48. this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) );
  49. }
  50. var worker = new Worker( this.workerSourceURL );
  51. worker._callbacks = {};
  52. worker._taskCosts = {};
  53. worker._taskLoad = 0;
  54. worker._taskCount = 0;
  55. worker.postMessage( {
  56. type: 'init',
  57. decoderPath: new URL( this.decoderPath, window.location.href ).href, // TODO: Fails in IE11.
  58. decoderConfig: this.decoderConfig
  59. } );
  60. worker.onmessage = function ( e ) {
  61. var message = e.data;
  62. switch ( message.type ) {
  63. case 'decode':
  64. var geometry = new THREE.BufferGeometry();
  65. geometry.setIndex( new THREE.BufferAttribute( message.mesh.index.buffer, 1 ) );
  66. for ( var i = 0; i < message.mesh.attributes.length; i++ ) {
  67. var attribute = message.mesh.attributes[ i ];
  68. geometry.addAttribute( attribute.name, new THREE.BufferAttribute( attribute.buffer, attribute.itemSize ) );
  69. }
  70. worker._callbacks[ message.id ]( geometry );
  71. worker._taskLoad -= worker._taskCosts[ message.id ];
  72. delete worker._callbacks[ message.id ];
  73. delete worker._taskCosts[ message.id ];
  74. break;
  75. default:
  76. throw new Error( 'THREE.GLTFDRACOLoader: Unexpected message, "' + message.type + '"' );
  77. }
  78. }
  79. this.workerPool.push( worker );
  80. } else {
  81. this.workerPool.sort( function ( a, b ) { return a._taskLoad > b._taskLoad ? -1 : 1; } );
  82. }
  83. return this.workerPool[ this.workerPool.length - 1 ];
  84. },
  85. dispose: function () {
  86. for ( var i = 0; i < this.workerPool.length; i++ ) {
  87. this.workerPool[ i ].terminate();
  88. }
  89. this.workerPool.length = 0;
  90. }
  91. };
  92. function GLTFDRACOWorker () {
  93. var decoderPath;
  94. var decoderConfig;
  95. var decoderModulePromise;
  96. onmessage = function ( e ) {
  97. var message = e.data;
  98. switch ( message.type ) {
  99. case 'init':
  100. console.log('worker:init');
  101. decoderPath = message.decoderPath;
  102. decoderConfig = message.decoderConfig;
  103. getDecoderModule();
  104. break;
  105. case 'decode':
  106. console.log('worker:decode');
  107. decode( message.rawBuffer, message.attributeIDs, message.attributeTypes )
  108. .then( function ( mesh ) {
  109. var buffers = [ mesh.index.buffer.buffer ];
  110. for ( var i = 0; i < mesh.attributes.length; i++ ) {
  111. buffers.push( mesh.attributes[ i ].buffer.buffer );
  112. }
  113. self.postMessage( { type: 'decode', id: message.id, mesh: mesh }, buffers );
  114. } );
  115. break;
  116. default:
  117. throw new Error( 'THREE.GLTFDRACOLoader: Unexpected message type.' );
  118. }
  119. };
  120. function getDecoderModule () {
  121. if ( decoderModulePromise ) return decoderModulePromise;
  122. var promise;
  123. if ( typeof WebAssembly !== 'object' || decoderConfig.type === 'js' ) {
  124. // Load with asm.js.
  125. self.importScripts( decoderPath + 'draco_decoder.js' );
  126. promise = Promise.resolve();
  127. } else {
  128. // Load with WebAssembly.
  129. self.importScripts( decoderPath + 'draco_wasm_wrapper.js' );
  130. decoderConfig.wasmBinaryFile = decoderPath + 'draco_decoder.wasm';
  131. promise = fetch( decoderConfig.wasmBinaryFile )
  132. .then( function ( wasmResponse ) { return wasmResponse.arrayBuffer(); } )
  133. .then( function ( wasmBinary ) { decoderConfig.wasmBinary = wasmBinary; } );
  134. }
  135. // Wait for source files, then create and return a decoder.
  136. promise = promise.then( function () {
  137. return new Promise( function ( resolve ) {
  138. decoderConfig.onModuleLoaded = function ( draco ) {
  139. // Module is Promise-like. Wrap before resolving to avoid loop.
  140. resolve( { draco: draco } );
  141. };
  142. DracoDecoderModule( decoderConfig );
  143. } );
  144. } );
  145. decoderModulePromise = promise;
  146. return promise;
  147. }
  148. function decode ( rawBuffer, attributeIDs, attributeTypes ) {
  149. return getDecoderModule().then( function ( module ) {
  150. var draco = module.draco;
  151. var decoder = new draco.Decoder();
  152. var buffer = new draco.DecoderBuffer();
  153. buffer.Init( new Int8Array( rawBuffer ), rawBuffer.byteLength );
  154. try {
  155. return this.decodeGeometry( draco, decoder, buffer, attributeIDs, attributeTypes );
  156. } finally {
  157. draco.destroy( buffer );
  158. draco.destroy( decoder );
  159. }
  160. } );
  161. }
  162. function decodeGeometry ( draco, decoder, buffer, attributeIDs, attributeTypes ) {
  163. var dracoGeometry = new draco.Mesh();
  164. var decodingStatus = decoder.DecodeBufferToMesh( buffer, dracoGeometry );
  165. if ( !decodingStatus.ok() || dracoGeometry.ptr == 0 ) {
  166. throw new Error( 'THREE.GLTFDRACOLoader: Decoding failed: ' + decodingStatus.error_msg() );
  167. }
  168. var geometry = { index: null, attributes: [] };
  169. var numFaces = dracoGeometry.num_faces();
  170. var numPoints = dracoGeometry.num_points();
  171. var numAttributes = dracoGeometry.num_attributes();
  172. // Add attributes of user specified unique id.
  173. for (var attributeName in attributeIDs) {
  174. var attributeType = self[ attributeTypes[ attributeName ] ];
  175. var attributeId = attributeIDs[attributeName];
  176. var attribute = decoder.GetAttributeByUniqueId( dracoGeometry, attributeId );
  177. geometry.attributes.push( this.decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) );
  178. }
  179. // Generate mesh faces.
  180. var numIndices = numFaces * 3;
  181. var index = new Uint32Array(numIndices);
  182. var indexArray = new draco.DracoInt32Array();
  183. for (var i = 0; i < numFaces; ++i) {
  184. decoder.GetFaceFromMesh(dracoGeometry, i, indexArray);
  185. index[i * 3] = indexArray.GetValue(0);
  186. index[i * 3 + 1] = indexArray.GetValue(1);
  187. index[i * 3 + 2] = indexArray.GetValue(2);
  188. }
  189. geometry.index = { buffer: index, itemSize: 1 };
  190. draco.destroy( indexArray );
  191. draco.destroy( dracoGeometry );
  192. return geometry;
  193. }
  194. function decodeAttribute ( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) {
  195. var numComponents = attribute.num_components();
  196. var numPoints = dracoGeometry.num_points();
  197. var numValues = numPoints * numComponents;
  198. var dracoArray;
  199. var buffer;
  200. switch ( attributeType ) {
  201. case Float32Array:
  202. dracoArray = new draco.DracoFloat32Array();
  203. decoder.GetAttributeFloatForAllPoints( dracoGeometry, attribute, dracoArray );
  204. buffer = new Float32Array( numValues );
  205. break;
  206. case Int8Array:
  207. dracoArray = new draco.DracoInt8Array();
  208. decoder.GetAttributeInt8ForAllPoints( dracoGeometry, attribute, dracoArray );
  209. buffer = new Int8Array( numValues );
  210. break;
  211. case Int16Array:
  212. dracoArray = new draco.DracoInt16Array();
  213. decoder.GetAttributeInt16ForAllPoints( dracoGeometry, attribute, dracoArray );
  214. buffer = new Int16Array( numValues );
  215. break;
  216. case Int32Array:
  217. dracoArray = new draco.DracoInt32Array();
  218. decoder.GetAttributeInt32ForAllPoints( dracoGeometry, attribute, dracoArray );
  219. buffer = new Int32Array( numValues );
  220. break;
  221. case Uint8Array:
  222. dracoArray = new draco.DracoUInt8Array();
  223. decoder.GetAttributeUInt8ForAllPoints( dracoGeometry, attribute, dracoArray );
  224. buffer = new Uint8Array( numValues );
  225. break;
  226. case Uint16Array:
  227. dracoArray = new draco.DracoUInt16Array();
  228. decoder.GetAttributeUInt16ForAllPoints( dracoGeometry, attribute, dracoArray );
  229. buffer = new Uint16Array( numValues );
  230. break;
  231. case Uint32Array:
  232. dracoArray = new draco.DracoUInt32Array();
  233. decoder.GetAttributeUInt32ForAllPoints( dracoGeometry, attribute, dracoArray );
  234. buffer = new Uint32Array( numValues );
  235. break;
  236. default:
  237. throw new Error( 'THREE.GLTFDRACOLoader: Unexpected attribute type.' );
  238. }
  239. for ( var i = 0; i < numValues; i++ ) {
  240. buffer[ i ] = dracoArray.GetValue( i );
  241. }
  242. draco.destroy( dracoArray );
  243. return {
  244. name: attributeName,
  245. buffer: buffer,
  246. itemSize: numComponents
  247. };
  248. };
  249. };