浏览代码

new THREE.TypedArrayUtils

performant quicksearch +kdtree for typed arrays, including example
Doidel 11 年之前
父节点
当前提交
68a54e5726
共有 3 个文件被更改,包括 1057 次插入0 次删除
  1. 403 0
      examples/js/TypedArrayUtils.js
  2. 251 0
      examples/webgl_nearestneighbour.html
  3. 403 0
      src/extras/TypedArrayUtils.js

+ 403 - 0
examples/js/TypedArrayUtils.js

@@ -0,0 +1,403 @@
+
+THREE.TypedArrayUtils = {};
+
+/**
+ * In-place quicksort for typed arrays (e.g. for Float32Array)
+ * provides fast sorting
+ * useful e.g. for a custom shader and/or BufferGeometry
+ *
+ * @author Roman Bolzern <[email protected]>, 2013
+ * @author I4DS http://www.fhnw.ch/i4ds, 2013
+ * @license MIT License <http://www.opensource.org/licenses/mit-license.php>
+ *
+ * Complexity: http://bigocheatsheet.com/ see Quicksort
+ *
+ * Example: 
+ * points: [x, y, z, x, y, z, x, y, z, ...]
+ * eleSize: 3 //because of (x, y, z)
+ * orderElement: 0 //order according to x
+ */
+
+THREE.TypedArrayUtils.quicksortIP = function(arr, eleSize, orderElement) {
+    var stack = [];
+    var sp = -1;
+    var left = 0;
+    var right = (arr.length) / eleSize - 1;
+	var tmp = 0.0, x = 0, y = 0;
+	var swapF = function(a, b) {
+		a *= eleSize; b *= eleSize;
+		for (y = 0; y < eleSize; y++) {
+			tmp=arr[a + y];
+			arr[a + y]=arr[b + y];
+			arr[b + y]=tmp;
+		}
+	};
+	
+    var i, j, swap = new Float32Array(eleSize), temp = new Float32Array(eleSize);
+    while(true) {
+        if(right - left <= 25){
+            for(j=left+1; j<=right; j++) {
+				for (x = 0; x < eleSize; x++) {
+					swap[x] = arr[j * eleSize + x];
+				}
+                i = j-1;
+                while(i >= left && arr[i * eleSize + orderElement] > swap[orderElement]) {
+					for (x = 0; x < eleSize; x++) {
+						arr[(i+1) * eleSize + x] = arr[i * eleSize + x];
+					}
+					i--;
+                }
+				for (x = 0; x < eleSize; x++) {
+					arr[(i+1) * eleSize + x] = swap[x];
+				}
+            }
+            if(sp == -1)    break;
+            right = stack[sp--]; //?
+            left = stack[sp--];
+        } else {
+            var median = (left + right) >> 1;
+            i = left + 1;
+            j = right;
+			
+			swapF(median, i);
+            //swap = arr[median]; arr[median] = arr[i]; arr[i] = swap;
+            if(arr[left * eleSize + orderElement] > arr[right * eleSize + orderElement]) {
+				swapF(left, right);
+                //swap = arr[left]; arr[left] = arr[right]; arr[right] = swap;
+            } if(arr[i * eleSize + orderElement] > arr[right * eleSize + orderElement]) {
+				swapF(i, right);
+                //swap = arr[i]; arr[i] = arr[right]; arr[right] = swap;
+            } if(arr[left * eleSize + orderElement] > arr[i * eleSize + orderElement]) {
+				swapF(left, i);
+                //swap = arr[left]; arr[left] = arr[i]; arr[i] = swap;
+            }
+			for (x = 0; x < eleSize; x++) {
+				temp[x] = arr[i * eleSize + x];
+			}
+            while(true){
+                do i++; while(arr[i * eleSize + orderElement] < temp[orderElement]);
+                do j--; while(arr[j * eleSize + orderElement] > temp[orderElement]);
+                if(j < i)    break;
+				swapF(i, j);
+                //swap = arr[i]; arr[i] = arr[j]; arr[j] = swap;
+            }
+			for (x = 0; x < eleSize; x++) {
+				arr[(left + 1) * eleSize + x] = arr[j * eleSize + x];
+				arr[j * eleSize + x] = temp[x];
+			}
+            if(right - i + 1 >= j - left){
+                stack[++sp] = i;
+                stack[++sp] = right;
+                right = j - 1;
+            }else{
+                stack[++sp] = left;
+                stack[++sp] = j - 1;
+                left = i;
+            }
+        }
+    }
+    return arr;
+};
+
+
+
+/**
+ * k-d Tree for typed arrays (e.g. for Float32Array), in-place
+ * provides fast nearest neighbour search
+ * useful e.g. for a custom shader and/or BufferGeometry, saves tons of memory
+ * has no insert and remove, only buildup and neares neighbour search
+ *
+ * Based on https://github.com/ubilabs/kd-tree-javascript by Ubilabs
+ *
+ * @author Roman Bolzern <[email protected]>, 2013
+ * @author I4DS http://www.fhnw.ch/i4ds, 2013
+ * @license MIT License <http://www.opensource.org/licenses/mit-license.php>
+ *
+ * Requires typed array quicksort
+ *
+ * Example: 
+ * points: [x, y, z, x, y, z, x, y, z, ...]
+ * metric: function(a, b){	return Math.pow(a[0] - b[0], 2) +  Math.pow(a[1] - b[1], 2) +  Math.pow(a[2] - b[2], 2); }  //Manhatten distance
+ * eleSize: 3 //because of (x, y, z)
+ *
+ * Further information (including mathematical properties)
+ * http://en.wikipedia.org/wiki/Binary_tree
+ * http://en.wikipedia.org/wiki/K-d_tree
+ *
+ * If you want to further minimize performance, remove Node.depth and replace in search algorithm with a traversal to root node
+ */
+
+ THREE.TypedArrayUtils.Kdtree = function (points, metric, eleSize) {
+
+    var self = this;
+	
+	var maxDepth = 0;
+	
+	var getPointSet = function(points, pos) {
+		return points.subarray(pos * eleSize, pos * eleSize + eleSize);
+	};
+    
+    function buildTree(points, depth, parent, pos) {
+      var dim = depth % eleSize,
+        median,
+        node,
+		plength = points.length / eleSize;
+
+	  if (depth > maxDepth) maxDepth = depth;
+		
+      if (plength === 0) return null;
+      if (plength === 1) {
+        return new self.Node(getPointSet(points, 0), depth, parent, pos);
+      }
+
+      THREE.TypedArrayUtils.quicksortIP(points, eleSize, dim);
+	  
+      median = Math.floor(plength / 2);
+	  
+	  //debugger;
+      node = new self.Node(getPointSet(points, median) , depth, parent, median + pos);
+	  node.left = buildTree(points.subarray( 0, median * eleSize), depth + 1, node, pos);
+      node.right = buildTree(points.subarray( (median + 1) * eleSize, points.length), depth + 1, node, pos + median + 1);
+
+      return node;
+    }
+
+	this.root = buildTree(points, 0, null, 0);
+		
+	this.getMaxDepth = function() { return maxDepth; };
+	
+	 /* point: array of size eleSize 
+	    maxNodes: max amount of nodes to return 
+	    maxDistance: maximum distance to point result nodes should have */
+    this.nearest = function (point, maxNodes , maxDistance ) {
+      var i,
+        result,
+        bestNodes;
+
+      bestNodes = new THREE.TypedArrayUtils.Kdtree.BinaryHeap(
+        function (e) { return -e[1]; }
+      );
+
+      function nearestSearch(node) {
+        var bestChild,
+          dimension = node.depth % eleSize,
+          ownDistance = metric(point, node.obj),
+		  linearDistance = 0,
+          otherChild,
+          i,
+          linearPoint = [];
+
+        function saveNode(node, distance) {
+          bestNodes.push([node, distance]);
+          if (bestNodes.size() > maxNodes) {
+            bestNodes.pop();
+          }
+        }
+
+        for (i = 0; i < eleSize; i += 1) {
+          if (i === node.depth % eleSize) {
+            linearPoint[i] = point[i];
+          } else {
+            linearPoint[i] = node.obj[i];
+          }
+        }
+
+        linearDistance = metric(linearPoint, node.obj);
+
+		//if it's a leaf
+        if (node.right === null && node.left === null) {
+          if (bestNodes.size() < maxNodes || ownDistance < bestNodes.peek()[1]) {
+            saveNode(node, ownDistance);
+          }
+          return;
+        }
+
+        if (node.right === null) {
+          bestChild = node.left;
+        } else if (node.left === null) {
+          bestChild = node.right;
+        } else {
+          if (point[dimension] < node.obj[dimension]) {
+            bestChild = node.left;
+          } else {
+            bestChild = node.right;
+          }
+        }
+
+		//recursive search
+        nearestSearch(bestChild);
+
+        if (bestNodes.size() < maxNodes || ownDistance < bestNodes.peek()[1]) {
+          saveNode(node, ownDistance);
+        }
+
+		//if there's still room or the current distance is nearer than the best distance
+        if (bestNodes.size() < maxNodes || Math.abs(linearDistance) < bestNodes.peek()[1]) {
+          if (bestChild === node.left) {
+            otherChild = node.right;
+          } else {
+            otherChild = node.left;
+          }
+          if (otherChild !== null) {
+            nearestSearch(otherChild);
+          }
+        }
+      }
+
+      if (maxDistance) {
+        for (i = 0; i < maxNodes; i += 1) {
+          bestNodes.push([null, maxDistance]);
+        }
+      }
+
+      nearestSearch(self.root);
+
+      result = [];
+
+      for (i = 0; i < maxNodes; i += 1) {
+        if (bestNodes.content[i][0]) {
+          result.push([bestNodes.content[i][0], bestNodes.content[i][1]]);
+        }
+      }
+      return result;
+    };
+	
+  }
+  
+ THREE.TypedArrayUtils.Kdtree.prototype.Node = function(obj, depth, parent, pos) {
+    this.obj = obj;
+    this.left = null;
+    this.right = null;
+    this.parent = parent;
+    this.depth = depth;
+	this.pos = pos;
+ }
+ 
+ 
+
+/**
+ * Binary heap implementation
+ * @author http://eloquentjavascript.net/appendix2.htm
+ */
+  THREE.TypedArrayUtils.Kdtree.BinaryHeap = function(scoreFunction){
+    this.content = [];
+    this.scoreFunction = scoreFunction;
+  }
+  
+  THREE.TypedArrayUtils.Kdtree.BinaryHeap.prototype = {
+    push: function(element) {
+      // Add the new element to the end of the array.
+      this.content.push(element);
+      // Allow it to bubble up.
+      this.bubbleUp(this.content.length - 1);
+    },
+
+    pop: function() {
+      // Store the first element so we can return it later.
+      var result = this.content[0];
+      // Get the element at the end of the array.
+      var end = this.content.pop();
+      // If there are any elements left, put the end element at the
+      // start, and let it sink down.
+      if (this.content.length > 0) {
+        this.content[0] = end;
+        this.sinkDown(0);
+      }
+      return result;
+    },
+
+    peek: function() {
+      return this.content[0];
+    },
+
+    remove: function(node) {
+      var len = this.content.length;
+      // To remove a value, we must search through the array to find
+      // it.
+      for (var i = 0; i < len; i++) {
+        if (this.content[i] == node) {
+          // When it is found, the process seen in 'pop' is repeated
+          // to fill up the hole.
+          var end = this.content.pop();
+          if (i != len - 1) {
+            this.content[i] = end;
+            if (this.scoreFunction(end) < this.scoreFunction(node))
+              this.bubbleUp(i);
+            else
+              this.sinkDown(i);
+          }
+          return;
+        }
+      }
+      throw new Error("Node not found.");
+    },
+
+    size: function() {
+      return this.content.length;
+    },
+
+    bubbleUp: function(n) {
+      // Fetch the element that has to be moved.
+      var element = this.content[n];
+      // When at 0, an element can not go up any further.
+      while (n > 0) {
+        // Compute the parent element's index, and fetch it.
+        var parentN = Math.floor((n + 1) / 2) - 1,
+            parent = this.content[parentN];
+        // Swap the elements if the parent is greater.
+        if (this.scoreFunction(element) < this.scoreFunction(parent)) {
+          this.content[parentN] = element;
+          this.content[n] = parent;
+          // Update 'n' to continue at the new position.
+          n = parentN;
+        }
+        // Found a parent that is less, no need to move it further.
+        else {
+          break;
+        }
+      }
+    },
+
+    sinkDown: function(n) {
+      // Look up the target element and its score.
+      var length = this.content.length,
+          element = this.content[n],
+          elemScore = this.scoreFunction(element);
+
+      while(true) {
+        // Compute the indices of the child elements.
+        var child2N = (n + 1) * 2, child1N = child2N - 1;
+        // This is used to store the new position of the element,
+        // if any.
+        var swap = null;
+        // If the first child exists (is inside the array)...
+        if (child1N < length) {
+          // Look it up and compute its score.
+          var child1 = this.content[child1N],
+              child1Score = this.scoreFunction(child1);
+          // If the score is less than our element's, we need to swap.
+          if (child1Score < elemScore)
+            swap = child1N;
+        }
+        // Do the same checks for the other child.
+        if (child2N < length) {
+          var child2 = this.content[child2N],
+              child2Score = this.scoreFunction(child2);
+          if (child2Score < (swap === null ? elemScore : child1Score)){
+            swap = child2N;
+          }
+        }
+
+        // If the element needs to be moved, swap it, and continue.
+        if (swap !== null) {
+          this.content[n] = this.content[swap];
+          this.content[swap] = element;
+          n = swap;
+        }
+        // Otherwise, we are done.
+        else {
+          break;
+        }
+      }
+    }
+  };

+ 251 - 0
examples/webgl_nearestneighbour.html

@@ -0,0 +1,251 @@
+<html>
+	<head>
+		<meta charset="utf-8">
+		<title>Kunstexploration 3D</title>
+		<style>
+			html, body {
+				width: 100%;
+				height: 100%;
+			}
+
+			body {
+				background-color: #ffffff;
+				margin: 0;
+				overflow: hidden;
+				font-family: arial;
+			}
+
+			#info {
+				text-align: center;
+				padding: 5px;
+				position: absolute;
+				width: 100%;
+				color: white;
+			}
+		</style>
+	</head>
+	<body>
+		
+		<div id="info"><a href="http://threejs.org" target="_blank">three.js</a> webgl - typed arrays - nearest neighbour for 500'000 sprites</div>
+		
+		<script src="../build/three.min.js"></script>
+		<script src="js/TypedArrayUtils.js"></script>
+		<script src="js/controls/FirstPersonControls.js"></script>
+		<script type="x-shader/x-vertex" id="vertexshader">
+			
+			//uniform float zoom;
+		
+			attribute float alpha;
+
+			varying float vAlpha;
+
+			void main() {
+			
+				vAlpha = 1.0 - alpha;
+				
+				vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
+
+				gl_PointSize = 4.0 * ( 300.0 / length( mvPosition.xyz ) );
+
+				gl_Position = projectionMatrix * mvPosition;
+
+			}
+
+		</script>
+
+		<script type="x-shader/x-fragment" id="fragmentshader">
+
+			uniform sampler2D tex1;
+
+			varying float vAlpha;
+
+			void main() {
+
+				gl_FragColor = texture2D(tex1, gl_PointCoord);
+				gl_FragColor.r = (1.0 - gl_FragColor.r) * vAlpha + gl_FragColor.r;
+
+			}
+
+		</script>
+		<script>		
+		
+			var camera, scene, renderer;
+			var geometry, material, mesh;
+			var controls;
+
+			var objects = [];
+
+			var amountOfParticles = 500000, maxDistance = Math.pow(120, 2);
+			var positions, alphas, particles, _particleGeom
+
+			var clock = new THREE.Clock();
+
+			var blocker = document.getElementById( 'blocker' );
+			var instructions = document.getElementById( 'instructions' );
+			
+			
+			function init() {
+
+			    camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1000000);
+
+				scene = new THREE.Scene();
+
+				controls = new THREE.FirstPersonControls( camera );
+				controls.movementSpeed = 100;
+				controls.lookSpeed = 0.1;
+
+				var materials = [
+
+					new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'textures/cube/skybox/px.jpg' ), overdraw: true } ), // right
+					new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'textures/cube/skybox/nx.jpg' ), overdraw: true } ), // left
+					new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'textures/cube/skybox/py.jpg' ), overdraw: true } ), // top
+					new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'textures/cube/skybox/ny.jpg' ), overdraw: true } ), // bottom
+					new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'textures/cube/skybox/pz.jpg' ), overdraw: true } ), // back
+					new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'textures/cube/skybox/nz.jpg' ), overdraw: true } )  // front
+
+				];
+
+				mesh = new THREE.Mesh( new THREE.CubeGeometry( 10000, 10000, 10000, 7, 7, 7 ), new THREE.MeshFaceMaterial( materials ) );
+				mesh.scale.x = - 1;
+				scene.add(mesh);
+				
+				//
+
+				renderer = new THREE.WebGLRenderer(); //Detector.webgl? new THREE.WebGLRenderer(): new THREE.CanvasRenderer()
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+				document.body.appendChild( renderer.domElement );
+
+				
+				
+				//create the custom shader
+				var imagePreviewTexture = THREE.ImageUtils.loadTexture( 'textures/crate.gif');
+				imagePreviewTexture.minFilter = THREE.LinearMipMapLinearFilter;
+				imagePreviewTexture.magFilter = THREE.LinearFilter;
+				
+				pointShaderMaterial = new THREE.ShaderMaterial( {
+					uniforms:       {
+										tex1: { type: "t", value: imagePreviewTexture },
+										zoom: { type: 'f', value: 9.0 },
+									},
+					attributes:     {
+										alpha: { type: 'f', value: null },
+									},
+					vertexShader:   document.getElementById( 'vertexshader' ).textContent,
+					fragmentShader: document.getElementById( 'fragmentshader' ).textContent,
+					transparent: true
+				});
+				
+				
+				//create particles with buffer geometry
+				var distanceFunction = function(a, b){
+					return Math.pow(a[0] - b[0], 2) +  Math.pow(a[1] - b[1], 2) +  Math.pow(a[2] - b[2], 2);
+				};
+				
+				_particleGeom = new THREE.BufferGeometry();
+				_particleGeom.dynamic = false;
+				_particleGeom.attributes = {
+
+					position: {
+						itemSize: 3,
+						array: new Float32Array( amountOfParticles * 3 )
+					},
+					
+					alpha: {
+						itemSize: 1,
+						array: new Float32Array( amountOfParticles ),
+						dynamic: true
+					}
+
+				};
+				positions = _particleGeom.attributes.position.array;
+				alphas = _particleGeom.attributes.alpha.array;
+				
+				particles = new THREE.ParticleSystem( _particleGeom, 
+					pointShaderMaterial
+				);
+				particles.dynamic = true;
+				
+				for (var x = 0; x < amountOfParticles; x++) {
+					positions[ x * 3 + 0 ] = Math.random() * 1000;
+					positions[ x * 3 + 1 ] = Math.random() * 1000;
+					positions[ x * 3 + 2 ] = Math.random() * 1000;
+					alphas[x] = 1.0;
+				}
+				
+				
+				var measureStart = new Date().getTime();
+				
+				//creating the kdtree takes a lot of time to execute, in turn the nearest neighbour search will be much faster
+				kdtree = new THREE.TypedArrayUtils.Kdtree( positions, distanceFunction, 3 );
+				
+				console.log('TIME building kdtree', new Date().getTime() - measureStart);
+				
+				//display particles after the kd-tree was generated and the sorting of the positions-array is done
+				scene.add(particles);
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+				controls.handleResize();
+			}
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+
+				//
+				displayNearest(camera.position);
+
+				controls.update( clock.getDelta() )
+
+				renderer.render( scene, camera );
+
+			}
+			
+			function displayNearest(position) {
+				
+				//take the nearest 200 around him. distance^2 'cause we use the manhattan distance and no square is applied in the distance function
+				var imagePositionsInRange = kdtree.nearest([position.x, position.y, position.z], 100, maxDistance);
+								
+				//We combine the nearest neighbour with a view frustum. Doesn't make sense if we change the sprites not in our view... well maybe it does. Whatever you want.
+				var _frustum = new THREE.Frustum();
+				var _projScreenMatrix = new THREE.Matrix4();
+				camera.matrixWorldInverse.getInverse( camera.matrixWorld );
+
+				_projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
+				_frustum.setFromMatrix( _projScreenMatrix );
+				
+				for ( i = 0, il = imagePositionsInRange.length; i < il; i ++ ) {
+					var object = imagePositionsInRange[i];
+					var objectPoint = new THREE.Vector3(0,0,0);
+					objectPoint.x = object[0].obj[0];
+					objectPoint.y = object[0].obj[1];
+					objectPoint.z = object[0].obj[2];
+					
+					if (_frustum.containsPoint(objectPoint)){
+					
+						var objectIndex = object[0].pos;
+						
+						//set the alpha according to distance
+						alphas[ objectIndex ] = 1.0 / maxDistance * object[1];
+						//update the attribute
+						_particleGeom.attributes.alpha.needsUpdate = true;
+					}
+				}
+			}
+			
+			
+			init();
+			animate();
+		</script>
+	</body>
+</html>

+ 403 - 0
src/extras/TypedArrayUtils.js

@@ -0,0 +1,403 @@
+
+THREE.TypedArrayUtils = {};
+
+/**
+ * In-place quicksort for typed arrays (e.g. for Float32Array)
+ * provides fast sorting
+ * useful e.g. for a custom shader and/or BufferGeometry
+ *
+ * @author Roman Bolzern <[email protected]>, 2013
+ * @author I4DS http://www.fhnw.ch/i4ds, 2013
+ * @license MIT License <http://www.opensource.org/licenses/mit-license.php>
+ *
+ * Complexity: http://bigocheatsheet.com/ see Quicksort
+ *
+ * Example: 
+ * points: [x, y, z, x, y, z, x, y, z, ...]
+ * eleSize: 3 //because of (x, y, z)
+ * orderElement: 0 //order according to x
+ */
+
+THREE.TypedArrayUtils.quicksortIP = function(arr, eleSize, orderElement) {
+    var stack = [];
+    var sp = -1;
+    var left = 0;
+    var right = (arr.length) / eleSize - 1;
+	var tmp = 0.0, x = 0, y = 0;
+	var swapF = function(a, b) {
+		a *= eleSize; b *= eleSize;
+		for (y = 0; y < eleSize; y++) {
+			tmp=arr[a + y];
+			arr[a + y]=arr[b + y];
+			arr[b + y]=tmp;
+		}
+	};
+	
+    var i, j, swap = new Float32Array(eleSize), temp = new Float32Array(eleSize);
+    while(true) {
+        if(right - left <= 25){
+            for(j=left+1; j<=right; j++) {
+				for (x = 0; x < eleSize; x++) {
+					swap[x] = arr[j * eleSize + x];
+				}
+                i = j-1;
+                while(i >= left && arr[i * eleSize + orderElement] > swap[orderElement]) {
+					for (x = 0; x < eleSize; x++) {
+						arr[(i+1) * eleSize + x] = arr[i * eleSize + x];
+					}
+					i--;
+                }
+				for (x = 0; x < eleSize; x++) {
+					arr[(i+1) * eleSize + x] = swap[x];
+				}
+            }
+            if(sp == -1)    break;
+            right = stack[sp--]; //?
+            left = stack[sp--];
+        } else {
+            var median = (left + right) >> 1;
+            i = left + 1;
+            j = right;
+			
+			swapF(median, i);
+            //swap = arr[median]; arr[median] = arr[i]; arr[i] = swap;
+            if(arr[left * eleSize + orderElement] > arr[right * eleSize + orderElement]) {
+				swapF(left, right);
+                //swap = arr[left]; arr[left] = arr[right]; arr[right] = swap;
+            } if(arr[i * eleSize + orderElement] > arr[right * eleSize + orderElement]) {
+				swapF(i, right);
+                //swap = arr[i]; arr[i] = arr[right]; arr[right] = swap;
+            } if(arr[left * eleSize + orderElement] > arr[i * eleSize + orderElement]) {
+				swapF(left, i);
+                //swap = arr[left]; arr[left] = arr[i]; arr[i] = swap;
+            }
+			for (x = 0; x < eleSize; x++) {
+				temp[x] = arr[i * eleSize + x];
+			}
+            while(true){
+                do i++; while(arr[i * eleSize + orderElement] < temp[orderElement]);
+                do j--; while(arr[j * eleSize + orderElement] > temp[orderElement]);
+                if(j < i)    break;
+				swapF(i, j);
+                //swap = arr[i]; arr[i] = arr[j]; arr[j] = swap;
+            }
+			for (x = 0; x < eleSize; x++) {
+				arr[(left + 1) * eleSize + x] = arr[j * eleSize + x];
+				arr[j * eleSize + x] = temp[x];
+			}
+            if(right - i + 1 >= j - left){
+                stack[++sp] = i;
+                stack[++sp] = right;
+                right = j - 1;
+            }else{
+                stack[++sp] = left;
+                stack[++sp] = j - 1;
+                left = i;
+            }
+        }
+    }
+    return arr;
+};
+
+
+
+/**
+ * k-d Tree for typed arrays (e.g. for Float32Array), in-place
+ * provides fast nearest neighbour search
+ * useful e.g. for a custom shader and/or BufferGeometry, saves tons of memory
+ * has no insert and remove, only buildup and neares neighbour search
+ *
+ * Based on https://github.com/ubilabs/kd-tree-javascript by Ubilabs
+ *
+ * @author Roman Bolzern <[email protected]>, 2013
+ * @author I4DS http://www.fhnw.ch/i4ds, 2013
+ * @license MIT License <http://www.opensource.org/licenses/mit-license.php>
+ *
+ * Requires typed array quicksort
+ *
+ * Example: 
+ * points: [x, y, z, x, y, z, x, y, z, ...]
+ * metric: function(a, b){	return Math.pow(a[0] - b[0], 2) +  Math.pow(a[1] - b[1], 2) +  Math.pow(a[2] - b[2], 2); }  //Manhatten distance
+ * eleSize: 3 //because of (x, y, z)
+ *
+ * Further information (including mathematical properties)
+ * http://en.wikipedia.org/wiki/Binary_tree
+ * http://en.wikipedia.org/wiki/K-d_tree
+ *
+ * If you want to further minimize performance, remove Node.depth and replace in search algorithm with a traversal to root node
+ */
+
+ THREE.TypedArrayUtils.Kdtree = function (points, metric, eleSize) {
+
+    var self = this;
+	
+	var maxDepth = 0;
+	
+	var getPointSet = function(points, pos) {
+		return points.subarray(pos * eleSize, pos * eleSize + eleSize);
+	};
+    
+    function buildTree(points, depth, parent, pos) {
+      var dim = depth % eleSize,
+        median,
+        node,
+		plength = points.length / eleSize;
+
+	  if (depth > maxDepth) maxDepth = depth;
+		
+      if (plength === 0) return null;
+      if (plength === 1) {
+        return new self.Node(getPointSet(points, 0), depth, parent, pos);
+      }
+
+      THREE.TypedArrayUtils.quicksortIP(points, eleSize, dim);
+	  
+      median = Math.floor(plength / 2);
+	  
+	  //debugger;
+      node = new self.Node(getPointSet(points, median) , depth, parent, median + pos);
+	  node.left = buildTree(points.subarray( 0, median * eleSize), depth + 1, node, pos);
+      node.right = buildTree(points.subarray( (median + 1) * eleSize, points.length), depth + 1, node, pos + median + 1);
+
+      return node;
+    }
+
+	this.root = buildTree(points, 0, null, 0);
+		
+	this.getMaxDepth = function() { return maxDepth; };
+	
+	 /* point: array of size eleSize 
+	    maxNodes: max amount of nodes to return 
+	    maxDistance: maximum distance to point result nodes should have */
+    this.nearest = function (point, maxNodes , maxDistance ) {
+      var i,
+        result,
+        bestNodes;
+
+      bestNodes = new THREE.TypedArrayUtils.Kdtree.BinaryHeap(
+        function (e) { return -e[1]; }
+      );
+
+      function nearestSearch(node) {
+        var bestChild,
+          dimension = node.depth % eleSize,
+          ownDistance = metric(point, node.obj),
+		  linearDistance = 0,
+          otherChild,
+          i,
+          linearPoint = [];
+
+        function saveNode(node, distance) {
+          bestNodes.push([node, distance]);
+          if (bestNodes.size() > maxNodes) {
+            bestNodes.pop();
+          }
+        }
+
+        for (i = 0; i < eleSize; i += 1) {
+          if (i === node.depth % eleSize) {
+            linearPoint[i] = point[i];
+          } else {
+            linearPoint[i] = node.obj[i];
+          }
+        }
+
+        linearDistance = metric(linearPoint, node.obj);
+
+		//if it's a leaf
+        if (node.right === null && node.left === null) {
+          if (bestNodes.size() < maxNodes || ownDistance < bestNodes.peek()[1]) {
+            saveNode(node, ownDistance);
+          }
+          return;
+        }
+
+        if (node.right === null) {
+          bestChild = node.left;
+        } else if (node.left === null) {
+          bestChild = node.right;
+        } else {
+          if (point[dimension] < node.obj[dimension]) {
+            bestChild = node.left;
+          } else {
+            bestChild = node.right;
+          }
+        }
+
+		//recursive search
+        nearestSearch(bestChild);
+
+        if (bestNodes.size() < maxNodes || ownDistance < bestNodes.peek()[1]) {
+          saveNode(node, ownDistance);
+        }
+
+		//if there's still room or the current distance is nearer than the best distance
+        if (bestNodes.size() < maxNodes || Math.abs(linearDistance) < bestNodes.peek()[1]) {
+          if (bestChild === node.left) {
+            otherChild = node.right;
+          } else {
+            otherChild = node.left;
+          }
+          if (otherChild !== null) {
+            nearestSearch(otherChild);
+          }
+        }
+      }
+
+      if (maxDistance) {
+        for (i = 0; i < maxNodes; i += 1) {
+          bestNodes.push([null, maxDistance]);
+        }
+      }
+
+      nearestSearch(self.root);
+
+      result = [];
+
+      for (i = 0; i < maxNodes; i += 1) {
+        if (bestNodes.content[i][0]) {
+          result.push([bestNodes.content[i][0], bestNodes.content[i][1]]);
+        }
+      }
+      return result;
+    };
+	
+  }
+  
+ THREE.TypedArrayUtils.Kdtree.prototype.Node = function(obj, depth, parent, pos) {
+    this.obj = obj;
+    this.left = null;
+    this.right = null;
+    this.parent = parent;
+    this.depth = depth;
+	this.pos = pos;
+ }
+ 
+ 
+
+/**
+ * Binary heap implementation
+ * @author http://eloquentjavascript.net/appendix2.htm
+ */
+  THREE.TypedArrayUtils.Kdtree.BinaryHeap = function(scoreFunction){
+    this.content = [];
+    this.scoreFunction = scoreFunction;
+  }
+  
+  THREE.TypedArrayUtils.Kdtree.BinaryHeap.prototype = {
+    push: function(element) {
+      // Add the new element to the end of the array.
+      this.content.push(element);
+      // Allow it to bubble up.
+      this.bubbleUp(this.content.length - 1);
+    },
+
+    pop: function() {
+      // Store the first element so we can return it later.
+      var result = this.content[0];
+      // Get the element at the end of the array.
+      var end = this.content.pop();
+      // If there are any elements left, put the end element at the
+      // start, and let it sink down.
+      if (this.content.length > 0) {
+        this.content[0] = end;
+        this.sinkDown(0);
+      }
+      return result;
+    },
+
+    peek: function() {
+      return this.content[0];
+    },
+
+    remove: function(node) {
+      var len = this.content.length;
+      // To remove a value, we must search through the array to find
+      // it.
+      for (var i = 0; i < len; i++) {
+        if (this.content[i] == node) {
+          // When it is found, the process seen in 'pop' is repeated
+          // to fill up the hole.
+          var end = this.content.pop();
+          if (i != len - 1) {
+            this.content[i] = end;
+            if (this.scoreFunction(end) < this.scoreFunction(node))
+              this.bubbleUp(i);
+            else
+              this.sinkDown(i);
+          }
+          return;
+        }
+      }
+      throw new Error("Node not found.");
+    },
+
+    size: function() {
+      return this.content.length;
+    },
+
+    bubbleUp: function(n) {
+      // Fetch the element that has to be moved.
+      var element = this.content[n];
+      // When at 0, an element can not go up any further.
+      while (n > 0) {
+        // Compute the parent element's index, and fetch it.
+        var parentN = Math.floor((n + 1) / 2) - 1,
+            parent = this.content[parentN];
+        // Swap the elements if the parent is greater.
+        if (this.scoreFunction(element) < this.scoreFunction(parent)) {
+          this.content[parentN] = element;
+          this.content[n] = parent;
+          // Update 'n' to continue at the new position.
+          n = parentN;
+        }
+        // Found a parent that is less, no need to move it further.
+        else {
+          break;
+        }
+      }
+    },
+
+    sinkDown: function(n) {
+      // Look up the target element and its score.
+      var length = this.content.length,
+          element = this.content[n],
+          elemScore = this.scoreFunction(element);
+
+      while(true) {
+        // Compute the indices of the child elements.
+        var child2N = (n + 1) * 2, child1N = child2N - 1;
+        // This is used to store the new position of the element,
+        // if any.
+        var swap = null;
+        // If the first child exists (is inside the array)...
+        if (child1N < length) {
+          // Look it up and compute its score.
+          var child1 = this.content[child1N],
+              child1Score = this.scoreFunction(child1);
+          // If the score is less than our element's, we need to swap.
+          if (child1Score < elemScore)
+            swap = child1N;
+        }
+        // Do the same checks for the other child.
+        if (child2N < length) {
+          var child2 = this.content[child2N],
+              child2Score = this.scoreFunction(child2);
+          if (child2Score < (swap === null ? elemScore : child1Score)){
+            swap = child2N;
+          }
+        }
+
+        // If the element needs to be moved, swap it, and continue.
+        if (swap !== null) {
+          this.content[n] = this.content[swap];
+          this.content[swap] = element;
+          n = swap;
+        }
+        // Otherwise, we are done.
+        else {
+          break;
+        }
+      }
+    }
+  };