STLLoader.js 8.6 KB
Newer Older
1 2 3
/**
 * @author aleeper / http://adamleeper.com/
 * @author mrdoob / http://mrdoob.com/
4
 * @author gero3 / https://github.com/gero3
5
 *
M
Mr.doob 已提交
6
 * Description: A THREE loader for STL ASCII files, as created by Solidworks and other CAD programs.
7
 *
A
Adam Leeper 已提交
8 9 10
 * 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.
11 12
 *				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...
13 14
 *
 * Usage:
M
Mr.doob 已提交
15 16
 * 	var loader = new THREE.STLLoader();
 * 	loader.addEventListener( 'load', function ( event ) {
17
 *
18 19
 * 		var geometry = event.content;
 * 		scene.add( new THREE.Mesh( geometry ) );
M
Mr.doob 已提交
20 21 22
 *
 * 	} );
 * 	loader.load( './models/stl/slotted_disk.stl' );
23 24 25
 */


26
THREE.STLLoader = function () {};
27 28 29

THREE.STLLoader.prototype = {

M
Mr.doob 已提交
30
	constructor: THREE.STLLoader,
31

M
Mr.doob 已提交
32 33 34
	addEventListener: THREE.EventDispatcher.prototype.addEventListener,
	hasEventListener: THREE.EventDispatcher.prototype.hasEventListener,
	removeEventListener: THREE.EventDispatcher.prototype.removeEventListener,
35 36
	dispatchEvent: THREE.EventDispatcher.prototype.dispatchEvent
};
37

38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
THREE.STLLoader.prototype.load = function (url) {
    var scope = this;
    console.log("Attempting to load URL: [" + url + "]");
    
    var xhr = new XMLHttpRequest();
    
    function onloaded( event ) {
    
        if ( event.target.status === 200 || event.target.status === 0 ) {
                var data = event.target.responseText;
                return scope.dispatchEvent({
                    type: 'load',
                    content: scope.parse(data)
                });
        } else {

            scope.dispatchEvent( { type: 'error', message: 'Couldn\'t load URL [' + url + ']',
                response: event.target.responseText } );
    
        }
    
    }
    
    xhr.addEventListener( 'load', onloaded, false );
    
    xhr.addEventListener( 'progress', function ( event ) {
    
        scope.dispatchEvent( { type: 'progress', loaded: event.loaded, total: event.total } );
    
    }, false );
    
    xhr.addEventListener( 'error', function () {
    
        scope.dispatchEvent( { type: 'error', message: 'Couldn\'t load URL [' + url + ']' } );
    
    }, false );
    
    xhr.overrideMimeType('text/plain; charset=x-user-defined');
    xhr.open( 'GET', url, true );
    xhr.send( null );
};
79

80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
THREE.STLLoader.prototype.parse = function (data) {
    var isBinary,
        _this = this;
    isBinary = function (data) {
        var expect, face_size, n_faces, reader;
        reader = new THREE.STLLoader.BinaryReader(data);
        reader.seek(80);
        face_size = (32 / 8 * 3) + ((32 / 8 * 3) * 3) + (16 / 8);
        n_faces = reader.readUInt32();
        expect = 80 + (32 / 8) + (n_faces * face_size);
        return expect === reader.getSize();
    };
    if (isBinary(data)) {
        return this.parseBinary(data);
    } else {
        return this.parseASCII(data);
    }
};
98

99
THREE.STLLoader.prototype.parseBinary = function (data) {
O
OpenShift guest 已提交
100 101
    var face, geometry, n_faces, reader, length, normal, i;
    
102 103 104 105
    reader = new THREE.STLLoader.BinaryReader(data);
    reader.seek(80);
    n_faces = reader.readUInt32();
    geometry = new THREE.Geometry();
O
OpenShift guest 已提交
106 107 108 109 110 111 112 113 114
    
    for (face = 0; face < n_faces; face++) {

        normal = new THREE.Vector3(reader.readFloat(),reader.readFloat(),reader.readFloat());
        
        for (i = 1; i <= 3; i++) {
            
            geometry.vertices.push(new THREE.Vector3(reader.readFloat(),reader.readFloat(),reader.readFloat()));
            
115
        }
O
OpenShift guest 已提交
116 117
        
        reader.readUInt16(); // attr doesn't get used yet.
118
        length = geometry.vertices.length;
O
OpenShift guest 已提交
119 120
        geometry.faces.push(new THREE.Face3(length - 3, length - 2, length - 1, normal));
        
121
    }
O
OpenShift guest 已提交
122
    
123 124 125
    geometry.computeCentroids();
    geometry.computeBoundingBox();
    geometry.computeBoundingSphere();
O
OpenShift guest 已提交
126
    
127 128
    return geometry;
};
129

130 131 132 133
THREE.STLLoader.prototype.parseASCII = function (data) {
    var geometry, length, normal, patternFace, patternNormal, patternVertex, result, text;
    geometry = new THREE.Geometry();
    patternFace = /facet([\s\S]*?)endfacet/g;
O
OpenShift guest 已提交
134
    
135
    while (((result = patternFace.exec(data)) != null)) {
O
OpenShift guest 已提交
136
        
137 138 139 140 141
        text = result[0];
        patternNormal = /normal[\s]+([\-+]?[0-9]+\.?[0-9]*([eE][\-+]?[0-9]+)?)+[\s]+([\-+]?[0-9]*\.?[0-9]+([eE][\-+]?[0-9]+)?)+[\s]+([\-+]?[0-9]*\.?[0-9]+([eE][\-+]?[0-9]+)?)+/g;
        while (((result = patternNormal.exec(text)) != null)) {
            normal = new THREE.Vector3(parseFloat(result[1]), parseFloat(result[3]), parseFloat(result[5]));
        }
O
OpenShift guest 已提交
142
        
143 144 145 146
        patternVertex = /vertex[\s]+([\-+]?[0-9]+\.?[0-9]*([eE][\-+]?[0-9]+)?)+[\s]+([\-+]?[0-9]*\.?[0-9]+([eE][\-+]?[0-9]+)?)+[\s]+([\-+]?[0-9]*\.?[0-9]+([eE][\-+]?[0-9]+)?)+/g;
        while (((result = patternVertex.exec(text)) != null)) {
            geometry.vertices.push(new THREE.Vector3(parseFloat(result[1]), parseFloat(result[3]), parseFloat(result[5])));
        }
O
OpenShift guest 已提交
147
        
148 149
        length = geometry.vertices.length;
        geometry.faces.push(new THREE.Face3(length - 3, length - 2, length - 1, normal));
O
OpenShift guest 已提交
150
        
151
    }
O
OpenShift guest 已提交
152
    
153 154 155
    geometry.computeCentroids();
    geometry.computeBoundingBox();
    geometry.computeBoundingSphere();
O
OpenShift guest 已提交
156
    
157 158
    return geometry;
};
159 160


161 162 163 164
THREE.STLLoader.BinaryReader = function (data) {
    this._buffer = data;
    this._pos = 0;
};
165

166
THREE.STLLoader.BinaryReader.prototype = {
167

168
    /* Public */
169

170 171 172 173 174 175
	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); },
176

177 178
	readFloat:	function (){ return this._decodeFloat(23, 8); },
	readDouble:	function (){ return this._decodeFloat(52, 11); },
179

180 181 182 183 184 185
	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;
M
Mr.doob 已提交
186 187
	},

188 189 190
	seek: function (pos) {
		this._pos = pos;
		this._checkSize(0);
191
	},
A
Adam Leeper 已提交
192

193 194
	getPosition: function () {
		return this._pos;
M
Mr.doob 已提交
195
	},
A
Adam Leeper 已提交
196

197 198
	getSize: function () {
		return this._buffer.length;
M
Mr.doob 已提交
199
	},
A
Adam Leeper 已提交
200

201

202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
	/* 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;
				}
				divisor *= 2;
M
Mr.doob 已提交
225
			}
226
		} while (precisionBits -= startBit);
227

228
		this._pos += size;
229

230 231 232
		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);
A
Adam Leeper 已提交
233 234
	},

235 236 237
	_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;
A
Adam Leeper 已提交
238

239 240 241
		this._pos += bits / 8;
		return result;
	},
A
Adam Leeper 已提交
242

243 244 245 246 247
	//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;
	},
A
Adam Leeper 已提交
248

249 250 251
	_readByte: function (i, size) {
		return this._buffer.charCodeAt(this._pos + size - i - 1) & 0xff;
	},
A
Adam Leeper 已提交
252

253 254 255 256 257 258
	_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;
A
Adam Leeper 已提交
259

260
		var sum = (this._readByte(curByte, size) >> offsetRight) & ((1 << (diff ? 8 - offsetRight : length)) - 1);
A
Adam Leeper 已提交
261

262 263 264
		if (diff && offsetLeft) {
			sum += (this._readByte(lastByte++, size) & ((1 << offsetLeft) - 1)) << (diff-- << 3) - offsetRight;
		}
A
Adam Leeper 已提交
265

266 267
		while (diff) {
			sum += this._shl(this._readByte(lastByte++, size), (diff-- << 3) - offsetRight);
268
		}
A
Adam Leeper 已提交
269

270 271
		return sum;
	},
A
Adam Leeper 已提交
272

273 274 275 276
	_checkSize: function (neededBits) {
		if (!(this._pos + Math.ceil(neededBits / 8) < this._buffer.length)) {
			throw new Error("Index out of bound");
		}
277
	}
O
OpenShift guest 已提交
278
};