mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-24 21:07:17 +00:00
Users now can preview stl and obj files in the browser
This commit is contained in:
318
media/js/thingiview/thingiloader.js
Normal file
318
media/js/thingiview/thingiloader.js
Normal file
@@ -0,0 +1,318 @@
|
||||
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);
|
||||
}
|
Reference in New Issue
Block a user