AMFLoader.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  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.length);
  87. var material = objdefaultmaterial;
  88. newgeometry.addAttribute('position', vertices.clone());
  89. newgeometry.addAttribute('index', new THREE.BufferAttribute(indexes, 1));
  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 = xmldata.documentElement.attributes['unit'].value.toLowerCase();
  140. var scale_units = {
  141. 'millimeter': 1.0,
  142. 'inch': 25.4,
  143. 'feet': 304.8,
  144. 'meter': 1000.0,
  145. 'micron': 0.001
  146. };
  147. if(scale_units[unit] !== undefined) {
  148. scale = scale_units[unit];
  149. }
  150. console.log(" Unit scale: " + scale);
  151. return scale;
  152. },
  153. _loadMaterials: function ( node ) {
  154. var mat = node;
  155. var loadedmaterial = null;
  156. var matname = "AMF Material";
  157. var matid = mat.attributes['id'].textContent;
  158. var color;
  159. for(var i = 0; i < mat.children.length; i++) {
  160. var matchildel = mat.children[i];
  161. if(matchildel.nodeName === "metadata" && matchildel.attributes['type'] !== undefined) {
  162. if(matchildel.attributes['type'].value === 'name') {
  163. matname = matchildel.textContent;
  164. }
  165. } else if(matchildel.nodeName === 'color') {
  166. color = this._loadColor(matchildel);
  167. }
  168. }
  169. loadedmaterial = new THREE.MeshPhongMaterial({
  170. shading: THREE.FlatShading,
  171. color: new THREE.Color(color.r, color.g, color.b),
  172. name: matname});
  173. if(color.opacity !== 1.0) {
  174. loadedmaterial.transparent = true;
  175. loadedmaterial.opacity = color.opacity;
  176. }
  177. return { 'id': matid, 'material': loadedmaterial };
  178. },
  179. _loadColor: function ( node ) {
  180. var color = {'r': 1.0, 'g': 1.0, 'b': 1.0, 'a': 1.0, opacity: 1.0};
  181. for(var i = 0; i < node.children.length; i++) {
  182. var matcolor = node.children[i];
  183. if(matcolor.nodeName === 'r') {
  184. color.r = matcolor.textContent;
  185. } else if(matcolor.nodeName === 'g') {
  186. color.g = matcolor.textContent;
  187. } else if(matcolor.nodeName === 'b') {
  188. color.b = matcolor.textContent;
  189. } else if(matcolor.nodeName === 'a') {
  190. color.opacity = matcolor.textContent;
  191. }
  192. }
  193. return color;
  194. },
  195. _loadMeshVolume: function( node ) {
  196. var volume = { "name": "", "triangles": [], "materialid": null };
  197. var currvolumenode = node.firstElementChild;
  198. if(node.attributes['materialid'] !== undefined) {
  199. volume.materialid = node.attributes['materialid'].nodeValue;
  200. }
  201. while( currvolumenode ) {
  202. if( currvolumenode.nodeName === "metadata" ) {
  203. if(currvolumenode.attributes['type'] !== undefined) {
  204. if(currvolumenode.attributes['type'].value === 'name') {
  205. volume.name = currvolumenode.textContent;
  206. }
  207. }
  208. } else if ( currvolumenode.nodeName === "triangle" ) {
  209. var trianglenode = currvolumenode.firstElementChild;
  210. while( trianglenode ) {
  211. if( trianglenode.nodeName === "v1" ||
  212. trianglenode.nodeName === "v2" ||
  213. trianglenode.nodeName === "v3") {
  214. volume.triangles.push(trianglenode.textContent);
  215. }
  216. trianglenode = trianglenode.nextElementSibling;
  217. }
  218. }
  219. currvolumenode = currvolumenode.nextElementSibling;
  220. }
  221. return volume;
  222. },
  223. _loadMeshVertices: function( node ) {
  224. var vert_array = [];
  225. var currverticesnode = node.firstElementChild;
  226. while( currverticesnode ) {
  227. if ( currverticesnode.nodeName === "vertex" ) {
  228. var vnode = currverticesnode.firstElementChild;
  229. while( vnode ) {
  230. if( vnode.nodeName === "coordinates") {
  231. var coordnode = vnode.firstElementChild;
  232. while( coordnode ) {
  233. if( coordnode.nodeName === "x" ||
  234. coordnode.nodeName === "y" ||
  235. coordnode.nodeName === "z") {
  236. vert_array.push(coordnode.textContent);
  237. }
  238. coordnode = coordnode.nextElementSibling;
  239. }
  240. }
  241. vnode = vnode.nextElementSibling;
  242. }
  243. }
  244. currverticesnode = currverticesnode.nextElementSibling;
  245. }
  246. return vert_array;
  247. },
  248. _loadObject: function ( node ) {
  249. "use strict";
  250. var objid = node.attributes['id'].textContent;
  251. var loadedobject = { "name": "amfobject", "meshes": [] };
  252. var currcolor = null;
  253. var currobjnode = node.firstElementChild;
  254. while( currobjnode ) {
  255. if(currobjnode.nodeName === "metadata") {
  256. if(currobjnode.attributes['type'] !== undefined) {
  257. if(currobjnode.attributes['type'].value === 'name') {
  258. loadedobject.name = currobjnode.textContent;
  259. }
  260. }
  261. } else if(currobjnode.nodeName === "color") {
  262. currcolor = this._loadColor(currobjnode);
  263. } else if(currobjnode.nodeName === "mesh") {
  264. var currmeshnode = currobjnode.firstElementChild;
  265. var mesh = {"vertices": [], "volumes": [], "color": currcolor };
  266. while( currmeshnode ) {
  267. if(currmeshnode.nodeName === "vertices") {
  268. mesh.vertices = mesh.vertices.concat(this._loadMeshVertices(currmeshnode));
  269. } else if(currmeshnode.nodeName === "volume") {
  270. mesh.volumes.push(this._loadMeshVolume(currmeshnode));
  271. }
  272. currmeshnode = currmeshnode.nextElementSibling;
  273. }
  274. loadedobject.meshes.push(mesh);
  275. }
  276. currobjnode = currobjnode.nextElementSibling;
  277. }
  278. return { 'id': objid, 'obj': loadedobject };
  279. }
  280. };