dhewm3/neo/renderer/draw_exp.cpp
dhewg 736ec20d4d Untangle the epic precompiled.h mess
Don't include the lazy precompiled.h everywhere, only what's
required for the compilation unit.
platform.h needs to be included instead to provide all essential
defines and types.
All includes use the relative path to the neo or the game
specific root.
Move all idlib related includes from idlib/Lib.h to precompiled.h.
precompiled.h still exists for the MFC stuff in tools/.
Add some missing header guards.
2011-12-19 23:21:47 +01:00

2617 lines
80 KiB
C++

/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 Source Code 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.
Doom 3 Source Code 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "sys/platform.h"
#include "sys/win32/win_local.h"
#include "renderer/tr_local.h"
/*
strictly experimental / research codepaths
!!!if we use front facing occluders, we can portal flow from light centers
try depth_component_16 rendering
do we care about portals from light perspective? back / front face issues.
how do we do weapon depth hacks with shadow buffers?
distort their world space vertexes instead of offsetting their depth?
jittering off the side of a projection will give wrong shadows
really huge lights, like sunlight, are going to be problematic with fixed projections
we could tile the projections and let the auto-resize cut them down as necessary
It sucks that depth buffers are non-linear, because the bias and compares change with distance
polygon offset factor causes occasional texture holes from highly angled textures
*/
static bool initialized;
static int lightBufferSize = 1024;
static int maxLightBufferSize = 1024;
static float lightBufferSizeFraction = 0.5;
static int viewBufferSize = 1024;
static int viewBufferHeight = 768;
static int maxViewBufferSize = 1024;
static float viewBufferSizeFraction = 0.5;
static float viewBufferHeightFraction = 0.5;
static bool nativeViewBuffer = false; // true if viewBufferSize is the viewport width
static HPBUFFERARB floatPbuffer;
static HDC floatPbufferDC;
static idImage *floatPbufferImage;
static HPBUFFERARB floatPbuffer2;
static HDC floatPbuffer2DC;
static idImage *floatPbuffer2Image;
static HPBUFFERARB floatPbufferQuarter;
static HDC floatPbufferQuarterDC;
static idImage *floatPbufferQuarterImage;
static HGLRC floatContext;
static HPBUFFERARB shadowPbuffer;
static HDC shadowPbufferDC;
static HPBUFFERARB viewPbuffer;
static HDC viewPbufferDC;
static idImage *shadowImage[3];
static idImage *viewDepthImage;
static idImage *viewAlphaImage;
static idImage *viewShadowImage;
static idImage *jitterImage16;
static idImage *jitterImage4;
static idImage *jitterImage1;
static idImage *random256Image;
static int shadowVertexProgram;
static int shadowFragmentProgram16;
static int shadowFragmentProgram4;
static int shadowFragmentProgram1;
static int shadowFragmentProgram0;
static int screenSpaceShadowVertexProgram;
static int screenSpaceShadowFragmentProgram16;
static int screenSpaceShadowFragmentProgram4;
static int screenSpaceShadowFragmentProgram1;
static int screenSpaceShadowFragmentProgram0;
static int depthMidpointVertexProgram;
static int depthMidpointFragmentProgram;
static int shadowResampleVertexProgram;
static int shadowResampleFragmentProgram;
static int gammaDitherVertexProgram;
static int gammaDitherFragmentProgram;
static int downSampleVertexProgram;
static int downSampleFragmentProgram;
static int bloomVertexProgram;
static int bloomFragmentProgram;
static float viewLightAxialSize;
idCVar r_sb_lightResolution( "r_sb_lightResolution", "1024", CVAR_RENDERER | CVAR_INTEGER, "Pixel dimensions for each shadow buffer, 64 - 2048" );
idCVar r_sb_viewResolution( "r_sb_viewResolution", "1024", CVAR_RENDERER | CVAR_INTEGER, "Width of screen space shadow sampling" );
idCVar r_sb_noShadows( "r_sb_noShadows", "0", CVAR_RENDERER | CVAR_BOOL, "don't draw any occluders" );
idCVar r_sb_usePbuffer( "r_sb_usePbuffer", "1", CVAR_RENDERER | CVAR_BOOL, "draw offscreen" );
idCVar r_sb_jitterScale( "r_sb_jitterScale", "0.006", CVAR_RENDERER | CVAR_FLOAT, "scale factor for jitter offset" );
idCVar r_sb_biasScale( "r_sb_biasScale", "0.0001", CVAR_RENDERER | CVAR_FLOAT, "scale factor for jitter bias" );
idCVar r_sb_samples( "r_sb_samples", "4", CVAR_RENDERER | CVAR_INTEGER, "0, 1, 4, or 16" );
idCVar r_sb_randomize( "r_sb_randomize", "1", CVAR_RENDERER | CVAR_BOOL, "randomly offset jitter texture each draw" );
// polyOfsFactor causes holes in low res images
idCVar r_sb_polyOfsFactor( "r_sb_polyOfsFactor", "2", CVAR_RENDERER | CVAR_FLOAT, "polygonOffset factor for drawing shadow buffer" );
idCVar r_sb_polyOfsUnits( "r_sb_polyOfsUnits", "3000", CVAR_RENDERER | CVAR_FLOAT, "polygonOffset units for drawing shadow buffer" );
idCVar r_sb_occluderFacing( "r_sb_occluderFacing", "0", CVAR_RENDERER | CVAR_INTEGER, "0 = front faces, 1 = back faces, 2 = midway between" );
// r_sb_randomizeBufferOrientation?
idCVar r_sb_frustomFOV( "r_sb_frustomFOV", "92", CVAR_RENDERER | CVAR_FLOAT, "oversize FOV for point light side matching" );
idCVar r_sb_showFrustumPixels( "r_sb_showFrustumPixels", "0", CVAR_RENDERER | CVAR_BOOL, "color the pixels contained in the frustum" );
idCVar r_sb_singleSide( "r_sb_singleSide", "-1", CVAR_RENDERER | CVAR_INTEGER, "only draw a single side (0-5) of point lights" );
idCVar r_sb_useCulling( "r_sb_useCulling", "1", CVAR_RENDERER | CVAR_BOOL, "cull geometry to individual side frustums" );
idCVar r_sb_linearFilter( "r_sb_linearFilter", "1", CVAR_RENDERER | CVAR_BOOL, "use GL_LINEAR instead of GL_NEAREST on shadow maps" );
idCVar r_sb_screenSpaceShadow( "r_sb_screenSpaceShadow", "1", CVAR_RENDERER | CVAR_BOOL, "build shadows in screen space instead of on surfaces" );
idCVar r_hdr_useFloats( "r_hdr_useFloats", "0", CVAR_RENDERER | CVAR_BOOL, "use a floating point rendering buffer" );
idCVar r_hdr_exposure( "r_hdr_exposure", "1.0", CVAR_RENDERER | CVAR_FLOAT, "maximum light scale" );
idCVar r_hdr_bloomFraction( "r_hdr_bloomFraction", "0.1", CVAR_RENDERER | CVAR_FLOAT, "fraction to smear across neighbors" );
idCVar r_hdr_gamma( "r_hdr_gamma", "1", CVAR_RENDERER | CVAR_FLOAT, "monitor gamma power" );
idCVar r_hdr_monitorDither( "r_hdr_monitorDither", "0.01", CVAR_RENDERER | CVAR_FLOAT, "random dither in monitor space" );
// from world space to light origin, looking down the X axis
static float unflippedLightMatrix[16];
// from world space to OpenGL view space, looking down the negative Z axis
static float lightMatrix[16];
// from OpenGL view space to OpenGL NDC ( -1 : 1 in XYZ )
static float lightProjectionMatrix[16];
void RB_ARB2_DrawInteraction( const drawInteraction_t *din );
typedef struct {
const char *name;
int num;
} wglString_t;
wglString_t wglString[] = {
{ "WGL_NUMBER_PIXEL_FORMATS_ARB", 0x2000 },
{ "WGL_DRAW_TO_WINDOW_ARB", 0x2001 },
{ "WGL_DRAW_TO_BITMAP_ARB", 0x2002 },
{ "WGL_ACCELERATION_ARB", 0x2003 },
{ "WGL_NEED_PALETTE_ARB", 0x2004 },
{ "WGL_NEED_SYSTEM_PALETTE_ARB", 0x2005 },
{ "WGL_SWAP_LAYER_BUFFERS_ARB", 0x2006 },
{ "WGL_SWAP_METHOD_ARB", 0x2007 },
{ "WGL_NUMBER_OVERLAYS_ARB", 0x2008 },
{ "WGL_NUMBER_UNDERLAYS_ARB", 0x2009 },
{ "WGL_TRANSPARENT_ARB", 0x200A },
{ "WGL_TRANSPARENT_RED_VALUE_ARB", 0x2037 },
{ "WGL_TRANSPARENT_GREEN_VALUE_ARB", 0x2038 },
{ "WGL_TRANSPARENT_BLUE_VALUE_ARB", 0x2039 },
{ "WGL_TRANSPARENT_ALPHA_VALUE_ARB", 0x203A },
{ "WGL_TRANSPARENT_INDEX_VALUE_ARB", 0x203B },
{ "WGL_SHARE_DEPTH_ARB", 0x200C },
{ "WGL_SHARE_STENCIL_ARB", 0x200D },
{ "WGL_SHARE_ACCUM_ARB", 0x200E },
{ "WGL_SUPPORT_GDI_ARB", 0x200F },
{ "WGL_SUPPORT_OPENGL_ARB", 0x2010 },
{ "WGL_DOUBLE_BUFFER_ARB", 0x2011 },
{ "WGL_STEREO_ARB", 0x2012 },
{ "WGL_PIXEL_TYPE_ARB", 0x2013 },
{ "WGL_COLOR_BITS_ARB", 0x2014 },
{ "WGL_RED_BITS_ARB", 0x2015 },
{ "WGL_RED_SHIFT_ARB", 0x2016 },
{ "WGL_GREEN_BITS_ARB", 0x2017 },
{ "WGL_GREEN_SHIFT_ARB", 0x2018 },
{ "WGL_BLUE_BITS_ARB", 0x2019 },
{ "WGL_BLUE_SHIFT_ARB", 0x201A },
{ "WGL_ALPHA_BITS_ARB", 0x201B },
{ "WGL_ALPHA_SHIFT_ARB", 0x201C },
{ "WGL_ACCUM_BITS_ARB", 0x201D },
{ "WGL_ACCUM_RED_BITS_ARB", 0x201E },
{ "WGL_ACCUM_GREEN_BITS_ARB", 0x201F },
{ "WGL_ACCUM_BLUE_BITS_ARB", 0x2020 },
{ "WGL_ACCUM_ALPHA_BITS_ARB", 0x2021 },
{ "WGL_DEPTH_BITS_ARB", 0x2022 },
{ "WGL_STENCIL_BITS_ARB", 0x2023 },
{ "WGL_AUX_BUFFERS_ARB", 0x2024 },
{ "WGL_NO_ACCELERATION_ARB", 0x2025 },
{ "WGL_GENERIC_ACCELERATION_ARB", 0x2026 },
{ "WGL_FULL_ACCELERATION_ARB", 0x2027 },
{ "WGL_SWAP_EXCHANGE_ARB", 0x2028 },
{ "WGL_SWAP_COPY_ARB", 0x2029 },
{ "WGL_SWAP_UNDEFINED_ARB", 0x202A },
{ "WGL_TYPE_RGBA_ARB", 0x202B },
{ "WGL_TYPE_COLORINDEX_ARB", 0x202C },
};
static const int NUM_WGL_STRINGS = sizeof( wglString ) / sizeof( wglString[0] );
static void R_CheckWglErrors( void ) {
int err = GetLastError();
char *name;
#if 0
LPVOID lpMsgBuf;
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL
);
#endif
err &= 0xffff;
switch ( err ) {
case 13: name = "ERROR_INVALID_DATA"; break;
case 6: name = "ERROR_INVALID_HANDLE"; break;
case 4317: name = "ERROR_INVALID_OPERATION"; break;
default: name = va( "code %i", err ); break;
}
common->Printf( "GetLastError: %s\n", name );
}
static void R_MakeCurrent( HDC dc, HGLRC context, HPBUFFERARB pbuffer ) {
if ( pbuffer ) {
if ( !wglReleaseTexImageARB( pbuffer, WGL_FRONT_LEFT_ARB ) ) {
R_CheckWglErrors();
common->Error( "wglReleaseTexImageARB failed" );
}
}
if ( !qwglMakeCurrent( dc, context ) ) {
R_CheckWglErrors();
common->FatalError( "qwglMakeCurrent failed" );
}
}
static void R_BindTexImage( HPBUFFERARB pbuffer ) {
if ( !wglReleaseTexImageARB( pbuffer, WGL_FRONT_LEFT_ARB ) ) {
R_CheckWglErrors();
common->Error( "wglReleaseTexImageARB failed" );
}
if ( !wglBindTexImageARB( pbuffer, WGL_FRONT_LEFT_ARB ) ) {
R_CheckWglErrors();
common->Error( "failed wglBindTexImageARB" );
}
}
static void R_ReportTextureParms( void ) {
int parms[8];
// q glGetTexParameteriv( GL_TEXTURE_RECTANGLE_NV,
qglGetIntegerv( GL_TEXTURE_BINDING_RECTANGLE_NV, parms );
}
/*
====================
RB_CreateBloomTable
====================
*/
static const int BLOOM_RADIUS = 8;
static void RB_CreateBloomTable( void ) {
float bloom[BLOOM_RADIUS];
float total = 0;
// gaussian
float stdDev = 2.0;
for ( int i = 0 ; i < BLOOM_RADIUS ; i++ ) {
float f = (float)i / stdDev;
bloom[i] = exp( -0.5 * f * f );
total += bloom[i];
}
total = ( total - bloom[0] ) * 2 + bloom[0];
// normalize to 1.0 contribution, so a full row or column will equal 1.0
for ( int i = 0 ; i < BLOOM_RADIUS ; i++ ) {
bloom[i] *= 1.0 / total;
common->Printf( "PARAM bloom%i = { %f };\n", i, bloom[i] );
}
}
/*
====================
GL_SelectTextureNoClient
====================
*/
static void GL_SelectTextureNoClient( int unit ) {
backEnd.glState.currenttmu = unit;
qglActiveTextureARB( GL_TEXTURE0_ARB + unit );
RB_LogComment( "glActiveTextureARB( %i )\n", unit );
}
/*
================
R_CreateShadowBufferImage
================
*/
static void R_CreateShadowBufferImage( idImage *image ) {
byte *data = (byte *)Mem_Alloc( lightBufferSize*lightBufferSize );
memset( data, 0, lightBufferSize*lightBufferSize );
image->GenerateImage( (byte *)data, 4, 4,
TF_LINEAR, false, TR_CLAMP_TO_BORDER, TD_HIGH_QUALITY );
// now reset it to a shadow depth image
GL_CheckErrors();
image->uploadWidth = image->uploadHeight = lightBufferSize;
qglTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24_ARB, lightBufferSize, lightBufferSize,
0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, data );
qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE );
// qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE );
qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL );
// explicit zero depth border
float color[4];
color[0] = color[1] = color[2] = color[3] = 0;
qglTexParameterfv( GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color );
GL_CheckErrors();
Mem_Free( data );
}
static void R_CreateViewAlphaImage( idImage *image ) {
int c = viewBufferSize*viewBufferSize*4;
byte *data = (byte *)Mem_Alloc( c );
// don't let it pick an intensity format
for ( int i = 0 ; i < c ; i++ ) {
data[i] = i;
}
memset( data, 0, viewBufferSize*viewBufferSize );
image->GenerateImage( (byte *)data, viewBufferSize, viewBufferSize,
TF_LINEAR, false, TR_CLAMP, TD_HIGH_QUALITY );
}
static void R_CreateStubImage( idImage *image ) {
float data[3][4][4];
// generate the texture number
qglGenTextures( 1, &image->texnum );
qglBindTexture( GL_TEXTURE_RECTANGLE_NV, image->texnum );
memset( data, 0, sizeof( data ) );
glTexImage2D( GL_TEXTURE_RECTANGLE_NV, 0, GL_FLOAT_RGBA16_NV, 4, 3, 0, GL_RGBA, GL_FLOAT, &data );
}
/*
================
R_CreateJitterImage
================
*/
const static int JITTER_SIZE = 128;
static void R_CreateJitterImage16( idImage *image ) {
byte data[JITTER_SIZE][JITTER_SIZE*16][4];
for ( int i = 0 ; i < JITTER_SIZE ; i++ ) {
for ( int s = 0 ; s < 16 ; s++ ) {
int sOfs = 64 * ( s & 3 );
int tOfs = 64 * ( ( s >> 2 ) & 3 );
for ( int j = 0 ; j < JITTER_SIZE ; j++ ) {
data[i][s*JITTER_SIZE+j][0] = (rand() & 63 ) | sOfs;
data[i][s*JITTER_SIZE+j][1] = (rand() & 63 ) | tOfs;
data[i][s*JITTER_SIZE+j][2] = rand();
data[i][s*JITTER_SIZE+j][3] = 0;
}
}
}
image->GenerateImage( (byte *)data, JITTER_SIZE*16, JITTER_SIZE,
TF_NEAREST, false, TR_REPEAT, TD_HIGH_QUALITY );
}
static void R_CreateJitterImage4( idImage *image ) {
byte data[JITTER_SIZE][JITTER_SIZE*4][4];
for ( int i = 0 ; i < JITTER_SIZE ; i++ ) {
for ( int s = 0 ; s < 4 ; s++ ) {
int sOfs = 128 * ( s & 1 );
int tOfs = 128 * ( ( s >> 1 ) & 1 );
for ( int j = 0 ; j < JITTER_SIZE ; j++ ) {
data[i][s*JITTER_SIZE+j][0] = (rand() & 127 ) | sOfs;
data[i][s*JITTER_SIZE+j][1] = (rand() & 127 ) | tOfs;
data[i][s*JITTER_SIZE+j][2] = rand();
data[i][s*JITTER_SIZE+j][3] = 0;
}
}
}
image->GenerateImage( (byte *)data, JITTER_SIZE*4, JITTER_SIZE,
TF_NEAREST, false, TR_REPEAT, TD_HIGH_QUALITY );
}
static void R_CreateJitterImage1( idImage *image ) {
byte data[JITTER_SIZE][JITTER_SIZE][4];
for ( int i = 0 ; i < JITTER_SIZE ; i++ ) {
for ( int j = 0 ; j < JITTER_SIZE ; j++ ) {
data[i][j][0] = rand();
data[i][j][1] = rand();
data[i][j][2] = rand();
data[i][j][3] = 0;
}
}
image->GenerateImage( (byte *)data, JITTER_SIZE, JITTER_SIZE,
TF_NEAREST, false, TR_REPEAT, TD_HIGH_QUALITY );
}
static void R_CreateRandom256Image( idImage *image ) {
byte data[256][256][4];
for ( int i = 0 ; i < 256 ; i++ ) {
for ( int j = 0 ; j < 256 ; j++ ) {
data[i][j][0] = rand();
data[i][j][1] = rand();
data[i][j][2] = rand();
data[i][j][3] = rand();
}
}
image->GenerateImage( (byte *)data, 256, 256,
TF_NEAREST, false, TR_REPEAT, TD_HIGH_QUALITY );
}
/*
==================
R_PrintPixelFormat
==================
*/
void R_PrintPixelFormat( int pixelFormat ) {
int res;
int iAttribute;
int iValue;
common->Printf( "----- pixelFormat %i -----\n", pixelFormat );
for ( int i = 1 ; i < NUM_WGL_STRINGS ; i++ ) {
iAttribute = wglString[i].num;
res = wglGetPixelFormatAttribivARB( win32.hDC, pixelFormat, 0, 1, &iAttribute, &iValue );
if ( res && iValue ) {
common->Printf( "%s : %i\n", wglString[i].name, iValue );
}
}
}
/*
==================
R_Exp_Allocate
==================
*/
void R_Exp_Allocate( void ) {
// find a pixel format for our floating point pbuffer
int iAttributes[NUM_WGL_STRINGS*2], *atr_p;
FLOAT fAttributes[] = {0, 0};
UINT numFormats;
int pixelformats[1024];
int ret;
int pbiAttributes[] = {0, 0};
initialized = true;
#if 1
//
// allocate the floating point rendering buffer
//
atr_p = iAttributes;
*atr_p++ = WGL_DRAW_TO_PBUFFER_ARB;
*atr_p++ = TRUE;
*atr_p++ = WGL_FLOAT_COMPONENTS_NV;
*atr_p++ = TRUE;
*atr_p++ = WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_RGBA_NV;
*atr_p++ = TRUE;
// *atr_p++ = WGL_BIND_TO_TEXTURE_RGBA_ARB;
// *atr_p++ = TRUE;
*atr_p++ = WGL_DEPTH_BITS_ARB;
*atr_p++ = 24;
*atr_p++ = WGL_STENCIL_BITS_ARB;
*atr_p++ = 8;
*atr_p++ = 0;
*atr_p++ = 0;
ret = wglChoosePixelFormatARB( win32.hDC, iAttributes, fAttributes,
sizeof( pixelformats ) / sizeof( pixelformats[0] ), pixelformats, &numFormats );
#if 0
for ( int i = 0 ; i < (int)numFormats ; i++ ) {
R_PrintPixelFormat( pixelformats[i] );
}
#endif
common->Printf( "\nfloatPbuffer:\n" );
R_PrintPixelFormat( pixelformats[0] );
// allocate a pbuffer with this pixel format
int pbiAttributesTexture[] = {
WGL_TEXTURE_FORMAT_ARB, WGL_TEXTURE_FLOAT_RGBA_NV,
WGL_TEXTURE_TARGET_ARB, WGL_TEXTURE_RECTANGLE_NV, // WGL_TEXTURE_2D_ARB,
0, 0};
floatPbuffer = wglCreatePbufferARB( win32.hDC, pixelformats[0], glConfig.vidWidth,
glConfig.vidHeight, pbiAttributesTexture );
if ( !floatPbuffer ) {
common->Printf( "failed to create floatPbuffer.\n" );
GL_CheckErrors();
}
floatPbufferDC = wglGetPbufferDCARB( floatPbuffer );
floatPbufferImage = globalImages->ImageFromFunction( "_floatPbuffer", R_CreateStubImage );
// create a second buffer for ping-pong operations
floatPbuffer2 = wglCreatePbufferARB( win32.hDC, pixelformats[0], glConfig.vidWidth,
glConfig.vidHeight, pbiAttributesTexture );
if ( !floatPbuffer2 ) {
common->Printf( "failed to create floatPbuffer.\n" );
GL_CheckErrors();
}
floatPbuffer2DC = wglGetPbufferDCARB( floatPbuffer2 );
floatPbuffer2Image = globalImages->ImageFromFunction( "_floatPbuffer2", R_CreateStubImage );
// create a third buffer for down sampling operations
floatPbufferQuarter = wglCreatePbufferARB( win32.hDC, pixelformats[0], glConfig.vidWidth / 4,
glConfig.vidHeight / 4, pbiAttributesTexture );
if ( !floatPbufferQuarter ) {
common->Printf( "failed to create floatPbuffer.\n" );
GL_CheckErrors();
}
floatPbufferQuarterDC = wglGetPbufferDCARB( floatPbufferQuarter );
floatPbufferQuarterImage = globalImages->ImageFromFunction( "floatPbufferQuarter", R_CreateStubImage );
// create a new GL context for this pixel format and share textures
floatContext = wglCreateContext( floatPbufferDC );
if ( !floatContext ) {
common->Printf( "failed to create context for floatPbufferDC.\n" );
GL_CheckErrors();
}
if ( !wglShareLists( floatContext, win32.hGLRC ) ) {
common->Printf( "failed to share lists.\n" );
}
// create a rendering context for this pixel format and share textures
// allocate a texture for the rendering
#endif
//=================================================================================
//
// allocate the shadow pbuffer
//
atr_p = iAttributes;
*atr_p++ = WGL_DRAW_TO_PBUFFER_ARB;
*atr_p++ = TRUE;
*atr_p++ = WGL_RED_BITS_ARB;
*atr_p++ = 8;
*atr_p++ = WGL_GREEN_BITS_ARB;
*atr_p++ = 8;
*atr_p++ = WGL_BLUE_BITS_ARB;
*atr_p++ = 8;
*atr_p++ = WGL_ALPHA_BITS_ARB;
*atr_p++ = 8;
*atr_p++ = WGL_DEPTH_BITS_ARB;
*atr_p++ = 24;
*atr_p++ = WGL_STENCIL_BITS_ARB;
*atr_p++ = 8;
*atr_p++ = 0;
*atr_p++ = 0;
ret = wglChoosePixelFormatARB( win32.hDC, iAttributes, fAttributes,
sizeof( pixelformats ) / sizeof( pixelformats[0] ), pixelformats, &numFormats );
#if 0
for ( int i = 0 ; i < (int)numFormats ; i++ ) {
R_PrintPixelFormat( pixelformats[i] );
}
#endif
common->Printf( "\nshadowPbuffer:\n" );
R_PrintPixelFormat( pixelformats[0] );
pixelformats[0] = win32.pixelformat; // forced to do this by wgl...
//-----------------------------------
lightBufferSize = maxLightBufferSize;
// allocate a pbuffer with this pixel format
shadowPbuffer = wglCreatePbufferARB( win32.hDC, pixelformats[0], lightBufferSize,
lightBufferSize, pbiAttributes );
// allocate a rendering context for the pbuffer
shadowPbufferDC = wglGetPbufferDCARB( shadowPbuffer );
// generate the texture number
shadowImage[0] = globalImages->ImageFromFunction( va("_shadowBuffer%i_0",lightBufferSize), R_CreateShadowBufferImage );
shadowImage[1] = globalImages->ImageFromFunction( va("_shadowBuffer%i_1",lightBufferSize), R_CreateShadowBufferImage );
shadowImage[2] = globalImages->ImageFromFunction( va("_shadowBuffer%i_2",lightBufferSize), R_CreateShadowBufferImage );
//-----------------------------------
lightBufferSize = maxViewBufferSize;
// allocate a pbuffer with this pixel format
viewPbuffer = wglCreatePbufferARB( win32.hDC, pixelformats[0], maxViewBufferSize,
maxViewBufferSize, pbiAttributes );
// allocate a rendering context for the pbuffer
viewPbufferDC = wglGetPbufferDCARB( viewPbuffer );
// create the image space depth buffer for image-space shadow trnasforms
viewDepthImage = globalImages->ImageFromFunction("_viewDepth", R_CreateShadowBufferImage );
// create the image space shadow alpha buffer for subsampling the shadow calculation
viewAlphaImage = globalImages->ImageFromFunction("_viewAlpha", R_CreateViewAlphaImage );
//-----------------------------------
// generate the jitter image
jitterImage16 = globalImages->ImageFromFunction( "_jitter16", R_CreateJitterImage16 );
jitterImage4 = globalImages->ImageFromFunction( "_jitter4", R_CreateJitterImage4 );
jitterImage1 = globalImages->ImageFromFunction( "_jitter1", R_CreateJitterImage1 );
depthMidpointVertexProgram = R_FindARBProgram( GL_VERTEX_PROGRAM_ARB, "depthMidpoint.vfp" );
depthMidpointFragmentProgram = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, "depthMidpoint.vfp" );
shadowResampleVertexProgram = R_FindARBProgram( GL_VERTEX_PROGRAM_ARB, "shadowResample.vfp" );
shadowResampleFragmentProgram = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, "shadowResample.vfp" );
screenSpaceShadowVertexProgram = R_FindARBProgram( GL_VERTEX_PROGRAM_ARB, "screenSpaceShadow1.vfp" );
screenSpaceShadowFragmentProgram0 = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, "screenSpaceShadow0.vfp" );
screenSpaceShadowFragmentProgram1 = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, "screenSpaceShadow1.vfp" );
screenSpaceShadowFragmentProgram4 = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, "screenSpaceShadow4.vfp" );
screenSpaceShadowFragmentProgram16 = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, "screenSpaceShadow16.vfp" );
shadowVertexProgram = R_FindARBProgram( GL_VERTEX_PROGRAM_ARB, "shadowBufferInteraction1.vfp" );
shadowFragmentProgram0 = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, "shadowBufferInteraction0.vfp" );
shadowFragmentProgram1 = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, "shadowBufferInteraction1.vfp" );
shadowFragmentProgram4 = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, "shadowBufferInteraction4.vfp" );
shadowFragmentProgram16 = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, "shadowBufferInteraction16.vfp" );
gammaDitherVertexProgram = R_FindARBProgram( GL_VERTEX_PROGRAM_ARB, "gammaDither.vfp" );
gammaDitherFragmentProgram = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, "gammaDither.vfp" );
downSampleVertexProgram = R_FindARBProgram( GL_VERTEX_PROGRAM_ARB, "downSample.vfp" );
downSampleFragmentProgram = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, "downSample.vfp" );
bloomVertexProgram = R_FindARBProgram( GL_VERTEX_PROGRAM_ARB, "bloom.vfp" );
bloomFragmentProgram = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, "bloom.vfp" );
random256Image = globalImages->ImageFromFunction( "_random256", R_CreateRandom256Image );
}
//===========================================================================================
static const int CULL_RECEIVER = 1; // still draw occluder, but it is out of the view
static const int CULL_OCCLUDER_AND_RECEIVER = 2; // the surface doesn't effect the view at all
/*
==================
RB_EXP_CullInteractions
Sets surfaceInteraction_t->cullBits
==================
*/
void RB_EXP_CullInteractions( viewLight_t *vLight, idPlane frustumPlanes[6] ) {
for ( idInteraction *inter = vLight->lightDef->firstInteraction ; inter ; inter = inter->lightNext ) {
const idRenderEntityLocal *entityDef = inter->entityDef;
if ( !entityDef ) {
continue;
}
if ( inter->numSurfaces < 1 ) {
continue;
}
int culled = 0;
if ( r_sb_useCulling.GetBool() ) {
// transform light frustum into object space, positive side points outside the light
idPlane localPlanes[6];
int plane;
for ( plane = 0 ; plane < 6 ; plane++ ) {
R_GlobalPlaneToLocal( entityDef->modelMatrix, frustumPlanes[plane], localPlanes[plane] );
}
// cull the entire entity bounding box
// has referenceBounds been tightened to the actual model bounds?
idVec3 corners[8];
for ( int i = 0 ; i < 8 ; i++ ) {
corners[i][0] = entityDef->referenceBounds[i&1][0];
corners[i][1] = entityDef->referenceBounds[(i>>1)&1][1];
corners[i][2] = entityDef->referenceBounds[(i>>2)&1][2];
}
for ( plane = 0 ; plane < 6 ; plane++ ) {
int j;
for ( j = 0 ; j < 8 ; j++ ) {
// if a corner is on the negative side (inside) of the frustum, the surface is not culled
// by this plane
if ( corners[j] * localPlanes[plane].ToVec4().ToVec3() + localPlanes[plane][3] < 0 ) {
break;
}
}
if ( j == 8 ) {
break; // all points outside the light
}
}
if ( plane < 6 ) {
culled = CULL_OCCLUDER_AND_RECEIVER;
}
}
for ( int i = 0 ; i < inter->numSurfaces ; i++ ) {
surfaceInteraction_t *surfInt = &inter->surfaces[i];
if ( !surfInt->ambientTris ) {
continue;
}
surfInt->expCulled = culled;
}
}
}
/*
==================
RB_EXP_RenderOccluders
==================
*/
void RB_EXP_RenderOccluders( viewLight_t *vLight ) {
for ( idInteraction *inter = vLight->lightDef->firstInteraction ; inter ; inter = inter->lightNext ) {
const idRenderEntityLocal *entityDef = inter->entityDef;
if ( !entityDef ) {
continue;
}
if ( inter->numSurfaces < 1 ) {
continue;
}
// no need to check for current on this, because each interaction is always
// a different space
float matrix[16];
myGlMultMatrix( inter->entityDef->modelMatrix, lightMatrix, matrix );
qglLoadMatrixf( matrix );
// draw each surface
for ( int i = 0 ; i < inter->numSurfaces ; i++ ) {
surfaceInteraction_t *surfInt = &inter->surfaces[i];
if ( !surfInt->ambientTris ) {
continue;
}
if ( surfInt->shader && !surfInt->shader->SurfaceCastsShadow() ) {
continue;
}
// cull it
if ( surfInt->expCulled == CULL_OCCLUDER_AND_RECEIVER ) {
continue;
}
// render it
const srfTriangles_t *tri = surfInt->ambientTris;
if ( !tri->ambientCache ) {
R_CreateAmbientCache( const_cast<srfTriangles_t *>(tri), false );
}
idDrawVert *ac = (idDrawVert *)vertexCache.Position( tri->ambientCache );
qglVertexPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->xyz.ToFloatPtr() );
qglTexCoordPointer( 2, GL_FLOAT, sizeof( idDrawVert ), ac->st.ToFloatPtr() );
if ( surfInt->shader ) {
surfInt->shader->GetEditorImage()->Bind();
}
RB_DrawElementsWithCounters( tri );
}
}
}
/*
==================
RB_RenderShadowBuffer
==================
*/
void RB_RenderShadowBuffer( viewLight_t *vLight, int side ) {
float xmin, xmax, ymin, ymax;
float width, height;
float zNear;
float fov = r_sb_frustomFOV.GetFloat();
//
// set up 90 degree projection matrix
//
zNear = 4;
ymax = zNear * tan( fov * idMath::PI / 360.0f );
ymin = -ymax;
xmax = zNear * tan( fov * idMath::PI / 360.0f );
xmin = -xmax;
width = xmax - xmin;
height = ymax - ymin;
lightProjectionMatrix[0] = 2 * zNear / width;
lightProjectionMatrix[4] = 0;
lightProjectionMatrix[8] = 0;
lightProjectionMatrix[12] = 0;
lightProjectionMatrix[1] = 0;
lightProjectionMatrix[5] = 2 * zNear / height;
lightProjectionMatrix[9] = 0;
lightProjectionMatrix[13] = 0;
// this is the far-plane-at-infinity formulation, and
// crunches the Z range slightly so w=0 vertexes do not
// rasterize right at the wraparound point
lightProjectionMatrix[2] = 0;
lightProjectionMatrix[6] = 0;
lightProjectionMatrix[10] = -0.999f;
lightProjectionMatrix[14] = -2.0f * zNear;
lightProjectionMatrix[3] = 0;
lightProjectionMatrix[7] = 0;
lightProjectionMatrix[11] = -1;
lightProjectionMatrix[15] = 0;
if ( r_sb_usePbuffer.GetBool() ) {
// set the current openGL drawable to the shadow buffer
R_MakeCurrent( shadowPbufferDC, win32.hGLRC, NULL /* !@# shadowPbuffer */ );
}
qglMatrixMode( GL_PROJECTION );
qglLoadMatrixf( lightProjectionMatrix );
qglMatrixMode( GL_MODELVIEW );
qglViewport( 0, 0, lightBufferSize, lightBufferSize );
qglScissor( 0, 0, lightBufferSize, lightBufferSize );
qglStencilFunc( GL_ALWAYS, 0, 255 );
qglClearColor( 0, 1, 0, 0 );
GL_State( GLS_DEPTHFUNC_LESS | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO ); // make sure depth mask is off before clear
qglClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
// draw all the occluders
qglColor3f( 1, 1, 1 );
GL_SelectTexture( 0 );
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
backEnd.currentSpace = NULL;
static float s_flipMatrix[16] = {
// convert from our coordinate system (looking down X)
// to OpenGL's coordinate system (looking down -Z)
0, 0, -1, 0,
-1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 0, 1
};
float viewMatrix[16];
idVec3 vec;
idVec3 origin = vLight->lightDef->globalLightOrigin;
if ( side == -1 ) {
// projected light
vec = vLight->lightDef->parms.target;
vec.Normalize();
viewMatrix[0] = vec[0];
viewMatrix[4] = vec[1];
viewMatrix[8] = vec[2];
vec = vLight->lightDef->parms.right;
vec.Normalize();
viewMatrix[1] = -vec[0];
viewMatrix[5] = -vec[1];
viewMatrix[9] = -vec[2];
vec = vLight->lightDef->parms.up;
vec.Normalize();
viewMatrix[2] = vec[0];
viewMatrix[6] = vec[1];
viewMatrix[10] = vec[2];
} else {
// side of a point light
memset( viewMatrix, 0, sizeof( viewMatrix ) );
switch ( side ) {
case 0:
viewMatrix[0] = 1;
viewMatrix[9] = 1;
viewMatrix[6] = -1;
break;
case 1:
viewMatrix[0] = -1;
viewMatrix[9] = -1;
viewMatrix[6] = -1;
break;
case 2:
viewMatrix[4] = 1;
viewMatrix[1] = -1;
viewMatrix[10] = 1;
break;
case 3:
viewMatrix[4] = -1;
viewMatrix[1] = -1;
viewMatrix[10] = -1;
break;
case 4:
viewMatrix[8] = 1;
viewMatrix[1] = -1;
viewMatrix[6] = -1;
break;
case 5:
viewMatrix[8] = -1;
viewMatrix[1] = 1;
viewMatrix[6] = -1;
break;
}
}
viewMatrix[12] = -origin[0] * viewMatrix[0] + -origin[1] * viewMatrix[4] + -origin[2] * viewMatrix[8];
viewMatrix[13] = -origin[0] * viewMatrix[1] + -origin[1] * viewMatrix[5] + -origin[2] * viewMatrix[9];
viewMatrix[14] = -origin[0] * viewMatrix[2] + -origin[1] * viewMatrix[6] + -origin[2] * viewMatrix[10];
viewMatrix[3] = 0;
viewMatrix[7] = 0;
viewMatrix[11] = 0;
viewMatrix[15] = 1;
memcpy( unflippedLightMatrix, viewMatrix, sizeof( unflippedLightMatrix ) );
myGlMultMatrix( viewMatrix, s_flipMatrix,lightMatrix);
// create frustum planes
idPlane globalFrustum[6];
// near clip
globalFrustum[0][0] = -viewMatrix[0];
globalFrustum[0][1] = -viewMatrix[4];
globalFrustum[0][2] = -viewMatrix[8];
globalFrustum[0][3] = -(origin[0] * globalFrustum[0][0] + origin[1] * globalFrustum[0][1] + origin[2] * globalFrustum[0][2]);
// far clip
globalFrustum[1][0] = viewMatrix[0];
globalFrustum[1][1] = viewMatrix[4];
globalFrustum[1][2] = viewMatrix[8];
globalFrustum[1][3] = -globalFrustum[0][3] - viewLightAxialSize;
// side clips
globalFrustum[2][0] = -viewMatrix[0] + viewMatrix[1];
globalFrustum[2][1] = -viewMatrix[4] + viewMatrix[5];
globalFrustum[2][2] = -viewMatrix[8] + viewMatrix[9];
globalFrustum[3][0] = -viewMatrix[0] - viewMatrix[1];
globalFrustum[3][1] = -viewMatrix[4] - viewMatrix[5];
globalFrustum[3][2] = -viewMatrix[8] - viewMatrix[9];
globalFrustum[4][0] = -viewMatrix[0] + viewMatrix[2];
globalFrustum[4][1] = -viewMatrix[4] + viewMatrix[6];
globalFrustum[4][2] = -viewMatrix[8] + viewMatrix[10];
globalFrustum[5][0] = -viewMatrix[0] - viewMatrix[2];
globalFrustum[5][1] = -viewMatrix[4] - viewMatrix[6];
globalFrustum[5][2] = -viewMatrix[8] - viewMatrix[10];
// is this nromalization necessary?
for ( int i = 0 ; i < 6 ; i++ ) {
globalFrustum[i].ToVec4().ToVec3().Normalize();
}
for ( int i = 2 ; i < 6 ; i++ ) {
globalFrustum[i][3] = - (origin * globalFrustum[i].ToVec4().ToVec3() );
}
RB_EXP_CullInteractions( vLight, globalFrustum );
// FIXME: we want to skip the sampling as well as the generation when not casting shadows
if ( !r_sb_noShadows.GetBool() && vLight->lightShader->LightCastsShadows() ) {
//
// set polygon offset for the rendering
//
switch ( r_sb_occluderFacing.GetInteger() ) {
case 0: // front sides
qglPolygonOffset( r_sb_polyOfsFactor.GetFloat(), r_sb_polyOfsUnits.GetFloat() );
qglEnable( GL_POLYGON_OFFSET_FILL );
RB_EXP_RenderOccluders( vLight );
qglDisable( GL_POLYGON_OFFSET_FILL );
break;
case 1: // back sides
qglPolygonOffset( -r_sb_polyOfsFactor.GetFloat(), -r_sb_polyOfsUnits.GetFloat() );
qglEnable( GL_POLYGON_OFFSET_FILL );
GL_Cull( CT_BACK_SIDED );
RB_EXP_RenderOccluders( vLight );
GL_Cull( CT_FRONT_SIDED );
qglDisable( GL_POLYGON_OFFSET_FILL );
break;
case 2: // both sides
GL_Cull( CT_BACK_SIDED );
RB_EXP_RenderOccluders( vLight );
GL_Cull( CT_FRONT_SIDED );
shadowImage[2]->Bind();
qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, 0, lightBufferSize, lightBufferSize );
RB_EXP_RenderOccluders( vLight );
shadowImage[1]->Bind();
qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, 0, lightBufferSize, lightBufferSize );
// fragment program to combine the two depth images
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, depthMidpointVertexProgram );
qglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, depthMidpointFragmentProgram );
qglEnable(GL_VERTEX_PROGRAM_ARB);
qglEnable(GL_FRAGMENT_PROGRAM_ARB);
GL_SelectTextureNoClient( 1 );
shadowImage[1]->Bind();
qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE );
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
GL_SelectTextureNoClient( 0 );
shadowImage[2]->Bind();
qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE );
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
// draw a full screen quad
qglMatrixMode( GL_PROJECTION );
qglLoadIdentity();
qglOrtho( 0, 1, 0, 1, -1, 1 );
qglMatrixMode( GL_MODELVIEW );
qglLoadIdentity();
GL_State( GLS_DEPTHFUNC_ALWAYS );
qglBegin( GL_TRIANGLE_FAN );
qglTexCoord2f( 0, 0 );
qglVertex2f( 0, 0 );
qglTexCoord2f( 0, lightBufferSizeFraction );
qglVertex2f( 0, 1 );
qglTexCoord2f( lightBufferSizeFraction, lightBufferSizeFraction );
qglVertex2f( 1, 1 );
qglTexCoord2f( lightBufferSizeFraction, 0 );
qglVertex2f( 1, 0 );
qglEnd();
qglDisable( GL_VERTEX_PROGRAM_ARB );
qglDisable( GL_FRAGMENT_PROGRAM_ARB );
break;
}
}
// copy to the texture
shadowImage[0]->Bind();
qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, 0, lightBufferSize, lightBufferSize );
qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
// reset the normal view matrix
qglMatrixMode( GL_PROJECTION );
qglLoadMatrixf( backEnd.viewDef->projectionMatrix );
qglMatrixMode( GL_MODELVIEW );
// the current modelView matrix is not valid
backEnd.currentSpace = NULL;
}
/*
==================
RB_EXP_DrawInteraction
==================
*/
void RB_EXP_DrawInteraction( const drawInteraction_t *din ) {
// load all the vertex program parameters
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_ORIGIN, din->localLightOrigin.ToFloatPtr() );
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_VIEW_ORIGIN, din->localViewOrigin.ToFloatPtr() );
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_PROJECT_S, din->lightProjection[0].ToFloatPtr() );
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_PROJECT_T, din->lightProjection[1].ToFloatPtr() );
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_PROJECT_Q, din->lightProjection[2].ToFloatPtr() );
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_FALLOFF_S, din->lightProjection[3].ToFloatPtr() );
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_BUMP_MATRIX_S, din->bumpMatrix[0].ToFloatPtr() );
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_BUMP_MATRIX_T, din->bumpMatrix[1].ToFloatPtr() );
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_DIFFUSE_MATRIX_S, din->diffuseMatrix[0].ToFloatPtr() );
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_DIFFUSE_MATRIX_T, din->diffuseMatrix[1].ToFloatPtr() );
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_SPECULAR_MATRIX_S, din->specularMatrix[0].ToFloatPtr() );
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_SPECULAR_MATRIX_T, din->specularMatrix[1].ToFloatPtr() );
// calculate depth projection for shadow buffer
float sRow[4];
float tRow[4];
float rRow[4];
float qRow[4];
float matrix[16];
float matrix2[16];
myGlMultMatrix( din->surf->space->modelMatrix, lightMatrix, matrix );
myGlMultMatrix( matrix, lightProjectionMatrix, matrix2 );
// the final values need to be in 0.0 : 1.0 range instead of -1 : 1
sRow[0] = 0.5 * lightBufferSizeFraction * ( matrix2[0] + matrix2[3] );
sRow[1] = 0.5 * lightBufferSizeFraction * ( matrix2[4] + matrix2[7] );
sRow[2] = 0.5 * lightBufferSizeFraction * ( matrix2[8] + matrix2[11] );
sRow[3] = 0.5 * lightBufferSizeFraction * ( matrix2[12] + matrix2[15] );
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, 18, sRow );
tRow[0] = 0.5 * lightBufferSizeFraction * ( matrix2[1] + matrix2[3] );
tRow[1] = 0.5 * lightBufferSizeFraction * ( matrix2[5] + matrix2[7] );
tRow[2] = 0.5 * lightBufferSizeFraction * ( matrix2[9] + matrix2[11] );
tRow[3] = 0.5 * lightBufferSizeFraction * ( matrix2[13] + matrix2[15] );
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, 19, tRow );
rRow[0] = 0.5 * ( matrix2[2] + matrix2[3] );
rRow[1] = 0.5 * ( matrix2[6] + matrix2[7] );
rRow[2] = 0.5 * ( matrix2[10] + matrix2[11] );
rRow[3] = 0.5 * ( matrix2[14] + matrix2[15] );
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, 20, rRow );
qRow[0] = matrix2[3];
qRow[1] = matrix2[7];
qRow[2] = matrix2[11];
qRow[3] = matrix2[15];
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, 21, qRow );
// testing fragment based normal mapping
if ( r_testARBProgram.GetBool() ) {
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 2, din->localLightOrigin.ToFloatPtr() );
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 3, din->localViewOrigin.ToFloatPtr() );
}
static const float zero[4] = { 0, 0, 0, 0 };
static const float one[4] = { 1, 1, 1, 1 };
static const float negOne[4] = { -1, -1, -1, -1 };
switch ( din->vertexColor ) {
case SVC_IGNORE:
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_MODULATE, zero );
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_ADD, one );
break;
case SVC_MODULATE:
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_MODULATE, one );
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_ADD, zero );
break;
case SVC_INVERSE_MODULATE:
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_MODULATE, negOne );
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_ADD, one );
break;
}
// set the constant colors
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 0, din->diffuseColor.ToFloatPtr() );
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 1, din->specularColor.ToFloatPtr() );
//-----------------------------------------------------
// screen power of two correction factor
float parm[4];
parm[0] = 1.0 / ( JITTER_SIZE * r_sb_samples.GetInteger() ) ;
parm[1] = 1.0 / JITTER_SIZE;
parm[2] = 0;
parm[3] = 1;
qglProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 2, parm );
// jitter tex scale
parm[0] =
parm[1] = r_sb_jitterScale.GetFloat() * lightBufferSizeFraction;
parm[2] = -r_sb_biasScale.GetFloat();
parm[3] = 0;
qglProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 3, parm );
// jitter tex offset
if ( r_sb_randomize.GetBool() ) {
parm[0] = (rand()&255) / 255.0;
parm[1] = (rand()&255) / 255.0;
} else {
parm[0] = parm[1] = 0;
}
parm[2] = 0;
parm[3] = 0;
qglProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 4, parm );
//-----------------------------------------------------
// set the textures
// texture 1 will be the per-surface bump map
GL_SelectTextureNoClient( 1 );
din->bumpImage->Bind();
// texture 2 will be the light falloff texture
GL_SelectTextureNoClient( 2 );
din->lightFalloffImage->Bind();
// texture 3 will be the light projection texture
GL_SelectTextureNoClient( 3 );
din->lightImage->Bind();
// texture 4 is the per-surface diffuse map
GL_SelectTextureNoClient( 4 );
din->diffuseImage->Bind();
// texture 5 is the per-surface specular map
GL_SelectTextureNoClient( 5 );
din->specularImage->Bind();
// draw it
RB_DrawElementsWithCounters( din->surf->geo );
}
/*
=============
RB_EXP_CreateDrawInteractions
=============
*/
void RB_EXP_CreateDrawInteractions( const drawSurf_t *surf ) {
if ( !surf ) {
return;
}
if ( r_sb_screenSpaceShadow.GetBool() ) {
// perform setup here that will be constant for all interactions
GL_State( GLS_SRCBLEND_DST_ALPHA | GLS_DSTBLEND_ONE | GLS_DEPTHMASK | backEnd.depthFunc );
if ( r_testARBProgram.GetBool() ) {
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_TEST );
qglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, FPROG_TEST );
} else {
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_INTERACTION );
qglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, FPROG_INTERACTION );
}
} else {
// perform setup here that will be constant for all interactions
GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHMASK | backEnd.depthFunc );
GL_State( GLS_DEPTHMASK | GLS_DEPTHFUNC_ALWAYS );//!@#
// bind the vertex program
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, shadowVertexProgram );
if ( r_sb_samples.GetInteger() == 16 ) {
qglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, shadowFragmentProgram16 );
} else if ( r_sb_samples.GetInteger() == 4 ) {
qglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, shadowFragmentProgram4 );
} else if ( r_sb_samples.GetInteger() == 1 ) {
qglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, shadowFragmentProgram1 );
} else {
qglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, shadowFragmentProgram0 );
}
}
qglEnable(GL_VERTEX_PROGRAM_ARB);
qglEnable(GL_FRAGMENT_PROGRAM_ARB);
// enable the vertex arrays
qglEnableVertexAttribArrayARB( 8 );
qglEnableVertexAttribArrayARB( 9 );
qglEnableVertexAttribArrayARB( 10 );
qglEnableVertexAttribArrayARB( 11 );
qglEnableClientState( GL_COLOR_ARRAY );
// texture 0 is the normalization cube map for the vector towards the light
GL_SelectTextureNoClient( 0 );
if ( backEnd.vLight->lightShader->IsAmbientLight() ) {
globalImages->normalCubeMapImage->Bind();
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_AMBIENT);
qglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, FPROG_AMBIENT);
} else {
globalImages->normalCubeMapImage->Bind();
}
// texture 6 is the specular lookup table
GL_SelectTextureNoClient( 6 );
if ( r_testARBProgram.GetBool() ) {
globalImages->specular2DTableImage->Bind(); // variable specularity in alpha channel
} else {
globalImages->specularTableImage->Bind();
}
for ( ; surf ; surf=surf->nextOnLight ) {
// perform setup here that will not change over multiple interaction passes
if ( backEnd.vLight->lightShader->IsAmbientLight() ) {
float parm[4];
parm[0] = surf->space->modelMatrix[0];
parm[1] = surf->space->modelMatrix[4];
parm[2] = surf->space->modelMatrix[8];
parm[3] = 0;
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, 20, parm );
parm[0] = surf->space->modelMatrix[1];
parm[1] = surf->space->modelMatrix[5];
parm[2] = surf->space->modelMatrix[9];
parm[3] = 0;
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, 21, parm );
parm[0] = surf->space->modelMatrix[2];
parm[1] = surf->space->modelMatrix[6];
parm[2] = surf->space->modelMatrix[10];
parm[3] = 0;
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, 22, parm );
GL_SelectTextureNoClient( 0 );
const shaderStage_t *stage = backEnd.vLight->lightShader->GetStage( 0 );
if ( stage->newStage ) {
stage->newStage->fragmentProgramImages[7]->BindFragment();
}
}
// set the vertex pointers
idDrawVert *ac = (idDrawVert *)vertexCache.Position( surf->geo->ambientCache );
qglColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( idDrawVert ), ac->color );
qglVertexAttribPointerARB( 11, 3, GL_FLOAT, false, sizeof( idDrawVert ), ac->normal.ToFloatPtr() );
qglVertexAttribPointerARB( 10, 3, GL_FLOAT, false, sizeof( idDrawVert ), ac->tangents[1].ToFloatPtr() );
qglVertexAttribPointerARB( 9, 3, GL_FLOAT, false, sizeof( idDrawVert ), ac->tangents[0].ToFloatPtr() );
qglVertexAttribPointerARB( 8, 2, GL_FLOAT, false, sizeof( idDrawVert ), ac->st.ToFloatPtr() );
qglVertexPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->xyz.ToFloatPtr() );
// this may cause RB_ARB2_DrawInteraction to be exacuted multiple
// times with different colors and images if the surface or light have multiple layers
if ( r_sb_screenSpaceShadow.GetBool() ) {
RB_CreateSingleDrawInteractions( surf, RB_ARB2_DrawInteraction );
} else {
RB_CreateSingleDrawInteractions( surf, RB_EXP_DrawInteraction );
}
}
qglDisableVertexAttribArrayARB( 8 );
qglDisableVertexAttribArrayARB( 9 );
qglDisableVertexAttribArrayARB( 10 );
qglDisableVertexAttribArrayARB( 11 );
qglDisableClientState( GL_COLOR_ARRAY );
// disable features
GL_SelectTextureNoClient( 6 );
globalImages->BindNull();
GL_SelectTextureNoClient( 5 );
globalImages->BindNull();
GL_SelectTextureNoClient( 4 );
globalImages->BindNull();
GL_SelectTextureNoClient( 3 );
globalImages->BindNull();
GL_SelectTextureNoClient( 2 );
globalImages->BindNull();
GL_SelectTextureNoClient( 1 );
globalImages->BindNull();
backEnd.glState.currenttmu = -1;
GL_SelectTexture( 0 );
qglDisable(GL_VERTEX_PROGRAM_ARB);
qglDisable(GL_FRAGMENT_PROGRAM_ARB);
}
void InvertByTranspose( const float a[16], float r[16] ) {
r[ 0] = a[ 0];
r[ 1] = a[ 4];
r[ 2] = a[ 8];
r[ 3] = 0;
r[ 4] = a[ 1];
r[ 5] = a[ 5];
r[ 6] = a[ 9];
r[ 7] = 0;
r[ 8] = a[ 2];
r[ 9] = a[ 6];
r[10] = a[10];
r[11] = 0;
r[12] = -(r[ 0]*a[12] + r[ 4]*a[13] + r[ 8]*a[14]);
r[13] = -(r[ 1]*a[12] + r[ 5]*a[13] + r[ 9]*a[14]);
r[14] = -(r[ 2]*a[12] + r[ 6]*a[13] + r[10]*a[14]);
r[15] = 1;
}
void FullInvert( const float a[16], float r[16] ) {
idMat4 am;
for ( int i = 0 ; i < 4 ; i++ ) {
for ( int j = 0 ; j < 4 ; j++ ) {
am[i][j] = a[j*4+i];
}
}
// idVec4 test( 100, 100, 100, 1 );
// idVec4 transformed, inverted;
// transformed = test * am;
if ( !am.InverseSelf() ) {
common->Error( "Invert failed" );
}
// inverted = transformed * am;
for ( int i = 0 ; i < 4 ; i++ ) {
for ( int j = 0 ; j < 4 ; j++ ) {
r[j*4+i] = am[i][j];
}
}
}
/*
==================
RB_Exp_TrianglesForFrustum
==================
*/
const srfTriangles_t *RB_Exp_TrianglesForFrustum( viewLight_t *vLight, int side ) {
const srfTriangles_t *tri;
static srfTriangles_t frustumTri;
static idDrawVert verts[5];
static glIndex_t indexes[18] = { 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 1, 2, 1, 4, 2, 4, 3 };
if ( side == -1 ) {
tri = vLight->frustumTris;
} else {
memset( verts, 0, sizeof( verts ) );
for ( int i = 0 ; i < 5 ; i++ ) {
verts[i].xyz = vLight->globalLightOrigin;
}
memset( &frustumTri, 0, sizeof( frustumTri ) );
frustumTri.indexes = indexes;
frustumTri.verts = verts;
frustumTri.numIndexes = 18;
frustumTri.numVerts = 5;
tri = &frustumTri;
float size = viewLightAxialSize;
switch ( side ) {
case 0:
verts[1].xyz[0] += size;
verts[2].xyz[0] += size;
verts[3].xyz[0] += size;
verts[4].xyz[0] += size;
verts[1].xyz[1] += size;
verts[1].xyz[2] += size;
verts[2].xyz[1] -= size;
verts[2].xyz[2] += size;
verts[3].xyz[1] -= size;
verts[3].xyz[2] -= size;
verts[4].xyz[1] += size;
verts[4].xyz[2] -= size;
break;
case 1:
verts[1].xyz[0] -= size;
verts[2].xyz[0] -= size;
verts[3].xyz[0] -= size;
verts[4].xyz[0] -= size;
verts[1].xyz[1] -= size;
verts[1].xyz[2] += size;
verts[2].xyz[1] += size;
verts[2].xyz[2] += size;
verts[3].xyz[1] += size;
verts[3].xyz[2] -= size;
verts[4].xyz[1] -= size;
verts[4].xyz[2] -= size;
break;
case 2:
verts[1].xyz[1] += size;
verts[2].xyz[1] += size;
verts[3].xyz[1] += size;
verts[4].xyz[1] += size;
verts[1].xyz[0] -= size;
verts[1].xyz[2] += size;
verts[2].xyz[0] += size;
verts[2].xyz[2] += size;
verts[3].xyz[0] += size;
verts[3].xyz[2] -= size;
verts[4].xyz[0] -= size;
verts[4].xyz[2] -= size;
break;
case 3:
verts[1].xyz[1] -= size;
verts[2].xyz[1] -= size;
verts[3].xyz[1] -= size;
verts[4].xyz[1] -= size;
verts[1].xyz[0] += size;
verts[1].xyz[2] += size;
verts[2].xyz[0] -= size;
verts[2].xyz[2] += size;
verts[3].xyz[0] -= size;
verts[3].xyz[2] -= size;
verts[4].xyz[0] += size;
verts[4].xyz[2] -= size;
break;
case 4:
verts[1].xyz[2] += size;
verts[2].xyz[2] += size;
verts[3].xyz[2] += size;
verts[4].xyz[2] += size;
verts[1].xyz[0] += size;
verts[1].xyz[1] += size;
verts[2].xyz[0] -= size;
verts[2].xyz[1] += size;
verts[3].xyz[0] -= size;
verts[3].xyz[1] -= size;
verts[4].xyz[0] += size;
verts[4].xyz[1] -= size;
break;
case 5:
verts[1].xyz[2] -= size;
verts[2].xyz[2] -= size;
verts[3].xyz[2] -= size;
verts[4].xyz[2] -= size;
verts[1].xyz[0] -= size;
verts[1].xyz[1] += size;
verts[2].xyz[0] += size;
verts[2].xyz[1] += size;
verts[3].xyz[0] += size;
verts[3].xyz[1] -= size;
verts[4].xyz[0] -= size;
verts[4].xyz[1] -= size;
break;
}
frustumTri.ambientCache = vertexCache.AllocFrameTemp( verts, sizeof( verts ) );
}
return tri;
}
/*
==================
RB_Exp_SelectFrustum
==================
*/
void RB_Exp_SelectFrustum( viewLight_t *vLight, int side ) {
qglLoadMatrixf( backEnd.viewDef->worldSpace.modelViewMatrix );
const srfTriangles_t *tri = RB_Exp_TrianglesForFrustum( vLight, side );
idDrawVert *ac = (idDrawVert *)vertexCache.Position( tri->ambientCache );
qglVertexPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->xyz.ToFloatPtr() );
qglDisable( GL_TEXTURE_2D );
qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
// clear stencil buffer
qglEnable( GL_SCISSOR_TEST );
qglEnable( GL_STENCIL_TEST );
qglClearStencil( 1 );
qglClear( GL_STENCIL_BUFFER_BIT );
// draw front faces of the light frustum, incrementing the stencil buffer on depth fail
// so we can't draw on those pixels
GL_State( GLS_COLORMASK | GLS_ALPHAMASK | GLS_DEPTHMASK | GLS_DEPTHFUNC_LESS );
qglStencilFunc( GL_ALWAYS, 0, 255 );
qglStencilOp( GL_KEEP, GL_INCR, GL_KEEP );
GL_Cull( CT_FRONT_SIDED );
RB_DrawElementsWithCounters( tri );
// draw back faces of the light frustum with
// depth test greater
// stencil test of equal 1
// zero stencil stencil when depth test passes, so subsequent surface drawing
// can occur on those pixels
// this pass does all the shadow filtering
qglStencilFunc( GL_EQUAL, 1, 255 );
qglStencilOp( GL_KEEP, GL_KEEP, GL_ZERO );
GL_Cull( CT_BACK_SIDED );
qglDepthFunc( GL_GREATER );
// write to destination alpha
if ( r_sb_showFrustumPixels.GetBool() ) {
GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHMASK | GLS_DEPTHFUNC_LESS );
qglDisable( GL_TEXTURE_2D );
qglColor4f( 0, 0.25, 0, 1 );
} else {
GL_State( GLS_COLORMASK | GLS_DEPTHMASK | GLS_DEPTHFUNC_LESS );
qglEnable(GL_VERTEX_PROGRAM_ARB);
qglEnable(GL_FRAGMENT_PROGRAM_ARB);
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, screenSpaceShadowVertexProgram );
switch ( r_sb_samples.GetInteger() ) {
case 0:
qglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, screenSpaceShadowFragmentProgram0 );
break;
case 1:
qglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, screenSpaceShadowFragmentProgram1 );
break;
case 4:
qglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, screenSpaceShadowFragmentProgram4 );
break;
case 16:
qglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, screenSpaceShadowFragmentProgram16 );
break;
}
}
/*
texture[0] = view depth texture
texture[1] = jitter texture
texture[2] = light depth texture
*/
GL_SelectTextureNoClient( 2 );
shadowImage[0]->Bind();
GL_SelectTextureNoClient( 1 );
if ( r_sb_samples.GetInteger() == 16 ) {
jitterImage16->Bind();
} else if ( r_sb_samples.GetInteger() == 4 ) {
jitterImage4->Bind();
} else {
jitterImage1->Bind();
}
GL_SelectTextureNoClient( 0 );
viewDepthImage->Bind();
/*
PARAM positionToDepthTexScale = program.local[0]; # fragment.position to screen depth texture transformation
PARAM zProject = program.local[1]; # projection[10], projection[14], 0, 0
PARAM positionToViewSpace = program.local[2]; # X add, Y add, X mul, Y mul
PARAM viewToLightS = program.local[3];
PARAM viewToLightT = program.local[4];
PARAM viewToLightR = program.local[5];
PARAM viewToLightQ = program.local[6];
PARAM positionToJitterTexScale = program.local[7]; # fragment.position to jitter texture
PARAM jitterTexScale = program.local[8];
PARAM jitterTexOffset = program.local[9];
*/
float parm[4];
int pot;
// calculate depth projection for shadow buffer
float sRow[4];
float tRow[4];
float rRow[4];
float qRow[4];
float invertedView[16];
float invertedProjection[16];
float matrix[16];
float matrix2[16];
// we need the inverse of the projection matrix to go from NDC to view
FullInvert( backEnd.viewDef->projectionMatrix, invertedProjection );
/*
from window to NDC:
( x - xMid ) * 1.0 / xMid
( y - yMid ) * 1.0 / yMid
( z - 0.5 ) * 2
from NDC to clip coordinates:
rcp(1/w)
*/
// we need the inverse of the viewMatrix to go from view (looking down negative Z) to world
InvertByTranspose( backEnd.viewDef->worldSpace.modelViewMatrix, invertedView );
// then we go from world to light view space (looking down negative Z)
myGlMultMatrix( invertedView, lightMatrix, matrix );
// then to light projection, giving X/w, Y/w, Z/w in the -1 : 1 range
myGlMultMatrix( matrix, lightProjectionMatrix, matrix2 );
// the final values need to be in 0.0 : 1.0 range instead of -1 : 1
sRow[0] = 0.5 * ( matrix2[0] + matrix2[3] ) * lightBufferSizeFraction;
sRow[1] = 0.5 * ( matrix2[4] + matrix2[7] ) * lightBufferSizeFraction;
sRow[2] = 0.5 * ( matrix2[8] + matrix2[11] ) * lightBufferSizeFraction;
sRow[3] = 0.5 * ( matrix2[12] + matrix2[15] ) * lightBufferSizeFraction;
qglProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 3, sRow );
tRow[0] = 0.5 * ( matrix2[1] + matrix2[3] ) * lightBufferSizeFraction;
tRow[1] = 0.5 * ( matrix2[5] + matrix2[7] ) * lightBufferSizeFraction;
tRow[2] = 0.5 * ( matrix2[9] + matrix2[11] ) * lightBufferSizeFraction;
tRow[3] = 0.5 * ( matrix2[13] + matrix2[15] ) * lightBufferSizeFraction;
qglProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 4, tRow );
rRow[0] = 0.5 * ( matrix2[2] + matrix2[3] );
rRow[1] = 0.5 * ( matrix2[6] + matrix2[7] );
rRow[2] = 0.5 * ( matrix2[10] + matrix2[11] );
rRow[3] = 0.5 * ( matrix2[14] + matrix2[15] );
qglProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 5, rRow );
qRow[0] = matrix2[3];
qRow[1] = matrix2[7];
qRow[2] = matrix2[11];
qRow[3] = matrix2[15];
qglProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 6, qRow );
//-----------------------------------------------------
// these should be constant for the entire frame
// convert 0..viewport-1 sizes to fractions inside the POT screen depth texture
int w = viewBufferSize;
pot = MakePowerOfTwo( w );
parm[0] = 1.0 / maxViewBufferSize; // * ( (float)viewBufferSize / w );
int h = viewBufferHeight;
pot = MakePowerOfTwo( h );
parm[1] = parm[0]; // 1.0 / pot;
parm[2] = 0;
parm[3] = 1;
qglProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 0, parm );
// zProject values
parm[0] = backEnd.viewDef->projectionMatrix[10];
parm[1] = backEnd.viewDef->projectionMatrix[14];
parm[2] = 0;
parm[3] = 0;
qglProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 1, parm );
// positionToViewSpace
parm[0] = -1.0 / backEnd.viewDef->projectionMatrix[0];
parm[1] = -1.0 / backEnd.viewDef->projectionMatrix[5];
parm[2] = 2.0/viewBufferSize;
parm[3] = 2.0/viewBufferSize;
qglProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 2, parm );
// positionToJitterTexScale
parm[0] = 1.0 / ( JITTER_SIZE * r_sb_samples.GetInteger() ) ;
parm[1] = 1.0 / JITTER_SIZE;
parm[2] = 0;
parm[3] = 1;
qglProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 7, parm );
// jitter tex scale
parm[0] =
parm[1] = r_sb_jitterScale.GetFloat() * lightBufferSizeFraction;
parm[2] = -r_sb_biasScale.GetFloat();
parm[3] = 0;
qglProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 8, parm );
// jitter tex offset
if ( r_sb_randomize.GetBool() ) {
parm[0] = (rand()&255) / 255.0;
parm[1] = (rand()&255) / 255.0;
} else {
parm[0] = parm[1] = 0;
}
parm[2] = 0;
parm[3] = 0;
qglProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 9, parm );
//-----------------------------------------------------
RB_DrawElementsWithCounters( tri );
qglDisable(GL_VERTEX_PROGRAM_ARB);
qglDisable(GL_FRAGMENT_PROGRAM_ARB);
GL_Cull( CT_FRONT_SIDED );
// qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
qglDepthFunc( GL_LEQUAL );
if ( r_sb_showFrustumPixels.GetBool() ) {
qglEnable( GL_TEXTURE_2D );
qglColor3f( 1, 1, 1 );
}
// after all the frustums have been drawn, the surfaces that have been drawn on will get interactions
// scissor may still be a win even with the stencil test for very fast rejects
qglStencilFunc( GL_EQUAL, 0, 255 );
qglStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );
// we can avoid clearing the stencil buffer by changing the hasLight value for each light
}
/*
==================
R_EXP_CalcLightAxialSize
all light side projections must currently match, so non-centered
and non-cubic lights must take the largest length
==================
*/
float R_EXP_CalcLightAxialSize( viewLight_t *vLight ) {
float max = 0;
if ( !vLight->lightDef->parms.pointLight ) {
idVec3 dir = vLight->lightDef->parms.target - vLight->lightDef->parms.origin;
max = dir.Length();
return max;
}
for ( int i = 0 ; i < 3 ; i++ ) {
float dist = fabs(vLight->lightDef->parms.lightCenter[i] );
dist += vLight->lightDef->parms.lightRadius[i];
if ( dist > max ) {
max = dist;
}
}
return max;
}
/*
==================
R_EXP_RenderViewDepthImage
This could be avoided by drop sampling the native view depth buffer with render to texture
Bilerp might even be aprorpiate, although it would cause issues at edges
==================
*/
void RB_T_FillDepthBuffer( const drawSurf_t *surf );
void R_EXP_RenderViewDepthImage( void ) {
if ( !r_sb_screenSpaceShadow.GetBool() ) {
return;
}
// if the screen resolution is exactly the window width, we can
// use the depth buffer we already have
if ( 0 ) { // nativeViewBuffer ) {
viewDepthImage->CopyDepthbuffer( backEnd.viewDef->viewport.x1,
backEnd.viewDef->viewport.y1, backEnd.viewDef->viewport.x2 - backEnd.viewDef->viewport.x1 + 1,
backEnd.viewDef->viewport.y2 - backEnd.viewDef->viewport.y1 + 1 );
} else {
RB_LogComment( "---------- R_EXP_RenderViewDepthImage ----------\n" );
if ( r_sb_usePbuffer.GetBool() ) {
GL_CheckErrors();
// set the current openGL drawable to the shadow buffer
R_MakeCurrent( viewPbufferDC, win32.hGLRC, NULL /* !@# viewPbuffer */ );
}
// render the depth to the new size
qglViewport( 0, 0, viewBufferSize, viewBufferHeight );
qglScissor( 0, 0, viewBufferSize, viewBufferHeight );
qglClear( GL_DEPTH_BUFFER_BIT );
qglStencilFunc( GL_ALWAYS, 0, 255 );
// the first texture will be used for alpha tested surfaces
GL_SelectTexture( 0 );
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
GL_State( GLS_DEPTHFUNC_LESS );
RB_RenderDrawSurfListWithFunction( backEnd.viewDef->drawSurfs, backEnd.viewDef->numDrawSurfs, RB_T_FillDepthBuffer );
//
// copy it to a texture
//
viewDepthImage->Bind();
qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, 0, viewBufferSize, viewBufferHeight );
if ( r_sb_usePbuffer.GetBool() ) {
// set the normal screen drawable current
R_MakeCurrent( win32.hDC, win32.hGLRC, NULL );
}
// reset the window clipping
qglMatrixMode( GL_PROJECTION );
qglLoadMatrixf( backEnd.viewDef->projectionMatrix );
qglMatrixMode( GL_MODELVIEW );
qglViewport( tr.viewportOffset[0] + backEnd.viewDef->viewport.x1,
tr.viewportOffset[1] + backEnd.viewDef->viewport.y1,
backEnd.viewDef->viewport.x2 + 1 - backEnd.viewDef->viewport.x1,
backEnd.viewDef->viewport.y2 + 1 - backEnd.viewDef->viewport.y1 );
qglScissor( tr.viewportOffset[0] + backEnd.viewDef->viewport.x1,
tr.viewportOffset[1] + backEnd.viewDef->viewport.y1,
backEnd.viewDef->viewport.x2 + 1 - backEnd.viewDef->viewport.x1,
backEnd.viewDef->viewport.y2 + 1 - backEnd.viewDef->viewport.y1 );
// the current modelView matrix is not valid
backEnd.currentSpace = NULL;
}
qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE );
qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE );
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
}
/*
==================
RB_EXP_SetNativeBuffer
This is always the back buffer, and scissor is set full screen
==================
*/
void RB_EXP_SetNativeBuffer( void ) {
// set the normal screen drawable current
R_MakeCurrent( win32.hDC, win32.hGLRC, NULL );
qglViewport( tr.viewportOffset[0] + backEnd.viewDef->viewport.x1,
tr.viewportOffset[1] + backEnd.viewDef->viewport.y1,
backEnd.viewDef->viewport.x2 + 1 - backEnd.viewDef->viewport.x1,
backEnd.viewDef->viewport.y2 + 1 - backEnd.viewDef->viewport.y1 );
backEnd.currentScissor = backEnd.viewDef->viewport;
if ( r_useScissor.GetBool() ) {
qglScissor( backEnd.viewDef->viewport.x1 + backEnd.currentScissor.x1,
backEnd.viewDef->viewport.y1 + backEnd.currentScissor.y1,
backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1,
backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1 );
}
}
/*
==================
RB_EXP_SetRenderBuffer
This may be to a float pBuffer, and scissor is set to cover only the light
==================
*/
void RB_EXP_SetRenderBuffer( viewLight_t *vLight ) {
if ( r_hdr_useFloats.GetBool() ) {
R_MakeCurrent( floatPbufferDC, floatContext, floatPbuffer );
} else {
if ( !qwglMakeCurrent( win32.hDC, win32.hGLRC ) ) {
GL_CheckErrors();
common->FatalError( "Couldn't return to normal drawing context" );
}
}
qglViewport( tr.viewportOffset[0] + backEnd.viewDef->viewport.x1,
tr.viewportOffset[1] + backEnd.viewDef->viewport.y1,
backEnd.viewDef->viewport.x2 + 1 - backEnd.viewDef->viewport.x1,
backEnd.viewDef->viewport.y2 + 1 - backEnd.viewDef->viewport.y1 );
if ( !vLight ) {
backEnd.currentScissor = backEnd.viewDef->viewport;
} else {
backEnd.currentScissor = vLight->scissorRect;
}
if ( r_useScissor.GetBool() ) {
qglScissor( backEnd.viewDef->viewport.x1 + backEnd.currentScissor.x1,
backEnd.viewDef->viewport.y1 + backEnd.currentScissor.y1,
backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1,
backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1 );
}
}
/*
==================
RB_shadowResampleAlpha
==================
*/
void RB_shadowResampleAlpha( void ) {
viewAlphaImage->Bind();
// we could make this a subimage, but it isn't relevent once we have render-to-texture
qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, 0, viewBufferSize, viewBufferHeight );
RB_EXP_SetRenderBuffer( backEnd.vLight );
//=====================
qglLoadMatrixf( backEnd.viewDef->worldSpace.modelViewMatrix );
// this uses the full light, not side frustums
const srfTriangles_t *tri = backEnd.vLight->frustumTris;
idDrawVert *ac = (idDrawVert *)vertexCache.Position( tri->ambientCache );
qglVertexPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->xyz.ToFloatPtr() );
// clear stencil buffer
qglEnable( GL_SCISSOR_TEST );
qglEnable( GL_STENCIL_TEST );
qglClearStencil( 1 );
qglClear( GL_STENCIL_BUFFER_BIT );
// draw front faces of the light frustum, incrementing the stencil buffer on depth fail
// so we can't draw on those pixels
GL_State( GLS_COLORMASK | GLS_ALPHAMASK | GLS_DEPTHMASK | GLS_DEPTHFUNC_LESS );
qglStencilFunc( GL_ALWAYS, 0, 255 );
qglStencilOp( GL_KEEP, GL_INCR, GL_KEEP );
GL_Cull( CT_FRONT_SIDED );
// set fragment / vertex program?
RB_DrawElementsWithCounters( tri );
// draw back faces of the light frustum with
// depth test greater
// stencil test of equal 1
// zero stencil stencil when depth test passes, so subsequent interaction drawing
// can occur on those pixels
// this pass does all the shadow filtering
qglStencilFunc( GL_EQUAL, 1, 255 );
qglStencilOp( GL_KEEP, GL_KEEP, GL_ZERO );
// write to destination alpha
if ( r_sb_showFrustumPixels.GetBool() ) {
GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHMASK | GLS_DEPTHFUNC_LESS );
qglDisable( GL_TEXTURE_2D );
qglColor4f( 0, 0.25, 0, 1 );
} else {
GL_State( GLS_COLORMASK | GLS_DEPTHMASK | GLS_DEPTHFUNC_LESS );
qglEnable(GL_VERTEX_PROGRAM_ARB);
qglEnable(GL_FRAGMENT_PROGRAM_ARB);
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, shadowResampleVertexProgram );
qglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, shadowResampleFragmentProgram );
// convert 0..viewport-1 sizes to fractions inside the POT screen depth texture
// shrink by one unit for bilerp
float parm[4];
parm[0] = 1.0 / (maxViewBufferSize+1) * viewBufferSize / maxViewBufferSize;
parm[1] = parm[0];
parm[2] = 0;
parm[3] = 1;
qglProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 0, parm );
}
GL_Cull( CT_BACK_SIDED );
qglDepthFunc( GL_GREATER );
RB_DrawElementsWithCounters( tri );
qglDisable(GL_VERTEX_PROGRAM_ARB);
qglDisable(GL_FRAGMENT_PROGRAM_ARB);
GL_Cull( CT_FRONT_SIDED );
qglDepthFunc( GL_LEQUAL );
if ( r_sb_showFrustumPixels.GetBool() ) {
qglEnable( GL_TEXTURE_2D );
qglColor3f( 1, 1, 1 );
}
// after all the frustums have been drawn, the surfaces that have been drawn on will get interactions
// scissor may still be a win even with the stencil test for very fast rejects
qglStencilFunc( GL_EQUAL, 0, 255 );
qglStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );
}
/*
==================
RB_EXP_CoverScreen
==================
*/
void RB_EXP_CoverScreen( void ) {
// draw a full screen quad
qglMatrixMode( GL_PROJECTION );
qglLoadIdentity();
qglOrtho( 0, 1, 0, 1, -1, 1 );
qglMatrixMode( GL_MODELVIEW );
qglLoadIdentity();
qglBegin( GL_TRIANGLE_FAN );
qglVertex2f( 0, 0 );
qglVertex2f( 0, 1 );
qglVertex2f( 1, 1 );
qglVertex2f( 1, 0 );
qglEnd();
}
/*
==================
RB_EXP_ReadFloatBuffer
==================
*/
void RB_EXP_ReadFloatBuffer( void ) {
int pixels = glConfig.vidWidth * glConfig.vidHeight;
float *buf = (float *)R_StaticAlloc( pixels * 4 * sizeof( float ) );
qglReadPixels( 0, 0, glConfig.vidWidth, glConfig.vidHeight, GL_RGBA, GL_FLOAT, buf );
float mins[4] = { 9999, 9999, 9999, 9999 };
float maxs[4] = { -9999, -9999, -9999, -9999 };
for ( int i = 0 ; i < pixels ; i++ ) {
for ( int j = 0 ; j < 4 ; j++ ) {
float v = buf[ i*4 + j ];
if ( v < mins[j] ) {
mins[j] = v;
}
if ( v > maxs[j] ) {
maxs[j] = v;
}
}
}
RB_EXP_SetNativeBuffer();
qglLoadIdentity();
qglMatrixMode( GL_PROJECTION );
GL_State( GLS_DEPTHFUNC_ALWAYS );
qglColor3f( 1, 1, 1 );
qglPushMatrix();
qglLoadIdentity();
qglDisable( GL_TEXTURE_2D );
qglOrtho( 0, 1, 0, 1, -1, 1 );
qglRasterPos2f( 0.01f, 0.01f );
qglDrawPixels( glConfig.vidWidth, glConfig.vidHeight, GL_RGBA, GL_FLOAT, buf );
qglPopMatrix();
qglEnable( GL_TEXTURE_2D );
qglMatrixMode( GL_MODELVIEW );
R_StaticFree( buf );
}
void RB_TestGamma( void );
/*
==================
RB_EXP_GammaDither
==================
*/
void RB_EXP_GammaDither( void ) {
if ( !r_hdr_useFloats.GetBool() ) {
return;
}
#if 0
r_testGamma.SetBool( true );
RB_TestGamma();
r_testGamma.SetBool( false );
#endif
RB_EXP_SetNativeBuffer();
/*
# texture 0 is the high dynamic range buffer
# texture 1 is the random dither texture
# texture 2 is the light bloom texture
# writes result.color as the 32 bit dithered and gamma corrected values
PARAM exposure = program.local[0]; # multiply HDR value by this to get screen pixels
PARAM gammaPower = program.local[1];
PARAM monitorDither = program.local[2];
PARAM positionToDitherScale = program.local[3];
PARAM bloomFraction = program.local[4];
PARAM positionToBloomScale = program.local[5];
*/
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB,gammaDitherVertexProgram );
qglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, gammaDitherFragmentProgram );
qglEnable(GL_VERTEX_PROGRAM_ARB);
qglEnable(GL_FRAGMENT_PROGRAM_ARB);
qglActiveTextureARB( GL_TEXTURE2_ARB );
qglBindTexture( GL_TEXTURE_RECTANGLE_NV, floatPbufferQuarterImage->texnum );
R_BindTexImage( floatPbufferQuarter );
qglActiveTextureARB( GL_TEXTURE1_ARB );
random256Image->BindFragment();
qglActiveTextureARB( GL_TEXTURE0_ARB );
qglBindTexture( GL_TEXTURE_RECTANGLE_NV, floatPbufferImage->texnum );
R_BindTexImage( floatPbuffer );
float parm[4];
parm[0] = r_hdr_exposure.GetFloat();
parm[1] = 0;
parm[2] = 0;
parm[3] = 0;
qglProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 0, parm );
parm[0] = r_hdr_gamma.GetFloat();
parm[1] = 0;
parm[2] = 0;
parm[3] = 0;
qglProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 1, parm );
parm[0] = r_hdr_monitorDither.GetFloat();
parm[1] = 0;
parm[2] = 0;
parm[3] = 0;
qglProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 2, parm );
parm[0] = 1.0 / 256;
parm[1] = parm[0];
parm[2] = rand()/65535.0;
parm[3] = rand()/65535.0;
qglProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 3, parm );
parm[0] = 1.0 - r_hdr_bloomFraction.GetFloat();
parm[1] = r_hdr_bloomFraction.GetFloat();
parm[2] = 0;
parm[3] = 0;
qglProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 4, parm );
parm[0] = 0.25;
parm[1] = 0.25;
parm[2] = 0;
parm[3] = 0;
qglProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 5, parm );
qglDisable( GL_STENCIL_TEST );
qglDisable( GL_SCISSOR_TEST );
qglDisable( GL_DEPTH_TEST );
RB_EXP_CoverScreen();
qglEnable( GL_DEPTH_TEST );
qglDisable(GL_VERTEX_PROGRAM_ARB);
qglDisable(GL_FRAGMENT_PROGRAM_ARB);
}
/*
==================
RB_EXP_Bloom
==================
*/
void RB_EXP_Bloom( void ) {
if ( !r_hdr_useFloats.GetBool() ) {
return;
}
if ( r_hdr_bloomFraction.GetFloat() == 0 ) {
return;
}
GL_CheckErrors();
//
// mip map
//
// draw to the second floatPbuffer
R_MakeCurrent( floatPbuffer2DC, floatContext, floatPbuffer2 );
GL_State( 0 );
qglDisable( GL_DEPTH_TEST );
qglDisable( GL_SCISSOR_TEST );
qglEnable(GL_VERTEX_PROGRAM_ARB);
qglEnable(GL_FRAGMENT_PROGRAM_ARB);
qglClearColor( 1.0, 0.5, 0, 0 );
qglClear( GL_COLOR_BUFFER_BIT );
qglViewport( 0, 0, glConfig.vidWidth>>1, glConfig.vidHeight>>1 );
// read from the original floatPbuffer
qglActiveTextureARB( GL_TEXTURE0_ARB );
qglBindTexture( GL_TEXTURE_RECTANGLE_NV, floatPbufferImage->texnum );
R_BindTexImage( floatPbuffer );
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, downSampleVertexProgram );
qglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, downSampleFragmentProgram );
RB_EXP_CoverScreen();
//
// mip map again
//
qglViewport( 0, 0, glConfig.vidWidth>>2, glConfig.vidHeight>>2 );
// draw to the second floatPbuffer
R_MakeCurrent( floatPbufferQuarterDC, floatContext, floatPbufferQuarter );
// read from the original floatPbuffer
qglBindTexture( GL_TEXTURE_RECTANGLE_NV, floatPbuffer2Image->texnum );
R_BindTexImage( floatPbuffer2 );
RB_EXP_CoverScreen();
//
// blur horizontally
//
/*
# texture 0 is the high dynamic range buffer
# writes result.color as the fp16 result of a smeared bloom
PARAM step = program.local[0]; # { 1, 0 } or { 0, 1 } for horizontal / vertical separation
*/
// draw to the second floatPbuffer
R_MakeCurrent( floatPbuffer2DC, floatContext, floatPbuffer2 );
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, bloomVertexProgram );
qglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, bloomFragmentProgram );
qglEnable(GL_VERTEX_PROGRAM_ARB);
qglEnable(GL_FRAGMENT_PROGRAM_ARB);
GL_SelectTextureNoClient( 0 );
// blur horizontally first to the second floatPbuffer
qglBindTexture( GL_TEXTURE_RECTANGLE_NV, floatPbufferQuarterImage->texnum );
R_BindTexImage( floatPbufferQuarter );
float parm[4];
parm[0] = 1;
parm[1] = 0;
parm[2] = 0;
parm[3] = 0;
qglProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 0, parm );
RB_EXP_CoverScreen();
//
// now blur vertically back to the quarter pbuffer
//
R_MakeCurrent( floatPbufferQuarterDC, floatContext, floatPbufferQuarter );
qglBindTexture( GL_TEXTURE_RECTANGLE_NV, floatPbuffer2Image->texnum );
R_BindTexImage( floatPbuffer2 );
parm[0] = 0;
parm[1] = 1;
parm[2] = 0;
parm[3] = 0;
qglProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 0, parm );
RB_EXP_CoverScreen();
//========================
qglEnable( GL_DEPTH_TEST );
qglEnable( GL_SCISSOR_TEST );
qglDisable(GL_VERTEX_PROGRAM_ARB);
qglDisable(GL_FRAGMENT_PROGRAM_ARB);
GL_CheckErrors();
}
/*
==================
RB_Exp_DrawInteractions
==================
*/
void RB_Exp_DrawInteractions( void ) {
if ( !initialized ) {
R_Exp_Allocate();
}
if ( !backEnd.viewDef->viewLights ) {
return;
}
// validate the samples
if ( r_sb_samples.GetInteger() != 16 && r_sb_samples.GetInteger() != 4 && r_sb_samples.GetInteger() != 1 ) {
r_sb_samples.SetInteger( 0 );
}
// validate the light resolution
if ( r_sb_lightResolution.GetInteger() < 64 ) {
r_sb_lightResolution.SetInteger( 64 );
} else if ( r_sb_lightResolution.GetInteger() > maxLightBufferSize ) {
r_sb_lightResolution.SetInteger( maxLightBufferSize );
}
lightBufferSize = r_sb_lightResolution.GetInteger();
lightBufferSizeFraction = (float)lightBufferSize / maxLightBufferSize;
// validate the view resolution
if ( r_sb_viewResolution.GetInteger() < 64 ) {
r_sb_viewResolution.SetInteger( 64 );
} else if ( r_sb_viewResolution.GetInteger() > maxViewBufferSize ) {
r_sb_viewResolution.SetInteger( maxViewBufferSize );
}
viewBufferSize = r_sb_viewResolution.GetInteger();
viewBufferHeight = viewBufferSize * 3 / 4;
viewBufferSizeFraction = (float)viewBufferSize / maxViewBufferSize;
viewBufferHeightFraction = (float)viewBufferHeight / maxViewBufferSize;
if ( viewBufferSize == backEnd.viewDef->viewport.x2 - backEnd.viewDef->viewport.x1 + 1 ) {
nativeViewBuffer = true;
} else {
nativeViewBuffer = false;
}
// set up for either point sampled or percentage-closer filtering for the shadow sampling
shadowImage[0]->BindFragment();
if ( r_sb_linearFilter.GetBool() ) {
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
} else {
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
}
qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE );
globalImages->BindNull();
// copy the current depth buffer to a texture for image-space shadowing,
// or re-render at a lower resolution
R_EXP_RenderViewDepthImage();
GL_SelectTexture( 0 );
qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
// disable stencil shadow test
qglStencilFunc( GL_ALWAYS, 128, 255 );
// the jitter image will be used to offset sample centers
GL_SelectTextureNoClient( 8 );
if ( r_sb_samples.GetInteger() == 16 ) {
jitterImage16->BindFragment();
} else if ( r_sb_samples.GetInteger() == 4 ) {
jitterImage4->BindFragment();
} else {
jitterImage1->BindFragment();
}
// if we are using a float buffer, clear it now
if ( r_hdr_useFloats.GetBool() ) {
RB_EXP_SetRenderBuffer( NULL );
// we need to set a lot of things, because this is a completely different context
RB_SetDefaultGLState();
qglClearColor( 0.001f, 1.0f, 0.01f, 0.1f );
qglClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
// clear the z buffer, set the projection matrix, etc
RB_BeginDrawingView();
RB_STD_FillDepthBuffer( (drawSurf_t **)&backEnd.viewDef->drawSurfs[0], backEnd.viewDef->numDrawSurfs );
}
//
// for each light, perform adding and shadowing
//
for ( viewLight_t *vLight = backEnd.viewDef->viewLights ; vLight ; vLight = vLight->next ) {
backEnd.vLight = vLight;
const idMaterial *lightShader = vLight->lightShader;
// do fogging later
if ( lightShader->IsFogLight() ) {
continue;
}
if ( lightShader->IsBlendLight() ) {
continue;
}
if ( !vLight->localInteractions && !vLight->globalInteractions
&& !vLight->translucentInteractions ) {
continue;
}
if ( !vLight->frustumTris->ambientCache ) {
R_CreateAmbientCache( const_cast<srfTriangles_t *>(vLight->frustumTris), false );
}
// all light side projections must currently match, so non-centered
// and non-cubic lights must take the largest length
viewLightAxialSize = R_EXP_CalcLightAxialSize( vLight );
int side, sideStop;
if ( vLight->lightDef->parms.pointLight ) {
if ( r_sb_singleSide.GetInteger() != -1 ) {
side = r_sb_singleSide.GetInteger();
sideStop = side+1;
} else {
side = 0;
sideStop = 6;
}
} else {
side = -1;
sideStop = 0;
}
for ( ; side < sideStop ; side++ ) {
// FIXME: check for frustums completely off the screen
// render a shadow buffer
RB_RenderShadowBuffer( vLight, side );
// back to view rendering, possibly in the off-screen buffer
if ( nativeViewBuffer || !r_sb_screenSpaceShadow.GetBool() ) {
// directly to screen
RB_EXP_SetRenderBuffer( vLight );
} else {
// to off screen buffer
if ( r_sb_usePbuffer.GetBool() ) {
GL_CheckErrors();
// set the current openGL drawable to the shadow buffer
R_MakeCurrent( viewPbufferDC, win32.hGLRC, viewPbuffer );
}
qglViewport( 0, 0, viewBufferSize, viewBufferHeight );
qglScissor( 0, 0, viewBufferSize, viewBufferHeight ); // !@# FIXME: scale light scissor
}
// render the shadows into destination alpha on the included pixels
RB_Exp_SelectFrustum( vLight, side );
if ( !r_sb_screenSpaceShadow.GetBool() ) {
// bind shadow buffer to texture
GL_SelectTextureNoClient( 7 );
shadowImage[0]->BindFragment();
RB_EXP_CreateDrawInteractions( vLight->localInteractions );
RB_EXP_CreateDrawInteractions( vLight->globalInteractions );
backEnd.depthFunc = GLS_DEPTHFUNC_LESS;
RB_EXP_CreateDrawInteractions( vLight->translucentInteractions );
backEnd.depthFunc = GLS_DEPTHFUNC_EQUAL;
}
}
// render the native window coordinates interactions
if ( r_sb_screenSpaceShadow.GetBool() ) {
if ( !nativeViewBuffer ) {
RB_shadowResampleAlpha();
qglEnable( GL_STENCIL_TEST );
} else {
RB_EXP_SetRenderBuffer( vLight );
if ( r_ignore.GetBool() ) {
qglEnable( GL_STENCIL_TEST ); //!@#
} else {
qglDisable( GL_STENCIL_TEST ); //!@#
}
}
RB_EXP_CreateDrawInteractions( vLight->localInteractions );
RB_EXP_CreateDrawInteractions( vLight->globalInteractions );
backEnd.depthFunc = GLS_DEPTHFUNC_LESS;
RB_EXP_CreateDrawInteractions( vLight->translucentInteractions );
backEnd.depthFunc = GLS_DEPTHFUNC_EQUAL;
}
}
GL_SelectTexture( 0 );
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
// experimental transfer function
for ( int i = 7 ; i >= 0 ; i-- ) {
GL_SelectTextureNoClient( i );
globalImages->BindNull();
}
GL_State( 0 );
RB_EXP_Bloom();
RB_EXP_GammaDither();
// these haven't been state saved
for ( int i = 0 ; i < 8 ; i++ ) {
backEnd.glState.tmu[i].current2DMap = -1;
}
// take it out of texture compare mode so I can testImage it for debugging
shadowImage[0]->BindFragment();
qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE );
}
/*
==================
R_Exp_Init
==================
*/
void R_Exp_Init( void ) {
glConfig.allowExpPath = false;
common->Printf( "---------- R_Exp_Init ----------\n" );
if ( !glConfig.ARBVertexProgramAvailable || !glConfig.ARBFragmentProgramAvailable ) {
common->Printf( "Not available.\n" );
return;
}
RB_CreateBloomTable();
#if 0
if ( !R_CheckExtension( "GL_NV_float_buffer" ) ) {
common->Printf( "Not available.\n" );
return;
}
if ( !R_CheckExtension( "GL_NV_texture_rectangle" ) ) {
common->Printf( "Not available.\n" );
return;
}
#endif
#if 0
qglCombinerParameterfvNV = (void (APIENTRY *)( GLenum pname, const GLfloat *params ))
GLimp_ExtensionPointer( "glCombinerParameterfvNV" );
#endif
common->Printf( "Available.\n" );
if ( !idStr::Icmp( r_renderer.GetString(), "exp" ) ) {
R_Exp_Allocate();
}
common->Printf( "--------------------------------------------\n" );
glConfig.allowExpPath = true;
}