From 425c351d1bc1d4d2db9317d6a2ecfcfa220247d2 Mon Sep 17 00:00:00 2001 From: Thilo Schulz Date: Sun, 27 Apr 2008 17:32:14 +0000 Subject: [PATCH] - Implement stereo rendering with anaglyph images. - Add r_greyscale for black&white rendering --- Makefile | 1 - README | 16 +++- code/client/cl_scrn.c | 7 +- code/renderer/tr_backend.c | 75 ++++++++++++++- code/renderer/tr_cmds.c | 143 +++++++++++++++++++++++----- code/renderer/tr_image.c | 74 ++++++++++----- code/renderer/tr_init.c | 15 ++- code/renderer/tr_local.h | 27 +++++- code/renderer/tr_main.c | 187 +++++++++++++++++++++++-------------- code/renderer/tr_scene.c | 2 + code/renderer/tr_shade.c | 12 +++ code/renderer/tr_shadows.c | 4 +- 12 files changed, 435 insertions(+), 128 deletions(-) diff --git a/Makefile b/Makefile index 417d3212..661912e7 100644 --- a/Makefile +++ b/Makefile @@ -155,7 +155,6 @@ LIBSDIR=$(MOUNT_DIR)/libs TEMPDIR=/tmp # extract version info -# echo $(BUILD_CLIENT) ifeq ($(BUILD_STANDALONE),1) VERSION=$(shell grep "\#define *PRODUCT_VERSION" $(CMDIR)/q_shared.h | head -n 1 | \ diff --git a/README b/README index affde4ca..bb9a5692 100644 --- a/README +++ b/README @@ -154,7 +154,21 @@ New cvars net_mcast6addr - multicast address to use for scanning for ipv6 servers on the local network 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 video [filename] - start video capture (use with demo command) diff --git a/code/client/cl_scrn.c b/code/client/cl_scrn.c index d1594848..3172f3da 100644 --- a/code/client/cl_scrn.c +++ b/code/client/cl_scrn.c @@ -474,7 +474,7 @@ void SCR_DrawScreenField( stereoFrame_t stereoFrame ) { case CA_LOADING: case CA_PRIMED: // draw the game information screen and loading progress - CL_CGameRendering( stereoFrame ); + CL_CGameRendering(STEREO_CENTER); // also draw the connection information, so it doesn't // 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 ); break; 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(); break; } @@ -528,7 +529,7 @@ void SCR_UpdateScreen( void ) { if(uivm) { // 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_RIGHT ); } else { diff --git a/code/renderer/tr_backend.c b/code/renderer/tr_backend.c index 2061dd8a..904c7d44 100644 --- a/code/renderer/tr_backend.c +++ b/code/renderer/tr_backend.c @@ -607,14 +607,38 @@ void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) { 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 ( depthRange ) { + if (oldDepthRange != 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); - } else { + } + else + { + if(backEnd.viewParms.stereoFrame != STEREO_CENTER) + { + qglMatrixMode(GL_PROJECTION); + qglLoadMatrixf(backEnd.viewParms.projectionMatrix); + qglMatrixMode(GL_MODELVIEW); + } + qglDepthRange (0, 1); } + 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: data = RB_TakeVideoFrameCmd( data ); break; - + case RC_COLORMASK: + data = RB_ColorMask(data); + break; + case RC_CLEARDEPTH: + data = RB_ClearDepth(data); + break; case RC_END_OF_LIST: default: // stop rendering on this thread diff --git a/code/renderer/tr_cmds.c b/code/renderer/tr_cmds.c index 318a1966..5d6011eb 100644 --- a/code/renderer/tr_cmds.c +++ b/code/renderer/tr_cmds.c @@ -91,6 +91,9 @@ void R_InitCommandBuffers( void ) { 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; } +#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 ) { - drawBufferCommand_t *cmd; + drawBufferCommand_t *cmd = NULL; + colorMaskCommand_t *colcmd; if ( !tr.registered ) { return; @@ -370,26 +406,22 @@ void RE_BeginFrame( stereoFrame_t stereoFrame ) { R_SetColorMappings(); } - // check for errors - if ( !r_ignoreGLErrors->integer ) { - int err; + // check for errors + if ( !r_ignoreGLErrors->integer ) + { + int err; R_SyncRenderThread(); - if ( ( err = qglGetError() ) != GL_NO_ERROR ) { - ri.Error( ERR_FATAL, "RE_BeginFrame() - glGetError() failed (0x%x)!\n", err ); - } - } - - // - // draw buffer stuff - // - cmd = R_GetCommandBuffer( sizeof( *cmd ) ); - if ( !cmd ) { - return; + if ((err = qglGetError()) != GL_NO_ERROR) + ri.Error(ERR_FATAL, "RE_BeginFrame() - glGetError() failed (0x%x)!\n", err); } - cmd->commandId = RC_DRAW_BUFFER; - if ( glConfig.stereoEnabled ) { + if (glConfig.stereoEnabled) { + if( !(cmd = R_GetCommandBuffer(sizeof(*cmd))) ) + return; + + cmd->commandId = RC_DRAW_BUFFER; + if ( stereoFrame == STEREO_LEFT ) { cmd->buffer = (int)GL_BACK_LEFT; } else if ( stereoFrame == STEREO_RIGHT ) { @@ -397,16 +429,79 @@ void RE_BeginFrame( stereoFrame_t stereoFrame ) { } else { 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 ); + } + 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; } - if ( !Q_stricmp( r_drawBuffer->string, "GL_FRONT" ) ) { - cmd->buffer = (int)GL_FRONT; - } else { - cmd->buffer = (int)GL_BACK; + 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; + else + cmd->buffer = (int)GL_BACK; } } + + tr.refdef.stereoFrame = stereoFrame; } diff --git a/code/renderer/tr_image.c b/code/renderer/tr_image.c index 339d91c1..737370f5 100644 --- a/code/renderer/tr_image.c +++ b/code/renderer/tr_image.c @@ -558,7 +558,16 @@ static void Upload32( unsigned *data, c = width*height; scan = ((byte *)data); samples = 3; - if (!lightMap) { + + if(lightMap) + { + if(r_greyscale->integer) + internalFormat = GL_LUMINANCE; + else + internalFormat = GL_RGB; + } + else + { for ( i = 0; i < c; i++ ) { if ( scan[i*4+0] > rMax ) @@ -582,41 +591,64 @@ static void Upload32( unsigned *data, // select proper internal format if ( samples == 3 ) { - if ( glConfig.textureCompression == TC_S3TC ) + if(r_greyscale->integer) { - internalFormat = GL_RGB4_S3TC; - } - else if ( r_texturebits->integer == 16 ) - { - internalFormat = GL_RGB5; - } - else if ( r_texturebits->integer == 32 ) - { - internalFormat = GL_RGB8; + if(r_texturebits->integer == 16) + internalFormat = GL_LUMINANCE8; + else if(r_texturebits->integer == 32) + internalFormat = GL_LUMINANCE16; + else + internalFormat = GL_LUMINANCE; } else { - internalFormat = 3; + if ( glConfig.textureCompression == TC_S3TC ) + { + internalFormat = GL_RGB4_S3TC; + } + else if ( r_texturebits->integer == 16 ) + { + internalFormat = GL_RGB5; + } + else if ( r_texturebits->integer == 32 ) + { + internalFormat = GL_RGB8; + } + else + { + internalFormat = GL_RGB; + } } } else if ( samples == 4 ) { - if ( r_texturebits->integer == 16 ) + if(r_greyscale->integer) { - internalFormat = GL_RGBA4; - } - else if ( r_texturebits->integer == 32 ) - { - internalFormat = GL_RGBA8; + 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 { - internalFormat = 4; + if ( r_texturebits->integer == 16 ) + { + internalFormat = GL_RGBA4; + } + else if ( r_texturebits->integer == 32 ) + { + internalFormat = GL_RGBA8; + } + else + { + internalFormat = GL_RGBA; + } } } - } else { - internalFormat = 3; } + // copy or resample data as appropriate for first MIP level if ( ( scaled_width == width ) && ( scaled_height == height ) ) { diff --git a/code/renderer/tr_init.c b/code/renderer/tr_init.c index 4618917d..0fe686a9 100644 --- a/code/renderer/tr_init.c +++ b/code/renderer/tr_init.c @@ -50,11 +50,18 @@ cvar_t *r_displayRefresh; cvar_t *r_detailTextures; cvar_t *r_znear; +cvar_t *r_zproj; +cvar_t *r_stereoSeparation; cvar_t *r_smp; cvar_t *r_showSmp; cvar_t *r_skipBackEnd; +cvar_t *r_stereoEnabled; +cvar_t *r_anaglyphMode; + +cvar_t *r_greyscale; + cvar_t *r_ignorehwgamma; cvar_t *r_measureOverdraw; @@ -93,7 +100,6 @@ cvar_t *r_logFile; cvar_t *r_stencilbits; cvar_t *r_depthbits; cvar_t *r_colorbits; -cvar_t *r_stereo; cvar_t *r_primitives; 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_texturebits = ri.Cvar_Get( "r_texturebits", "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_depthbits = ri.Cvar_Get( "r_depthbits", "0", 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_subdivisions = ri.Cvar_Get ("r_subdivisions", "4", 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_greyscale = ri.Cvar_Get("r_greyscale", "0", CVAR_ARCHIVE | CVAR_LATCH); // // 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_znear = ri.Cvar_Get( "r_znear", "4", CVAR_CHEAT ); 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_fastsky = ri.Cvar_Get( "r_fastsky", "0", 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_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 // diff --git a/code/renderer/tr_local.h b/code/renderer/tr_local.h index ec5ff910..d55c4d21 100644 --- a/code/renderer/tr_local.h +++ b/code/renderer/tr_local.h @@ -433,6 +433,8 @@ typedef struct { vec3_t vieworg; vec3_t viewaxis[3]; // transformation matrix + stereoFrame_t stereoFrame; + int time; // time in milliseconds for shader effects and other time dependent rendering issues int rdflags; // RDF_NOWORLDMODEL, etc @@ -504,6 +506,7 @@ typedef struct { cplane_t frustum[4]; vec3_t visBounds[2]; float zFar; + stereoFrame_t stereoFrame; } 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_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_depthbits; // number of desired depth bits @@ -1088,6 +1093,11 @@ extern cvar_t *r_smp; extern cvar_t *r_showSmp; 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_overBrightBits; @@ -1136,6 +1146,7 @@ int R_CullLocalBox (vec3_t bounds[2]); int R_CullPointAndRadius( 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 ); /* @@ -1614,6 +1625,18 @@ typedef struct { qboolean motionJpeg; } videoFrameCommand_t; +typedef struct +{ + int commandId; + + GLboolean rgba[4]; +} colorMaskCommand_t; + +typedef struct +{ + int commandId; +} clearDepthCommand_t; + typedef enum { RC_END_OF_LIST, RC_SET_COLOR, @@ -1622,7 +1645,9 @@ typedef enum { RC_DRAW_BUFFER, RC_SWAP_BUFFERS, RC_SCREENSHOT, - RC_VIDEOFRAME + RC_VIDEOFRAME, + RC_COLORMASK, + RC_CLEARDEPTH } renderCommand_t; diff --git a/code/renderer/tr_main.c b/code/renderer/tr_main.c index d45492cc..c5996bae 100644 --- a/code/renderer/tr_main.c +++ b/code/renderer/tr_main.c @@ -381,7 +381,7 @@ void R_RotateForViewer (void) /* ** SetFarClip */ -static void SetFarClip( void ) +static void R_SetFarClip( void ) { float farthestCornerDistance = 0; int i; @@ -442,97 +442,141 @@ static void SetFarClip( void ) 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 =============== */ -void R_SetupProjection( void ) { +void R_SetupProjection(viewParms_t *dest, float zProj, qboolean computeFrustum) +{ float xmin, xmax, ymin, ymax; - float width, height, depth; - float zNear, zFar; + float width, height, stereoSep; - // 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) + stereoSep = zProj / r_stereoSeparation->value; + else if(dest->stereoFrame == STEREO_RIGHT) + stereoSep = zProj / -r_stereoSeparation->value; + else + stereoSep = 0; - // - // set up projection matrix - // - zNear = r_znear->value; - zFar = tr.viewParms.zFar; - - ymax = zNear * tan( tr.refdef.fov_y * M_PI / 360.0f ); + ymax = zProj * tan(dest->fovY * M_PI / 360.0f); ymin = -ymax; - xmax = zNear * tan( tr.refdef.fov_x * M_PI / 360.0f ); + xmax = zProj * tan(dest->fovX * M_PI / 360.0f); xmin = -xmax; width = xmax - xmin; height = ymax - ymin; - depth = zFar - zNear; + + dest->projectionMatrix[0] = 2 * zProj / width; + dest->projectionMatrix[4] = 0; + dest->projectionMatrix[8] = (xmax + xmin + 2 * stereoSep) / width; + dest->projectionMatrix[12] = 2 * zProj * stereoSep / width; - tr.viewParms.projectionMatrix[0] = 2 * zNear / width; - tr.viewParms.projectionMatrix[4] = 0; - tr.viewParms.projectionMatrix[8] = ( xmax + xmin ) / width; // normally 0 - tr.viewParms.projectionMatrix[12] = 0; + dest->projectionMatrix[1] = 0; + dest->projectionMatrix[5] = 2 * zProj / height; + dest->projectionMatrix[9] = ( ymax + ymin ) / height; // normally 0 + dest->projectionMatrix[13] = 0; - tr.viewParms.projectionMatrix[1] = 0; - tr.viewParms.projectionMatrix[5] = 2 * zNear / height; - tr.viewParms.projectionMatrix[9] = ( ymax + ymin ) / height; // normally 0 - tr.viewParms.projectionMatrix[13] = 0; - - tr.viewParms.projectionMatrix[2] = 0; - tr.viewParms.projectionMatrix[6] = 0; - tr.viewParms.projectionMatrix[10] = -( zFar + zNear ) / depth; - tr.viewParms.projectionMatrix[14] = -2 * zFar * zNear / depth; - - tr.viewParms.projectionMatrix[3] = 0; - tr.viewParms.projectionMatrix[7] = 0; - tr.viewParms.projectionMatrix[11] = -1; - tr.viewParms.projectionMatrix[15] = 0; + dest->projectionMatrix[3] = 0; + dest->projectionMatrix[7] = 0; + dest->projectionMatrix[11] = -1; + dest->projectionMatrix[15] = 0; + + // Now that we have all the data for the projection matrix we can also setup the view frustum. + if(computeFrustum) + R_SetupFrustum(dest, xmin, xmax, ymax, zProj, stereoSep); } /* -================= -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) { - int i; - float xs, xc; - float ang; +void R_SetupProjectionZ(viewParms_t *dest) +{ + float zNear, zFar, depth; + + zNear = r_znear->value; + zFar = dest->zFar; + depth = zFar - zNear; - ang = tr.viewParms.fovX / 180 * M_PI * 0.5f; - xs = sin( ang ); - xc = cos( ang ); - - VectorScale( tr.viewParms.or.axis[0], xs, tr.viewParms.frustum[0].normal ); - VectorMA( tr.viewParms.frustum[0].normal, xc, tr.viewParms.or.axis[1], tr.viewParms.frustum[0].normal ); - - VectorScale( tr.viewParms.or.axis[0], xs, tr.viewParms.frustum[1].normal ); - 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] ); - } + dest->projectionMatrix[2] = 0; + dest->projectionMatrix[6] = 0; + dest->projectionMatrix[10] = -( zFar + zNear ) / depth; + dest->projectionMatrix[14] = -2 * zFar * zNear / depth; } - /* ================= R_MirrorPoint @@ -1251,7 +1295,12 @@ void R_GenerateDrawSurfs( void ) { // this needs to be done before entities are // added, because they use the projection // 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 (); } @@ -1336,7 +1385,7 @@ void R_RenderView (viewParms_t *parms) { // set viewParms.world R_RotateForViewer (); - R_SetupFrustum (); + R_SetupProjection(&tr.viewParms, r_zproj->value, qtrue); R_GenerateDrawSurfs(); diff --git a/code/renderer/tr_scene.c b/code/renderer/tr_scene.c index 515a629b..3c5083b7 100644 --- a/code/renderer/tr_scene.c +++ b/code/renderer/tr_scene.c @@ -389,6 +389,8 @@ void RE_RenderScene( const refdef_t *fd ) { parms.fovX = tr.refdef.fov_x; parms.fovY = tr.refdef.fov_y; + + parms.stereoFrame = tr.refdef.stereoFrame; VectorCopy( fd->vieworg, parms.or.origin ); VectorCopy( fd->viewaxis[0], parms.or.axis[0] ); diff --git a/code/renderer/tr_shade.c b/code/renderer/tr_shade.c index 6f22b7c2..8eb60a6e 100644 --- a/code/renderer/tr_shade.c +++ b/code/renderer/tr_shade.c @@ -938,6 +938,18 @@ static void ComputeColors( shaderStage_t *pStage ) 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; + } + } } /* diff --git a/code/renderer/tr_shadows.c b/code/renderer/tr_shadows.c index 1162c8fd..f412b00d 100644 --- a/code/renderer/tr_shadows.c +++ b/code/renderer/tr_shadows.c @@ -155,6 +155,7 @@ void RB_ShadowTessEnd( void ) { int i; int numTris; vec3_t lightDir; + GLboolean rgba[4]; // we can only do this if we have enough space in the vertex buffers if ( tess.numVertexes >= SHADER_MAX_VERTEXES / 2 ) { @@ -215,6 +216,7 @@ void RB_ShadowTessEnd( void ) { qglColor3f( 0.2f, 0.2f, 0.2f ); // don't write to the color buffer + qglGetBooleanv(GL_COLOR_WRITEMASK, rgba); qglColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE ); qglEnable( GL_STENCIL_TEST ); @@ -245,7 +247,7 @@ void RB_ShadowTessEnd( void ) { // reenable writing to the color buffer - qglColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE ); + qglColorMask(rgba[0], rgba[1], rgba[2], rgba[3]); }