📄 tr_backend.c
字号:
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "tr_local.h"
backEndData_t *backEndData[SMP_FRAMES];
backEndState_t backEnd;
static float s_flipMatrix[16] = {
// convert from our coordinate system (looking down X)
// to OpenGL's coordinate system (looking down -Z)
0, 0, -1, 0,
-1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 0, 1
};
/*
** GL_Bind
*/
void GL_Bind( image_t *image ) {
int texnum;
if ( !image ) {
ri.Printf( PRINT_WARNING, "GL_Bind: NULL image\n" );
texnum = tr.defaultImage->texnum;
} else {
texnum = image->texnum;
}
if ( r_nobind->integer && tr.dlightImage ) { // performance evaluation option
texnum = tr.dlightImage->texnum;
}
if ( glState.currenttextures[glState.currenttmu] != texnum ) {
image->frameUsed = tr.frameCount;
glState.currenttextures[glState.currenttmu] = texnum;
qglBindTexture (GL_TEXTURE_2D, texnum);
}
}
/*
** GL_SelectTexture
*/
void GL_SelectTexture( int unit )
{
if ( glState.currenttmu == unit )
{
return;
}
if ( unit == 0 )
{
qglActiveTextureARB( GL_TEXTURE0_ARB );
GLimp_LogComment( "glActiveTextureARB( GL_TEXTURE0_ARB )\n" );
qglClientActiveTextureARB( GL_TEXTURE0_ARB );
GLimp_LogComment( "glClientActiveTextureARB( GL_TEXTURE0_ARB )\n" );
}
else if ( unit == 1 )
{
qglActiveTextureARB( GL_TEXTURE1_ARB );
GLimp_LogComment( "glActiveTextureARB( GL_TEXTURE1_ARB )\n" );
qglClientActiveTextureARB( GL_TEXTURE1_ARB );
GLimp_LogComment( "glClientActiveTextureARB( GL_TEXTURE1_ARB )\n" );
} else {
ri.Error( ERR_DROP, "GL_SelectTexture: unit = %i", unit );
}
glState.currenttmu = unit;
}
/*
** GL_BindMultitexture
*/
void GL_BindMultitexture( image_t *image0, GLuint env0, image_t *image1, GLuint env1 ) {
int texnum0, texnum1;
texnum0 = image0->texnum;
texnum1 = image1->texnum;
if ( r_nobind->integer && tr.dlightImage ) { // performance evaluation option
texnum0 = texnum1 = tr.dlightImage->texnum;
}
if ( glState.currenttextures[1] != texnum1 ) {
GL_SelectTexture( 1 );
image1->frameUsed = tr.frameCount;
glState.currenttextures[1] = texnum1;
qglBindTexture( GL_TEXTURE_2D, texnum1 );
}
if ( glState.currenttextures[0] != texnum0 ) {
GL_SelectTexture( 0 );
image0->frameUsed = tr.frameCount;
glState.currenttextures[0] = texnum0;
qglBindTexture( GL_TEXTURE_2D, texnum0 );
}
}
/*
** GL_Cull
*/
void GL_Cull( int cullType ) {
if ( glState.faceCulling == cullType ) {
return;
}
glState.faceCulling = cullType;
if ( cullType == CT_TWO_SIDED )
{
qglDisable( GL_CULL_FACE );
}
else
{
qglEnable( GL_CULL_FACE );
if ( cullType == CT_BACK_SIDED )
{
if ( backEnd.viewParms.isMirror )
{
qglCullFace( GL_FRONT );
}
else
{
qglCullFace( GL_BACK );
}
}
else
{
if ( backEnd.viewParms.isMirror )
{
qglCullFace( GL_BACK );
}
else
{
qglCullFace( GL_FRONT );
}
}
}
}
/*
** GL_TexEnv
*/
void GL_TexEnv( int env )
{
if ( env == glState.texEnv[glState.currenttmu] )
{
return;
}
glState.texEnv[glState.currenttmu] = env;
switch ( env )
{
case GL_MODULATE:
qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
break;
case GL_REPLACE:
qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
break;
case GL_DECAL:
qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL );
break;
case GL_ADD:
qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD );
break;
default:
ri.Error( ERR_DROP, "GL_TexEnv: invalid env '%d' passed\n", env );
break;
}
}
/*
** GL_State
**
** This routine is responsible for setting the most commonly changed state
** in Q3.
*/
void GL_State( unsigned long stateBits )
{
unsigned long diff = stateBits ^ glState.glStateBits;
if ( !diff )
{
return;
}
//
// check depthFunc bits
//
if ( diff & GLS_DEPTHFUNC_EQUAL )
{
if ( stateBits & GLS_DEPTHFUNC_EQUAL )
{
qglDepthFunc( GL_EQUAL );
}
else
{
qglDepthFunc( GL_LEQUAL );
}
}
//
// check blend bits
//
if ( diff & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ) )
{
GLenum srcFactor, dstFactor;
if ( stateBits & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ) )
{
switch ( stateBits & GLS_SRCBLEND_BITS )
{
case GLS_SRCBLEND_ZERO:
srcFactor = GL_ZERO;
break;
case GLS_SRCBLEND_ONE:
srcFactor = GL_ONE;
break;
case GLS_SRCBLEND_DST_COLOR:
srcFactor = GL_DST_COLOR;
break;
case GLS_SRCBLEND_ONE_MINUS_DST_COLOR:
srcFactor = GL_ONE_MINUS_DST_COLOR;
break;
case GLS_SRCBLEND_SRC_ALPHA:
srcFactor = GL_SRC_ALPHA;
break;
case GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA:
srcFactor = GL_ONE_MINUS_SRC_ALPHA;
break;
case GLS_SRCBLEND_DST_ALPHA:
srcFactor = GL_DST_ALPHA;
break;
case GLS_SRCBLEND_ONE_MINUS_DST_ALPHA:
srcFactor = GL_ONE_MINUS_DST_ALPHA;
break;
case GLS_SRCBLEND_ALPHA_SATURATE:
srcFactor = GL_SRC_ALPHA_SATURATE;
break;
default:
srcFactor = GL_ONE; // to get warning to shut up
ri.Error( ERR_DROP, "GL_State: invalid src blend state bits\n" );
break;
}
switch ( stateBits & GLS_DSTBLEND_BITS )
{
case GLS_DSTBLEND_ZERO:
dstFactor = GL_ZERO;
break;
case GLS_DSTBLEND_ONE:
dstFactor = GL_ONE;
break;
case GLS_DSTBLEND_SRC_COLOR:
dstFactor = GL_SRC_COLOR;
break;
case GLS_DSTBLEND_ONE_MINUS_SRC_COLOR:
dstFactor = GL_ONE_MINUS_SRC_COLOR;
break;
case GLS_DSTBLEND_SRC_ALPHA:
dstFactor = GL_SRC_ALPHA;
break;
case GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA:
dstFactor = GL_ONE_MINUS_SRC_ALPHA;
break;
case GLS_DSTBLEND_DST_ALPHA:
dstFactor = GL_DST_ALPHA;
break;
case GLS_DSTBLEND_ONE_MINUS_DST_ALPHA:
dstFactor = GL_ONE_MINUS_DST_ALPHA;
break;
default:
dstFactor = GL_ONE; // to get warning to shut up
ri.Error( ERR_DROP, "GL_State: invalid dst blend state bits\n" );
break;
}
qglEnable( GL_BLEND );
qglBlendFunc( srcFactor, dstFactor );
}
else
{
qglDisable( GL_BLEND );
}
}
//
// check depthmask
//
if ( diff & GLS_DEPTHMASK_TRUE )
{
if ( stateBits & GLS_DEPTHMASK_TRUE )
{
qglDepthMask( GL_TRUE );
}
else
{
qglDepthMask( GL_FALSE );
}
}
//
// fill/line mode
//
if ( diff & GLS_POLYMODE_LINE )
{
if ( stateBits & GLS_POLYMODE_LINE )
{
qglPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
}
else
{
qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
}
}
//
// depthtest
//
if ( diff & GLS_DEPTHTEST_DISABLE )
{
if ( stateBits & GLS_DEPTHTEST_DISABLE )
{
qglDisable( GL_DEPTH_TEST );
}
else
{
qglEnable( GL_DEPTH_TEST );
}
}
//
// alpha test
//
if ( diff & GLS_ATEST_BITS )
{
switch ( stateBits & GLS_ATEST_BITS )
{
case 0:
qglDisable( GL_ALPHA_TEST );
break;
case GLS_ATEST_GT_0:
qglEnable( GL_ALPHA_TEST );
qglAlphaFunc( GL_GREATER, 0.0f );
break;
case GLS_ATEST_LT_80:
qglEnable( GL_ALPHA_TEST );
qglAlphaFunc( GL_LESS, 0.5f );
break;
case GLS_ATEST_GE_80:
qglEnable( GL_ALPHA_TEST );
qglAlphaFunc( GL_GEQUAL, 0.5f );
break;
default:
assert( 0 );
break;
}
}
glState.glStateBits = stateBits;
}
/*
================
RB_Hyperspace
A player has predicted a teleport, but hasn't arrived yet
================
*/
static void RB_Hyperspace( void ) {
float c;
if ( !backEnd.isHyperspace ) {
// do initialization shit
}
c = ( backEnd.refdef.time & 255 ) / 255.0f;
qglClearColor( c, c, c, 1 );
qglClear( GL_COLOR_BUFFER_BIT );
backEnd.isHyperspace = qtrue;
}
static void SetViewportAndScissor( void ) {
qglMatrixMode(GL_PROJECTION);
qglLoadMatrixf( backEnd.viewParms.projectionMatrix );
qglMatrixMode(GL_MODELVIEW);
// set the window clipping
qglViewport( backEnd.viewParms.viewportX, backEnd.viewParms.viewportY,
backEnd.viewParms.viewportWidth, backEnd.viewParms.viewportHeight );
qglScissor( backEnd.viewParms.viewportX, backEnd.viewParms.viewportY,
backEnd.viewParms.viewportWidth, backEnd.viewParms.viewportHeight );
}
/*
=================
RB_BeginDrawingView
Any mirrored or portaled views have already been drawn, so prepare
to actually render the visible surfaces for this view
=================
*/
void RB_BeginDrawingView (void) {
int clearBits = 0;
// sync with gl if needed
if ( r_finish->integer == 1 && !glState.finishCalled ) {
qglFinish ();
glState.finishCalled = qtrue;
}
if ( r_finish->integer == 0 ) {
glState.finishCalled = qtrue;
}
// we will need to change the projection matrix before drawing
// 2D images again
backEnd.projection2D = qfalse;
//
// set the modelview matrix for the viewer
//
SetViewportAndScissor();
// ensures that depth writes are enabled for the depth clear
GL_State( GLS_DEFAULT );
// clear relevant buffers
clearBits = GL_DEPTH_BUFFER_BIT;
if ( r_measureOverdraw->integer || r_shadows->integer == 2 )
{
clearBits |= GL_STENCIL_BUFFER_BIT;
}
if ( r_fastsky->integer && !( backEnd.refdef.rdflags & RDF_NOWORLDMODEL ) )
{
clearBits |= GL_COLOR_BUFFER_BIT; // FIXME: only if sky shaders have been used
#ifdef _DEBUG
qglClearColor( 0.8f, 0.7f, 0.4f, 1.0f ); // FIXME: get color of sky
#else
qglClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); // FIXME: get color of sky
#endif
}
qglClear( clearBits );
if ( ( backEnd.refdef.rdflags & RDF_HYPERSPACE ) )
{
RB_Hyperspace();
return;
}
else
{
backEnd.isHyperspace = qfalse;
}
glState.faceCulling = -1; // force face culling to set next time
// we will only draw a sun if there was sky rendered in this view
backEnd.skyRenderedThisView = qfalse;
// clip to the plane of the portal
if ( backEnd.viewParms.isPortal ) {
float plane[4];
double plane2[4];
plane[0] = backEnd.viewParms.portalPlane.normal[0];
plane[1] = backEnd.viewParms.portalPlane.normal[1];
plane[2] = backEnd.viewParms.portalPlane.normal[2];
plane[3] = backEnd.viewParms.portalPlane.dist;
plane2[0] = DotProduct (backEnd.viewParms.or.axis[0], plane);
plane2[1] = DotProduct (backEnd.viewParms.or.axis[1], plane);
plane2[2] = DotProduct (backEnd.viewParms.or.axis[2], plane);
plane2[3] = DotProduct (plane, backEnd.viewParms.or.origin) - plane[3];
qglLoadMatrixf( s_flipMatrix );
qglClipPlane (GL_CLIP_PLANE0, plane2);
qglEnable (GL_CLIP_PLANE0);
} else {
qglDisable (GL_CLIP_PLANE0);
}
}
#define MAC_EVENT_PUMP_MSEC 5
/*
==================
RB_RenderDrawSurfList
==================
*/
void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) {
shader_t *shader, *oldShader;
int fogNum, oldFogNum;
int entityNum, oldEntityNum;
int dlighted, oldDlighted;
qboolean depthRange, oldDepthRange;
int i;
drawSurf_t *drawSurf;
int oldSort;
float originalTime;
#ifdef __MACOS__
int macEventTime;
Sys_PumpEvents(); // crutch up the mac's limited buffer queue size
// we don't want to pump the event loop too often and waste time, so
// we are going to check every shader change
macEventTime = ri.Milliseconds() + MAC_EVENT_PUMP_MSEC;
#endif
// save original time for entity shader offsets
originalTime = backEnd.refdef.floatTime;
// clear the z buffer, set the modelview, etc
RB_BeginDrawingView ();
// draw everything
oldEntityNum = -1;
backEnd.currentEntity = &tr.worldEntity;
oldShader = NULL;
oldFogNum = -1;
oldDepthRange = qfalse;
oldDlighted = qfalse;
oldSort = -1;
depthRange = qfalse;
backEnd.pc.c_surfaces += numDrawSurfs;
for (i = 0, drawSurf = drawSurfs ; i < numDrawSurfs ; i++, drawSurf++) {
if ( drawSurf->sort == oldSort ) {
// fast path, same as previous sort
rb_surfaceTable[ *drawSurf->surface ]( drawSurf->surface );
continue;
}
oldSort = drawSurf->sort;
R_DecomposeSort( drawSurf->sort, &entityNum, &shader, &fogNum, &dlighted );
//
// change the tess parameters if needed
// a "entityMergable" shader is a shader that can have surfaces from seperate
// entities merged into a single batch, like smoke and blood puff sprites
if (shader != oldShader || fogNum != oldFogNum || dlighted != oldDlighted
|| ( entityNum != oldEntityNum && !shader->entityMergable ) ) {
if (oldShader != NULL) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -