This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/engine/client/gl_backend.c

736 lines
16 KiB
C
Raw Normal View History

2010-11-28 22:00:00 +01:00
//=======================================================================
// Copyright XashXT Group 2010 <20>
// gl_backend.c - rendering backend
//=======================================================================
#include "common.h"
#include "client.h"
#include "gl_local.h"
2010-12-22 22:00:00 +01:00
#include "mathlib.h"
2010-12-06 22:00:00 +01:00
char r_speeds_msg[MAX_SYSPATH];
ref_speeds_t r_stats; // r_speeds counters
/*
===============
R_SpeedsMessage
===============
*/
qboolean R_SpeedsMessage( char *out, size_t size )
{
if( r_speeds->integer <= 0 ) return false;
if( !out || !size ) return false;
com.strncpy( out, r_speeds_msg, size );
return true;
}
/*
==============
GL_BackendStartFrame
==============
*/
void GL_BackendStartFrame( void )
{
r_speeds_msg[0] = '\0';
}
/*
==============
GL_BackendEndFrame
==============
*/
void GL_BackendEndFrame( void )
{
2010-12-17 22:00:00 +01:00
if( r_speeds->integer <= 0 || !RI.drawWorld )
return;
switch( r_speeds->integer )
{
case 5:
com.snprintf( r_speeds_msg, sizeof( r_speeds_msg ), "%3i studio models drawn\n%3i sprites drawn",
r_stats.c_studio_models_drawn, r_stats.c_sprite_models_drawn );
break;
case 6:
com.snprintf( r_speeds_msg, sizeof( r_speeds_msg ), "%3i static entities\n%3i normal entities",
r_numStatics, r_numEntities );
break;
2011-01-03 22:00:00 +01:00
case 7:
com.snprintf( r_speeds_msg, sizeof( r_speeds_msg ), "%3i temp entities\n%3i view beams",
r_stats.c_active_tents_count, r_stats.c_view_beams_count );
2010-12-17 22:00:00 +01:00
}
2011-01-03 22:00:00 +01:00
Mem_Set( &r_stats, 0, sizeof( r_stats ));
2010-12-06 22:00:00 +01:00
}
/*
=================
GL_LoadTexMatrix
=================
*/
void GL_LoadTexMatrix( const matrix4x4 m )
{
pglMatrixMode( GL_TEXTURE );
GL_LoadMatrix( m );
glState.texIdentityMatrix[glState.activeTMU] = false;
}
/*
=================
GL_LoadMatrix
=================
*/
void GL_LoadMatrix( const matrix4x4 source )
{
GLfloat dest[16];
Matrix4x4_ToArrayFloatGL( source, dest );
pglLoadMatrixf( dest );
}
/*
=================
GL_LoadIdentityTexMatrix
=================
*/
void GL_LoadIdentityTexMatrix( void )
{
if( glState.texIdentityMatrix[glState.activeTMU] )
return;
pglMatrixMode( GL_TEXTURE );
pglLoadIdentity();
glState.texIdentityMatrix[glState.activeTMU] = true;
}
2010-11-28 22:00:00 +01:00
/*
=================
GL_SelectTexture
=================
*/
void GL_SelectTexture( GLenum texture )
{
if( !GL_Support( GL_ARB_MULTITEXTURE ))
return;
if( glState.activeTMU == texture )
return;
glState.activeTMU = texture;
if( pglActiveTextureARB )
{
pglActiveTextureARB( texture + GL_TEXTURE0_ARB );
pglClientActiveTextureARB( texture + GL_TEXTURE0_ARB );
}
else if( pglSelectTextureSGIS )
{
pglSelectTextureSGIS( texture + GL_TEXTURE0_SGIS );
}
}
2010-12-06 22:00:00 +01:00
/*
==============
GL_DisableAllTexGens
==============
*/
void GL_DisableAllTexGens( void )
{
GL_TexGen( GL_S, 0 );
GL_TexGen( GL_T, 0 );
GL_TexGen( GL_R, 0 );
GL_TexGen( GL_Q, 0 );
}
/*
==============
GL_CleanUpTextureUnits
==============
*/
void GL_CleanUpTextureUnits( int last )
{
int i;
for( i = glState.activeTMU; i > last - 1; i-- )
{
GL_DisableAllTexGens();
pglDisable( GL_TEXTURE_2D );
GL_SelectTexture( i - 1 );
}
}
/*
=================
GL_MultiTexCoord2f
=================
*/
void GL_MultiTexCoord2f( GLenum texture, GLfloat s, GLfloat t )
{
if( pglMultiTexCoord2f )
{
pglMultiTexCoord2f( texture + GL_TEXTURE0_ARB, s, t );
}
else if( pglMTexCoord2fSGIS )
{
pglMTexCoord2fSGIS( texture + GL_TEXTURE0_SGIS, s, t );
}
}
2010-11-28 22:00:00 +01:00
/*
=================
GL_TexEnv
=================
*/
void GL_TexEnv( GLenum mode )
{
if( glState.currentEnvModes[glState.activeTMU] == mode )
return;
glState.currentEnvModes[glState.activeTMU] = mode;
pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, mode );
}
/*
=================
GL_TexGen
=================
*/
void GL_TexGen( GLenum coord, GLenum mode )
{
int tmu = glState.activeTMU;
int bit, gen;
switch( coord )
{
case GL_S:
bit = 1;
gen = GL_TEXTURE_GEN_S;
break;
case GL_T:
bit = 2;
gen = GL_TEXTURE_GEN_T;
break;
case GL_R:
bit = 4;
gen = GL_TEXTURE_GEN_R;
break;
case GL_Q:
bit = 8;
gen = GL_TEXTURE_GEN_Q;
break;
default: return;
}
if( mode )
{
if(!( glState.genSTEnabled[tmu] & bit ))
{
pglEnable( gen );
glState.genSTEnabled[tmu] |= bit;
}
pglTexGeni( coord, GL_TEXTURE_GEN_MODE, mode );
}
else
{
if( glState.genSTEnabled[tmu] & bit )
{
pglDisable( gen );
glState.genSTEnabled[tmu] &= ~bit;
}
}
}
/*
=================
GL_Cull
=================
*/
void GL_Cull( GLenum cull )
{
if( glState.faceCull == cull )
return;
if( !cull )
{
pglDisable( GL_CULL_FACE );
glState.faceCull = 0;
return;
}
if( !glState.faceCull )
pglEnable( GL_CULL_FACE );
pglCullFace( cull );
glState.faceCull = cull;
}
/*
=================
GL_FrontFace
=================
*/
void GL_FrontFace( GLenum front )
{
pglFrontFace( front ? GL_CW : GL_CCW );
glState.frontFace = front;
}
2010-11-29 22:00:00 +01:00
2010-12-02 22:00:00 +01:00
/*
=================
GL_SetState
=================
*/
void GL_SetState( int state )
{
int diff;
if( glState.in2DMode )
state |= GLSTATE_NO_DEPTH_TEST;
if( state & GLSTATE_NO_DEPTH_TEST )
state &= ~( GLSTATE_DEPTHWRITE|GLSTATE_DEPTHFUNC_EQ );
diff = glState.flags ^ state;
if( !diff ) return;
if( diff & ( GLSTATE_BLEND_MTEX|GLSTATE_SRCBLEND_MASK|GLSTATE_DSTBLEND_MASK ))
{
if( state & ( GLSTATE_SRCBLEND_MASK|GLSTATE_DSTBLEND_MASK ))
{
int blendsrc, blenddst;
switch( state & GLSTATE_SRCBLEND_MASK )
{
case GLSTATE_SRCBLEND_ZERO:
blendsrc = GL_ZERO;
break;
case GLSTATE_SRCBLEND_DST_COLOR:
blendsrc = GL_DST_COLOR;
break;
case GLSTATE_SRCBLEND_ONE_MINUS_DST_COLOR:
blendsrc = GL_ONE_MINUS_DST_COLOR;
break;
case GLSTATE_SRCBLEND_SRC_ALPHA:
blendsrc = GL_SRC_ALPHA;
break;
case GLSTATE_SRCBLEND_ONE_MINUS_SRC_ALPHA:
blendsrc = GL_ONE_MINUS_SRC_ALPHA;
break;
case GLSTATE_SRCBLEND_DST_ALPHA:
blendsrc = GL_DST_ALPHA;
break;
case GLSTATE_SRCBLEND_ONE_MINUS_DST_ALPHA:
blendsrc = GL_ONE_MINUS_DST_ALPHA;
break;
case GLSTATE_SRCBLEND_ONE:
default:
blendsrc = GL_ONE;
break;
}
switch( state & GLSTATE_DSTBLEND_MASK )
{
case GLSTATE_DSTBLEND_ONE:
blenddst = GL_ONE;
break;
case GLSTATE_DSTBLEND_SRC_COLOR:
blenddst = GL_SRC_COLOR;
break;
case GLSTATE_DSTBLEND_ONE_MINUS_SRC_COLOR:
blenddst = GL_ONE_MINUS_SRC_COLOR;
break;
case GLSTATE_DSTBLEND_SRC_ALPHA:
blenddst = GL_SRC_ALPHA;
break;
case GLSTATE_DSTBLEND_ONE_MINUS_SRC_ALPHA:
blenddst = GL_ONE_MINUS_SRC_ALPHA;
break;
case GLSTATE_DSTBLEND_DST_ALPHA:
blenddst = GL_DST_ALPHA;
break;
case GLSTATE_DSTBLEND_ONE_MINUS_DST_ALPHA:
blenddst = GL_ONE_MINUS_DST_ALPHA;
break;
case GLSTATE_DSTBLEND_ZERO:
default:
blenddst = GL_ZERO;
break;
}
if( state & GLSTATE_BLEND_MTEX )
{
if( glState.currentEnvModes[glState.activeTMU] != GL_REPLACE )
pglEnable( GL_BLEND );
else pglDisable( GL_BLEND );
}
else
{
pglEnable( GL_BLEND );
}
pglBlendFunc( blendsrc, blenddst );
}
else
{
pglDisable( GL_BLEND );
}
}
if( diff & GLSTATE_ALPHAFUNC )
{
if( state & GLSTATE_ALPHAFUNC )
{
if(!( glState.flags & GLSTATE_ALPHAFUNC ))
pglEnable( GL_ALPHA_TEST );
if( state & GLSTATE_AFUNC_GT0 )
pglAlphaFunc( GL_GREATER, 0 );
else if( state & GLSTATE_AFUNC_LT128 )
pglAlphaFunc( GL_LESS, 0.5f );
else pglAlphaFunc( GL_GEQUAL, 0.5f );
}
else
{
pglDisable( GL_ALPHA_TEST );
}
}
if( diff & GLSTATE_DEPTHFUNC_EQ )
{
if( state & GLSTATE_DEPTHFUNC_EQ )
pglDepthFunc( GL_EQUAL );
else pglDepthFunc( GL_LEQUAL );
}
if( diff & GLSTATE_DEPTHWRITE )
{
if( state & GLSTATE_DEPTHWRITE )
pglDepthMask( GL_TRUE );
else pglDepthMask( GL_FALSE );
}
if( diff & GLSTATE_NO_DEPTH_TEST )
{
if( state & GLSTATE_NO_DEPTH_TEST )
pglDisable( GL_DEPTH_TEST );
else pglEnable( GL_DEPTH_TEST );
}
if( diff & GLSTATE_OFFSET_FILL )
{
if( state & GLSTATE_OFFSET_FILL )
pglEnable( GL_POLYGON_OFFSET_FILL );
else pglDisable( GL_POLYGON_OFFSET_FILL );
}
glState.flags = state;
}
void GL_SetRenderMode( int mode )
{
int state, texEnv;
2010-12-27 22:00:00 +01:00
// GoldSrc in 2D mode uses default mode as TransTexture
if( glState.in2DMode && mode == kRenderNormal )
mode = kRenderTransTexture;
2010-12-02 22:00:00 +01:00
switch( mode )
{
case kRenderNormal:
default:
state = GLSTATE_DEPTHWRITE;
2010-12-23 22:00:00 +01:00
texEnv = GL_REPLACE;
2010-12-02 22:00:00 +01:00
break;
case kRenderTransColor:
2011-01-03 22:00:00 +01:00
state = GLSTATE_SRCBLEND_ZERO|GLSTATE_DSTBLEND_SRC_COLOR;
2010-12-27 22:00:00 +01:00
texEnv = GL_REPLACE;
2010-12-02 22:00:00 +01:00
break;
case kRenderTransAlpha:
state = GLSTATE_AFUNC_GE128|GLSTATE_DEPTHWRITE;
texEnv = GL_MODULATE;
break;
2010-12-12 22:00:00 +01:00
case kRenderTransTexture:
2010-12-13 22:00:00 +01:00
state = GLSTATE_SRCBLEND_SRC_ALPHA|GLSTATE_DSTBLEND_ONE_MINUS_SRC_ALPHA;
2010-12-12 22:00:00 +01:00
texEnv = GL_MODULATE;
break;
2010-12-02 22:00:00 +01:00
case kRenderGlow:
2010-12-12 22:00:00 +01:00
state = GLSTATE_SRCBLEND_SRC_ALPHA|GLSTATE_DSTBLEND_ONE|GLSTATE_NO_DEPTH_TEST;
2010-12-02 22:00:00 +01:00
texEnv = GL_MODULATE;
break;
case kRenderTransAdd:
2010-12-12 22:00:00 +01:00
state = GLSTATE_SRCBLEND_SRC_ALPHA|GLSTATE_DSTBLEND_ONE;
2010-12-02 22:00:00 +01:00
texEnv = GL_MODULATE;
break;
case kRenderTransInverse:
2010-12-13 22:00:00 +01:00
state = GLSTATE_SRCBLEND_ONE_MINUS_SRC_ALPHA|GLSTATE_DSTBLEND_SRC_ALPHA;
2010-12-02 22:00:00 +01:00
texEnv = GL_MODULATE;
break;
}
GL_TexEnv( texEnv );
GL_SetState( state );
}
2010-11-29 22:00:00 +01:00
/*
==============================================================================
SCREEN SHOTS
==============================================================================
*/
// used for 'env' and 'sky' shots
typedef struct envmap_s
{
vec3_t angles;
int flags;
} envmap_t;
const envmap_t r_skyBoxInfo[6] =
{
{{ 0, 270, 180}, IMAGE_FLIP_X },
{{ 0, 90, 180}, IMAGE_FLIP_X },
{{ -90, 0, 180}, IMAGE_FLIP_X },
{{ 90, 0, 180}, IMAGE_FLIP_X },
{{ 0, 0, 180}, IMAGE_FLIP_X },
{{ 0, 180, 180}, IMAGE_FLIP_X },
};
const envmap_t r_envMapInfo[6] =
{
{{ 0, 0, 90}, 0 },
{{ 0, 180, -90}, 0 },
{{ 0, 90, 0}, 0 },
{{ 0, 270, 180}, 0 },
{{-90, 180, -90}, 0 },
{{ 90, 180, 90}, 0 }
};
/*
================
VID_ImageAdjustGamma
================
*/
void VID_ImageAdjustGamma( byte *in, uint width, uint height )
{
int i, c = width * height;
float g = 1.0f / bound( 0.5f, vid_gamma->value, 3.0f );
byte r_gammaTable[256]; // adjust screenshot gamma
byte *p = in;
// rebuild the gamma table
for( i = 0; i < 256; i++ )
{
if ( g == 1.0f ) r_gammaTable[i] = i;
else r_gammaTable[i] = bound( 0, 255 * pow((i + 0.5)/255.5f, g ) + 0.5, 255 );
}
// adjust screenshots gamma
for( i = 0; i < c; i++, p += 3 )
{
p[0] = r_gammaTable[p[0]];
p[1] = r_gammaTable[p[1]];
p[2] = r_gammaTable[p[2]];
}
}
qboolean VID_ScreenShot( const char *filename, int shot_type )
{
rgbdata_t *r_shot;
uint flags = IMAGE_FLIP_Y;
int width = 0, height = 0;
qboolean result;
r_shot = Mem_Alloc( r_temppool, sizeof( rgbdata_t ));
r_shot->width = glState.width;
r_shot->height = glState.height;
r_shot->flags = IMAGE_HAS_COLOR;
r_shot->type = PF_RGB_24;
r_shot->size = r_shot->width * r_shot->height * PFDesc( r_shot->type )->bpp;
r_shot->palette = NULL;
2011-02-15 22:00:00 +01:00
r_shot->buffer = Mem_Alloc( r_temppool, r_shot->size );
2010-11-29 22:00:00 +01:00
// get screen frame
pglReadPixels( 0, 0, glState.width, glState.height, GL_RGB, GL_UNSIGNED_BYTE, r_shot->buffer );
switch( shot_type )
{
case VID_SCREENSHOT:
VID_ImageAdjustGamma( r_shot->buffer, r_shot->width, r_shot->height ); // adjust brightness
break;
case VID_LEVELSHOT:
flags |= IMAGE_RESAMPLE;
height = 384;
width = 512;
break;
case VID_MINISHOT:
flags |= IMAGE_RESAMPLE;
height = 200;
width = 320;
break;
}
Image_Process( &r_shot, width, height, flags );
// write image
result = FS_SaveImage( filename, r_shot );
FS_FreeImage( r_shot );
return result;
}
/*
=================
VID_CubemapShot
=================
*/
qboolean VID_CubemapShot( const char *base, uint size, const float *vieworg, qboolean skyshot )
{
rgbdata_t *r_shot, *r_side;
byte *temp = NULL;
byte *buffer = NULL;
string basename;
int i = 1, flags, result;
if( !RI.drawWorld || !cl.worldmodel )
return false;
// make sure the specified size is valid
while( i < size ) i<<=1;
if( i != size ) return false;
if( size > glState.width || size > glState.height )
return false;
// setup refdef
RI.params |= RP_ENVVIEW; // do not render non-bmodel entities
// alloc space
temp = Mem_Alloc( r_temppool, size * size * 3 );
buffer = Mem_Alloc( r_temppool, size * size * 3 * 6 );
r_shot = Mem_Alloc( r_temppool, sizeof( rgbdata_t ));
r_side = Mem_Alloc( r_temppool, sizeof( rgbdata_t ));
// use client vieworg
if( !vieworg ) vieworg = r_lastRefdef.vieworg;
for( i = 0; i < 6; i++ )
{
if( skyshot )
{
R_DrawCubemapView( vieworg, r_skyBoxInfo[i].angles, size );
flags = r_skyBoxInfo[i].flags;
}
else
{
R_DrawCubemapView( vieworg, r_envMapInfo[i].angles, size );
flags = r_envMapInfo[i].flags;
}
pglReadPixels( 0, glState.height - size, size, size, GL_RGB, GL_UNSIGNED_BYTE, temp );
r_side->flags = IMAGE_HAS_COLOR;
r_side->width = r_side->height = size;
r_side->type = PF_RGB_24;
r_side->size = r_side->width * r_side->height * 3;
r_side->buffer = temp;
if( flags ) Image_Process( &r_side, 0, 0, flags );
Mem_Copy( buffer + (size * size * 3 * i), r_side->buffer, size * size * 3 );
}
RI.params &= ~RP_ENVVIEW;
r_shot->flags = IMAGE_HAS_COLOR;
r_shot->flags |= (skyshot) ? IMAGE_SKYBOX : IMAGE_CUBEMAP;
r_shot->width = size;
r_shot->height = size;
r_shot->type = PF_RGB_24;
r_shot->size = r_shot->width * r_shot->height * 3 * 6;
r_shot->palette = NULL;
r_shot->buffer = buffer;
// make sure what we have right extension
com.strncpy( basename, base, MAX_STRING );
FS_StripExtension( basename );
2011-01-03 22:00:00 +01:00
FS_DefaultExtension( basename, ".tga" );
2010-11-29 22:00:00 +01:00
// write image as dds packet
result = FS_SaveImage( basename, r_shot );
FS_FreeImage( r_shot );
FS_FreeImage( r_side );
return result;
}
//=======================================================
/*
===============
R_ShowTextures
Draw all the images to the screen, on top of whatever
was there. This is used to test for texture thrashing.
===============
*/
void R_ShowTextures( void )
{
gltexture_t *image;
float x, y, w, h;
int i, j, base_w, base_h;
if( !gl_showtextures->integer )
return;
if( !glState.in2DMode )
{
R_Set2DMode( true );
}
pglClear( GL_COLOR_BUFFER_BIT );
pglFinish();
if( gl_showtextures->integer == TEX_LIGHTMAP )
{
// draw lightmaps as big images
base_w = 5;
base_h = 4;
}
else
{
base_w = 16;
base_h = 12;
}
for( i = j = 0; i < MAX_TEXTURES; i++ )
{
image = R_GetTexture( i );
if( !image->texnum ) continue;
if( image->texType != gl_showtextures->integer )
continue;
w = glState.width / base_w;
h = glState.height / base_h;
x = j % base_w * w;
y = j / base_w * h;
pglColor4f( 1.0f, 1.0f, 1.0f, 1.0f );
GL_Bind( GL_TEXTURE0, image->texnum );
GL_CheckForErrors();
pglBegin( GL_QUADS );
pglTexCoord2f( 0, 0 );
pglVertex2f( x, y );
pglTexCoord2f( 1, 0 );
pglVertex2f( x + w, y );
pglTexCoord2f( 1, 1 );
pglVertex2f( x + w, y + h );
pglTexCoord2f( 0, 1 );
pglVertex2f( x, y + h );
pglEnd();
j++;
}
pglFinish();
GL_CheckForErrors ();
}
//=======================================================