123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319 |
- Thingiloader = function(event) {
- // Code from https://developer.mozilla.org/En/Using_XMLHttpRequest#Receiving_binary_data
- this.load_binary_resource = function(url) {
- var req = new XMLHttpRequest();
- req.open('GET', url, false);
- // The following line says we want to receive data as Binary and not as Unicode
- req.overrideMimeType('text/plain; charset=x-user-defined');
- req.send(null);
- if (req.status != 200) return '';
- return req.responseText;
- };
- this.loadSTL = function(url) {
- var looksLikeBinary = function(reader) {
- // STL files don't specify a way to distinguish ASCII from binary.
- // The usual way is checking for "solid" at the start of the file --
- // but Thingiverse has seen at least one binary STL file in the wild
- // that breaks this.
- // The approach here is different: binary STL files contain a triangle
- // count early in the file. If this correctly predicts the file's length,
- // it is most probably a binary STL file.
- reader.seek(80); // skip the header
- var count = reader.readUInt32();
- var predictedSize = 80 /* header */ + 4 /* count */ + 50 * count;
- return reader.getSize() == predictedSize;
- };
- workerFacadeMessage({'status':'message', 'content':'Downloading ' + url});
- var file = this.load_binary_resource(url);
- var reader = new BinaryReader(file);
- if (looksLikeBinary(reader)) {
- this.loadSTLBinary(reader);
- } else {
- this.loadSTLString(file);
- }
- };
- this.loadOBJ = function(url) {
- workerFacadeMessage({'status':'message', 'content':'Downloading ' + url});
- var file = this.load_binary_resource(url);
- this.loadOBJString(file);
- };
- this.loadJSON = function(url) {
- workerFacadeMessage({'status':'message', 'content':'Downloading ' + url});
- var file = this.load_binary_resource(url);
- this.loadJSONString(file);
- };
-
- this.loadPLY = function(url) {
- workerFacadeMessage({'status':'message', 'content':'Downloading ' + url});
-
- var file = this.load_binary_resource(url);
-
- if (file.match(/format ascii/i)) {
- this.loadPLYString(file);
- } else {
- this.loadPLYBinary(file);
- }
- };
- this.loadSTLString = function(STLString) {
- workerFacadeMessage({'status':'message', 'content':'Parsing STL String...'});
- workerFacadeMessage({'status':'complete', 'content':this.ParseSTLString(STLString)});
- };
- this.loadSTLBinary = function(STLBinary) {
- workerFacadeMessage({'status':'message', 'content':'Parsing STL Binary...'});
- workerFacadeMessage({'status':'complete', 'content':this.ParseSTLBinary(STLBinary)});
- };
- this.loadOBJString = function(OBJString) {
- workerFacadeMessage({'status':'message', 'content':'Parsing OBJ String...'});
- workerFacadeMessage({'status':'complete', 'content':this.ParseOBJString(OBJString)});
- };
- this.loadJSONString = function(JSONString) {
- workerFacadeMessage({'status':'message', 'content':'Parsing JSON String...'});
- workerFacadeMessage({'status':'complete', 'content':eval(JSONString)});
- };
-
- this.loadPLYString = function(PLYString) {
- workerFacadeMessage({'status':'message', 'content':'Parsing PLY String...'});
- workerFacadeMessage({'status':'complete_points', 'content':this.ParsePLYString(PLYString)});
- };
- this.loadPLYBinary = function(PLYBinary) {
- workerFacadeMessage({'status':'message', 'content':'Parsing PLY Binary...'});
- workerFacadeMessage({'status':'complete_points', 'content':this.ParsePLYBinary(PLYBinary)});
- };
- this.ParsePLYString = function(input) {
- var properties = [];
- var vertices = [];
- var colors = [];
- var vertex_count = 0;
-
- var header = /ply\n([\s\S]+)\nend_header/ig.exec(input)[1];
- var data = /end_header\n([\s\S]+)$/ig.exec(input)[1];
-
- // workerFacadeMessage({'status':'message', 'content':'header:\n' + header});
- // workerFacadeMessage({'status':'message', 'content':'data:\n' + data});
- header_parts = header.split("\n");
-
- for (i in header_parts) {
- if (/element vertex/i.test(header_parts[i])) {
- vertex_count = /element vertex (\d+)/i.exec(header_parts[i])[1];
- } else if (/property/i.test(header_parts[i])) {
- properties.push(/property (.*) (.*)/i.exec(header_parts[i])[2]);
- }
- }
-
- // workerFacadeMessage({'status':'message', 'content':'properties: ' + properties});
- data_parts = data.split("\n");
-
- for (i in data_parts) {
- data_line = data_parts[i];
- data_line_parts = data_line.split(" ");
-
- vertices.push([
- parseFloat(data_line_parts[properties.indexOf("x")]),
- parseFloat(data_line_parts[properties.indexOf("y")]),
- parseFloat(data_line_parts[properties.indexOf("z")])
- ]);
-
- colors.push([
- parseInt(data_line_parts[properties.indexOf("red")]),
- parseInt(data_line_parts[properties.indexOf("green")]),
- parseInt(data_line_parts[properties.indexOf("blue")])
- ]);
- }
- // workerFacadeMessage({'status':'message', 'content':'vertices: ' + vertices});
- return [vertices, colors];
- };
- this.ParsePLYBinary = function(input) {
- return false;
- };
- this.ParseSTLBinary = function(input) {
- // Skip the header.
- input.seek(80);
- // Load the number of vertices.
- var count = input.readUInt32();
- // During the parse loop we maintain the following data structures:
- var vertices = []; // Append-only list of all unique vertices.
- var vert_hash = {}; // Mapping from vertex to index in 'vertices', above.
- var faces = []; // List of triangle descriptions, each a three-element
- // list of indices in 'vertices', above.
- for (var i = 0; i < count; i++) {
- if (i % 100 == 0) {
- workerFacadeMessage({
- 'status':'message',
- 'content':'Parsing ' + (i+1) + ' of ' + count + ' polygons...'
- });
- workerFacadeMessage({
- 'status':'progress',
- 'content':parseInt(i / count * 100) + '%'
- });
- }
-
- // Skip the normal (3 single-precision floats)
- input.seek(input.getPosition() + 12);
- var face_indices = [];
- for (var x = 0; x < 3; x++) {
- var vertex = [input.readFloat(), input.readFloat(), input.readFloat()];
-
- var vertexIndex = vert_hash[vertex];
- if (vertexIndex == null) {
- vertexIndex = vertices.length;
- vertices.push(vertex);
- vert_hash[vertex] = vertexIndex;
- }
- face_indices.push(vertexIndex);
- }
- faces.push(face_indices);
-
- // Skip the "attribute" field (unused in common models)
- input.readUInt16();
- }
- return [vertices, faces];
- };
- // build stl's vertex and face arrays
- this.ParseSTLString = function(STLString) {
- var vertexes = [];
- var faces = [];
-
- var face_vertexes = [];
- var vert_hash = {}
- // console.log(STLString);
- // strip out extraneous stuff
- STLString = STLString.replace(/\r/, "\n");
- STLString = STLString.replace(/^solid[^\n]*/, "");
- STLString = STLString.replace(/\n/g, " ");
- STLString = STLString.replace(/facet normal /g,"");
- STLString = STLString.replace(/outer loop/g,"");
- STLString = STLString.replace(/vertex /g,"");
- STLString = STLString.replace(/endloop/g,"");
- STLString = STLString.replace(/endfacet/g,"");
- STLString = STLString.replace(/endsolid[^\n]*/, "");
- STLString = STLString.replace(/\s+/g, " ");
- STLString = STLString.replace(/^\s+/, "");
- // console.log(STLString);
- var facet_count = 0;
- var block_start = 0;
- var points = STLString.split(" ");
- workerFacadeMessage({'status':'message', 'content':'Parsing vertices...'});
- for (var i=0; i<points.length/12-1; i++) {
- if ((i % 100) == 0) {
- workerFacadeMessage({'status':'progress', 'content':parseInt(i / (points.length/12-1) * 100) + '%'});
- }
-
- var face_indices = [];
- for (var x=0; x<3; x++) {
- var vertex = [parseFloat(points[block_start+x*3+3]), parseFloat(points[block_start+x*3+4]), parseFloat(points[block_start+x*3+5])];
- var vertexIndex = vert_hash[vertex];
- if (vertexIndex == null) {
- vertexIndex = vertexes.length;
- vertexes.push(vertex);
- vert_hash[vertex] = vertexIndex;
- }
- face_indices.push(vertexIndex);
- }
- faces.push(face_indices);
-
- block_start = block_start + 12;
- }
- return [vertexes, faces];
- };
- this.ParseOBJString = function(OBJString) {
- var vertexes = [];
- var faces = [];
- var lines = OBJString.split("\n");
-
- // var normal_position = 0;
-
- for (var i=0; i<lines.length; i++) {
- workerFacadeMessage({'status':'progress', 'content':parseInt(i / lines.length * 100) + '%'});
-
- line_parts = lines[i].replace(/\s+/g, " ").split(" ");
-
- if (line_parts[0] == "v") {
- vertexes.push([parseFloat(line_parts[1]), parseFloat(line_parts[2]), parseFloat(line_parts[3])]);
- } else if (line_parts[0] == "f") {
- faces.push([parseFloat(line_parts[1].split("/")[0])-1, parseFloat(line_parts[2].split("/")[0])-1, parseFloat(line_parts[3].split("/")[0]-1), 0])
- }
- }
-
- return [vertexes, faces];
- };
- switch(event.data.cmd) {
- case "loadSTL":
- this.loadSTL(event.data.param);
- break;
- case "loadSTLString":
- this.loadSTLString(event.data.param);
- break;
- case "loadSTLBinary":
- this.loadSTLBinary(event.data.param);
- break;
- case "loadOBJ":
- this.loadOBJ(event.data.param);
- break;
- case "loadOBJString":
- this.loadOBJString(event.data.param);
- break;
- case "loadJSON":
- this.loadJSON(event.data.param);
- break;
- case "loadPLY":
- this.loadPLY(event.data.param);
- break;
- case "loadPLYString":
- this.loadPLYString(event.data.param);
- break;
- case "loadPLYBinary":
- this.loadPLYBinary(event.data.param);
- break;
- }
- };
- if (typeof(window) === "undefined") {
- onmessage = Thingiloader;
- workerFacadeMessage = postMessage;
- importScripts('binaryReader.js');
- } else {
- workerFacadeMessage = WorkerFacade.add(thingiurlbase + "/thingiloader.js", Thingiloader);
- }
|