- ///@INFO: BASE
- /** RenderFrameContext
- * This class is used when you want to render the scene not to the screen but to some texture for postprocessing
- * It helps to create the textures and bind them easily, add extra buffers or show it on the screen.
- * Check the FrameFX and CameraFX components to see it in action.
- * Dependencies: LS.Renderer (writes there only)
- *
- * @class RenderFrameContext
- * @namespace LS
- * @constructor
- */
- function RenderFrameContext( o )
- {
- this.width = 0; //0 means the same size as the viewport, negative numbers mean reducing the texture in half N times
- this.height = 0; //0 means the same size as the viewport
- this.precision = RenderFrameContext.DEFAULT_PRECISION; //LOW_PRECISION uses a byte, MEDIUM uses a half_float, HIGH uses a float, or directly the texture type (p.e gl.UNSIGNED_SHORT_4_4_4_4 )
- this.filter_texture = true; //magFilter: in case the texture is shown, do you want to see it pixelated?
- this.format = GL.RGB; //how many color channels, or directly the texture internalformat
- this.use_depth_texture = true; //store the depth in a texture
- this.use_stencil_buffer = false; //add an stencil buffer (cannot be read as a texture in webgl)
- this.num_extra_textures = 0; //number of extra textures in case we want to render to several buffers
- this.name = null; //if a name is provided all the textures will be stored in the LS.ResourcesManager
-
- this.generate_mipmaps = false; //try to generate mipmaps if possible (only when the texture is power of two)
- this.adjust_aspect = false; //when the size doesnt match the canvas size it could look distorted, settings this to true will fix the problem
- this.clone_after_unbind = false; //clones the textures after unbinding it. Used when the texture will be in the 3D scene
-
- this._fbo = null;
- this._color_texture = null;
- this._depth_texture = null;
- this._textures = []; //all color textures (the first will be _color_texture)
- this._cloned_textures = null; //in case we set the clone_after_unbind to true
-
- this._version = 1; //to detect changes
- this._minFilter = gl.NEAREST;
-
- if(o)
- this.configure(o);
- }
-
- RenderFrameContext.current = null;
- RenderFrameContext.stack = [];
-
- RenderFrameContext.DEFAULT_PRECISION = 0; //selected by the renderer
- RenderFrameContext.LOW_PRECISION = 1; //byte
- RenderFrameContext.MEDIUM_PRECISION = 2; //half_float or float
- RenderFrameContext.HIGH_PRECISION = 3; //float
-
- RenderFrameContext.DEFAULT_PRECISION_WEBGL_TYPE = GL.UNSIGNED_BYTE;
-
- RenderFrameContext["@width"] = { type: "number", step: 1, precision: 0 };
- RenderFrameContext["@height"] = { type: "number", step: 1, precision: 0 };
-
- //definitions for the GUI
- RenderFrameContext["@precision"] = { widget: "combo", values: {
- "default": RenderFrameContext.DEFAULT_PRECISION,
- "low": RenderFrameContext.LOW_PRECISION,
- "medium": RenderFrameContext.MEDIUM_PRECISION,
- "high": RenderFrameContext.HIGH_PRECISION
- }
- };
-
- RenderFrameContext["@format"] = { widget: "combo", values: {
- "RGB": GL.RGB,
- "RGBA": GL.RGBA
- }
- };
-
- RenderFrameContext["@num_extra_textures"] = { type: "number", step: 1, min: 0, max: 4, precision: 0 };
- RenderFrameContext["@name"] = { type: "string" };
-
- RenderFrameContext.prototype.clear = function()
- {
- if(this.name)
- {
- for(var i = 0; i < this._textures.length; ++i)
- delete LS.ResourcesManager.textures[ this.name + (i > 1 ? i : "") ];
- if(this._depth_texture)
- delete LS.ResourcesManager.textures[ this.name + "_depth"];
- }
-
- this._fbo = null;
- this._textures = [];
- this._color_texture = null;
- this._depth_textures = null;
- }
-
- RenderFrameContext.prototype.configure = function(o)
- {
- this.width = o.width || 0;
- this.height = o.height || 0;
- this.format = o.format || GL.RGBA;
- this.precision = o.precision || 0;
- this.filter_texture = !!o.filter_texture;
- this.adjust_aspect = !!o.adjust_aspect;
- this.use_depth_texture = !!o.use_depth_texture;
- this.use_stencil_buffer = !!o.use_stencil_buffer;
- this.num_extra_textures = o.num_extra_textures || 0;
- this.name = o.name;
- this.clone_after_unbind = !!o.clone_after_unbind;
- }
-
- RenderFrameContext.prototype.serialize = function()
- {
- return {
- width: this.width,
- height: this.height,
- filter_texture: this.filter_texture,
- precision: this.precision,
- format: this.format,
- adjust_aspect: this.adjust_aspect,
- use_depth_texture: this.use_depth_texture,
- use_stencil_buffer: this.use_stencil_buffer,
- num_extra_textures: this.num_extra_textures,
- clone_after_unbind: this.clone_after_unbind,
- name: this.name
- };
- }
-
- RenderFrameContext.prototype.prepare = function( viewport_width, viewport_height )
- {
- //compute the right size for the textures
- var final_width = this.width;
- var final_height = this.height;
- if(final_width == 0)
- final_width = viewport_width;
- else if(final_width < 0)
- final_width = viewport_width >> Math.abs( this.width ); //subsampling
- if(final_height == 0)
- final_height = viewport_height;
- else if(final_height < 0)
- final_height = viewport_height >> Math.abs( this.height ); //subsampling
-
- var format = this.format;
- var magFilter = this.filter_texture ? gl.LINEAR : gl.NEAREST ;
- var type = 0;
-
- var minFilter = gl.LINEAR;
- if(this.generate_mipmaps && GL.isPowerOfTwo(final_width) && GL.isPowerOfTwo(final_height) )
- minFilter = gl.LINEAR_MIPMAP_LINEAR;
- this._minFilter = minFilter;
-
- switch( this.precision )
- {
- case RenderFrameContext.LOW_PRECISION:
- type = gl.UNSIGNED_BYTE; break;
- case RenderFrameContext.MEDIUM_PRECISION:
- type = gl.HIGH_PRECISION_FORMAT; break; //gl.HIGH_PRECISION_FORMAT is HALF_FLOAT_OES, if not supported then is FLOAT, otherwise is UNSIGNED_BYTE
- case RenderFrameContext.HIGH_PRECISION:
- type = gl.FLOAT; break;
- case RenderFrameContext.DEFAULT_PRECISION:
- type = RenderFrameContext.DEFAULT_PRECISION_WEBGL_TYPE; break;
- default:
- type = this.precision; break; //used for custom formats
- }
-
- var textures = this._textures;
-
- //for the color: check that the texture size matches
- if( !this._color_texture ||
- this._color_texture.width != final_width || this._color_texture.height != final_height ||
- this._color_texture.type != type || this._color_texture.format != format || this._color_texture.minFilter != minFilter )
- this._color_texture = new GL.Texture( final_width, final_height, { minFilter: minFilter, magFilter: magFilter, format: format, type: type });
- else
- this._color_texture.setParameter( gl.TEXTURE_MAG_FILTER, magFilter );
- textures[0] = this._color_texture;
-
- //extra color texture (multibuffer rendering)
- var total_extra = Math.min( this.num_extra_textures, 4 );
-
- //extra buffers not supported in this webgl context
- if(gl.webgl_version == 1 && !gl.extensions["WEBGL_draw_buffers"])
- total_extra = 0;
-
- for(var i = 0; i < total_extra; ++i) //MAX is 4
- {
- var extra_texture = textures[1 + i];
- if( (!extra_texture || extra_texture.width != final_width || extra_texture.height != final_height || extra_texture.type != type || extra_texture.format != format || extra_texture.minFilter != minFilter) )
- extra_texture = new GL.Texture( final_width, final_height, { minFilter: minFilter, magFilter: magFilter, format: format, type: type });
- else
- extra_texture.setParameter( gl.TEXTURE_MAG_FILTER, magFilter );
- textures[1 + i] = extra_texture;
- }
-
- //for the depth
- var depth_format = gl.DEPTH_COMPONENT;
- var depth_type = gl.UNSIGNED_INT;
-
- if(this.use_stencil_buffer && gl.extensions.WEBGL_depth_texture)
- {
- depth_format = gl.DEPTH_STENCIL;
- depth_type = gl.extensions.WEBGL_depth_texture.UNSIGNED_INT_24_8_WEBGL;
- }
-
- if( this.use_depth_texture &&
- (!this._depth_texture || this._depth_texture.width != final_width || this._depth_texture.height != final_height || this._depth_texture.format != depth_format || this._depth_texture.type != depth_type ) &&
- gl.extensions["WEBGL_depth_texture"] )
- this._depth_texture = new GL.Texture( final_width, final_height, { filter: gl.NEAREST, format: depth_format, type: depth_type });
- else if( !this.use_depth_texture )
- this._depth_texture = null;
-
- //we will store some extra info in the depth texture for the near and far plane distances
- if(this._depth_texture)
- {
- if(!this._depth_texture.near_far_planes)
- this._depth_texture.near_far_planes = vec2.create();
- }
-
- //create FBO
- if( !this._fbo )
- this._fbo = new GL.FBO();
-
- //cut extra
- textures.length = 1 + total_extra;
-
- //assign textures (this will enable the FBO but it will restore the old one after finishing)
- this._fbo.stencil = this.use_stencil_buffer;
- this._fbo.setTextures( textures, this._depth_texture );
- this._version += 1;
- }
-
- /**
- * Called to bind the rendering to this context, from now on all the render will be stored in the textures inside
- *
- * @method enable
- */
- RenderFrameContext.prototype.enable = function( render_settings, viewport, camera )
- {
- viewport = viewport || gl.viewport_data;
-
- //create FBO and textures (pass width and height of current viewport)
- this.prepare( viewport[2], viewport[3] );
-
- if(!this._fbo)
- throw("No FBO created in RenderFrameContext");
-
- //enable FBO
- RenderFrameContext.enableFBO( this._fbo, this.adjust_aspect );
-
- if(LS.RenderFrameContext.current)
- RenderFrameContext.stack.push( LS.RenderFrameContext.current );
- LS.RenderFrameContext.current = this;
-
- //set depth info inside the texture
- camera = camera || LS.Renderer._current_camera;
- if(this._depth_texture && camera)
- {
- this._depth_texture.near_far_planes[0] = camera.near;
- this._depth_texture.near_far_planes[1] = camera.far;
- }
- }
-
- //we cannot read and write in the same buffer, so we need to clone the textures
- //done from... ?
- RenderFrameContext.prototype.cloneBuffers = function()
- {
- //we do not call this._fbo.unbind because it will set the previous FBO
- gl.bindFramebuffer( gl.FRAMEBUFFER, null );
-
- ///for every color texture
- if( this._textures.length )
- {
- if(!this._cloned_textures)
- this._cloned_textures = [];
- var textures = this._textures;
- this._cloned_textures.length = textures.length;
- for(var i = 0; i < textures.length; ++i)
- {
- var texture = textures[i];
- var cloned_texture = this._cloned_textures[i];
- if( !cloned_texture || cloned_texture.hasSameSize( texture[i] ) || !cloned_texture.hasSameProperties( texture ) )
- cloned_texture = this._cloned_textures[i] = new GL.Texture( texture.width, texture.height, texture.getProperties() );
- texture.copyTo( cloned_texture );
- if(i == 0)
- LS.ResourcesManager.textures[":color_buffer" ] = cloned_texture;
- }
- }
-
- //for depth
- if( this._depth_texture )
- {
- var depth = this._depth_texture;
- if(!this._cloned_depth_texture || this._cloned_depth_texture.width != depth.width || this._cloned_depth_texture.height != depth.height || !this._cloned_depth_texture.hasSameProperties( depth ) )
- this._cloned_depth_texture = new GL.Texture( depth.width, depth.height, depth.getProperties() );
-
- depth.copyTo( this._cloned_depth_texture );
- LS.ResourcesManager.textures[":depth_buffer" ] = this._cloned_depth_texture;
- }
-
- //rebind FBO
- gl.bindFramebuffer( gl.FRAMEBUFFER, this._fbo.handler );
- }
-
- /**
- * Called to stop rendering to this context
- *
- * @method disable
- */
- RenderFrameContext.prototype.disable = function()
- {
- //sets some global parameters for aspect and current RFC
- RenderFrameContext.disableFBO( this._fbo );
-
- //if we need to store the textures in the ResourcesManager
- if(this.name)
- {
- var textures = this._textures;
- for(var i = 0; i < textures.length; ++i)
- {
- var name = this.name + (i > 0 ? i : "");
- textures[i].filename = name;
- var final_texture = textures[i];
-
- //only clone main color if requested
- if( this.clone_after_unbind && i === 0 )
- {
- if( !this._cloned_texture ||
- this._cloned_texture.width !== final_texture.width ||
- this._cloned_texture.height !== final_texture.height ||
- this._cloned_texture.type !== final_texture.type )
- this._cloned_texture = final_texture.clone();
- else
- final_texture.copyTo( this._cloned_texture );
- final_texture = this._cloned_texture;
- }
-
- if( this._minFilter == gl.LINEAR_MIPMAP_LINEAR )
- {
- final_texture.bind(0);
- gl.generateMipmap(gl.TEXTURE_2D);
- final_texture.has_mipmaps = true;
- }
-
- LS.ResourcesManager.textures[ name ] = final_texture;
- }
-
- if(this._depth_texture)
- {
- var name = this.name + "_depth";
- this._depth_texture.filename = name;
- LS.ResourcesManager.textures[ name ] = this._depth_texture;
- //LS.ResourcesManager.textures[ ":depth" ] = this._depth_texture;
- }
- }
-
- if( RenderFrameContext.stack.length )
- LS.RenderFrameContext.current = RenderFrameContext.stack.pop();
- else
- LS.RenderFrameContext.current = null;
- }
-
- /**
- * returns the texture containing the data rendered in this context
- *
- * @method getColorTexture
- * @param {number} index the number of the texture (in case there is more than one)
- * @return {GL.Texture} the texture
- */
- RenderFrameContext.prototype.getColorTexture = function(num)
- {
- return this._textures[ num || 0 ] || null;
- }
-
- /**
- * returns the depth texture containing the depth data rendered in this context (in case the use_depth_texture is set to true)
- *
- * @method getDepthTexture
- * @return {GL.Texture} the depth texture
- */
- RenderFrameContext.prototype.getDepthTexture = function()
- {
- return this._depth_texture || null;
- }
-
- /**
- * Fills the textures with a flat color
- * @method clearTextures
- */
- RenderFrameContext.prototype.clearTextures = function()
- {
- for(var i = 0; i < this._textures.length; ++i)
- {
- var texture = this._textures[i];
- if(!texture)
- continue;
- texture.fill([0,0,0,0]);
- }
- }
-
- //enables the FBO and sets every texture with a flag so it cannot be used during the rendering process
- RenderFrameContext.enableFBO = function( fbo, adjust_aspect )
- {
- fbo.bind( true ); //changes viewport to full FBO size (saves old)
-
- LS.Renderer._full_viewport.set( gl.viewport_data );
- if( adjust_aspect )
- {
- fbo._old_aspect = LS.Renderer.global_aspect;
- LS.Renderer.global_aspect = (gl.canvas.width / gl.canvas.height) / (fbo.color_textures[0].width / fbo.color_textures[0].height);
- }
- else
- delete fbo._old_aspect;
- }
-
- RenderFrameContext.disableFBO = function( fbo )
- {
- fbo.unbind(); //restores viewport to old saved one
- LS.Renderer._full_viewport.set( fbo._old_viewport );
- if( fbo._old_aspect )
- LS.Renderer.global_aspect = fbo._old_aspect;
- }
-
-
- /**
- * Render the context of the context to the viewport (allows to apply FXAA)
- *
- * @method show
- * @param {boolean} use_antialiasing in case you want to render with FXAA antialiasing
- */
- RenderFrameContext.prototype.show = function( use_antialiasing )
- {
- var texture = this._color_texture;
- if(!use_antialiasing)
- {
- texture.toViewport();
- return;
- }
-
- var viewport = gl.getViewport();
- var shader = GL.Shader.getFXAAShader();
- var mesh = GL.Mesh.getScreenQuad();
- texture.bind(0);
- shader.uniforms( { u_texture:0, uViewportSize: viewport.subarray(2,4), u_iViewportSize: [1 / texture.width, 1 / texture.height]} ).draw( mesh );
- }
-
- //Resets the current WebGL fbo so it renders to the screen
- RenderFrameContext.reset = function()
- {
- gl.bindFramebuffer( gl.FRAMEBUFFER, null );
- LS.RenderFrameContext.current = null;
- LS.RenderFrameContext.stack.length = 0;
- }
-
-
- LS.RenderFrameContext = RenderFrameContext;
-
-