1 /**
  2  * potree.js 
  3  * http://potree.org
  4  *
  5  * Copyright 2012, Markus Sch�tz
  6  * Licensed under the GPL Version 2 or later.
  7  * - http://potree.org/wp/?page_id=7
  8  * - http://www.gnu.org/licenses/gpl-3.0.html
  9  *
 10  */
 11 
 12 importScripts("PointAttributes.js");
 13 importScripts("PlyLoader.js");
 14 
 15 self.onmessage = function(message){
 16 //	postMessage("onmessage");
 17 	var plyFile = message.data;
 18 	PlyBinaryWorker.load(plyFile);
 19 };
 20 
 21 /**
 22  * A worker thread class that loads data from ply files in the background.
 23  * 
 24  * @class
 25  * 
 26  */
 27 function PlyBinaryWorker(){
 28 	
 29 }
 30 
 31 PlyBinaryWorker.load = function PlyLoader_load(source){
 32 //	postMessage("load");
 33 	if(source instanceof Blob){
 34 		PlyBinaryWorker.loadFromFile(source);
 35 	}else if(source instanceof ArrayBuffer){
 36 		PlyBinaryWorker.loadFromBuffer(source);
 37 	}else if(typeof source == 'string'){
 38 		PlyBinaryWorker.loadFromUrl(source);
 39 	}
 40 //	postMessage("sis: " + (source instanceof String));
 41 };
 42 
 43 PlyBinaryWorker.loadFromFile = function(source){
 44 	var reader = new FileReaderSync();
 45 	var buffer = reader.readAsArrayBuffer(source);
 46 	PlyBinaryWorker.loadFromBuffer(buffer);
 47 };
 48 
 49 PlyBinaryWorker.loadFromUrl = function(source){
 50 //	postMessage("fromUrl");
 51 	var xhr = new XMLHttpRequest();
 52 	xhr.open('GET', source, true);
 53 	xhr.overrideMimeType('text/plain; charset=x-user-defined');
 54 	xhr.responseType = 'arraybuffer';
 55 	
 56 	xhr.onreadystatechange = function(){
 57 		if(xhr.readyState == 4){
 58 			if (xhr.status == 200 || xhr.status == 0) {
 59 				var buffer = xhr.response;
 60 				PlyBinaryWorker.loadFromBuffer(buffer);
 61 			} else {
 62 				// post message failed
 63 			}
 64 		}
 65 	};
 66 	
 67 	try{
 68 		xhr.send(null);
 69 	}catch(e){
 70 		// post message failed
 71 	}
 72 };
 73 
 74 PlyBinaryWorker.loadFromAscii = function loadFromAscii(plyFile){
 75 	var vertexElement = plyFile.header.elements[0];
 76 	var plyPointAttributes = PlyLoader.pointAttributesFromProperties(vertexElement.properties);
 77 	var pointAttributes = PlyLoader.pointAttributesFromProperties(vertexElement.properties, true);
 78 	var pointBuffer = new ArrayBuffer(pointAttributes.byteSize * vertexElement.size);
 79 	var pointDataView = new DataView(pointBuffer);
 80 	var plyBuffer = plyFile.buffer;
 81 	var plyBufferUint8 = new Uint8Array(plyBuffer, plyFile.header.byteSize);
 82 	
 83 	var currentLineStart = 0;
 84 	var nextLineStart = 0;
 85 	var getNextLineStart = function(){
 86 		for(var i = currentLineStart+1; i < plyBufferUint8.byteLength; i++){
 87 			if(plyBufferUint8[i] == "\n".charCodeAt(0)){
 88 				return i;
 89 			}
 90 		}
 91 		
 92 		return null;
 93 	};
 94 	
 95 	var linesRead = 0;
 96 	var targetOffset = 0;
 97 	var aabb = {
 98 		lx: Infinity,
 99 		ly: Infinity,
100 		lz: Infinity,
101 		ux: -Infinity,
102 		uy: -Infinity,
103 		uz: -Infinity
104 	};
105 	
106 	while(nextLineStart < plyBuffer.byteLength && nextLineStart != null && targetOffset < pointDataView.byteLength ){
107 		nextLineStart = getNextLineStart();
108 		var line = String.fromCharCode.apply(this, plyBufferUint8.subarray(currentLineStart, nextLineStart));
109 		var tokens = line.trim().split(" ");
110 //		if(tokens.length != plyPointAttributes.attributes.length){
111 //			continue;
112 //		}
113 		
114 		for(var j = 0; j < plyPointAttributes.attributes.length; j++){
115 			var pointAttribute = plyPointAttributes.attributes[j];
116 			
117 			if(pointAttribute == PointAttribute.POSITION_CARTESIAN){
118 				var x = parseFloat(tokens.shift());
119 				var y = parseFloat(tokens.shift());
120 				var z = parseFloat(tokens.shift());
121 				pointDataView.setFloat32(targetOffset, x, true);
122 				pointDataView.setFloat32(targetOffset+4, y, true);
123 				pointDataView.setFloat32(targetOffset+8, z, true);
124 				
125 				if(!isNaN(x) && !isNaN(y) && !isNaN(z)){
126 					aabb.lx = Math.min(aabb.lx, x);
127 					aabb.ly = Math.min(aabb.ly, y);
128 					aabb.lz = Math.min(aabb.lz, z);
129 					aabb.ux = Math.max(aabb.ux, x);
130 					aabb.uy = Math.max(aabb.uy, y);
131 					aabb.uz = Math.max(aabb.uz, z);
132 				}
133 				
134 				targetOffset += 12;
135 			}else if(pointAttribute == PointAttribute.NORMAL_FLOATS){
136 				var nx = parseFloat(tokens.shift());
137 				var ny = parseFloat(tokens.shift());
138 				var nz = parseFloat(tokens.shift());
139 				pointDataView.setFloat32(targetOffset, nx, true);
140 				pointDataView.setFloat32(targetOffset+4, ny, true);
141 				pointDataView.setFloat32(targetOffset+8, nz, true);
142 				
143 				targetOffset += 12;
144 			}else if(pointAttribute == PointAttribute.RGB_PACKED){
145 				var r = parseInt(tokens.shift());
146 				var g = parseInt(tokens.shift());
147 				var b = parseInt(tokens.shift());
148 				pointDataView.setUint8(targetOffset, r);
149 				pointDataView.setUint8(targetOffset+1, g);
150 				pointDataView.setUint8(targetOffset+2, b);
151 				pointDataView.setUint8(targetOffset+3, 255);
152 				
153 				targetOffset += 4;
154 			}else if(pointAttribute == PointAttribute.RGBA_PACKED){
155 				var r = parseInt(tokens.shift());
156 				var g = parseInt(tokens.shift());
157 				var b = parseInt(tokens.shift());
158 				var a = parseInt(tokens.shift());
159 				pointDataView.setUint8(targetOffset, r);
160 				pointDataView.setUint8(targetOffset+1, g);
161 				pointDataView.setUint8(targetOffset+2, b);
162 				pointDataView.setUint8(targetOffset+3, a);
163 				targetOffset += 4;
164 			}
165 		}
166 		
167 		linesRead++;
168 		if((linesRead % (1000)) == 0){
169 			var message = {
170 				"type": "progress",
171 				"pointsLoaded": linesRead 
172 			};
173 			postMessage(message);
174 		}
175 		
176 		currentLineStart = nextLineStart;
177 		
178 //		if(linesRead >= 10000){
179 //			break;
180 //		}
181 	}
182 	
183 	
184 	var result = {
185 			"type": "result",
186 			"buffer": pointBuffer,
187 			"pointAttributes": pointAttributes,
188 			"aabb": aabb
189 	};
190 	postMessage(result);
191 	
192 };
193 
194 PlyBinaryWorker.loadFromBinary = function loadFromBinary(plyFile){
195 	var littleEndian = true;
196 	if(plyFile.header.type == PlyFileType.BINARY_LITTLE_ENDIAN){
197 		littleEndian = true;
198 	}else{
199 		littleEndian = false;
200 	}
201 
202 	
203 	var vertexElement = plyFile.header.elements[0];
204 //	vertexElement.size = 10000;
205 	var plyPointAttributes = PlyLoader.pointAttributesFromProperties(vertexElement.properties);
206 	var pointAttributes = PlyLoader.pointAttributesFromProperties(vertexElement.properties, true);
207 	var pointBuffer = new ArrayBuffer(pointAttributes.byteSize * vertexElement.size);
208 	var pointDataView = new DataView(pointBuffer);
209 	var plyDataView = new DataView(plyFile.buffer, plyFile.header.byteSize);
210 	var aabb = {
211 			lx: Infinity,
212 			ly: Infinity,
213 			lz: Infinity,
214 			ux: -Infinity,
215 			uy: -Infinity,
216 			uz: -Infinity
217 		};
218 	
219 	for(var i = 0; i < vertexElement.size; i++){
220 		var targetOffset = i * pointAttributes.byteSize;
221 		var plyOffset = i * plyPointAttributes.byteSize;
222 		for(var j = 0; j < plyPointAttributes.attributes.length; j++){
223 			var pointAttribute = plyPointAttributes.attributes[j];
224 			
225 			if(pointAttribute == PointAttribute.POSITION_CARTESIAN){
226 				var x = plyDataView.getFloat32(plyOffset, littleEndian);
227 				var y = plyDataView.getFloat32(plyOffset+4, littleEndian);
228 				var z = plyDataView.getFloat32(plyOffset+8, littleEndian);
229 				pointDataView.setFloat32(targetOffset, x, true);
230 				pointDataView.setFloat32(targetOffset+4, y, true);
231 				pointDataView.setFloat32(targetOffset+8, z, true);
232 				
233 				if(!isNaN(x) && !isNaN(y) && !isNaN(z)){
234 					aabb.lx = Math.min(aabb.lx, x);
235 					aabb.ly = Math.min(aabb.ly, y);
236 					aabb.lz = Math.min(aabb.lz, z);
237 					aabb.ux = Math.max(aabb.ux, x);
238 					aabb.uy = Math.max(aabb.uy, y);
239 					aabb.uz = Math.max(aabb.uz, z);
240 				}
241 				
242 				targetOffset += 12;
243 				plyOffset += 12;
244 			}else if(pointAttribute == PointAttribute.NORMAL_FLOATS){
245 				pointDataView.setFloat32(targetOffset, plyDataView.getFloat32(plyOffset, littleEndian), true);
246 				pointDataView.setFloat32(targetOffset+4, plyDataView.getFloat32(plyOffset+4, littleEndian), true);
247 				pointDataView.setFloat32(targetOffset+8, plyDataView.getFloat32(plyOffset+8, littleEndian), true);
248 				targetOffset += 12;
249 				plyOffset += 12;
250 			}else if(pointAttribute == PointAttribute.RGB_PACKED){
251 				pointDataView.setUint8(targetOffset, plyDataView.getUint8(plyOffset));
252 				pointDataView.setUint8(targetOffset+1, plyDataView.getUint8(plyOffset+1));
253 				pointDataView.setUint8(targetOffset+2, plyDataView.getUint8(plyOffset+2));
254 				pointDataView.setUint8(targetOffset+3, 255);
255 				targetOffset += 4;
256 				plyOffset += 3;
257 			}else if(pointAttribute == PointAttribute.RGBA_PACKED){
258 				pointDataView.setUint8(targetOffset, plyDataView.getUint8(plyOffset));
259 				pointDataView.setUint8(targetOffset+1, plyDataView.getUint8(plyOffset+1));
260 				pointDataView.setUint8(targetOffset+2, plyDataView.getUint8(plyOffset+2));
261 				pointDataView.setUint8(targetOffset+3, plyDataView.getUint8(plyOffset+3));
262 				targetOffset += 4;
263 				plyOffset += 4;
264 			}else{
265 				plyOffset += pointAttribute.byteSize;
266 			}
267 		}
268 		if(i % (10*1000) == 0){
269 			var message = {
270 				"type": "progress",
271 				"pointsLoaded": i 
272 			};
273 			postMessage(message);
274 		}
275 	}
276 	
277 	var result = {
278 			"type": "result",
279 			"buffer": pointBuffer,
280 			"pointAttributes": pointAttributes,
281 			"aabb": aabb
282 	};
283 	postMessage(result);
284 };
285 
286 PlyBinaryWorker.loadFromBuffer = function PlyLoader_loadFromBuffer(buffer){
287 	var plyFile = new PlyFile(buffer);
288 	var possibleHeader = String.fromCharCode.apply(null, new Uint8Array(buffer, 0, 5000));
289 	var headerLength = possibleHeader.indexOf("end_header") + 11;
290 	if(headerLength == 0){
291 		throw "unable to read ply header";
292 	}
293 	
294 	var strHeader = possibleHeader.substr(0, headerLength);
295 	var plyHeader = PlyLoader.parseHeader(strHeader);
296 	plyFile.header = plyHeader;
297 	postMessage({
298 		"type": "header",
299 		"header": strHeader});
300 	
301 	if(plyFile.header.type == PlyFileType.ASCII){
302 		PlyBinaryWorker.loadFromAscii(plyFile);
303 	}else{
304 		PlyBinaryWorker.loadFromBinary(plyFile);
305 	}
306 };
307