API Docs for:
Show:

File: ../src/picking.js

///@INFO: UNCOMMON
/**
* Picking is used to detect which element is below one pixel (used the GPU) or using raycast
*
* @class Picking
* @namespace LS
* @constructor
*/
var Picking = {

	picking_color_offset: 10, //color difference between picking objects

	/**
	* Renders the pixel and retrieves the color to detect which object it was, slow but accurate
	* @method getNodeAtCanvasPosition
	* @param {number} x in canvas coordinates
	* @param {number} y in canvas coordinates
	* @param {Camera} camera default is all cameras
	* @param {number} layers default is 0xFFFF which is all
	* @param {Scene} scene default is GlobalScene
	*/
	getNodeAtCanvasPosition: function( x, y, camera, layers, scene )
	{
		var instance = this.getInstanceAtCanvasPosition( x, y, camera, layers, scene );
		if(!instance)
			return null;

		if(instance.constructor == LS.SceneNode)
			return instance;

		if(instance._root && instance._root.constructor == LS.SceneNode)
			return instance._root;

		if(instance.node)
			return instance.node;

		return null;
	},

	/**
	* Returns the instance under a screen position
	* @method getInstanceAtCanvasPosition
	* @param {number} x in canvas coordinates
	* @param {number} y in canvas coordinates
	* @param {Camera} camera
	* @param {number} layers default is 0xFFFF which is all
	* @param {Scene} scene
	* @return {Object} the info supplied by the picker (usually a SceneNode)
	*/
	getInstanceAtCanvasPosition: function( x, y, camera, layers, scene )
	{
		scene = scene || LS.GlobalScene;

		if(!camera)
			camera = LS.Renderer.getCameraAtPosition( x, y, scene._cameras );

		if(!camera)
			return null;

		this._picking_nodes = {};

		//render all Render Instances
		this.getPickingColorFromBuffer( scene, camera, x,y, layers );

		this._picking_color[3] = 0; //remove alpha, because alpha is always 255
		var id = new Uint32Array(this._picking_color.buffer)[0]; //get only element

		var instance_info = this._picking_nodes[id];
		this._picking_nodes = {};
		return instance_info;
	},	

	/**
	* Returns a color you should use to paint this node during picking rendering
	* you tell what info you want to retrieve associated with this object if it is clicked
	* @method getNextPickingColor
	* @param {*} info
	* @return {vec3} array containing all the RenderInstances that collided with the ray
	*/
	getNextPickingColor: function( info )
	{
		this._picking_next_color_id += this.picking_color_offset;
		var pick_color = new Uint32Array(1); //store four bytes number
		pick_color[0] = this._picking_next_color_id; //with the picking color for this object
		var byte_pick_color = new Uint8Array( pick_color.buffer ); //read is as bytes
		//byte_pick_color[3] = 255; //Set the alpha to 1

		this._picking_nodes[ this._picking_next_color_id ] = info;
		return vec4.fromValues( byte_pick_color[0] / 255, byte_pick_color[1] / 255, byte_pick_color[2] / 255, 1 );
	},

	//picking
	_pickingMap: null,
	_picking_color: new Uint8Array(4),
	_picking_depth: 0,
	_picking_next_color_id: 0,
	_picking_nodes: {},
	_picking_render_settings: new RenderSettings(),

	getPickingColorFromBuffer: function( scene, camera, x, y, layers )
	{
		//create texture
		if(this._pickingMap == null || this._pickingMap.width != gl.canvas.width || this._pickingMap.height != gl.canvas.height )
		{
			this._pickingMap = new GL.Texture( gl.canvas.width, gl.canvas.height, { format: gl.RGBA, filter: gl.NEAREST });
			this._pickingFBO = new GL.FBO([this._pickingMap]);
			//LS.ResourcesManager.textures[":picking"] = this._pickingMap; //debug the texture
		}

		//y = gl.canvas.height - y; //reverse Y
		var small_area = true;

		LS.Renderer._current_target = this._pickingMap;

		this._pickingFBO.bind();

			//var viewport = camera.getLocalViewport();
			//camera._real_aspect = viewport[2] / viewport[3];
			//gl.viewport( viewport[0], viewport[1], viewport[2], viewport[3] );

			if(small_area)
			{
				gl.scissor(x-1,y-1,2,2);
				gl.enable(gl.SCISSOR_TEST);
			}

			this.renderPickingBuffer( scene, camera, layers, [x,y] );

			gl.readPixels(x,y,1,1,gl.RGBA,gl.UNSIGNED_BYTE, this._picking_color );

			if(small_area)
				gl.disable(gl.SCISSOR_TEST);

		this._pickingFBO.unbind();

		LS.Renderer._current_target = null; //??? deprecated

		//if(!this._picking_color) this._picking_color = new Uint8Array(4); //debug
		//trace(" END Rendering: ", this._picking_color );
		return this._picking_color;
	},

	renderPickingBuffer: function( scene, camera, layers, mouse_pos )
	{
		if(layers === undefined)
			layers = 0xFFFF;
		var picking_render_settings = this._picking_render_settings;

		LS.Renderer.enableCamera( camera, this._picking_render_settings );

		gl.clearColor(0,0,0,0);
		gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

		this._picking_next_color_id = 0;
		LS.Renderer.setRenderPass( PICKING_PASS );
		picking_render_settings.layers = layers;

		//check instances colliding with cursor using a ray against AABBs
		var instances = null;
		if( mouse_pos ) //not tested yet
		{
			var ray = camera.getRayInPixel( mouse_pos[0], mouse_pos[1] );
			var instances_collisions = LS.Physics.raycastRenderInstances( ray.origin, ray.direction );
			if( instances_collisions )
			{
				instances = Array( instances_collisions.length );
				for(var i = 0; i < instances_collisions.length; ++i)
					instances[i] = instances_collisions[i].instance;
			}
			//console.log("Instances ray collided:", instances_collisions.length);
		}
		else
			instances = scene._instances;

		LS.Renderer.renderInstances( picking_render_settings, instances );

		LEvent.trigger( scene, "renderPicking", mouse_pos );
		LEvent.trigger( LS.Renderer, "renderPicking", mouse_pos );

		LS.Renderer.setRenderPass( COLOR_PASS );
	}
};

LS.Picking = Picking;


//helper
//to visualize picking buffer
//LS.Renderer.registerRenderPass( "color", { id: 1, render_instance: LS.Renderer.renderPickingPassInstance } );