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 /**
 13  * List of possible ply file types
 14  * 
 15  * @class
 16  */
 17 function PlyFileType(){}
 18 PlyFileType.ASCII = "ascii";
 19 PlyFileType.BINARY_LITTLE_ENDIAN = "binary_little_endian";
 20 PlyFileType.BINARY_BIG_ENDIAN = "binary_big_endian";
 21 
 22 /**
 23  * represents a property definition in a ply file
 24  * 
 25  * @class
 26  * 
 27  */
 28 function PlyProperty(name, type){
 29 	this.name = name;
 30 	this.type = type;
 31 }
 32 
 33 function PlyPropertyDataType(name, size){
 34 	this.name = name;
 35 	this.size = size;
 36 };
 37 
 38 /**
 39  * represent an element in a ply file. (such as vertex/face/...)
 40  * 
 41  * @class
 42  * 
 43  */
 44 function PlyElement(name){
 45 	this.name = name;
 46 	this.properties = new Array();
 47 	this.size = 0;
 48 }
 49 
 50 PlyPropertyDataType.char   = new PlyPropertyDataType("char", 1);
 51 PlyPropertyDataType.uchar  = new PlyPropertyDataType("uchar", 1);
 52 PlyPropertyDataType.short  = new PlyPropertyDataType("short", 2);
 53 PlyPropertyDataType.ushort = new PlyPropertyDataType("ushort", 2);
 54 PlyPropertyDataType.int    = new PlyPropertyDataType("int", 4);
 55 PlyPropertyDataType.uint   = new PlyPropertyDataType("uint", 4);
 56 PlyPropertyDataType.float  = new PlyPropertyDataType("float", 4);
 57 PlyPropertyDataType.double = new PlyPropertyDataType("double", 8);
 58 
 59 /**
 60  * holds ply header data
 61  * 
 62  * @class
 63  * 
 64  */
 65 function PlyHeader(){
 66 	this.type = null;
 67 	this.byteSize = 0;
 68 	this.elements = new Array();
 69 }
 70 
 71 /**
 72  * Contains ply header and a buffer with byte-data 
 73  * 
 74  * @class
 75  */
 76 function PlyFile(buffer){
 77 	this.buffer = buffer;
 78 	this.header = new PlyHeader();
 79 }
 80 
 81 /**
 82  * This listener is called while a ply file is beeing loaded
 83  */
 84 function PlyLoaderListener(){
 85 	
 86 }
 87 
 88 PlyLoaderListener.prototype.finishedLoading = function(pointcloud){
 89 	
 90 };
 91 
 92 PlyLoaderListener.prototype.pointsLoaded = function(numLoadedPoints, numPoints){
 93 	
 94 };
 95 
 96 PlyLoaderListener.prototype.onProgress = function(progress){
 97 	
 98 };
 99 
100 /**
101  * 
102  * @class
103  */
104 function PlyLoader(){
105 	
106 }
107 
108 /**
109  * Loads source in a background task. 
110  * Once loading is finished, listener.finishedLoading(pointCloud) is called.
111  */
112 PlyLoader.load = function(source, listener){
113 	var plyFile = new PlyFile();
114 	var worker = new Worker("src/loader/PlyLoaderWorker.js");
115 	worker.onmessage = function(event){
116 		if(event.data.type == "header"){
117 			plyFile.header = PlyLoader.parseHeader(event.data.header);
118 		}else if(event.data.type == "progress"){
119 			listener.pointsLoaded(event.data.pointsLoaded, plyFile.header.elements[0].size);
120 		}else if(event.data.type == "result"){
121 			var pointBuffer = event.data.buffer;
122 			var aabb = event.data.aabb;
123 			var vertexElement = plyFile.header.elements[0];
124 			var pointAttributes = PlyLoader.pointAttributesFromProperties(vertexElement.properties, true);
125 			var numPoints = pointBuffer.byteLength / pointAttributes.byteSize;
126 			var pointCloud = new PointCloud("test", pointAttributes);
127 			pointCloud.setVertexBufferData(pointBuffer);
128 			pointCloud.size = numPoints;
129 			pointCloud.aabb = new AABB();
130 			var min = V3.$(aabb.lx, aabb.ly, aabb.lz);
131 			var max = V3.$(aabb.ux, aabb.uy, aabb.uz);
132 			pointCloud.aabb.setDimensionByMinMax(min, max);
133 			
134 			listener.finishedLoading(pointCloud);
135 		}else if(event.data.type == "log"){
136 			console.log(event.data.message);
137 		}else{
138 			alert(event.data);
139 		}
140 	};
141 	worker.postMessage(source);
142 };
143 
144 PlyLoader.parseHeader = function PlyLoader_parseHeader(header){
145 	var lines = header.split('\n');
146 	var plyHeader = new PlyHeader();
147 	plyHeader.byteSize = header.length;
148 	var vertexElement = new PlyElement("vertex");
149 	plyHeader.elements.push(vertexElement);
150 	
151 	var formatPattern = /^format (ascii|binary_little_endian).*/;
152 	var elementPattern = /element (\w*) (\d*)/;
153 	var propertyPattern = /property (char|uchar|short|ushort|int|uint|float|double) (\w*)/;
154 	
155 	while(lines.length > 0){
156 	//for(var i = 0; i < lines.length; i++){
157 		var line = lines.shift();
158 		
159 		if(line == "ply"){
160 		}else if(line.search(formatPattern) >= 0){
161 			var result = line.match(formatPattern);
162 			plyHeader.type = PlyFileType[result[1].toUpperCase()];
163 		}else if(line.search(elementPattern) >= 0){
164 			var result = line.match(elementPattern);
165 			var name = result[1];
166 			var count = parseInt(result[2]);
167 			
168 			if(name != "vertex"){
169 				throw "As of now, only ply files with 'vertex' as the first element are supported.";
170 			}
171 			
172 			vertexElement.size = count;
173 			// handle properties
174 			while(lines[0].search(propertyPattern) >= 0){
175 				var result = lines.shift().match(propertyPattern);
176 				var name = result[2];
177 				var type = PlyPropertyDataType[result[1]];
178 				var property = new PlyProperty(name, type);
179 				vertexElement.properties.push(property);
180 			}
181 			break;
182 		}
183 	}
184 	
185 	return plyHeader;
186 };
187 
188 PlyLoader.pointAttributesFromProperties = function PlyLoader_pointAttributesFromProperties(properties, forTargetBuffer){
189 	if(forTargetBuffer === undefined){
190 		forTargetBuffer = false;
191 	}
192 	var pointAttributes = new PointAttributes();
193 	var i = 0; 
194 	while(i < properties.length){
195 		var property = properties[i];
196 		
197 		if(property.name == "x"){
198 			var p0 = property;
199 			var p1 = properties[i+1];
200 			var p2 = properties[i+2];
201 			
202 			if(p1.name != "y" || p2.name != "z"){
203 				throw "unsupported ply format";
204 			}
205 			
206 			if((p0.type.name + p1.type.name + p2.type.name) != "floatfloatfloat"){
207 				throw "unsupported ply format";
208 			}
209 			
210 			pointAttributes.add(PointAttribute.POSITION_CARTESIAN);
211 			i+=3;
212 		}else if(property.name == "nx"){
213 			var p0 = property;
214 			var p1 = properties[i+1];
215 			var p2 = properties[i+2];
216 			
217 			if(p1.name != "ny" || p2.name != "nz"){
218 				throw "unsupported ply format";
219 			}
220 			
221 			if((p0.type.name + p1.type.name + p2.type.name) != "floatfloatfloat"){
222 				throw "unsupported ply format";
223 			}
224 			
225 			pointAttributes.add(PointAttribute.NORMAL_FLOATS);
226 			i+=3;
227 		}else if(property.name == "red"){
228 			var c0 = property;
229 			var c1 = properties[i+1];
230 			var c2 = properties[i+2];
231 			
232 			if(c1.name != "green" || c2.name != "blue"){
233 				throw "unsupported ply format";
234 			}
235 			
236 			if((c0.type.name + c1.type.name + c2.type.name) != "ucharucharuchar"){
237 				throw "unsupported ply format";
238 			}
239 			
240 			if(forTargetBuffer){
241 				pointAttributes.add(PointAttribute.RGBA_PACKED);
242 			}else{
243 				pointAttributes.add(PointAttribute.RGB_PACKED);
244 			}
245 			i+=3;
246 		}else{
247 			
248 			if(!forTargetBuffer){
249 				var attribute = new PointAttribute(PointAttributeNames.FILLER, 
250 						PointAttributeTypes.DATA_TYPE_INT8, property.size);
251 				pointAttributes.add(attribute);
252 			}
253 			i++;
254 		}
255 	}
256 	
257 	return pointAttributes;
258 };
259 
260