3DMLoader.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676
  1. /**
  2. * @author Luis Fraguada / https://github.com/fraguada
  3. */
  4. import {
  5. BufferAttribute,
  6. BufferGeometry,
  7. BufferGeometryLoader,
  8. FileLoader,
  9. Loader,
  10. Object3D,
  11. MeshStandardMaterial,
  12. Mesh,
  13. Color,
  14. Points,
  15. PointsMaterial
  16. } from "../../../build/three.module.js";
  17. var Rhino3dmLoader = function ( manager ) {
  18. Loader.call( this, manager );
  19. this.libraryPath = '';
  20. this.libraryPending = null;
  21. this.libraryBinary = null;
  22. this.libraryConfig = {};
  23. this.workerLimit = 4;
  24. this.workerPool = [];
  25. this.workerNextTaskID = 1;
  26. this.workerSourceURL = '';
  27. this.workerConfig = {};
  28. };
  29. Rhino3dmLoader.taskCache = new WeakMap();
  30. Rhino3dmLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
  31. constructor: Rhino3dmLoader,
  32. setLibraryPath: function ( path ) {
  33. this.libraryPath = path;
  34. return this;
  35. },
  36. setWorkerLimit: function ( workerLimit ) {
  37. this.workerLimit = workerLimit;
  38. return this;
  39. },
  40. load: function ( url, onLoad, onProgress, onError ) {
  41. var loader = new FileLoader( this.manager );
  42. loader.setPath( this.path );
  43. loader.setResponseType( 'arraybuffer' );
  44. loader.load( url, ( buffer ) => {
  45. // Check for an existing task using this buffer. A transferred buffer cannot be transferred
  46. // again from this thread.
  47. if ( Rhino3dmLoader.taskCache.has( buffer ) ) {
  48. var cachedTask = Rhino3dmLoader.taskCache.get( buffer );
  49. return cachedTask.promise.then( onLoad ).catch( onError );
  50. }
  51. this.decodeObjects( buffer, url )
  52. .then( onLoad )
  53. .catch( onError );
  54. }, onProgress, onError );
  55. },
  56. debug: function () {
  57. console.log( 'Task load: ', this.workerPool.map( ( worker ) => worker._taskLoad ) );
  58. },
  59. decodeObjects: function ( buffer, url ) {
  60. var worker;
  61. var taskID;
  62. var taskCost = buffer.byteLength;
  63. var objectPending = this._getWorker( taskCost )
  64. .then( ( _worker ) => {
  65. worker = _worker;
  66. taskID = this.workerNextTaskID ++; //hmmm
  67. return new Promise( ( resolve, reject ) => {
  68. worker._callbacks[ taskID ] = { resolve, reject };
  69. worker.postMessage( { type: 'decode', id: taskID, buffer }, [ buffer ] );
  70. //this.debug();
  71. } );
  72. } )
  73. .then( ( message ) => this._createGeometry( message.data ) );
  74. // Remove task from the task list.
  75. // Note: replaced '.finally()' with '.catch().then()' block - iOS 11 support (#19416)
  76. objectPending
  77. .catch( () => true )
  78. .then( () => {
  79. if ( worker && taskID ) {
  80. this._releaseTask( worker, taskID );
  81. //this.debug();
  82. }
  83. } );
  84. // Cache the task result.
  85. Rhino3dmLoader.taskCache.set( buffer, {
  86. url: url,
  87. promise: objectPending
  88. } );
  89. return objectPending;
  90. },
  91. parse: function ( ) {
  92. // parsing logic goes here
  93. console.log('3dm parsing');
  94. },
  95. _createMaterial: function ( material ) {
  96. if ( material === undefined ) {
  97. return new MeshStandardMaterial( {
  98. color: new Color( 1,1,1 ),
  99. metalness: 0.8,
  100. name: 'default',
  101. side: 2
  102. } );
  103. }
  104. var _diffuseColor = material.diffuseColor;
  105. var diffusecolor = new Color( _diffuseColor.r/ 255.0, _diffuseColor.g / 255.0, _diffuseColor.b / 255.0 );
  106. if ( _diffuseColor.r === 0 && _diffuseColor.g === 0 && _diffuseColor.b === 0 ) {
  107. diffusecolor.r = 1;
  108. diffusecolor.g = 1;
  109. diffusecolor.b = 1;
  110. }
  111. return new MeshStandardMaterial( {
  112. color: diffusecolor,
  113. metalness: 0.8,
  114. name: material.name,
  115. side: 2
  116. } );
  117. },
  118. _createGeometry: function ( data ) {
  119. console.log(data);
  120. var object = new Object3D();
  121. object.userData['layers'] = data.layers;
  122. object.userData['groups'] = data.groups;
  123. var loader = new BufferGeometryLoader();
  124. var objects = data.objects;
  125. var materials = data.materials;
  126. for( var i = 0; i < objects.length; i++ ){
  127. var obj = objects[i];
  128. //console.log(obj);
  129. var attributes = obj.attributes;
  130. var geometry = null;
  131. var material = null;
  132. switch( obj.objectType ) {
  133. case 'PointSet':
  134. geometry = loader.parse( obj.geometry );
  135. material = new PointsMaterial( { sizeAttenuation: true, vertexColors:true } );
  136. var points = new Points( geometry, material );
  137. points.userData['attributes'] = attributes;
  138. object.add(points);
  139. break;
  140. case 'Mesh':
  141. case 'Extrusion':
  142. geometry = loader.parse( obj.geometry );
  143. var material = this._createMaterial( materials[attributes.materialIndex] );
  144. var mesh = new Mesh(geometry, material);
  145. mesh.castShadow = attributes.castsShadows;
  146. mesh.receiveShadow = attributes.receivesShadows;
  147. mesh.userData['attributes'] = attributes;
  148. mesh.userData['objectType'] = obj.objectType;
  149. object.add( mesh );
  150. break;
  151. case 'Brep':
  152. var brepObject = new Object3D();
  153. var material = this._createMaterial( materials[attributes.materialIndex] );
  154. for( var j = 0; j < obj.geometry.length; j++ ) {
  155. geometry = loader.parse( obj.geometry[j] );
  156. var mesh = new Mesh(geometry, material);
  157. mesh.castShadow = attributes.castsShadows;
  158. mesh.receiveShadow = attributes.receivesShadows;
  159. brepObject.add( mesh );
  160. }
  161. brepObject.userData['attributes'] = attributes;
  162. brepObject.userData['objectType'] = obj.objectType;
  163. object.add( brepObject );
  164. break;
  165. }
  166. }
  167. return object;
  168. },
  169. _initLibrary: function () {
  170. if ( ! this.libraryPending ) {
  171. // Load rhino3dm wrapper.
  172. var jsLoader = new FileLoader( this.manager );
  173. jsLoader.setPath( this.libraryPath );
  174. var jsContent = new Promise( ( resolve, reject ) => {
  175. jsLoader.load( 'rhino3dm.js', resolve, undefined, reject );
  176. } );
  177. // Load rhino3dm WASM binary.
  178. var binaryLoader = new FileLoader( this.manager );
  179. binaryLoader.setPath( this.libraryPath );
  180. binaryLoader.setResponseType( 'arraybuffer' );
  181. var binaryContent = new Promise( ( resolve, reject ) => {
  182. binaryLoader.load( 'rhino3dm.wasm', resolve, undefined, reject );
  183. } );
  184. this.libraryPending = Promise.all( [ jsContent, binaryContent ] )
  185. .then( ( [ jsContent, binaryContent ] ) => {
  186. //this.libraryBinary = binaryContent;
  187. this.libraryConfig.wasmBinary = binaryContent;
  188. var fn = Rhino3dmLoader.Rhino3dmWorker.toString();
  189. var body = [
  190. '/* rhino3dm.js */',
  191. jsContent,
  192. '/* worker */',
  193. fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) )
  194. ].join( '\n' );
  195. this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) );
  196. } );
  197. }
  198. return this.libraryPending;
  199. },
  200. _getWorker: function ( taskCost ) {
  201. return this._initLibrary().then( () => {
  202. if ( this.workerPool.length < this.workerLimit ) {
  203. var worker = new Worker( this.workerSourceURL );
  204. worker._callbacks = {};
  205. worker._taskCosts = {};
  206. worker._taskLoad = 0;
  207. worker.postMessage( {
  208. type: 'init',
  209. libraryConfig: this.libraryConfig
  210. } );
  211. worker.onmessage = function ( e ) {
  212. var message = e.data;
  213. switch ( message.type ) {
  214. case 'decode':
  215. worker._callbacks[ message.id ].resolve( message );
  216. break;
  217. case 'error':
  218. worker._callbacks[ message.id ].reject( message );
  219. break;
  220. default:
  221. console.error( 'THREE.Rhino3dmLoader: Unexpected message, "' + message.type + '"' );
  222. }
  223. };
  224. this.workerPool.push( worker );
  225. } else {
  226. this.workerPool.sort( function ( a, b ) {
  227. return a._taskLoad > b._taskLoad ? - 1 : 1;
  228. } );
  229. }
  230. var worker = this.workerPool[ this.workerPool.length - 1 ];
  231. worker._taskLoad += taskCost;
  232. return worker;
  233. } );
  234. },
  235. _releaseTask: function ( worker, taskID ) {
  236. worker._taskLoad -= worker._taskCosts[ taskID ];
  237. delete worker._callbacks[ taskID ];
  238. delete worker._taskCosts[ taskID ];
  239. },
  240. dispose: function () {
  241. for ( var i = 0; i < this.workerPool.length; ++ i ) {
  242. this.workerPool[ i ].terminate();
  243. }
  244. this.workerPool.length = 0;
  245. return this;
  246. }
  247. } );
  248. /* WEB WORKER */
  249. Rhino3dmLoader.Rhino3dmWorker = function () {
  250. var libraryPending;
  251. var libraryConfig;
  252. var rhino;
  253. onmessage = function ( e ) {
  254. var message = e.data;
  255. switch ( message.type ) {
  256. case 'init':
  257. libraryConfig = message.libraryConfig;
  258. var wasmBinary = libraryConfig.wasmBinary;
  259. var RhinoModule;
  260. libraryPending = new Promise( function ( resolve ) {
  261. /* Like Basis Loader */
  262. RhinoModule = { wasmBinary, onRuntimeInitialized: resolve };
  263. rhino3dm( RhinoModule );
  264. } ).then( () => {
  265. rhino = RhinoModule;
  266. });
  267. break;
  268. case 'decode':
  269. var buffer = message.buffer;
  270. libraryPending.then( () => {
  271. var data = decodeObjects( rhino, buffer );
  272. self.postMessage( { type: 'decode', id: message.id, data } );
  273. } );
  274. break;
  275. }
  276. };
  277. function decodeObjects( rhino, buffer ) {
  278. var arr = new Uint8Array(buffer);
  279. var doc = rhino.File3dm.fromByteArray(arr);
  280. var objects = [];
  281. var materials = [];
  282. var layers = [];
  283. var views = [];
  284. var namedViews = [];
  285. var groups = [];
  286. // var strings = [];
  287. //Handle objects
  288. for( var i = 0; i < doc.objects().count; i++ ) {
  289. var _object = doc.objects().get(i);
  290. var _geometry = _object.geometry();
  291. var _attributes = _object.attributes();
  292. var objectType = _geometry.objectType;
  293. var geometry = null;
  294. // TODO: handle other geometry types
  295. switch( objectType ) {
  296. case rhino.ObjectType.Point:
  297. case rhino.ObjectType.Light:
  298. case rhino.ObjectType.Curve:
  299. case rhino.ObjectType.Annotation:
  300. case rhino.ObjectType.InstanceReference:
  301. case rhino.ObjectType.TextDot:
  302. case rhino.ObjectType.Hatch:
  303. case rhino.ObjectType.SubD:
  304. case rhino.ObjectType.ClipPlane:
  305. console.warn(`THREE.3DMLoader: TODO: Implement ${objectType.constructor.name}`);
  306. break;
  307. case rhino.ObjectType.PointSet:
  308. case rhino.ObjectType.Mesh:
  309. geometry = _geometry.toThreejsJSON();
  310. break;
  311. case rhino.ObjectType.Brep:
  312. var faces = _geometry.faces();
  313. geometry = [];
  314. for (var faceIndex = 0; faceIndex < faces.count; faceIndex++) {
  315. var face = faces.get( faceIndex );
  316. var mesh = face.getMesh( rhino.MeshType.Any );
  317. if ( mesh ) {
  318. geometry.push( mesh.toThreejsJSON() );
  319. mesh.delete();
  320. }
  321. face.delete();
  322. }
  323. faces.delete();
  324. break;
  325. case rhino.ObjectType.Extrusion:
  326. var mesh = _geometry.getMesh(rhino.MeshType.Any);
  327. if( mesh ) {
  328. geometry = mesh.toThreejsJSON();
  329. mesh.delete();
  330. }
  331. break;
  332. }
  333. if( geometry ) {
  334. var attributes = extractProperties( _attributes );
  335. objectType = objectType.constructor.name;
  336. objectType = objectType.substring( 11, objectType.length );
  337. objects.push( { geometry, attributes, objectType: objectType } );
  338. }
  339. _geometry.delete();
  340. _object.delete();
  341. }
  342. //Handle materials
  343. for( var i = 0; i < doc.materials().count(); i++) {
  344. var _material = doc.materials().get( i );
  345. var materialProperties = extractProperties( _material );
  346. var pbMaterialProperties = extractProperties( _material.physicallyBased() );
  347. var material = Object.assign(materialProperties, pbMaterialProperties);
  348. materials.push( material );
  349. _material.delete();
  350. }
  351. // Handle layers
  352. for( var i = 0; i < doc.layers().count(); i++) {
  353. var _layer = doc.layers().get( i );
  354. var layer = extractProperties( _layer );
  355. layers.push( layer );
  356. _layer.delete();
  357. }
  358. // Handle views
  359. for( var i = 0; i < doc.views().count(); i++) {
  360. var _view = doc.views().get( i );
  361. var view = extractProperties( _view );
  362. views.push( view );
  363. _view.delete();
  364. }
  365. // Handle named views
  366. for( var i = 0; i < doc.namedViews().count(); i++) {
  367. var _namedView = doc.namedViews().get( i );
  368. var namedView = extractProperties( _namedView );
  369. namedViews.push( namedView );
  370. _namedView.delete();
  371. }
  372. // Handle groups
  373. for( var i = 0; i < doc.groups().count(); i++ ){
  374. var _group = doc.groups().get( i );
  375. var group = extractProperties( _group );
  376. groups.push( group );
  377. _group.delete();
  378. }
  379. // Handle settings
  380. var settings = extractProperties( doc.settings() );
  381. //TODO: Handle other document stuff like dimstyles, instance definitions, bitmaps etc.
  382. // Handle dimstyles
  383. // console.log(`Dimstyle Count: ${doc.dimstyles().count()}`);
  384. // Handle bitmaps
  385. // console.log(`Bitmap Count: ${doc.bitmaps().count()}`);
  386. // Handle instance definitions
  387. // console.log(`Instance Definitions Count: ${doc.instanceDefinitions().count()}`);
  388. // Handle strings -- this seems to be broken at the moment in rhino3dm
  389. // console.log(`Strings Count: ${doc.strings().count()}`);
  390. /*
  391. for( var i = 0; i < doc.strings().count(); i++ ){
  392. var _string= doc.strings().get( i );
  393. console.log(_string);
  394. var string = extractProperties( _group );
  395. strings.push( string );
  396. _string.delete();
  397. }
  398. */
  399. doc.delete();
  400. return { objects, materials, layers, views, namedViews, groups, settings };
  401. }
  402. function extractProperties( object ) {
  403. var result = {};
  404. for ( var property in object ) {
  405. if( typeof object[property] !== 'function' ){
  406. result[property] = object[property];
  407. } else {
  408. // console.log(`${property}: ${object[property]}`);
  409. }
  410. }
  411. return result;
  412. }
  413. };
  414. export { Rhino3dmLoader };