- Implement stereo rendering with anaglyph images.

- Add r_greyscale for black&white rendering
This commit is contained in:
Thilo Schulz 2008-04-27 17:32:14 +00:00
parent 48d9999698
commit 425c351d1b
12 changed files with 435 additions and 128 deletions

View file

@ -155,7 +155,6 @@ LIBSDIR=$(MOUNT_DIR)/libs
TEMPDIR=/tmp TEMPDIR=/tmp
# extract version info # extract version info
# echo $(BUILD_CLIENT)
ifeq ($(BUILD_STANDALONE),1) ifeq ($(BUILD_STANDALONE),1)
VERSION=$(shell grep "\#define *PRODUCT_VERSION" $(CMDIR)/q_shared.h | head -n 1 | \ VERSION=$(shell grep "\#define *PRODUCT_VERSION" $(CMDIR)/q_shared.h | head -n 1 | \

14
README
View file

@ -155,6 +155,20 @@ New cvars
ipv6 servers on the local network ipv6 servers on the local network
net_mcastiface - outgoing interface to use for scan net_mcastiface - outgoing interface to use for scan
r_zProj - distance of observer camera to projection
plane
r_greyscale - render black and white images
r_anaglyphMode - Enable rendering of anaglyph images
red-cyan glasses: 1
red-blue: 2
red-green: 3
To swap the colors for both sides just
add 3 to the value for the wanted color
combination. For red-blue and red-green
you probably want to enable r_greyscale.
r_stereoSeparation - Control eye separation. Resulting
separation is r_zProj divided by this
value.
New commands New commands
video [filename] - start video capture (use with demo command) video [filename] - start video capture (use with demo command)

View file

@ -474,7 +474,7 @@ void SCR_DrawScreenField( stereoFrame_t stereoFrame ) {
case CA_LOADING: case CA_LOADING:
case CA_PRIMED: case CA_PRIMED:
// draw the game information screen and loading progress // draw the game information screen and loading progress
CL_CGameRendering( stereoFrame ); CL_CGameRendering(STEREO_CENTER);
// also draw the connection information, so it doesn't // also draw the connection information, so it doesn't
// flash away too briefly on local or lan games // flash away too briefly on local or lan games
@ -483,7 +483,8 @@ void SCR_DrawScreenField( stereoFrame_t stereoFrame ) {
VM_Call( uivm, UI_DRAW_CONNECT_SCREEN, qtrue ); VM_Call( uivm, UI_DRAW_CONNECT_SCREEN, qtrue );
break; break;
case CA_ACTIVE: case CA_ACTIVE:
CL_CGameRendering( stereoFrame ); // always supply STEREO_CENTER as vieworg offset is now done by the engine.
CL_CGameRendering(STEREO_CENTER);
SCR_DrawDemoRecording(); SCR_DrawDemoRecording();
break; break;
} }
@ -528,7 +529,7 @@ void SCR_UpdateScreen( void ) {
if(uivm) if(uivm)
{ {
// if running in stereo, we need to draw the frame twice // if running in stereo, we need to draw the frame twice
if ( cls.glconfig.stereoEnabled ) { if ( cls.glconfig.stereoEnabled || Cvar_VariableIntegerValue("r_anaglyphMode")) {
SCR_DrawScreenField( STEREO_LEFT ); SCR_DrawScreenField( STEREO_LEFT );
SCR_DrawScreenField( STEREO_RIGHT ); SCR_DrawScreenField( STEREO_RIGHT );
} else { } else {

View file

@ -607,14 +607,38 @@ void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) {
qglLoadMatrixf( backEnd.or.modelMatrix ); qglLoadMatrixf( backEnd.or.modelMatrix );
// //
// change depthrange if needed // change depthrange. Also change projection matrix so first person weapon does not look like coming
// out of the screen.
// //
if ( oldDepthRange != depthRange ) { if (oldDepthRange != depthRange)
if ( depthRange ) { {
if (depthRange)
{
if(backEnd.viewParms.stereoFrame != STEREO_CENTER)
{
viewParms_t temp = backEnd.viewParms;
R_SetupProjection(&temp, r_znear->value, qfalse);
qglMatrixMode(GL_PROJECTION);
qglLoadMatrixf(temp.projectionMatrix);
qglMatrixMode(GL_MODELVIEW);
}
qglDepthRange (0, 0.3); qglDepthRange (0, 0.3);
} else { }
else
{
if(backEnd.viewParms.stereoFrame != STEREO_CENTER)
{
qglMatrixMode(GL_PROJECTION);
qglLoadMatrixf(backEnd.viewParms.projectionMatrix);
qglMatrixMode(GL_MODELVIEW);
}
qglDepthRange (0, 1); qglDepthRange (0, 1);
} }
oldDepthRange = depthRange; oldDepthRange = depthRange;
} }
@ -988,6 +1012,42 @@ void RB_ShowImages( void ) {
} }
/*
=============
RB_ColorMask
=============
*/
const void *RB_ColorMask(const void *data)
{
const colorMaskCommand_t *cmd = data;
qglColorMask(cmd->rgba[0], cmd->rgba[1], cmd->rgba[2], cmd->rgba[3]);
return (const void *)(cmd + 1);
}
/*
=============
RB_ClearDepth
=============
*/
const void *RB_ClearDepth(const void *data)
{
const clearDepthCommand_t *cmd = data;
if(tess.numIndexes)
RB_EndSurface();
// texture swapping test
if (r_showImages->integer)
RB_ShowImages();
qglClear(GL_DEPTH_BUFFER_BIT);
return (const void *)(cmd + 1);
}
/* /*
============= =============
@ -1084,7 +1144,12 @@ void RB_ExecuteRenderCommands( const void *data ) {
case RC_VIDEOFRAME: case RC_VIDEOFRAME:
data = RB_TakeVideoFrameCmd( data ); data = RB_TakeVideoFrameCmd( data );
break; break;
case RC_COLORMASK:
data = RB_ColorMask(data);
break;
case RC_CLEARDEPTH:
data = RB_ClearDepth(data);
break;
case RC_END_OF_LIST: case RC_END_OF_LIST:
default: default:
// stop rendering on this thread // stop rendering on this thread

View file

@ -91,6 +91,9 @@ void R_InitCommandBuffers( void ) {
ri.Printf( PRINT_ALL, "...failed.\n" ); ri.Printf( PRINT_ALL, "...failed.\n" );
} }
} }
if(r_stereoEnabled->integer)
glConfig.stereoEnabled = qtrue;
} }
/* /*
@ -293,6 +296,38 @@ void RE_StretchPic ( float x, float y, float w, float h,
cmd->t2 = t2; cmd->t2 = t2;
} }
#define MODE_RED_CYAN 1
#define MODE_RED_BLUE 2
#define MODE_RED_GREEN 3
#define MODE_MAX MODE_RED_GREEN
void R_SetColorMode(GLboolean *rgba, stereoFrame_t stereoFrame, int colormode)
{
rgba[0] = rgba[1] = rgba[2] = rgba[3] = GL_TRUE;
if(colormode > MODE_MAX)
{
if(stereoFrame == STEREO_LEFT)
stereoFrame = STEREO_RIGHT;
else if(stereoFrame == STEREO_RIGHT)
stereoFrame = STEREO_LEFT;
colormode -= MODE_MAX;
}
if(stereoFrame == STEREO_LEFT)
rgba[1] = rgba[2] = GL_FALSE;
else if(stereoFrame == STEREO_RIGHT)
{
rgba[0] = GL_FALSE;
if(colormode == MODE_RED_BLUE)
rgba[1] = 0;
else if(colormode == MODE_RED_GREEN)
rgba[2] = 0;
}
}
/* /*
==================== ====================
@ -303,7 +338,8 @@ for each RE_EndFrame
==================== ====================
*/ */
void RE_BeginFrame( stereoFrame_t stereoFrame ) { void RE_BeginFrame( stereoFrame_t stereoFrame ) {
drawBufferCommand_t *cmd; drawBufferCommand_t *cmd = NULL;
colorMaskCommand_t *colcmd;
if ( !tr.registered ) { if ( !tr.registered ) {
return; return;
@ -371,25 +407,21 @@ void RE_BeginFrame( stereoFrame_t stereoFrame ) {
} }
// check for errors // check for errors
if ( !r_ignoreGLErrors->integer ) { if ( !r_ignoreGLErrors->integer )
{
int err; int err;
R_SyncRenderThread(); R_SyncRenderThread();
if ( ( err = qglGetError() ) != GL_NO_ERROR ) { if ((err = qglGetError()) != GL_NO_ERROR)
ri.Error( ERR_FATAL, "RE_BeginFrame() - glGetError() failed (0x%x)!\n", err ); ri.Error(ERR_FATAL, "RE_BeginFrame() - glGetError() failed (0x%x)!\n", err);
}
} }
// if (glConfig.stereoEnabled) {
// draw buffer stuff if( !(cmd = R_GetCommandBuffer(sizeof(*cmd))) )
//
cmd = R_GetCommandBuffer( sizeof( *cmd ) );
if ( !cmd ) {
return; return;
}
cmd->commandId = RC_DRAW_BUFFER; cmd->commandId = RC_DRAW_BUFFER;
if ( glConfig.stereoEnabled ) {
if ( stereoFrame == STEREO_LEFT ) { if ( stereoFrame == STEREO_LEFT ) {
cmd->buffer = (int)GL_BACK_LEFT; cmd->buffer = (int)GL_BACK_LEFT;
} else if ( stereoFrame == STEREO_RIGHT ) { } else if ( stereoFrame == STEREO_RIGHT ) {
@ -397,16 +429,79 @@ void RE_BeginFrame( stereoFrame_t stereoFrame ) {
} else { } else {
ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is enabled, but stereoFrame was %i", stereoFrame ); ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is enabled, but stereoFrame was %i", stereoFrame );
} }
} else {
if ( stereoFrame != STEREO_CENTER ) {
ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is disabled, but stereoFrame was %i", stereoFrame );
} }
if ( !Q_stricmp( r_drawBuffer->string, "GL_FRONT" ) ) { else
{
if(r_anaglyphMode->integer)
{
if(r_anaglyphMode->modified)
{
// clear both, front and backbuffer.
qglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
qglDrawBuffer(GL_FRONT);
qglClear(GL_COLOR_BUFFER_BIT);
qglDrawBuffer(GL_BACK);
qglClear(GL_COLOR_BUFFER_BIT);
r_anaglyphMode->modified = qfalse;
}
if(stereoFrame == STEREO_LEFT)
{
if( !(cmd = R_GetCommandBuffer(sizeof(*cmd))) )
return;
if( !(colcmd = R_GetCommandBuffer(sizeof(*colcmd))) )
return;
}
else if(stereoFrame == STEREO_RIGHT)
{
clearDepthCommand_t *cldcmd;
if( !(cldcmd = R_GetCommandBuffer(sizeof(*cldcmd))) )
return;
cldcmd->commandId = RC_CLEARDEPTH;
if( !(colcmd = R_GetCommandBuffer(sizeof(*colcmd))) )
return;
}
else
ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is enabled, but stereoFrame was %i", stereoFrame );
R_SetColorMode(colcmd->rgba, stereoFrame, r_anaglyphMode->integer);
colcmd->commandId = RC_COLORMASK;
}
else
{
if(stereoFrame != STEREO_CENTER)
ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is disabled, but stereoFrame was %i", stereoFrame );
if( !(cmd = R_GetCommandBuffer(sizeof(*cmd))) )
return;
}
if(cmd)
{
cmd->commandId = RC_DRAW_BUFFER;
if(stereoFrame == STEREO_CENTER && r_anaglyphMode->modified)
{
if(!r_anaglyphMode->integer)
qglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
r_anaglyphMode->modified = qfalse;
}
if (!Q_stricmp(r_drawBuffer->string, "GL_FRONT"))
cmd->buffer = (int)GL_FRONT; cmd->buffer = (int)GL_FRONT;
} else { else
cmd->buffer = (int)GL_BACK; cmd->buffer = (int)GL_BACK;
} }
} }
tr.refdef.stereoFrame = stereoFrame;
} }

View file

@ -558,7 +558,16 @@ static void Upload32( unsigned *data,
c = width*height; c = width*height;
scan = ((byte *)data); scan = ((byte *)data);
samples = 3; samples = 3;
if (!lightMap) {
if(lightMap)
{
if(r_greyscale->integer)
internalFormat = GL_LUMINANCE;
else
internalFormat = GL_RGB;
}
else
{
for ( i = 0; i < c; i++ ) for ( i = 0; i < c; i++ )
{ {
if ( scan[i*4+0] > rMax ) if ( scan[i*4+0] > rMax )
@ -581,6 +590,17 @@ static void Upload32( unsigned *data,
} }
// select proper internal format // select proper internal format
if ( samples == 3 ) if ( samples == 3 )
{
if(r_greyscale->integer)
{
if(r_texturebits->integer == 16)
internalFormat = GL_LUMINANCE8;
else if(r_texturebits->integer == 32)
internalFormat = GL_LUMINANCE16;
else
internalFormat = GL_LUMINANCE;
}
else
{ {
if ( glConfig.textureCompression == TC_S3TC ) if ( glConfig.textureCompression == TC_S3TC )
{ {
@ -596,10 +616,22 @@ static void Upload32( unsigned *data,
} }
else else
{ {
internalFormat = 3; internalFormat = GL_RGB;
}
} }
} }
else if ( samples == 4 ) else if ( samples == 4 )
{
if(r_greyscale->integer)
{
if(r_texturebits->integer == 16)
internalFormat = GL_LUMINANCE8_ALPHA8;
else if(r_texturebits->integer == 32)
internalFormat = GL_LUMINANCE16_ALPHA16;
else
internalFormat = GL_LUMINANCE_ALPHA;
}
else
{ {
if ( r_texturebits->integer == 16 ) if ( r_texturebits->integer == 16 )
{ {
@ -611,12 +643,12 @@ static void Upload32( unsigned *data,
} }
else else
{ {
internalFormat = 4; internalFormat = GL_RGBA;
} }
} }
} else {
internalFormat = 3;
} }
}
// copy or resample data as appropriate for first MIP level // copy or resample data as appropriate for first MIP level
if ( ( scaled_width == width ) && if ( ( scaled_width == width ) &&
( scaled_height == height ) ) { ( scaled_height == height ) ) {

View file

@ -50,11 +50,18 @@ cvar_t *r_displayRefresh;
cvar_t *r_detailTextures; cvar_t *r_detailTextures;
cvar_t *r_znear; cvar_t *r_znear;
cvar_t *r_zproj;
cvar_t *r_stereoSeparation;
cvar_t *r_smp; cvar_t *r_smp;
cvar_t *r_showSmp; cvar_t *r_showSmp;
cvar_t *r_skipBackEnd; cvar_t *r_skipBackEnd;
cvar_t *r_stereoEnabled;
cvar_t *r_anaglyphMode;
cvar_t *r_greyscale;
cvar_t *r_ignorehwgamma; cvar_t *r_ignorehwgamma;
cvar_t *r_measureOverdraw; cvar_t *r_measureOverdraw;
@ -93,7 +100,6 @@ cvar_t *r_logFile;
cvar_t *r_stencilbits; cvar_t *r_stencilbits;
cvar_t *r_depthbits; cvar_t *r_depthbits;
cvar_t *r_colorbits; cvar_t *r_colorbits;
cvar_t *r_stereo;
cvar_t *r_primitives; cvar_t *r_primitives;
cvar_t *r_texturebits; cvar_t *r_texturebits;
@ -916,7 +922,6 @@ void R_Register( void )
r_detailTextures = ri.Cvar_Get( "r_detailtextures", "1", CVAR_ARCHIVE | CVAR_LATCH ); r_detailTextures = ri.Cvar_Get( "r_detailtextures", "1", CVAR_ARCHIVE | CVAR_LATCH );
r_texturebits = ri.Cvar_Get( "r_texturebits", "0", CVAR_ARCHIVE | CVAR_LATCH ); r_texturebits = ri.Cvar_Get( "r_texturebits", "0", CVAR_ARCHIVE | CVAR_LATCH );
r_colorbits = ri.Cvar_Get( "r_colorbits", "0", CVAR_ARCHIVE | CVAR_LATCH ); r_colorbits = ri.Cvar_Get( "r_colorbits", "0", CVAR_ARCHIVE | CVAR_LATCH );
r_stereo = ri.Cvar_Get( "r_stereo", "0", CVAR_ARCHIVE | CVAR_LATCH );
r_stencilbits = ri.Cvar_Get( "r_stencilbits", "8", CVAR_ARCHIVE | CVAR_LATCH ); r_stencilbits = ri.Cvar_Get( "r_stencilbits", "8", CVAR_ARCHIVE | CVAR_LATCH );
r_depthbits = ri.Cvar_Get( "r_depthbits", "0", CVAR_ARCHIVE | CVAR_LATCH ); r_depthbits = ri.Cvar_Get( "r_depthbits", "0", CVAR_ARCHIVE | CVAR_LATCH );
r_overBrightBits = ri.Cvar_Get ("r_overBrightBits", "1", CVAR_ARCHIVE | CVAR_LATCH ); r_overBrightBits = ri.Cvar_Get ("r_overBrightBits", "1", CVAR_ARCHIVE | CVAR_LATCH );
@ -931,7 +936,9 @@ void R_Register( void )
r_uiFullScreen = ri.Cvar_Get( "r_uifullscreen", "0", 0); r_uiFullScreen = ri.Cvar_Get( "r_uifullscreen", "0", 0);
r_subdivisions = ri.Cvar_Get ("r_subdivisions", "4", CVAR_ARCHIVE | CVAR_LATCH); r_subdivisions = ri.Cvar_Get ("r_subdivisions", "4", CVAR_ARCHIVE | CVAR_LATCH);
r_smp = ri.Cvar_Get( "r_smp", "0", CVAR_ARCHIVE | CVAR_LATCH); r_smp = ri.Cvar_Get( "r_smp", "0", CVAR_ARCHIVE | CVAR_LATCH);
r_stereoEnabled = ri.Cvar_Get( "r_stereoEnabled", "0", CVAR_ARCHIVE | CVAR_LATCH);
r_ignoreFastPath = ri.Cvar_Get( "r_ignoreFastPath", "1", CVAR_ARCHIVE | CVAR_LATCH ); r_ignoreFastPath = ri.Cvar_Get( "r_ignoreFastPath", "1", CVAR_ARCHIVE | CVAR_LATCH );
r_greyscale = ri.Cvar_Get("r_greyscale", "0", CVAR_ARCHIVE | CVAR_LATCH);
// //
// temporary latched variables that can only change over a restart // temporary latched variables that can only change over a restart
@ -951,6 +958,8 @@ void R_Register( void )
r_flares = ri.Cvar_Get ("r_flares", "0", CVAR_ARCHIVE ); r_flares = ri.Cvar_Get ("r_flares", "0", CVAR_ARCHIVE );
r_znear = ri.Cvar_Get( "r_znear", "4", CVAR_CHEAT ); r_znear = ri.Cvar_Get( "r_znear", "4", CVAR_CHEAT );
AssertCvarRange( r_znear, 0.001f, 200, qtrue ); AssertCvarRange( r_znear, 0.001f, 200, qtrue );
r_zproj = ri.Cvar_Get( "r_zproj", "64", CVAR_ARCHIVE );
r_stereoSeparation = ri.Cvar_Get( "r_stereoSeparation", "32", CVAR_ARCHIVE );
r_ignoreGLErrors = ri.Cvar_Get( "r_ignoreGLErrors", "1", CVAR_ARCHIVE ); r_ignoreGLErrors = ri.Cvar_Get( "r_ignoreGLErrors", "1", CVAR_ARCHIVE );
r_fastsky = ri.Cvar_Get( "r_fastsky", "0", CVAR_ARCHIVE ); r_fastsky = ri.Cvar_Get( "r_fastsky", "0", CVAR_ARCHIVE );
r_inGameVideo = ri.Cvar_Get( "r_inGameVideo", "1", CVAR_ARCHIVE ); r_inGameVideo = ri.Cvar_Get( "r_inGameVideo", "1", CVAR_ARCHIVE );
@ -973,6 +982,8 @@ void R_Register( void )
r_ambientScale = ri.Cvar_Get( "r_ambientScale", "0.6", CVAR_CHEAT ); r_ambientScale = ri.Cvar_Get( "r_ambientScale", "0.6", CVAR_CHEAT );
r_directedScale = ri.Cvar_Get( "r_directedScale", "1", CVAR_CHEAT ); r_directedScale = ri.Cvar_Get( "r_directedScale", "1", CVAR_CHEAT );
r_anaglyphMode = ri.Cvar_Get("r_anaglyphMode", "0", CVAR_ARCHIVE);
// //
// temporary variables that can change at any time // temporary variables that can change at any time
// //

View file

@ -433,6 +433,8 @@ typedef struct {
vec3_t vieworg; vec3_t vieworg;
vec3_t viewaxis[3]; // transformation matrix vec3_t viewaxis[3]; // transformation matrix
stereoFrame_t stereoFrame;
int time; // time in milliseconds for shader effects and other time dependent rendering issues int time; // time in milliseconds for shader effects and other time dependent rendering issues
int rdflags; // RDF_NOWORLDMODEL, etc int rdflags; // RDF_NOWORLDMODEL, etc
@ -504,6 +506,7 @@ typedef struct {
cplane_t frustum[4]; cplane_t frustum[4];
vec3_t visBounds[2]; vec3_t visBounds[2];
float zFar; float zFar;
stereoFrame_t stereoFrame;
} viewParms_t; } viewParms_t;
@ -996,6 +999,8 @@ extern cvar_t *r_verbose; // used for verbose debug spew
extern cvar_t *r_ignoreFastPath; // allows us to ignore our Tess fast paths extern cvar_t *r_ignoreFastPath; // allows us to ignore our Tess fast paths
extern cvar_t *r_znear; // near Z clip plane extern cvar_t *r_znear; // near Z clip plane
extern cvar_t *r_zproj; // z distance of projection plane
extern cvar_t *r_stereoSeparation; // separation of cameras for stereo rendering
extern cvar_t *r_stencilbits; // number of desired stencil bits extern cvar_t *r_stencilbits; // number of desired stencil bits
extern cvar_t *r_depthbits; // number of desired depth bits extern cvar_t *r_depthbits; // number of desired depth bits
@ -1088,6 +1093,11 @@ extern cvar_t *r_smp;
extern cvar_t *r_showSmp; extern cvar_t *r_showSmp;
extern cvar_t *r_skipBackEnd; extern cvar_t *r_skipBackEnd;
extern cvar_t *r_stereoEnabled;
extern cvar_t *r_anaglyphMode;
extern cvar_t *r_greyscale;
extern cvar_t *r_ignoreGLErrors; extern cvar_t *r_ignoreGLErrors;
extern cvar_t *r_overBrightBits; extern cvar_t *r_overBrightBits;
@ -1136,6 +1146,7 @@ int R_CullLocalBox (vec3_t bounds[2]);
int R_CullPointAndRadius( vec3_t origin, float radius ); int R_CullPointAndRadius( vec3_t origin, float radius );
int R_CullLocalPointAndRadius( vec3_t origin, float radius ); int R_CullLocalPointAndRadius( vec3_t origin, float radius );
void R_SetupProjection(viewParms_t *dest, float zProj, qboolean computeFrustum);
void R_RotateForEntity( const trRefEntity_t *ent, const viewParms_t *viewParms, orientationr_t *or ); void R_RotateForEntity( const trRefEntity_t *ent, const viewParms_t *viewParms, orientationr_t *or );
/* /*
@ -1614,6 +1625,18 @@ typedef struct {
qboolean motionJpeg; qboolean motionJpeg;
} videoFrameCommand_t; } videoFrameCommand_t;
typedef struct
{
int commandId;
GLboolean rgba[4];
} colorMaskCommand_t;
typedef struct
{
int commandId;
} clearDepthCommand_t;
typedef enum { typedef enum {
RC_END_OF_LIST, RC_END_OF_LIST,
RC_SET_COLOR, RC_SET_COLOR,
@ -1622,7 +1645,9 @@ typedef enum {
RC_DRAW_BUFFER, RC_DRAW_BUFFER,
RC_SWAP_BUFFERS, RC_SWAP_BUFFERS,
RC_SCREENSHOT, RC_SCREENSHOT,
RC_VIDEOFRAME RC_VIDEOFRAME,
RC_COLORMASK,
RC_CLEARDEPTH
} renderCommand_t; } renderCommand_t;

View file

@ -381,7 +381,7 @@ void R_RotateForViewer (void)
/* /*
** SetFarClip ** SetFarClip
*/ */
static void SetFarClip( void ) static void R_SetFarClip( void )
{ {
float farthestCornerDistance = 0; float farthestCornerDistance = 0;
int i; int i;
@ -442,97 +442,141 @@ static void SetFarClip( void )
tr.viewParms.zFar = sqrt( farthestCornerDistance ); tr.viewParms.zFar = sqrt( farthestCornerDistance );
} }
/*
=================
R_SetupFrustum
Set up the culling frustum planes for the current view using the results we got from computing the first two rows of
the projection matrix.
=================
*/
void R_SetupFrustum (viewParms_t *dest, float xmin, float xmax, float ymax, float zProj, float stereoSep)
{
vec3_t ofsorigin;
float oppleg, adjleg, length;
int i;
if(stereoSep == 0)
{
// symmetric case can be simplified
VectorCopy(dest->or.origin, ofsorigin);
length = sqrt(xmax * xmax + zProj * zProj);
oppleg = xmax / length;
adjleg = zProj / length;
VectorScale(dest->or.axis[0], oppleg, dest->frustum[0].normal);
VectorMA(dest->frustum[0].normal, adjleg, dest->or.axis[1], dest->frustum[0].normal);
VectorScale(dest->or.axis[0], oppleg, dest->frustum[1].normal);
VectorMA(dest->frustum[1].normal, -adjleg, dest->or.axis[1], dest->frustum[1].normal);
}
else
{
// In stereo rendering, due to the modification of the projection matrix, dest->or.origin is not the
// actual origin that we're rendering so offset the tip of the view pyramid.
VectorMA(dest->or.origin, stereoSep, dest->or.axis[1], ofsorigin);
oppleg = xmax + stereoSep;
length = sqrt(oppleg * oppleg + zProj * zProj);
VectorScale(dest->or.axis[0], oppleg / length, dest->frustum[0].normal);
VectorMA(dest->frustum[0].normal, zProj / length, dest->or.axis[1], dest->frustum[0].normal);
oppleg = xmin + stereoSep;
length = sqrt(oppleg * oppleg + zProj * zProj);
VectorScale(dest->or.axis[0], -oppleg / length, dest->frustum[1].normal);
VectorMA(dest->frustum[1].normal, -zProj / length, dest->or.axis[1], dest->frustum[1].normal);
}
length = sqrt(ymax * ymax + zProj * zProj);
oppleg = ymax / length;
adjleg = zProj / length;
VectorScale(dest->or.axis[0], oppleg, dest->frustum[2].normal);
VectorMA(dest->frustum[2].normal, adjleg, dest->or.axis[2], dest->frustum[2].normal);
VectorScale(dest->or.axis[0], oppleg, dest->frustum[3].normal);
VectorMA(dest->frustum[3].normal, -adjleg, dest->or.axis[2], dest->frustum[3].normal);
for (i=0 ; i<4 ; i++) {
dest->frustum[i].type = PLANE_NON_AXIAL;
dest->frustum[i].dist = DotProduct (ofsorigin, dest->frustum[i].normal);
SetPlaneSignbits( &dest->frustum[i] );
}
}
/* /*
=============== ===============
R_SetupProjection R_SetupProjection
=============== ===============
*/ */
void R_SetupProjection( void ) { void R_SetupProjection(viewParms_t *dest, float zProj, qboolean computeFrustum)
{
float xmin, xmax, ymin, ymax; float xmin, xmax, ymin, ymax;
float width, height, depth; float width, height, stereoSep;
float zNear, zFar;
// dynamically compute far clip plane distance /*
SetFarClip(); * offset the view origin of the viewer for stereo rendering
* by setting the projection matrix appropriately.
*/
// if(dest->stereoFrame == STEREO_LEFT)
// set up projection matrix stereoSep = zProj / r_stereoSeparation->value;
// else if(dest->stereoFrame == STEREO_RIGHT)
zNear = r_znear->value; stereoSep = zProj / -r_stereoSeparation->value;
zFar = tr.viewParms.zFar; else
stereoSep = 0;
ymax = zNear * tan( tr.refdef.fov_y * M_PI / 360.0f ); ymax = zProj * tan(dest->fovY * M_PI / 360.0f);
ymin = -ymax; ymin = -ymax;
xmax = zNear * tan( tr.refdef.fov_x * M_PI / 360.0f ); xmax = zProj * tan(dest->fovX * M_PI / 360.0f);
xmin = -xmax; xmin = -xmax;
width = xmax - xmin; width = xmax - xmin;
height = ymax - ymin; height = ymax - ymin;
depth = zFar - zNear;
tr.viewParms.projectionMatrix[0] = 2 * zNear / width; dest->projectionMatrix[0] = 2 * zProj / width;
tr.viewParms.projectionMatrix[4] = 0; dest->projectionMatrix[4] = 0;
tr.viewParms.projectionMatrix[8] = ( xmax + xmin ) / width; // normally 0 dest->projectionMatrix[8] = (xmax + xmin + 2 * stereoSep) / width;
tr.viewParms.projectionMatrix[12] = 0; dest->projectionMatrix[12] = 2 * zProj * stereoSep / width;
tr.viewParms.projectionMatrix[1] = 0; dest->projectionMatrix[1] = 0;
tr.viewParms.projectionMatrix[5] = 2 * zNear / height; dest->projectionMatrix[5] = 2 * zProj / height;
tr.viewParms.projectionMatrix[9] = ( ymax + ymin ) / height; // normally 0 dest->projectionMatrix[9] = ( ymax + ymin ) / height; // normally 0
tr.viewParms.projectionMatrix[13] = 0; dest->projectionMatrix[13] = 0;
tr.viewParms.projectionMatrix[2] = 0; dest->projectionMatrix[3] = 0;
tr.viewParms.projectionMatrix[6] = 0; dest->projectionMatrix[7] = 0;
tr.viewParms.projectionMatrix[10] = -( zFar + zNear ) / depth; dest->projectionMatrix[11] = -1;
tr.viewParms.projectionMatrix[14] = -2 * zFar * zNear / depth; dest->projectionMatrix[15] = 0;
tr.viewParms.projectionMatrix[3] = 0; // Now that we have all the data for the projection matrix we can also setup the view frustum.
tr.viewParms.projectionMatrix[7] = 0; if(computeFrustum)
tr.viewParms.projectionMatrix[11] = -1; R_SetupFrustum(dest, xmin, xmax, ymax, zProj, stereoSep);
tr.viewParms.projectionMatrix[15] = 0;
} }
/* /*
================= ===============
R_SetupFrustum R_SetupProjectionZ
Setup that culling frustum planes for the current view Sets the z-component transformation part in the projection matrix
================= ===============
*/ */
void R_SetupFrustum (void) { void R_SetupProjectionZ(viewParms_t *dest)
int i; {
float xs, xc; float zNear, zFar, depth;
float ang;
ang = tr.viewParms.fovX / 180 * M_PI * 0.5f; zNear = r_znear->value;
xs = sin( ang ); zFar = dest->zFar;
xc = cos( ang ); depth = zFar - zNear;
VectorScale( tr.viewParms.or.axis[0], xs, tr.viewParms.frustum[0].normal ); dest->projectionMatrix[2] = 0;
VectorMA( tr.viewParms.frustum[0].normal, xc, tr.viewParms.or.axis[1], tr.viewParms.frustum[0].normal ); dest->projectionMatrix[6] = 0;
dest->projectionMatrix[10] = -( zFar + zNear ) / depth;
VectorScale( tr.viewParms.or.axis[0], xs, tr.viewParms.frustum[1].normal ); dest->projectionMatrix[14] = -2 * zFar * zNear / depth;
VectorMA( tr.viewParms.frustum[1].normal, -xc, tr.viewParms.or.axis[1], tr.viewParms.frustum[1].normal );
ang = tr.viewParms.fovY / 180 * M_PI * 0.5f;
xs = sin( ang );
xc = cos( ang );
VectorScale( tr.viewParms.or.axis[0], xs, tr.viewParms.frustum[2].normal );
VectorMA( tr.viewParms.frustum[2].normal, xc, tr.viewParms.or.axis[2], tr.viewParms.frustum[2].normal );
VectorScale( tr.viewParms.or.axis[0], xs, tr.viewParms.frustum[3].normal );
VectorMA( tr.viewParms.frustum[3].normal, -xc, tr.viewParms.or.axis[2], tr.viewParms.frustum[3].normal );
for (i=0 ; i<4 ; i++) {
tr.viewParms.frustum[i].type = PLANE_NON_AXIAL;
tr.viewParms.frustum[i].dist = DotProduct (tr.viewParms.or.origin, tr.viewParms.frustum[i].normal);
SetPlaneSignbits( &tr.viewParms.frustum[i] );
}
} }
/* /*
================= =================
R_MirrorPoint R_MirrorPoint
@ -1251,7 +1295,12 @@ void R_GenerateDrawSurfs( void ) {
// this needs to be done before entities are // this needs to be done before entities are
// added, because they use the projection // added, because they use the projection
// matrix for lod calculation // matrix for lod calculation
R_SetupProjection ();
// dynamically compute far clip plane distance
R_SetFarClip();
// we know the size of the clipping volume. Now set the rest of the projection matrix.
R_SetupProjectionZ (&tr.viewParms);
R_AddEntitySurfaces (); R_AddEntitySurfaces ();
} }
@ -1336,7 +1385,7 @@ void R_RenderView (viewParms_t *parms) {
// set viewParms.world // set viewParms.world
R_RotateForViewer (); R_RotateForViewer ();
R_SetupFrustum (); R_SetupProjection(&tr.viewParms, r_zproj->value, qtrue);
R_GenerateDrawSurfs(); R_GenerateDrawSurfs();

View file

@ -390,6 +390,8 @@ void RE_RenderScene( const refdef_t *fd ) {
parms.fovX = tr.refdef.fov_x; parms.fovX = tr.refdef.fov_x;
parms.fovY = tr.refdef.fov_y; parms.fovY = tr.refdef.fov_y;
parms.stereoFrame = tr.refdef.stereoFrame;
VectorCopy( fd->vieworg, parms.or.origin ); VectorCopy( fd->vieworg, parms.or.origin );
VectorCopy( fd->viewaxis[0], parms.or.axis[0] ); VectorCopy( fd->viewaxis[0], parms.or.axis[0] );
VectorCopy( fd->viewaxis[1], parms.or.axis[1] ); VectorCopy( fd->viewaxis[1], parms.or.axis[1] );

View file

@ -938,6 +938,18 @@ static void ComputeColors( shaderStage_t *pStage )
break; break;
} }
} }
// if in greyscale rendering mode turn all color values into greyscale.
if(r_greyscale->integer)
{
int scale;
for(i = 0; i < tess.numVertexes; i++)
{
scale = (tess.svars.colors[i][0] + tess.svars.colors[i][1] + tess.svars.colors[i][2]) / 3;
tess.svars.colors[i][0] = tess.svars.colors[i][1] = tess.svars.colors[i][2] = scale;
}
}
} }
/* /*

View file

@ -155,6 +155,7 @@ void RB_ShadowTessEnd( void ) {
int i; int i;
int numTris; int numTris;
vec3_t lightDir; vec3_t lightDir;
GLboolean rgba[4];
// we can only do this if we have enough space in the vertex buffers // we can only do this if we have enough space in the vertex buffers
if ( tess.numVertexes >= SHADER_MAX_VERTEXES / 2 ) { if ( tess.numVertexes >= SHADER_MAX_VERTEXES / 2 ) {
@ -215,6 +216,7 @@ void RB_ShadowTessEnd( void ) {
qglColor3f( 0.2f, 0.2f, 0.2f ); qglColor3f( 0.2f, 0.2f, 0.2f );
// don't write to the color buffer // don't write to the color buffer
qglGetBooleanv(GL_COLOR_WRITEMASK, rgba);
qglColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE ); qglColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE );
qglEnable( GL_STENCIL_TEST ); qglEnable( GL_STENCIL_TEST );
@ -245,7 +247,7 @@ void RB_ShadowTessEnd( void ) {
// reenable writing to the color buffer // reenable writing to the color buffer
qglColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE ); qglColorMask(rgba[0], rgba[1], rgba[2], rgba[3]);
} }