3DMLoader.js 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264
  1. ( function () {
  2. var Rhino3dmLoader = function ( manager ) {
  3. THREE.Loader.call( this, manager );
  4. this.libraryPath = '';
  5. this.libraryPending = null;
  6. this.libraryBinary = null;
  7. this.libraryConfig = {};
  8. this.url = '';
  9. this.workerLimit = 4;
  10. this.workerPool = [];
  11. this.workerNextTaskID = 1;
  12. this.workerSourceURL = '';
  13. this.workerConfig = {};
  14. this.materials = [];
  15. };
  16. Rhino3dmLoader.taskCache = new WeakMap();
  17. Rhino3dmLoader.prototype = Object.assign( Object.create( THREE.Loader.prototype ), {
  18. constructor: Rhino3dmLoader,
  19. setLibraryPath: function ( path ) {
  20. this.libraryPath = path;
  21. return this;
  22. },
  23. setWorkerLimit: function ( workerLimit ) {
  24. this.workerLimit = workerLimit;
  25. return this;
  26. },
  27. load: function ( url, onLoad, onProgress, onError ) {
  28. var loader = new THREE.FileLoader( this.manager );
  29. loader.setPath( this.path );
  30. loader.setResponseType( 'arraybuffer' );
  31. loader.setRequestHeader( this.requestHeader );
  32. this.url = url;
  33. loader.load( url, buffer => {
  34. // Check for an existing task using this buffer. A transferred buffer cannot be transferred
  35. // again from this thread.
  36. if ( Rhino3dmLoader.taskCache.has( buffer ) ) {
  37. var cachedTask = Rhino3dmLoader.taskCache.get( buffer );
  38. return cachedTask.promise.then( onLoad ).catch( onError );
  39. }
  40. this.decodeObjects( buffer, url ).then( onLoad ).catch( onError );
  41. }, onProgress, onError );
  42. },
  43. debug: function () {
  44. console.log( 'Task load: ', this.workerPool.map( worker => worker._taskLoad ) );
  45. },
  46. decodeObjects: function ( buffer, url ) {
  47. var worker;
  48. var taskID;
  49. var taskCost = buffer.byteLength;
  50. var objectPending = this._getWorker( taskCost ).then( _worker => {
  51. worker = _worker;
  52. taskID = this.workerNextTaskID ++; //hmmm
  53. return new Promise( ( resolve, reject ) => {
  54. worker._callbacks[ taskID ] = {
  55. resolve,
  56. reject
  57. };
  58. worker.postMessage( {
  59. type: 'decode',
  60. id: taskID,
  61. buffer
  62. }, [ buffer ] ); //this.debug();
  63. } );
  64. } ).then( message => this._createGeometry( message.data ) ); // Remove task from the task list.
  65. // Note: replaced '.finally()' with '.catch().then()' block - iOS 11 support (#19416)
  66. objectPending.catch( () => true ).then( () => {
  67. if ( worker && taskID ) {
  68. this._releaseTask( worker, taskID ); //this.debug();
  69. }
  70. } ); // Cache the task result.
  71. Rhino3dmLoader.taskCache.set( buffer, {
  72. url: url,
  73. promise: objectPending
  74. } );
  75. return objectPending;
  76. },
  77. parse: function ( data, onLoad, onError ) {
  78. this.decodeObjects( data, '' ).then( onLoad ).catch( onError );
  79. },
  80. _compareMaterials: function ( material ) {
  81. var mat = {};
  82. mat.name = material.name;
  83. mat.color = {};
  84. mat.color.r = material.color.r;
  85. mat.color.g = material.color.g;
  86. mat.color.b = material.color.b;
  87. mat.type = material.type;
  88. for ( var i = 0; i < this.materials.length; i ++ ) {
  89. var m = this.materials[ i ];
  90. var _mat = {};
  91. _mat.name = m.name;
  92. _mat.color = {};
  93. _mat.color.r = m.color.r;
  94. _mat.color.g = m.color.g;
  95. _mat.color.b = m.color.b;
  96. _mat.type = m.type;
  97. if ( JSON.stringify( mat ) === JSON.stringify( _mat ) ) {
  98. return m;
  99. }
  100. }
  101. this.materials.push( material );
  102. return material;
  103. },
  104. _createMaterial: function ( material ) {
  105. if ( material === undefined ) {
  106. return new THREE.MeshStandardMaterial( {
  107. color: new THREE.Color( 1, 1, 1 ),
  108. metalness: 0.8,
  109. name: 'default',
  110. side: 2
  111. } );
  112. }
  113. var _diffuseColor = material.diffuseColor;
  114. var diffusecolor = new THREE.Color( _diffuseColor.r / 255.0, _diffuseColor.g / 255.0, _diffuseColor.b / 255.0 );
  115. if ( _diffuseColor.r === 0 && _diffuseColor.g === 0 && _diffuseColor.b === 0 ) {
  116. diffusecolor.r = 1;
  117. diffusecolor.g = 1;
  118. diffusecolor.b = 1;
  119. } // console.log( material );
  120. var mat = new THREE.MeshStandardMaterial( {
  121. color: diffusecolor,
  122. name: material.name,
  123. side: 2,
  124. transparent: material.transparency > 0 ? true : false,
  125. opacity: 1.0 - material.transparency
  126. } );
  127. var textureLoader = new THREE.TextureLoader();
  128. for ( var i = 0; i < material.textures.length; i ++ ) {
  129. var texture = material.textures[ i ];
  130. if ( texture.image !== null ) {
  131. var map = textureLoader.load( texture.image );
  132. switch ( texture.type ) {
  133. case 'Diffuse':
  134. mat.map = map;
  135. break;
  136. case 'Bump':
  137. mat.bumpMap = map;
  138. break;
  139. case 'Transparency':
  140. mat.alphaMap = map;
  141. mat.transparent = true;
  142. break;
  143. case 'Emap':
  144. mat.envMap = map;
  145. break;
  146. }
  147. }
  148. }
  149. return mat;
  150. },
  151. _createGeometry: function ( data ) {
  152. // console.log(data);
  153. var object = new THREE.Object3D();
  154. var instanceDefinitionObjects = [];
  155. var instanceDefinitions = [];
  156. var instanceReferences = [];
  157. object.userData[ 'layers' ] = data.layers;
  158. object.userData[ 'groups' ] = data.groups;
  159. object.userData[ 'settings' ] = data.settings;
  160. object.userData[ 'objectType' ] = 'File3dm';
  161. object.userData[ 'materials' ] = null;
  162. object.name = this.url;
  163. var objects = data.objects;
  164. var materials = data.materials;
  165. for ( var i = 0; i < objects.length; i ++ ) {
  166. var obj = objects[ i ];
  167. var attributes = obj.attributes;
  168. switch ( obj.objectType ) {
  169. case 'InstanceDefinition':
  170. instanceDefinitions.push( obj );
  171. break;
  172. case 'InstanceReference':
  173. instanceReferences.push( obj );
  174. break;
  175. default:
  176. if ( attributes.materialIndex >= 0 ) {
  177. var rMaterial = materials[ attributes.materialIndex ];
  178. var material = this._createMaterial( rMaterial );
  179. material = this._compareMaterials( material );
  180. var _object = this._createObject( obj, material );
  181. } else {
  182. var material = this._createMaterial();
  183. var _object = this._createObject( obj, material );
  184. }
  185. if ( _object === undefined ) {
  186. continue;
  187. }
  188. var layer = data.layers[ attributes.layerIndex ];
  189. _object.visible = layer ? data.layers[ attributes.layerIndex ].visible : true;
  190. if ( attributes.isInstanceDefinitionObject ) {
  191. instanceDefinitionObjects.push( _object );
  192. } else {
  193. object.add( _object );
  194. }
  195. break;
  196. }
  197. }
  198. for ( var i = 0; i < instanceDefinitions.length; i ++ ) {
  199. var iDef = instanceDefinitions[ i ];
  200. var objects = [];
  201. for ( var j = 0; j < iDef.attributes.objectIds.length; j ++ ) {
  202. var objId = iDef.attributes.objectIds[ j ];
  203. for ( var p = 0; p < instanceDefinitionObjects.length; p ++ ) {
  204. var idoId = instanceDefinitionObjects[ p ].userData.attributes.id;
  205. if ( objId === idoId ) {
  206. objects.push( instanceDefinitionObjects[ p ] );
  207. }
  208. }
  209. } // Currently clones geometry and does not take advantage of instancing
  210. for ( var j = 0; j < instanceReferences.length; j ++ ) {
  211. var iRef = instanceReferences[ j ];
  212. if ( iRef.geometry.parentIdefId === iDef.attributes.id ) {
  213. var iRefObject = new THREE.Object3D();
  214. var xf = iRef.geometry.xform.array;
  215. var matrix = new THREE.Matrix4();
  216. matrix.set( xf[ 0 ], xf[ 1 ], xf[ 2 ], xf[ 3 ], xf[ 4 ], xf[ 5 ], xf[ 6 ], xf[ 7 ], xf[ 8 ], xf[ 9 ], xf[ 10 ], xf[ 11 ], xf[ 12 ], xf[ 13 ], xf[ 14 ], xf[ 15 ] );
  217. iRefObject.applyMatrix4( matrix );
  218. for ( var p = 0; p < objects.length; p ++ ) {
  219. iRefObject.add( objects[ p ].clone( true ) );
  220. }
  221. object.add( iRefObject );
  222. }
  223. }
  224. }
  225. object.userData[ 'materials' ] = this.materials;
  226. return object;
  227. },
  228. _createObject: function ( obj, mat ) {
  229. var loader = new THREE.BufferGeometryLoader();
  230. var attributes = obj.attributes;
  231. switch ( obj.objectType ) {
  232. case 'Point':
  233. case 'PointSet':
  234. var geometry = loader.parse( obj.geometry );
  235. var material = null;
  236. if ( geometry.attributes.hasOwnProperty( 'color' ) ) {
  237. material = new THREE.PointsMaterial( {
  238. vertexColors: true,
  239. sizeAttenuation: false,
  240. size: 2
  241. } );
  242. } else {
  243. var _color = attributes.drawColor;
  244. var color = new THREE.Color( _color.r / 255.0, _color.g / 255.0, _color.b / 255.0 );
  245. material = new THREE.PointsMaterial( {
  246. color: color,
  247. sizeAttenuation: false,
  248. size: 2
  249. } );
  250. }
  251. material = this._compareMaterials( material );
  252. var points = new THREE.Points( geometry, material );
  253. points.userData[ 'attributes' ] = attributes;
  254. points.userData[ 'objectType' ] = obj.objectType;
  255. if ( attributes.name ) {
  256. points.name = attributes.name;
  257. }
  258. return points;
  259. case 'Mesh':
  260. case 'Extrusion':
  261. case 'SubD':
  262. case 'Brep':
  263. if ( obj.geometry === null ) return;
  264. var geometry = loader.parse( obj.geometry );
  265. if ( geometry.attributes.hasOwnProperty( 'color' ) ) {
  266. mat.vertexColors = true;
  267. }
  268. if ( mat === null ) {
  269. mat = this._createMaterial();
  270. mat = this._compareMaterials( mat );
  271. }
  272. var mesh = new THREE.Mesh( geometry, mat );
  273. mesh.castShadow = attributes.castsShadows;
  274. mesh.receiveShadow = attributes.receivesShadows;
  275. mesh.userData[ 'attributes' ] = attributes;
  276. mesh.userData[ 'objectType' ] = obj.objectType;
  277. if ( attributes.name ) {
  278. mesh.name = attributes.name;
  279. }
  280. return mesh;
  281. case 'Curve':
  282. geometry = loader.parse( obj.geometry );
  283. var _color = attributes.drawColor;
  284. var color = new THREE.Color( _color.r / 255.0, _color.g / 255.0, _color.b / 255.0 );
  285. var material = new THREE.LineBasicMaterial( {
  286. color: color
  287. } );
  288. material = this._compareMaterials( material );
  289. var lines = new THREE.Line( geometry, material );
  290. lines.userData[ 'attributes' ] = attributes;
  291. lines.userData[ 'objectType' ] = obj.objectType;
  292. if ( attributes.name ) {
  293. lines.name = attributes.name;
  294. }
  295. return lines;
  296. case 'TextDot':
  297. geometry = obj.geometry;
  298. var ctx = document.createElement( 'canvas' ).getContext( '2d' );
  299. var font = `${geometry.fontHeight}px ${geometry.fontFace}`;
  300. ctx.font = font;
  301. var width = ctx.measureText( geometry.text ).width + 10;
  302. var height = geometry.fontHeight + 10;
  303. var r = window.devicePixelRatio;
  304. ctx.canvas.width = width * r;
  305. ctx.canvas.height = height * r;
  306. ctx.canvas.style.width = width + 'px';
  307. ctx.canvas.style.height = height + 'px';
  308. ctx.setTransform( r, 0, 0, r, 0, 0 );
  309. ctx.font = font;
  310. ctx.textBaseline = 'middle';
  311. ctx.textAlign = 'center';
  312. var color = attributes.drawColor;
  313. ctx.fillStyle = `rgba(${color.r},${color.g},${color.b},${color.a})`;
  314. ctx.fillRect( 0, 0, width, height );
  315. ctx.fillStyle = 'white';
  316. ctx.fillText( geometry.text, width / 2, height / 2 );
  317. var texture = new THREE.CanvasTexture( ctx.canvas );
  318. texture.minFilter = THREE.LinearFilter;
  319. texture.wrapS = THREE.ClampToEdgeWrapping;
  320. texture.wrapT = THREE.ClampToEdgeWrapping;
  321. var material = new THREE.SpriteMaterial( {
  322. map: texture,
  323. depthTest: false
  324. } );
  325. var sprite = new THREE.Sprite( material );
  326. sprite.position.set( geometry.point[ 0 ], geometry.point[ 1 ], geometry.point[ 2 ] );
  327. sprite.scale.set( width / 10, height / 10, 1.0 );
  328. sprite.userData[ 'attributes' ] = attributes;
  329. sprite.userData[ 'objectType' ] = obj.objectType;
  330. if ( attributes.name ) {
  331. sprite.name = attributes.name;
  332. }
  333. return sprite;
  334. case 'Light':
  335. geometry = obj.geometry;
  336. var light;
  337. if ( geometry.isDirectionalLight ) {
  338. light = new THREE.DirectionalLight();
  339. light.castShadow = attributes.castsShadows;
  340. light.position.set( geometry.location[ 0 ], geometry.location[ 1 ], geometry.location[ 2 ] );
  341. light.target.position.set( geometry.direction[ 0 ], geometry.direction[ 1 ], geometry.direction[ 2 ] );
  342. light.shadow.normalBias = 0.1;
  343. } else if ( geometry.isPointLight ) {
  344. light = new THREE.PointLight();
  345. light.castShadow = attributes.castsShadows;
  346. light.position.set( geometry.location[ 0 ], geometry.location[ 1 ], geometry.location[ 2 ] );
  347. light.shadow.normalBias = 0.1;
  348. } else if ( geometry.isRectangularLight ) {
  349. light = new THREE.RectAreaLight();
  350. var width = Math.abs( geometry.width[ 2 ] );
  351. var height = Math.abs( geometry.length[ 0 ] );
  352. light.position.set( geometry.location[ 0 ] - height / 2, geometry.location[ 1 ], geometry.location[ 2 ] - width / 2 );
  353. light.height = height;
  354. light.width = width;
  355. light.lookAt( new THREE.Vector3( geometry.direction[ 0 ], geometry.direction[ 1 ], geometry.direction[ 2 ] ) );
  356. } else if ( geometry.isSpotLight ) {
  357. light = new THREE.SpotLight();
  358. light.castShadow = attributes.castsShadows;
  359. light.position.set( geometry.location[ 0 ], geometry.location[ 1 ], geometry.location[ 2 ] );
  360. light.target.position.set( geometry.direction[ 0 ], geometry.direction[ 1 ], geometry.direction[ 2 ] );
  361. light.angle = geometry.spotAngleRadians;
  362. light.shadow.normalBias = 0.1;
  363. } else if ( geometry.isLinearLight ) {
  364. console.warn( 'THREE.3DMLoader: No conversion exists for linear lights.' );
  365. return;
  366. }
  367. if ( light ) {
  368. light.intensity = geometry.intensity;
  369. var _color = geometry.diffuse;
  370. var color = new THREE.Color( _color.r / 255.0, _color.g / 255.0, _color.b / 255.0 );
  371. light.color = color;
  372. light.userData[ 'attributes' ] = attributes;
  373. light.userData[ 'objectType' ] = obj.objectType;
  374. }
  375. return light;
  376. }
  377. },
  378. _initLibrary: function () {
  379. if ( ! this.libraryPending ) {
  380. // Load rhino3dm wrapper.
  381. var jsLoader = new THREE.FileLoader( this.manager );
  382. jsLoader.setPath( this.libraryPath );
  383. var jsContent = new Promise( ( resolve, reject ) => {
  384. jsLoader.load( 'rhino3dm.js', resolve, undefined, reject );
  385. } ); // Load rhino3dm WASM binary.
  386. var binaryLoader = new THREE.FileLoader( this.manager );
  387. binaryLoader.setPath( this.libraryPath );
  388. binaryLoader.setResponseType( 'arraybuffer' );
  389. var binaryContent = new Promise( ( resolve, reject ) => {
  390. binaryLoader.load( 'rhino3dm.wasm', resolve, undefined, reject );
  391. } );
  392. this.libraryPending = Promise.all( [ jsContent, binaryContent ] ).then( ( [ jsContent, binaryContent ] ) => {
  393. //this.libraryBinary = binaryContent;
  394. this.libraryConfig.wasmBinary = binaryContent;
  395. var fn = Rhino3dmLoader.Rhino3dmWorker.toString();
  396. var body = [ '/* rhino3dm.js */', jsContent, '/* worker */', fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) ) ].join( '\n' );
  397. this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) );
  398. } );
  399. }
  400. return this.libraryPending;
  401. },
  402. _getWorker: function ( taskCost ) {
  403. return this._initLibrary().then( () => {
  404. if ( this.workerPool.length < this.workerLimit ) {
  405. var worker = new Worker( this.workerSourceURL );
  406. worker._callbacks = {};
  407. worker._taskCosts = {};
  408. worker._taskLoad = 0;
  409. worker.postMessage( {
  410. type: 'init',
  411. libraryConfig: this.libraryConfig
  412. } );
  413. worker.onmessage = function ( e ) {
  414. var message = e.data;
  415. switch ( message.type ) {
  416. case 'decode':
  417. worker._callbacks[ message.id ].resolve( message );
  418. break;
  419. case 'error':
  420. worker._callbacks[ message.id ].reject( message );
  421. break;
  422. default:
  423. console.error( 'THREE.Rhino3dmLoader: Unexpected message, "' + message.type + '"' );
  424. }
  425. };
  426. this.workerPool.push( worker );
  427. } else {
  428. this.workerPool.sort( function ( a, b ) {
  429. return a._taskLoad > b._taskLoad ? - 1 : 1;
  430. } );
  431. }
  432. var worker = this.workerPool[ this.workerPool.length - 1 ];
  433. worker._taskLoad += taskCost;
  434. return worker;
  435. } );
  436. },
  437. _releaseTask: function ( worker, taskID ) {
  438. worker._taskLoad -= worker._taskCosts[ taskID ];
  439. delete worker._callbacks[ taskID ];
  440. delete worker._taskCosts[ taskID ];
  441. },
  442. dispose: function () {
  443. for ( var i = 0; i < this.workerPool.length; ++ i ) {
  444. this.workerPool[ i ].terminate();
  445. }
  446. this.workerPool.length = 0;
  447. return this;
  448. }
  449. } );
  450. /* WEB WORKER */
  451. Rhino3dmLoader.Rhino3dmWorker = function () {
  452. var libraryPending;
  453. var libraryConfig;
  454. var rhino;
  455. onmessage = function ( e ) {
  456. var message = e.data;
  457. switch ( message.type ) {
  458. case 'init':
  459. libraryConfig = message.libraryConfig;
  460. var wasmBinary = libraryConfig.wasmBinary;
  461. var RhinoModule;
  462. libraryPending = new Promise( function ( resolve ) {
  463. /* Like Basis THREE.Loader */
  464. RhinoModule = {
  465. wasmBinary,
  466. onRuntimeInitialized: resolve
  467. };
  468. rhino3dm( RhinoModule ); // eslint-disable-line no-undef
  469. } ).then( () => {
  470. rhino = RhinoModule;
  471. } );
  472. break;
  473. case 'decode':
  474. var buffer = message.buffer;
  475. libraryPending.then( () => {
  476. var data = decodeObjects( rhino, buffer );
  477. self.postMessage( {
  478. type: 'decode',
  479. id: message.id,
  480. data
  481. } );
  482. } );
  483. break;
  484. }
  485. };
  486. function decodeObjects( rhino, buffer ) {
  487. var arr = new Uint8Array( buffer );
  488. var doc = rhino.File3dm.fromByteArray( arr );
  489. var objects = [];
  490. var materials = [];
  491. var layers = [];
  492. var views = [];
  493. var namedViews = [];
  494. var groups = []; //Handle objects
  495. var objs = doc.objects();
  496. var cnt = objs.count;
  497. for ( var i = 0; i < cnt; i ++ ) {
  498. var _object = objs.get( i );
  499. var object = extractObjectData( _object, doc );
  500. _object.delete();
  501. if ( object ) {
  502. objects.push( object );
  503. }
  504. } // Handle instance definitions
  505. // console.log( `Instance Definitions Count: ${doc.instanceDefinitions().count()}` );
  506. for ( var i = 0; i < doc.instanceDefinitions().count(); i ++ ) {
  507. var idef = doc.instanceDefinitions().get( i );
  508. var idefAttributes = extractProperties( idef );
  509. idefAttributes.objectIds = idef.getObjectIds();
  510. objects.push( {
  511. geometry: null,
  512. attributes: idefAttributes,
  513. objectType: 'InstanceDefinition'
  514. } );
  515. } // Handle materials
  516. var textureTypes = [// rhino.TextureType.Bitmap,
  517. rhino.TextureType.Diffuse, rhino.TextureType.Bump, rhino.TextureType.Transparency, rhino.TextureType.Opacity, rhino.TextureType.Emap ];
  518. var pbrTextureTypes = [ rhino.TextureType.PBR_BaseColor, rhino.TextureType.PBR_Subsurface, rhino.TextureType.PBR_SubsurfaceScattering, rhino.TextureType.PBR_SubsurfaceScatteringRadius, rhino.TextureType.PBR_Metallic, rhino.TextureType.PBR_Specular, rhino.TextureType.PBR_SpecularTint, rhino.TextureType.PBR_Roughness, rhino.TextureType.PBR_Anisotropic, rhino.TextureType.PBR_Anisotropic_Rotation, rhino.TextureType.PBR_Sheen, rhino.TextureType.PBR_SheenTint, rhino.TextureType.PBR_Clearcoat, rhino.TextureType.PBR_ClearcoatBump, rhino.TextureType.PBR_ClearcoatRoughness, rhino.TextureType.PBR_OpacityIor, rhino.TextureType.PBR_OpacityRoughness, rhino.TextureType.PBR_Emission, rhino.TextureType.PBR_AmbientOcclusion, rhino.TextureType.PBR_Displacement ];
  519. for ( var i = 0; i < doc.materials().count(); i ++ ) {
  520. var _material = doc.materials().get( i );
  521. var _pbrMaterial = _material.physicallyBased();
  522. var material = extractProperties( _material );
  523. var textures = [];
  524. for ( var j = 0; j < textureTypes.length; j ++ ) {
  525. var _texture = _material.getTexture( textureTypes[ j ] );
  526. if ( _texture ) {
  527. var textureType = textureTypes[ j ].constructor.name;
  528. textureType = textureType.substring( 12, textureType.length );
  529. var texture = {
  530. type: textureType
  531. };
  532. var image = doc.getEmbeddedFileAsBase64( _texture.fileName );
  533. if ( image ) {
  534. texture.image = 'data:image/png;base64,' + image;
  535. } else {
  536. console.warn( `THREE.3DMLoader: Image for ${textureType} texture not embedded in file.` );
  537. texture.image = null;
  538. }
  539. textures.push( texture );
  540. _texture.delete();
  541. }
  542. }
  543. material.textures = textures;
  544. if ( _pbrMaterial.supported ) {
  545. console.log( 'pbr true' );
  546. for ( var j = 0; j < pbrTextureTypes.length; j ++ ) {
  547. var _texture = _material.getTexture( textureTypes[ j ] );
  548. if ( _texture ) {
  549. var image = doc.getEmbeddedFileAsBase64( _texture.fileName );
  550. var textureType = textureTypes[ j ].constructor.name;
  551. textureType = textureType.substring( 12, textureType.length );
  552. var texture = {
  553. type: textureType,
  554. image: 'data:image/png;base64,' + image
  555. };
  556. textures.push( texture );
  557. _texture.delete();
  558. }
  559. }
  560. var pbMaterialProperties = extractProperties( _material.physicallyBased() );
  561. material = Object.assign( pbMaterialProperties, material );
  562. }
  563. materials.push( material );
  564. _material.delete();
  565. _pbrMaterial.delete();
  566. } // Handle layers
  567. for ( var i = 0; i < doc.layers().count(); i ++ ) {
  568. var _layer = doc.layers().get( i );
  569. var layer = extractProperties( _layer );
  570. layers.push( layer );
  571. _layer.delete();
  572. } // Handle views
  573. for ( var i = 0; i < doc.views().count(); i ++ ) {
  574. var _view = doc.views().get( i );
  575. var view = extractProperties( _view );
  576. views.push( view );
  577. _view.delete();
  578. } // Handle named views
  579. for ( var i = 0; i < doc.namedViews().count(); i ++ ) {
  580. var _namedView = doc.namedViews().get( i );
  581. var namedView = extractProperties( _namedView );
  582. namedViews.push( namedView );
  583. _namedView.delete();
  584. } // Handle groups
  585. for ( var i = 0; i < doc.groups().count(); i ++ ) {
  586. var _group = doc.groups().get( i );
  587. var group = extractProperties( _group );
  588. groups.push( group );
  589. _group.delete();
  590. } // Handle settings
  591. var settings = extractProperties( doc.settings() ); //TODO: Handle other document stuff like dimstyles, instance definitions, bitmaps etc.
  592. // Handle dimstyles
  593. // console.log( `Dimstyle Count: ${doc.dimstyles().count()}` );
  594. // Handle bitmaps
  595. // console.log( `Bitmap Count: ${doc.bitmaps().count()}` );
  596. // Handle strings -- this seems to be broken at the moment in rhino3dm
  597. // console.log( `Document Strings Count: ${doc.strings().count()}` );
  598. /*
  599. for( var i = 0; i < doc.strings().count(); i++ ){
  600. var _string= doc.strings().get( i );
  601. console.log(_string);
  602. var string = extractProperties( _group );
  603. strings.push( string );
  604. _string.delete();
  605. }
  606. */
  607. doc.delete();
  608. return {
  609. objects,
  610. materials,
  611. layers,
  612. views,
  613. namedViews,
  614. groups,
  615. settings
  616. };
  617. }
  618. function extractObjectData( object, doc ) {
  619. var _geometry = object.geometry();
  620. var _attributes = object.attributes();
  621. var objectType = _geometry.objectType;
  622. var geometry = null;
  623. var attributes = null; // skip instance definition objects
  624. //if( _attributes.isInstanceDefinitionObject ) { continue; }
  625. // TODO: handle other geometry types
  626. switch ( objectType ) {
  627. case rhino.ObjectType.Curve:
  628. var pts = curveToPoints( _geometry, 100 );
  629. var position = {};
  630. var attributes = {};
  631. var data = {};
  632. position.itemSize = 3;
  633. position.type = 'Float32Array';
  634. position.array = [];
  635. for ( var j = 0; j < pts.length; j ++ ) {
  636. position.array.push( pts[ j ][ 0 ] );
  637. position.array.push( pts[ j ][ 1 ] );
  638. position.array.push( pts[ j ][ 2 ] );
  639. }
  640. attributes.position = position;
  641. data.attributes = attributes;
  642. geometry = {
  643. data
  644. };
  645. break;
  646. case rhino.ObjectType.Point:
  647. var pt = _geometry.location;
  648. var position = {};
  649. var color = {};
  650. var attributes = {};
  651. var data = {};
  652. position.itemSize = 3;
  653. position.type = 'Float32Array';
  654. position.array = [ pt[ 0 ], pt[ 1 ], pt[ 2 ] ];
  655. var _color = _attributes.drawColor( doc );
  656. color.itemSize = 3;
  657. color.type = 'Float32Array';
  658. color.array = [ _color.r / 255.0, _color.g / 255.0, _color.b / 255.0 ];
  659. attributes.position = position;
  660. attributes.color = color;
  661. data.attributes = attributes;
  662. geometry = {
  663. data
  664. };
  665. break;
  666. case rhino.ObjectType.PointSet:
  667. case rhino.ObjectType.Mesh:
  668. geometry = _geometry.toThreejsJSON();
  669. break;
  670. case rhino.ObjectType.Brep:
  671. var faces = _geometry.faces();
  672. var mesh = new rhino.Mesh();
  673. for ( var faceIndex = 0; faceIndex < faces.count; faceIndex ++ ) {
  674. var face = faces.get( faceIndex );
  675. var _mesh = face.getMesh( rhino.MeshType.Any );
  676. if ( _mesh ) {
  677. mesh.append( _mesh );
  678. _mesh.delete();
  679. }
  680. face.delete();
  681. }
  682. if ( mesh.faces().count > 0 ) {
  683. mesh.compact();
  684. geometry = mesh.toThreejsJSON();
  685. faces.delete();
  686. }
  687. mesh.delete();
  688. break;
  689. case rhino.ObjectType.Extrusion:
  690. var mesh = _geometry.getMesh( rhino.MeshType.Any );
  691. if ( mesh ) {
  692. geometry = mesh.toThreejsJSON();
  693. mesh.delete();
  694. }
  695. break;
  696. case rhino.ObjectType.TextDot:
  697. geometry = extractProperties( _geometry );
  698. break;
  699. case rhino.ObjectType.Light:
  700. geometry = extractProperties( _geometry );
  701. break;
  702. case rhino.ObjectType.InstanceReference:
  703. geometry = extractProperties( _geometry );
  704. geometry.xform = extractProperties( _geometry.xform );
  705. geometry.xform.array = _geometry.xform.toFloatArray( true );
  706. break;
  707. case rhino.ObjectType.SubD:
  708. // TODO: precalculate resulting vertices and faces and warn on excessive results
  709. _geometry.subdivide( 3 );
  710. var mesh = rhino.Mesh.createFromSubDControlNet( _geometry );
  711. if ( mesh ) {
  712. geometry = mesh.toThreejsJSON();
  713. mesh.delete();
  714. }
  715. break;
  716. /*
  717. case rhino.ObjectType.Annotation:
  718. case rhino.ObjectType.Hatch:
  719. case rhino.ObjectType.ClipPlane:
  720. */
  721. default:
  722. console.warn( `THREE.3DMLoader: TODO: Implement ${objectType.constructor.name}` );
  723. break;
  724. }
  725. if ( geometry ) {
  726. var attributes = extractProperties( _attributes );
  727. attributes.geometry = extractProperties( _geometry );
  728. if ( _attributes.groupCount > 0 ) {
  729. attributes.groupIds = _attributes.getGroupList();
  730. }
  731. if ( _attributes.userStringCount > 0 ) {
  732. attributes.userStrings = _attributes.getUserStrings();
  733. }
  734. if ( _geometry.userStringCount > 0 ) {
  735. attributes.geometry.userStrings = _geometry.getUserStrings();
  736. }
  737. attributes.drawColor = _attributes.drawColor( doc );
  738. objectType = objectType.constructor.name;
  739. objectType = objectType.substring( 11, objectType.length );
  740. return {
  741. geometry,
  742. attributes,
  743. objectType
  744. };
  745. } else {
  746. console.warn( `THREE.3DMLoader: ${objectType.constructor.name} has no associated mesh geometry.` );
  747. }
  748. }
  749. function extractProperties( object ) {
  750. var result = {};
  751. for ( var property in object ) {
  752. var value = object[ property ];
  753. if ( typeof value !== 'function' ) {
  754. if ( typeof value === 'object' && value !== null && value.hasOwnProperty( 'constructor' ) ) {
  755. result[ property ] = {
  756. name: value.constructor.name,
  757. value: value.value
  758. };
  759. } else {
  760. result[ property ] = value;
  761. }
  762. } else { // these are functions that could be called to extract more data.
  763. //console.log( `${property}: ${object[ property ].constructor.name}` );
  764. }
  765. }
  766. return result;
  767. }
  768. function curveToPoints( curve, pointLimit ) {
  769. var pointCount = pointLimit;
  770. var rc = [];
  771. var ts = [];
  772. if ( curve instanceof rhino.LineCurve ) {
  773. return [ curve.pointAtStart, curve.pointAtEnd ];
  774. }
  775. if ( curve instanceof rhino.PolylineCurve ) {
  776. pointCount = curve.pointCount;
  777. for ( var i = 0; i < pointCount; i ++ ) {
  778. rc.push( curve.point( i ) );
  779. }
  780. return rc;
  781. }
  782. if ( curve instanceof rhino.PolyCurve ) {
  783. var segmentCount = curve.segmentCount;
  784. for ( var i = 0; i < segmentCount; i ++ ) {
  785. var segment = curve.segmentCurve( i );
  786. var segmentArray = curveToPoints( segment, pointCount );
  787. rc = rc.concat( segmentArray );
  788. segment.delete();
  789. }
  790. return rc;
  791. }
  792. if ( curve instanceof rhino.ArcCurve ) {
  793. pointCount = Math.floor( curve.angleDegrees / 5 );
  794. pointCount = pointCount < 2 ? 2 : pointCount; // alternative to this hardcoded version: https://stackoverflow.com/a/18499923/2179399
  795. }
  796. if ( curve instanceof rhino.NurbsCurve && curve.degree === 1 ) {
  797. const pLine = curve.tryGetPolyline();
  798. for ( var i = 0; i < pLine.count; i ++ ) {
  799. rc.push( pLine.get( i ) );
  800. }
  801. pLine.delete();
  802. return rc;
  803. }
  804. var domain = curve.domain;
  805. var divisions = pointCount - 1.0;
  806. for ( var j = 0; j < pointCount; j ++ ) {
  807. var t = domain[ 0 ] + j / divisions * ( domain[ 1 ] - domain[ 0 ] );
  808. if ( t === domain[ 0 ] || t === domain[ 1 ] ) {
  809. ts.push( t );
  810. continue;
  811. }
  812. var tan = curve.tangentAt( t );
  813. var prevTan = curve.tangentAt( ts.slice( - 1 )[ 0 ] ); // Duplicated from THREE.Vector3
  814. // How to pass imports to worker?
  815. var tS = tan[ 0 ] * tan[ 0 ] + tan[ 1 ] * tan[ 1 ] + tan[ 2 ] * tan[ 2 ];
  816. var ptS = prevTan[ 0 ] * prevTan[ 0 ] + prevTan[ 1 ] * prevTan[ 1 ] + prevTan[ 2 ] * prevTan[ 2 ];
  817. var denominator = Math.sqrt( tS * ptS );
  818. var angle;
  819. if ( denominator === 0 ) {
  820. angle = Math.PI / 2;
  821. } else {
  822. var theta = ( tan.x * prevTan.x + tan.y * prevTan.y + tan.z * prevTan.z ) / denominator;
  823. angle = Math.acos( Math.max( - 1, Math.min( 1, theta ) ) );
  824. }
  825. if ( angle < 0.1 ) continue;
  826. ts.push( t );
  827. }
  828. rc = ts.map( t => curve.pointAt( t ) );
  829. return rc;
  830. }
  831. };
  832. THREE.Rhino3dmLoader = Rhino3dmLoader;
  833. } )();