Index: code/renderer/tr_local.h =================================================================== --- code/renderer/tr_local.h (Revision 2216) +++ code/renderer/tr_local.h (Arbeitskopie) @@ -903,6 +903,8 @@ qboolean projection2D; // if qtrue, drawstretchpic doesn't need to change modes byte color2D[4]; qboolean vertexes2D; // shader needs to be finished + qboolean doneBloom; // done bloom this frame + qboolean doneSurfaces; // done any 3d surfaces already trRefEntity_t entity2D; // currentEntity will point at this when doing 2D rendering } backEndState_t; @@ -1147,6 +1149,10 @@ extern cvar_t *r_marksOnTriangleMeshes; +extern cvar_t *r_lensReflection1; +extern cvar_t *r_lensReflection2; +extern cvar_t *r_lensReflectionBrightness; + //==================================================================== float R_NoiseGet4f( float x, float y, float z, float t ); @@ -1361,6 +1367,7 @@ extern shaderCommands_t tess; +void RB_SetGL2D (void); void RB_BeginSurface(shader_t *shader, int fogNum ); void RB_EndSurface(void); void RB_CheckOverflow( int verts, int indexes ); @@ -1747,5 +1754,8 @@ void R_DoneFreeType( void ); void RE_RegisterFont(const char *fontName, int pointSize, fontInfo_t *font); +//Bloom Stuff +void R_BloomInit( void ); +void R_BloomScreen( void ); #endif //TR_LOCAL_H Index: code/renderer/tr_init.c =================================================================== --- code/renderer/tr_init.c (Revision 2216) +++ code/renderer/tr_init.c (Arbeitskopie) @@ -168,6 +168,11 @@ cvar_t *r_maxpolyverts; int max_polyverts; +// tcpp +cvar_t *r_lensReflection1; +cvar_t *r_lensReflection2; +cvar_t *r_lensReflectionBrightness; + /* ** InitOpenGL ** @@ -1127,6 +1132,10 @@ r_maxpolys = ri.Cvar_Get( "r_maxpolys", va("%d", MAX_POLYS), 0); r_maxpolyverts = ri.Cvar_Get( "r_maxpolyverts", va("%d", MAX_POLYVERTS), 0); + r_lensReflection1 = ri.Cvar_Get( "r_lensReflection1", "0" , CVAR_ARCHIVE ); // sharp reflection + r_lensReflection2 = ri.Cvar_Get( "r_lensReflection2", "0" , CVAR_ARCHIVE ); // fuzzy reflection + r_lensReflectionBrightness = ri.Cvar_Get( "r_lensReflectionBrightness", "0.5" , CVAR_ARCHIVE ); + // make sure all the commands added here are also // removed in R_Shutdown ri.Cmd_AddCommand( "imagelist", R_ImageList_f ); @@ -1199,6 +1208,8 @@ R_NoiseInit(); R_Register(); + + R_BloomInit(); max_polys = r_maxpolys->integer; if (max_polys < MAX_POLYS) Index: code/renderer/tr_flares.c =================================================================== --- code/renderer/tr_flares.c (Revision 2216) +++ code/renderer/tr_flares.c (Arbeitskopie) @@ -313,6 +313,7 @@ int iColor[3]; float distance, intensity, factor; byte fogFactors[3] = {255, 255, 255}; + int ind=0; backEnd.pc.c_flareRenders++; @@ -420,6 +421,194 @@ tess.indexes[tess.numIndexes++] = 2; tess.indexes[tess.numIndexes++] = 3; + ind+=4; + + // reflections -- tcpparena + + if(r_lensReflection1->integer){ + + // renders sharp lens flare. + + float cx, cy; + float dx, dy; + float size2; + const float poses[]= {-.15f, 0.6f, -.1f, -.6f, -1.8f}; + const float sizes[]= {0.14f, 0.2f, 0.1f, 0.2f, 1.0f}; + int brightness1[]= {8,25, 40, 26, 10}; // red + int brightness2[]= {15,23, 25, 30, 5}; // green + int brightness3[]= {12,20, 30, 28, 10}; // blue + const float r3_2=0.866025403784439f; + int n; + cx=backEnd.viewParms.viewportX+(backEnd.viewParms.viewportWidth>>1); + cy=backEnd.viewParms.viewportY+(backEnd.viewParms.viewportHeight>>1); + for(n=0;n<5;n++){ + dx=(f->windowX-cx)*poses[n]+cx; + dy=(f->windowY-cy)*poses[n]+cy; + size2=sizes[n]*backEnd.viewParms.viewportWidth*.25f; + + brightness1[n]=(int)(brightness1[n]*r_lensReflectionBrightness->value); + brightness2[n]=(int)(brightness2[n]*r_lensReflectionBrightness->value); + brightness3[n]=(int)(brightness3[n]*r_lensReflectionBrightness->value); + + tess.xyz[tess.numVertexes][0] = dx-size2; + tess.xyz[tess.numVertexes][1] = dy; + tess.texCoords[tess.numVertexes][0][0] = .5f; + tess.texCoords[tess.numVertexes][0][1] = .5f; + tess.vertexColors[tess.numVertexes][0] = (iColor[0]*brightness1[n])>>8; + tess.vertexColors[tess.numVertexes][1] = (iColor[1]*brightness2[n])>>8; + tess.vertexColors[tess.numVertexes][2] = (iColor[2]*brightness3[n])>>8; + tess.vertexColors[tess.numVertexes][3] = 255; + tess.numVertexes++; + + tess.xyz[tess.numVertexes][0] = dx-size2*.5f; + tess.xyz[tess.numVertexes][1] = dy-size2*r3_2; + tess.texCoords[tess.numVertexes][0][0] = .5f; + tess.texCoords[tess.numVertexes][0][1] = .5f; + tess.vertexColors[tess.numVertexes][0] = (iColor[0]*brightness1[n])>>8; + tess.vertexColors[tess.numVertexes][1] = (iColor[1]*brightness2[n])>>8; + tess.vertexColors[tess.numVertexes][2] = (iColor[2]*brightness3[n])>>8; + tess.vertexColors[tess.numVertexes][3] = 255; + tess.numVertexes++; + + tess.xyz[tess.numVertexes][0] = dx+size2*.5f; + tess.xyz[tess.numVertexes][1] = dy-size2*r3_2; + tess.texCoords[tess.numVertexes][0][0] = .5f; + tess.texCoords[tess.numVertexes][0][1] = .5f; + tess.vertexColors[tess.numVertexes][0] = (iColor[0]*brightness1[n])>>8; + tess.vertexColors[tess.numVertexes][1] = (iColor[1]*brightness2[n])>>8; + tess.vertexColors[tess.numVertexes][2] = (iColor[2]*brightness3[n])>>8; + tess.vertexColors[tess.numVertexes][3] = 255; + tess.numVertexes++; + + tess.xyz[tess.numVertexes][0] = dx+size2; + tess.xyz[tess.numVertexes][1] = dy; + tess.texCoords[tess.numVertexes][0][0] = .5f; + tess.texCoords[tess.numVertexes][0][1] = .5f; + tess.vertexColors[tess.numVertexes][0] = (iColor[0]*brightness1[n])>>8; + tess.vertexColors[tess.numVertexes][1] = (iColor[1]*brightness2[n])>>8; + tess.vertexColors[tess.numVertexes][2] = (iColor[2]*brightness3[n])>>8; + tess.vertexColors[tess.numVertexes][3] = 255; + tess.numVertexes++; + + tess.xyz[tess.numVertexes][0] = dx+size2*.5f; + tess.xyz[tess.numVertexes][1] = dy+size2*r3_2; + tess.texCoords[tess.numVertexes][0][0] = .5f; + tess.texCoords[tess.numVertexes][0][1] = .5f; + tess.vertexColors[tess.numVertexes][0] = (iColor[0]*brightness1[n])>>8; + tess.vertexColors[tess.numVertexes][1] = (iColor[1]*brightness2[n])>>8; + tess.vertexColors[tess.numVertexes][2] = (iColor[2]*brightness3[n])>>8; + tess.vertexColors[tess.numVertexes][3] = 255; + tess.numVertexes++; + + tess.xyz[tess.numVertexes][0] = dx-size2*.5f; + tess.xyz[tess.numVertexes][1] = dy+size2*r3_2; + tess.texCoords[tess.numVertexes][0][0] = .5f; + tess.texCoords[tess.numVertexes][0][1] = .5f; + tess.vertexColors[tess.numVertexes][0] = (iColor[0]*brightness1[n])>>8; + tess.vertexColors[tess.numVertexes][1] = (iColor[1]*brightness2[n])>>8; + tess.vertexColors[tess.numVertexes][2] = (iColor[2]*brightness3[n])>>8; + tess.vertexColors[tess.numVertexes][3] = 255; + tess.numVertexes++; + + tess.indexes[tess.numIndexes++] = 2+ind; + tess.indexes[tess.numIndexes++] = 1+ind; + tess.indexes[tess.numIndexes++] = 0+ind; + tess.indexes[tess.numIndexes++] = 3+ind; + tess.indexes[tess.numIndexes++] = 2+ind; + tess.indexes[tess.numIndexes++] = 0+ind; + tess.indexes[tess.numIndexes++] = 4+ind; + tess.indexes[tess.numIndexes++] = 3+ind; + tess.indexes[tess.numIndexes++] = 0+ind; + tess.indexes[tess.numIndexes++] = 5+ind; + tess.indexes[tess.numIndexes++] = 4+ind; + tess.indexes[tess.numIndexes++] = 0+ind; + + ind+=6; + + } + } + + if(r_lensReflection2->integer){ + + // renders fuzzy lens flare. + + float cx, cy; + float dx, dy; + float size2; + const float poses[]= {1.7f, -0.2f}; + const float sizes[]= {1.2f, 0.2f}; + int brightness1[]= {25, 40}; // red + int brightness2[]= {35, 10}; // green + int brightness3[]= {30, 15}; // blue + int n; + cx=backEnd.viewParms.viewportX+(backEnd.viewParms.viewportWidth>>1); + cy=backEnd.viewParms.viewportY+(backEnd.viewParms.viewportHeight>>1); + + + for(n=0;n<2;n++){ + dx=(f->windowX-cx)*poses[n]+cx; + dy=(f->windowY-cy)*poses[n]+cy; + size2=sizes[n]*backEnd.viewParms.viewportWidth*.25f; + + brightness1[n]=(int)(brightness1[n]*r_lensReflectionBrightness->value); + brightness2[n]=(int)(brightness2[n]*r_lensReflectionBrightness->value); + brightness3[n]=(int)(brightness3[n]*r_lensReflectionBrightness->value); + + tess.xyz[tess.numVertexes][0] = dx-size2; + tess.xyz[tess.numVertexes][1] = dy-size2; + tess.texCoords[tess.numVertexes][0][0] = 0.f; + tess.texCoords[tess.numVertexes][0][1] = 0.f; + tess.vertexColors[tess.numVertexes][0] = (iColor[0]*brightness1[n])>>8; + tess.vertexColors[tess.numVertexes][1] = (iColor[1]*brightness2[n])>>8; + tess.vertexColors[tess.numVertexes][2] = (iColor[2]*brightness3[n])>>8; + tess.vertexColors[tess.numVertexes][3] = 255; + tess.numVertexes++; + + tess.xyz[tess.numVertexes][0] = dx-size2; + tess.xyz[tess.numVertexes][1] = dy+size2; + tess.texCoords[tess.numVertexes][0][0] = 0.f; + tess.texCoords[tess.numVertexes][0][1] = 1.f; + tess.vertexColors[tess.numVertexes][0] = (iColor[0]*brightness1[n])>>8; + tess.vertexColors[tess.numVertexes][1] = (iColor[1]*brightness2[n])>>8; + tess.vertexColors[tess.numVertexes][2] = (iColor[2]*brightness3[n])>>8; + tess.vertexColors[tess.numVertexes][3] = 255; + tess.numVertexes++; + + tess.xyz[tess.numVertexes][0] = dx+size2; + tess.xyz[tess.numVertexes][1] = dy+size2; + tess.texCoords[tess.numVertexes][0][0] = 1.f; + tess.texCoords[tess.numVertexes][0][1] = 1.f; + tess.vertexColors[tess.numVertexes][0] = (iColor[0]*brightness1[n])>>8; + tess.vertexColors[tess.numVertexes][1] = (iColor[1]*brightness2[n])>>8; + tess.vertexColors[tess.numVertexes][2] = (iColor[2]*brightness3[n])>>8; + tess.vertexColors[tess.numVertexes][3] = 255; + tess.numVertexes++; + + tess.xyz[tess.numVertexes][0] = dx+size2; + tess.xyz[tess.numVertexes][1] = dy-size2; + tess.texCoords[tess.numVertexes][0][0] = 1.f; + tess.texCoords[tess.numVertexes][0][1] = 0.f; + tess.vertexColors[tess.numVertexes][0] = (iColor[0]*brightness1[n])>>8; + tess.vertexColors[tess.numVertexes][1] = (iColor[1]*brightness2[n])>>8; + tess.vertexColors[tess.numVertexes][2] = (iColor[2]*brightness3[n])>>8; + tess.vertexColors[tess.numVertexes][3] = 255; + tess.numVertexes++; + + tess.indexes[tess.numIndexes++] = 0+ind; + tess.indexes[tess.numIndexes++] = 1+ind; + tess.indexes[tess.numIndexes++] = 2+ind; + tess.indexes[tess.numIndexes++] = 0+ind; + tess.indexes[tess.numIndexes++] = 2+ind; + tess.indexes[tess.numIndexes++] = 3+ind; + + ind+=4; + + + } + } + + + RB_EndSurface(); } Index: code/renderer/tr_bloom.c =================================================================== --- code/renderer/tr_bloom.c (Revision 0) +++ code/renderer/tr_bloom.c (Revision 0) @@ -0,0 +1,453 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +// tr_bloom.c: 2D lighting post process effect + +#include "tr_local.h" + + +static cvar_t *r_bloom; +static cvar_t *r_bloom_sample_size; +static cvar_t *r_bloom_fast_sample; +static cvar_t *r_bloom_alpha; +static cvar_t *r_bloom_darken; +static cvar_t *r_bloom_intensity; +static cvar_t *r_bloom_diamond_size; + +/* +============================================================================== + + LIGHT BLOOMS + +============================================================================== +*/ + +static float Diamond8x[8][8] = +{ + { 0.0f, 0.0f, 0.0f, 0.1f, 0.1f, 0.0f, 0.0f, 0.0f, }, + { 0.0f, 0.0f, 0.2f, 0.3f, 0.3f, 0.2f, 0.0f, 0.0f, }, + { 0.0f, 0.2f, 0.4f, 0.6f, 0.6f, 0.4f, 0.2f, 0.0f, }, + { 0.1f, 0.3f, 0.6f, 0.9f, 0.9f, 0.6f, 0.3f, 0.1f, }, + { 0.1f, 0.3f, 0.6f, 0.9f, 0.9f, 0.6f, 0.3f, 0.1f, }, + { 0.0f, 0.2f, 0.4f, 0.6f, 0.6f, 0.4f, 0.2f, 0.0f, }, + { 0.0f, 0.0f, 0.2f, 0.3f, 0.3f, 0.2f, 0.0f, 0.0f, }, + { 0.0f, 0.0f, 0.0f, 0.1f, 0.1f, 0.0f, 0.0f, 0.0f } +}; + +static float Diamond6x[6][6] = +{ + { 0.0f, 0.0f, 0.1f, 0.1f, 0.0f, 0.0f, }, + { 0.0f, 0.3f, 0.5f, 0.5f, 0.3f, 0.0f, }, + { 0.1f, 0.5f, 0.9f, 0.9f, 0.5f, 0.1f, }, + { 0.1f, 0.5f, 0.9f, 0.9f, 0.5f, 0.1f, }, + { 0.0f, 0.3f, 0.5f, 0.5f, 0.3f, 0.0f, }, + { 0.0f, 0.0f, 0.1f, 0.1f, 0.0f, 0.0f } +}; + +static float Diamond4x[4][4] = +{ + { 0.3f, 0.4f, 0.4f, 0.3f, }, + { 0.4f, 0.9f, 0.9f, 0.4f, }, + { 0.4f, 0.9f, 0.9f, 0.4f, }, + { 0.3f, 0.4f, 0.4f, 0.3f } +}; + +static struct { + struct { + image_t *texture; + int width, height; + float readW, readH; + } effect; + struct { + image_t *texture; + int width, height; + float readW, readH; + } screen; + struct { + int width, height; + } work; + qboolean started; +} bloom; + + +static void ID_INLINE R_Bloom_Quad( int width, int height, float texX, float texY, float texWidth, float texHeight ) { + int x = 0; + int y = 0; + x = 0; + y += glConfig.vidHeight - height; + width += x; + height += y; + + texWidth += texX; + texHeight += texY; + + qglBegin( GL_QUADS ); + qglTexCoord2f( texX, texHeight ); + qglVertex2f( x, y ); + + qglTexCoord2f( texX, texY ); + qglVertex2f( x, height ); + + qglTexCoord2f( texWidth, texY ); + qglVertex2f( width, height ); + + qglTexCoord2f( texWidth, texHeight ); + qglVertex2f( width, y ); + qglEnd (); +} + + +/* +================= +R_Bloom_InitTextures +================= +*/ +static void R_Bloom_InitTextures( void ) +{ + byte *data; + + // find closer power of 2 to screen size + for (bloom.screen.width = 1;bloom.screen.width< glConfig.vidWidth;bloom.screen.width *= 2); + for (bloom.screen.height = 1;bloom.screen.height < glConfig.vidHeight;bloom.screen.height *= 2); + + bloom.screen.readW = glConfig.vidWidth / (float)bloom.screen.width; + bloom.screen.readH = glConfig.vidHeight / (float)bloom.screen.height; + + // find closer power of 2 to effect size + bloom.work.width = r_bloom_sample_size->integer; + bloom.work.height = bloom.work.width * ( glConfig.vidWidth / glConfig.vidHeight ); + + for (bloom.effect.width = 1;bloom.effect.width < bloom.work.width;bloom.effect.width *= 2); + for (bloom.effect.height = 1;bloom.effect.height < bloom.work.height;bloom.effect.height *= 2); + + bloom.effect.readW = bloom.work.width / (float)bloom.effect.width; + bloom.effect.readH = bloom.work.height / (float)bloom.effect.height; + + + // disable bloom if we can't handle a texture of that size + if( bloom.screen.width > glConfig.maxTextureSize || + bloom.screen.height > glConfig.maxTextureSize || + bloom.effect.width > glConfig.maxTextureSize || + bloom.effect.height > glConfig.maxTextureSize || + bloom.work.width > glConfig.vidWidth || + bloom.work.height > glConfig.vidHeight + ) { + ri.Cvar_Set( "r_bloom", "0" ); + Com_Printf( S_COLOR_YELLOW"WARNING: 'R_InitBloomTextures' too high resolution for light bloom, effect disabled\n" ); + return; + } + + // disable bloom if number of texture bits is less than 32 + if( r_texturebits->integer < 32 ) { + ri.Cvar_Set( "r_bloom", "0" ); + Com_Printf( S_COLOR_YELLOW"WARNING: 'R_InitBloomTextures' require 32-bit textures for light bloom, effect disabled\n" ); + return; + } + + data = ri.Hunk_AllocateTempMemory( bloom.screen.width * bloom.screen.height * 4 ); + Com_Memset( data, 0, bloom.screen.width * bloom.screen.height * 4 ); + bloom.screen.texture = R_CreateImage( "***bloom screen texture***", data, bloom.screen.width, bloom.screen.height, qfalse, qfalse, qfalse ); + ri.Hunk_FreeTempMemory( data ); + + data = ri.Hunk_AllocateTempMemory( bloom.effect.width * bloom.effect.height * 4 ); + Com_Memset( data, 0, bloom.effect.width * bloom.effect.height * 4 ); + bloom.effect.texture = R_CreateImage( "***bloom effect texture***", data, bloom.effect.width, bloom.effect.height, qfalse, qfalse, qfalse ); + ri.Hunk_FreeTempMemory( data ); + bloom.started = qtrue; +} + +/* +================= +R_InitBloomTextures +================= +*/ +void R_InitBloomTextures( void ) +{ + if( !r_bloom->integer ) + return; + memset( &bloom, 0, sizeof( bloom )); + R_Bloom_InitTextures (); +} + +/* +================= +R_Bloom_DrawEffect +================= +*/ +static void R_Bloom_DrawEffect( void ) +{ + GL_Bind( bloom.effect.texture ); + GL_State( GLS_DEPTHTEST_DISABLE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); + qglColor4f( r_bloom_alpha->value, r_bloom_alpha->value, r_bloom_alpha->value, 1.0f ); + R_Bloom_Quad( glConfig.vidWidth, glConfig.vidHeight, 0, 0, bloom.effect.readW, bloom.effect.readW ); +} + + +/* +================= +R_Bloom_GeneratexDiamonds +================= +*/ +static void R_Bloom_WarsowEffect( void ) +{ + int i, j, k; + float intensity, scale, *diamond; + + + qglColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); + //Take the backup texture and downscale it + GL_Bind( bloom.screen.texture ); + GL_State( GLS_DEPTHTEST_DISABLE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO ); + R_Bloom_Quad( bloom.work.width, bloom.work.height, 0, 0, bloom.screen.readW, bloom.screen.readH ); + //Copy downscaled framebuffer into a texture + GL_Bind( bloom.effect.texture ); + qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, 0, bloom.work.width, bloom.work.height ); + // darkening passes with repeated filter + if( r_bloom_darken->integer ) { + int i; + GL_State( GLS_DEPTHTEST_DISABLE | GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO ); + + for( i = 0; i < r_bloom_darken->integer; i++ ) { + R_Bloom_Quad( bloom.work.width, bloom.work.height, + 0, 0, + bloom.effect.readW, bloom.effect.readH ); + } + qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, 0, bloom.work.width, bloom.work.height ); + } + /* Copy the result to the effect texture */ + GL_Bind( bloom.effect.texture ); + qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, 0, bloom.work.width, bloom.work.height ); + + // bluring passes, warsow uses a repeated semi blend on a selectable diamond grid + qglColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); + GL_State( GLS_DEPTHTEST_DISABLE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE_MINUS_SRC_COLOR ); + if( r_bloom_diamond_size->integer > 7 || r_bloom_diamond_size->integer <= 3 ) { + if( r_bloom_diamond_size->integer != 8 ) + ri.Cvar_Set( "r_bloom_diamond_size", "8" ); + } else if( r_bloom_diamond_size->integer > 5 ) { + if( r_bloom_diamond_size->integer != 6 ) + ri.Cvar_Set( "r_bloom_diamond_size", "6" ); + } else if( r_bloom_diamond_size->integer > 3 ) { + if( r_bloom_diamond_size->integer != 4 ) + ri.Cvar_Set( "r_bloom_diamond_size", "4" ); + } + + switch( r_bloom_diamond_size->integer ) { + case 4: + k = 2; + diamond = &Diamond4x[0][0]; + scale = r_bloom_intensity->value * 0.8f; + break; + case 6: + k = 3; + diamond = &Diamond6x[0][0]; + scale = r_bloom_intensity->value * 0.5f; + break; + default: +// case 8: + k = 4; + diamond = &Diamond8x[0][0]; + scale = r_bloom_intensity->value * 0.3f; + break; + } + + for( i = 0; i < r_bloom_diamond_size->integer; i++ ) { + for( j = 0; j < r_bloom_diamond_size->integer; j++, diamond++ ) { + float x, y; + intensity = *diamond * scale; + if( intensity < 0.01f ) + continue; + qglColor4f( intensity, intensity, intensity, 1.0 ); + x = (i - k) * ( 2 / 640.0f ) * bloom.effect.readW; + y = (j - k) * ( 2 / 480.0f ) * bloom.effect.readH; + + R_Bloom_Quad( bloom.work.width, bloom.work.height, x, y, bloom.effect.readW, bloom.effect.readH ); + } + } + qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, 0, bloom.work.width, bloom.work.height ); +} + +/* +================= +R_Bloom_BackupScreen +Backup the full original screen to a texture for downscaling and later restoration +================= +*/ +static void R_Bloom_BackupScreen( void ) { + GL_Bind( bloom.screen.texture ); + qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, 0, glConfig.vidWidth, glConfig.vidHeight ); +} +/* +================= +R_Bloom_RestoreScreen +Restore the temporary framebuffer section we used with the backup texture +================= +*/ +static void R_Bloom_RestoreScreen( void ) { + GL_State( GLS_DEPTHTEST_DISABLE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO ); + GL_Bind( bloom.screen.texture ); + qglColor4f( 1, 1, 1, 1 ); + R_Bloom_Quad( bloom.work.width, bloom.work.height, 0, 0, + bloom.work.width / (float)bloom.screen.width, + bloom.work.height / (float)bloom.screen.height ); +} + + +/* +================= +R_Bloom_DownsampleView +Scale the copied screen back to the sample size used for subsequent passes +================= +*/ +/*static void R_Bloom_DownsampleView( void ) +{ + // TODO, Provide option to control the color strength here / +// qglColor4f( r_bloom_darken->value, r_bloom_darken->value, r_bloom_darken->value, 1.0f ); + qglColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); + GL_Bind( bloom.screen.texture ); + GL_State( GLS_DEPTHTEST_DISABLE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO ); + //Downscale it + R_Bloom_Quad( bloom.work.width, bloom.work.height, 0, 0, bloom.screen.readW, bloom.screen.readH ); +#if 1 + GL_Bind( bloom.effect.texture ); + qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, 0, bloom.work.width, bloom.work.height ); + // darkening passes + if( r_bloom_darken->integer ) { + int i; + GL_State( GLS_DEPTHTEST_DISABLE | GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO ); + + for( i = 0; i < r_bloom_darken->integer; i++ ) { + R_Bloom_Quad( bloom.work.width, bloom.work.height, + 0, 0, + bloom.effect.readW, bloom.effect.readH ); + } + qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, 0, bloom.work.width, bloom.work.height ); + } +#endif + // Copy the result to the effect texture / + GL_Bind( bloom.effect.texture ); + qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, 0, bloom.work.width, bloom.work.height ); +} + +static void R_Bloom_CreateEffect( void ) { + int dir, x; + int range; + + //First step will zero dst, rest will one add + GL_State( GLS_DEPTHTEST_DISABLE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO ); +// GL_Bind( bloom.screen.texture ); + GL_Bind( bloom.effect.texture ); + range = 4; + for (dir = 0;dir < 2;dir++) + { + // blend on at multiple vertical offsets to achieve a vertical blur + // TODO: do offset blends using GLSL + for (x = -range;x <= range;x++) + { + float xoffset, yoffset, r; + if (!dir){ + xoffset = 0; + yoffset = x*1.5; + } else { + xoffset = x*1.5; + yoffset = 0; + } + xoffset /= bloom.work.width; + yoffset /= bloom.work.height; + // this r value looks like a 'dot' particle, fading sharply to + // black at the edges + // (probably not realistic but looks good enough) + //r = ((range*range+1)/((float)(x*x+1)))/(range*2+1); + //r = (dir ? 1.0f : brighten)/(range*2+1); + r = 2.0f /(range*2+1)*(1 - x*x/(float)(range*range)); +// r *= r_bloom_darken->value; + qglColor4f(r, r, r, 1); + R_Bloom_Quad( bloom.work.width, bloom.work.height, + xoffset, yoffset, + bloom.effect.readW, bloom.effect.readH ); +// bloom.screen.readW, bloom.screen.readH ); + GL_State( GLS_DEPTHTEST_DISABLE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); + } + } + GL_Bind( bloom.effect.texture ); + qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, 0, bloom.work.width, bloom.work.height ); +}*/ + +/* +================= +R_BloomScreen +================= +*/ +void R_BloomScreen( void ) +{ + if( !r_bloom->integer ) + return; + if ( backEnd.doneBloom ) + return; + if ( !backEnd.doneSurfaces ) + return; + backEnd.doneBloom = qtrue; + if( !bloom.started ) { + R_Bloom_InitTextures(); + if( !bloom.started ) + return; + } + + if ( !backEnd.projection2D ) + RB_SetGL2D(); +#if 0 + // set up full screen workspace + GL_TexEnv( GL_MODULATE ); + qglScissor( 0, 0, glConfig.vidWidth, glConfig.vidHeight ); + qglViewport( 0, 0, glConfig.vidWidth, glConfig.vidHeight ); + qglMatrixMode( GL_PROJECTION ); + qglLoadIdentity (); + qglOrtho( 0, glConfig.vidWidth, glConfig.vidHeight, 0, 0, 1 ); + qglMatrixMode( GL_MODELVIEW ); + qglLoadIdentity (); + + GL_Cull( CT_TWO_SIDED ); +#endif + + qglColor4f( 1, 1, 1, 1 ); + + //Backup the old screen in a texture + R_Bloom_BackupScreen(); + // create the bloom texture using one of a few methods + R_Bloom_WarsowEffect (); +// R_Bloom_CreateEffect(); + // restore the screen-backup to the screen + R_Bloom_RestoreScreen(); + // Do the final pass using the bloom texture for the final effect + R_Bloom_DrawEffect (); +} + + +void R_BloomInit( void ) { + memset( &bloom, 0, sizeof( bloom )); + + r_bloom = ri.Cvar_Get( "r_bloom", "0", CVAR_ARCHIVE ); + r_bloom_alpha = ri.Cvar_Get( "r_bloom_alpha", "0.3", CVAR_ARCHIVE ); + r_bloom_diamond_size = ri.Cvar_Get( "r_bloom_diamond_size", "8", CVAR_ARCHIVE ); + r_bloom_intensity = ri.Cvar_Get( "r_bloom_intensity", "1.3", CVAR_ARCHIVE ); + r_bloom_darken = ri.Cvar_Get( "r_bloom_darken", "4", CVAR_ARCHIVE ); + r_bloom_sample_size = ri.Cvar_Get( "r_bloom_sample_size", "256", CVAR_ARCHIVE|CVAR_LATCH ); + r_bloom_fast_sample = ri.Cvar_Get( "r_bloom_fast_sample", "0", CVAR_ARCHIVE|CVAR_LATCH ); +} + Index: code/renderer/tr_backend.c =================================================================== --- code/renderer/tr_backend.c (Revision 2216) +++ code/renderer/tr_backend.c (Arbeitskopie) @@ -944,6 +944,8 @@ backEnd.refdef = cmd->refdef; backEnd.viewParms = cmd->viewParms; + //TODO Maybe check for rdf_noworld stuff but q3mme has full 3d ui + backEnd.doneSurfaces = qtrue; RB_RenderDrawSurfList( cmd->drawSurfs, cmd->numDrawSurfs ); return (const void *)(cmd + 1); @@ -1119,6 +1121,9 @@ backEnd.projection2D = qfalse; + backEnd.doneBloom = qfalse; + backEnd.doneSurfaces = qfalse; + return (const void *)(cmd + 1); } @@ -1149,6 +1154,8 @@ data = RB_SetColor( data ); break; case RC_STRETCH_PIC: + //Check if it's time for BLOOM! + R_BloomScreen(); data = RB_StretchPic( data ); break; case RC_DRAW_SURFS: @@ -1158,6 +1165,8 @@ data = RB_DrawBuffer( data ); break; case RC_SWAP_BUFFERS: + //Check if it's time for BLOOM! + R_BloomScreen(); data = RB_SwapBuffers( data ); break; case RC_SCREENSHOT: Index: Makefile =================================================================== --- Makefile (Revision 2216) +++ Makefile (Arbeitskopie) @@ -1450,6 +1450,7 @@ Q3ROBJ = \ $(B)/renderer/tr_animation.o \ $(B)/renderer/tr_backend.o \ + $(B)/renderer/tr_bloom.o \ $(B)/renderer/tr_bsp.o \ $(B)/renderer/tr_cmds.o \ $(B)/renderer/tr_curve.o \