GLTFDRACOLoader.js 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  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();
  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. decoderPath = message.decoderPath;
  101. decoderConfig = message.decoderConfig;
  102. getDecoderModule();
  103. break;
  104. case 'decode':
  105. decode( message.rawBuffer, message.attributeIDs, message.attributeTypes )
  106. .then( function ( mesh ) {
  107. var buffers = [ mesh.index.buffer.buffer ];
  108. for ( var i = 0; i < mesh.attributes.length; i++ ) {
  109. buffers.push( mesh.attributes[ i ].buffer.buffer );
  110. }
  111. self.postMessage( { type: 'decode', id: message.id, mesh: mesh }, buffers );
  112. } );
  113. break;
  114. default:
  115. throw new Error( 'THREE.GLTFDRACOLoader: Unexpected message type.' );
  116. }
  117. };
  118. function getDecoderModule () {
  119. if ( decoderModulePromise ) return decoderModulePromise;
  120. var promise;
  121. if ( typeof WebAssembly !== 'object' || decoderConfig.type === 'js' ) {
  122. // Load with asm.js.
  123. self.importScripts( decoderPath + 'draco_decoder.js' );
  124. promise = Promise.resolve();
  125. } else {
  126. // Load with WebAssembly.
  127. self.importScripts( decoderPath + 'draco_wasm_wrapper.js' );
  128. decoderConfig.wasmBinaryFile = decoderPath + 'draco_decoder.wasm';
  129. promise = fetch( decoderConfig.wasmBinaryFile )
  130. .then( function ( wasmResponse ) { return wasmResponse.arrayBuffer(); } )
  131. .then( function ( wasmBinary ) { decoderConfig.wasmBinary = wasmBinary; } );
  132. }
  133. // Wait for source files, then create and return a decoder.
  134. promise = promise.then( function () {
  135. return new Promise( function ( resolve ) {
  136. decoderConfig.onModuleLoaded = function ( draco ) {
  137. // Module is Promise-like. Wrap before resolving to avoid loop.
  138. resolve( { draco: draco } );
  139. };
  140. DracoDecoderModule( decoderConfig );
  141. } );
  142. } );
  143. decoderModulePromise = promise;
  144. return promise;
  145. }
  146. function decode ( rawBuffer, attributeIDs, attributeTypes ) {
  147. return getDecoderModule().then( function ( module ) {
  148. var draco = module.draco;
  149. var decoder = new draco.Decoder();
  150. var buffer = new draco.DecoderBuffer();
  151. buffer.Init( new Int8Array( rawBuffer ), rawBuffer.byteLength );
  152. try {
  153. return this.decodeGeometry( draco, decoder, buffer, attributeIDs, attributeTypes );
  154. } finally {
  155. draco.destroy( buffer );
  156. draco.destroy( decoder );
  157. }
  158. } );
  159. }
  160. function decodeGeometry ( draco, decoder, buffer, attributeIDs, attributeTypes ) {
  161. var dracoGeometry = new draco.Mesh();
  162. var decodingStatus = decoder.DecodeBufferToMesh( buffer, dracoGeometry );
  163. if ( !decodingStatus.ok() || dracoGeometry.ptr == 0 ) {
  164. throw new Error( 'THREE.GLTFDRACOLoader: Decoding failed: ' + decodingStatus.error_msg() );
  165. }
  166. var geometry = { index: null, attributes: [] };
  167. var numFaces = dracoGeometry.num_faces();
  168. var numPoints = dracoGeometry.num_points();
  169. var numAttributes = dracoGeometry.num_attributes();
  170. // Add attributes of user specified unique id.
  171. for (var attributeName in attributeIDs) {
  172. var attributeType = self[ attributeTypes[ attributeName ] ];
  173. var attributeId = attributeIDs[attributeName];
  174. var attribute = decoder.GetAttributeByUniqueId( dracoGeometry, attributeId );
  175. geometry.attributes.push( this.decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) );
  176. }
  177. // Generate mesh faces.
  178. var numIndices = numFaces * 3;
  179. var index = new Uint32Array(numIndices);
  180. var indexArray = new draco.DracoInt32Array();
  181. for (var i = 0; i < numFaces; ++i) {
  182. decoder.GetFaceFromMesh(dracoGeometry, i, indexArray);
  183. index[i * 3] = indexArray.GetValue(0);
  184. index[i * 3 + 1] = indexArray.GetValue(1);
  185. index[i * 3 + 2] = indexArray.GetValue(2);
  186. }
  187. geometry.index = { buffer: index, itemSize: 1 };
  188. draco.destroy( indexArray );
  189. draco.destroy( dracoGeometry );
  190. return geometry;
  191. }
  192. function decodeAttribute ( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) {
  193. var numComponents = attribute.num_components();
  194. var numPoints = dracoGeometry.num_points();
  195. var numValues = numPoints * numComponents;
  196. var dracoArray;
  197. var buffer;
  198. switch ( attributeType ) {
  199. case Float32Array:
  200. dracoArray = new draco.DracoFloat32Array();
  201. decoder.GetAttributeFloatForAllPoints( dracoGeometry, attribute, dracoArray );
  202. buffer = new Float32Array( numValues );
  203. break;
  204. case Int8Array:
  205. dracoArray = new draco.DracoInt8Array();
  206. decoder.GetAttributeInt8ForAllPoints( dracoGeometry, attribute, dracoArray );
  207. buffer = new Int8Array( numValues );
  208. break;
  209. case Int16Array:
  210. dracoArray = new draco.DracoInt16Array();
  211. decoder.GetAttributeInt16ForAllPoints( dracoGeometry, attribute, dracoArray );
  212. buffer = new Int16Array( numValues );
  213. break;
  214. case Int32Array:
  215. dracoArray = new draco.DracoInt32Array();
  216. decoder.GetAttributeInt32ForAllPoints( dracoGeometry, attribute, dracoArray );
  217. buffer = new Int32Array( numValues );
  218. break;
  219. case Uint8Array:
  220. dracoArray = new draco.DracoUInt8Array();
  221. decoder.GetAttributeUInt8ForAllPoints( dracoGeometry, attribute, dracoArray );
  222. buffer = new Uint8Array( numValues );
  223. break;
  224. case Uint16Array:
  225. dracoArray = new draco.DracoUInt16Array();
  226. decoder.GetAttributeUInt16ForAllPoints( dracoGeometry, attribute, dracoArray );
  227. buffer = new Uint16Array( numValues );
  228. break;
  229. case Uint32Array:
  230. dracoArray = new draco.DracoUInt32Array();
  231. decoder.GetAttributeUInt32ForAllPoints( dracoGeometry, attribute, dracoArray );
  232. buffer = new Uint32Array( numValues );
  233. break;
  234. default:
  235. throw new Error( 'THREE.GLTFDRACOLoader: Unexpected attribute type.' );
  236. }
  237. for ( var i = 0; i < numValues; i++ ) {
  238. buffer[ i ] = dracoArray.GetValue( i );
  239. }
  240. draco.destroy( dracoArray );
  241. return {
  242. name: attributeName,
  243. buffer: buffer,
  244. itemSize: numComponents
  245. };
  246. };
  247. };