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