[Solved] Picking area and SelectionBox.js

Dear all.

I am using a three.js scene with hardware instancing implemented. My information comes from CATIA big CATPRODUCTS and we want to optimize the Scene Building process and also have a solution memory optimal. In order to be faster as possible I am using WEBGL2 shaders.

I am putting all instances dependent attributes as IntancedBufferAttributes (InstanceId, color, visible, Transformation Matrix, etc)

I have tried to use the module SelectionBox.js in order to be able to perform picking area selection, but the module doesn’t work with instances.

My idea is to project a plane in the near plane of my PerspectiveCamera and draw a boundingBox taking the 3d box from the square projected to the far plane. The idea is to pass current BoxSelection as (xmin,ymin,zmin),(xmax,ymax,zmax) as an uniform to my Shader and discard fragments if the vertex is not contained in bbox passed as uniform. As this operation is easy to compute in the Vertex Shader as a triple number range condition match, the idea is to compute this in the Vertex and pass a variable to the Fragment Shader which will discard if the variable is zero and display if variable is >0

My main problem is that I don’t know how to project from 2D Canvas square selected by user to the real Box3 in 3D Space Area.

My other problem is how to pass all the PartId’s which patch with the selected area to the CPU in order to send this information in an Event to the application layer in order to synchronize this selection with a JSTREE object in which I can see the Procut Structure of the CATPRODUCT. I think the best option is to pass this information in a shared buffer but I don’t know how to implement it. I have used PBO for single click instance selection in order to avoid the block IO between GPU and CPU but no idea how to use it in this way to pass an array of InstanceId’s

Is this the proper way? Any other better idea?

Could anybody help me?

Many thanks in advanced.

Hi.

FInally I deveped my own selectionHelper and BoxSelection to be working with instances.

Thanks anyway

Just to avoid confusion: Porting a shader to a newer GLSL syntax does not mean it is faster. You have to use WebGL 2 specific features like for example Uniform Buffer Objects do have a performance improvement during rendering. Stuff like instancing or VAOs is also available with WebGL 1.

1 Like

Dear Mugen.

Many thanks for your response.

Finally I got Selection working with instances but for perspective camera.

The point is that I need to make Selection Box working also for ortographic camera.

I attach an example in order you see the problem, as you will see when you double click you activa selection area but with an ortographic camera it is not working.
I think the problem is on the createFrustum function but I am not an exapert like you.

COuld you give a clue?

Many thanks in advanced for your support.

Best regards

Here the code:

three.js webgl - scene animation body { font-family: Monospace; background-color: #f0f0f0; margin: 0px; overflow: hidden; }
		.selectBox {
			border: 1px solid #55aaff;
			background-color: rgba(75, 160, 255, 0.3);
			position: fixed;
		}
	</style>
</head>

<body>

	<div id="container"></div>

	<div id="info">
	<a href="http://threejs.org" target="_blank">three.js</a> webgl - scene animation - <a href="https://clara.io/view/96106133-2e99-40cf-8abd-64defd153e61">Three Gears Scene</a> courtesy of David Sarno</div>

	<script src="https://threejs.org/build/three.js"></script>
	<script src="/libs/Detector.js"></script>
	<script src="https://threejs.org/examples/js/libs/stats.min.js"></script>
	<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
	<script src="/libs/threex.rendererstats.js"></script>
	

	<script id="vertInstanced" type="x-shader/x-vertex">
		precision highp float;
		float EPSILON = 0.3421;
		in float visible;
		in float selected;
		in vec4 lodInfo;
		in vec3 mcol0;
		in vec3 mcol1;
		in vec3 mcol2;
		in vec3 mcol3;
		#ifdef PICKING
			in float pn_UUID;
		#else
			in vec4 color;
		#endif
		out vec3 vPosition;
		out float vFragDepth; 
		out float intensity;    
		out vec4 vColor;
		out float vVisible;
		uniform float logDepthBufFC;
		vec3 lightDirection;
		void main() {
			vVisible = visible;
			if (vVisible > 0.0) {
				mat4 matrix = mat4(
					vec4( mcol0, 0 ),
					vec4( mcol1, 0 ),
					vec4( mcol2, 0 ),
					vec4( mcol3, 1 )
				);
				vec3 tPosition = (matrix * vec4(position, 1.0)).xyz;
				vec3 positionEye = vec3( modelViewMatrix * vec4(tPosition, 1.0 ) ).xyz;
				//vec3 positionEye = ( modelViewMatrix * matrix * vec4( position, 1.0 ) ).xyz;
				vec3 cameraPos1 =vec3( modelViewMatrix * vec4(cameraPosition, 1.0 ) ).xyz;
				
				gl_Position = projectionMatrix * vec4( positionEye, 1.0 );
				vec3 cameraP = (projectionMatrix * vec4(cameraPos1, 1.0)).xyz;
				vFragDepth = 1.0 + gl_Position.w;
				gl_Position.z = log2(max( EPSILON, gl_Position.w + 1.0 )) * logDepthBufFC;
				lightDirection = normalize(vec3(cameraPosition.x-gl_Position.x, cameraPosition.y-gl_Position.y, cameraPosition.z-gl_Position.z));
				intensity =  pow( 1.0 - dot (normal, lightDirection), 1.0);
				vPosition = vec3(gl_Position.x, gl_Position.y, gl_Position.z);
				
				//if ( distance(cameraPos1, positionEye) > lodInfo.a ) {
				//	vVisible = 0.0;
				//}
				
				#ifdef PICKING
					vColor = vec4(tPosition, pn_UUID);
				#else
					if ( selected > 0.0 ) {
						vColor = vec4(1.0, color.g, color.b, 0.5);
					} else {
						vColor = color;
					}
				#endif
			}
		}
	</script>

	<script id="fragInstanced" type="x-shader/x-fragment">
		precision highp float;
		in float vFragDepth;
		in float intensity;
		in float vVisible;
		in vec4 vColor;
		in vec3 vPosition;
		out vec4 myColor;
		uniform float logDepthBufFC;
		void main()	{
			if ( vVisible > 0.0) {
				#if defined(USE_LOGDEPTHBUF) && defined(USE_LOGDEPTHBUF_EXT)                                    
					gl_FragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;                                 
				#endif
				#ifdef PICKING
					myColor = vColor;
				#else
					myColor = vColor;
					myColor.rgb *= intensity * 0.65;
				#endif
			} else {
				discard;
			}
		}
	</script>

	<script>
		var rendererStats = new THREEx.RendererStats()
		var container, stats;
		var selectionBox, helper;
		var isSelecting = false;
		var camera, controls, scene, renderer;
		var objects = [];
		var materialList  = [];
		var geometryList  = [];
		var dummyVector = new THREE.Vector3();
		//var instanceCount  = 10;
		var objectCount  = 0;
		
		var vert = document.getElementById( 'vertInstanced' ).textContent;
		var frag = document.getElementById( 'fragInstanced' ).textContent;
		var material = new THREE.ShaderMaterial( {
			vertexShader: "#version 300 es\n" + vert,
			fragmentShader: "#version 300 es\n" +frag,
			transparent: true,
			side: THREE.DoubleSide,
			clipping: true,
			clipShadows: false
		} );
		materialList.push( material );
		var pickingMaterial = new THREE.ShaderMaterial( {
			vertexShader: "#version 300 es\n" +"#define PICKING\n" + vert,
			fragmentShader: "#version 300 es\n" +"#define PICKING\n" + frag,
			clipping: true,
			clipShadows: false
		} );
		materialList.push( pickingMaterial );
		console.log(materialList);

		// Internal Functions:
		 
		var SelectionHelper = function( selectionBox, renderer, cssClassName ) {
			var selectionHelper = this;
			selectionHelper.isEnabled = isSelecting;
			selectionHelper.element = document.createElement( "div" );
			selectionHelper.element.classList.add( cssClassName );
			selectionHelper.element.style.pointerEvents = "none";
			selectionHelper.renderer = renderer;
			selectionHelper.startPoint = { x: 0, y: 0 };
			selectionHelper.pointTopLeft = { x: 0, y: 0 };
			selectionHelper.pointBottomRight = { x: 0, y: 0 };
			selectionHelper.isDown = false;
			selectionHelper.renderer.domElement.addEventListener( "mousedown", function ( event ) {
				selectionHelper.isDown = true;
				selectionHelper.onSelectStart( event );
			}.bind( selectionHelper ), false );
			selectionHelper.renderer.domElement.addEventListener( "mousemove", function ( event ) {
				if ( selectionHelper.isDown ) {
					selectionHelper.onSelectMove( event );
				}
			}.bind( selectionHelper ), false );
			
			selectionHelper.renderer.domElement.addEventListener( "mouseup", function ( event ) {
				selectionHelper.isDown = false;
				selectionHelper.onSelectOver( event );
			}.bind( selectionHelper ), false );
			
			selectionHelper.onSelectStart = function ( event ) {
				if (isSelecting == true) {
					selectionHelper.renderer.domElement.parentElement.appendChild( selectionHelper.element );
					selectionHelper.element.style.left = event.clientX + "px";
					selectionHelper.element.style.top = event.clientY + "px";
					selectionHelper.element.style.width = "0px";
					selectionHelper.element.style.height = "0px";
					selectionHelper.startPoint.x = event.clientX;
					selectionHelper.startPoint.y = event.clientY;
				}
			}

			selectionHelper.onSelectMove = function ( event ) {
				if (isSelecting == true) {
					selectionHelper.pointBottomRight.x = Math.max( selectionHelper.startPoint.x, event.clientX );
					selectionHelper.pointBottomRight.y = Math.max( selectionHelper.startPoint.y, event.clientY );
					selectionHelper.pointTopLeft.x = Math.min( selectionHelper.startPoint.x, event.clientX );
					selectionHelper.pointTopLeft.y = Math.min( selectionHelper.startPoint.y, event.clientY );
					selectionHelper.element.style.left = selectionHelper.pointTopLeft.x + "px";
					selectionHelper.element.style.top = selectionHelper.pointTopLeft.y + "px";
					selectionHelper.element.style.width = ( selectionHelper.pointBottomRight.x - selectionHelper.pointTopLeft.x ) + "px";
					selectionHelper.element.style.height = ( selectionHelper.pointBottomRight.y - selectionHelper.pointTopLeft.y ) + "px";
				}
			}

			selectionHelper.onSelectOver = function ( event ) {
				if (isSelecting == true) {
					selectionHelper.element.parentElement.removeChild( selectionHelper.element );
				}
			}
		}

		var SelectionBox = function( camera, scene, deep ) {
			var selectionBox = this;
			selectionBox.camera = camera;
			selectionBox.scene = scene;
			selectionBox.startPoint = new THREE.Vector3();
			selectionBox.endPoint = new THREE.Vector3();
			selectionBox.collection = [];
			selectionBox.deep = deep || Number.MAX_VALUE;
			
			selectionBox.select = function ( startPoint, endPoint ) {
				selectionBox.startPoint = startPoint || selectionBox.startPoint;
				selectionBox.endPoint = endPoint || selectionBox.endPoint;
				selectionBox.collection = [];
				var boxSelectionFrustum = selectionBox.createFrustum( selectionBox.startPoint, selectionBox.endPoint );
				console.log(boxSelectionFrustum);
				selectionBox.searchChildInFrustum( boxSelectionFrustum, selectionBox.scene );
				console.log("selectionBox.collection:", selectionBox.collection);
				return selectionBox.collection;
			}

			selectionBox.createFrustum = function ( startPoint, endPoint ) {
				console.log("startPoint", startPoint, "endPoint", endPoint);
				startPoint = startPoint || selectionBox.startPoint;
				endPoint = endPoint || selectionBox.endPoint
				
				selectionBox.camera.updateProjectionMatrix();
				selectionBox.camera.updateMatrixWorld();
				selectionBox.camera.updateMatrix();
				
				var tmpPoint = startPoint.clone();
				tmpPoint.x = Math.min( startPoint.x, endPoint.x );
				tmpPoint.y = Math.max( startPoint.y, endPoint.y );
				endPoint.x = Math.max( startPoint.x, endPoint.x );
				endPoint.y = Math.min( startPoint.y, endPoint.y );
				
				var vecNear = selectionBox.camera.position.clone();
				var vecTopLeft = tmpPoint.clone();
				var vecTopRight = new THREE.Vector3( endPoint.x, tmpPoint.y, 0 );
				var vecDownRight = endPoint.clone();
				var vecDownLeft = new THREE.Vector3( tmpPoint.x, endPoint.y, 0 );
				vecTopLeft.unproject( selectionBox.camera );
				vecTopRight.unproject( selectionBox.camera );
				vecDownRight.unproject( selectionBox.camera );
				vecDownLeft.unproject( selectionBox.camera );
				
				var vectemp1 = vecTopLeft.clone().sub( vecNear );
				var vectemp2 = vecTopRight.clone().sub( vecNear );
				var vectemp3 = vecDownRight.clone().sub( vecNear );
				vectemp1.normalize();
				vectemp2.normalize();
				vectemp3.normalize();
				
				vectemp1.multiplyScalar( selectionBox.deep );
				vectemp2.multiplyScalar( selectionBox.deep );
				vectemp3.multiplyScalar( selectionBox.deep );
				vectemp1.add( vecNear );
				vectemp2.add( vecNear );
				vectemp3.add( vecNear );
				
				var planeTop = new THREE.Plane();
				planeTop.setFromCoplanarPoints( vecNear, vecTopLeft, vecTopRight );
				
				var planeRight = new THREE.Plane();
				planeRight.setFromCoplanarPoints( vecNear, vecTopRight, vecDownRight );
				
				var planeDown = new THREE.Plane();
				planeDown.setFromCoplanarPoints( vecDownRight, vecDownLeft, vecNear );
				
				var planeLeft = new THREE.Plane();
				planeLeft.setFromCoplanarPoints( vecDownLeft, vecTopLeft, vecNear );
				
				var planeFront = new THREE.Plane();
				planeFront.setFromCoplanarPoints( vecTopRight, vecDownRight, vecDownLeft );
				
				var planeBack = new THREE.Plane();
				planeBack.setFromCoplanarPoints( vectemp3, vectemp2, vectemp1 );
				planeBack.normal = planeBack.normal.multiplyScalar( -1 );
				
				return new THREE.Frustum( planeTop, planeRight, planeDown, planeLeft, planeFront, planeBack );
			}


			selectionBox.searchChildInFrustum = function ( frustum, scene ) {
				var geometry
				selectionBox.collection = [];
				scene.traverse(function(object) {
					if ( object.geometry != undefined ) {
						//console.log(object.geometry);
						object.geometry.name = object.geometry.uuid;
						object.geometry.computeBoundingSphere();
						object.geometry.computeBoundingBox();
						if (object.geometry.attributes.pn_UUID != undefined) {
							var uuidArray = [];
							var colorArray = [];
							var matrixArray = [];
							for (var i in object.geometry.attributes.pn_UUID.array) {
								var matrix = new THREE.Matrix4();
								matrix.elements = [
									object.geometry.attributes.mcol0.array[i*3], object.geometry.attributes.mcol0.array[(i*3)+1], object.geometry.attributes.mcol0.array[(i*3)+2], 0,
									object.geometry.attributes.mcol1.array[i*3], object.geometry.attributes.mcol1.array[(i*3)+1], object.geometry.attributes.mcol1.array[(i*3)+2], 0,
									object.geometry.attributes.mcol2.array[i*3], object.geometry.attributes.mcol2.array[(i*3)+1], object.geometry.attributes.mcol2.array[(i*3)+2], 0,
									object.geometry.attributes.mcol3.array[i*3], object.geometry.attributes.mcol3.array[(i*3)+1], object.geometry.attributes.mcol3.array[(i*3)+2], 1
								];
								var center = object.geometry.boundingSphere.center.clone().applyMatrix4( matrix );
								//console.log("i:", i, "pn_UUID:", object.geometry.attributes.pn_UUID.array[i]);
								if ( frustum.containsPoint( center ) ) {
									//console.log("frustum.containsPoint:" + object.geometry.attributes.pn_UUID.array[i]);
									uuidArray.push(object.geometry.userData.uuid2IdArray[object.geometry.attributes.pn_UUID.array[i]]);
									colorArray.push([
										object.geometry.attributes.color.array[i*4],
										object.geometry.attributes.color.array[(i*4)+1],
										object.geometry.attributes.color.array[(i*4)+2],
										object.geometry.attributes.color.array[(i*4)+3]
									]);
									var matrix = new THREE.Matrix4();
									matrix.elements = [
										object.geometry.attributes.mcol0.array[i*3], object.geometry.attributes.mcol0.array[(i*3)+1], object.geometry.attributes.mcol0.array[(i*3)+2], 0,
										object.geometry.attributes.mcol1.array[i*3], object.geometry.attributes.mcol1.array[(i*3)+1], object.geometry.attributes.mcol1.array[(i*3)+2], 0,
										object.geometry.attributes.mcol2.array[i*3], object.geometry.attributes.mcol2.array[(i*3)+1], object.geometry.attributes.mcol2.array[(i*3)+2], 0,
										object.geometry.attributes.mcol3.array[i*3], object.geometry.attributes.mcol3.array[(i*3)+1], object.geometry.attributes.mcol3.array[(i*3)+2], 0,
									];
									matrixArray.push(matrix);
									//console.log("object.geometry.attributes.pn_UUID.array[i]", object.geometry.attributes.pn_UUID.array[i]);
								}
							}
							if (uuidArray.length > 0) {
								selectionBox.collection.push( {
									geo: object.geometry,
									id:object.geometry.name,
									uuidArray: uuidArray,
									colorArray: colorArray,
									matrixArray: matrixArray
								});
								console.log(selectionBox.collection);
							}
						}
					}
				});
			}
		}

		var randomizeMatrix = function() {
			var position = new THREE.Vector3();
			var rotation = new THREE.Euler();
			var quaternion = new THREE.Quaternion();
			var scale = new THREE.Vector3();
			return function( matrix ) {
				position.x = Math.random() * 800 - 90;
				position.y = Math.random() * 800 - 90;
				position.z = Math.random() * 800 - 90;
				rotation.x = Math.random() * 2 * Math.PI;
				rotation.y = Math.random() * 2 * Math.PI;
				rotation.z = Math.random() * 2 * Math.PI;
				quaternion.setFromEuler( rotation, false );
				scale.x = scale.y = scale.z = 1;
				matrix.compose( position, quaternion, scale );
			};
		}();
		
		// BEGIN:
		init();
		animate();

		function makeInstanced( geo, instanceCount ) {
			// geometry
			geometryList.push( geo );
			geo.computeBoundingSphere();
			geo.computeBoundingBox();
			geometrySize = geo.boundingBox.getSize(dummyVector);
			console.log("geometrySize:", geometrySize);
			var sizeArray = [geometrySize.x, geometrySize.y, geometrySize.z].sort((a, b) => b - a);
			var occlussionDistance = (sizeArray[0]+ sizeArray[1]) * 0.5 * 500;
			//console.log("occlussionDistance:", occlussionDistance);
			geometryCenter = geo.boundingBox.getCenter(dummyVector);
			//console.log("geometryCenter:", geometryCenter);
			
			var igeo = new THREE.InstancedBufferGeometry();
			igeo.index = geo.index;
			igeo.attributes.position = geo.attributes.position;
			igeo.attributes.normal = geo.attributes.normal;
			igeo.attributes.uv = geo.attributes.uv;
			geometryList.push( igeo );
			
			igeo.userData = {
				uuid2IdArray: []
			}
			
			
			// Instanced Attributes Definition:
			
			// Visible:
			var visible = new THREE.InstancedBufferAttribute( new Float32Array( instanceCount * 1 ), 1, true, 1 );
			
			// Selected:
			var selected = new THREE.InstancedBufferAttribute( new Float32Array( instanceCount * 1 ), 1, true, 1 );
			
			// Absolute Transformation Matrix:
			var mcol0 = new THREE.InstancedBufferAttribute( new Float32Array( instanceCount * 3 ), 3, true, 1 );
			var mcol1 = new THREE.InstancedBufferAttribute( new Float32Array( instanceCount * 3 ), 3, true, 1 );
			var mcol2 = new THREE.InstancedBufferAttribute( new Float32Array( instanceCount * 3 ), 3, true, 1 );
			var mcol3 = new THREE.InstancedBufferAttribute( new Float32Array( instanceCount * 3 ), 3, true, 1 );
			
			// Center and Occlussion Distance of object from camera position:
			var lodInfo = new THREE.InstancedBufferAttribute( new Float32Array( instanceCount * 4 ), 4,true, 1 );
			
			// Scene Colors:
			var colors = new THREE.InstancedBufferAttribute( new Float32Array( instanceCount * 4 ), 4, true, 1 );
			
			// UUID information
			var pn_UUID = new THREE.InstancedBufferAttribute( new Float32Array( instanceCount * 1 ), 1 ,true, 1 );
			
			// Instanced Attributes Assignation:
			var matrix = new THREE.Matrix4();
			var me = matrix.elements;
			console.log(instanceCount);
			for ( var i=0;  i < instanceCount; i ++ ) {
				objectCount ++;
				visible.array[i] = 1;
				
				selected.array[i] = 0;
				
				randomizeMatrix( matrix );
				
				var instanceCenter =  geometryCenter.applyMatrix4(matrix);
				//console.log("instanceCenter:", instanceCenter);
				mcol0.setXYZ( i, me[ 0 ], me[ 1 ], me[ 2 ] );
				mcol1.setXYZ( i, me[ 4 ], me[ 5 ], me[ 6 ] );
				mcol2.setXYZ( i, me[ 8 ], me[ 9 ], me[ 10 ] );
				mcol3.setXYZ( i, me[ 12 ], me[ 13 ], me[ 14 ] );
				
				lodInfo.array[i*4] = instanceCenter.x;
				lodInfo.array[(i*4) + 1] = instanceCenter.y;
				lodInfo.array[(i*4) + 2] = instanceCenter.z;
				lodInfo.array[(i*4) + 3] = occlussionDistance;
				
				//colors.array[i*4] = Math.random();
				//colors.array[(i*4) + 1] = Math.random();
				//colors.array[(i*4) + 2] = Math.random();
				//colors.array[(i*4) + 3] = 1.0;
				
				colors.array[i*4] = 0.0;
				colors.array[(i*4) + 1] = 0.0;
				colors.array[(i*4) + 2] = 1.0;
				colors.array[(i*4) + 3] = 1.0;
				igeo.userData.uuid2IdArray[objectCount] = i;
				pn_UUID.array[i] = objectCount;
			}
			
			// Instanced Attributes addition:
			igeo.addAttribute( 'visible', visible );
			igeo.addAttribute( 'selected', selected );
			igeo.addAttribute( 'mcol0', mcol0 );
			igeo.addAttribute( 'mcol1', mcol1 );
			igeo.addAttribute( 'mcol2', mcol2 );
			igeo.addAttribute( 'mcol3', mcol3 );
			igeo.addAttribute( 'lodInfo', lodInfo );
			igeo.addAttribute( 'color', colors );
			igeo.addAttribute( 'pn_UUID', pn_UUID );
			// mesh
			mesh = new THREE.Mesh( igeo, material );
			mesh.frustumCulled = false;
			console.log(mesh);
			scene.add( mesh );
		}

		function updateInstances(geometry, uuidArray, attribute, value) {
			//console.log("geometry 1:", geometry);
			for (var i in uuidArray) {
				var uuid = uuidArray[i];
				switch (attribute) {
					case "color": // Replaces color in instanced attributes
						if (value.length == 1) {
							geometry.attributes.color.array[uuid*4] = value[0][0];
							geometry.attributes.color.array[(uuid*4)+1] = value[0][1];
							geometry.attributes.color.array[(uuid*4)+2] = value[0][2];
							geometry.attributes.color.array[(uuid*4)+3] = value[0][3];
						} else {
							geometry.attributes.color.array[uuid*4] = value[i][0];
							geometry.attributes.color.array[(uuid*4)+1] = value[i][1];
							geometry.attributes.color.array[(uuid*4)+2] = value[i][2];
							geometry.attributes.color.array[(uuid*4)+3] = value[i][3];
						}
						geometry.attributes.color.needsUpdate = true;
						break;
					case "visible": // Replaces visible in instanced attributes
						if (value.length == 1) {
							geometry.attributes.visible.array[uuid] = value[0];
						} else {
							geometry.attributes.visible.array[uuid] = value[i];
						}
						geometry.attributes.visible.needsUpdate = true;
						break;
					case "tMatrix": // multiplies absmatrix in instanced attributes by passed matrix
						break;
					case "rMatrix": // Replaces absmatrix in instanced attributes
						break;
					case "selected": // Replaces selected in instanced attributes
						if (value.length == 1) {
							geometry.attributes.selected.array[uuid] = value[0];
						} else {
							geometry.attributes.selected.array[uuid] = value[i];
						}
						geometry.attributes.selected.needsUpdate = true;
						break;
					case "pickable": // Replaces color in instanced attributes
						if (value.length == 1) {
							geometry.attributes.pickable.array[uuid] = value[0];
						} else {
							geometry.attributes.pickable.array[uuid] = value[i];
						}
						geometry.attributes.pickable.needsUpdate = true;
						break;
				}
			}
		}

		function switchCamera() {
			if (camera instanceof THREE.PerspectiveCamera) {
				camera = new THREE.OrthographicCamera(
				window.innerWidth / - 16, window.innerWidth / 16,window.innerHeight / 16, window.innerHeight / - 16, -200, 500 );
				camera.position.x = 2;
				camera.position.y = 1;
				camera.position.z = 3;
				camera.lookAt(scene.position);
				this.perspective = "Orthographic";
			} else {
				camera = new THREE.PerspectiveCamera(45,
				window.innerWidth / window.innerHeight, 0.1, 1000);
				camera.position.x = 120;
				camera.position.y = 60;
				camera.position.z = 180;
				camera.lookAt(scene.position);
				this.perspective = "Perspective";
			}
		}


		function init() {
			//container = document.createElement( 'div' );
			//document.body.appendChild( container );
			//camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 5000 );
			
			
			
			//camera = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, 1, 5000 );
			
			//camera = new THREE.OrthographicCamera( 0, window.innerWidth, window.innerHeight,0, 1, 5000 );
			
			frustumSize = 1000;
			var aspect = window.innerWidth / window.innerHeight;
			camera = new THREE.OrthographicCamera( frustumSize * aspect / - 2, frustumSize * aspect / 2, frustumSize / 2, frustumSize / - 2, 0.1, 1000 );
			
			//var w = window.clientWidth;
			//var h = window.clientHeight;
			//var viewSize = h;
			//var aspectRatio = w / h;
            //
			//var _viewport = {
			//	viewSize: viewSize,
			//	aspectRatio: aspectRatio,
			//	left: (-aspectRatio * viewSize) / 2,
			//	right: (aspectRatio * viewSize) / 2,
			//	top: viewSize / 2,
			//	bottom: -viewSize / 2,
			//	near: 0.1,
			//	far: 10000
			//}
            //
			//camera = new THREE.OrthographicCamera ( 
			//	_viewport.left, 
			//	_viewport.right, 
			//	_viewport.top, 
			//	_viewport.bottom, 
			//	_viewport.near, 
			//	_viewport.far 
			//);
			
			
			
			camera.position.z = 1000;
			scene = new THREE.Scene();
			scene.background = new THREE.Color( 0xf0f0f0 );
			//scene.add( new THREE.AmbientLight( 0x505050 ) );
			//var light = new THREE.SpotLight( 0xffffff, 1.5 );
			//light.position.set( 0, 500, 2000 );
			//light.angle = Math.PI / 9;
			//light.castShadow = true;
			//light.shadow.camera.near = 1000;
			//light.shadow.camera.far = 4000;
			//light.shadow.mapSize.width = 1024;
			//light.shadow.mapSize.height = 1024;
			//scene.add( light );
			var geometry1 = new THREE.BoxBufferGeometry( 20, 20, 20 );
			makeInstanced( geometry1, 500 )
			var geometry2 = new THREE.SphereBufferGeometry( 10, 8, 6 );
			makeInstanced( geometry2, 500 )
			var geometry3 = new THREE.CylinderBufferGeometry( 10, 5, 20, 32 );
			makeInstanced( geometry3, 500 )
			var geometry4 = new THREE.CylinderBufferGeometry( 10, 10, 20, 32 );
			makeInstanced( geometry4, 500 )
			container = document.getElementById( "container" );
			var canvas = document.createElement( 'canvas' );
			gl = canvas.getContext( 'webgl2', { antialias: true } );
			gl.getExtension('EXT_color_buffer_float');
			gl.enable(gl.SAMPLE_ALPHA_TO_COVERAGE);
			renderer = new THREE.WebGLRenderer( {  
				logarithmicDepthBuffer: true, 
				canvas: canvas, context: gl 
			} );
			
			renderer.setClearColor( 0xffffff );
			renderer.autoClear = true;
			renderer.setPixelRatio( window.devicePixelRatio );
			renderer.setSize( window.innerWidth, window.innerHeight );
			renderer.sortObjects = true;
			container.appendChild( renderer.domElement );
			
			var info = document.createElement( 'div' );
			info.style.position = 'absolute';
			info.style.top = '0px';
			info.style.width = '100%';
			info.style.textAlign = 'center';
			info.innerHTML = '<a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> webgl - box selection';
			container.appendChild( info );
			//stats = new Stats();
			//container.appendChild( stats.dom );
			
			rendererStats.domElement.style.position	= 'absolute'
			rendererStats.domElement.style.left	= '0px'
			rendererStats.domElement.style.bottom	= '0px'
			document.body.appendChild( rendererStats.domElement )
			
			
			controls = new THREE.OrbitControls( camera, renderer.domElement );
			controls.addEventListener( 'change', render );
			renderer.domElement.addEventListener('dblclick', onDblclickGl, false);
			window.addEventListener( 'resize', onWindowResize, false );
		}

		function onDblclickGl(e){
			console.log(e);
			if (isSelecting == false) {
				isSelecting =true;
				if (controls != undefined) {
					console.log("removing Listener in controls");
					//controls.removeEventListener( 'change', render );
					//controls = undefined;
					controls.enabled = false;
				}
				selectionBox = new SelectionBox( camera, scene );
				helper = new SelectionHelper( selectionBox, renderer, "selectBox" );
			} else {
				isSelecting = false;
				if (selectionBox != undefined && helper != undefined) {
					console.log("aqui");
					console.log(selectionBox, helper);
					//helper.destroy();
					//helper.element = undefined;
				}
				selectionBox = undefined;
				helper = undefined;
				controls.enabled = true;
			}
		}

		function onWindowResize() {
			camera.aspect = window.innerWidth / window.innerHeight;
			camera.updateProjectionMatrix();
			renderer.setSize( window.innerWidth, window.innerHeight );
		}

		function animate() {
			requestAnimationFrame( animate );
			render();
			//stats.update();
			rendererStats.update(renderer);
		}

		function render() {
			renderer.render( scene, camera );
		}

		// Events:
		document.addEventListener("mousedown", function ( event ) {
			console.log("mousedown",isSelecting);
			console.log("geometries:", renderer.info.memory.geometries, 
				"calls:", renderer.info.render.calls, 
				"frame:", renderer.info.render.frame, 
				"lines:", renderer.info.render.lines,
				"points:", renderer.info.render.points, 
				"triangles:", renderer.info.render.triangles 
			);
			if (isSelecting == true) {
				for ( var i in selectionBox.collection ) {
					// Change attributes
					//updateInstances(selectionBox.collection[i].geo, selectionBox.collection[i].uuidArray, "color", [[1,0,0,1]])
					updateInstances(selectionBox.collection[i].geo, selectionBox.collection[i].uuidArray, "selected", [1])
				}
				selectionBox.startPoint.set(
					( event.clientX / window.innerWidth ) * 2 - 1,
					-( event.clientY / window.innerHeight ) * 2 + 1,
					0.01 );
			}
		} );
		
		document.addEventListener( "mousemove", function ( event ) {
			if (isSelecting == true) {
				if ( helper.isDown ) {
					for ( var i = 0; i < selectionBox.collection.length; i++ ) {
						// Reset all objects:
						//updateInstances(selectionBox.collection[i].geo, selectionBox.collection[i].uuidArray, "color", selectionBox.collection[i].colorArray);
						updateInstances(selectionBox.collection[i].geo, selectionBox.collection[i].uuidArray, "selected", [0])
					}
					selectionBox.endPoint.set(
						( event.clientX / window.innerWidth ) * 2 - 1,
						-( event.clientY / window.innerHeight ) * 2 + 1,
						0.5 );
					var allSelected = selectionBox.select();
					for ( var i = 0; i < allSelected.length; i++ ) {
						// Change attributes
						//updateInstances(selectionBox.collection[i].geo, selectionBox.collection[i].uuidArray, "color", [[1,0,0,1]]);
						updateInstances(selectionBox.collection[i].geo, selectionBox.collection[i].uuidArray, "selected", [1])
					}
				}
			}
		} );
		
		document.addEventListener("mouseup", function ( event ) {
			if (isSelecting == true) {
				selectionBox.endPoint.set(
					(event.clientX / window.innerWidth) * 2 - 1,
					-(event.clientY / window.innerHeight) * 2 + 1,
					0.5 );
				var allSelected = selectionBox.select();
				for ( var i = 0; i < allSelected.length; i++ ) {
					// Finally change attrs
					//updateInstances(selectionBox.collection[i].geo, selectionBox.collection[i].uuidArray, "color", [[1,0,0,1]])
					updateInstances(selectionBox.collection[i].geo, selectionBox.collection[i].uuidArray, "selected", [0])
				}
			}
		});
	</script>
</body>