3DMLoader.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. /**
  2. * @author Luis Fraguada / https://github.com/fraguada
  3. */
  4. import {
  5. BufferAttribute,
  6. BufferGeometry,
  7. BufferGeometryLoader,
  8. FileLoader,
  9. Loader
  10. } from "../../../build/three.module.js";
  11. var Rhino3dmLoader = function ( manager ) {
  12. Loader.call( this, manager );
  13. this.libraryPath = '';
  14. this.libraryPending = null;
  15. this.libraryBinary = null;
  16. this.libraryConfig = {};
  17. this.workerLimit = 4;
  18. this.workerPool = [];
  19. this.workerNextTaskID = 1;
  20. this.workerSourceURL = '';
  21. this.workerConfig = {};
  22. };
  23. Rhino3dmLoader.taskCache = new WeakMap();
  24. Rhino3dmLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
  25. constructor: Rhino3dmLoader,
  26. setLibraryPath: function ( path ) {
  27. this.libraryPath = path;
  28. return this;
  29. },
  30. setWorkerLimit: function ( workerLimit ) {
  31. this.workerLimit = workerLimit;
  32. return this;
  33. },
  34. load: function ( url, onLoad, onProgress, onError ) {
  35. var loader = new FileLoader( this.manager );
  36. loader.setPath( this.path );
  37. loader.setResponseType( 'arraybuffer' );
  38. loader.load( url, ( buffer ) => {
  39. // Check for an existing task using this buffer. A transferred buffer cannot be transferred
  40. // again from this thread.
  41. if ( Rhino3dmLoader.taskCache.has( buffer ) ) {
  42. var cachedTask = Rhino3dmLoader.taskCache.get( buffer );
  43. return cachedTask.promise.then( onLoad ).catch( onError );
  44. }
  45. this.decodeObjects( buffer, url )
  46. .then( onLoad )
  47. .catch( onError );
  48. }, onProgress, onError );
  49. },
  50. debug: function () {
  51. console.log( 'Task load: ', this.workerPool.map( ( worker ) => worker._taskLoad ) );
  52. },
  53. decodeObjects: function ( buffer, url ) {
  54. var worker;
  55. var taskID;
  56. var taskCost = buffer.byteLength;
  57. var objectPending = this._getWorker( taskCost )
  58. .then( ( _worker ) => {
  59. worker = _worker;
  60. taskID = this.workerNextTaskID ++; //hmmm
  61. return new Promise( ( resolve, reject ) => {
  62. worker._callbacks[ taskID ] = { resolve, reject };
  63. worker.postMessage( { type: 'decode', id: taskID, buffer }, [ buffer ] );
  64. //this.debug();
  65. } );
  66. } )
  67. .then( ( message ) => this._createGeometry( message.geometryList ) );
  68. // Remove task from the task list.
  69. // Note: replaced '.finally()' with '.catch().then()' block - iOS 11 support (#19416)
  70. objectPending
  71. .catch( () => true )
  72. .then( () => {
  73. if ( worker && taskID ) {
  74. this._releaseTask( worker, taskID );
  75. //this.debug();
  76. }
  77. } );
  78. // Cache the task result.
  79. Rhino3dmLoader.taskCache.set( buffer, {
  80. url: url,
  81. promise: objectPending
  82. } );
  83. return objectPending;
  84. },
  85. parse: function ( ) {
  86. // parsing logic goes here
  87. console.log('3dm parsing');
  88. },
  89. _createGeometry: function ( geometryData ) {
  90. var geometries = [];
  91. let loader = new BufferGeometryLoader();
  92. for( var i = 0; i < geometryData.length; i++ ){
  93. var geometry = loader.parse( geometryData[i] );
  94. geometries.push( geometry );
  95. }
  96. return geometries;
  97. },
  98. _initLibrary: function () {
  99. if ( ! this.libraryPending ) {
  100. // Load rhino3dm wrapper.
  101. var jsLoader = new FileLoader( this.manager );
  102. jsLoader.setPath( this.libraryPath );
  103. var jsContent = new Promise( ( resolve, reject ) => {
  104. jsLoader.load( 'rhino3dm.js', resolve, undefined, reject );
  105. } );
  106. // Load rhino3dm WASM binary.
  107. var binaryLoader = new FileLoader( this.manager );
  108. binaryLoader.setPath( this.libraryPath );
  109. binaryLoader.setResponseType( 'arraybuffer' );
  110. var binaryContent = new Promise( ( resolve, reject ) => {
  111. binaryLoader.load( 'rhino3dm.wasm', resolve, undefined, reject );
  112. } );
  113. this.libraryPending = Promise.all( [ jsContent, binaryContent ] )
  114. .then( ( [ jsContent, binaryContent ] ) => {
  115. //this.libraryBinary = binaryContent;
  116. this.libraryConfig.wasmBinary = binaryContent;
  117. var fn = Rhino3dmLoader.Rhino3dmWorker.toString();
  118. var body = [
  119. '/* rhino3dm.js */',
  120. jsContent,
  121. '/* worker */',
  122. fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) )
  123. ].join( '\n' );
  124. this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) );
  125. } );
  126. }
  127. return this.libraryPending;
  128. },
  129. _getWorker: function ( taskCost ) {
  130. return this._initLibrary().then( () => {
  131. if ( this.workerPool.length < this.workerLimit ) {
  132. var worker = new Worker( this.workerSourceURL );
  133. worker._callbacks = {};
  134. worker._taskCosts = {};
  135. worker._taskLoad = 0;
  136. worker.postMessage( {
  137. type: 'init',
  138. libraryConfig: this.libraryConfig
  139. } );
  140. worker.onmessage = function ( e ) {
  141. var message = e.data;
  142. switch ( message.type ) {
  143. case 'decode':
  144. worker._callbacks[ message.id ].resolve( message );
  145. break;
  146. case 'error':
  147. worker._callbacks[ message.id ].reject( message );
  148. break;
  149. default:
  150. console.error( 'THREE.Rhino3dmLoader: Unexpected message, "' + message.type + '"' );
  151. }
  152. };
  153. this.workerPool.push( worker );
  154. } else {
  155. this.workerPool.sort( function ( a, b ) {
  156. return a._taskLoad > b._taskLoad ? - 1 : 1;
  157. } );
  158. }
  159. var worker = this.workerPool[ this.workerPool.length - 1 ];
  160. worker._taskLoad += taskCost;
  161. return worker;
  162. } );
  163. },
  164. _releaseTask: function ( worker, taskID ) {
  165. worker._taskLoad -= worker._taskCosts[ taskID ];
  166. delete worker._callbacks[ taskID ];
  167. delete worker._taskCosts[ taskID ];
  168. },
  169. dispose: function () {
  170. for ( var i = 0; i < this.workerPool.length; ++ i ) {
  171. this.workerPool[ i ].terminate();
  172. }
  173. this.workerPool.length = 0;
  174. return this;
  175. }
  176. } );
  177. /* WEB WORKER */
  178. Rhino3dmLoader.Rhino3dmWorker = function () {
  179. var libraryPending;
  180. var libraryConfig;
  181. var rhino;
  182. onmessage = function ( e ) {
  183. var message = e.data;
  184. switch ( message.type ) {
  185. case 'init':
  186. libraryConfig = message.libraryConfig;
  187. var wasmBinary = libraryConfig.wasmBinary;
  188. var RhinoModule;
  189. libraryPending = new Promise( function ( resolve ) {
  190. /* Like Basis Loader */
  191. RhinoModule = { wasmBinary, onRuntimeInitialized: resolve };
  192. rhino3dm( RhinoModule );
  193. } ).then( () => {
  194. rhino = RhinoModule;
  195. });
  196. break;
  197. case 'decode':
  198. var buffer = message.buffer;
  199. libraryPending.then( () => {
  200. var arr = new Uint8Array(buffer);
  201. var doc = rhino.File3dm.fromByteArray(arr);
  202. var objects = doc.objects();
  203. var geometryList = [];
  204. for ( var i = 0; i < objects.count; i++ ) {
  205. var obj = objects.get(i).geometry();
  206. if( obj instanceof rhino.Mesh ) {
  207. var geometry = obj.toThreejsJSON();
  208. geometryList.push( geometry );
  209. }
  210. /*
  211. if(obj instanceof rhino.PointCloud){
  212. let threePts = pointsToThreejs(obj, ptMat);
  213. scene.add(threePts);
  214. }
  215. */
  216. }
  217. self.postMessage( { type: 'decode', id: message.id, geometryList } );
  218. } );
  219. break;
  220. }
  221. }
  222. };
  223. export { Rhino3dmLoader };