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 function POCRenderQueue(){
 13 	this.byteSize = 0;
 14 	this.nodes = new Object();
 15 	this.nodeList = new Array();
 16 	this.length = 0;
 17 }
 18 
 19 POCRenderQueue.prototype.add = function(node){
 20 	this.nodes[node.id] = node;
 21 	this.nodeList.push(node);
 22 	this.byteSize += node.sizeInBytes();
 23 	this.length++;
 24 };
 25 
 26 POCRenderQueue.prototype.contains = function(node){
 27 	return this.nodes[node.id] != null;
 28 };
 29 
 30 POCRenderQueue.prototype.get = function(index){
 31 	return this.nodeList[index];
 32 };
 33 
 34 /**
 35  * remove nodes from the end until this.byteSize is smaller than byteSize
 36  * @param byteSize
 37  */
 38 POCRenderQueue.prototype.cap = function(byteSize){
 39 	var numNodesToRemove = 0;
 40 	var byteCountDown = this.byteSize;
 41 	while(byteCountDown > byteSize){
 42 		var node = this.nodeList[this.nodeList.length - numNodesToRemove -1];
 43 		numNodesToRemove++;
 44 		byteCountDown -= node.sizeInBytes();
 45 	}
 46 	if(numNodesToRemove < 0){
 47 		this.nodeList.splice(-numNodesToRemove, numNodesToRemove);
 48 	}
 49 	this.length = this.nodeList.length;
 50 };
 51 
 52 POCRenderQueue.prototype.capNodeCount = function(numNodes){
 53 	var num = Math.min(numNodes, this.length);
 54 	
 55 	this.nodeList = this.nodeList.splice(0, num);
 56 	
 57 	this.length = this.nodeList.length;
 58 };
 59 
 60 /**
 61  * Octree Object that might be attached to an PointcloudOctreeSceneNode.
 62  * 
 63  * @class
 64  * @author Markus Sch�tz
 65  */
 66 function PointcloudOctree(){
 67 	this.rootNode = null;
 68 	this.pointAttributes = null;
 69 	// the maximum amount of nodes that will be rendered each frame 
 70 	this.maxRenderingNodes = 500;
 71 	this.nodesRenderedThisFrame = 0;
 72 	this.octreeDir = null;
 73 	this.loadQueue = new Array();
 74 	if(MaterialManager.getMaterial("pointCloud") != null){
 75 		this.material = MaterialManager.getMaterial("pointCloud");
 76 	}
 77 };
 78 
 79 PointcloudOctree.prototype.setMaterial = function(material){
 80 	this.material = material;
 81 };
 82 
 83 PointcloudOctree.prototype.setRootNode = function(rootNode){
 84 	this.rootNode = rootNode;
 85 };
 86 
 87 PointcloudOctree.prototype.setPointAttributes = function(pointAttributes){
 88 	this.pointAttributes = pointAttributes;
 89 };
 90 
 91 PointcloudOctree.visibilityToggle = 0;
 92 
 93 /**
 94  * - updates list of visible and loaded nodes<br>
 95  * - load visible but unloaded nodes<br>
 96  * - notify lru collection of all the visible nodes<br>
 97  */
 98 PointcloudOctree.prototype.prepareRender = function prepareRender(pocSceneNode, camera){
 99 	PointcloudOctree.visibilityToggle = PointcloudOctree.visibilityToggle + 1;
100 	this.renderQueue = new POCRenderQueue();
101 	var stack = new Array();
102 	stack.push(this.rootNode);
103 	
104 	var view = camera.viewMatrix;
105 	var frustum = null;
106 	if(Potree.Settings.frustumCulling){
107 		frustum = camera.frustum;
108 	}
109 	
110 	while(stack.length > 0){
111 		var childStack = new Array();
112 		
113 		while(stack.length > 0 /*&& this.renderQueue.length < 100*/){
114 			var current = stack.pop();
115 			var shouldBeRendered = true;
116 			
117 //			if(PointcloudOctree.visibilityToggle >= 5){
118 			// update aabb transformation only if frustum culling is enabled or node is visible and lod is enabled
119 			if(Potree.Settings.frustumCulling || (shouldBeRendered && Potree.Settings.LOD)){
120 					current.aabb.setTransform(pocSceneNode.globalTransformation);
121 			}
122 			
123 			//FIXME clean this mess
124 			// frustum culling
125 			if(Potree.Settings.frustumCulling){
126 				if(frustum.isOutside(current.aabb)){
127 					shouldBeRendered = false;
128 				}
129 			}
130 //			
131 //			// LOD
132 			if (shouldBeRendered && Potree.Settings.LOD) {
133 				
134 				var vCenter = V3.transform(current.aabb.center, view);
135 				var distSquare = vCenter[0]*vCenter[0] + vCenter[1] * vCenter[1] + vCenter[2] * vCenter[2];
136 				
137 				if (current.level > 1 && distSquare > 10*Potree.Settings.LODMultiplicator*current.aabb.radius* current.aabb.radius) {
138 					// cull nodes that are far away.
139 					current.fadeOut();
140 					shouldBeRendered = false;
141 				}
142 			}
143 			
144 //			if(!current.isFadingOut()){
145 			// schedule loading of missing point cloud data
146 			if (shouldBeRendered && current.pointCloud == null) {
147 				if(!current.isLoading){
148 					this.loadQueue.push(current);
149 				}
150 				continue;
151 			}
152 			
153 			if(current.isLoading){
154 				continue;
155 			}
156 			
157 			// make nodes visible if they're in sight
158 			if(!current.isVisible()){
159 				if(shouldBeRendered){
160 					current.fadeIn();
161 				}
162 				continue;
163 			}
164 //			}
165 			
166 //			PointcloudOctree.visibilityToggle = 10000;
167 //			}else{
168 //				shouldBeRendered = current.shouldBeRendered;
169 //			}
170 			
171 //			if(current.level > 2){
172 //				shouldBeRendered = false;	
173 //			}
174 			
175 			if(shouldBeRendered){
176 				for ( var index in current.children) {
177 //					if(index == 3){
178 						childStack.push(current.children[index]);
179 //					}
180 				}
181 				
182 //				if(current.children[0] != null){
183 //					childStack.push(current.children[0]);
184 //				}
185 				
186 				this.renderQueue.add(current);
187 				PointcloudOctreeNode.lruNodes.touch(current);
188 			}
189 			
190 			current.shouldBeRendered = shouldBeRendered;
191 		}
192 		
193 		stack = childStack;
194 	}
195 	
196 //	if(PointcloudOctree.visibilityToggle >= 10000){
197 //		PointcloudOctree.visibilityToggle = 0;
198 //	}
199 	
200 	this.renderQueue.capNodeCount(200);
201 //	this.renderQueue.cap(PointcloudOctreeNode.memoryThreshold);
202 	
203 	this.processLoadQueue();
204 };
205 
206 
207 /**
208  * remove some data from memory if cache limit has been reached.
209  * 
210  * exceptionsRenderQueue nodes in the renderQueue will not be unloaded
211  */
212 PointcloudOctree.cleanupCache = function(bytesNeeded, exceptionsRenderQueue){
213 	//FIXME fix and readd this!
214 //	var lru = PointcloudOctreeNode.lruNodes;
215 //	for(var i = 0; i < 6; i++){
216 //		if(lru.byteSize + bytesNeeded > PointcloudOctreeNode.memoryThreshold){
217 //			var node = lru.getLRUItem();
218 //			if(node != null && !exceptionsRenderQueue.contains(node)){
219 //				node = lru.removeLRUItem();
220 //				node.unload();
221 //			}
222 //		}
223 //	}
224 };
225 	
226 PointcloudOctree.prototype.render = function(pocSceneNode, camera, lights) {
227 	this.prepareRender(pocSceneNode, camera);
228 	
229 	this.material.render(pocSceneNode, camera, lights);
230 };
231 
232 PointcloudOctree.prototype.addTime = function(time){
233 	this.rootNode.addTime(time);
234 };
235 
236 PointcloudOctree.prototype.processLoadQueue = function(){
237 	var lru = PointcloudOctreeNode.lruNodes;
238 //	if(lru != null){
239 //		debugView.set("loadedNodes: ", lru.size() );
240 //	}
241 	var x = 5;
242 	var bytesNeeded = 0;
243 	
244 	if(this.loadQueue.length > 0){
245 		for(var i = 0; i < Math.min(x, this.loadQueue.length); i++){
246 			bytesNeeded += this.loadQueue[i].sizeInBytes();
247 		}
248 	}
249 	
250 	// free some cache
251 	PointcloudOctree.cleanupCache(bytesNeeded, this.renderQueue);
252 	
253 	// process the first x nodes in the queue.
254 	if(this.loadQueue.length > 0 ){
255 		for(var i = 0; i < Math.min(x, this.loadQueue.length); i++){
256 			if(lru.byteSize + this.loadQueue[i].sizeInBytes() < PointcloudOctreeNode.memoryThreshold){
257 				PointcloudOctreeNode.loadCloudAjax(this.loadQueue[i]);
258 			}
259 		}
260 	}
261 	
262 	// clear loadQueue
263 	this.loadQueue = new Array();
264 };