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 };