Paranoia2/cl_dll/render/gl_framebuffer.cpp
2020-08-31 19:50:41 +03:00

628 lines
20 KiB
C++

/*
gl_framebuffer.cpp - framebuffer implementation class
Copyright (C) 2014 Uncle Mike
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include "hud.h"
#include "cl_util.h"
#include "gl_local.h"
#include <mathlib.h>
#include <stringlib.h>
static gl_drawbuffer_t gl_drawbuffers[MAX_FRAMEBUFFERS];
static int gl_num_drawbuffers;
/*
==================
GL_AllocDrawbuffer
==================
*/
gl_drawbuffer_t *GL_AllocDrawbuffer( const char *name, int width, int height, int depth )
{
gl_drawbuffer_t *fbo;
int i;
if( !GL_Support( R_FRAMEBUFFER_OBJECT ))
return NULL;
// find a free FBO slot
for( i = 0, fbo = gl_drawbuffers; i < gl_num_drawbuffers; i++, fbo++ )
if( !fbo->name[0] ) break;
if( i == gl_num_drawbuffers )
{
if( gl_num_drawbuffers == MAX_FRAMEBUFFERS )
HOST_ERROR( "GL_AllocDrawBuffer: MAX_FRAMEBUFFERS limit exceeds\n" );
gl_num_drawbuffers++;
}
if( !GL_Support( R_ARB_TEXTURE_NPOT_EXT ))
{
width = NearestPOW( width, true );
height = NearestPOW( height, true );
}
// fill it in
Q_strncpy( fbo->name, name, sizeof( fbo->name ));
// make sure it's fit in limits
fbo->width = Q_min( glConfig.max_2d_texture_size, width );
fbo->height = Q_min( glConfig.max_2d_texture_size, height );
fbo->depth = depth;
// generate the drawbuffer
pglGenFramebuffers( 1, &fbo->id );
return fbo;
}
/*
==================
GL_ResizeDrawbuffer
==================
*/
void GL_ResizeDrawbuffer( gl_drawbuffer_t *fbo, int width, int height, int depth )
{
ASSERT( fbo != NULL );
// fill it in
fbo->width = width;
fbo->height = height;
fbo->depth = depth;
}
/*
==================
GL_AttachColorTextureToFBO
==================
*/
void GL_AttachColorTextureToFBO( gl_drawbuffer_t *fbo, int texture, int colorIndex, int index )
{
GLuint target = RENDER_GET_PARM( PARM_TEX_TARGET, texture );
GLuint format = RENDER_GET_PARM( PARM_TEX_GLFORMAT, texture );
GLint width = RENDER_GET_PARM( PARM_TEX_WIDTH, texture );
GLint height = RENDER_GET_PARM( PARM_TEX_HEIGHT, texture );
GLint depth = RENDER_GET_PARM( PARM_TEX_DEPTH, texture );
if( target == GL_TEXTURE_2D )
{
// set the texture
fbo->colortarget[colorIndex] = texture;
// if the drawbuffer has been resized
if( width != fbo->width || height != fbo->height )
{
// check the dimensions
if( fbo->width > glConfig.max_2d_texture_size || fbo->height > glConfig.max_2d_texture_size )
HOST_ERROR( "GL_AttachColorTextureToFBO: size exceeds hardware limits (%i > %i or %i > %i)\n",
fbo->width, glConfig.max_2d_texture_size, fbo->height, glConfig.max_2d_texture_size );
// reallocate the texture
GL_UpdateTexSize( texture, fbo->width, fbo->height, fbo->depth );
GL_BindTexture( GL_KEEP_UNIT, texture );
// need to refresh real FBO size with possible hardware limitations
fbo->width = RENDER_GET_PARM( PARM_TEX_WIDTH, texture );
fbo->height = RENDER_GET_PARM( PARM_TEX_HEIGHT, texture );
pglTexImage2D( target, 0, format, fbo->width, fbo->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL );
}
// Bind the drawbuffer
GL_BindDrawbuffer( fbo );
// Set up the color attachment
pglFramebufferTexture2D( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + colorIndex, GL_TEXTURE_2D, texture, 0 );
}
else if( target == GL_TEXTURE_CUBE_MAP_ARB )
{
// set the texture
fbo->colortarget[colorIndex] = texture;
// if the drawbuffer has been resized
if( width != fbo->width || height != fbo->height )
{
// check the dimensions
if( fbo->width > glConfig.max_cubemap_size || fbo->height > glConfig.max_cubemap_size )
HOST_ERROR( "GL_AttachColorTextureToFBO: size exceeds hardware limits (%i > %i or %i > %i)\n",
fbo->width, glConfig.max_cubemap_size, fbo->height, glConfig.max_cubemap_size );
// reallocate the texture
GL_UpdateTexSize( texture, fbo->width, fbo->height, fbo->depth );
GL_BindTexture( GL_KEEP_UNIT, texture );
// need to refresh real FBO size with possible hardware limitations
fbo->width = RENDER_GET_PARM( PARM_TEX_WIDTH, texture );
fbo->height = RENDER_GET_PARM( PARM_TEX_HEIGHT, texture );
pglTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB, 0, format, fbo->width, fbo->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL );
pglTexImage2D( GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB, 0, format, fbo->width, fbo->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL );
pglTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB, 0, format, fbo->width, fbo->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL );
pglTexImage2D( GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB, 0, format, fbo->width, fbo->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL );
pglTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB, 0, format, fbo->width, fbo->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL );
pglTexImage2D( GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB, 0, format, fbo->width, fbo->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL );
}
// bind the drawbuffer
GL_BindDrawbuffer( fbo );
// set up the color attachment
pglFramebufferTexture2D( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + colorIndex, GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + index, texture, 0 );
}
else if( target == GL_TEXTURE_2D_ARRAY_EXT )
{
// Set the texture
fbo->colortarget[colorIndex] = texture;
// if the drawbuffer has been resized
if( width != fbo->width || height != fbo->height || depth != fbo->depth )
{
// check the dimensions
if( fbo->width > glConfig.max_2d_texture_size || fbo->height > glConfig.max_2d_texture_size || fbo->depth > glConfig.max_2d_texture_layers )
HOST_ERROR( "GL_AttachColorTextureToFBO: size exceeds hardware limits (%i > %i or %i > %i or %i > %i)\n",
fbo->width, glConfig.max_2d_texture_size, fbo->height, glConfig.max_2d_texture_size, fbo->depth, glConfig.max_2d_texture_layers );
// reallocate the texture
GL_UpdateTexSize( texture, fbo->width, fbo->height, fbo->depth );
GL_BindTexture( GL_KEEP_UNIT, texture );
// need to refresh real FBO size with possible hardware limitations
fbo->width = RENDER_GET_PARM( PARM_TEX_WIDTH, texture );
fbo->height = RENDER_GET_PARM( PARM_TEX_HEIGHT, texture );
fbo->depth = RENDER_GET_PARM( PARM_TEX_DEPTH, texture );
pglTexImage3D( target, 0, format, fbo->width, fbo->height, fbo->depth, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL );
}
// bind the drawbuffer
GL_BindDrawbuffer( fbo );
// Set up the color attachment
pglFramebufferTextureLayer( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + colorIndex, texture, 0, index );
}
else if( target == GL_TEXTURE_3D )
{
// Set the texture
fbo->colortarget[colorIndex] = texture;
// if the drawbuffer has been resized
if( width != fbo->width || height != fbo->height || depth != fbo->depth )
{
// check the dimensions
if( fbo->width > glConfig.max_3d_texture_size || fbo->height > glConfig.max_3d_texture_size || fbo->depth > glConfig.max_3d_texture_size )
HOST_ERROR( "GL_AttachColorTextureToFBO: size exceeds hardware limits (%i > %i or %i > %i or %i > %i)\n",
fbo->width, glConfig.max_3d_texture_size, fbo->height, glConfig.max_3d_texture_size, fbo->depth, glConfig.max_3d_texture_size );
// reallocate the texture
GL_UpdateTexSize( texture, fbo->width, fbo->height, fbo->depth );
GL_BindTexture( GL_KEEP_UNIT, texture );
// need to refresh real FBO size with possible hardware limitations
fbo->width = RENDER_GET_PARM( PARM_TEX_WIDTH, texture );
fbo->height = RENDER_GET_PARM( PARM_TEX_HEIGHT, texture );
fbo->depth = RENDER_GET_PARM( PARM_TEX_DEPTH, texture );
pglTexImage3D( target, 0, format, fbo->width, fbo->height, fbo->depth, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL );
}
// bind the drawbuffer
GL_BindDrawbuffer( fbo );
// Set up the color attachment
pglFramebufferTexture3D( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + colorIndex, GL_TEXTURE_3D, texture, 0, index );
}
else
{
HOST_ERROR( "GL_AttachColorTextureToFBO: bad texture target (%i)\n", target );
}
}
/*
==================
GL_AttachDepthTextureToFBO
==================
*/
void GL_AttachDepthTextureToFBO( gl_drawbuffer_t *fbo, int texture, int index )
{
GLuint target = RENDER_GET_PARM( PARM_TEX_TARGET, texture );
GLuint format = RENDER_GET_PARM( PARM_TEX_GLFORMAT, texture );
GLint width = RENDER_GET_PARM( PARM_TEX_WIDTH, texture );
GLint height = RENDER_GET_PARM( PARM_TEX_HEIGHT, texture );
GLint depth = RENDER_GET_PARM( PARM_TEX_DEPTH, texture );
if( target == GL_TEXTURE_2D )
{
// set the texture
fbo->depthtarget = texture;
// if the drawbuffer has been resized
if( width != fbo->width || height != fbo->height )
{
// check the dimensions
if( fbo->width > glConfig.max_2d_texture_size || fbo->height > glConfig.max_2d_texture_size )
HOST_ERROR( "GL_AttachDepthTextureToFBO: size exceeds hardware limits (%i > %i or %i > %i)\n",
fbo->width, glConfig.max_2d_texture_size, fbo->height, glConfig.max_2d_texture_size );
// reallocate the texture
GL_UpdateTexSize( texture, fbo->width, fbo->height, fbo->depth );
GL_BindTexture( GL_KEEP_UNIT, texture );
// need to refresh real FBO size with possible hardware limitations
fbo->width = RENDER_GET_PARM( PARM_TEX_WIDTH, texture );
fbo->height = RENDER_GET_PARM( PARM_TEX_HEIGHT, texture );
pglTexImage2D( target, 0, format, fbo->width, fbo->height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL );
}
// Bind the drawbuffer
GL_BindDrawbuffer( fbo );
// Set up the color attachment
pglFramebufferTexture2D( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, texture, 0 );
}
else if( target == GL_TEXTURE_CUBE_MAP_ARB )
{
// set the texture
fbo->depthtarget = texture;
// if the drawbuffer has been resized
if( width != fbo->width || height != fbo->height )
{
// check the dimensions
if( fbo->width > glConfig.max_cubemap_size || fbo->height > glConfig.max_cubemap_size )
HOST_ERROR( "GL_AttachDepthTextureToFBO: size exceeds hardware limits (%i > %i or %i > %i)\n",
fbo->width, glConfig.max_cubemap_size, fbo->height, glConfig.max_cubemap_size );
// reallocate the texture
GL_UpdateTexSize( texture, fbo->width, fbo->height, fbo->depth );
GL_BindTexture( GL_KEEP_UNIT, texture );
// need to refresh real FBO size with possible hardware limitations
fbo->width = RENDER_GET_PARM( PARM_TEX_WIDTH, texture );
fbo->height = RENDER_GET_PARM( PARM_TEX_HEIGHT, texture );
pglTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB, 0, format, fbo->width, fbo->height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL );
pglTexImage2D( GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB, 0, format, fbo->width, fbo->height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL );
pglTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB, 0, format, fbo->width, fbo->height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL );
pglTexImage2D( GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB, 0, format, fbo->width, fbo->height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL );
pglTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB, 0, format, fbo->width, fbo->height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL );
pglTexImage2D( GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB, 0, format, fbo->width, fbo->height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL );
}
// bind the drawbuffer
GL_BindDrawbuffer( fbo );
// set up the color attachment
pglFramebufferTexture2D( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + index, texture, 0 );
}
else if( target == GL_TEXTURE_2D_ARRAY_EXT )
{
// Set the texture
fbo->depthtarget = texture;
// if the drawbuffer has been resized
if( width != fbo->width || height != fbo->height || depth != fbo->depth )
{
// check the dimensions
if( fbo->width > glConfig.max_2d_texture_size || fbo->height > glConfig.max_2d_texture_size || fbo->depth > glConfig.max_2d_texture_layers )
HOST_ERROR( "GL_AttachDepthTextureToFBO: size exceeds hardware limits (%i > %i or %i > %i or %i > %i)\n",
fbo->width, glConfig.max_2d_texture_size, fbo->height, glConfig.max_2d_texture_size, fbo->depth, glConfig.max_2d_texture_layers );
// reallocate the texture
GL_UpdateTexSize( texture, fbo->width, fbo->height, fbo->depth );
GL_BindTexture( GL_KEEP_UNIT, texture );
// need to refresh real FBO size with possible hardware limitations
fbo->width = RENDER_GET_PARM( PARM_TEX_WIDTH, texture );
fbo->height = RENDER_GET_PARM( PARM_TEX_HEIGHT, texture );
fbo->depth = RENDER_GET_PARM( PARM_TEX_DEPTH, texture );
pglTexImage3D( target, 0, format, fbo->width, fbo->height, fbo->depth, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL );
}
// bind the drawbuffer
GL_BindDrawbuffer( fbo );
// Set up the color attachment
pglFramebufferTextureLayer( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, texture, 0, index );
}
else
{
HOST_ERROR( "GL_AttachDepthTextureToFBO: bad texture target (%i)\n", target );
}
}
/*
==================
GL_CheckFBOStatus
==================
*/
void GL_CheckFBOStatus( gl_drawbuffer_t *fbo )
{
const char *string;
int status;
// bind the drawbuffer
GL_BindDrawbuffer( fbo );
// check the framebuffer status
status = pglCheckFramebufferStatus( GL_FRAMEBUFFER_EXT );
if( status == GL_FRAMEBUFFER_COMPLETE_EXT )
return;
switch( status )
{
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
string = "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
string = "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
break;
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
string = "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER";
break;
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
string = "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER";
break;
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT:
string = "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE";
break;
case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
string = "GL_FRAMEBUFFER_UNSUPPORTED";
break;
case GL_FRAMEBUFFER_UNDEFINED_EXT:
string = "GL_FRAMEBUFFER_UNDEFINED";
break;
default:
string = "UNKNOWN STATUS";
break;
}
HOST_ERROR( "GL_CheckFBOStatus: %s for '%s'\n", string, fbo->name );
}
/*
==================
GL_FreeDrawbuffers
==================
*/
void GL_FreeDrawbuffers( void )
{
gl_drawbuffer_t *fbo;
// unbind the drawbuffer
GL_BindDrawbuffer( NULL );
// delete all the drawbuffers
for( int i = 0; i < gl_num_drawbuffers; i++ )
{
fbo = &gl_drawbuffers[i];
pglDeleteFramebuffers( 1, &fbo->id );
}
// NOTE: let the engine release attached textures
memset( gl_drawbuffers, 0, sizeof( gl_drawbuffers ));
gl_num_drawbuffers = 0;
}
CFrameBuffer :: CFrameBuffer( void )
{
m_iFrameWidth = m_iFrameHeight = 0;
m_iFrameBuffer = m_iDepthBuffer = 0;
m_iTexture = m_iAttachment = 0;
m_bAllowFBO = false;
}
CFrameBuffer :: ~CFrameBuffer( void )
{
// NOTE: static case will be failed
Free();
}
int CFrameBuffer :: m_iBufferNum = 0;
bool CFrameBuffer :: Init( FBO_TYPE type, GLuint width, GLuint height, GLuint flags )
{
Free(); // release old buffer
m_iFlags = flags;
if( !GL_Support( R_ARB_TEXTURE_NPOT_EXT ))
SetBits( m_iFlags, FBO_MAKEPOW );
if( FBitSet( m_iFlags, FBO_MAKEPOW ))
{
width = NearestPOW( width, true );
height = NearestPOW( height, true );
}
// clamp size to hardware limits
if( type == FBO_CUBE )
{
m_iFrameWidth = bound( 0, width, glConfig.max_cubemap_size );
m_iFrameHeight = bound( 0, height, glConfig.max_cubemap_size );
}
else
{
m_iFrameWidth = bound( 0, width, glConfig.max_2d_texture_size );
m_iFrameHeight = bound( 0, height, glConfig.max_2d_texture_size );
}
if( !m_iFrameWidth || !m_iFrameHeight )
{
ALERT( at_error, "CFrameBuffer( %i x %i ) invalid size\n", m_iFrameWidth, m_iFrameHeight );
return false;
}
// create FBO texture
if( !FBitSet( m_iFlags, FBO_NOTEXTURE ))
{
int texFlags = (TF_NOMIPMAP|TF_CLAMP);
if( type == FBO_CUBE )
SetBits( texFlags, TF_CUBEMAP );
else if( type == FBO_DEPTH )
SetBits( texFlags, TF_DEPTHMAP );
if( !FBitSet( m_iFlags, FBO_LINEAR ))
SetBits( texFlags, TF_NEAREST );
if( FBitSet( m_iFlags, FBO_FLOAT ))
SetBits( texFlags, TF_ARB_FLOAT );
if( FBitSet( m_iFlags, FBO_RECTANGLE ))
SetBits( texFlags, TF_RECTANGLE );
if( FBitSet( m_iFlags, FBO_LUMINANCE ))
SetBits( texFlags, TF_LUMINANCE );
else if( type == FBO_COLOR )
SetBits( texFlags, TF_HAS_ALPHA );
m_iTexture = CREATE_TEXTURE( va( "*framebuffer#%i", m_iBufferNum++ ), m_iFrameWidth, m_iFrameHeight, NULL, texFlags );
}
m_bAllowFBO = (GL_Support( R_FRAMEBUFFER_OBJECT )) ? true : false;
if( m_bAllowFBO )
{
// frame buffer
pglGenFramebuffers( 1, &m_iFrameBuffer );
pglBindFramebuffer( GL_FRAMEBUFFER_EXT, m_iFrameBuffer );
// depth buffer
pglGenRenderbuffers( 1, &m_iDepthBuffer );
pglBindRenderbuffer( GL_RENDERBUFFER_EXT, m_iDepthBuffer );
pglRenderbufferStorage( GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, m_iFrameWidth, m_iFrameHeight );
// attach depthbuffer to framebuffer
pglFramebufferRenderbuffer( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_iDepthBuffer );
// attach the framebuffer to our texture, which may be a depth texture
if( type == FBO_DEPTH )
{
m_iAttachment = GL_DEPTH_ATTACHMENT_EXT;
pglDrawBuffer( GL_NONE );
pglReadBuffer( GL_NONE );
}
else
{
m_iAttachment = GL_COLOR_ATTACHMENT0_EXT;
pglDrawBuffer( m_iAttachment );
pglReadBuffer( m_iAttachment );
}
if( m_iTexture != 0 )
{
GLuint target = RENDER_GET_PARM( PARM_TEX_TARGET, m_iTexture );
GLuint texnum = RENDER_GET_PARM( PARM_TEX_TEXNUM, m_iTexture );
if( target == GL_TEXTURE_CUBE_MAP_ARB )
{
target = GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB;
for( int i = 0; i < 6; i++ )
pglFramebufferTexture2D( GL_FRAMEBUFFER_EXT, m_iAttachment, target + i, texnum, 0 );
}
else pglFramebufferTexture2D( GL_FRAMEBUFFER_EXT, m_iAttachment, target, texnum, 0 );
}
m_bAllowFBO = ValidateFBO();
pglBindFramebuffer( GL_FRAMEBUFFER_EXT, 0 );
}
return true;
}
void CFrameBuffer :: Free( void )
{
if( !m_bAllowFBO ) return;
if( !FBitSet( m_iFlags, FBO_NOTEXTURE ) && m_iTexture != 0 )
FREE_TEXTURE( m_iTexture );
pglDeleteRenderbuffers( 1, &m_iDepthBuffer );
pglDeleteFramebuffers( 1, &m_iFrameBuffer );
m_iFrameWidth = m_iFrameHeight = 0;
m_iFrameBuffer = m_iDepthBuffer = 0;
m_iTexture = m_iAttachment = 0;
m_bAllowFBO = false;
}
bool CFrameBuffer :: ValidateFBO( void )
{
if( !GL_Support( R_FRAMEBUFFER_OBJECT ))
return false;
// check FBO status
GLenum status = pglCheckFramebufferStatus( GL_FRAMEBUFFER_EXT );
switch( status )
{
case GL_FRAMEBUFFER_COMPLETE_EXT:
return true;
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
ALERT( at_error, "CFrameBuffer: attachment is NOT complete\n" );
return false;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
ALERT( at_error, "CFrameBuffer: no image is attached to FBO\n" );
return false;
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
ALERT( at_error, "CFrameBuffer: attached images have different dimensions\n" );
return false;
case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
ALERT( at_error, "CFrameBuffer: color attached images have different internal formats\n" );
return false;
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
ALERT( at_error, "CFrameBuffer: draw buffer incomplete\n" );
return false;
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
ALERT( at_error, "CFrameBuffere: read buffer incomplete\n" );
return false;
case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
ALERT( at_error, "CFrameBuffer: unsupported by current FBO implementation\n" );
return false;
default:
ALERT( at_error, "CFrameBuffer: unknown error\n" );
return false;
}
}
void CFrameBuffer :: Bind( GLuint texture, GLuint side )
{
if( !m_bAllowFBO ) return;
if( glState.frameBuffer != m_iFrameBuffer )
{
pglBindFramebuffer( GL_FRAMEBUFFER_EXT, m_iFrameBuffer );
glState.frameBuffer = m_iFrameBuffer;
}
// change texture if needs
if( FBitSet( m_iFlags, FBO_NOTEXTURE ) && texture != 0 )
{
m_iTexture = texture;
GLuint target = RENDER_GET_PARM( PARM_TEX_TARGET, m_iTexture );
GLuint texnum = RENDER_GET_PARM( PARM_TEX_TEXNUM, m_iTexture );
if( target == GL_TEXTURE_CUBE_MAP_ARB )
target = GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + side;
pglFramebufferTexture2D( GL_FRAMEBUFFER_EXT, m_iAttachment, target, texnum, 0 );
}
ValidateFBO();
}