AMFLoader.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. /*
  2. * @author tamarintech / https://tamarintech.com
  3. *
  4. * Description: Early release of an AMF Loader following the pattern of the
  5. * example loaders in the three.js project.
  6. *
  7. * More information about the AMF format: http://amf.wikispaces.com
  8. *
  9. * Usage:
  10. * var loader = new AMFLoader();
  11. * loader.load('/path/to/project.amf', function(objecttree) {
  12. * scene.add(objecttree);
  13. * });
  14. *
  15. * Materials now supported, material colors supported
  16. * Zip support, requires jszip
  17. * TextDecoder polyfill required by some browsers (particularly IE, Edge)
  18. * No constellation support (yet)!
  19. *
  20. */
  21. THREE.AMFLoader = function ( manager ) {
  22. this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
  23. };
  24. THREE.AMFLoader.prototype = {
  25. constructor: THREE.AMFLoader,
  26. load: function ( url, onLoad, onProgress, onError ) {
  27. var scope = this;
  28. var loader = new THREE.XHRLoader( scope.manager );
  29. loader.setCrossOrigin( this.crossOrigin );
  30. loader.setResponseType( 'arraybuffer' );
  31. loader.load( url, function( text ) {
  32. var amfObject = scope.parse( text );
  33. onLoad( amfObject );
  34. }, onProgress, onError );
  35. },
  36. parse: function ( data ) {
  37. var amfName = "";
  38. var amfAuthor = "";
  39. var amfScale = 1.0;
  40. var amfMaterials = {};
  41. var amfObjects = {};
  42. var xmldata = this._loadDocument( data );
  43. amfScale = this._loadDocumentScale( xmldata );
  44. var documentchildren = xmldata.documentElement.children;
  45. for ( var i = 0; i < documentchildren.length; i ++ ) {
  46. if ( documentchildren[ i ].nodeName === 'metadata' ) {
  47. if ( documentchildren[ i ].attributes[ 'type' ] !== undefined ) {
  48. if ( documentchildren[ i ].attributes[ 'type' ].value === 'name' ) {
  49. amfName = documentchildren[ i ].textContent;
  50. } else if ( documentchildren[ i ].attributes[ 'type' ].value === 'author' ) {
  51. amfAuthor = documentchildren[ i ].textContent;
  52. }
  53. }
  54. } else if ( documentchildren[ i ].nodeName === 'material' ) {
  55. var loadedmaterial = this._loadMaterials( documentchildren[ i ] );
  56. amfMaterials[ loadedmaterial.id ] = loadedmaterial.material;
  57. } else if ( documentchildren[ i ].nodeName === 'object' ) {
  58. var loadedobject = this._loadObject( documentchildren[ i ] );
  59. amfObjects[ loadedobject.id ] = loadedobject.obj;
  60. }
  61. }
  62. var sceneobject = new THREE.Object3D();
  63. sceneobject.name = amfName;
  64. sceneobject.userData.author = amfAuthor;
  65. sceneobject.userData.loader = "AMF";
  66. var defaultmaterial = new THREE.MeshPhongMaterial( { shading: THREE.FlatShading, color: 0xaaaaff } );
  67. for ( var objid in amfObjects ) {
  68. var newobject = new THREE.Object3D();
  69. for ( var meshi = 0; meshi < amfObjects[ objid ].meshes.length; meshi ++ ) {
  70. var meshvertices = Float32Array.from( amfObjects[ objid ].meshes[ meshi ].vertices );
  71. var vertices = new THREE.BufferAttribute( Float32Array.from( meshvertices ), 3 );
  72. var objdefaultmaterial = defaultmaterial;
  73. if ( amfObjects[ objid ].meshes[ meshi ].color ) {
  74. var color = amfObjects[ objid ].meshes[ meshi ].color;
  75. objdefaultmaterial = defaultmaterial.clone();
  76. objdefaultmaterial.color = new THREE.Color( color.r, color.g, color.b );
  77. if ( color.a != 1.0 ) {
  78. objdefaultmaterial.transparent = true;
  79. objdefaultmaterial.opacity = color.a;
  80. }
  81. }
  82. for ( var voli = 0; voli < amfObjects[ objid ].meshes[ meshi ].volumes.length; voli ++ ) {
  83. var currvolume = amfObjects[ objid ].meshes[ meshi ].volumes[ voli ];
  84. var newgeometry = new THREE.BufferGeometry();
  85. var indexes = Uint32Array.from( currvolume.triangles );
  86. var normals = new Uint32Array( vertices.array.length );
  87. var material = objdefaultmaterial;
  88. newgeometry.setIndex( new THREE.BufferAttribute( indexes, 1 ) );
  89. newgeometry.addAttribute( 'position', vertices.clone() );
  90. if ( amfMaterials[ currvolume.materialid ] !== undefined ) {
  91. material = amfMaterials[ currvolume.materialid ];
  92. }
  93. newgeometry.scale( amfScale, amfScale, amfScale );
  94. var newmesh = new THREE.Mesh( newgeometry, material.clone() );
  95. newobject.add( newmesh );
  96. }
  97. }
  98. sceneobject.add( newobject );
  99. }
  100. return sceneobject;
  101. },
  102. _loadDocument: function ( data ) {
  103. var view = new DataView( data );
  104. var magic = String.fromCharCode( view.getUint8( 0 ), view.getUint8( 1 ) );
  105. if ( magic === "PK" ) {
  106. console.log( "Loading Zip" );
  107. var zip = null;
  108. var file = null;
  109. try {
  110. zip = new JSZip( data );
  111. } catch ( e ) {
  112. if ( e instanceof ReferenceError ) {
  113. console.log( " jszip missing and file is compressed." );
  114. return null;
  115. }
  116. }
  117. for ( file in zip.files ) {
  118. if ( file.toLowerCase().endsWith( ".amf" ) ) {
  119. break;
  120. }
  121. }
  122. console.log( " Trying to load file asset: " + file );
  123. view = new DataView( zip.file( file ).asArrayBuffer() );
  124. }
  125. if ( TextDecoder === undefined ) {
  126. console.log( " TextDecoder not present. Please use TextDecoder polyfill." );
  127. return null;
  128. }
  129. var filetext = new TextDecoder( 'utf-8' ).decode( view );
  130. var xmldata = new DOMParser().parseFromString( filetext, 'application/xml' );
  131. if ( xmldata.documentElement.nodeName.toLowerCase() !== "amf" ) {
  132. console.log( " Error loading AMF - no AMF document found." );
  133. return null;
  134. }
  135. return xmldata;
  136. },
  137. _loadDocumentScale: function ( xmldata ) {
  138. var scale = 1.0;
  139. var unit = 'millimeter';
  140. if ( xmldata.documentElement.attributes[ 'unit' ] !== undefined ) {
  141. unit = xmldata.documentElement.attributes[ 'unit' ].value.toLowerCase();
  142. }
  143. var scale_units = {
  144. 'millimeter': 1.0,
  145. 'inch': 25.4,
  146. 'feet': 304.8,
  147. 'meter': 1000.0,
  148. 'micron': 0.001
  149. };
  150. if ( scale_units[ unit ] !== undefined ) {
  151. scale = scale_units[ unit ];
  152. }
  153. console.log( " Unit scale: " + scale );
  154. return scale;
  155. },
  156. _loadMaterials: function ( node ) {
  157. var mat = node;
  158. var loadedmaterial = null;
  159. var matname = "AMF Material";
  160. var matid = mat.attributes[ 'id' ].textContent;
  161. var color;
  162. for ( var i = 0; i < mat.children.length; i ++ ) {
  163. var matchildel = mat.children[ i ];
  164. if ( matchildel.nodeName === "metadata" && matchildel.attributes[ 'type' ] !== undefined ) {
  165. if ( matchildel.attributes[ 'type' ].value === 'name' ) {
  166. matname = matchildel.textContent;
  167. }
  168. } else if ( matchildel.nodeName === 'color' ) {
  169. color = this._loadColor( matchildel );
  170. }
  171. }
  172. loadedmaterial = new THREE.MeshPhongMaterial( {
  173. shading: THREE.FlatShading,
  174. color: new THREE.Color( color.r, color.g, color.b ),
  175. name: matname } );
  176. if ( color.opacity !== 1.0 ) {
  177. loadedmaterial.transparent = true;
  178. loadedmaterial.opacity = color.opacity;
  179. }
  180. return { 'id': matid, 'material': loadedmaterial };
  181. },
  182. _loadColor: function ( node ) {
  183. var color = { 'r': 1.0, 'g': 1.0, 'b': 1.0, 'a': 1.0, opacity: 1.0 };
  184. for ( var i = 0; i < node.children.length; i ++ ) {
  185. var matcolor = node.children[ i ];
  186. if ( matcolor.nodeName === 'r' ) {
  187. color.r = matcolor.textContent;
  188. } else if ( matcolor.nodeName === 'g' ) {
  189. color.g = matcolor.textContent;
  190. } else if ( matcolor.nodeName === 'b' ) {
  191. color.b = matcolor.textContent;
  192. } else if ( matcolor.nodeName === 'a' ) {
  193. color.opacity = matcolor.textContent;
  194. }
  195. }
  196. return color;
  197. },
  198. _loadMeshVolume: function( node ) {
  199. var volume = { "name": "", "triangles": [], "materialid": null };
  200. var currvolumenode = node.firstElementChild;
  201. if ( node.attributes[ 'materialid' ] !== undefined ) {
  202. volume.materialid = node.attributes[ 'materialid' ].nodeValue;
  203. }
  204. while ( currvolumenode ) {
  205. if ( currvolumenode.nodeName === "metadata" ) {
  206. if ( currvolumenode.attributes[ 'type' ] !== undefined ) {
  207. if ( currvolumenode.attributes[ 'type' ].value === 'name' ) {
  208. volume.name = currvolumenode.textContent;
  209. }
  210. }
  211. } else if ( currvolumenode.nodeName === "triangle" ) {
  212. var trianglenode = currvolumenode.firstElementChild;
  213. while ( trianglenode ) {
  214. if ( trianglenode.nodeName === "v1" ||
  215. trianglenode.nodeName === "v2" ||
  216. trianglenode.nodeName === "v3" ) {
  217. volume.triangles.push( trianglenode.textContent );
  218. }
  219. trianglenode = trianglenode.nextElementSibling;
  220. }
  221. }
  222. currvolumenode = currvolumenode.nextElementSibling;
  223. }
  224. return volume;
  225. },
  226. _loadMeshVertices: function( node ) {
  227. var vert_array = [];
  228. var currverticesnode = node.firstElementChild;
  229. while ( currverticesnode ) {
  230. if ( currverticesnode.nodeName === "vertex" ) {
  231. var vnode = currverticesnode.firstElementChild;
  232. while ( vnode ) {
  233. if ( vnode.nodeName === "coordinates" ) {
  234. var coordnode = vnode.firstElementChild;
  235. while ( coordnode ) {
  236. if ( coordnode.nodeName === "x" ||
  237. coordnode.nodeName === "y" ||
  238. coordnode.nodeName === "z" ) {
  239. vert_array.push( coordnode.textContent );
  240. }
  241. coordnode = coordnode.nextElementSibling;
  242. }
  243. }
  244. vnode = vnode.nextElementSibling;
  245. }
  246. }
  247. currverticesnode = currverticesnode.nextElementSibling;
  248. }
  249. return vert_array;
  250. },
  251. _loadObject: function ( node ) {
  252. "use strict";
  253. var objid = node.attributes[ 'id' ].textContent;
  254. var loadedobject = { "name": "amfobject", "meshes": [] };
  255. var currcolor = null;
  256. var currobjnode = node.firstElementChild;
  257. while ( currobjnode ) {
  258. if ( currobjnode.nodeName === "metadata" ) {
  259. if ( currobjnode.attributes[ 'type' ] !== undefined ) {
  260. if ( currobjnode.attributes[ 'type' ].value === 'name' ) {
  261. loadedobject.name = currobjnode.textContent;
  262. }
  263. }
  264. } else if ( currobjnode.nodeName === "color" ) {
  265. currcolor = this._loadColor( currobjnode );
  266. } else if ( currobjnode.nodeName === "mesh" ) {
  267. var currmeshnode = currobjnode.firstElementChild;
  268. var mesh = { "vertices": [], "volumes": [], "color": currcolor };
  269. while ( currmeshnode ) {
  270. if ( currmeshnode.nodeName === "vertices" ) {
  271. mesh.vertices = mesh.vertices.concat( this._loadMeshVertices( currmeshnode ) );
  272. } else if ( currmeshnode.nodeName === "volume" ) {
  273. mesh.volumes.push( this._loadMeshVolume( currmeshnode ) );
  274. }
  275. currmeshnode = currmeshnode.nextElementSibling;
  276. }
  277. loadedobject.meshes.push( mesh );
  278. }
  279. currobjnode = currobjnode.nextElementSibling;
  280. }
  281. return { 'id': objid, 'obj': loadedobject };
  282. }
  283. };