|
@@ -8,7 +8,7 @@
|
|
* Supports both binary and ASCII encoded files, with automatic detection of type.
|
|
* Supports both binary and ASCII encoded files, with automatic detection of type.
|
|
*
|
|
*
|
|
* Limitations: Binary decoding ignores header. There doesn't seem to be much of a use for it.
|
|
* Limitations: Binary decoding ignores header. There doesn't seem to be much of a use for it.
|
|
- * There is perhaps some question as to how valid it is to always assume little-endian-ness.
|
|
|
|
|
|
+ * There is perhaps some question as to how valid it is to always assume little-endian-ness.
|
|
* ASCII decoding assumes file is UTF-8. Seems to work for the examples...
|
|
* ASCII decoding assumes file is UTF-8. Seems to work for the examples...
|
|
*
|
|
*
|
|
* Usage:
|
|
* Usage:
|
|
@@ -46,7 +46,7 @@ THREE.STLLoader.prototype.load = function (url, callback) {
|
|
|
|
|
|
if ( event.target.status === 200 || event.target.status === 0 ) {
|
|
if ( event.target.status === 200 || event.target.status === 0 ) {
|
|
|
|
|
|
- var geometry = scope.parse( event.target.responseText );
|
|
|
|
|
|
+ var geometry = scope.parse( event.target.response || event.target.responseText );
|
|
|
|
|
|
scope.dispatchEvent( { type: 'load', content: geometry } );
|
|
scope.dispatchEvent( { type: 'load', content: geometry } );
|
|
|
|
|
|
@@ -77,64 +77,68 @@ THREE.STLLoader.prototype.load = function (url, callback) {
|
|
|
|
|
|
xhr.overrideMimeType('text/plain; charset=x-user-defined');
|
|
xhr.overrideMimeType('text/plain; charset=x-user-defined');
|
|
xhr.open( 'GET', url, true );
|
|
xhr.open( 'GET', url, true );
|
|
|
|
+ xhr.responseType = "arraybuffer";
|
|
xhr.send( null );
|
|
xhr.send( null );
|
|
|
|
|
|
};
|
|
};
|
|
|
|
|
|
THREE.STLLoader.prototype.parse = function (data) {
|
|
THREE.STLLoader.prototype.parse = function (data) {
|
|
|
|
|
|
- var isBinary = function (data) {
|
|
|
|
|
|
+
|
|
|
|
+ var isBinary = function () {
|
|
|
|
|
|
var expect, face_size, n_faces, reader;
|
|
var expect, face_size, n_faces, reader;
|
|
- reader = new THREE.STLLoader.BinaryReader(data);
|
|
|
|
- reader.seek(80);
|
|
|
|
|
|
+ reader = new DataView( binData );
|
|
face_size = (32 / 8 * 3) + ((32 / 8 * 3) * 3) + (16 / 8);
|
|
face_size = (32 / 8 * 3) + ((32 / 8 * 3) * 3) + (16 / 8);
|
|
- n_faces = reader.readUInt32();
|
|
|
|
|
|
+ n_faces = reader.getUint32(80,true);
|
|
expect = 80 + (32 / 8) + (n_faces * face_size);
|
|
expect = 80 + (32 / 8) + (n_faces * face_size);
|
|
- return expect === reader.getSize();
|
|
|
|
|
|
+ return expect === reader.byteLength;
|
|
|
|
|
|
};
|
|
};
|
|
|
|
+
|
|
|
|
+ var binData = this.ensureBinary(data);
|
|
|
|
|
|
- if (isBinary(data)) {
|
|
|
|
|
|
+ if (isBinary()) {
|
|
|
|
|
|
- return this.parseBinary(data);
|
|
|
|
|
|
+ return this.parseBinary(binData);
|
|
|
|
|
|
} else {
|
|
} else {
|
|
-
|
|
|
|
- return this.parseASCII(data);
|
|
|
|
|
|
+
|
|
|
|
+ return this.parseASCII(this.ensureString(data));
|
|
|
|
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
THREE.STLLoader.prototype.parseBinary = function (data) {
|
|
THREE.STLLoader.prototype.parseBinary = function (data) {
|
|
|
|
|
|
- var face, geometry, n_faces, reader, length, normal, i;
|
|
|
|
-
|
|
|
|
- reader = new THREE.STLLoader.BinaryReader(data);
|
|
|
|
- reader.seek(80);
|
|
|
|
- n_faces = reader.readUInt32();
|
|
|
|
|
|
+ var face, geometry, n_faces, reader, length, normal, i, dataOffset, faceLength, start, vertexstart;
|
|
|
|
+
|
|
|
|
+ reader = new DataView( data );
|
|
|
|
+ n_faces = reader.getUint32(80,true);
|
|
geometry = new THREE.Geometry();
|
|
geometry = new THREE.Geometry();
|
|
-
|
|
|
|
|
|
+ dataOffset = 84;
|
|
|
|
+ faceLength = 12 * 4 + 2;
|
|
|
|
+
|
|
for (face = 0; face < n_faces; face++) {
|
|
for (face = 0; face < n_faces; face++) {
|
|
|
|
|
|
- normal = new THREE.Vector3(reader.readFloat(),reader.readFloat(),reader.readFloat());
|
|
|
|
|
|
+ start = dataOffset + face * faceLength;
|
|
|
|
+ normal = new THREE.Vector3(reader.getFloat32(start,true),reader.getFloat32(start + 4,true),reader.getFloat32(start + 8,true));
|
|
|
|
|
|
for (i = 1; i <= 3; i++) {
|
|
for (i = 1; i <= 3; i++) {
|
|
-
|
|
|
|
- geometry.vertices.push(new THREE.Vector3(reader.readFloat(),reader.readFloat(),reader.readFloat()));
|
|
|
|
|
|
+
|
|
|
|
+ vertexstart = start + i * 12;
|
|
|
|
+ geometry.vertices.push(new THREE.Vector3(reader.getFloat32(vertexstart,true),reader.getFloat32(vertexstart +4,true),reader.getFloat32(vertexstart + 8,true)));
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- reader.readUInt16(); // attr doesn't get used yet.
|
|
|
|
length = geometry.vertices.length;
|
|
length = geometry.vertices.length;
|
|
geometry.faces.push(new THREE.Face3(length - 3, length - 2, length - 1, normal));
|
|
geometry.faces.push(new THREE.Face3(length - 3, length - 2, length - 1, normal));
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
geometry.computeCentroids();
|
|
geometry.computeCentroids();
|
|
- geometry.computeBoundingBox();
|
|
|
|
geometry.computeBoundingSphere();
|
|
geometry.computeBoundingSphere();
|
|
-
|
|
|
|
|
|
+
|
|
return geometry;
|
|
return geometry;
|
|
|
|
|
|
};
|
|
};
|
|
@@ -177,153 +181,173 @@ THREE.STLLoader.prototype.parseASCII = function (data) {
|
|
|
|
|
|
};
|
|
};
|
|
|
|
|
|
-
|
|
|
|
-THREE.STLLoader.BinaryReader = function (data) {
|
|
|
|
-
|
|
|
|
- this._buffer = data;
|
|
|
|
- this._pos = 0;
|
|
|
|
|
|
+THREE.STLLoader.prototype.ensureString = function (buf) {
|
|
|
|
+
|
|
|
|
+ if (typeof buf !== "string"){
|
|
|
|
+ var array_buffer = new Uint8Array(buf);
|
|
|
|
+ var str = '';
|
|
|
|
+ for(var i = 0; i < buf.byteLength; i++) {
|
|
|
|
+ str += String.fromCharCode(array_buffer[i]); // implicitly assumes little-endian
|
|
|
|
+ }
|
|
|
|
+ return str;
|
|
|
|
+ } else {
|
|
|
|
+ return buf;
|
|
|
|
+ }
|
|
|
|
|
|
};
|
|
};
|
|
|
|
+THREE.STLLoader.prototype.ensureBinary = function (buf) {
|
|
|
|
+
|
|
|
|
+ if (typeof buf === "string"){
|
|
|
|
+ var array_buffer = new Uint8Array(buf.length);
|
|
|
|
+ for(var i = 0; i < buf.length; i++) {
|
|
|
|
+ array_buffer[i] = buf.charCodeAt(i) & 0xff; // implicitly assumes little-endian
|
|
|
|
+ }
|
|
|
|
+ return array_buffer.buffer || array_buffer;
|
|
|
|
+ } else {
|
|
|
|
+ return buf;
|
|
|
|
+ }
|
|
|
|
|
|
-THREE.STLLoader.BinaryReader.prototype = {
|
|
|
|
-
|
|
|
|
- /* Public */
|
|
|
|
-
|
|
|
|
- readInt8: function (){ return this._decodeInt(8, true); },
|
|
|
|
- readUInt8: function (){ return this._decodeInt(8, false); },
|
|
|
|
- readInt16: function (){ return this._decodeInt(16, true); },
|
|
|
|
- readUInt16: function (){ return this._decodeInt(16, false); },
|
|
|
|
- readInt32: function (){ return this._decodeInt(32, true); },
|
|
|
|
- readUInt32: function (){ return this._decodeInt(32, false); },
|
|
|
|
-
|
|
|
|
- readFloat: function (){ return this._decodeFloat(23, 8); },
|
|
|
|
- readDouble: function (){ return this._decodeFloat(52, 11); },
|
|
|
|
-
|
|
|
|
- readChar: function () { return this.readString(1); },
|
|
|
|
-
|
|
|
|
- readString: function (length) {
|
|
|
|
-
|
|
|
|
- this._checkSize(length * 8);
|
|
|
|
- var result = this._buffer.substr(this._pos, length);
|
|
|
|
- this._pos += length;
|
|
|
|
- return result;
|
|
|
|
-
|
|
|
|
- },
|
|
|
|
-
|
|
|
|
- seek: function (pos) {
|
|
|
|
-
|
|
|
|
- this._pos = pos;
|
|
|
|
- this._checkSize(0);
|
|
|
|
-
|
|
|
|
- },
|
|
|
|
-
|
|
|
|
- getPosition: function () {
|
|
|
|
-
|
|
|
|
- return this._pos;
|
|
|
|
-
|
|
|
|
- },
|
|
|
|
-
|
|
|
|
- getSize: function () {
|
|
|
|
-
|
|
|
|
- return this._buffer.length;
|
|
|
|
-
|
|
|
|
- },
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- /* Private */
|
|
|
|
-
|
|
|
|
- _decodeFloat: function(precisionBits, exponentBits){
|
|
|
|
-
|
|
|
|
- var length = precisionBits + exponentBits + 1;
|
|
|
|
- var size = length >> 3;
|
|
|
|
-
|
|
|
|
- this._checkSize(length);
|
|
|
|
|
|
+};
|
|
|
|
|
|
- var bias = Math.pow(2, exponentBits - 1) - 1;
|
|
|
|
- var signal = this._readBits(precisionBits + exponentBits, 1, size);
|
|
|
|
- var exponent = this._readBits(precisionBits, exponentBits, size);
|
|
|
|
- var significand = 0;
|
|
|
|
- var divisor = 2;
|
|
|
|
- // var curByte = length + (-precisionBits >> 3) - 1;
|
|
|
|
- var curByte = 0;
|
|
|
|
- do {
|
|
|
|
- var byteValue = this._readByte(++curByte, size);
|
|
|
|
- var startBit = precisionBits % 8 || 8;
|
|
|
|
- var mask = 1 << startBit;
|
|
|
|
- while (mask >>= 1) {
|
|
|
|
- if (byteValue & mask) {
|
|
|
|
- significand += 1 / divisor;
|
|
|
|
|
|
+if ( typeof DataView === 'undefined'){
|
|
|
|
+
|
|
|
|
+ DataView = function(buffer, byteOffset, byteLength){
|
|
|
|
+ this.buffer = buffer;
|
|
|
|
+ this.byteOffset = byteOffset || 0;
|
|
|
|
+ this.byteLength = byteLength || buffer.byteLength || buffer.length;
|
|
|
|
+ this._isString = typeof buffer === "string";
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ DataView.prototype = {
|
|
|
|
+ _getCharCodes:function(buffer,start,length){
|
|
|
|
+ start = start || 0;
|
|
|
|
+ length = length || buffer.length;
|
|
|
|
+ var end = start + length;
|
|
|
|
+ var codes = [];
|
|
|
|
+ for (var i = start; i < end; i++) {
|
|
|
|
+ codes.push(buffer.charCodeAt(i) & 0xff);
|
|
|
|
+ }
|
|
|
|
+ return codes;
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ _getBytes: function (length, byteOffset, littleEndian) {
|
|
|
|
+ var result;
|
|
|
|
+
|
|
|
|
+ // Handle the lack of endianness
|
|
|
|
+ if (littleEndian === undefined) {
|
|
|
|
+ littleEndian = this._littleEndian;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Handle the lack of byteOffset
|
|
|
|
+ if (byteOffset === undefined) {
|
|
|
|
+ byteOffset = this.byteOffset;
|
|
|
|
+ } else {
|
|
|
|
+ byteOffset = this.byteOffset + byteOffset;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (length === undefined) {
|
|
|
|
+ length = this.byteLength - byteOffset;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Error Checking
|
|
|
|
+ if (typeof byteOffset !== 'number') {
|
|
|
|
+ throw new TypeError('DataView byteOffset is not a number');
|
|
|
|
+ }
|
|
|
|
+ if (length < 0 || byteOffset + length > this.byteLength) {
|
|
|
|
+ throw new Error('DataView length or (byteOffset+length) value is out of bounds');
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (this.isString){
|
|
|
|
+ result = this._getCharCodes(this.buffer, byteOffset, byteOffset + length);
|
|
|
|
+ } else {
|
|
|
|
+ result = this.buffer.slice(byteOffset, byteOffset + length);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!littleEndian && length > 1) {
|
|
|
|
+ if (!(result instanceof Array)) {
|
|
|
|
+ result = Array.prototype.slice.call(result);
|
|
}
|
|
}
|
|
- divisor *= 2;
|
|
|
|
|
|
+
|
|
|
|
+ result.reverse();
|
|
}
|
|
}
|
|
- } while (precisionBits -= startBit);
|
|
|
|
-
|
|
|
|
- this._pos += size;
|
|
|
|
-
|
|
|
|
- return exponent == (bias << 1) + 1 ? significand ? NaN : signal ? -Infinity : +Infinity
|
|
|
|
- : (1 + signal * -2) * (exponent || significand ? !exponent ? Math.pow(2, -bias + 1) * significand
|
|
|
|
- : Math.pow(2, exponent - bias) * (1 + significand) : 0);
|
|
|
|
-
|
|
|
|
- },
|
|
|
|
-
|
|
|
|
- _decodeInt: function(bits, signed){
|
|
|
|
-
|
|
|
|
- var x = this._readBits(0, bits, bits / 8), max = Math.pow(2, bits);
|
|
|
|
- var result = signed && x >= max / 2 ? x - max : x;
|
|
|
|
-
|
|
|
|
- this._pos += bits / 8;
|
|
|
|
- return result;
|
|
|
|
-
|
|
|
|
- },
|
|
|
|
-
|
|
|
|
- //shl fix: Henri Torgemane ~1996 (compressed by Jonas Raoni)
|
|
|
|
- _shl: function (a, b){
|
|
|
|
-
|
|
|
|
- for (++b; --b; a = ((a %= 0x7fffffff + 1) & 0x40000000) == 0x40000000 ? a * 2 : (a - 0x40000000) * 2 + 0x7fffffff + 1);
|
|
|
|
- return a;
|
|
|
|
-
|
|
|
|
- },
|
|
|
|
-
|
|
|
|
- _readByte: function (i, size) {
|
|
|
|
-
|
|
|
|
- return this._buffer.charCodeAt(this._pos + size - i - 1) & 0xff;
|
|
|
|
-
|
|
|
|
- },
|
|
|
|
-
|
|
|
|
- _readBits: function (start, length, size) {
|
|
|
|
-
|
|
|
|
- var offsetLeft = (start + length) % 8;
|
|
|
|
- var offsetRight = start % 8;
|
|
|
|
- var curByte = size - (start >> 3) - 1;
|
|
|
|
- var lastByte = size + (-(start + length) >> 3);
|
|
|
|
- var diff = curByte - lastByte;
|
|
|
|
-
|
|
|
|
- var sum = (this._readByte(curByte, size) >> offsetRight) & ((1 << (diff ? 8 - offsetRight : length)) - 1);
|
|
|
|
-
|
|
|
|
- if (diff && offsetLeft) {
|
|
|
|
-
|
|
|
|
- sum += (this._readByte(lastByte++, size) & ((1 << offsetLeft) - 1)) << (diff-- << 3) - offsetRight;
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- while (diff) {
|
|
|
|
-
|
|
|
|
- sum += this._shl(this._readByte(lastByte++, size), (diff-- << 3) - offsetRight);
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return sum;
|
|
|
|
-
|
|
|
|
- },
|
|
|
|
-
|
|
|
|
- _checkSize: function (neededBits) {
|
|
|
|
-
|
|
|
|
- if (!(this._pos + Math.ceil(neededBits / 8) < this._buffer.length)) {
|
|
|
|
-
|
|
|
|
- throw new Error("Index out of bound");
|
|
|
|
-
|
|
|
|
|
|
+
|
|
|
|
+ return result;
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // Compatibility functions on a String Buffer
|
|
|
|
+
|
|
|
|
+ getFloat64: function (byteOffset, littleEndian) {
|
|
|
|
+ var b = this._getBytes(8, byteOffset, littleEndian),
|
|
|
|
+
|
|
|
|
+ sign = 1 - (2 * (b[7] >> 7)),
|
|
|
|
+ exponent = ((((b[7] << 1) & 0xff) << 3) | (b[6] >> 4)) - ((1 << 10) - 1),
|
|
|
|
+
|
|
|
|
+ // Binary operators such as | and << operate on 32 bit values, using + and Math.pow(2) instead
|
|
|
|
+ mantissa = ((b[6] & 0x0f) * Math.pow(2, 48)) + (b[5] * Math.pow(2, 40)) + (b[4] * Math.pow(2, 32)) +
|
|
|
|
+ (b[3] * Math.pow(2, 24)) + (b[2] * Math.pow(2, 16)) + (b[1] * Math.pow(2, 8)) + b[0];
|
|
|
|
+
|
|
|
|
+ if (exponent === 1024) {
|
|
|
|
+ if (mantissa !== 0) {
|
|
|
|
+ return NaN;
|
|
|
|
+ } else {
|
|
|
|
+ return sign * Infinity;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (exponent === -1023) { // Denormalized
|
|
|
|
+ return sign * mantissa * Math.pow(2, -1022 - 52);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return sign * (1 + mantissa * Math.pow(2, -52)) * Math.pow(2, exponent);
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ getFloat32: function (byteOffset, littleEndian) {
|
|
|
|
+ var b = this._getBytes(4, byteOffset, littleEndian),
|
|
|
|
+
|
|
|
|
+ sign = 1 - (2 * (b[3] >> 7)),
|
|
|
|
+ exponent = (((b[3] << 1) & 0xff) | (b[2] >> 7)) - 127,
|
|
|
|
+ mantissa = ((b[2] & 0x7f) << 16) | (b[1] << 8) | b[0];
|
|
|
|
+
|
|
|
|
+ if (exponent === 128) {
|
|
|
|
+ if (mantissa !== 0) {
|
|
|
|
+ return NaN;
|
|
|
|
+ } else {
|
|
|
|
+ return sign * Infinity;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (exponent === -127) { // Denormalized
|
|
|
|
+ return sign * mantissa * Math.pow(2, -126 - 23);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return sign * (1 + mantissa * Math.pow(2, -23)) * Math.pow(2, exponent);
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ getInt32: function (byteOffset, littleEndian) {
|
|
|
|
+ var b = this._getBytes(4, byteOffset, littleEndian);
|
|
|
|
+ return (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | b[0];
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ getUint32: function (byteOffset, littleEndian) {
|
|
|
|
+ return this.getInt32(byteOffset, littleEndian) >>> 0;
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ getInt16: function (byteOffset, littleEndian) {
|
|
|
|
+ return (this.getUint16(byteOffset, littleEndian) << 16) >> 16;
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ getUint16: function (byteOffset, littleEndian) {
|
|
|
|
+ var b = this._getBytes(2, byteOffset, littleEndian);
|
|
|
|
+ return (b[1] << 8) | b[0];
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ getInt8: function (byteOffset) {
|
|
|
|
+ return (this.getUint8(byteOffset) << 24) >> 24;
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ getUint8: function (byteOffset) {
|
|
|
|
+ return this._getBytes(1, byteOffset)[0];
|
|
}
|
|
}
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-};
|
|
|
|
|
|
+ };
|
|
|
|
+}
|