/* * @author tamarintech / https://tamarintech.com * * Description: Early release of an AMF Loader following the pattern of the * example loaders in the three.js project. * * More information about the AMF format: http://amf.wikispaces.com * * Usage: * var loader = new AMFLoader(); * loader.load('/path/to/project.amf', function(objecttree) { * scene.add(objecttree); * }); * * Materials now supported, material colors supported * Zip support, requires jszip * TextDecoder polyfill required by some browsers (particularly IE, Edge) * No constellation support (yet)! * */ THREE.AMFLoader = function( manager ) { this.manager = (manager !== undefined) ? manager : THREE.DefaultLoadingManager; }; THREE.AMFLoader.prototype = { constructor: THREE.AMFLoader, load: function(url, onLoad, onProgress, onError) { var scope = this; var loader = new THREE.XHRLoader(scope.manager); loader.setCrossOrigin(this.crossOrigin); loader.setResponseType('arraybuffer'); loader.load(url, function(text) { var amfObject = scope.parse(text); onLoad(amfObject); }, onProgress, onError); }, parse: function(data) { var amfName = ""; var amfAuthor = ""; var amfScale = 1.0; var amfMaterials = {}; var amfObjects = {}; var xmldata = this._loadDocument(data); amfScale = this._loadDocumentScale(xmldata); var documentchildren = xmldata.documentElement.children; for(var i = 0; i < documentchildren.length; i++) { if(documentchildren[i].nodeName === 'metadata') { if(documentchildren[i].attributes['type'] !== undefined) { if(documentchildren[i].attributes['type'].value === 'name') { amfName = documentchildren[i].textContent; } else if(documentchildren[i].attributes['type'].value === 'author') { amfAuthor = documentchildren[i].textContent; } } } else if(documentchildren[i].nodeName === 'material') { var loadedmaterial = this._loadMaterials(documentchildren[i]); amfMaterials[loadedmaterial.id] = loadedmaterial.material; } else if(documentchildren[i].nodeName === 'object') { var loadedobject = this._loadObject(documentchildren[i]); amfObjects[loadedobject.id] = loadedobject.obj; } } var sceneobject = new THREE.Object3D(); sceneobject.name = amfName; sceneobject.userData.author = amfAuthor; sceneobject.userData.loader = "AMF"; var defaultmaterial = new THREE.MeshPhongMaterial({shading: THREE.FlatShading, color: 0xaaaaff}); for(var objid in amfObjects) { var newobject = new THREE.Object3D(); for(var meshi = 0; meshi < amfObjects[objid].meshes.length; meshi++) { var meshvertices = Float32Array.from(amfObjects[objid].meshes[meshi].vertices); var vertices = new THREE.BufferAttribute(Float32Array.from(meshvertices), 3); var objdefaultmaterial = defaultmaterial; if(amfObjects[objid].meshes[meshi].color) { var color = amfObjects[objid].meshes[meshi].color; objdefaultmaterial = defaultmaterial.clone(); objdefaultmaterial.color = new THREE.Color(color.r, color.g, color.b); if(color.a != 1.0) { objdefaultmaterial.transparent = true; objdefaultmaterial.opacity = color.a; } } for(var voli = 0; voli < amfObjects[objid].meshes[meshi].volumes.length; voli++) { var currvolume = amfObjects[objid].meshes[meshi].volumes[voli]; var newgeometry = new THREE.BufferGeometry(); var indexes = Uint32Array.from(currvolume.triangles); var normals = new Uint32Array(vertices.length); var material = objdefaultmaterial; newgeometry.addAttribute('position', vertices.clone()); newgeometry.addAttribute('index', new THREE.BufferAttribute(indexes, 1)); if(amfMaterials[currvolume.materialid] !== undefined) { material = amfMaterials[currvolume.materialid]; } newgeometry.scale(amfScale, amfScale, amfScale); var newmesh = new THREE.Mesh(newgeometry, material.clone()); newobject.add(newmesh); } } sceneobject.add(newobject); } return sceneobject; }, _loadDocument: function ( data ) { var view = new DataView(data); var magic = String.fromCharCode(view.getUint8(0), view.getUint8(1)); if(magic === "PK") { console.log("Loading Zip"); var zip = null; var file = null; try { zip = new JSZip(data); } catch (e) { if (e instanceof ReferenceError) { console.log(" jszip missing and file is compressed."); return null; } } for(file in zip.files) { if(file.toLowerCase().endsWith(".amf")) { break; } } console.log(" Trying to load file asset: " + file); view = new DataView(zip.file(file).asArrayBuffer()); } if(TextDecoder === undefined) { console.log(" TextDecoder not present. Please use TextDecoder polyfill."); return null; } var filetext = new TextDecoder('utf-8').decode(view); var xmldata = new DOMParser().parseFromString(filetext, 'application/xml'); if(xmldata.documentElement.nodeName.toLowerCase() !== "amf") { console.log(" Error loading AMF - no AMF document found."); return null; } return xmldata; }, _loadDocumentScale: function ( xmldata ) { var scale = 1.0; var unit = xmldata.documentElement.attributes['unit'].value.toLowerCase(); var scale_units = { 'millimeter': 1.0, 'inch': 25.4, 'feet': 304.8, 'meter': 1000.0, 'micron': 0.001 }; if(scale_units[unit] !== undefined) { scale = scale_units[unit]; } console.log(" Unit scale: " + scale); return scale; }, _loadMaterials: function ( node ) { var mat = node; var loadedmaterial = null; var matname = "AMF Material"; var matid = mat.attributes['id'].textContent; var color; for(var i = 0; i < mat.children.length; i++) { var matchildel = mat.children[i]; if(matchildel.nodeName === "metadata" && matchildel.attributes['type'] !== undefined) { if(matchildel.attributes['type'].value === 'name') { matname = matchildel.textContent; } } else if(matchildel.nodeName === 'color') { color = this._loadColor(matchildel); } } loadedmaterial = new THREE.MeshPhongMaterial({ shading: THREE.FlatShading, color: new THREE.Color(color.r, color.g, color.b), name: matname}); if(color.opacity !== 1.0) { loadedmaterial.transparent = true; loadedmaterial.opacity = color.opacity; } return { 'id': matid, 'material': loadedmaterial }; }, _loadColor: function ( node ) { var color = {'r': 1.0, 'g': 1.0, 'b': 1.0, 'a': 1.0, opacity: 1.0}; for(var i = 0; i < node.children.length; i++) { var matcolor = node.children[i]; if(matcolor.nodeName === 'r') { color.r = matcolor.textContent; } else if(matcolor.nodeName === 'g') { color.g = matcolor.textContent; } else if(matcolor.nodeName === 'b') { color.b = matcolor.textContent; } else if(matcolor.nodeName === 'a') { color.opacity = matcolor.textContent; } } return color; }, _loadMeshVolume: function( node ) { var volume = { "name": "", "triangles": [], "materialid": null }; var currvolumenode = node.firstElementChild; if(node.attributes['materialid'] !== undefined) { volume.materialid = node.attributes['materialid'].nodeValue; } while( currvolumenode ) { if( currvolumenode.nodeName === "metadata" ) { if(currvolumenode.attributes['type'] !== undefined) { if(currvolumenode.attributes['type'].value === 'name') { volume.name = currvolumenode.textContent; } } } else if ( currvolumenode.nodeName === "triangle" ) { var trianglenode = currvolumenode.firstElementChild; while( trianglenode ) { if( trianglenode.nodeName === "v1" || trianglenode.nodeName === "v2" || trianglenode.nodeName === "v3") { volume.triangles.push(trianglenode.textContent); } trianglenode = trianglenode.nextElementSibling; } } currvolumenode = currvolumenode.nextElementSibling; } return volume; }, _loadMeshVertices: function( node ) { var vert_array = []; var currverticesnode = node.firstElementChild; while( currverticesnode ) { if ( currverticesnode.nodeName === "vertex" ) { var vnode = currverticesnode.firstElementChild; while( vnode ) { if( vnode.nodeName === "coordinates") { var coordnode = vnode.firstElementChild; while( coordnode ) { if( coordnode.nodeName === "x" || coordnode.nodeName === "y" || coordnode.nodeName === "z") { vert_array.push(coordnode.textContent); } coordnode = coordnode.nextElementSibling; } } vnode = vnode.nextElementSibling; } } currverticesnode = currverticesnode.nextElementSibling; } return vert_array; }, _loadObject: function ( node ) { "use strict"; var objid = node.attributes['id'].textContent; var loadedobject = { "name": "amfobject", "meshes": [] }; var currcolor = null; var currobjnode = node.firstElementChild; while( currobjnode ) { if(currobjnode.nodeName === "metadata") { if(currobjnode.attributes['type'] !== undefined) { if(currobjnode.attributes['type'].value === 'name') { loadedobject.name = currobjnode.textContent; } } } else if(currobjnode.nodeName === "color") { currcolor = this._loadColor(currobjnode); } else if(currobjnode.nodeName === "mesh") { var currmeshnode = currobjnode.firstElementChild; var mesh = {"vertices": [], "volumes": [], "color": currcolor }; while( currmeshnode ) { if(currmeshnode.nodeName === "vertices") { mesh.vertices = mesh.vertices.concat(this._loadMeshVertices(currmeshnode)); } else if(currmeshnode.nodeName === "volume") { mesh.volumes.push(this._loadMeshVolume(currmeshnode)); } currmeshnode = currmeshnode.nextElementSibling; } loadedobject.meshes.push(mesh); } currobjnode = currobjnode.nextElementSibling; } return { 'id': objid, 'obj': loadedobject }; } };