mirror of
https://github.com/dhewm/dhewm3.git
synced 2024-11-23 21:02:11 +00:00
b83868090c
The DirectX SDK is not required anymore.
2652 lines
82 KiB
C++
2652 lines
82 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
|
|
|
|
*/
|
|
|
|
// WGL_ARB_pixel_format
|
|
static PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB;
|
|
|
|
// WGL_ARB_pixel_format
|
|
static PFNWGLGETPIXELFORMATATTRIBIVARBPROC wglGetPixelFormatAttribivARB;
|
|
static PFNWGLGETPIXELFORMATATTRIBFVARBPROC wglGetPixelFormatAttribfvARB;
|
|
|
|
// WGL_ARB_pbuffer
|
|
static PFNWGLCREATEPBUFFERARBPROC wglCreatePbufferARB;
|
|
static PFNWGLGETPBUFFERDCARBPROC wglGetPbufferDCARB;
|
|
static PFNWGLRELEASEPBUFFERDCARBPROC wglReleasePbufferDCARB;
|
|
static PFNWGLDESTROYPBUFFERARBPROC wglDestroyPbufferARB;
|
|
static PFNWGLQUERYPBUFFERARBPROC wglQueryPbufferARB;
|
|
|
|
// WGL_ARB_render_texture
|
|
static PFNWGLBINDTEXIMAGEARBPROC wglBindTexImageARB;
|
|
static PFNWGLRELEASETEXIMAGEARBPROC wglReleaseTexImageARB;
|
|
static PFNWGLSETPBUFFERATTRIBARBPROC wglSetPbufferAttribARB;
|
|
|
|
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 );
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
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;
|
|
|
|
// WGL_ARB_pixel_format
|
|
wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)GLimp_ExtensionPointer("wglChoosePixelFormatARB");
|
|
|
|
// WGL_ARB_pixel_format
|
|
wglGetPixelFormatAttribivARB = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC)GLimp_ExtensionPointer("wglGetPixelFormatAttribivARB");
|
|
wglGetPixelFormatAttribfvARB = (PFNWGLGETPIXELFORMATATTRIBFVARBPROC)GLimp_ExtensionPointer("wglGetPixelFormatAttribfvARB");
|
|
|
|
// WGL_ARB_pbuffer
|
|
wglCreatePbufferARB = (PFNWGLCREATEPBUFFERARBPROC)GLimp_ExtensionPointer("wglCreatePbufferARB");
|
|
wglGetPbufferDCARB = (PFNWGLGETPBUFFERDCARBPROC)GLimp_ExtensionPointer("wglGetPbufferDCARB");
|
|
wglReleasePbufferDCARB = (PFNWGLRELEASEPBUFFERDCARBPROC)GLimp_ExtensionPointer("wglReleasePbufferDCARB");
|
|
wglDestroyPbufferARB = (PFNWGLDESTROYPBUFFERARBPROC)GLimp_ExtensionPointer("wglDestroyPbufferARB");
|
|
wglQueryPbufferARB = (PFNWGLQUERYPBUFFERARBPROC)GLimp_ExtensionPointer("wglQueryPbufferARB");
|
|
|
|
// WGL_ARB_render_texture
|
|
wglBindTexImageARB = (PFNWGLBINDTEXIMAGEARBPROC)GLimp_ExtensionPointer("wglBindTexImageARB");
|
|
wglReleaseTexImageARB = (PFNWGLRELEASETEXIMAGEARBPROC)GLimp_ExtensionPointer("wglReleaseTexImageARB");
|
|
wglSetPbufferAttribARB = (PFNWGLSETPBUFFERATTRIBARBPROC)GLimp_ExtensionPointer("wglSetPbufferAttribARB");
|
|
|
|
#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 {
|
|
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;
|
|
}
|