aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorthilo <thilo@edf5b092-35ff-0310-97b2-ce42778d08ea>2008-04-27 17:32:14 +0000
committerthilo <thilo@edf5b092-35ff-0310-97b2-ce42778d08ea>2008-04-27 17:32:14 +0000
commita1757f0abe8b9780d4c85415bc0fc9af1af31ff2 (patch)
treea84aba4e58244abb55e324cda0fdd04774ce1cca
parent7eec87380d6b4bbf0ffa4f8ea258b8853c0428bc (diff)
downloadioquake3-aero-a1757f0abe8b9780d4c85415bc0fc9af1af31ff2.tar.gz
ioquake3-aero-a1757f0abe8b9780d4c85415bc0fc9af1af31ff2.zip
- Implement stereo rendering with anaglyph images.
- Add r_greyscale for black&white rendering git-svn-id: svn://svn.icculus.org/quake3/trunk@1328 edf5b092-35ff-0310-97b2-ce42778d08ea
-rw-r--r--Makefile1
-rw-r--r--README16
-rw-r--r--code/client/cl_scrn.c7
-rw-r--r--code/renderer/tr_backend.c75
-rw-r--r--code/renderer/tr_cmds.c143
-rw-r--r--code/renderer/tr_image.c74
-rw-r--r--code/renderer/tr_init.c15
-rw-r--r--code/renderer/tr_local.h27
-rw-r--r--code/renderer/tr_main.c195
-rw-r--r--code/renderer/tr_scene.c2
-rw-r--r--code/renderer/tr_shade.c12
-rw-r--r--code/renderer/tr_shadows.c4
12 files changed, 439 insertions, 132 deletions
diff --git a/Makefile b/Makefile
index 417d321..661912e 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 affde4c..bb9a569 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 d159484..3172f3d 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 2061dd8..904c7d4 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 318a196..5d6011e 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 339d91c..737370f 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 )
- {
- internalFormat = GL_RGBA4;
- }
- else if ( r_texturebits->integer == 32 )
+ if(r_greyscale->integer)
{
- 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 4618917..0fe686a 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 ec5ff91..d55c4d2 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 d45492c..c5996ba 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;
-
- // dynamically compute far clip plane distance
- SetFarClip();
-
- //
- // set up projection matrix
- //
- zNear = r_znear->value;
- zFar = tr.viewParms.zFar;
-
- ymax = zNear * tan( tr.refdef.fov_y * M_PI / 360.0f );
+ float width, height, stereoSep;
+
+ /*
+ * 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;
+
+ 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;
-
- 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;
-
- 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[0] = 2 * zProj / width;
+ dest->projectionMatrix[4] = 0;
+ dest->projectionMatrix[8] = (xmax + xmin + 2 * stereoSep) / width;
+ dest->projectionMatrix[12] = 2 * zProj * stereoSep / width;
+
+ dest->projectionMatrix[1] = 0;
+ dest->projectionMatrix[5] = 2 * zProj / height;
+ dest->projectionMatrix[9] = ( ymax + ymin ) / height; // normally 0
+ dest->projectionMatrix[13] = 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;
-
- 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 );
+void R_SetupProjectionZ(viewParms_t *dest)
+{
+ float zNear, zFar, depth;
+
+ zNear = r_znear->value;
+ zFar = dest->zFar;
+ depth = zFar - zNear;
- 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 515a629..3c5083b 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 6f22b7c..8eb60a6 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 1162c8f..f412b00 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]);
}