API Docs for:
Show:

File: ../src/render/renderer.js


//************************************
/**
* The Renderer is in charge of generating one frame of the scene. Contains all the passes and intermediate functions to create the frame.
*
* @class Renderer
* @namespace LS
* @constructor
*/

//passes
var COLOR_PASS = 1;
var SHADOW_PASS = 2;
var PICKING_PASS = 3;

var Renderer = {

	default_render_settings: new LS.RenderSettings(), //overwritten by the global info or the editor one
	default_material: new LS.StandardMaterial(), //used for objects without material

	render_passes: {}, //used to specify the render function for every kind of render pass (color, shadow, picking, etc)
	renderPassFunction: null, //function to call when rendering instances

	global_aspect: 1, //used when rendering to a texture that doesnt have the same aspect as the screen

	default_point_size: 1, //point size in pixels (could be overwritte by render instances)

	_global_viewport: vec4.create(), //the viewport we have available to render the full frame (including subviewports), usually is the 0,0,gl.canvas.width,gl.canvas.height
	_full_viewport: vec4.create(), //contains info about the full viewport available to render (current texture size or canvas size)

	//temporal info during rendering
	_current_scene: null,
	_current_render_settings: null,
	_current_camera: null,
	_current_target: null, //texture where the image is being rendered
	_current_pass: null,

	_queues: [], //render queues in order

	_main_camera: null,

	_visible_cameras: null,
	_visible_lights: null,
	_visible_instances: null,
	_near_lights: [],
	_active_samples: [],

	//stats
	_frame_cpu_time: 0,
	_rendercalls: 0, //calls to instance.render
	_rendered_instances: 0, //instances processed
	_rendered_passes: 0,
	_frame: 0,

	//settings
	_collect_frequency: 1, //used to reuse info (WIP)

	//reusable locals
	_view_matrix: mat4.create(),
	_projection_matrix: mat4.create(),
	_viewprojection_matrix: mat4.create(),
	_2Dviewprojection_matrix: mat4.create(),

	_temp_matrix: mat4.create(),
	_identity_matrix: mat4.create(),
	_render_uniforms: {},

	_reflection_probes: [],

	//fixed texture slots for global textures
	SHADOWMAP_TEXTURE_SLOT: 6,
	ENVIRONMENT_TEXTURE_SLOT: 5,
	IRRADIANCE_TEXTURE_SLOT: 4,
	LIGHTPROJECTOR_TEXTURE_SLOT: 3,
	LIGHTEXTRA_TEXTURE_SLOT: 2,

	//used in special cases
	BONES_TEXTURE_SLOT: 3,
	MORPHS_TEXTURE_SLOT: 2,
	MORPHS_TEXTURE2_SLOT: 1,

	//called from...
	init: function()
	{
		//this is used in case a texture is missing
		this._black_texture = new GL.Texture(1,1, { pixel_data: [0,0,0,255] });
		this._gray_texture = new GL.Texture(1,1, { pixel_data: [128,128,128,255] });
		this._white_texture = new GL.Texture(1,1, { pixel_data: [255,255,255,255] });
		this._normal_texture = new GL.Texture(1,1, { pixel_data: [128,128,255,255] });
		this._missing_texture = this._gray_texture;
		LS.ResourcesManager.textures[":black"] = this._black_texture;
		LS.ResourcesManager.textures[":gray"] = this._gray_texture;
		LS.ResourcesManager.textures[":white"] = this._white_texture;
		LS.ResourcesManager.textures[":flatnormal"] = this._normal_texture;

		//draw helps rendering debug stuff
		LS.Draw.init();
		LS.Draw.onRequestFrame = function() { LS.GlobalScene.requestFrame(); }

		//enable webglCanvas lib so it is easy to render in 2D
		if(global.enableWebGLCanvas && !gl.canvas.canvas2DtoWebGL_enabled)
			global.enableWebGLCanvas( gl.canvas );

		//there are different render passes, they have different render functions
		this.registerRenderPass( "color", { id: COLOR_PASS, render_instance: this.renderColorPassInstance } );
		this.registerRenderPass( "shadow", { id: SHADOW_PASS, render_instance: this.renderShadowPassInstance } );
		this.registerRenderPass( "picking", { id: PICKING_PASS, render_instance: this.renderPickingPassInstance } );

		// we use fixed slots to avoid changing texture slots all the time
		// from more common to less (to avoid overlappings with material textures)
		// the last slot is reserved for litegl binding stuff
		var max_texture_units = this._max_texture_units = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS );
		this.SHADOWMAP_TEXTURE_SLOT = max_texture_units - 2;
		this.ENVIRONMENT_TEXTURE_SLOT = max_texture_units - 3;
		this.IRRADIANCE_TEXTURE_SLOT = max_texture_units - 4;

		this.LIGHTPROJECTOR_TEXTURE_SLOT = max_texture_units - 5;
		this.LIGHTEXTRA_TEXTURE_SLOT = max_texture_units - 6;

		this.BONES_TEXTURE_SLOT = max_texture_units - 7;
		this.MORPHS_TEXTURE_SLOT = max_texture_units - 8;
		this.MORPHS_TEXTURE2_SLOT = max_texture_units - 9;

		this._active_samples.length = max_texture_units;

		//set render queues
		this.createRenderQueue( LS.RenderQueue.BACKGROUND, LS.RenderQueue.NO_SORT );
		this.createRenderQueue( LS.RenderQueue.GEOMETRY, LS.RenderQueue.SORT_NEAR_TO_FAR );
		this.createRenderQueue( LS.RenderQueue.TRANSPARENT, LS.RenderQueue.SORT_FAR_TO_NEAR );

		//very special queue that must change the renderframecontext before start rendering anything
		this.createRenderQueue( LS.RenderQueue.READBACK_COLOR, LS.RenderQueue.SORT_FAR_TO_NEAR, {
			onStart: function( render_settings, pass ){
				if( LS.RenderFrameContext.current && pass.name === "color" )
					LS.RenderFrameContext.current.cloneBuffers();
			}
		});

		this.createRenderQueue( LS.RenderQueue.OVERLAY, LS.RenderQueue.NO_SORT );

	},

	reset: function()
	{
	},

	//used to store which is the current full viewport available (could be different from the canvas in case is a FBO or the camera has a partial viewport)
	setFullViewport: function(x,y,w,h)
	{
		if(x.constructor === Number)
		{
			this._full_viewport[0] = x; this._full_viewport[1] = y; this._full_viewport[2] = w; this._full_viewport[3] = h;
		}
		else if(x.length)
			this._full_viewport.set(x);
	},

	/**
	* Renders the current scene to the screen
	* Many steps are involved, from gathering info from the scene tree, generating shadowmaps, setup FBOs, render every camera
	* If you want to change the rendering pipeline, do not overwrite this function, try to understand it first, otherwise you will miss lots of features
	*
	* @method render
	* @param {SceneTree} scene
	* @param {RenderSettings} render_settings
	* @param {Array} [cameras=null] if no cameras are specified the cameras are taken from the scene
	*/
	render: function( scene, render_settings, cameras )
	{
		if(!LS.ShadersManager.ready)
			return; //not ready

		render_settings = render_settings || this.default_render_settings;
		this._current_render_settings = render_settings;
		this._current_scene = scene;
		this._main_camera = cameras ? cameras[0] : null;

		//done at the beginning just in case it crashes
		scene._frame += 1;
		this._frame += 1;
		scene._must_redraw = false;

		var start_time = getTime();
		this._rendercalls = 0;
		this._rendered_instances = 0;
		this._rendered_passes = 0;

		//to restore from a possible exception (not fully tested, remove if problem)
		if(!render_settings.ignore_reset)
			LS.RenderFrameContext.reset();

		if(gl.canvas.canvas2DtoWebGL_enabled)
			gl.resetTransform(); //reset 

		//force fullscreen viewport
		if( !render_settings.keep_viewport )
		{
			gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
			this.setFullViewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
		}
		else
			this.setFullViewport( gl.viewport_data );
		this._global_viewport.set( gl.viewport_data );

		//Event: beforeRender used in actions that could affect which info is collected for the rendering
		LEvent.trigger( scene, "beforeRender", render_settings );

		//get render instances, cameras, lights, materials and all rendering info ready (computeVisibility)
		this.processVisibleData( scene, render_settings, cameras );

		//Define the main camera, the camera that should be the most important (used for LOD info, or shadowmaps)
		cameras = cameras || this._visible_cameras;
		this._visible_cameras = cameras; //the cameras being rendered
		this._main_camera = cameras[0];

		//remove the lights that do not lay in front of any camera (this way we avoid creating shadowmaps)
		//TODO

		//Event: renderShadowmaps helps to generate shadowMaps that need some camera info (which could be not accessible during processVisibleData)
		LEvent.trigger(scene, "renderShadows", render_settings );

		//Event: afterVisibility allows to cull objects according to the main camera
		LEvent.trigger(scene, "afterVisibility", render_settings );

		//Event: renderReflections in case some realtime reflections are needed, this is the moment to render them inside textures
		LEvent.trigger(scene, "renderReflections", render_settings );

		//Event: beforeRenderMainPass in case a last step is missing
		LEvent.trigger(scene, "beforeRenderMainPass", render_settings );

		//allows to overwrite renderer
		if(this.custom_renderer && this.custom_renderer.render && render_settings.custom_renderer )
		{
			this.custom_renderer.render( cameras, render_settings );
		}
		else
		{
			//enable FX
			if(render_settings.render_fx)
				LEvent.trigger( scene, "enableFrameContext", render_settings );

			//render
			this.renderFrameCameras( cameras, render_settings );

			//keep original viewport
			if( render_settings.keep_viewport )
				gl.setViewport( this._global_viewport );

			//disable and show FX
			if(render_settings.render_fx)
				LEvent.trigger( scene, "showFrameContext", render_settings );
		}

		if(render_settings.render_gui)
			LEvent.trigger( scene, "renderGUI", render_settings );

		this._frame_cpu_time = getTime() - start_time;

		//Event: afterRender to give closure to some actions
		LEvent.trigger(scene, "afterRender", render_settings );
	},

	/**
	* Calls renderFrame of every camera in the cameras list (triggering the appropiate events)
	*
	* @method renderFrameCameras
	* @param {Array} cameras
	* @param {RenderSettings} render_settings
	*/
	renderFrameCameras: function( cameras, render_settings )
	{
		var scene = this._current_scene;

		//for each camera
		for(var i = 0; i < cameras.length; ++i)
		{
			var current_camera = cameras[i];

			LEvent.trigger(scene, "beforeRenderFrame", render_settings );
			LEvent.trigger(current_camera, "beforeRenderFrame", render_settings );
			LEvent.trigger(current_camera, "enableFrameContext", render_settings );

			//main render
			this.renderFrame( current_camera, render_settings ); 

			LEvent.trigger(current_camera, "showFrameContext", render_settings );
			LEvent.trigger(current_camera, "afterRenderFrame", render_settings );
			LEvent.trigger(scene, "afterRenderFrame", render_settings );
		}
	},

	/**
	* renders the view from one camera to the current viewport (could be the screen or a texture)
	*
	* @method renderFrame
	* @param {Camera} camera 
	* @param {Object} render_settings
	* @param {SceneTree} scene [optional] this can be passed when we are rendering a different scene from LS.GlobalScene (used in renderMaterialPreview)
	*/
	renderFrame: function ( camera, render_settings, scene )
	{
		//get all the data
		if(scene) //in case we use another scene
			this.processVisibleData(scene, render_settings);
		this._current_scene = scene = scene || this._current_scene; //ugly, I know

		//set as active camera and set viewport
		this.enableCamera( camera, render_settings, render_settings.skip_viewport, scene ); 

		//compute the rendering order
		this.sortRenderQueues( camera, render_settings );

		//clear buffer
		this.clearBuffer( camera, render_settings );

		//send before events
		LEvent.trigger(scene, "beforeRenderScene", camera );
		LEvent.trigger(this, "beforeRenderScene", camera );

		//here we render all the instances
		this.renderInstances(render_settings);

		//send after events
		LEvent.trigger( scene, "afterRenderScene", camera );
		LEvent.trigger( this, "afterRenderScene", camera );

		//render helpers (guizmos)
		if(render_settings.render_helpers)
			LEvent.trigger(this, "renderHelpers", camera );
	},

	//shows a RenderFrameContext to the viewport (warning, some components may do it bypassing this function)
	showRenderFrameContext: function( render_frame_context, camera )
	{
		//if( !this._current_render_settings.onPlayer)
		//	return;
		LEvent.trigger(this, "beforeShowFrameContext", render_frame_context );
		render_frame_context.show();
	},

	/**
	* Sets camera as the current camera, sets the viewport according to camera info, updates matrices, and prepares LS.Draw
	*
	* @method enableCamera
	* @param {Camera} camera
	* @param {RenderSettings} render_settings
	*/
	enableCamera: function(camera, render_settings, skip_viewport, scene )
	{
		scene = scene || this._current_scene || LS.GlobalScene;

		LEvent.trigger( camera, "beforeEnabled", render_settings );
		LEvent.trigger( scene, "beforeCameraEnabled", camera );

		//assign viewport manually (shouldnt use camera.getLocalViewport to unify?)
		var startx = this._full_viewport[0];
		var starty = this._full_viewport[1];
		var width = this._full_viewport[2];
		var height = this._full_viewport[3];

		var final_x = Math.floor(width * camera._viewport[0] + startx);
		var final_y = Math.floor(height * camera._viewport[1] + starty);
		var final_width = Math.ceil(width * camera._viewport[2]);
		var final_height = Math.ceil(height * camera._viewport[3]);

		if(!skip_viewport)
		{
			//force fullscreen viewport?
			if(render_settings && render_settings.ignore_viewports )
			{
				camera.final_aspect = this.global_aspect * camera._aspect * (width / height);
				gl.viewport( this._full_viewport[0], this._full_viewport[1], this._full_viewport[2], this._full_viewport[3] );
			}
			else
			{
				camera.final_aspect = this.global_aspect * camera._aspect * (final_width / final_height); //what if we want to change the aspect?
				gl.viewport( final_x, final_y, final_width, final_height );
			}
		}

		camera.updateMatrices();

		//store matrices locally
		mat4.copy( this._view_matrix, camera._view_matrix );
		mat4.copy( this._projection_matrix, camera._projection_matrix );
		mat4.copy( this._viewprojection_matrix, camera._viewprojection_matrix );

		//2D Camera: TODO: MOVE THIS SOMEWHERE ELSE
		mat4.ortho( this._2Dviewprojection_matrix, -1, 1, -1, 1, 1, -1 );

		//set as the current camera
		this._current_camera = camera;

		//Draw allows to render debug info easily
		Draw.reset(); //clear 
		Draw.setCameraPosition( camera.getEye() );
		Draw.setViewProjectionMatrix( this._view_matrix, this._projection_matrix, this._viewprojection_matrix );

		LEvent.trigger( camera, "afterEnabled", render_settings );
		LEvent.trigger( scene, "afterCameraEnabled", camera ); //used to change stuff according to the current camera (reflection textures)
	},

	//clear color using camerae info
	clearBuffer: function( camera, render_settings )
	{
		if( render_settings.ignore_clear || (!camera.clear_color && !camera.clear_depth) )
			return;

		//scissors test for the gl.clear, otherwise the clear affects the full viewport
		gl.scissor( gl.viewport_data[0], gl.viewport_data[1], gl.viewport_data[2], gl.viewport_data[3] );
		gl.enable(gl.SCISSOR_TEST);

		//clear color buffer 
		gl.colorMask( true, true, true, true );
		gl.clearColor( camera.background_color[0], camera.background_color[1], camera.background_color[2], camera.background_color[3] );

		//clear depth buffer
		gl.depthMask( true );

		//to clear the stencil
		gl.enable( gl.STENCIL_TEST );
		gl.clearStencil( 0x0 );

		//do the clearing
		gl.clear( ( camera.clear_color ? gl.COLOR_BUFFER_BIT : 0) | (camera.clear_depth ? gl.DEPTH_BUFFER_BIT : 0) | gl.STENCIL_BUFFER_BIT );

		gl.disable( gl.SCISSOR_TEST );
		gl.disable( gl.STENCIL_TEST );
	},

	sortRenderQueues: function( camera, render_settings )
	{
		var instances = this._visible_instances;

		//compute distance to camera
		var camera_eye = camera.getEye();
		for(var i = 0, l = instances.length; i < l; ++i)
		{
			var instance = instances[i];
			if(instance)
				instance._dist = vec3.dist( instance.center, camera_eye );
		}

		//sort queues
		for(var i = 0, l = this._queues.length; i < l; ++i)
		{
			var queue = this._queues[i];
			if(!queue || !queue.sort_mode)
				continue;
			queue.sort();
		}
	},

	/**
	* To set gl state to a known and constant state in every render pass
	*
	* @method resetGLState
	* @param {RenderSettings} render_settings
	*/
	resetGLState: function( render_settings )
	{
		render_settings = render_settings || this._current_render_settings;

		//maybe we should use this function instead
		//LS.RenderState.reset(); 

		gl.enable( gl.CULL_FACE );
		gl.frontFace(gl.CCW);

		gl.colorMask(true,true,true,true);

		gl.enable( gl.DEPTH_TEST );
		gl.depthFunc( gl.LESS );
		gl.depthMask(true);

		gl.disable( gl.BLEND );
		gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA );

		gl.disable( gl.STENCIL_TEST );
		gl.stencilMask( 0xFF );
		gl.stencilOp( gl.KEEP, gl.KEEP, gl.KEEP );
		gl.stencilFunc( gl.ALWAYS, 1, 0xFF );
	},

	/**
	* Calls the render method for every RenderInstance (it also takes into account events and frustrum culling)
	*
	* @method renderInstances
	* @param {RenderSettings} render_settings
	* @param {Array} instances array of RIs, if not specified the last visible_instances are rendered
	*/
	renderInstances: function( render_settings, instances )
	{
		var scene = this._current_scene;
		if(!scene)
		{
			console.warn("LS.Renderer.renderInstances: no scene found");
			return 0;
		}

		this._rendered_passes += 1;

		var pass = this._current_pass;
		var camera = this._current_camera;
		var camera_index_flag = camera._rendering_index != -1 ? (1<<(camera._rendering_index)) : 0;
		var apply_frustum_culling = render_settings.frustum_culling;
		var frustum_planes = camera.updateFrustumPlanes();
		var layers_filter = camera.layers & render_settings.layers;

		LEvent.trigger( scene, "beforeRenderInstances", render_settings );
		//scene.triggerInNodes( "beforeRenderInstances", render_settings );

		//compute global scene info
		this.fillSceneShaderQuery( scene, render_settings );
		this.fillSceneShaderUniforms( scene, render_settings );

		//reset state of everything!
		this.resetGLState( render_settings );

		LEvent.trigger( scene, "renderInstances", render_settings );
		LEvent.trigger( this, "renderInstances", render_settings );

		//reset again!
		this.resetGLState( render_settings );

		var render_instance_func = pass.render_instance;
		if(!render_instance_func)
			return 0;

		var render_instances = instances || this._visible_instances;

		//compute visibility pass
		for(var i = 0, l = render_instances.length; i < l; ++i)
		{
			//render instance
			var instance = render_instances[i];
			var node_flags = instance.node.flags;
			instance._is_visible = false;

			//hidden nodes
			if( pass.id == SHADOW_PASS && !(instance.material.flags.cast_shadows) )
				continue;
			if( pass.id == PICKING_PASS && node_flags.selectable === false )
				continue;
			if( (layers_filter & instance.layers) === 0 )
				continue;

			//done here because sometimes some nodes are moved in this action
			if(instance.onPreRender)
				if( instance.onPreRender( render_settings ) === false)
					continue;

			if(instance.material.opacity <= 0) //TODO: remove this, do it somewhere else
				continue;

			//test visibility against camera frustum
			if( apply_frustum_culling && instance.use_bounding && !instance.material.flags.ignore_frustum )
			{
				if(geo.frustumTestBox( frustum_planes, instance.aabb ) == CLIP_OUTSIDE )
					continue;
			}

			//save visibility info
			instance._is_visible = true;
			if(camera_index_flag) //shadowmap cameras dont have an index
				instance._camera_visibility |= camera_index_flag;
		}

		var start = this._rendered_instances;

		//process render queues
		for(var j = 0; j < this._queues.length; ++j)
		{
			var queue = this._queues[j];
			if(!queue || !queue.instances.length) //empty
				continue;

			//used to change RenderFrameContext stuff (cloning textures for refraction, etc)
			if(queue.onStart)
				if( queue.onStart( render_settings, pass ) === false )
					continue;

			var render_instances = queue.instances;

			//for each render instance
			for(var i = 0, l = render_instances.length; i < l; ++i)
			{
				//render instance
				var instance = render_instances[i];

				if( !instance._is_visible || !instance.mesh )
					continue;

				this._rendered_instances += 1;

				//choose the appropiate render pass
				render_instance_func.call( this, instance, render_settings, pass ); //by default calls renderColorInstance but it could call renderShadowPassInstance

				//some instances do a post render action
				if(instance.onPostRender)
					instance.onPostRender( render_settings );
			}

			if(queue.onFinish)
				queue.onFinish( render_settings, pass );
		}

		this.resetGLState( render_settings );

		LEvent.trigger( scene, "renderScreenSpace", render_settings);

		//restore state
		this.resetGLState( render_settings );

		LEvent.trigger( scene, "afterRenderInstances", render_settings );
		LEvent.trigger( this, "afterRenderInstances", render_settings );

		//and finally again
		this.resetGLState( render_settings );

		return this._rendered_instances - start;
	},

	/**
	* returns a list of all the lights overlapping this instance (it uses sperical bounding so it could returns lights that are not really overlapping)
	* It is used by the multipass lighting to iterate 
	*
	* @method getNearLights
	* @param {RenderInstance} instance the render instance
	* @param {Array} result [optional] the output array
	* @return {Array} array containing a list of LS.Light affecting this RenderInstance
	*/
	getNearLights: function( instance, result )
	{
		result = result || [];

		result.length = 0; //clear old lights

		//it uses the lights gathered by prepareVisibleData
		var lights = this._visible_lights;
		if(!lights || !lights.length)
			return result;

		//Compute lights affecting this RI (by proximity, only takes into account spherical bounding)
		result.length = 0;
		var numLights = lights.length;
		for(var j = 0; j < numLights; j++)
		{
			var light = lights[j];
			//same layer?
			if( (light._root.layers & instance.layers) == 0 || (light._root.layers & this._current_camera.layers) == 0)
				continue;
			var light_intensity = light.computeLightIntensity();
			//light intensity too low?
			if(light_intensity < 0.0001)
				continue;
			var light_radius = light.computeLightRadius();
			var light_pos = light.position;
			//overlapping?
			if( light_radius == -1 || instance.overlapsSphere( light_pos, light_radius ) )
				result.push( light );
		}

		return result;
	},

	//this function is in charge of rendering the regular color pass (used also for reflections)
	renderColorPassInstance: function( instance, render_settings, pass )
	{
		//render instance
		var renderered = false;
		if( instance.material && instance.material.renderInstance )
			renderered = instance.material.renderInstance( instance, render_settings, pass );

		//render using default system (slower but it works)
		if(!renderered)
			this.renderStandardColorMultiPassLightingInstance( instance, render_settings, pass );
	},

	//this function is in charge of rendering an instance in the shadowmap
	renderShadowPassInstance: function( instance, render_settings, pass )
	{
		//render instance
		var renderered = false;
		if( instance.material && instance.material.renderShadowInstance )
			renderered = instance.material.renderShadowInstance( instance, render_settings, pass );

		//render using default system (slower but it works)
		if(!renderered)
			this.renderStandardShadowPassInstance( instance, render_settings, pass);
	},


	/**
	* Renders the RenderInstance taking into account all the lights that affect it and doing a render for every light
	* This function it is not as fast as I would like but enables lots of interesting features
	*
	* @method renderColorMultiPassLightingInstance
	* @param {RenderInstance} instance
	* @param {RenderSettings} render_settings
	* @param {Array} lights array containing al the lights affecting this RI
	*/
	renderStandardColorMultiPassLightingInstance: function( instance, render_settings )
	{
		var camera = this._current_camera;
		var node = instance.node;
		var material = instance.material;
		var scene = this._current_scene;
		var renderer_uniforms = this._render_uniforms;
		var instance_final_query = instance._final_query;
		var lights = this.getNearLights( instance, this._near_lights );

		//compute matrices
		var model = instance.matrix;

		renderer_uniforms.u_model = model; 
		renderer_uniforms.u_normal_model = instance.normal_matrix; 

		//just to be sure
		LS.RenderState.reset();

		//FLAGS: enable GL flags like cull_face, CCW, etc
		material._render_state.enable();
		//this.enableInstanceFlags(instance, render_settings);

		//merge all samplers
		var samplers = [];
		this.mergeSamplers([ scene._samplers, material._samplers, instance.samplers ], samplers);

		//enable samplers and store where [TODO: maybe they are not used..., improve here]
		if(samplers.length)
			this.bindSamplers( samplers );

		//find shader name
		var shader_name = render_settings.default_shader_id;
		if(render_settings.low_quality)
			shader_name = render_settings.default_low_shader_id;
		if( material.shader_name )
			shader_name = material.shader_name;

		//multi pass instance rendering
		var num_lights = lights.length;

		//no lights rendering (flat light)
		var ignore_lights = material.flags.ignore_lights || render_settings.lights_disabled;
		if(!num_lights || ignore_lights)
		{
			var query = new LS.ShaderQuery( shader_name, { FIRST_PASS:"", LAST_PASS:"", USE_AMBIENT_ONLY:"" });
			query.add( scene._query );
			query.add( camera._query );
			query.add( instance_final_query ); //contains node, material and instance macros

			if( ignore_lights )
				query.setMacro( "USE_IGNORE_LIGHTS" );
			if( render_settings.clipping_plane )
				query.setMacro( "USE_CLIPPING_PLANE" );

			if( material.onModifyQuery )
				material.onModifyQuery( query );

			//resolve the shader
			var shader = ShadersManager.resolve( query );

			//assign uniforms
			shader.uniformsArray( [ scene._uniforms, camera._uniforms, material._uniforms, renderer_uniforms, instance.uniforms ] );

			//render
			instance.render( shader );
			this._rendercalls += 1;
			return;
		}

		//Regular rendering (multipass)
		for(var iLight = 0; iLight < num_lights; iLight++)
		{
			var light = lights[iLight];

			var query = new LS.ShaderQuery( shader_name );
			query.add( scene._query );

			var light_query = light.getQuery( instance, render_settings );
			if(iLight === 0)
				query.setMacro("FIRST_PASS");
			if(iLight === (num_lights-1))
				query.setMacro("LAST_PASS");

			query.add( light_query );
			query.add( instance_final_query ); //contains node, material and instance macros

			if( render_settings.clipping_plane )
				query.setMacro("USE_CLIPPING_PLANE");

			if( material.onModifyQuery )
				material.onModifyQuery( query );

			var shader = LS.ShadersManager.resolve( query );

			//light textures like shadowmap or projective texture
			if(light._samplers.length)
				this.bindSamplers( light._samplers );

			//secondary pass flags to make it additive
			if(iLight > 0)
			{
				gl.enable( gl.BLEND );
				gl.blendFunc( gl.SRC_ALPHA, gl.ONE );
				gl.depthFunc( gl.EQUAL );
			}
			//set depth func
			if(material.depth_func)
				gl.depthFunc( gl[material.depth_func] );

			//assign uniforms
			shader.uniformsArray( [ scene._uniforms, camera._uniforms, light._uniforms, material._uniforms, renderer_uniforms, instance.uniforms ] );

			//render the instance
			instance.render( shader );
			this._rendercalls += 1;

			//avoid multipass in simple shaders
			if(shader.global && !shader.global.multipass)
				break; 
		}
	},

	/**
	* Renders this RenderInstance into the shadowmap
	*
	* @method renderShadowPassInstance
	* @param {RenderInstance} instance
	* @param {RenderSettings} render_settings
	*/
	renderStandardShadowPassInstance: function( instance, render_settings )
	{
		var scene = this._current_scene;
		var camera = this._current_camera;
		var node = instance.node;
		var material = instance.material;
		var scene = this._current_scene;
		var renderer_uniforms = this._render_uniforms;

		//compute matrices
		var model = instance.matrix;

		renderer_uniforms.u_model = model; 
		renderer_uniforms.u_normal_model = instance.normal_matrix; 

		var instance_final_query = instance._final_query;

		//FLAGS
		material._render_state.enable();
		//this.enableInstanceFlags( instance, render_settings );

		var query = new ShaderQuery("depth");
		query.add( scene._query );
		query.add( instance_final_query ); //final = node + material + instance

		var shader = LS.ShadersManager.resolve( query );

		var samplers = [];
		this.mergeSamplers([ material._samplers, instance.samplers ], samplers);
		this.bindSamplers( samplers );

		shader.uniformsArray([ scene._uniforms, camera._uniforms, renderer_uniforms, material._uniforms, instance.uniforms ]);

		instance.render(shader);
		this._rendercalls += 1;
	},

	/**
	* Render instance into the picking buffer
	*
	* @method renderPickingInstance
	* @param {RenderInstance} instance
	* @param {RenderSettings} render_settings
	*/
	renderPickingPassInstance: function( instance, render_settings )
	{
		var scene = this._current_scene;
		var camera = this._current_camera;
		var node = instance.node;
		var material = instance.material;
		var model = instance.matrix;
		var renderer_uniforms = this._render_uniforms;

		renderer_uniforms.u_model = model; 
		renderer_uniforms.u_normal_model = instance.normal_matrix; 

		var pick_color = LS.Picking.getNextPickingColor( node );

		var query = new LS.ShaderQuery("flat");
		query.add( scene._query );
		query.add( material._query ); //?
		query.add( instance._final_query );

		var shader = ShadersManager.resolve( query );
		shader.uniformsArray([ scene._uniforms, camera._uniforms, material._uniforms, renderer_uniforms, instance.uniforms ]);
		shader.uniforms({ u_material_color: pick_color });

		instance.render( shader );
	},

	mergeSamplers: function( samplers, result )
	{
		result = result || [];
		result.length = this._max_texture_units;

		for(var i = 0; i < result.length; ++i)
		{
			for(var j = samplers.length - 1; j >= 0; --j)
			{
				if(	samplers[j][i] )
				{
					result[i] = samplers[j][i];
					break;
				}
			}
		}

		return result;
	},

	//to be sure we dont have anything binded
	clearSamplers: function()
	{
		for(var i = 0; i < this._max_texture_units; ++i)
		{
			gl.activeTexture(gl.TEXTURE0 + i);
			gl.bindTexture( gl.TEXTURE_2D, null );
			gl.bindTexture( gl.TEXTURE_CUBE_MAP, null );
			this._active_samples[i] = null;
		}
	},

	bindSamplers: function( samplers )
	{
		if(!samplers)
			return;

		for(var i = 0; i < samplers.length; ++i)
		{
			var sampler = samplers[i];
			if(!sampler) 
				continue;

			//REFACTOR THIS
			var tex = null;
			if(sampler.constructor === String || sampler.constructor === GL.Texture) //old way
			{
				tex = sampler;
				sampler = null;
			}
			else if(sampler.texture)
				tex = sampler.texture;
			else //dont know what this var type is?
			{
				//continue; //if we continue the sampler slot will remain empty which could lead to problems
			}

			if(tex && tex.constructor === String)
				tex = LS.ResourcesManager.textures[ tex ];
			if(!tex)
			{
				if(sampler)
				{
					switch( sampler.missing )
					{
						case "black": tex = this._black_texture; break;
						case "white": tex = this._white_texture; break;
						case "gray": tex = this._gray_texture; break;
						case "normal": tex = this._normal_texture; break;
						default: tex = this._missing_texture;
					}
				}
				else
					tex = this._missing_texture;
			}

			//avoid to read from the same texture we are rendering to (generates warnings)
			if(tex._in_current_fbo) 
				tex = this._missing_texture;

			tex.bind( i );
			this._active_samples[i] = tex;

			//texture properties
			if(sampler)// && sampler._must_update ) //disabled because samplers ALWAYS must set to the value, in case the same texture is used in several places in the scene
			{
				if(sampler.minFilter)
				{
					if( sampler.minFilter !== gl.LINEAR_MIPMAP_LINEAR || (GL.isPowerOfTwo( tex.width ) && GL.isPowerOfTwo( tex.height )) )
						gl.texParameteri(tex.texture_type, gl.TEXTURE_MIN_FILTER, sampler.minFilter);
				}
				if(sampler.magFilter)
					gl.texParameteri(tex.texture_type, gl.TEXTURE_MAG_FILTER, sampler.magFilter);
				if(sampler.wrap)
				{
					gl.texParameteri(tex.texture_type, gl.TEXTURE_WRAP_S, sampler.wrap);
					gl.texParameteri(tex.texture_type, gl.TEXTURE_WRAP_T, sampler.wrap);
				}
				//sampler._must_update = false;
			}
		}
	},

	/**
	* Update the scene shader query according to the render pass
	* Do not reuse the query, they change between rendering passes (shadows, reflections, etc)
	*
	* @method fillSceneShaderQuery
	* @param {SceneTree} scene
	* @param {RenderSettings} render_settings
	*/
	fillSceneShaderQuery: function( scene, render_settings )
	{
		var query = new LS.ShaderQuery();

		//camera info
		if( this._current_pass.id == COLOR_PASS )
		{
			if(render_settings.linear_pipeline)
				query.setMacro("USE_LINEAR_PIPELINE");
		}

		if(this._current_renderframe && this._current_renderframe.use_extra_texture && gl.extensions["WEBGL_draw_buffers"])
			query.setMacro("USE_DRAW_BUFFERS");

		//so components can add stuff (like Fog, etc)
		LEvent.trigger( scene, "fillSceneQuery", query );

		scene._query = query;
	},

	//Called at the beginning of renderInstances (once per renderFrame)
	//DO NOT CACHE, parameters can change between render passes
	fillSceneShaderUniforms: function( scene, render_settings )
	{
		//global uniforms
		var uniforms = {
			u_point_size: this.default_point_size,
			u_time: scene._time || getTime() * 0.001,
			u_ambient_light: scene.info.ambient_color,
			u_viewport: gl.viewport_data
		};

		if(render_settings.clipping_plane)
			uniforms.u_clipping_plane = render_settings.clipping_plane;

		if( this._current_pass.id == COLOR_PASS && render_settings.linear_pipeline )
			uniforms.u_gamma = 2.2;

		scene._uniforms = uniforms;
		scene._samplers = scene._samplers || [];
		scene._samplers.length = 0;

		for(var i in scene.info.textures)
		{
			var texture = LS.getTexture( scene.info.textures[i] );
			if(!texture)
				continue;

			var slot = 0;
			if( i == "environment" )
				slot = LS.Renderer.ENVIRONMENT_TEXTURE_SLOT;
			else if( i == "irradiance" )
				slot = LS.Renderer.IRRADIANCE_TEXTURE_SLOT;
			else
				continue; 

			var type = (texture.texture_type == gl.TEXTURE_2D ? "_texture" : "_cubemap");
			if(texture.texture_type == gl.TEXTURE_2D)
			{
				texture.bind(0);
				texture.setParameter( gl.TEXTURE_MIN_FILTER, gl.LINEAR ); //avoid artifact
			}
			scene._samplers[ slot ] = texture;
			scene._uniforms[ i + type ] = slot;
			scene._query.macros[ "USE_" + (i + type).toUpperCase() ] = "uvs_polar_reflected";
		}

		LEvent.trigger( scene, "fillSceneUniforms", scene._uniforms );
	},	

	/**
	* Collects and process the rendering instances, cameras and lights that are visible
	* Its a prepass shared among all rendering passes
	* Warning: rendering order is computed here, so it is shared among all the cameras (TO DO, move somewhere else)
	*
	* @method processVisibleData
	* @param {SceneTree} scene
	* @param {RenderSettings} render_settings
	* @param {Array} cameras in case you dont want to use the scene cameras
	*/
	processVisibleData: function( scene, render_settings, cameras )
	{
		//options = options || {};
		//options.scene = scene;

		//update info about scene (collecting it all or reusing the one collected in the frame before)
		if( this._frame % this._collect_frequency == 0)
			scene.collectData();
		else
			scene.updateCollectedData();
		LEvent.trigger( scene, "afterCollectData", scene );

		cameras = cameras || scene._cameras;

		//prepare cameras
		for(var i = 0, l = cameras.length; i < l; ++i)
		{
			var camera = cameras[i];
			camera._rendering_index = i;
			camera.prepare();
		}

		//meh!
		if(!this._main_camera)
		{
			if( cameras.length )
				this._main_camera = cameras[0];
			else
				this._main_camera = new LS.Camera(); // ??
		}

		var materials = {}; //I dont want repeated materials here

		var instances = scene._instances;
		var camera = this._main_camera; // || scene.getCamera();
		var camera_eye = camera.getEye();

		//clear render queues
		for(var i = 0; i < this._queues.length; ++i)
			if(this._queues[i])
				this._queues[i].clear();

		//process render instances (add stuff if needed)
		for(var i = 0, l = instances.length; i < l; ++i)
		{
			var instance = instances[i];
			if(!instance)
				continue;

			var node_flags = instance.node.flags;

			if(!instance.mesh)
			{
				console.warn("RenderInstance must always have mesh");
				continue;
			}

			//materials
			if(!instance.material)
				instance.material = this.default_material;
			if( materials[ instance.material.uid ] && instance.material !== materials[ instance.material.uid ] )
				console.warn("Different Materials with same UID");
			materials[ instance.material.uid ] = instance.material;

			//add extra info
			instance._dist = vec3.dist( instance.center, camera_eye );

			//change conditionaly
			if(render_settings.force_wireframe && instance.primitive != gl.LINES ) 
			{
				instance.primitive = gl.LINES;
				if(instance.mesh)
				{
					if(!instance.mesh.indexBuffers["wireframe"])
						instance.mesh.computeWireframe();
					instance.index_buffer = instance.mesh.indexBuffers["wireframe"];
				}
			}

			//add to queues
			var queue_index = instance.material.queue;
			var queue = null;
			if( queue_index === undefined || queue_index === LS.RenderQueue.DEFAULT )
			{
				//TODO: maybe this case should be treated directly in StandardMaterial
				if( instance.material._render_state.blend )
					queue = this._queues[ LS.RenderQueue.TRANSPARENT ];
				else
					queue = this._queues[ LS.RenderQueue.GEOMETRY ];
			}
			else
			{
				queue = this._queues[ queue_index ];
				if(!queue)
					LS.Renderer.createRenderQueue( queue_index );
			}
			if(!queue)
				continue;
			queue.add( instance );

			//node & mesh constant information
			var query = instance.query;

			/* deprecated
			var buffers = instance.vertex_buffers;
			if(!("normals" in buffers))
				query.macros.NO_NORMALS = "";
			if(!("coords" in buffers))
				query.macros.NO_COORDS = "";
			if(("coords1" in buffers))
				query.macros.USE_COORDS1_STREAM = "";
			if(("colors" in buffers))
				query.macros.USE_COLOR_STREAM = "";
			if(("tangents" in buffers))
				query.macros.USE_TANGENT_STREAM = "";
			*/

			instance._camera_visibility = 0|0;
		}

		//update materials info only if they are in use
		if(render_settings.update_materials)
			this._prepareMaterials( materials, scene );

		//pack all macros, uniforms, and samplers relative to this instance in single containers
		for(var i = 0, l = instances.length; i < l; ++i)
		{
			var instance = instances[i];
			var node = instance.node;
			var material = instance.material;
			instance.index = i;

			var query = instance._final_query;
			query.clear();
			query.add( node._query );
			query.add( material._query );
			query.add( instance.query );
		}

		//store all the info
		this._visible_instances = scene._instances;
		this._visible_lights = scene._lights;
		this._visible_cameras = cameras; 
		this._visible_materials = materials;

		//prepare lights (collect data and generate shadowmaps)
		for(var i = 0, l = this._visible_lights.length; i < l; ++i)
			this._visible_lights[i].prepare( render_settings );
	},

	//outside of processVisibleData to allow optimizations in processVisibleData
	_prepareMaterials: function( materials, scene )
	{
		for(var i in materials)
		{
			var material = materials[i];
			material._last_frame_update = this._frame;
			if( material.prepare )
				material.prepare( scene );
		}
	},

	/**
	* Renders a frame into a texture (could be a cubemap, in which case does the six passes)
	*
	* @method renderInstancesToRT
	* @param {Camera} cam
	* @param {Texture} texture
	* @param {RenderSettings} render_settings
	*/
	renderInstancesToRT: function( cam, texture, render_settings )
	{
		render_settings = render_settings || this.default_render_settings;
		this._current_target = texture;
		var scene = LS.Renderer._current_scene;
		texture._in_current_fbo = true;

		if(texture.texture_type == gl.TEXTURE_2D)
		{
			this.enableCamera(cam, render_settings);
			texture.drawTo( inner_draw_2d );
		}
		else if( texture.texture_type == gl.TEXTURE_CUBE_MAP)
			this.renderToCubemap( cam.getEye(), texture.width, texture, render_settings, cam.near, cam.far );
		this._current_target = null;
		texture._in_current_fbo = false;

		function inner_draw_2d()
		{
			LS.Renderer.clearBuffer( cam, render_settings );
			/*
			gl.clearColor(cam.background_color[0], cam.background_color[1], cam.background_color[2], cam.background_color[3] );
			if(render_settings.ignore_clear != true)
				gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
			*/
			//render scene
			LS.Renderer.renderInstances( render_settings );
		}
	},

	/**
	* Renders the current scene to a cubemap centered in the given position
	*
	* @method renderToCubemap
	* @param {vec3} position center of the camera where to render the cubemap
	* @param {number} size texture size
	* @param {Texture} texture to reuse the same texture
	* @param {RenderSettings} render_settings
	* @param {number} near
	* @param {number} far
	* @return {Texture} the resulting texture
	*/
	renderToCubemap: function( position, size, texture, render_settings, near, far, background_color )
	{
		size = size || 256;
		near = near || 1;
		far = far || 1000;

		var eye = position;
		if( !texture || texture.constructor != GL.Texture)
			texture = null;

		var scene = this._current_scene;
		var camera = this._cubemap_camera;
		if(!camera)
			camera = this._cubemap_camera = new LS.Camera();
		camera.configure({ fov: 90, aspect: 1.0, near: near, far: far });

		texture = texture || new GL.Texture(size,size,{texture_type: gl.TEXTURE_CUBE_MAP, minFilter: gl.NEAREST});
		this._current_target = texture;
		texture.drawTo( function(texture, side) {

			var info = LS.Camera.cubemap_camera_parameters[side];
			if(texture._is_shadowmap || !background_color )
				gl.clearColor(0,0,0,0);
			else
				gl.clearColor( background_color[0], background_color[1], background_color[2], background_color[3] );
			gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
			camera.configure({ eye: eye, center: [ eye[0] + info.dir[0], eye[1] + info.dir[1], eye[2] + info.dir[2]], up: info.up });
			LS.Renderer.enableCamera( camera, render_settings, true );
			LS.Renderer.renderInstances( render_settings );
		});

		this._current_target = null;
		return texture;
	},

	/**
	* Renders the material preview to an image (or to the screen)
	*
	* @method renderMaterialPreview
	* @param {Material} material
	* @param {number} size image size
	* @param {Object} options could be environment_texture, to_viewport
	* @param {HTMLCanvas} canvas [optional] the output canvas where to store the preview
	* @return {Image} the preview image (in canvas format) or null if it was rendered to the viewport
	*/
	renderMaterialPreview: function( material, size, options, canvas )
	{
		options = options || {};

		if(!material)
		{
			console.error("No material provided to renderMaterialPreview");
			return;
		}

		//create scene
		var scene = this._material_scene;
		if(!scene)
		{
			scene = this._material_scene = new LS.SceneTree();
			scene.root.camera.background_color.set([0.0,0.0,0.0,0]);
			if(options.environment_texture)
				scene.info.textures.environment = options.environment_texture;
			var node = new LS.SceneNode( "sphere" );
			var compo = new LS.Components.GeometricPrimitive( { size: 40, subdivisions: 50, geometry: LS.Components.GeometricPrimitive.SPHERE } );
			node.addComponent( compo );
			scene.root.addChild( node );
		}

		if(!this._preview_material_render_settings)
			this._preview_material_render_settings = new LS.RenderSettings({ skip_viewport: true, render_helpers: false, update_materials: true });
		var render_settings = this._preview_material_render_settings;

		if(options.background_color)
			scene.root.camera.background_color.set(options.background_color);

		var node = scene.getNode( "sphere");
		if(!node)
		{
			console.error("Node not found in Material Preview Scene");
			return null;
		}

		if(options.rotate)
			node.transform.rotateY( options.rotate );

		var new_material = null;
		if( material.constructor === String )
			new_material = material;
		else
		{
			new_material = new material.constructor();
			new_material.configure( material.serialize() );
		}
		node.material = new_material;

		if(options.to_viewport)
		{
			LS.Renderer.renderFrame( scene.root.camera, render_settings, scene );
			return;
		}

		var tex = this._material_preview_texture || new GL.Texture(size,size);
		if(!this._material_preview_texture)
			this._material_preview_texture = tex;

		tex.drawTo( function()
		{
			//it already clears everything
			//just render
			LS.Renderer.renderFrame( scene.root.camera, render_settings, scene );
		});

		var canvas = tex.toCanvas( canvas, true );
		return canvas;
	},

	/**
	* Returns the last camera that falls into a given screen position
	*
	* @method getCameraAtPosition
	* @param {number} x
	* @param {number} y
	* @param {SceneTree} scene if not specified last rendered scene will be used
	* @return {Camera} the camera
	*/
	getCameraAtPosition: function(x,y, cameras)
	{
		cameras = cameras || this._visible_cameras;
		if(!cameras || !cameras.length)
			return null;

		for(var i = cameras.length - 1; i >= 0; --i)
		{
			var camera = cameras[i];
			if(!camera.enabled || camera.render_to_texture)
				continue;

			if( camera.isPointInCamera(x,y) )
				return camera;
		}
		return null;
	},

	/**
	* Sets the render pass to use, this allow to change between "color","shadow","picking",etc
	*
	* @method setRenderPass
	* @param {String} name name of the render pass as in render_passes
	*/
	setRenderPass: function( name )
	{
		this._current_pass = this.render_passes[ name ] || this.render_passes[ "color" ];
	},

	/**
	* Register a render pass to be used during the rendering
	*
	* @method registerRenderPass
	* @param {String} name name of the render pass as in render_passes
	* @param {Object} info render pass info, { render_instance: Function( instance, render_settings ) }
	*/
	registerRenderPass: function( name, info )
	{
		info.name = name;
		this.render_passes[ name ] = info;
		if(!this._current_pass)
			this._current_pass = info;
	},

	/**
	* Adds a new RenderQueue to the Renderer.
	*
	* @method addRenderQueue
	* @param {RenderQueue} name name of the render pass as in render_passes
	* @param {Number} sorting which algorithm use to sort ( LS.RenderQueue.NO_SORT, LS.RenderQueue.SORT_NEAR_TO_FAR, LS.RenderQueue.SORT_FAR_TO_NEAR )
	* @param {Object} options extra stuff to add to the queue ( like callbacks onStart, onFinish )
	* @return {Number} index of the render queue
	*/
	createRenderQueue: function( index, sorting, options )
	{
		if(index === undefined)
			throw("RenderQueue must have index");
		var queue = new LS.RenderQueue( sorting );
		if( this._queues[ index ] )
			console.warn("There is already a RenderQueue in slot ",index );
		this._queues[ index ] = queue;

		if(options)
			for(var i in options)
				queue[i] = options[i];
	}
};

//Add to global Scope
LS.Renderer = Renderer;