Index: code/sdl/sdl_glimp.c =================================================================== --- code/sdl/sdl_glimp.c (revision 1752) +++ code/sdl/sdl_glimp.c (working copy) @@ -83,6 +83,41 @@ void (APIENTRYP qglLockArraysEXT) (GLint first, GLsizei count); void (APIENTRYP qglUnlockArraysEXT) (void); +#ifdef FRAMEBUFFER_AND_GLSL_SUPPORT +//added framebuffer extensions +void (APIENTRYP qglGenFramebuffersEXT )(GLsizei, GLuint *); +void (APIENTRYP qglBindFramebufferEXT )(GLenum, GLuint); +void (APIENTRYP qglGenRenderbuffersEXT )(GLsizei, GLuint *); +void (APIENTRYP qglBindRenderbufferEXT )(GLenum, GLuint); +void (APIENTRYP qglRenderbufferStorageEXT )(GLenum, GLenum, GLsizei, GLsizei); +void (APIENTRYP qglRenderbufferStorageMultisampleEXT )(GLenum, GLsizei, GLenum, GLsizei, GLsizei); +void (APIENTRYP qglFramebufferRenderbufferEXT )(GLenum, GLenum, GLenum, GLuint); +void (APIENTRYP qglFramebufferTexture2DEXT )(GLenum, GLenum, GLenum, GLuint, GLint); +GLenum (APIENTRYP qglCheckFramebufferStatusEXT )(GLenum); +void (APIENTRYP qglDeleteFramebuffersEXT )(GLsizei, const GLuint *); +void (APIENTRYP qglDeleteRenderbuffersEXT )(GLsizei, const GLuint *); + +//added fragment/vertex program extensions +void (APIENTRYP qglAttachShader) (GLuint, GLuint); +void (APIENTRYP qglBindAttribLocation) (GLuint, GLuint, const GLchar *); +void (APIENTRYP qglCompileShader) (GLuint); +GLuint (APIENTRYP qglCreateProgram) (void); +GLuint (APIENTRYP qglCreateShader) (GLenum); +void (APIENTRYP qglDeleteProgram) (GLuint); +void (APIENTRYP qglDeleteShader) (GLuint); +void (APIENTRYP qglShaderSource) (GLuint, GLsizei, const GLchar* *, const GLint *); +void (APIENTRYP qglLinkProgram) (GLuint); +void (APIENTRYP qglUseProgram) (GLuint); +GLint (APIENTRYP qglGetUniformLocation) (GLuint, const GLchar *); +void (APIENTRYP qglUniform1f) (GLint, GLfloat); +void (APIENTRYP qglUniform2f) (GLint, GLfloat, GLfloat); +void (APIENTRYP qglUniform1i) (GLint, GLint); +void (APIENTRYP qglGetProgramiv) (GLuint, GLenum, GLint *); +void (APIENTRYP qglGetProgramInfoLog) (GLuint, GLsizei, GLsizei *, GLchar *); +void (APIENTRYP qglGetShaderiv) (GLuint, GLenum, GLint *); +void (APIENTRYP qglGetShaderInfoLog) (GLuint, GLsizei, GLsizei *, GLchar *); +#endif + /* =============== GLimp_Shutdown @@ -654,6 +689,107 @@ { ri.Printf( PRINT_ALL, "...GL_EXT_texture_filter_anisotropic not found\n" ); } + +#ifdef FRAMEBUFFER_AND_GLSL_SUPPORT + qglGenFramebuffersEXT = NULL; + qglBindFramebufferEXT = NULL; + qglGenRenderbuffersEXT = NULL; + qglBindRenderbufferEXT = NULL; + qglRenderbufferStorageEXT = NULL; + qglRenderbufferStorageMultisampleEXT = NULL; + qglFramebufferRenderbufferEXT = NULL; + qglFramebufferTexture2DEXT = NULL; + qglCheckFramebufferStatusEXT = NULL; + qglDeleteFramebuffersEXT = NULL; + qglDeleteRenderbuffersEXT =NULL; + + //added fragment/vertex program extensions + qglAttachShader = NULL; + qglBindAttribLocation = NULL; + qglCompileShader = NULL; + qglCreateProgram = NULL; + qglCreateShader = NULL; + qglDeleteProgram = NULL; + qglDeleteShader = NULL; + qglShaderSource = NULL; + qglLinkProgram = NULL; + qglUseProgram =NULL; + qglGetUniformLocation = NULL; + qglUniform1f = NULL; + qglUniform2f = NULL; + qglUniform1i = NULL; + qglGetProgramiv = NULL; + qglGetProgramInfoLog = NULL; + qglGetShaderiv =NULL; + qglGetShaderInfoLog = NULL; + + framebufferSupported = qfalse; + glslSupported = qfalse; + if ( GLimp_HaveExtension( "GL_EXT_framebuffer_object" ) && + GLimp_HaveExtension( "GL_ARB_texture_non_power_of_two" ) ) + { + ri.Printf( PRINT_ALL, "...using GL_EXT_framebuffer_object\n" ); + ri.Printf( PRINT_ALL, "...using GL_ARB_texture_non_power_of_two\n" ); + framebufferSupported = qtrue; + qglGenFramebuffersEXT = ( void (APIENTRY * )(GLsizei, GLuint *) ) SDL_GL_GetProcAddress( "glGenFramebuffersEXT"); + qglBindFramebufferEXT = ( void (APIENTRY * )(GLenum, GLuint) ) SDL_GL_GetProcAddress( "glBindFramebufferEXT"); + qglGenRenderbuffersEXT = ( void (APIENTRY * )(GLsizei, GLuint *) ) SDL_GL_GetProcAddress( "glGenRenderbuffersEXT"); + qglBindRenderbufferEXT = ( void (APIENTRY * )(GLenum, GLuint) ) SDL_GL_GetProcAddress( "glBindRenderbufferEXT"); + qglRenderbufferStorageEXT = ( void (APIENTRY * )(GLenum, GLenum, GLsizei, GLsizei) ) SDL_GL_GetProcAddress( "glRenderbufferStorageEXT"); + if ( GLimp_HaveExtension( "GL_EXT_framebuffer_multisample" ) ) { + ri.Printf( PRINT_ALL, "...using GL_EXT_framebuffer_multisample\n" ); + qglRenderbufferStorageMultisampleEXT = ( void (APIENTRY * )(GLenum, GLsizei, GLenum, GLsizei, GLsizei) ) SDL_GL_GetProcAddress( "glRenderbufferStorageMultisampleEXT"); + } else { + ri.Printf( PRINT_WARNING, "WARNING: GL_EXT_framebuffer_multisample is missing\n"); + } + qglFramebufferRenderbufferEXT = ( void (APIENTRY * )(GLenum, GLenum, GLenum, GLuint) ) SDL_GL_GetProcAddress( "glFramebufferRenderbufferEXT"); + qglFramebufferTexture2DEXT = ( void (APIENTRY * )(GLenum, GLenum, GLenum, GLuint, GLint) ) SDL_GL_GetProcAddress( "glFramebufferTexture2DEXT"); + qglCheckFramebufferStatusEXT = ( GLenum (APIENTRY *)(GLenum) ) SDL_GL_GetProcAddress( "glCheckFramebufferStatusEXT"); + qglDeleteFramebuffersEXT = ( void (APIENTRY * )(GLsizei, const GLuint *) ) SDL_GL_GetProcAddress( "glDeleteFramebuffersEXT"); + qglDeleteRenderbuffersEXT = ( void (APIENTRY * )(GLsizei, const GLuint *) ) SDL_GL_GetProcAddress( "glDeleteRenderbuffersEXT"); + + if ( !strstr( glConfig.extensions_string, "GL_ARB_depth_texture" ) ) { + ri.Printf( PRINT_WARNING, "WARNING: GL_ARB_depth_texture is missing\n"); + } + if ( !strstr( glConfig.extensions_string, "GL_EXT_packed_depth_stencil" ) || + !strstr( glConfig.extensions_string, "GL_NV_packed_depth_stencil" ) ) { + ri.Printf( PRINT_WARNING, "WARNING: packed_depth_stencil is missing\n"); + } + + } + //added fragment/vertex program extensions + if ( GLimp_HaveExtension( "GL_ARB_fragment_shader" ) && + GLimp_HaveExtension( "GL_ARB_vertex_program" ) && + GLimp_HaveExtension( "GL_ARB_vertex_shader" ) && + GLimp_HaveExtension( "GL_ARB_fragment_program" ) && + GLimp_HaveExtension( "GL_ARB_shading_language_100" ) ) + { + ri.Printf( PRINT_ALL, "...using GL_ARB_fragment_program\n" ); + ri.Printf( PRINT_ALL, "...using GL_ARB_vertex_program\n" ); + ri.Printf( PRINT_ALL, "...using GL_ARB_vertex_shader\n" ); + ri.Printf( PRINT_ALL, "...using GL_ARB_fragment_program\n" ); + ri.Printf( PRINT_ALL, "...using GL_ARB_shading_language_100\n" ); + glslSupported = qtrue; + qglAttachShader = ( void (APIENTRY * ) (GLuint, GLuint) ) SDL_GL_GetProcAddress( "glAttachShader"); + qglBindAttribLocation = ( void (APIENTRY * ) (GLuint, GLuint, const GLchar *) ) SDL_GL_GetProcAddress( "glBindAttribLocation"); + qglCompileShader = ( void (APIENTRY * ) (GLuint) ) SDL_GL_GetProcAddress( "glCompileShader"); + qglCreateProgram = ( GLuint (APIENTRY * ) (void) ) SDL_GL_GetProcAddress( "glCreateProgram"); + qglCreateShader = ( GLuint (APIENTRY * ) (GLenum) ) SDL_GL_GetProcAddress( "glCreateShader"); + qglDeleteProgram = ( void (APIENTRY * ) (GLuint) ) SDL_GL_GetProcAddress( "glDeleteProgram"); + qglDeleteShader = ( void (APIENTRY * ) (GLuint) ) SDL_GL_GetProcAddress( "glDeleteShader"); + qglShaderSource = ( void (APIENTRY * ) (GLuint, GLsizei, const GLchar* *, const GLint *) ) SDL_GL_GetProcAddress( "glShaderSource"); + qglLinkProgram = ( void (APIENTRY * ) (GLuint) ) SDL_GL_GetProcAddress( "glLinkProgram"); + qglUseProgram = ( void (APIENTRY * ) (GLuint) ) SDL_GL_GetProcAddress( "glUseProgram"); + qglGetUniformLocation = ( GLint (APIENTRY * ) (GLuint, const GLchar *) ) SDL_GL_GetProcAddress( "glGetUniformLocation"); + qglUniform1f = ( void (APIENTRY * ) (GLint, GLfloat) ) SDL_GL_GetProcAddress( "glUniform1f"); + qglUniform2f = ( void (APIENTRY * ) (GLint, GLfloat, GLfloat) ) SDL_GL_GetProcAddress( "glUniform2f"); + qglUniform1i = ( void (APIENTRY * ) (GLint, GLint) ) SDL_GL_GetProcAddress( "glUniform1i"); + qglGetProgramiv = ( void (APIENTRY * ) (GLuint, GLenum, GLint *) ) SDL_GL_GetProcAddress( "glGetProgramiv"); + qglGetProgramInfoLog = ( void (APIENTRY * ) (GLuint, GLsizei, GLsizei *, GLchar *) ) SDL_GL_GetProcAddress( "glGetProgramInfoLog"); + qglGetShaderiv = ( void (APIENTRY * ) (GLuint, GLenum, GLint *) ) SDL_GL_GetProcAddress( "glGetShaderiv"); + qglGetShaderInfoLog = ( void (APIENTRY * ) (GLuint, GLsizei, GLsizei *, GLchar *) ) SDL_GL_GetProcAddress( "glGetShaderInfoLog"); + } +#endif } #define R_MODE_FALLBACK 3 // 640 * 480 Index: code/renderer/tr_local.h =================================================================== --- code/renderer/tr_local.h (revision 1752) +++ code/renderer/tr_local.h (working copy) @@ -982,6 +982,11 @@ extern int maxAnisotropy; extern float displayAspect; +#ifdef FRAMEBUFFER_AND_GLSL_SUPPORT +//same as above +extern qboolean framebufferSupported; +extern qboolean glslSupported; +#endif // // cvars @@ -1119,6 +1124,20 @@ extern cvar_t *r_GLlibCoolDownMsec; +#ifdef FRAMEBUFFER_AND_GLSL_SUPPORT +extern cvar_t *r_ext_framebuffer; +extern cvar_t *r_ext_framebuffer_bloom; +extern cvar_t *r_ext_framebuffer_blur_size; +extern cvar_t *r_ext_framebuffer_blur_ammount; +extern cvar_t *r_ext_framebuffer_blur_samples; + +extern cvar_t *r_ext_framebuffer_bloom_sharpness; +extern cvar_t *r_ext_framebuffer_bloom_brightness; + +extern cvar_t *r_ext_framebuffer_rotoscope; +extern cvar_t *r_ext_framebuffer_rotoscope_zedge; +#endif + //==================================================================== float R_NoiseGet4f( float x, float y, float z, float t ); @@ -1260,9 +1279,26 @@ void R_ShaderList_f( void ); void R_RemapShader(const char *oldShader, const char *newShader, const char *timeOffset); +#ifdef FRAMEBUFFER_AND_GLSL_SUPPORT /* ==================================================================== +FRAMEBUFFER RENDER PATH SPECIFIC FUNCTIONS AND STATE VARIABLES + +==================================================================== +*/ +void R_FrameBufferBind( void ); +void R_FrameBufferUnBind( void ); +void R_FrameBuffer_Init( void ); +void R_FrameBuffer_BeginFrame( void ); +void R_FrameBuffer_EndFrame( void ); +void R_FrameBuffer_ResetDraw( void ); +void R_FrameBuffer_Shutdown( void ); +#endif + +/* +==================================================================== + IMPLEMENTATION SPECIFIC FUNCTIONS ==================================================================== Index: code/renderer/tr_init.c =================================================================== --- code/renderer/tr_init.c (revision 1752) +++ code/renderer/tr_init.c (working copy) @@ -163,6 +163,20 @@ cvar_t *r_maxpolyverts; int max_polyverts; +#ifdef FRAMEBUFFER_AND_GLSL_SUPPORT +cvar_t *r_ext_framebuffer; +cvar_t *r_ext_framebuffer_bloom; +cvar_t *r_ext_framebuffer_blur_size; +cvar_t *r_ext_framebuffer_blur_ammount; +cvar_t *r_ext_framebuffer_blur_samples; + +cvar_t *r_ext_framebuffer_bloom_sharpness; +cvar_t *r_ext_framebuffer_bloom_brightness; + +cvar_t *r_ext_framebuffer_rotoscope; +cvar_t *r_ext_framebuffer_rotoscope_zedge; +#endif + /* ** InitOpenGL ** @@ -1018,6 +1032,19 @@ r_maxpolys = ri.Cvar_Get( "r_maxpolys", va("%d", MAX_POLYS), 0); r_maxpolyverts = ri.Cvar_Get( "r_maxpolyverts", va("%d", MAX_POLYVERTS), 0); +#ifdef FRAMEBUFFER_AND_GLSL_SUPPORT + // Framebuffer variables + r_ext_framebuffer = ri.Cvar_Get( "r_ext_framebuffer", "0", CVAR_ARCHIVE | CVAR_LATCH); + r_ext_framebuffer_bloom = ri.Cvar_Get( "r_ext_framebuffer_bloom", "0", CVAR_ARCHIVE | CVAR_LATCH); + r_ext_framebuffer_blur_size = ri.Cvar_Get( "r_ext_framebuffer_blur_size", "256", CVAR_ARCHIVE | CVAR_LATCH); + r_ext_framebuffer_blur_ammount = ri.Cvar_Get( "r_ext_framebuffer_blur_amount", "7", CVAR_ARCHIVE); + r_ext_framebuffer_blur_samples = ri.Cvar_Get( "r_ext_framebuffer_blur_samples", "9", CVAR_ARCHIVE | CVAR_LATCH); + r_ext_framebuffer_bloom_sharpness = ri.Cvar_Get( "r_ext_framebuffer_bloom_sharpness", "0.75", CVAR_ARCHIVE ); + r_ext_framebuffer_bloom_brightness = ri.Cvar_Get( "r_ext_framebuffer_bloom_brightness", "0.85", CVAR_ARCHIVE ); + r_ext_framebuffer_rotoscope = ri.Cvar_Get( "r_ext_framebuffer_rotoscope", "0", CVAR_ARCHIVE | CVAR_LATCH); + r_ext_framebuffer_rotoscope_zedge = ri.Cvar_Get( "r_ext_framebuffer_rotoscope_zedge", "0", CVAR_ARCHIVE | CVAR_LATCH); +#endif + // make sure all the commands added here are also // removed in R_Shutdown ri.Cmd_AddCommand( "imagelist", R_ImageList_f ); @@ -1116,6 +1143,10 @@ InitOpenGL(); +#ifdef FRAMEBUFFER_AND_GLSL_SUPPORT + R_FrameBuffer_Init(); +#endif + R_InitImages(); R_InitShaders(); @@ -1158,6 +1189,9 @@ R_SyncRenderThread(); R_ShutdownCommandBuffers(); R_DeleteTextures(); +#ifdef FRAMEBUFFER_AND_GLSL_SUPPORT + R_FrameBuffer_Shutdown(); +#endif } R_DoneFreeType(); Index: code/renderer/tr_glslprogs.c =================================================================== --- code/renderer/tr_glslprogs.c (revision 0) +++ code/renderer/tr_glslprogs.c (revision 0) @@ -0,0 +1,308 @@ +/* + * tr_glslprogs.c + * + * Copyright 2007 Gord Allott + * + * 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 2 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include "tr_glslprogs.h" +//for all the glsl source its best if we keep them in the mainline code unless +//someone (not me!) wants to implement them into the q3 mainline pak code +//and of course provide a 'standard library' for paks that don't have the glsl +//sources in them. this would allow mod authors to code their own effects but +//meh, they might as well just hack them into the source and send the changes +//upstream. + +//when implementing glsl code try and stick to the carmark q3 coding style ie, +//variables go myVariable and stuff. also end lines with \n\ instead of \ as it +//makes debugging glsl code 15,823x easier. (looks nasty either way...) + + +//this vertex shader is basically complete, we shouldn't really need anything +//else(?). it just maps the vertex position to a screen position. +const char *glslBase_vert = "\n\ +void main() {\n\ + gl_TexCoord[0] = gl_MultiTexCoord0;\n\ + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n\ +}\ +"; + +const char *glslGauss9 = "\n\ +#define NK9_0 0.17857142857142855\n\ +#define NK9_1 0.1607142857142857\n\ +#define NK9_2 0.14285714285714285\n\ +#define NK9_3 0.071428571428571425\n\ +#define NK9_4 0.035714285714285712\n\ +\ +vec4 GaussPass(sampler2D src, vec2 coord, vec2 blrsize) {\n\ + //blrsize is the size (in texture coordinates) of the blur kernel\n\ +\n\ + vec4 accum;\n\ + vec2 step, pos;\n\ + step = blrsize / 9.0;\n\ + pos = coord - (step * 4.0);\n\ +\n\ + accum = texture(src, pos) * NK9_4; pos += step;\n\ + accum += texture(src, pos) * NK9_3; pos += step;\n\ + accum += texture(src, pos) * NK9_2; pos += step;\n\ + accum += texture(src, pos) * NK9_1; pos += step;\n\ + accum += texture(src, pos) * NK9_0; pos += step;\n\ + accum += texture(src, pos) * NK9_1; pos += step;\n\ + accum += texture(src, pos) * NK9_2; pos += step;\n\ + accum += texture(src, pos) * NK9_3; pos += step;\n\ + accum += texture(src, pos) * NK9_4; pos += step;\n\ +\n\ + return accum;\n\ +\n\ +}\n\ +"; + + +const char *glslGauss7 = "\n\ +#define NK7_0 0.19230769230769229\n\ +#define NK7_1 0.18269230769230768\n\ +#define NK7_2 0.15384615384615385\n\ +#define NK7_3 0.067307692307692304\n\ +\n\ +vec4 GaussPass(sampler2D src, vec2 coord, vec2 blrsize) {\n\ + //blrsize is the size (in texture coordinates) of the blur kernel\n\ +\n\ + vec4 accum;\n\ + vec2 step, pos;\n\ + step = blrsize / 7.0;\n\ + pos = coord - (step * 3.0);\n\ +\n\ + accum = texture(src, pos) * NK7_3; pos += step;\n\ + accum += texture(src, pos) * NK7_2; pos += step;\n\ + accum += texture(src, pos) * NK7_1; pos += step;\n\ + accum += texture(src, pos) * NK7_0; pos += step;\n\ + accum += texture(src, pos) * NK7_1; pos += step;\n\ + accum += texture(src, pos) * NK7_2; pos += step;\n\ + accum += texture(src, pos) * NK7_3; pos += step;\n\ + \n\ + return accum;\n\ +\n\ +}\n\ +"; + +const char *glslGauss5 = "\n\ +#define NK5_0 0.33333333\n\ +#define NK5_1 0.26666666\n\ +#define NK5_2 0.06666666\n\ +\n\ +vec4 GaussPass(sampler2D src, vec2 coord, vec2 blrsize) {\n\ + //blrsize is the size (in texture coordinates) of the blur kernel\n\ + \n\ + vec4 accum;\n\ + vec2 step, pos;\n\ + step = blrsize / 5.0;\n\ + pos = coord - (step * 2.0);\n\ + \n\ + accum = texture(src, pos) * NK5_2; pos += step;\n\ + accum += texture(src, pos) * NK5_1; pos += step;\n\ + accum += texture(src, pos) * NK5_0; pos += step;\n\ + accum += texture(src, pos) * NK5_1; pos += step;\n\ + accum += texture(src, pos) * NK5_2; pos += step;\n\ + \n\ + return accum;\n\ + \n\ +}\n\ +"; + +const char *glslBlurMain = "\n\ +uniform sampler2D srcSampler;\n\ +uniform vec2 blurSize;\n\ +void main()\n\ +{\n\ + gl_FragColor = GaussPass(srcSampler, gl_TexCoord[0].xy, blurSize);\n\ +}\n\ +"; + +const char *glslSigScreen = "\n\ +uniform sampler2D srcSampler;\n\ +uniform sampler2D blurSampler;\n\ +//#define sharpness 0.75 \n\ +uniform float sharpness;\n\ +//#define brightness 0.85\n\ +uniform float brightness;\n\ +#define SIGMOIDAL_BASE 2.0\n\ +#define SIGMOIDAL_RANGE 20.0\n\ +\n\ +void main()\n\ +{\n\ + \n\ + vec4 blurcolor = texture( blurSampler, gl_TexCoord[0].xy);\n\ + vec4 basecolor = texture( srcSampler, gl_TexCoord[0].xy);\n\ + \n\ + //vec4 val = 1.0 / (1.0 + exp (-(SIGMOIDAL_BASE + (sharpness * SIGMOIDAL_RANGE)) * (blurcolor - 0.5)));\n\ + vec4 val;\n\ + val = -(SIGMOIDAL_BASE + (sharpness * SIGMOIDAL_RANGE)) * (blurcolor - 0.5);\n\ + val = 1.0 + pow(vec4(2.718281828459045), val);\n\ + val = 1.0 / val;\n\ + val = val * brightness;\n\ + \n\ + gl_FragColor = 1.0 - ((1.0 - basecolor) * (1.0 - val));\n\ +}\n\ +"; + +const char *glslSobel = "\n\ +float sobel(sampler2D tex, vec2 basecoord, vec2 texel_size) {\n\ + /* computes a sobel value from the surrounding pixels */\n\ + vec4 hori, vert;\n\ + //vec2 basecoord = coord;\n\ + float stepw, steph;\n\ + stepw = texel_size.x;\n\ + steph = texel_size.y;\n\ + \n\ + vert = texture(tex, basecoord + vec2(-stepw, -steph)) * -1.0;\n\ + vert += texture(tex, basecoord + vec2(-stepw, 0.0 )) * -2.0;\n\ + vert += texture(tex, basecoord + vec2(-stepw, +steph)) * -1.0;\n\ + \n\ + vert += texture(tex, basecoord + vec2( stepw, -steph)) * 1.0;\n\ + vert += texture(tex, basecoord + vec2( stepw, 0.0 )) * 2.0;\n\ + vert += texture(tex, basecoord + vec2( stepw, +steph)) * 1.0;\n\ + \n\ + hori = texture(tex, basecoord + vec2(-stepw, -steph)) * -1.0;\n\ + hori += texture(tex, basecoord + vec2( 0.0 , -steph)) * -2.0;\n\ + hori += texture(tex, basecoord + vec2(+stepw, -steph)) * -1.0;\n\ +\n\ + hori += texture(tex, basecoord + vec2(-stepw, steph)) * 1.0;\n\ + hori += texture(tex, basecoord + vec2( 0.0 , steph)) * 2.0;\n\ + hori += texture(tex, basecoord + vec2(+stepw, steph)) * 1.0;\n\ +\n\ + /* could use dist() but this is more compatible */\n\ + return sqrt(float((vert * vert) + (hori * hori)));\n\ + \n\ +}\n\ +"; + +const char *glslSobelZ = "\n\ +float sobel(sampler2D tex, vec2 basecoord, vec2 texel_size) {\n\ + /* computes a sobel value from the surrounding pixels */\n\ + vec4 hori, vert;\n\ + //vec2 basecoord = coord;\n\ + float stepw, steph;\n\ + stepw = texel_size.x;\n\ + steph = texel_size.y;\n\ + \n\ + //when used with a zbuffer we first need to figure out how deep the texel is\n\ + float depth = texture(tex, basecoord).r;\n\ + depth = ((exp(depth * 5.0) / 2.7182818284590451) - 0.36787944117144233);\n\ + depth = depth * 1.5819767068693265;\n\ + \n\ + vert = texture(tex, basecoord + vec2(-stepw, -steph)) * -1.0;\n\ + vert += texture(tex, basecoord + vec2(-stepw, 0.0 )) * -2.0;\n\ + vert += texture(tex, basecoord + vec2(-stepw, +steph)) * -1.0;\n\ + \n\ + vert += texture(tex, basecoord + vec2( stepw, -steph)) * 1.0;\n\ + vert += texture(tex, basecoord + vec2( stepw, 0.0 )) * 2.0;\n\ + vert += texture(tex, basecoord + vec2( stepw, +steph)) * 1.0;\n\ + \n\ + hori = texture(tex, basecoord + vec2(-stepw, -steph)) * -1.0;\n\ + hori += texture(tex, basecoord + vec2( 0.0 , -steph)) * -2.0;\n\ + hori += texture(tex, basecoord + vec2(+stepw, -steph)) * -1.0;\n\ +\n\ + hori += texture(tex, basecoord + vec2(-stepw, steph)) * 1.0;\n\ + hori += texture(tex, basecoord + vec2( 0.0 , steph)) * 2.0;\n\ + hori += texture(tex, basecoord + vec2(+stepw, steph)) * 1.0;\n\ +\n\ + /* could use dist() but this is more compatible */\n\ + return sqrt(float((vert * vert) + (hori * hori))) * depth;\n\ + \n\ +}\n\ +"; + +const char *glslToonColour = "\n\ +vec4 ToonColour(vec4 incolour) {\n\ +\n\ + vec3 huetemp;\n\ + huetemp.x = 0.0;\n\ + huetemp.y = 0.0;\n\ + huetemp.z = 0.0;\n\ +\n\ + huetemp.x = incolour.x + incolour.y + incolour.z;\n\ + huetemp.y = 1.0 / huetemp.x;\n\ + \n\ + /* multiply the pixel colourby 1 / sumrgb */\n\ + incolour = incolour * huetemp.y;\n\ + /* get the tones */\n\ + \n\ + if (huetemp.x > 0.2) {\n\ + huetemp.z = 0.4;\n\ + \n\ + } else {\n\ + huetemp.z = 0.0;\n\ + }\n\ + \n\ + if (huetemp.x > 0.4) {\n\ + huetemp.y = 1.0;\n\ + } else {\n\ + huetemp.y = 0.0;\n\ + }\n\ + \n\ + if (huetemp.x > 1.0) {\n\ + huetemp.x = 1.5;\n\ + } else {\n\ + huetemp.x = 0.0;\n\ + }\n\ +\n\ + \n\ + /* sum the huetones */\n\ + \n\ + huetemp.x = huetemp.x + huetemp.y + huetemp.z;\n\ + \n\ + /* multiply the pixel colour with the resulting intensity */\n\ + \n\ + incolour = incolour * huetemp.x;\n\ +\n\ + return vec4(incolour);\n\ +}\n\ +"; + +const char *glslRotoscope = "\n\ +uniform vec2 texelSize;\n\ +uniform sampler2D srcSampler;\n\ +void main()\n\ +{\n\ +\n\ + float fragsobel = sobel(srcSampler, gl_TexCoord[0].xy, texelSize);\n\ + vec4 final_color = ToonColour(texture(srcSampler, gl_TexCoord[0].xy));\n\ +\n\ + fragsobel = 1.0 - clamp(fragsobel - 0.2, 0.0, 1.0);\n\ + gl_FragColor = final_color * fragsobel;\n\ +\n\ +}\n\ +"; + +const char *glslRotoscopeZ = "\n\ +uniform vec2 texelSize;\n\ +uniform sampler2D srcSampler;\n\ +uniform sampler2D depthSampler;\n\ +\n\ +void main()\n\ +{\n\ +\n\ + float fragsobel = sobel(depthSampler, gl_TexCoord[0].xy, texelSize);\n\ + vec4 final_color = ToonColour(texture(srcSampler, gl_TexCoord[0].xy));\n\ +\n\ + fragsobel = clamp(fragsobel, 0.0, 1.0);\n\ + //gl_FragColor = vec4(texture(depthSampler, gl_TexCoord[0].xy).r) / 2.0;\n\ + //final_color = 1.0;\n\ + gl_FragColor = final_color * (1.0 - fragsobel);\n\ +\n\ +}\n\ +"; Index: code/renderer/tr_glslprogs.h =================================================================== --- code/renderer/tr_glslprogs.h (revision 0) +++ code/renderer/tr_glslprogs.h (revision 0) @@ -0,0 +1,42 @@ +/* + * tr_glslprogs.h + * + * Copyright 2007 Gord Allott + * + * 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 2 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef TR_GLSLPROGS_H +#define TR_GLSLPROGS_H + +//basicly the idea here is to just store fragments of glsl source files so that +//we can reuse the code in different programs. the problem with glsl is that +//there is no defined way of 'including' code from other places. so we will +//just have to live with this for now + + +extern const char *glslBase_vert; +extern const char *glslGauss9; +extern const char *glslGauss7; +extern const char *glslGauss5; +extern const char *glslBlurMain; +extern const char *glslSigScreen; +extern const char *glslToonColour; +extern const char *glslSobel; +extern const char *glslRotoscope; +extern const char *glslRotoscopeZ; +extern const char *glslSobelZ; +#endif //TR_GLSLPROGS_H Index: code/renderer/qgl.h =================================================================== --- code/renderer/qgl.h (revision 1752) +++ code/renderer/qgl.h (working copy) @@ -39,7 +39,41 @@ extern void (APIENTRYP qglLockArraysEXT) (GLint first, GLsizei count); extern void (APIENTRYP qglUnlockArraysEXT) (void); +#ifdef FRAMEBUFFER_AND_GLSL_SUPPORT +//added framebuffer extensions +extern void (APIENTRYP qglGenFramebuffersEXT )(GLsizei, GLuint *); +extern void (APIENTRYP qglBindFramebufferEXT )(GLenum, GLuint); +extern void (APIENTRYP qglGenRenderbuffersEXT )(GLsizei, GLuint *); +extern void (APIENTRYP qglBindRenderbufferEXT )(GLenum, GLuint); +extern void (APIENTRYP qglRenderbufferStorageEXT )(GLenum, GLenum, GLsizei, GLsizei); +extern void (APIENTRYP qglRenderbufferStorageMultisampleEXT )(GLenum, GLsizei, GLenum, GLsizei, GLsizei); +extern void (APIENTRYP qglFramebufferRenderbufferEXT )(GLenum, GLenum, GLenum, GLuint); +extern void (APIENTRYP qglFramebufferTexture2DEXT )(GLenum, GLenum, GLenum, GLuint, GLint); +extern GLenum (APIENTRYP qglCheckFramebufferStatusEXT )(GLenum); +extern void (APIENTRYP qglDeleteFramebuffersEXT )(GLsizei, const GLuint *); +extern void (APIENTRYP qglDeleteRenderbuffersEXT )(GLsizei, const GLuint *); +//added fragment/vertex program extensions +extern void (APIENTRYP qglAttachShader) (GLuint, GLuint); +extern void (APIENTRYP qglBindAttribLocation) (GLuint, GLuint, const GLchar *); +extern void (APIENTRYP qglCompileShader) (GLuint); +extern GLuint (APIENTRYP qglCreateProgram) (void); +extern GLuint (APIENTRYP qglCreateShader) (GLenum); +extern void (APIENTRYP qglDeleteProgram) (GLuint); +extern void (APIENTRYP qglDeleteShader) (GLuint); +extern void (APIENTRYP qglShaderSource) (GLuint, GLsizei, const GLchar* *, const GLint *); +extern void (APIENTRYP qglLinkProgram) (GLuint); +extern void (APIENTRYP qglUseProgram) (GLuint); +extern GLint (APIENTRYP qglGetUniformLocation) (GLuint, const GLchar *); +extern void (APIENTRYP qglUniform1f) (GLint, GLfloat); +extern void (APIENTRYP qglUniform2f) (GLint, GLfloat, GLfloat); +extern void (APIENTRYP qglUniform1i) (GLint, GLint); +extern void (APIENTRYP qglGetProgramiv) (GLuint, GLenum, GLint *); +extern void (APIENTRYP qglGetProgramInfoLog) (GLuint, GLsizei, GLsizei *, GLchar *); +extern void (APIENTRYP qglGetShaderiv) (GLuint, GLenum, GLint *); +extern void (APIENTRYP qglGetShaderInfoLog) (GLuint, GLsizei, GLsizei *, GLchar *); +#endif + //=========================================================================== #define qglAccum glAccum Index: code/renderer/tr_framebuffer.c =================================================================== --- code/renderer/tr_framebuffer.c (revision 0) +++ code/renderer/tr_framebuffer.c (revision 0) @@ -0,0 +1,1084 @@ +/* + * tr_framebuffer.c + * + * Copyright 2007 Gord Allott + * + * 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 2 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +// tr_framebuffer.c: framebuffer object rendering path code +// Okay i am going to try and document what I doing here, appologies to anyone +// that already understands this. basically the idea is that normally everything +// opengl renders will be rendered into client memory, that is the space the +// graphics card reserves for anything thats going to be sent to the monitor. +// Using this method we instead redirect all the rendering to a separate bit of +// memory called a frame buffer. +// we can then bind this framebuffer to a texture and render that texture to the +// client memory again so that the image will be sent to the monitor. this +// redirection allows for some neat effects to be applied. + +// Some ideas for what to use this path for: +// - Bloom -done +// - Rotoscope cartoon effects (edge detect + colour mapping) +// - Fake anti-aliasing. (edge detect and blur positive matches) +// - Motion blur +// - generate a speed vector based on how the camera has moved since +// the last frame and use that to compute per pixel blur vectors +// - These would require mods to use some sort of framebuffer qvm api +// - special effects for certain powerups +// - Image Blur when the player is hit +// - Depth of field blur + + +#include "tr_local.h" +#include "tr_glslprogs.h" +#include "qgl.h" + +#ifndef GL_DEPTH_STENCIL_EXT +#define GL_DEPTH_STENCIL_EXT GL_DEPTH_STENCIL_NV +#endif + +#ifndef GL_UNSIGNED_INT_24_8_EXT +#define GL_UNSIGNED_INT_24_8_EXT GL_UNSIGNED_INT_24_8_NV +#endif + +qboolean framebufferSupported; +qboolean glslSupported; +qboolean packedDepthStencilSupported; +qboolean depthTextureSupported; +qboolean separateDepthStencilSupported; + +struct glslobj { + const char **vertex_glsl; + int vert_numSources; + const char **fragment_glsl; + int frag_numSources; + GLuint vertex; + GLuint fragment; + GLuint program; +} glslobj; + +struct r_fbuffer { + GLuint fbo; + GLuint numBuffers; + GLuint *buffers; + GLuint *front; //front buffer + GLuint *back; //back buffer + GLuint *depth; //depth buffer + GLuint *stencil; //stencil buffer + int modeFlags; //the modeflags + int renderbuff; //either FB_FRONT or FB_BACK +} r_fbuffer; + +qboolean needBlur = qfalse; //is set if effects need a blur +qboolean rendered = qfalse; //is set when FBO has been rendered + +struct r_fbuffer screenBuffer; +struct r_fbuffer gaussblurBuffer; + +// define not available in SDL<1.3 +#ifndef GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT 0x8D56 +#endif + +// FBO error reporting inspired by http://www.opengl.org/wiki/GL_EXT_framebuffer_multisample +qboolean R_CheckFramebufferStatus( const char* tag ) +{ + const char* message ; + GLenum status = qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + switch(status) + { + case GL_FRAMEBUFFER_COMPLETE_EXT: + return qtrue; + case GL_FRAMEBUFFER_UNSUPPORTED_EXT: + message = "Framebuffer object format is unsupported by the video hardware. (GL_FRAMEBUFFER_UNSUPPORTED_EXT)(FBO - 820)"; + break; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: + message = "Incomplete attachment. (GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT)(FBO - 820)"; + break; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: + message = "Incomplete missing attachment. (GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT)(FBO - 820)"; + break; + case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: + message = "Incomplete dimensions. (GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT)(FBO - 820)"; + break; + case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: + message = "Incomplete formats. (GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT)(FBO - 820)"; + break; + case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: + message = "Incomplete draw buffer. (GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT)(FBO - 820)"; + break; + case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: + message = "Incomplete read buffer. (GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT)(FBO - 820)"; + break; + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT: + message = "Incomplete multisample buffer. (GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT)(FBO - 820)"; + break; + default: + //Programming error; will fail on all hardware + message = "Some video driver error or programming error occured. Framebuffer object status is invalid. (FBO - 823)"; + } + ri.Printf(PRINT_WARNING,"WARNING, %s: %s\n", tag, message ); + return qfalse; +} + +//two functions to bind and unbind the main framebuffer, generally just to be +//called externaly +void R_FrameBufferBind(void) { + if (!framebufferSupported) { + return; + } + if (r_ext_framebuffer->integer != 1) { + return; + } + + qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, screenBuffer.fbo); +} + +void R_FrameBufferUnBind(void) { + if (!framebufferSupported) { + return; + } + if (r_ext_framebuffer->integer != 1) { + return; + } + + qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); +} + +void R_SetGL2DSize (int width, int height) { + // set 2D virtual screen size + qglViewport( 0, 0, width, height ); + qglScissor( 0, 0, width, height ); + qglMatrixMode(GL_PROJECTION); + qglLoadIdentity (); + qglOrtho (0, width, height, 0, 0, 1); + qglMatrixMode(GL_MODELVIEW); + qglLoadIdentity (); + + GL_State( GLS_DEPTHTEST_DISABLE ); + qglDisable( GL_BLEND ); +} + +void R_DrawQuadMT( GLuint tex1, GLuint tex2, int width, int height ) { + qglEnable(GL_TEXTURE_2D); + if ( glState.currenttextures[1] != tex2 ) { + GL_SelectTexture( 1 ); + qglBindTexture(GL_TEXTURE_2D, tex2); + glState.currenttextures[1] = tex2; + } + if ( glState.currenttextures[0] != tex1 ) { + GL_SelectTexture( 0 ); + qglBindTexture(GL_TEXTURE_2D, tex1); + glState.currenttextures[0] = tex1; + } + + qglBegin(GL_QUADS); + qglMultiTexCoord2fARB(GL_TEXTURE0, 0.0, 1.0); qglMultiTexCoord2fARB(GL_TEXTURE1, 0.0, 1.0); qglVertex2f(0.0 , 0.0 ); + qglMultiTexCoord2fARB(GL_TEXTURE0, 1.0, 1.0); qglMultiTexCoord2fARB(GL_TEXTURE1, 1.0, 1.0); qglVertex2f(width, 0.0 ); + qglMultiTexCoord2fARB(GL_TEXTURE0, 1.0, 0.0); qglMultiTexCoord2fARB(GL_TEXTURE1, 1.0, 0.0); qglVertex2f(width, height); + qglMultiTexCoord2fARB(GL_TEXTURE0, 0.0, 0.0); qglMultiTexCoord2fARB(GL_TEXTURE1, 0.0, 0.0); qglVertex2f(0.0 , height); + qglEnd(); + + qglActiveTextureARB(GL_TEXTURE0); +} + +void R_DrawQuad( GLuint tex, int width, int height) { + qglEnable(GL_TEXTURE_2D); + if ( glState.currenttextures[0] != tex ) { + GL_SelectTexture( 0 ); + qglBindTexture(GL_TEXTURE_2D, tex); + glState.currenttextures[0] = tex; + } + + qglBegin(GL_QUADS); + qglTexCoord2f(0.0, 1.0); qglVertex2f(0.0 , 0.0 ); + qglTexCoord2f(1.0, 1.0); qglVertex2f(width, 0.0 ); + qglTexCoord2f(1.0, 0.0); qglVertex2f(width, height); + qglTexCoord2f(0.0, 0.0); qglVertex2f(0.0 , height); + qglEnd(); +} + +GLuint *R_CreateTexbuffer( GLuint *store, int width, int height, + qboolean smooth, GLenum bindSize, GLenum bindType) +{ + GLenum filter = GL_NEAREST; + + if (smooth) { + filter = GL_LINEAR; + } + + glGenTextures( 1, store ); + glBindTexture( GL_TEXTURE_2D, *store ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter ); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + glTexImage2D( GL_TEXTURE_2D, 0, bindType, width, height, 0, + bindType, bindSize, 0 ); + return store; +} + +GLuint *R_CreateRenderbuffer( GLuint *store, int width, int height, + GLenum bindType) +{ + qglGenRenderbuffersEXT( 1, store ); + qglBindRenderbufferEXT( GL_RENDERBUFFER_EXT, *store ); + if (r_ext_multisample->integer) { + R_CheckFramebufferStatus("error check before GL_EXT_framebuffer_multisample use try"); + // Add support for GL_EXT_framebuffer_multisample + ri.Printf( PRINT_ALL, "...trying to use GL_EXT_framebuffer_multisample\n" ); + qglRenderbufferStorageMultisampleEXT( GL_RENDERBUFFER_EXT, r_ext_multisample->integer, bindType, width, height ); + ri.Printf( PRINT_ALL, "...trying to use GL_EXT_framebuffer_multisample: %s\n", + R_CheckFramebufferStatus("framebuffer_multisample") ? "success" : "failure" ); + } else { + qglRenderbufferStorageEXT( GL_RENDERBUFFER_EXT, bindType, width, height ); + } + + return store; +} + +//------------------------------ +// better framebuffer creation +//------------------------------ +// for this we do a more opengl way of figuring out what level of framebuffer +// objects are supported. we try each mode from 'best' to 'worst' until we +// get a mode that works. + +#define FB_ZBUFFER 0x01 +#define FB_STENCIL 0x02 +#define FB_PACKED 0x04 +#define FB_ZTEXTURE 0x08 +#define FB_BACKBUFFER 0x10 +#define FB_SEPARATEZS 0x20 +#define FB_SMOOTH 0x40 + +#define FB_FRONT 0x01 +#define FB_BACK 0x02 + +int R_DeleteFBuffer( struct r_fbuffer *buffer) { + int flags = buffer->modeFlags; + + if (flags & FB_STENCIL) { + if (flags & FB_ZTEXTURE) { + qglDeleteFramebuffersEXT(1, buffer->depth); + qglDeleteFramebuffersEXT(1, buffer->stencil); + qglDeleteTextures(1, buffer->depth); + } + else { + qglDeleteRenderbuffersEXT(1, buffer->depth); + qglDeleteRenderbuffersEXT(1, buffer->stencil); + } + } + else if (flags & FB_ZBUFFER) { + if (flags & FB_ZTEXTURE) { + qglDeleteFramebuffersEXT(1, buffer->depth); + qglDeleteTextures(1, buffer->depth); + } + else { + qglDeleteRenderbuffersEXT(1, buffer->depth); + } + } + if (flags & FB_BACKBUFFER) { + qglDeleteFramebuffersEXT(1, buffer->back); + qglDeleteTextures(1, buffer->back); + } + + qglDeleteFramebuffersEXT(1, buffer->front); + qglDeleteTextures(1, buffer->front); + + ri.Free(buffer->buffers); + + qglDeleteFramebuffersEXT(1, &(buffer->fbo)); + + return 0; +} + +int R_CreateFBuffer( struct r_fbuffer *buffer, int width, int height, int flags) +{ + int index = 0; + qboolean filter = qfalse; + + //do some checks + if ((flags & FB_PACKED) && (flags & FB_SEPARATEZS)) { + return -1; + } + if ((flags & FB_PACKED) && !(flags & FB_STENCIL)) { + return -2; + } + if ((flags & FB_SEPARATEZS) && !(flags & FB_STENCIL)) { + return -3; + } + + //store the flags in the struct + buffer->modeFlags = flags; + + //allocate enough storage buffers + buffer->numBuffers = 1; + if (flags & FB_STENCIL) { + if (flags & FB_PACKED) { + buffer->numBuffers += 1; + } + if (flags & FB_SEPARATEZS) { + buffer->numBuffers += 2; + } + } + if (flags & FB_ZBUFFER) { + buffer->numBuffers += 1; + } + if (flags & FB_BACKBUFFER) { + buffer->numBuffers += 1; + } + + buffer->buffers = ri.Malloc(sizeof(GLuint) * buffer->numBuffers); + + //link the named variables to the storage space + buffer->front = &(buffer->buffers[index]); + index++; + + if (flags & FB_BACKBUFFER) { + buffer->back = &(buffer->buffers[index]); + index++; + } + + if (flags & FB_STENCIL) { + if (flags & FB_PACKED) { + buffer->stencil = &(buffer->buffers[index]); + buffer->depth = &(buffer->buffers[index]); + index++; + } + if (flags & FB_SEPARATEZS) { + buffer->depth = &(buffer->buffers[index]); + index++; + buffer->stencil = &(buffer->buffers[index]); + index++; + } + } + else if (flags & FB_ZBUFFER) { + buffer->depth = &(buffer->buffers[index]); + index++; + } + + //set the filter state + if (flags & FB_SMOOTH) { + filter = qtrue; + } + + //gen the frame buffer + qglGenFramebuffersEXT(1, &(buffer->fbo)); + qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, buffer->fbo); + + if (flags & FB_STENCIL) { + if (flags & FB_PACKED) { + if (flags & FB_ZTEXTURE) { + R_CreateTexbuffer( buffer->depth, width, height, qfalse, + GL_UNSIGNED_INT_24_8_EXT, GL_DEPTH_STENCIL_EXT); + + qglFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, + GL_TEXTURE_2D, *buffer->depth, 0); + + qglFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, + GL_TEXTURE_2D, *buffer->stencil, 0); + } + else { + R_CreateRenderbuffer(buffer->depth, width, height, GL_DEPTH_STENCIL_EXT); + qglFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, + GL_DEPTH_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, + *buffer->depth); + + qglFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, + GL_STENCIL_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, + *buffer->stencil); + } + } + if (flags & FB_SEPARATEZS) { + if (flags & FB_ZTEXTURE) { + R_CreateTexbuffer( buffer->depth, width, height, qfalse, + GL_UNSIGNED_INT, GL_DEPTH_COMPONENT); + + qglFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, + GL_TEXTURE_2D, *buffer->depth, 0); + + R_CreateRenderbuffer(buffer->stencil, width, height, GL_STENCIL_INDEX8_EXT); + + qglFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, *buffer->stencil); + } + else { + R_CreateRenderbuffer(buffer->depth, width, height, GL_DEPTH_STENCIL_EXT); + qglFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, + GL_DEPTH_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, + *buffer->depth); + + R_CreateRenderbuffer(buffer->stencil, width, height, GL_STENCIL_INDEX8_EXT); + qglFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, + GL_STENCIL_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, + *buffer->stencil); + } + } + } + else if (flags & FB_ZBUFFER) { + if (flags & FB_ZTEXTURE) { + R_CreateTexbuffer( buffer->depth, width, height, qfalse, + GL_UNSIGNED_INT, GL_DEPTH_COMPONENT); + + qglFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, + GL_TEXTURE_2D, *buffer->depth, 0); + } + else { + R_CreateRenderbuffer(buffer->depth, width, height, GL_DEPTH_STENCIL_EXT); + qglFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, + GL_DEPTH_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, + *buffer->depth); + } + } + + if (flags & FB_BACKBUFFER) { + R_CreateTexbuffer( buffer->back, width, height, filter, + GL_UNSIGNED_BYTE, GL_RGBA); + + //we link to the second colour attachment + qglFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, + GL_TEXTURE_2D, *buffer->back, 0); + } + + //create the main colour buffer + R_CreateTexbuffer( buffer->front, width, height, filter, + GL_UNSIGNED_BYTE, GL_RGBA); + + qglFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, *buffer->front, 0); + + buffer->renderbuff = FB_FRONT; + + return R_CheckFramebufferStatus("FBO creation") ? 0 : -1 ; +} + +qboolean R_TestFbuffer_SeparateDS( void ) { + int width = 512; + int height = 512; + qboolean status; + + //try and get a perfect separate stencil/zbuffer + //this does not work on any hardware i know, but might in the future. + GLuint buffers[3]; //three buffers GL_UNSIGNED_INT_24_8_EXT + qglGenFramebuffersEXT(3, buffers); + qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, buffers[0]); + + //create the depth buffer + R_CreateRenderbuffer( &buffers[2], width, height, GL_DEPTH_COMPONENT); + + //stencil buffer as a render buffer + R_CreateRenderbuffer( &buffers[2], width, height, GL_STENCIL_INDEX8_EXT); + //attach the textures/renderbuffers to the framebuffer + qglFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, buffers[1]); + qglFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, buffers[2]); + + //create our framebuffer context + + R_CreateTexbuffer( &buffers[0], width, height, qfalse, + GL_UNSIGNED_BYTE, GL_RGBA); + + //shall we link our texture to the frame buffer? yes! + qglFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, buffers[0], 0); + + status = R_CheckFramebufferStatus("separate depth stencil"); + + qglDeleteFramebuffersEXT(1, &buffers[0]); + qglDeleteTextures(1, &buffers[0]); + qglDeleteRenderbuffersEXT(1, &buffers[1]); + qglDeleteRenderbuffersEXT(1, &buffers[2]); + + return status; +} + +qboolean R_TestFbuffer_PackedDS( void ) { + int width = 512; + int height = 512; + qboolean status; + GLuint buffers[2]; + + //try and get a packed separate stencil/zbuffer + qglGenFramebuffersEXT(2, buffers); + qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, buffers[0]); + + //create the buffer + R_CreateRenderbuffer( &buffers[1], width, height, GL_DEPTH_STENCIL_EXT); + //attach the textures/renderbuffers to the framebuffer + qglFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, buffers[1]); + qglFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, buffers[1]); + + qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, buffers[1]); + //create our framebuffer context + + R_CreateTexbuffer( &buffers[0], width, height, qfalse, + GL_UNSIGNED_BYTE, GL_RGBA); + + //shall we link our texture to the frame buffer? yes! + qglFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, buffers[0], 0); + + status = R_CheckFramebufferStatus("packed_depth_stencil"); + + qglDeleteFramebuffersEXT(1, &buffers[0]); + qglDeleteTextures(1, &buffers[0]); + qglDeleteRenderbuffersEXT(1, &buffers[1]); + + return status; +} + +qboolean R_TestFbuffer_texD( void ) { + int width = 512; + int height = 512; + qboolean status; + GLuint buffers[2]; + + //try and get a packed separate stencil/zbuffer + qglGenFramebuffersEXT(2, buffers); + qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, buffers[0]); + + //create the buffer + + R_CreateTexbuffer( &buffers[0], width, height, qfalse, + GL_UNSIGNED_INT, GL_DEPTH_COMPONENT); + + qglFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, + GL_TEXTURE_2D, buffers[1], 0); + + //create our framebuffer context + + R_CreateTexbuffer( &buffers[0], width, height, qfalse, + GL_UNSIGNED_BYTE, GL_RGBA); + + //shall we link our texture to the frame buffer? yes! + qglFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, buffers[0], 0); + + status = R_CheckFramebufferStatus("depth texture"); + + qglDeleteFramebuffersEXT(1, &buffers[0]); + qglDeleteTextures(1, &buffers[0]); + qglDeleteFramebuffersEXT(1, &buffers[1]); + qglDeleteTextures(1, &buffers[1]); + + return status; +} + + +// for shader debugging +void printShaderInfoLog(GLuint obj) +{ + int infologLength = 0; + int charsWritten = 0; + char *infoLog; + + qglGetShaderiv(obj, GL_INFO_LOG_LENGTH,&infologLength); + + if (infologLength > 1) + { + infoLog = (char *)ri.Malloc(infologLength); + qglGetShaderInfoLog(obj, infologLength, &charsWritten, infoLog); + ri.Printf( PRINT_ALL, "----- Shader InfoLog -----\n" );; + ri.Printf( PRINT_ALL, infoLog ); + ri.Free(infoLog); + } +} + +void printProgramInfoLog(GLuint obj) +{ + int infologLength = 0; + int charsWritten = 0; + char *infoLog; + + qglGetProgramiv(obj, GL_INFO_LOG_LENGTH,&infologLength); + + if (infologLength > 1) + { + infoLog = (char *)ri.Malloc(infologLength); + qglGetProgramInfoLog(obj, infologLength, &charsWritten, infoLog); + ri.Printf( PRINT_ALL, "----- Program InfoLog -----\n" );; + ri.Printf( PRINT_ALL, infoLog ); + ri.Free(infoLog); + } +} + +void R_Build_glsl(struct glslobj *obj) { + GLuint vert_shader, frag_shader, program; + + vert_shader = qglCreateShader(GL_VERTEX_SHADER); + frag_shader = qglCreateShader(GL_FRAGMENT_SHADER); + + qglShaderSource(vert_shader, obj->vert_numSources, obj->vertex_glsl, NULL); + qglShaderSource(frag_shader, obj->frag_numSources, obj->fragment_glsl, NULL); + + printShaderInfoLog(vert_shader); + printShaderInfoLog(frag_shader); + + qglCompileShader(vert_shader); + qglCompileShader(frag_shader); + + program = qglCreateProgram(); + qglAttachShader(program, vert_shader); + qglAttachShader(program, frag_shader); + qglLinkProgram(program); + + printProgramInfoLog(program); + + obj->vertex = vert_shader; + obj->fragment = frag_shader; + obj->program = program; + +} + +void R_Delete_glsl(struct glslobj *obj) { + qglDeleteProgram(obj->program); + qglDeleteShader(obj->vertex); + qglDeleteShader(obj->fragment); +} + +struct glslobj glslBlur; + +void R_FrameBuffer_BlurInit( void ) { + int blur_size ; + //inits our blur code; + if (!needBlur) { + return; + } + // Force blur_size to be at least 2 + blur_size = ( r_ext_framebuffer_blur_size->integer < 2 ) ? 2 : r_ext_framebuffer_blur_size->integer; + + R_CreateFBuffer( &gaussblurBuffer, blur_size, blur_size, FB_BACKBUFFER | FB_SMOOTH); + qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + + //create our glsl shader + glslBlur.vert_numSources = 1; + glslBlur.frag_numSources = 2; + + glslBlur.vertex_glsl = ri.Malloc(sizeof(char *) * glslBlur.vert_numSources); + glslBlur.vertex_glsl[0] = glslBase_vert; + + glslBlur.fragment_glsl = ri.Malloc(sizeof(char *) * glslBlur.frag_numSources); + switch (r_ext_framebuffer_blur_samples->integer) { + case (5): + glslBlur.fragment_glsl[0] = glslGauss5; + break; + case (7): + glslBlur.fragment_glsl[0] = glslGauss7; + break; + case (9): + glslBlur.fragment_glsl[0] = glslGauss9; + break; + default: + glslBlur.fragment_glsl[0] = glslGauss9; + break; + } + glslBlur.fragment_glsl[1] = glslBlurMain; + + R_Build_glsl(&glslBlur); +} + +void R_FrameBuffer_BlurDraw( GLuint *srcTex ) { + int fb_size ; + GLuint program, loc; + + if (!needBlur) { + return; + } + // Force fb_size to be at least 2 like blur_size + fb_size = ( r_ext_framebuffer_blur_size->integer < 2 ) ? 2 : r_ext_framebuffer_blur_size->integer; + + // first we draw the framebuffer into the blur buffer before any fragment + // programs are used is quicker, the rational behind this is that we want + // as many texels to fit inside the texture cache as possible for the + // gaussian sampling + qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, gaussblurBuffer.fbo); + glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); + R_SetGL2DSize(fb_size, fb_size); + qglUseProgram(0); + R_DrawQuad( *srcTex, fb_size, fb_size); + + //now we do the first gaussian pass + + glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT); + R_SetGL2DSize(fb_size, fb_size); + + program = glslBlur.program; + qglUseProgram(0); + qglUseProgram(program); + + //find and set the samplers + //set the texture number... silly this really. oh well thats glsl + loc = qglGetUniformLocation(program, "srcSampler"); + qglUniform1i(loc, 0); + loc = qglGetUniformLocation(program, "blurSize"); + qglUniform2f(loc, r_ext_framebuffer_blur_ammount->value / 100.0, 0.0); + + R_DrawQuad( *gaussblurBuffer.front, fb_size, fb_size); + + //we do the second pass of the blur here + glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); + loc = qglGetUniformLocation(program, "blurSize"); + qglUniform2f(loc, 0.0, r_ext_framebuffer_blur_ammount->value / 100.0); + + R_SetGL2DSize(fb_size, fb_size); + R_DrawQuad( *gaussblurBuffer.back, fb_size, fb_size); + qglUseProgram(0); + + //finally the FRONT buffer in the fbo is given the blurred image +} + +void R_FrameBuffer_BlurDelete( void ) { + + R_Delete_glsl(&glslBlur); + ri.Free(glslBlur.vertex_glsl); + ri.Free(glslBlur.fragment_glsl); + + R_DeleteFBuffer(&gaussblurBuffer); +} + +struct glslobj glslRoto; + +void R_FrameBuffer_RotoInit( struct r_fbuffer *src ) { + + // check to see if we need to create a framebuffer for this (only if there + // is a shader running after this) + + //create our glsl shader + glslRoto.vert_numSources = 1; + glslRoto.frag_numSources = 3; + + glslRoto.vertex_glsl = ri.Malloc(sizeof(char *) * glslRoto.vert_numSources); + glslRoto.vertex_glsl[0] = glslBase_vert; + + glslRoto.fragment_glsl = ri.Malloc(sizeof(char *) * glslRoto.frag_numSources); + glslRoto.fragment_glsl[0] = glslToonColour; + + if ((r_ext_framebuffer_rotoscope_zedge->integer) && (src->modeFlags & FB_ZTEXTURE)) { + glslRoto.fragment_glsl[1] = glslSobelZ; + glslRoto.fragment_glsl[2] = glslRotoscopeZ; + } else { + glslRoto.fragment_glsl[1] = glslSobel; + glslRoto.fragment_glsl[2] = glslRotoscope; + } + + R_Build_glsl(&glslRoto); +} + +void R_FrameBuffer_RotoDraw( struct r_fbuffer *src, GLuint *srcTex ) { + GLuint program, loc; + + program = glslRoto.program; + qglUseProgram(0); + qglUseProgram(program); + + R_SetGL2DSize(glConfig.vidWidth, glConfig.vidHeight); + + //find and set the samplers + loc = qglGetUniformLocation(program, "srcSampler"); + qglUniform1i(loc, 0); + loc = qglGetUniformLocation(program, "depthSampler"); + qglUniform1i(loc, 1); + loc = qglGetUniformLocation(program, "texelSize"); + qglUniform2f(loc, 1.0 / glConfig.vidWidth, 1.0 / glConfig.vidHeight); + + if ((r_ext_framebuffer_rotoscope_zedge->integer) && (src->modeFlags & FB_ZTEXTURE)) { + R_DrawQuadMT( *srcTex, *src->depth, + glConfig.vidWidth, glConfig.vidHeight); + } else { + R_DrawQuad( *srcTex, glConfig.vidWidth, glConfig.vidHeight); + } + qglUseProgram(0); +} + +void R_FrameBuffer_RotoDelete( void ) { + R_Delete_glsl(&glslRoto); + ri.Free(glslRoto.vertex_glsl); + ri.Free(glslRoto.fragment_glsl); +} + +struct glslobj glslBloom; + +void R_FrameBuffer_BloomInit( void ) { + //we need blur for this + needBlur = qtrue; + //create our glsl shader + glslBloom.vert_numSources = 1; + glslBloom.frag_numSources = 1; + glslBloom.vertex_glsl = ri.Malloc(sizeof(char *) * glslBloom.frag_numSources); + glslBloom.vertex_glsl[0] = glslBase_vert; + glslBloom.fragment_glsl = ri.Malloc(sizeof(char *) * glslBloom.frag_numSources); + glslBloom.fragment_glsl[0] = glslSigScreen; + + R_Build_glsl(&glslBloom); +} + +void R_FrameBuffer_BloomDraw( GLuint *srcTex ) { + GLuint program, loc; + + program = glslBloom.program; + qglUseProgram(0); + qglUseProgram(program); + + R_SetGL2DSize(glConfig.vidWidth, glConfig.vidHeight); + + //find and set the samplers + loc = qglGetUniformLocation(program, "srcSampler"); + qglUniform1i(loc, 0); + loc = qglGetUniformLocation(program, "blurSampler"); + qglUniform1i(loc, 1); + loc = qglGetUniformLocation(program, "brightness"); + qglUniform1f(loc, r_ext_framebuffer_bloom_brightness->value); + loc = qglGetUniformLocation(program, "sharpness"); + qglUniform1f(loc, r_ext_framebuffer_bloom_sharpness->value); + + R_DrawQuadMT( *srcTex, *gaussblurBuffer.front, + glConfig.vidWidth, glConfig.vidHeight); + qglUseProgram(0); + //quick test to just see the blur + //R_DrawQuad(*gaussblurBuffer.front, glConfig.vidWidth, glConfig.vidHeight); +} + +void R_FrameBuffer_BloomDelete( void ) { + R_Delete_glsl(&glslBloom); + ri.Free(glslBloom.vertex_glsl); + ri.Free(glslBloom.fragment_glsl); +} + +static void R_FrameBuffer_Draw( void ) { + //draws the framebuffer to the screen, pretty simple really. + R_SetGL2DSize(glConfig.vidWidth, glConfig.vidHeight); + R_DrawQuad( *(screenBuffer.front), glConfig.vidWidth, glConfig.vidHeight); +} + +void R_FrameBuffer_Init( void ) { + GLint maxbuffers; + int screenbuff_flags = 0x00; + int status; + + needBlur = qfalse; + if (!framebufferSupported || !glslSupported) { + ri.Printf( PRINT_WARNING, "WARNING: Framebuffer rendering path disabled\n"); + return; + } + + if (r_ext_framebuffer->integer != 1) { + return; + } + + ri.Printf( PRINT_ALL, "----- Enabling FrameBuffer Path -----\n" ); + + R_CheckFramebufferStatus("FBO current status"); + + //lets see what works and what doesn't + packedDepthStencilSupported = qfalse; + depthTextureSupported = qfalse; + separateDepthStencilSupported = qfalse; + + if (R_TestFbuffer_PackedDS()) { + packedDepthStencilSupported = qtrue; + } else { + ri.Printf( PRINT_WARNING, "WARNING: packed_depth_stencil failed\n"); + } + if (R_TestFbuffer_SeparateDS()) { + separateDepthStencilSupported = qtrue; + } else { + ri.Printf( PRINT_WARNING, "WARNING: separate depth stencil failed\n"); + } + if (R_TestFbuffer_texD()) { + depthTextureSupported = qtrue; + } else { + ri.Printf( PRINT_WARNING, "WARNING: depth texture failed\n"); + } + + qglGetIntegerv(GL_MAX_COLOR_ATTACHMENTS_EXT, &maxbuffers); + if (maxbuffers < 2) { + framebufferSupported = qfalse; + ri.Cvar_Set("r_ext_framebuffer","0"); + ri.Printf( PRINT_WARNING, "ERROR: Framebuffer rendering path disabled: Not enough color buffers available\n"); + Cbuf_AddText( "vid_restart" ); // Reset the video without FBO + return; + } + + //set our main screen flags + if ((glConfig.stencilBits > 0)) { + if ( packedDepthStencilSupported ) { + screenbuff_flags |= FB_PACKED | FB_STENCIL; + } + else if ( separateDepthStencilSupported ) { + screenbuff_flags |= FB_SEPARATEZS | FB_STENCIL; + } + } + + if ((depthTextureSupported) && (r_ext_framebuffer_rotoscope_zedge->integer)) { + screenbuff_flags |= FB_ZTEXTURE; + } + screenbuff_flags |= FB_BACKBUFFER; + + screenbuff_flags |= FB_ZBUFFER; + + //create our main frame buffer + status = R_CreateFBuffer( &screenBuffer, glConfig.vidWidth, + glConfig.vidHeight, screenbuff_flags); + + if (status) { + // if the main fbuffer failed then we should disable framebuffer + // rendering + framebufferSupported = qfalse; + ri.Cvar_Set("r_ext_framebuffer","0"); + ri.Printf( PRINT_WARNING, "ERROR: Framebuffer rendering path disabled: Framebuffer creation failed\n"); + Cbuf_AddText( "vid_restart" ); // Reset the video without FBO + return; + } + + //init our effects + if (r_ext_framebuffer_rotoscope->integer == 1) { + R_FrameBuffer_RotoInit(&screenBuffer); + } + + if (r_ext_framebuffer_bloom->integer == 1) { + R_FrameBuffer_BloomInit(); + } + //we don't need an if here, if any effects before need a blur then this + //auto detects that its needed + R_FrameBuffer_BlurInit(); + + qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, *screenBuffer.front); +} + +void R_FrameBuffer_BeginFrame( void ) { + if (!framebufferSupported || !glslSupported) { + return; + } + if (r_ext_framebuffer->integer != 1) { + return; + } + + // Be sure to reset the frame buffer + if (rendered) + R_FrameBuffer_ResetDraw(); + + // Reset the rendered flag + rendered = qfalse; +} + +void R_FrameBuffer_EndFrame( void ) { + qboolean screenDrawDone = 0; + GLuint *srcBuffer ; + + if (!framebufferSupported || !glslSupported) { + return; + } + if (r_ext_framebuffer->integer != 1) { + return; + } + if (rendered) { + return; + } + + //don't flip if drawing to front buffer + if ( Q_stricmp( r_drawBuffer->string, "GL_FRONT" ) == 0 ) { + return; + } + + GL_State( GLS_DEPTHTEST_DISABLE ); + GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO ); + + qglColor4f( 1, 1, 1, 1 ); + + srcBuffer = screenBuffer.front; + + if (r_ext_framebuffer_rotoscope->integer == 1) { + if (r_ext_framebuffer_bloom->integer == 1) { + //we need to draw to the back buffer + glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT); + R_FrameBuffer_RotoDraw(&screenBuffer, srcBuffer); + srcBuffer = screenBuffer.back; + glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); + } + else { + qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + R_FrameBuffer_RotoDraw(&screenBuffer, srcBuffer); + screenDrawDone = 1; + } + } + //everything before this point does not need blurred surfaces (or can do + //with the last frames burred surface) + //call the blur code, it auto detects weather its needed :) + R_FrameBuffer_BlurDraw(srcBuffer); + + if (r_ext_framebuffer_bloom->integer == 1) { + qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + R_FrameBuffer_BloomDraw(srcBuffer); + screenDrawDone = 1; + } + + if (screenDrawDone == 0) { + qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + R_FrameBuffer_Draw(); + } + + //Any other draw will be rendered other that view + rendered = qtrue; +} + +void R_FrameBuffer_ResetDraw( void ) { + if (!framebufferSupported || !glslSupported) { + return; + } + if (r_ext_framebuffer->integer != 1) { + return; + } + //We re-bind our framebuffer so everything gets rendered into it. + qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, screenBuffer.fbo); + glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); + + // Reset the rendered flag + rendered = qfalse; +} + +void R_FrameBuffer_Shutdown( void ) { + //cleanup + if (!framebufferSupported || !glslSupported) { + return; + } + if (r_ext_framebuffer->integer != 1) { + return; + } + qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + + if (r_ext_framebuffer_bloom->integer == 1) { + R_FrameBuffer_BloomDelete(); + } + if (needBlur) { + R_FrameBuffer_BlurDelete(); + } + if ( r_ext_framebuffer_rotoscope->integer == 1) { + R_FrameBuffer_RotoDelete(); + } + + //delete the main screen buffer + R_DeleteFBuffer(&screenBuffer); +} Index: code/renderer/tr_backend.c =================================================================== --- code/renderer/tr_backend.c (revision 1752) +++ code/renderer/tr_backend.c (working copy) @@ -750,6 +750,11 @@ } R_SyncRenderThread(); +#ifdef FRAMEBUFFER_AND_GLSL_SUPPORT + // Needed here to support cinematics + R_FrameBuffer_EndFrame(); +#endif + // we definately want to sync every frame for the cinematics qglFinish(); @@ -1108,6 +1113,14 @@ ri.Hunk_FreeTempMemory( stencilReadback ); } +#ifdef FRAMEBUFFER_AND_GLSL_SUPPORT + // Check to render Framebuffer if still not done + if (!backEnd.projection2D) { + R_FrameBuffer_EndFrame(); + } + //reset the framebuffer so next frame will render in the right place buffer + R_FrameBuffer_ResetDraw(); +#endif if ( !glState.finishCalled ) { qglFinish(); @@ -1147,22 +1160,41 @@ data = RB_SetColor( data ); break; case RC_STRETCH_PIC: +#ifdef FRAMEBUFFER_AND_GLSL_SUPPORT + R_FrameBuffer_EndFrame(); +#endif data = RB_StretchPic( data ); break; case RC_DRAW_SURFS: data = RB_DrawSurfs( data ); break; case RC_DRAW_BUFFER: +#ifdef FRAMEBUFFER_AND_GLSL_SUPPORT + R_FrameBuffer_BeginFrame(); +#endif data = RB_DrawBuffer( data ); break; case RC_SWAP_BUFFERS: data = RB_SwapBuffers( data ); break; + //these two use a hack to let them copy the framebuffer effects too case RC_SCREENSHOT: +#ifdef FRAMEBUFFER_AND_GLSL_SUPPORT + R_FrameBufferUnBind(); +#endif data = RB_TakeScreenshotCmd( data ); +#ifdef FRAMEBUFFER_AND_GLSL_SUPPORT + R_FrameBufferBind(); +#endif break; case RC_VIDEOFRAME: +#ifdef FRAMEBUFFER_AND_GLSL_SUPPORT + R_FrameBufferUnBind(); +#endif data = RB_TakeVideoFrameCmd( data ); +#ifdef FRAMEBUFFER_AND_GLSL_SUPPORT + R_FrameBufferBind(); +#endif break; case RC_COLORMASK: data = RB_ColorMask(data); Index: code/null/null_glimp.c =================================================================== --- code/null/null_glimp.c (revision 1752) +++ code/null/null_glimp.c (working copy) @@ -31,7 +31,41 @@ void ( * qglLockArraysEXT)( int, int); void ( * qglUnlockArraysEXT) ( void ); +#ifdef FRAMEBUFFER_AND_GLSL_SUPPORT +//added framebuffer extensions + void ( * glGenFramebuffersEXT )(GLsizei, GLuint *); + void ( * glBindFramebufferEXT )(GLenum, GLuint); + void ( * glGenRenderbuffersEXT )(GLsizei, GLuint *); + void ( * glBindRenderbufferEXT )(GLenum, GLuint); + void ( * glRenderbufferStorageEXT )(GLenum, GLenum, GLsizei, GLsizei); + void ( * glRenderbufferStorageMultisampleEXT )(GLenum, GLsizei, GLenum, GLsizei, GLsizei); + void ( * glFramebufferRenderbufferEXT )(GLenum, GLenum, GLenum, GLuint); + void ( * glFramebufferTexture2DEXT )(GLenum, GLenum, GLenum, GLuint, GLint); + GLenum ( * glCheckFramebufferStatusEXT )(GLenum); + void ( * glDeleteFramebuffersEXT )(GLsizei, const GLuint *); + void ( * glDeleteRenderbuffersEXT )(GLsizei, const GLuint *); +//added fragment/vertex program extensions + void ( * glAttachShader) (GLuint, GLuint); + void ( * glBindAttribLocation) (GLuint, GLuint, const GLchar *); + void ( * glCompileShader) (GLuint); +GLuint ( * glCreateProgram) (void); +GLuint ( * glCreateShader) (GLenum); +void ( * glDeleteProgram) (GLuint); +void ( * glDeleteShader) (GLuint); +void ( * glShaderSource) (GLuint, GLsizei, const GLchar* *, const GLint *); +void ( * glLinkProgram) (GLuint); +void ( * glUseProgram) (GLuint); +GLint ( * glGetUniformLocation) (GLuint, const GLchar *); +void ( * glUniform1f) (GLint, GLfloat); +void ( * glUniform2f) (GLint, GLfloat, GLfloat); +void ( * glUniform1i) (GLint, GLint); +void ( * glGetProgramiv) (GLuint, GLenum, GLint *); +void ( * glGetProgramInfoLog) (GLuint, GLsizei, GLsizei *, GLchar *); +void ( * glGetShaderiv) (GLuint, GLenum, GLint *); +void ( * glGetShaderInfoLog) (GLuint, GLsizei, GLsizei *, GLchar *); +#endif + void GLimp_EndFrame( void ) { } Index: Makefile =================================================================== --- Makefile (revision 1752) +++ Makefile (working copy) @@ -155,6 +155,10 @@ DEBUG_CFLAGS=-g -O0 endif +ifndef FRAMEBUFFER_AND_GLSL_SUPPORT +FRAMEBUFFER_AND_GLSL_SUPPORT=0 +endif + ############################################################################# BD=$(BUILD_DIR)/debug-$(PLATFORM)-$(ARCH) @@ -876,6 +880,10 @@ endif endif +ifeq ($(FRAMEBUFFER_AND_GLSL_SUPPORT),1) + CLIENT_CFLAGS += -DFRAMEBUFFER_AND_GLSL_SUPPORT +endif + ifeq ($(USE_INTERNAL_ZLIB),1) BASE_CFLAGS += -DNO_GZIP ifneq ($(USE_LOCAL_HEADERS),1) @@ -1449,6 +1457,12 @@ $(B)/client/con_log.o \ $(B)/client/sys_main.o +ifeq ($(FRAMEBUFFER_AND_GLSL_SUPPORT),1) + Q3OBJ += \ + $(B)/client/tr_framebuffer.o \ + $(B)/client/tr_glslprogs.o +endif + ifeq ($(ARCH),i386) Q3OBJ += \ $(B)/client/snd_mixa.o \