Browse Source

Adding AMFLoader

Elijah Snyder 9 years ago
parent
commit
d508c73c12
3 changed files with 372 additions and 0 deletions
  1. 1 0
      editor/index.html
  2. 16 0
      editor/js/Loader.js
  3. 355 0
      examples/js/loaders/AMFLoader.js

+ 1 - 0
editor/index.html

@@ -14,6 +14,7 @@
 
 		<script src="../examples/js/controls/EditorControls.js"></script>
 		<script src="../examples/js/controls/TransformControls.js"></script>
+		<script src="../examples/js/loaders/AMFLoader.js"></script>
 		<script src="../examples/js/loaders/AWDLoader.js"></script>
 		<script src="../examples/js/loaders/BabylonLoader.js"></script>
 		<script src="../examples/js/loaders/ColladaLoader.js"></script>

+ 16 - 0
editor/js/Loader.js

@@ -16,6 +16,22 @@ var Loader = function ( editor ) {
 
 		switch ( extension ) {
 
+			case 'amf':
+
+				var reader = new FileReader();
+				reader.addEventListener( 'load', function ( event ) {
+
+					var loader = new THREE.AMFLoader();
+					var amfobject = loader.parse( event.target.result );
+
+					editor.addObject( amfobject );
+					editor.select( amfobject );
+
+				}, false );
+				reader.readAsArrayBuffer( file );
+
+				break;
+
 			case 'awd':
 
 				var reader = new FileReader();

+ 355 - 0
examples/js/loaders/AMFLoader.js

@@ -0,0 +1,355 @@
+/*
+ * @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 };
+  }
+};