3MFLoader.js 27 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265
  1. /**
  2. * @author technohippy / https://github.com/technohippy
  3. * @author Mugen87 / https://github.com/Mugen87
  4. *
  5. * 3D Manufacturing Format (3MF) specification: https://3mf.io/specification/
  6. *
  7. * The following features from the core specification are supported:
  8. *
  9. * - 3D Models
  10. * - Object Resources (Meshes and Components)
  11. * - Material Resources (Base Materials)
  12. *
  13. * 3MF Materials and Properties Extension are only partially supported.
  14. *
  15. * - Texture 2D
  16. * - Texture 2D Groups
  17. */
  18. import {
  19. BufferAttribute,
  20. BufferGeometry,
  21. ClampToEdgeWrapping,
  22. FileLoader,
  23. Float32BufferAttribute,
  24. Group,
  25. LinearFilter,
  26. LinearMipmapLinearFilter,
  27. Loader,
  28. LoaderUtils,
  29. Matrix4,
  30. Mesh,
  31. MeshPhongMaterial,
  32. MirroredRepeatWrapping,
  33. NearestFilter,
  34. RepeatWrapping,
  35. TextureLoader,
  36. sRGBEncoding
  37. } from "../../../build/three.module.js";
  38. var ThreeMFLoader = function ( manager ) {
  39. Loader.call( this, manager );
  40. this.availableExtensions = [];
  41. };
  42. ThreeMFLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
  43. constructor: ThreeMFLoader,
  44. load: function ( url, onLoad, onProgress, onError ) {
  45. var scope = this;
  46. var loader = new FileLoader( scope.manager );
  47. loader.setPath( scope.path );
  48. loader.setResponseType( 'arraybuffer' );
  49. loader.load( url, function ( buffer ) {
  50. onLoad( scope.parse( buffer ) );
  51. }, onProgress, onError );
  52. },
  53. parse: function ( data ) {
  54. var scope = this;
  55. var textureLoader = new TextureLoader( this.manager );
  56. var textureMap = {};
  57. function loadDocument( data ) {
  58. var zip = null;
  59. var file = null;
  60. var relsName;
  61. var modelRelsName;
  62. var modelPartNames = [];
  63. var printTicketPartNames = [];
  64. var texturesPartNames = [];
  65. var otherPartNames = [];
  66. var rels;
  67. var modelRels;
  68. var modelParts = {};
  69. var printTicketParts = {};
  70. var texturesParts = {};
  71. var otherParts = {};
  72. try {
  73. zip = new JSZip( data ); // eslint-disable-line no-undef
  74. } catch ( e ) {
  75. if ( e instanceof ReferenceError ) {
  76. console.error( 'THREE.3MFLoader: jszip missing and file is compressed.' );
  77. return null;
  78. }
  79. }
  80. for ( file in zip.files ) {
  81. if ( file.match( /\_rels\/.rels$/ ) ) {
  82. relsName = file;
  83. } else if ( file.match( /3D\/_rels\/.*\.model\.rels$/ ) ) {
  84. modelRelsName = file;
  85. } else if ( file.match( /^3D\/.*\.model$/ ) ) {
  86. modelPartNames.push( file );
  87. } else if ( file.match( /^3D\/Metadata\/.*\.xml$/ ) ) {
  88. printTicketPartNames.push( file );
  89. } else if ( file.match( /^3D\/Textures?\/.*/ ) ) {
  90. texturesPartNames.push( file );
  91. } else if ( file.match( /^3D\/Other\/.*/ ) ) {
  92. otherPartNames.push( file );
  93. }
  94. }
  95. //
  96. var relsView = new Uint8Array( zip.file( relsName ).asArrayBuffer() );
  97. var relsFileText = LoaderUtils.decodeText( relsView );
  98. rels = parseRelsXml( relsFileText );
  99. //
  100. if ( modelRelsName ) {
  101. var relsView = new Uint8Array( zip.file( modelRelsName ).asArrayBuffer() );
  102. var relsFileText = LoaderUtils.decodeText( relsView );
  103. modelRels = parseRelsXml( relsFileText );
  104. }
  105. //
  106. for ( var i = 0; i < modelPartNames.length; i ++ ) {
  107. var modelPart = modelPartNames[ i ];
  108. var view = new Uint8Array( zip.file( modelPart ).asArrayBuffer() );
  109. var fileText = LoaderUtils.decodeText( view );
  110. var xmlData = new DOMParser().parseFromString( fileText, 'application/xml' );
  111. if ( xmlData.documentElement.nodeName.toLowerCase() !== 'model' ) {
  112. console.error( 'THREE.3MFLoader: Error loading 3MF - no 3MF document found: ', modelPart );
  113. }
  114. var modelNode = xmlData.querySelector( 'model' );
  115. var extensions = {};
  116. for ( var i = 0; i < modelNode.attributes.length; i ++ ) {
  117. var attr = modelNode.attributes[ i ];
  118. if ( attr.name.match( /^xmlns:(.+)$/ ) ) {
  119. extensions[ attr.value ] = RegExp.$1;
  120. }
  121. }
  122. var modelData = parseModelNode( modelNode );
  123. modelData[ 'xml' ] = modelNode;
  124. if ( 0 < Object.keys( extensions ).length ) {
  125. modelData[ 'extensions' ] = extensions;
  126. }
  127. modelParts[ modelPart ] = modelData;
  128. }
  129. //
  130. for ( var i = 0; i < texturesPartNames.length; i ++ ) {
  131. var texturesPartName = texturesPartNames[ i ];
  132. texturesParts[ texturesPartName ] = zip.file( texturesPartName ).asArrayBuffer();
  133. }
  134. return {
  135. rels: rels,
  136. modelRels: modelRels,
  137. model: modelParts,
  138. printTicket: printTicketParts,
  139. texture: texturesParts,
  140. other: otherParts
  141. };
  142. }
  143. function parseRelsXml( relsFileText ) {
  144. var relationships = [];
  145. var relsXmlData = new DOMParser().parseFromString( relsFileText, 'application/xml' );
  146. var relsNodes = relsXmlData.querySelectorAll( 'Relationship' );
  147. for ( var i = 0; i < relsNodes.length; i ++ ) {
  148. var relsNode = relsNodes[ i ];
  149. var relationship = {
  150. target: relsNode.getAttribute( 'Target' ), //required
  151. id: relsNode.getAttribute( 'Id' ), //required
  152. type: relsNode.getAttribute( 'Type' ) //required
  153. };
  154. relationships.push( relationship );
  155. }
  156. return relationships;
  157. }
  158. function parseMetadataNodes( metadataNodes ) {
  159. var metadataData = {};
  160. for ( var i = 0; i < metadataNodes.length; i ++ ) {
  161. var metadataNode = metadataNodes[ i ];
  162. var name = metadataNode.getAttribute( 'name' );
  163. var validNames = [
  164. 'Title',
  165. 'Designer',
  166. 'Description',
  167. 'Copyright',
  168. 'LicenseTerms',
  169. 'Rating',
  170. 'CreationDate',
  171. 'ModificationDate'
  172. ];
  173. if ( 0 <= validNames.indexOf( name ) ) {
  174. metadataData[ name ] = metadataNode.textContent;
  175. }
  176. }
  177. return metadataData;
  178. }
  179. function parseBasematerialsNode( basematerialsNode ) {
  180. var basematerialsData = {
  181. id: basematerialsNode.getAttribute( 'id' ), // required
  182. basematerials: []
  183. };
  184. var basematerialNodes = basematerialsNode.querySelectorAll( 'base' );
  185. for ( var i = 0; i < basematerialNodes.length; i ++ ) {
  186. var basematerialNode = basematerialNodes[ i ];
  187. var basematerialData = parseBasematerialNode( basematerialNode );
  188. basematerialsData.basematerials.push( basematerialData );
  189. }
  190. return basematerialsData;
  191. }
  192. function parseTexture2DNode( texture2DNode ) {
  193. var texture2dData = {
  194. id: texture2DNode.getAttribute( 'id' ), // required
  195. path: texture2DNode.getAttribute( 'path' ), // required
  196. contenttype: texture2DNode.getAttribute( 'contenttype' ), // required
  197. tilestyleu: texture2DNode.getAttribute( 'tilestyleu' ),
  198. tilestylev: texture2DNode.getAttribute( 'tilestylev' ),
  199. filter: texture2DNode.getAttribute( 'filter' ),
  200. };
  201. return texture2dData;
  202. }
  203. function parseTextures2DGroupNodes( texture2DGroupNode ) {
  204. var texture2DGroupData = {
  205. id: texture2DGroupNode.getAttribute( 'id' ), // required
  206. texid: texture2DGroupNode.getAttribute( 'texid' ), // required
  207. displaypropertiesid: texture2DGroupNode.getAttribute( 'displaypropertiesid' )
  208. };
  209. var tex2coordNodes = texture2DGroupNode.querySelectorAll( 'tex2coord' );
  210. var uvs = [];
  211. for ( var i = 0; i < tex2coordNodes.length; i ++ ) {
  212. var tex2coordNode = tex2coordNodes[ i ];
  213. var u = tex2coordNode.getAttribute( 'u' );
  214. var v = tex2coordNode.getAttribute( 'v' );
  215. uvs.push( parseFloat( u ), parseFloat( v ) );
  216. }
  217. texture2DGroupData[ 'uvs' ] = new Float32Array( uvs );
  218. return texture2DGroupData;
  219. }
  220. function parseBasematerialNode( basematerialNode ) {
  221. var basematerialData = {};
  222. basematerialData[ 'name' ] = basematerialNode.getAttribute( 'name' ); // required
  223. basematerialData[ 'displaycolor' ] = basematerialNode.getAttribute( 'displaycolor' ); // required
  224. return basematerialData;
  225. }
  226. function parseMeshNode( meshNode ) {
  227. var meshData = {};
  228. var vertices = [];
  229. var vertexNodes = meshNode.querySelectorAll( 'vertices vertex' );
  230. for ( var i = 0; i < vertexNodes.length; i ++ ) {
  231. var vertexNode = vertexNodes[ i ];
  232. var x = vertexNode.getAttribute( 'x' );
  233. var y = vertexNode.getAttribute( 'y' );
  234. var z = vertexNode.getAttribute( 'z' );
  235. vertices.push( parseFloat( x ), parseFloat( y ), parseFloat( z ) );
  236. }
  237. meshData[ 'vertices' ] = new Float32Array( vertices );
  238. var triangleProperties = [];
  239. var triangles = [];
  240. var triangleNodes = meshNode.querySelectorAll( 'triangles triangle' );
  241. for ( var i = 0; i < triangleNodes.length; i ++ ) {
  242. var triangleNode = triangleNodes[ i ];
  243. var v1 = triangleNode.getAttribute( 'v1' );
  244. var v2 = triangleNode.getAttribute( 'v2' );
  245. var v3 = triangleNode.getAttribute( 'v3' );
  246. var p1 = triangleNode.getAttribute( 'p1' );
  247. var p2 = triangleNode.getAttribute( 'p2' );
  248. var p3 = triangleNode.getAttribute( 'p3' );
  249. var pid = triangleNode.getAttribute( 'pid' );
  250. triangles.push( parseInt( v1, 10 ), parseInt( v2, 10 ), parseInt( v3, 10 ) );
  251. var triangleProperty = {};
  252. if ( p1 ) {
  253. triangleProperty[ 'p1' ] = parseInt( p1, 10 );
  254. }
  255. if ( p2 ) {
  256. triangleProperty[ 'p2' ] = parseInt( p2, 10 );
  257. }
  258. if ( p3 ) {
  259. triangleProperty[ 'p3' ] = parseInt( p3, 10 );
  260. }
  261. if ( pid ) {
  262. triangleProperty[ 'pid' ] = pid;
  263. }
  264. if ( 0 < Object.keys( triangleProperty ).length ) {
  265. triangleProperties.push( triangleProperty );
  266. }
  267. }
  268. meshData[ 'triangleProperties' ] = triangleProperties;
  269. meshData[ 'triangles' ] = new Uint32Array( triangles );
  270. return meshData;
  271. }
  272. function parseComponentsNode( componentsNode ) {
  273. var components = [];
  274. var componentNodes = componentsNode.querySelectorAll( 'component' );
  275. for ( var i = 0; i < componentNodes.length; i ++ ) {
  276. var componentNode = componentNodes[ i ];
  277. var componentData = parseComponentNode( componentNode );
  278. components.push( componentData );
  279. }
  280. return components;
  281. }
  282. function parseComponentNode( componentNode ) {
  283. var componentData = {};
  284. componentData[ 'objectId' ] = componentNode.getAttribute( 'objectid' ); // required
  285. var transform = componentNode.getAttribute( 'transform' );
  286. if ( transform ) {
  287. componentData[ 'transform' ] = parseTransform( transform );
  288. }
  289. return componentData;
  290. }
  291. function parseTransform( transform ) {
  292. var t = [];
  293. transform.split( ' ' ).forEach( function ( s ) {
  294. t.push( parseFloat( s ) );
  295. } );
  296. var matrix = new Matrix4();
  297. matrix.set(
  298. t[ 0 ], t[ 3 ], t[ 6 ], t[ 9 ],
  299. t[ 1 ], t[ 4 ], t[ 7 ], t[ 10 ],
  300. t[ 2 ], t[ 5 ], t[ 8 ], t[ 11 ],
  301. 0.0, 0.0, 0.0, 1.0
  302. );
  303. return matrix;
  304. }
  305. function parseObjectNode( objectNode ) {
  306. var objectData = {
  307. type: objectNode.getAttribute( 'type' )
  308. };
  309. var id = objectNode.getAttribute( 'id' );
  310. if ( id ) {
  311. objectData[ 'id' ] = id;
  312. }
  313. var pid = objectNode.getAttribute( 'pid' );
  314. if ( pid ) {
  315. objectData[ 'pid' ] = pid;
  316. }
  317. var pindex = objectNode.getAttribute( 'pindex' );
  318. if ( pindex ) {
  319. objectData[ 'pindex' ] = pindex;
  320. }
  321. var thumbnail = objectNode.getAttribute( 'thumbnail' );
  322. if ( thumbnail ) {
  323. objectData[ 'thumbnail' ] = thumbnail;
  324. }
  325. var partnumber = objectNode.getAttribute( 'partnumber' );
  326. if ( partnumber ) {
  327. objectData[ 'partnumber' ] = partnumber;
  328. }
  329. var name = objectNode.getAttribute( 'name' );
  330. if ( name ) {
  331. objectData[ 'name' ] = name;
  332. }
  333. var meshNode = objectNode.querySelector( 'mesh' );
  334. if ( meshNode ) {
  335. objectData[ 'mesh' ] = parseMeshNode( meshNode );
  336. }
  337. var componentsNode = objectNode.querySelector( 'components' );
  338. if ( componentsNode ) {
  339. objectData[ 'components' ] = parseComponentsNode( componentsNode );
  340. }
  341. return objectData;
  342. }
  343. function parseResourcesNode( resourcesNode ) {
  344. var resourcesData = {};
  345. var basematerialsNode = resourcesNode.querySelector( 'basematerials' );
  346. if ( basematerialsNode ) {
  347. resourcesData[ 'basematerials' ] = parseBasematerialsNode( basematerialsNode );
  348. }
  349. //
  350. resourcesData[ 'texture2d' ] = {};
  351. var textures2DNodes = resourcesNode.querySelectorAll( 'texture2d' );
  352. for ( var i = 0; i < textures2DNodes.length; i ++ ) {
  353. var textures2DNode = textures2DNodes[ i ];
  354. var texture2DData = parseTexture2DNode( textures2DNode );
  355. resourcesData[ 'texture2d' ][ texture2DData[ 'id' ] ] = texture2DData;
  356. }
  357. //
  358. resourcesData[ 'texture2dgroup' ] = {};
  359. var textures2DGroupNodes = resourcesNode.querySelectorAll( 'texture2dgroup' );
  360. for ( var i = 0; i < textures2DGroupNodes.length; i ++ ) {
  361. var textures2DGroupNode = textures2DGroupNodes[ i ];
  362. var textures2DGroupData = parseTextures2DGroupNodes( textures2DGroupNode );
  363. resourcesData[ 'texture2dgroup' ][ textures2DGroupData[ 'id' ] ] = textures2DGroupData;
  364. }
  365. //
  366. resourcesData[ 'object' ] = {};
  367. var objectNodes = resourcesNode.querySelectorAll( 'object' );
  368. for ( var i = 0; i < objectNodes.length; i ++ ) {
  369. var objectNode = objectNodes[ i ];
  370. var objectData = parseObjectNode( objectNode );
  371. resourcesData[ 'object' ][ objectData[ 'id' ] ] = objectData;
  372. }
  373. return resourcesData;
  374. }
  375. function parseBuildNode( buildNode ) {
  376. var buildData = [];
  377. var itemNodes = buildNode.querySelectorAll( 'item' );
  378. for ( var i = 0; i < itemNodes.length; i ++ ) {
  379. var itemNode = itemNodes[ i ];
  380. var buildItem = {
  381. objectId: itemNode.getAttribute( 'objectid' )
  382. };
  383. var transform = itemNode.getAttribute( 'transform' );
  384. if ( transform ) {
  385. buildItem[ 'transform' ] = parseTransform( transform );
  386. }
  387. buildData.push( buildItem );
  388. }
  389. return buildData;
  390. }
  391. function parseModelNode( modelNode ) {
  392. var modelData = { unit: modelNode.getAttribute( 'unit' ) || 'millimeter' };
  393. var metadataNodes = modelNode.querySelectorAll( 'metadata' );
  394. if ( metadataNodes ) {
  395. modelData[ 'metadata' ] = parseMetadataNodes( metadataNodes );
  396. }
  397. var resourcesNode = modelNode.querySelector( 'resources' );
  398. if ( resourcesNode ) {
  399. modelData[ 'resources' ] = parseResourcesNode( resourcesNode );
  400. }
  401. var buildNode = modelNode.querySelector( 'build' );
  402. if ( buildNode ) {
  403. modelData[ 'build' ] = parseBuildNode( buildNode );
  404. }
  405. return modelData;
  406. }
  407. function buildGroups( geometry, modelData, meshData ) {
  408. var basematerialsData = modelData[ 'resources' ][ 'basematerials' ];
  409. var triangleProperties = meshData[ 'triangleProperties' ];
  410. var start = 0;
  411. var count = 0;
  412. var currentMaterialIndex = - 1;
  413. for ( var i = 0, l = triangleProperties.length; i < l; i ++ ) {
  414. var triangleProperty = triangleProperties[ i ];
  415. var pid = triangleProperty.pid;
  416. // only proceed if the triangle refers to a basematerials definition
  417. if ( basematerialsData && ( basematerialsData.id === pid ) ) {
  418. if ( currentMaterialIndex === - 1 ) currentMaterialIndex = triangleProperty.p1;
  419. if ( currentMaterialIndex === triangleProperty.p1 ) {
  420. count += 3; // primitives per triangle
  421. } else {
  422. geometry.addGroup( start, count, currentMaterialIndex );
  423. start += count;
  424. count = 3;
  425. currentMaterialIndex = triangleProperty.p1;
  426. }
  427. }
  428. }
  429. if ( geometry.groups.length > 0 ) mergeGroups( geometry );
  430. }
  431. function buildGeometry( modelData, meshData, objectData ) {
  432. var geometry = new BufferGeometry();
  433. geometry.setIndex( new BufferAttribute( meshData[ 'triangles' ], 1 ) );
  434. geometry.setAttribute( 'position', new BufferAttribute( meshData[ 'vertices' ], 3 ) );
  435. //
  436. var texture2dgroups = modelData.resources.texture2dgroup;
  437. if ( texture2dgroups ) {
  438. var textureCoordinates = [];
  439. var triangleProperties = meshData[ 'triangleProperties' ];
  440. var texture2dgroupObjectLevel;
  441. // check reference to texture coordinates on object level
  442. var texid;
  443. var pid = objectData.pid;
  444. if ( pid && texture2dgroups[ pid ] ) texture2dgroupObjectLevel = texture2dgroups[ pid ];
  445. // process all triangles
  446. for ( var i = 0, l = triangleProperties.length; i < l; i ++ ) {
  447. var texture2dgroup = texture2dgroupObjectLevel;
  448. var triangleProperty = triangleProperties[ i ];
  449. pid = triangleProperty.pid;
  450. // overwrite existing resource reference if necessary
  451. if ( pid && texture2dgroups[ pid ] ) texture2dgroup = texture2dgroups[ pid ];
  452. if ( texture2dgroup ) {
  453. texid = texture2dgroup.texid; // the loader only supports a single texture for a single geometry right now (and not per face)
  454. var uvs = texture2dgroup.uvs;
  455. textureCoordinates.push( uvs[ ( triangleProperty.p1 * 2 ) + 0 ] );
  456. textureCoordinates.push( uvs[ ( triangleProperty.p1 * 2 ) + 1 ] );
  457. textureCoordinates.push( uvs[ ( triangleProperty.p2 * 2 ) + 0 ] );
  458. textureCoordinates.push( uvs[ ( triangleProperty.p2 * 2 ) + 1 ] );
  459. textureCoordinates.push( uvs[ ( triangleProperty.p3 * 2 ) + 0 ] );
  460. textureCoordinates.push( uvs[ ( triangleProperty.p3 * 2 ) + 1 ] );
  461. }
  462. }
  463. if ( textureCoordinates.length > 0 ) {
  464. // uvs are defined on face level so the same vertex can have multiple uv coordinates
  465. geometry = geometry.toNonIndexed();
  466. geometry.setAttribute( 'uv', new Float32BufferAttribute( textureCoordinates, 2 ) );
  467. geometry.__texid = texid; // save the relationship between texture coordinates and texture
  468. return geometry;
  469. }
  470. }
  471. return geometry;
  472. }
  473. function buildTexture( geometry, modelData, textureData ) {
  474. var texid = geometry.__texid;
  475. if ( texid !== undefined ) {
  476. delete geometry.__texid;
  477. if ( textureMap[ texid ] !== undefined ) {
  478. return textureMap[ texid ];
  479. } else {
  480. var texture2ds = modelData.resources.texture2d;
  481. var texture2d = texture2ds[ texid ];
  482. if ( texture2d ) {
  483. var data = textureData[ texture2d.path ];
  484. var type = texture2d.contenttype;
  485. var blob = new Blob( [ data ], { type: type } );
  486. var sourceURI = URL.createObjectURL( blob );
  487. var texture = textureLoader.load( sourceURI, function () {
  488. URL.revokeObjectURL( sourceURI );
  489. } );
  490. texture.encoding = sRGBEncoding;
  491. // texture parameters
  492. switch ( texture2d.tilestyleu ) {
  493. case 'wrap':
  494. texture.wrapS = RepeatWrapping;
  495. break;
  496. case 'mirror':
  497. texture.wrapS = MirroredRepeatWrapping;
  498. break;
  499. case 'none':
  500. case 'clamp':
  501. texture.wrapS = ClampToEdgeWrapping;
  502. break;
  503. default:
  504. texture.wrapS = RepeatWrapping;
  505. }
  506. switch ( texture2d.tilestylev ) {
  507. case 'wrap':
  508. texture.wrapT = RepeatWrapping;
  509. break;
  510. case 'mirror':
  511. texture.wrapT = MirroredRepeatWrapping;
  512. break;
  513. case 'none':
  514. case 'clamp':
  515. texture.wrapT = ClampToEdgeWrapping;
  516. break;
  517. default:
  518. texture.wrapT = RepeatWrapping;
  519. }
  520. switch ( texture2d.filter ) {
  521. case 'auto':
  522. texture.magFilter = LinearFilter;
  523. texture.minFilter = LinearMipmapLinearFilter;
  524. break;
  525. case 'linear':
  526. texture.magFilter = LinearFilter;
  527. texture.minFilter = LinearFilter;
  528. break;
  529. case 'nearest':
  530. texture.magFilter = NearestFilter;
  531. texture.minFilter = NearestFilter;
  532. break;
  533. default:
  534. texture.magFilter = LinearFilter;
  535. texture.minFilter = LinearMipmapLinearFilter;
  536. }
  537. textureMap[ texid ] = texture;
  538. return texture;
  539. }
  540. }
  541. }
  542. return null;
  543. }
  544. function buildMesh( meshData, objects, modelData, textureData, objectData ) {
  545. var geometry = buildGeometry( modelData, meshData, objectData );
  546. var texture = buildTexture( geometry, modelData, textureData );
  547. // groups
  548. buildGroups( geometry, modelData, meshData );
  549. // material
  550. var material = null;
  551. // add material if an object-level definition is present
  552. var basematerialsData = modelData[ 'resources' ][ 'basematerials' ];
  553. if ( basematerialsData && ( basematerialsData.id === objectData.pid ) ) {
  554. var materialIndex = objectData.pindex;
  555. var basematerialData = basematerialsData.basematerials[ materialIndex ];
  556. material = getBuild( basematerialData, objects, modelData, textureData, objectData, buildBasematerial );
  557. }
  558. // add/overwrite material if definitions on triangles are present
  559. if ( geometry.groups.length > 0 ) {
  560. var groups = geometry.groups;
  561. material = [];
  562. for ( var i = 0, l = groups.length; i < l; i ++ ) {
  563. var group = groups[ i ];
  564. var basematerialData = basematerialsData.basematerials[ group.materialIndex ];
  565. material.push( getBuild( basematerialData, objects, modelData, textureData, objectData, buildBasematerial ) );
  566. }
  567. }
  568. // default material
  569. if ( material === null ) {
  570. if ( texture === null ) {
  571. material = new MeshPhongMaterial( { color: 0xaaaaff, flatShading: true } );
  572. } else {
  573. material = new MeshPhongMaterial( { map: texture, flatShading: true } );
  574. }
  575. }
  576. return new Mesh( geometry, material );
  577. }
  578. function mergeGroups( geometry ) {
  579. // sort by material index
  580. var groups = geometry.groups.sort( function ( a, b ) {
  581. if ( a.materialIndex !== b.materialIndex ) return a.materialIndex - b.materialIndex;
  582. return a.start - b.start;
  583. } );
  584. // reorganize index buffer
  585. var index = geometry.index;
  586. var itemSize = index.itemSize;
  587. var srcArray = index.array;
  588. var targetOffset = 0;
  589. var targetArray = new srcArray.constructor( srcArray.length );
  590. for ( var i = 0; i < groups.length; i ++ ) {
  591. var group = groups[ i ];
  592. var groupLength = group.count * itemSize;
  593. var groupStart = group.start * itemSize;
  594. var sub = srcArray.subarray( groupStart, groupStart + groupLength );
  595. targetArray.set( sub, targetOffset );
  596. targetOffset += groupLength;
  597. }
  598. srcArray.set( targetArray );
  599. // update groups
  600. var start = 0;
  601. for ( i = 0; i < groups.length; i ++ ) {
  602. group = groups[ i ];
  603. group.start = start;
  604. start += group.count;
  605. }
  606. // merge groups
  607. var lastGroup = groups[ 0 ];
  608. geometry.groups = [ lastGroup ];
  609. for ( i = 1; i < groups.length; i ++ ) {
  610. group = groups[ i ];
  611. if ( lastGroup.materialIndex === group.materialIndex ) {
  612. lastGroup.count += group.count;
  613. } else {
  614. lastGroup = group;
  615. geometry.groups.push( lastGroup );
  616. }
  617. }
  618. }
  619. function applyExtensions( extensions, meshData, modelXml ) {
  620. if ( ! extensions ) {
  621. return;
  622. }
  623. var availableExtensions = [];
  624. var keys = Object.keys( extensions );
  625. for ( var i = 0; i < keys.length; i ++ ) {
  626. var ns = keys[ i ];
  627. for ( var j = 0; j < scope.availableExtensions.length; j ++ ) {
  628. var extension = scope.availableExtensions[ j ];
  629. if ( extension.ns === ns ) {
  630. availableExtensions.push( extension );
  631. }
  632. }
  633. }
  634. for ( var i = 0; i < availableExtensions.length; i ++ ) {
  635. var extension = availableExtensions[ i ];
  636. extension.apply( modelXml, extensions[ extension[ 'ns' ] ], meshData );
  637. }
  638. }
  639. function getBuild( data, objects, modelData, textureData, objectData, builder ) {
  640. if ( data.build !== undefined ) return data.build;
  641. data.build = builder( data, objects, modelData, textureData, objectData );
  642. return data.build;
  643. }
  644. function buildBasematerial( materialData ) {
  645. var material = new MeshPhongMaterial( { flatShading: true } );
  646. material.name = materialData.name;
  647. // displaycolor MUST be specified with a value of a 6 or 8 digit hexadecimal number, e.g. "#RRGGBB" or "#RRGGBBAA"
  648. var displaycolor = materialData.displaycolor;
  649. var color = displaycolor.substring( 0, 7 );
  650. material.color.setStyle( color );
  651. material.color.convertSRGBToLinear(); // displaycolor is in sRGB
  652. // process alpha if set
  653. if ( displaycolor.length === 9 ) {
  654. material.opacity = parseInt( displaycolor.charAt( 7 ) + displaycolor.charAt( 8 ), 16 ) / 255;
  655. }
  656. return material;
  657. }
  658. function buildComposite( compositeData, objects, modelData, textureData ) {
  659. var composite = new Group();
  660. for ( var j = 0; j < compositeData.length; j ++ ) {
  661. var component = compositeData[ j ];
  662. var build = objects[ component.objectId ];
  663. if ( build === undefined ) {
  664. buildObject( component.objectId, objects, modelData, textureData );
  665. build = objects[ component.objectId ];
  666. }
  667. var object3D = build.clone();
  668. // apply component transfrom
  669. var transform = component.transform;
  670. if ( transform ) {
  671. object3D.applyMatrix( transform );
  672. }
  673. composite.add( object3D );
  674. }
  675. return composite;
  676. }
  677. function buildObject( objectId, objects, modelData, textureData ) {
  678. var objectData = modelData[ 'resources' ][ 'object' ][ objectId ];
  679. if ( objectData[ 'mesh' ] ) {
  680. var meshData = objectData[ 'mesh' ];
  681. var extensions = modelData[ 'extensions' ];
  682. var modelXml = modelData[ 'xml' ];
  683. applyExtensions( extensions, meshData, modelXml );
  684. objects[ objectData.id ] = getBuild( meshData, objects, modelData, textureData, objectData, buildMesh );
  685. } else {
  686. var compositeData = objectData[ 'components' ];
  687. objects[ objectData.id ] = getBuild( compositeData, objects, modelData, textureData, objectData, buildComposite );
  688. }
  689. }
  690. function buildObjects( data3mf ) {
  691. var modelsData = data3mf.model;
  692. var modelRels = data3mf.modelRels;
  693. var objects = {};
  694. var modelsKeys = Object.keys( modelsData );
  695. var textureData = {};
  696. // evaluate model relationships to textures
  697. if ( modelRels ) {
  698. for ( var i = 0, l = modelRels.length; i < l; i ++ ) {
  699. var modelRel = modelRels[ i ];
  700. var textureKey = modelRel.target.substring( 1 );
  701. if ( data3mf.texture[ textureKey ] ) {
  702. textureData[ modelRel.target ] = data3mf.texture[ textureKey ];
  703. }
  704. }
  705. }
  706. // start build
  707. for ( var i = 0; i < modelsKeys.length; i ++ ) {
  708. var modelsKey = modelsKeys[ i ];
  709. var modelData = modelsData[ modelsKey ];
  710. var objectIds = Object.keys( modelData[ 'resources' ][ 'object' ] );
  711. for ( var j = 0; j < objectIds.length; j ++ ) {
  712. var objectId = objectIds[ j ];
  713. buildObject( objectId, objects, modelData, textureData );
  714. }
  715. }
  716. return objects;
  717. }
  718. function build( objects, data3mf ) {
  719. var group = new Group();
  720. var relationship = data3mf[ 'rels' ][ 0 ];
  721. var buildData = data3mf.model[ relationship[ 'target' ].substring( 1 ) ][ 'build' ];
  722. for ( var i = 0; i < buildData.length; i ++ ) {
  723. var buildItem = buildData[ i ];
  724. var object3D = objects[ buildItem[ 'objectId' ] ];
  725. // apply transform
  726. var transform = buildItem[ 'transform' ];
  727. if ( transform ) {
  728. object3D.applyMatrix( transform );
  729. }
  730. group.add( object3D );
  731. }
  732. return group;
  733. }
  734. var data3mf = loadDocument( data );
  735. var objects = buildObjects( data3mf );
  736. return build( objects, data3mf );
  737. },
  738. addExtension: function ( extension ) {
  739. this.availableExtensions.push( extension );
  740. }
  741. } );
  742. export { ThreeMFLoader };