2630 lines
61 KiB
C
2630 lines
61 KiB
C
/*
|
|
Copyright (C) 1997-2001 Id Software, Inc.
|
|
Copyright (C) 2002-2007 Victor Luchits
|
|
|
|
This program 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.
|
|
|
|
This program 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 this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
// r_main.c
|
|
|
|
#include <stdio.h> // sscanf
|
|
#include "r_local.h"
|
|
#include "mathlib.h"
|
|
#include "quatlib.h"
|
|
|
|
render_imp_t ri;
|
|
stdlib_api_t com;
|
|
|
|
ref_model_t *r_worldmodel;
|
|
mbrushmodel_t *r_worldbrushmodel;
|
|
|
|
float gldepthmin, gldepthmax;
|
|
|
|
mapconfig_t mapConfig;
|
|
|
|
refinst_t RI, prevRI;
|
|
ref_params_t r_lastRefdef;
|
|
|
|
image_t *r_cintexture; // cinematic texture
|
|
image_t *r_portaltexture; // portal view
|
|
image_t *r_portaltexture2; // refraction image for distortions
|
|
image_t *r_notexture; // use for bad textures
|
|
image_t *r_particletexture; // little dot for particles
|
|
image_t *r_whitetexture;
|
|
image_t *r_blacktexture;
|
|
image_t *r_blankbumptexture;
|
|
image_t *r_dlighttexture;
|
|
image_t *r_fogtexture;
|
|
image_t *r_coronatexture;
|
|
image_t *r_shadowmapTextures[MAX_SHADOWGROUPS];
|
|
|
|
static int r_numnullentities;
|
|
static ref_entity_t *r_nullentities[MAX_EDICTS];
|
|
ref_model_t *cl_models[MAX_MODELS]; // client replacement modeltable
|
|
static int r_numbmodelentities;
|
|
static ref_entity_t *r_bmodelentities[MAX_EDICTS];
|
|
|
|
static byte r_entVisBits[MAX_EDICTS/8];
|
|
|
|
int r_pvsframecount; // bumped when going to a new PVS
|
|
int r_framecount; // used for dlight push checking
|
|
|
|
int c_brush_polys, c_world_leafs;
|
|
|
|
int r_mark_leaves, r_world_node;
|
|
int r_add_polys, r_add_entities;
|
|
int r_sort_meshes, r_draw_meshes;
|
|
|
|
msurface_t *r_debug_surface;
|
|
|
|
char r_speeds_msg[MAX_RSPEEDSMSGSIZE];
|
|
|
|
//
|
|
// screen size info
|
|
//
|
|
unsigned int r_numEntities;
|
|
ref_entity_t r_entities[MAX_ENTITIES];
|
|
ref_entity_t *r_worldent = &r_entities[0];
|
|
|
|
unsigned int r_numDlights;
|
|
dlight_t r_dlights[MAX_DLIGHTS];
|
|
|
|
unsigned int r_numPolys;
|
|
poly_t r_polys[MAX_POLYS];
|
|
|
|
lightstyle_t r_lightStyles[MAX_LIGHTSTYLES];
|
|
|
|
int r_viewcluster, r_oldviewcluster;
|
|
|
|
float r_farclip_min, r_farclip_bias = 64.0f;
|
|
|
|
/*
|
|
=================
|
|
GL_Cull
|
|
=================
|
|
*/
|
|
void GL_Cull( int 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_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;
|
|
default:
|
|
case GLSTATE_SRCBLEND_ONE:
|
|
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;
|
|
default:
|
|
case GLSTATE_DSTBLEND_ZERO:
|
|
blenddst = GL_ZERO;
|
|
break;
|
|
}
|
|
|
|
if( state & GLSTATE_BLEND_MTEX )
|
|
{
|
|
if( glState.currentEnvModes[glState.currentTMU] != 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;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
GL_FrontFace
|
|
=================
|
|
*/
|
|
void GL_FrontFace( int front )
|
|
{
|
|
pglFrontFace( front ? GL_CW : GL_CCW );
|
|
glState.frontFace = front;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
R_TransformEntityBBox
|
|
=============
|
|
*/
|
|
void R_TransformEntityBBox( ref_entity_t *e, vec3_t mins, vec3_t maxs, vec3_t bbox[8], bool local )
|
|
{
|
|
int i;
|
|
vec3_t axis[3], tmp;
|
|
|
|
if( e == r_worldent )
|
|
local = false;
|
|
if( local )
|
|
Matrix_Transpose( e->axis, axis ); // switch row-column order
|
|
|
|
// rotate local bounding box and compute the full bounding box
|
|
for( i = 0; i < 8; i++ )
|
|
{
|
|
vec_t *corner = bbox[i];
|
|
|
|
corner[0] = ( ( i & 1 ) ? mins[0] : maxs[0] );
|
|
corner[1] = ( ( i & 2 ) ? mins[1] : maxs[1] );
|
|
corner[2] = ( ( i & 4 ) ? mins[2] : maxs[2] );
|
|
|
|
if( local )
|
|
{
|
|
Matrix_TransformVector( axis, corner, tmp );
|
|
VectorAdd( tmp, e->origin, corner );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
R_LoadIdentity
|
|
=============
|
|
*/
|
|
void R_LoadIdentity( void )
|
|
{
|
|
Matrix4_Identity( RI.objectMatrix );
|
|
Matrix4_Copy( RI.worldviewMatrix, RI.modelviewMatrix );
|
|
pglLoadMatrixf( RI.modelviewMatrix );
|
|
}
|
|
|
|
/*
|
|
=============
|
|
R_RotateForEntity
|
|
=============
|
|
*/
|
|
void R_RotateForEntity( ref_entity_t *e )
|
|
{
|
|
if( e == r_worldent )
|
|
{
|
|
R_LoadIdentity();
|
|
return;
|
|
}
|
|
|
|
if( e->scale != 1.0f )
|
|
{
|
|
RI.objectMatrix[0] = e->axis[0][0] * e->scale;
|
|
RI.objectMatrix[1] = e->axis[0][1] * e->scale;
|
|
RI.objectMatrix[2] = e->axis[0][2] * e->scale;
|
|
RI.objectMatrix[4] = e->axis[1][0] * e->scale;
|
|
RI.objectMatrix[5] = e->axis[1][1] * e->scale;
|
|
RI.objectMatrix[6] = e->axis[1][2] * e->scale;
|
|
RI.objectMatrix[8] = e->axis[2][0] * e->scale;
|
|
RI.objectMatrix[9] = e->axis[2][1] * e->scale;
|
|
RI.objectMatrix[10] = e->axis[2][2] * e->scale;
|
|
}
|
|
else
|
|
{
|
|
RI.objectMatrix[0] = e->axis[0][0];
|
|
RI.objectMatrix[1] = e->axis[0][1];
|
|
RI.objectMatrix[2] = e->axis[0][2];
|
|
RI.objectMatrix[4] = e->axis[1][0];
|
|
RI.objectMatrix[5] = e->axis[1][1];
|
|
RI.objectMatrix[6] = e->axis[1][2];
|
|
RI.objectMatrix[8] = e->axis[2][0];
|
|
RI.objectMatrix[9] = e->axis[2][1];
|
|
RI.objectMatrix[10] = e->axis[2][2];
|
|
}
|
|
|
|
RI.objectMatrix[3] = 0;
|
|
RI.objectMatrix[7] = 0;
|
|
RI.objectMatrix[11] = 0;
|
|
RI.objectMatrix[12] = e->origin[0];
|
|
RI.objectMatrix[13] = e->origin[1];
|
|
RI.objectMatrix[14] = e->origin[2];
|
|
RI.objectMatrix[15] = 1.0;
|
|
|
|
Matrix4_MultiplyFast( RI.worldviewMatrix, RI.objectMatrix, RI.modelviewMatrix );
|
|
pglLoadMatrixf( RI.modelviewMatrix );
|
|
}
|
|
|
|
/*
|
|
=============
|
|
R_TranslateForEntity
|
|
=============
|
|
*/
|
|
void R_TranslateForEntity( ref_entity_t *e )
|
|
{
|
|
if( e == r_worldent )
|
|
{
|
|
R_LoadIdentity();
|
|
return;
|
|
}
|
|
|
|
Matrix4_Identity( RI.objectMatrix );
|
|
|
|
RI.objectMatrix[12] = e->origin[0];
|
|
RI.objectMatrix[13] = e->origin[1];
|
|
RI.objectMatrix[14] = e->origin[2];
|
|
|
|
Matrix4_MultiplyFast( RI.worldviewMatrix, RI.objectMatrix, RI.modelviewMatrix );
|
|
pglLoadMatrixf( RI.modelviewMatrix );
|
|
}
|
|
|
|
/*
|
|
=============
|
|
R_LerpTag
|
|
=============
|
|
*/
|
|
bool R_LerpTag( orientation_t *orient, const ref_model_t *mod, int oldframe, int frame, float lerpfrac, const char *name )
|
|
{
|
|
if( !orient )
|
|
return false;
|
|
|
|
VectorClear( orient->origin );
|
|
Matrix_Identity( orient->axis );
|
|
|
|
if( !name )
|
|
return false;
|
|
|
|
if( mod->type == mod_alias )
|
|
return R_AliasModelLerpTag( orient, mod->extradata, oldframe, frame, lerpfrac, name );
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
R_FogForSphere
|
|
=============
|
|
*/
|
|
mfog_t *R_FogForSphere( const vec3_t centre, const float radius )
|
|
{
|
|
int i, j;
|
|
mfog_t *fog;
|
|
cplane_t *plane;
|
|
|
|
if( !r_worldmodel || ( RI.refdef.rdflags & RDF_NOWORLDMODEL ) || !r_worldbrushmodel->numfogs )
|
|
return NULL;
|
|
if( RI.params & RP_SHADOWMAPVIEW )
|
|
return NULL;
|
|
if( r_worldbrushmodel->globalfog )
|
|
return r_worldbrushmodel->globalfog;
|
|
|
|
fog = r_worldbrushmodel->fogs;
|
|
for( i = 0; i < r_worldbrushmodel->numfogs; i++, fog++ )
|
|
{
|
|
if( !fog->shader )
|
|
continue;
|
|
|
|
plane = fog->planes;
|
|
for( j = 0; j < fog->numplanes; j++, plane++ )
|
|
{
|
|
// if completely in front of face, no intersection
|
|
if( PlaneDiff( centre, plane ) > radius )
|
|
break;
|
|
}
|
|
|
|
if( j == fog->numplanes )
|
|
return fog;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
R_CompletelyFogged
|
|
=============
|
|
*/
|
|
bool R_CompletelyFogged( mfog_t *fog, vec3_t origin, float radius )
|
|
{
|
|
// note that fog->distanceToEye < 0 is always true if
|
|
// globalfog is not NULL and we're inside the world boundaries
|
|
if( fog && fog->shader && RI.fog_dist_to_eye[fog-r_worldbrushmodel->fogs] < 0 )
|
|
{
|
|
float vpnDist = ( ( RI.viewOrigin[0] - origin[0] ) * RI.vpn[0] + ( RI.viewOrigin[1] - origin[1] ) * RI.vpn[1] + ( RI.viewOrigin[2] - origin[2] ) * RI.vpn[2] );
|
|
return ( ( vpnDist + radius ) / fog->shader->fog_dist ) < -1;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
=============================================================
|
|
|
|
CUSTOM COLORS
|
|
|
|
=============================================================
|
|
*/
|
|
|
|
static rgba_t r_customColors[NUM_CUSTOMCOLORS];
|
|
|
|
/*
|
|
=================
|
|
R_InitCustomColors
|
|
=================
|
|
*/
|
|
void R_InitCustomColors( void )
|
|
{
|
|
memset( r_customColors, 255, sizeof( r_customColors ) );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_SetCustomColor
|
|
=================
|
|
*/
|
|
void R_SetCustomColor( int num, int r, int g, int b )
|
|
{
|
|
if( num < 0 || num >= NUM_CUSTOMCOLORS )
|
|
return;
|
|
Vector4Set( r_customColors[num], (byte)r, (byte)g, (byte)b, 255 );
|
|
}
|
|
/*
|
|
=================
|
|
R_GetCustomColor
|
|
=================
|
|
*/
|
|
int R_GetCustomColor( int num )
|
|
{
|
|
if( num < 0 || num >= NUM_CUSTOMCOLORS )
|
|
return MakeRGBA( 255, 255, 255, 255 );
|
|
return *(int *)r_customColors[num];
|
|
}
|
|
|
|
/*
|
|
=============================================================
|
|
|
|
SPRITE MODELS AND FLARES
|
|
|
|
=============================================================
|
|
*/
|
|
|
|
static vec4_t spr_xyz[4] = { {0,0,0,1}, {0,0,0,1}, {0,0,0,1}, {0,0,0,1} };
|
|
static vec2_t spr_st[4] = { {0, 1}, {0, 0}, {1,0}, {1,1} };
|
|
static rgba_t spr_color[4];
|
|
static mesh_t spr_mesh = { 4, spr_xyz, spr_xyz, NULL, spr_st, { 0, 0, 0, 0 }, { spr_color, spr_color, spr_color, spr_color }, 6, NULL };
|
|
|
|
/*
|
|
=================
|
|
R_PushSprite
|
|
=================
|
|
*/
|
|
static bool R_PushSprite( const meshbuffer_t *mb, float rotation, float right, float left, float up, float down )
|
|
{
|
|
int i, features;
|
|
vec3_t point;
|
|
vec3_t v_right, v_up;
|
|
ref_entity_t *e = RI.currententity;
|
|
ref_shader_t *shader;
|
|
|
|
if( rotation )
|
|
{
|
|
RotatePointAroundVector( v_right, RI.vpn, RI.vright, rotation );
|
|
CrossProduct( RI.vpn, v_right, v_up );
|
|
}
|
|
else
|
|
{
|
|
VectorCopy( RI.vright, v_right );
|
|
VectorCopy( RI.vup, v_up );
|
|
}
|
|
|
|
VectorScale( v_up, down, point );
|
|
VectorMA( point, -left, v_right, spr_xyz[0] );
|
|
VectorMA( point, -right, v_right, spr_xyz[3] );
|
|
|
|
VectorScale( v_up, up, point );
|
|
VectorMA( point, -left, v_right, spr_xyz[1] );
|
|
VectorMA( point, -right, v_right, spr_xyz[2] );
|
|
|
|
if( e->scale != 1.0f )
|
|
{
|
|
for( i = 0; i < 4; i++ )
|
|
VectorScale( spr_xyz[i], e->scale, spr_xyz[i] );
|
|
}
|
|
|
|
MB_NUM2SHADER( mb->shaderkey, shader );
|
|
|
|
// the code below is disgusting, but some q3a shaders use 'rgbgen vertex'
|
|
// and 'alphagen vertex' for effects instead of 'rgbgen entity' and 'alphagen entity'
|
|
if( shader->features & MF_COLORS )
|
|
{
|
|
for( i = 0; i < 4; i++ )
|
|
Vector4Copy( e->color, spr_color[i] );
|
|
}
|
|
|
|
features = MF_NOCULL | MF_TRIFAN | shader->features;
|
|
if( r_shownormals->integer )
|
|
features |= MF_NORMALS;
|
|
|
|
if( shader->flags & SHADER_ENTITY_MERGABLE )
|
|
{
|
|
for( i = 0; i < 4; i++ )
|
|
VectorAdd( spr_xyz[i], e->origin, spr_xyz[i] );
|
|
R_PushMesh( &spr_mesh, features );
|
|
return false;
|
|
}
|
|
|
|
R_PushMesh( &spr_mesh, MF_NONBATCHED | features );
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_PushFlareSurf
|
|
=================
|
|
*/
|
|
static void R_PushFlareSurf( const meshbuffer_t *mb )
|
|
{
|
|
int i;
|
|
vec4_t color;
|
|
vec3_t origin, point, v;
|
|
float radius = r_flaresize->value, colorscale, depth;
|
|
float up = radius, down = -radius, left = -radius, right = radius;
|
|
mbrushmodel_t *bmodel = ( mbrushmodel_t * )RI.currentmodel->extradata;
|
|
msurface_t *surf = &bmodel->surfaces[mb->infokey - 1];
|
|
ref_shader_t *shader;
|
|
|
|
if( RI.currentmodel != r_worldmodel )
|
|
{
|
|
Matrix_TransformVector( RI.currententity->axis, surf->origin, origin );
|
|
VectorAdd( origin, RI.currententity->origin, origin );
|
|
}
|
|
else
|
|
{
|
|
VectorCopy( surf->origin, origin );
|
|
}
|
|
R_TransformToScreen_Vec3( origin, v );
|
|
|
|
if( v[0] < RI.refdef.viewport[0] || v[0] > RI.refdef.viewport[0] + RI.refdef.viewport[2] )
|
|
return;
|
|
if( v[1] < RI.refdef.viewport[1] || v[1] > RI.refdef.viewport[1] + RI.refdef.viewport[3] )
|
|
return;
|
|
|
|
pglReadPixels( (int)( v[0] /* + 0.5f*/ ), (int)( v[1] /* + 0.5f*/ ), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth );
|
|
if( depth + 1e-4 < v[2] )
|
|
return; // occluded
|
|
|
|
VectorCopy( origin, origin );
|
|
|
|
VectorMA( origin, down, RI.vup, point );
|
|
VectorMA( point, -left, RI.vright, spr_xyz[0] );
|
|
VectorMA( point, -right, RI.vright, spr_xyz[3] );
|
|
|
|
VectorMA( origin, up, RI.vup, point );
|
|
VectorMA( point, -left, RI.vright, spr_xyz[1] );
|
|
VectorMA( point, -right, RI.vright, spr_xyz[2] );
|
|
|
|
colorscale = 255.0 / r_flarefade->value;
|
|
Vector4Set( color,
|
|
surf->color[0] * colorscale,
|
|
surf->color[1] * colorscale,
|
|
surf->color[2] * colorscale,
|
|
255 );
|
|
for( i = 0; i < 4; i++ )
|
|
color[i] = bound( 0, color[i], 255 );
|
|
|
|
for( i = 0; i < 4; i++ )
|
|
Vector4Copy( color, spr_color[i] );
|
|
|
|
MB_NUM2SHADER( mb->shaderkey, shader );
|
|
|
|
R_PushMesh( &spr_mesh, MF_NOCULL | MF_TRIFAN | shader->features );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_PushCorona
|
|
=================
|
|
*/
|
|
static void R_PushCorona( const meshbuffer_t *mb )
|
|
{
|
|
int i;
|
|
vec4_t color;
|
|
vec3_t origin, point;
|
|
dlight_t *light = r_dlights + ( -mb->infokey - 1 );
|
|
float radius = light->intensity, colorscale;
|
|
float up = radius, down = -radius, left = -radius, right = radius;
|
|
ref_shader_t *shader;
|
|
|
|
VectorCopy( light->origin, origin );
|
|
|
|
VectorMA( origin, down, RI.vup, point );
|
|
VectorMA( point, -left, RI.vright, spr_xyz[0] );
|
|
VectorMA( point, -right, RI.vright, spr_xyz[3] );
|
|
|
|
VectorMA( origin, up, RI.vup, point );
|
|
VectorMA( point, -left, RI.vright, spr_xyz[1] );
|
|
VectorMA( point, -right, RI.vright, spr_xyz[2] );
|
|
|
|
colorscale = 255.0 * bound( 0, r_coronascale->value, 1.0 );
|
|
Vector4Set( color,
|
|
light->color[0] * colorscale,
|
|
light->color[1] * colorscale,
|
|
light->color[2] * colorscale,
|
|
255 );
|
|
for( i = 0; i < 4; i++ )
|
|
color[i] = bound( 0, color[i], 255 );
|
|
|
|
for( i = 0; i < 4; i++ )
|
|
Vector4Copy( color, spr_color[i] );
|
|
|
|
MB_NUM2SHADER( mb->shaderkey, shader );
|
|
|
|
R_PushMesh( &spr_mesh, MF_NOCULL | MF_TRIFAN | shader->features );
|
|
}
|
|
|
|
#ifdef QUAKE2_JUNK
|
|
/*
|
|
=================
|
|
R_PushSpriteModel
|
|
=================
|
|
*/
|
|
bool R_PushSpriteModel( const meshbuffer_t *mb )
|
|
{
|
|
sframe_t *frame;
|
|
smodel_t *psprite;
|
|
ref_entity_t *e = RI.currententity;
|
|
ref_model_t *model = e->model;
|
|
|
|
psprite = ( smodel_t * )model->extradata;
|
|
frame = psprite->frames + e->frame;
|
|
|
|
return R_PushSprite( mb, e->rotation, frame->origin_x, frame->origin_x - frame->width, frame->height - frame->origin_y, -frame->origin_y );
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
=================
|
|
R_PushSpritePoly
|
|
=================
|
|
*/
|
|
bool R_PushSpritePoly( const meshbuffer_t *mb )
|
|
{
|
|
ref_entity_t *e = RI.currententity;
|
|
|
|
if( ( mb->sortkey & 3 ) == MB_CORONA )
|
|
{
|
|
R_PushCorona( mb );
|
|
return false;
|
|
}
|
|
if( mb->infokey > 0 )
|
|
{
|
|
R_PushFlareSurf( mb );
|
|
return false;
|
|
}
|
|
|
|
return R_PushSprite( mb, e->rotation, -e->radius, e->radius, e->radius, -e->radius );
|
|
}
|
|
|
|
#ifdef QUAKE2_JUNK
|
|
/*
|
|
=================
|
|
R_AddSpriteModelToList
|
|
=================
|
|
*/
|
|
static void R_AddSpriteModelToList( ref_entity_t *e )
|
|
{
|
|
sframe_t *frame;
|
|
smodel_t *psprite;
|
|
ref_model_t *model = e->model;
|
|
float dist;
|
|
meshbuffer_t *mb;
|
|
|
|
if( !( psprite = ( ( smodel_t * )model->extradata ) ) )
|
|
return;
|
|
|
|
dist =
|
|
( e->origin[0] - RI.refdef.vieworg[0] ) * RI.vpn[0] +
|
|
( e->origin[1] - RI.refdef.vieworg[1] ) * RI.vpn[1] +
|
|
( e->origin[2] - RI.refdef.vieworg[2] ) * RI.vpn[2];
|
|
if( dist < 0 )
|
|
return; // cull it because we don't want to sort unneeded things
|
|
|
|
e->frame %= psprite->numframes;
|
|
frame = psprite->frames + e->frame;
|
|
|
|
if( RI.refdef.rdflags & ( RDF_PORTALINVIEW|RDF_SKYPORTALINVIEW ) || ( RI.params & RP_SKYPORTALVIEW ) )
|
|
{
|
|
if( R_VisCullSphere( e->origin, frame->radius ) )
|
|
return;
|
|
}
|
|
|
|
// select skin
|
|
if( e->customShader )
|
|
mb = R_AddMeshToList( MB_MODEL, R_FogForSphere( e->origin, frame->radius ), e->customShader, -1 );
|
|
else
|
|
mb = R_AddMeshToList( MB_MODEL, R_FogForSphere( e->origin, frame->radius ), frame->shader, -1 );
|
|
if( mb )
|
|
mb->shaderkey |= ( bound( 1, 0x4000 - (unsigned int)dist, 0x4000 - 1 ) << 12 );
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
=================
|
|
R_AddSpritePolyToList
|
|
=================
|
|
*/
|
|
static void R_AddSpritePolyToList( ref_entity_t *e )
|
|
{
|
|
float dist;
|
|
meshbuffer_t *mb;
|
|
|
|
dist =
|
|
( e->origin[0] - RI.refdef.vieworg[0] ) * RI.vpn[0] +
|
|
( e->origin[1] - RI.refdef.vieworg[1] ) * RI.vpn[1] +
|
|
( e->origin[2] - RI.refdef.vieworg[2] ) * RI.vpn[2];
|
|
if( dist < 0 )
|
|
return; // cull it because we don't want to sort unneeded things
|
|
if( RI.refdef.rdflags & ( RDF_PORTALINVIEW|RDF_SKYPORTALINVIEW ) || ( RI.params & RP_SKYPORTALVIEW ) )
|
|
{
|
|
if( R_VisCullSphere( e->origin, e->radius ) )
|
|
return;
|
|
}
|
|
|
|
mb = R_AddMeshToList( MB_SPRITE, R_FogForSphere( e->origin, e->radius ), e->customShader, -1 );
|
|
if( mb )
|
|
mb->shaderkey |= ( bound( 1, 0x4000 - (unsigned int)dist, 0x4000 - 1 ) << 12 );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_SpriteOverflow
|
|
=================
|
|
*/
|
|
bool R_SpriteOverflow( void )
|
|
{
|
|
return R_MeshOverflow( &spr_mesh );
|
|
}
|
|
|
|
//==================================================================================
|
|
|
|
static vec4_t pic_xyz[4] = { {0,0,0,1}, {0,0,0,1}, {0,0,0,1}, {0,0,0,1} };
|
|
static vec2_t pic_st[4];
|
|
static rgba_t pic_colors[4];
|
|
static mesh_t pic_mesh = { 4, pic_xyz, pic_xyz, NULL, pic_st, { 0, 0, 0, 0 }, { pic_colors, pic_colors, pic_colors, pic_colors }, 6, NULL };
|
|
static meshbuffer_t pic_mbuffer;
|
|
|
|
/*
|
|
===============
|
|
R_Set2DMode
|
|
===============
|
|
*/
|
|
void R_Set2DMode( bool enable )
|
|
{
|
|
if( enable )
|
|
{
|
|
if( glState.in2DMode )
|
|
return;
|
|
|
|
// set 2D virtual screen size
|
|
pglScissor( 0, 0, glState.width, glState.height );
|
|
pglViewport( 0, 0, glState.width, glState.height );
|
|
pglMatrixMode( GL_PROJECTION );
|
|
pglLoadIdentity();
|
|
pglOrtho( 0, glState.width, glState.height, 0, -99999, 99999 );
|
|
pglMatrixMode( GL_MODELVIEW );
|
|
pglLoadIdentity();
|
|
|
|
GL_Cull( 0 );
|
|
GL_SetState( GLSTATE_NO_DEPTH_TEST );
|
|
|
|
pglColor4f( 1, 1, 1, 1 );
|
|
|
|
glState.in2DMode = true;
|
|
RI.currententity = RI.previousentity = NULL;
|
|
RI.currentmodel = NULL;
|
|
|
|
pic_mbuffer.infokey = -1;
|
|
pic_mbuffer.shaderkey = 0;
|
|
}
|
|
else
|
|
{
|
|
if( pic_mbuffer.infokey != -1 )
|
|
{
|
|
R_RenderMeshBuffer( &pic_mbuffer );
|
|
pic_mbuffer.infokey = -1;
|
|
}
|
|
|
|
glState.in2DMode = false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_DrawStretchPic
|
|
===============
|
|
*/
|
|
void R_DrawStretchPic( float x, float y, float w, float h, float s1, float t1, float s2, float t2, shader_t shadernum )
|
|
{
|
|
int bcolor;
|
|
ref_shader_t *shader = &r_shaders[shadernum]; // FIXME: check bounds
|
|
|
|
if( !shader ) return;
|
|
|
|
// lower-left
|
|
Vector2Set( pic_xyz[0], x, y );
|
|
Vector2Set( pic_st[0], s1, t1 );
|
|
Vector4Set( pic_colors[0], R_FloatToByte( glState.draw_color[0] ), R_FloatToByte( glState.draw_color[1] ),
|
|
R_FloatToByte( glState.draw_color[2] ), R_FloatToByte( glState.draw_color[3] ));
|
|
bcolor = *(int *)pic_colors[0];
|
|
|
|
// lower-right
|
|
Vector2Set( pic_xyz[1], x+w, y );
|
|
Vector2Set( pic_st[1], s2, t1 );
|
|
*(int *)pic_colors[1] = bcolor;
|
|
|
|
// upper-right
|
|
Vector2Set( pic_xyz[2], x+w, y+h );
|
|
Vector2Set( pic_st[2], s2, t2 );
|
|
*(int *)pic_colors[2] = bcolor;
|
|
|
|
// upper-left
|
|
Vector2Set( pic_xyz[3], x, y+h );
|
|
Vector2Set( pic_st[3], s1, t2 );
|
|
*(int *)pic_colors[3] = bcolor;
|
|
|
|
if( pic_mbuffer.shaderkey != (int)shader->sortkey || -pic_mbuffer.infokey-1+4 > MAX_ARRAY_VERTS )
|
|
{
|
|
if( pic_mbuffer.shaderkey )
|
|
{
|
|
pic_mbuffer.infokey = -1;
|
|
R_RenderMeshBuffer( &pic_mbuffer );
|
|
}
|
|
}
|
|
|
|
pic_mbuffer.infokey -= 4;
|
|
pic_mbuffer.shaderkey = shader->sortkey;
|
|
|
|
// upload video right before rendering
|
|
if( shader->flags & SHADER_VIDEOMAP )
|
|
R_UploadCinematicShader( shader );
|
|
|
|
R_PushMesh( &pic_mesh, MF_TRIFAN | shader->features | ( r_shownormals->integer ? MF_NORMALS : 0 ) );
|
|
}
|
|
|
|
/*
|
|
=============
|
|
R_DrawStretchRaw
|
|
=============
|
|
*/
|
|
void R_DrawStretchRaw( int x, int y, int w, int h, int cols, int rows, byte *data, bool redraw )
|
|
{
|
|
int samples = 3;
|
|
|
|
GL_Bind( 0, r_cintexture );
|
|
|
|
R_Upload32( &data, cols, rows, IT_CINEMATIC, NULL, NULL, &samples, ( cols == r_cintexture->width && rows == r_cintexture->height ) );
|
|
|
|
r_cintexture->width = cols;
|
|
r_cintexture->height = rows;
|
|
|
|
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();
|
|
}
|
|
|
|
/*
|
|
============
|
|
R_PolyBlend
|
|
============
|
|
*/
|
|
static void R_PolyBlend( void )
|
|
{
|
|
if( !r_polyblend->integer )
|
|
return;
|
|
if( RI.refdef.blend[3] < 0.01f )
|
|
return;
|
|
|
|
pglMatrixMode( GL_PROJECTION );
|
|
pglLoadIdentity();
|
|
pglOrtho( 0, 1, 1, 0, -99999, 99999 );
|
|
|
|
pglMatrixMode( GL_MODELVIEW );
|
|
pglLoadIdentity();
|
|
|
|
GL_Cull( 0 );
|
|
GL_SetState( GLSTATE_NO_DEPTH_TEST|GLSTATE_SRCBLEND_SRC_ALPHA|GLSTATE_DSTBLEND_ONE_MINUS_SRC_ALPHA );
|
|
|
|
pglDisable( GL_TEXTURE_2D );
|
|
|
|
pglColor4fv( RI.refdef.blend );
|
|
|
|
pglBegin( GL_TRIANGLES );
|
|
pglVertex2f( -5, -5 );
|
|
pglVertex2f( 10, -5 );
|
|
pglVertex2f( -5, 10 );
|
|
pglEnd();
|
|
|
|
pglEnable( GL_TEXTURE_2D );
|
|
|
|
pglColor4f( 1, 1, 1, 1 );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_ApplySoftwareGamma
|
|
===============
|
|
*/
|
|
static void R_ApplySoftwareGamma( void )
|
|
{
|
|
double f, div;
|
|
|
|
// apply software gamma
|
|
if( !r_ignorehwgamma->integer )
|
|
return;
|
|
|
|
pglMatrixMode( GL_PROJECTION );
|
|
pglLoadIdentity();
|
|
pglOrtho( 0, 1, 1, 0, -99999, 99999 );
|
|
|
|
pglMatrixMode( GL_MODELVIEW );
|
|
pglLoadIdentity();
|
|
|
|
GL_Cull( 0 );
|
|
GL_SetState( GLSTATE_NO_DEPTH_TEST | GLSTATE_SRCBLEND_DST_COLOR | GLSTATE_DSTBLEND_ONE );
|
|
|
|
pglDisable( GL_TEXTURE_2D );
|
|
|
|
if( r_overbrightbits->integer > 0 )
|
|
div = 0.5 * (double)( 1 << r_overbrightbits->integer );
|
|
else
|
|
div = 0.5;
|
|
f = div + r_gamma->value;
|
|
f = bound( 0.1f, f, 5.0f );
|
|
|
|
pglBegin( GL_TRIANGLES );
|
|
|
|
while( f >= 1.01f )
|
|
{
|
|
if( f >= 2 )
|
|
pglColor4f( 1.0f, 1.0f, 1.0f, 1.0f );
|
|
else
|
|
pglColor4f( f - 1.0f, f - 1.0f, f - 1.0f, 1.0f );
|
|
|
|
pglVertex2f( -5, -5 );
|
|
pglVertex2f( 10, -5 );
|
|
pglVertex2f( -5, 10 );
|
|
f *= 0.5;
|
|
}
|
|
|
|
pglEnd();
|
|
|
|
pglEnable( GL_TEXTURE_2D );
|
|
|
|
pglColor4f( 1, 1, 1, 1 );
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
#ifdef HARDWARE_OUTLINES
|
|
|
|
static ref_shader_t *r_outlineShader;
|
|
|
|
/*
|
|
===============
|
|
R_InitOutlines
|
|
===============
|
|
*/
|
|
void R_InitOutlines( void )
|
|
{
|
|
r_outlineShader = R_LoadShader( "celloutline/default", SHADER_OUTLINE, false, 0, SHADER_INVALID );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_AddModelMeshOutline
|
|
===============
|
|
*/
|
|
void R_AddModelMeshOutline( unsigned int modhandle, mfog_t *fog, int meshnum )
|
|
{
|
|
meshbuffer_t *mb = R_AddMeshToList( MB_MODEL, fog, r_outlineShader, -( meshnum+1 ) );
|
|
if( mb )
|
|
mb->LODModelHandle = modhandle;
|
|
}
|
|
#endif
|
|
|
|
//=======================================================================
|
|
|
|
/*
|
|
===============
|
|
R_SetupFrustum
|
|
===============
|
|
*/
|
|
static void R_SetupFrustum( void )
|
|
{
|
|
int i;
|
|
vec3_t farPoint;
|
|
|
|
// 0 - left
|
|
// 1 - right
|
|
// 2 - down
|
|
// 3 - up
|
|
// 4 - farclip
|
|
|
|
// rotate RI.vpn right by FOV_X/2 degrees
|
|
RotatePointAroundVector( RI.frustum[0].normal, RI.vup, RI.vpn, -( 90-RI.refdef.fov_x / 2 ) );
|
|
// rotate RI.vpn left by FOV_X/2 degrees
|
|
RotatePointAroundVector( RI.frustum[1].normal, RI.vup, RI.vpn, 90-RI.refdef.fov_x / 2 );
|
|
// rotate RI.vpn up by FOV_X/2 degrees
|
|
RotatePointAroundVector( RI.frustum[2].normal, RI.vright, RI.vpn, 90-RI.refdef.fov_y / 2 );
|
|
// rotate RI.vpn down by FOV_X/2 degrees
|
|
RotatePointAroundVector( RI.frustum[3].normal, RI.vright, RI.vpn, -( 90 - RI.refdef.fov_y / 2 ) );
|
|
// negate forward vector
|
|
VectorNegate( RI.vpn, RI.frustum[4].normal );
|
|
|
|
for( i = 0; i < 4; i++ )
|
|
{
|
|
RI.frustum[i].dist = DotProduct( RI.viewOrigin, RI.frustum[i].normal );
|
|
PlaneClassify( &RI.frustum[i] );
|
|
}
|
|
|
|
VectorMA( RI.viewOrigin, RI.farClip, RI.vpn, farPoint );
|
|
RI.frustum[i].dist = DotProduct( farPoint, RI.frustum[i].normal );
|
|
PlaneClassify( &RI.frustum[i] );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_FarClip
|
|
===============
|
|
*/
|
|
static float R_FarClip( void )
|
|
{
|
|
float farclip_dist;
|
|
|
|
if( r_worldmodel && !( RI.refdef.rdflags & RDF_NOWORLDMODEL ) )
|
|
{
|
|
int i;
|
|
float dist;
|
|
vec3_t tmp;
|
|
|
|
farclip_dist = 0;
|
|
for( i = 0; i < 8; i++ )
|
|
{
|
|
tmp[0] = ( ( i & 1 ) ? RI.visMins[0] : RI.visMaxs[0] );
|
|
tmp[1] = ( ( i & 2 ) ? RI.visMins[1] : RI.visMaxs[1] );
|
|
tmp[2] = ( ( i & 4 ) ? RI.visMins[2] : RI.visMaxs[2] );
|
|
|
|
dist = VectorDistance2( tmp, RI.viewOrigin );
|
|
farclip_dist = max( farclip_dist, dist );
|
|
}
|
|
|
|
farclip_dist = sqrt( farclip_dist );
|
|
|
|
if( r_worldbrushmodel->globalfog )
|
|
{
|
|
float fogdist = r_worldbrushmodel->globalfog->shader->fog_dist;
|
|
if( farclip_dist > fogdist )
|
|
farclip_dist = fogdist;
|
|
else
|
|
RI.clipFlags &= ~16;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
farclip_dist = 2048;
|
|
}
|
|
|
|
return max( r_farclip_min, farclip_dist ) + r_farclip_bias;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
R_SetupProjectionMatrix
|
|
=============
|
|
*/
|
|
static void R_SetupProjectionMatrix( const ref_params_t *rd, mat4x4_t m )
|
|
{
|
|
GLdouble xMin, xMax, yMin, yMax, zNear, zFar;
|
|
|
|
if( rd->rdflags & RDF_NOWORLDMODEL )
|
|
RI.farClip = 2048;
|
|
else
|
|
RI.farClip = R_FarClip();
|
|
|
|
zNear = Z_NEAR;
|
|
zFar = RI.farClip;
|
|
|
|
yMax = zNear *tan( rd->fov_y *M_PI / 360.0 );
|
|
yMin = -yMax;
|
|
|
|
xMax = zNear *tan( rd->fov_x *M_PI / 360.0 );
|
|
xMin = -xMax;
|
|
|
|
xMin += -( 2 * glState.cameraSeparation ) / zNear;
|
|
xMax += -( 2 * glState.cameraSeparation ) / zNear;
|
|
|
|
m[0] = ( 2.0 * zNear ) / ( xMax - xMin );
|
|
m[1] = 0.0f;
|
|
m[2] = 0.0f;
|
|
m[3] = 0.0f;
|
|
m[4] = 0.0f;
|
|
m[5] = ( 2.0 * zNear ) / ( yMax - yMin );
|
|
m[6] = 0.0f;
|
|
m[7] = 0.0f;
|
|
m[8] = ( xMax + xMin ) / ( xMax - xMin );
|
|
m[9] = ( yMax + yMin ) / ( yMax - yMin );
|
|
m[10] = -( zFar + zNear ) / ( zFar - zNear );
|
|
m[11] = -1.0f;
|
|
m[12] = 0.0f;
|
|
m[13] = 0.0f;
|
|
m[14] = -( 2.0 * zFar * zNear ) / ( zFar - zNear );
|
|
m[15] = 0.0f;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
R_SetupModelviewMatrix
|
|
=============
|
|
*/
|
|
static void R_SetupModelviewMatrix( const ref_params_t *rd, mat4x4_t m )
|
|
{
|
|
#if 0
|
|
Matrix4_Identity( m );
|
|
Matrix4_Rotate( m, -90, 1, 0, 0 );
|
|
Matrix4_Rotate( m, 90, 0, 0, 1 );
|
|
#else
|
|
Vector4Set( &m[0], 0, 0, -1, 0 );
|
|
Vector4Set( &m[4], -1, 0, 0, 0 );
|
|
Vector4Set( &m[8], 0, 1, 0, 0 );
|
|
Vector4Set( &m[12], 0, 0, 0, 1 );
|
|
#endif
|
|
Matrix4_Rotate( m, -rd->viewangles[2], 1, 0, 0 );
|
|
Matrix4_Rotate( m, -rd->viewangles[0], 0, 1, 0 );
|
|
Matrix4_Rotate( m, -rd->viewangles[1], 0, 0, 1 );
|
|
Matrix4_Translate( m, -rd->vieworg[0], -rd->vieworg[1], -rd->vieworg[2] );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_SetupFrame
|
|
===============
|
|
*/
|
|
static void R_SetupFrame( void )
|
|
{
|
|
mleaf_t *leaf;
|
|
|
|
// build the transformation matrix for the given view angles
|
|
VectorCopy( RI.refdef.vieworg, RI.viewOrigin );
|
|
AngleVectors( RI.refdef.viewangles, RI.viewAxis[0], RI.viewAxis[1], RI.viewAxis[2] );
|
|
RI.vpn = RI.viewAxis[0];
|
|
RI.vright = RI.viewAxis[1];
|
|
RI.vup = RI.viewAxis[2];
|
|
|
|
if( RI.params & RP_SHADOWMAPVIEW )
|
|
return;
|
|
|
|
r_framecount++;
|
|
|
|
RI.lod_dist_scale_for_fov = tan( RI.refdef.fov_x * ( M_PI/180 ) * 0.5f );
|
|
|
|
// current viewcluster
|
|
if( !( RI.refdef.rdflags & RDF_NOWORLDMODEL ) )
|
|
{
|
|
VectorCopy( r_worldmodel->mins, RI.visMins );
|
|
VectorCopy( r_worldmodel->maxs, RI.visMaxs );
|
|
|
|
if( !( RI.params & RP_OLDVIEWCLUSTER ) )
|
|
{
|
|
r_oldviewcluster = r_viewcluster;
|
|
leaf = Mod_PointInLeaf( RI.pvsOrigin, r_worldmodel );
|
|
r_viewcluster = leaf->cluster;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_SetupViewMatrices
|
|
===============
|
|
*/
|
|
static void R_SetupViewMatrices( void )
|
|
{
|
|
R_SetupModelviewMatrix( &RI.refdef, RI.worldviewMatrix );
|
|
if( RI.params & RP_SHADOWMAPVIEW )
|
|
{
|
|
int i;
|
|
float x1, x2, y1, y2;
|
|
int ix1, ix2, iy1, iy2;
|
|
int sizex = RI.refdef.viewport[2], sizey = RI.refdef.viewport[3];
|
|
int diffx, diffy;
|
|
shadowGroup_t *group = RI.shadowGroup;
|
|
|
|
R_SetupProjectionMatrix( &RI.refdef, RI.projectionMatrix );
|
|
Matrix4_Multiply( RI.projectionMatrix, RI.worldviewMatrix, RI.worldviewProjectionMatrix );
|
|
|
|
// compute optimal fov to increase depth precision (so that shadow group objects are
|
|
// as close to the nearplane as possible)
|
|
// note that it's suboptimal to use bbox calculated in worldspace (FIXME)
|
|
x1 = y1 = 999999;
|
|
x2 = y2 = -999999;
|
|
for( i = 0; i < 8; i++ )
|
|
{ // compute and rotate a full bounding box
|
|
vec3_t v, tmp;
|
|
|
|
tmp[0] = ( ( i & 1 ) ? group->mins[0] : group->maxs[0] );
|
|
tmp[1] = ( ( i & 2 ) ? group->mins[1] : group->maxs[1] );
|
|
tmp[2] = ( ( i & 4 ) ? group->mins[2] : group->maxs[2] );
|
|
|
|
// transform to screen
|
|
R_TransformToScreen_Vec3( tmp, v );
|
|
x1 = min( x1, v[0] ); y1 = min( y1, v[1] );
|
|
x2 = max( x2, v[0] ); y2 = max( y2, v[1] );
|
|
}
|
|
|
|
// give it 1 pixel gap on both sides
|
|
ix1 = x1 - 1.0f; ix2 = x2 + 1.0f;
|
|
iy1 = y1 - 1.0f; iy2 = y2 + 1.0f;
|
|
|
|
diffx = sizex - min( ix1, sizex - ix2 ) * 2;
|
|
diffy = sizey - min( iy1, sizey - iy2 ) * 2;
|
|
|
|
// adjust fov
|
|
RI.refdef.fov_x = 2 * RAD2DEG( atan( (float)diffx / (float)sizex ) );
|
|
RI.refdef.fov_y = 2 * RAD2DEG( atan( (float)diffy / (float)sizey ) );
|
|
}
|
|
R_SetupProjectionMatrix( &RI.refdef, RI.projectionMatrix );
|
|
if( RI.params & RP_MIRRORVIEW )
|
|
RI.projectionMatrix[0] = -RI.projectionMatrix[0];
|
|
Matrix4_Multiply( RI.projectionMatrix, RI.worldviewMatrix, RI.worldviewProjectionMatrix );
|
|
}
|
|
|
|
/*
|
|
=============
|
|
R_Clear
|
|
=============
|
|
*/
|
|
static void R_Clear( int bitMask )
|
|
{
|
|
int bits;
|
|
|
|
bits = GL_DEPTH_BUFFER_BIT;
|
|
|
|
if( !( RI.refdef.rdflags & RDF_NOWORLDMODEL ) && r_fastsky->integer )
|
|
bits |= GL_COLOR_BUFFER_BIT;
|
|
if( glState.stencilEnabled && ( r_shadows->integer >= SHADOW_PLANAR ) )
|
|
bits |= GL_STENCIL_BUFFER_BIT;
|
|
|
|
bits &= bitMask;
|
|
|
|
if( bits & GL_STENCIL_BUFFER_BIT )
|
|
pglClearStencil( 128 );
|
|
|
|
if( bits & GL_COLOR_BUFFER_BIT )
|
|
{
|
|
byte *color = r_worldmodel && !( RI.refdef.rdflags & RDF_NOWORLDMODEL ) && r_worldbrushmodel->globalfog ?
|
|
r_worldbrushmodel->globalfog->shader->fog_color : mapConfig.environmentColor;
|
|
pglClearColor( (float)color[0]*( 1.0/255.0 ), (float)color[1]*( 1.0/255.0 ), (float)color[2]*( 1.0/255.0 ), 1 );
|
|
}
|
|
|
|
pglClear( bits );
|
|
|
|
gldepthmin = 0;
|
|
gldepthmax = 1;
|
|
pglDepthRange( gldepthmin, gldepthmax );
|
|
}
|
|
|
|
/*
|
|
=============
|
|
R_SetupGL
|
|
=============
|
|
*/
|
|
static void R_SetupGL( void )
|
|
{
|
|
pglScissor( RI.scissor[0], RI.scissor[1], RI.scissor[2], RI.scissor[3] );
|
|
pglViewport( RI.viewport[0], RI.viewport[1], RI.viewport[2], RI.viewport[3] );
|
|
|
|
pglMatrixMode( GL_PROJECTION );
|
|
pglLoadMatrixf( RI.projectionMatrix );
|
|
|
|
pglMatrixMode( GL_MODELVIEW );
|
|
pglLoadMatrixf( RI.worldviewMatrix );
|
|
|
|
if( RI.params & RP_CLIPPLANE )
|
|
{
|
|
GLdouble clip[4];
|
|
cplane_t *p = &RI.clipPlane;
|
|
|
|
clip[0] = p->normal[0];
|
|
clip[1] = p->normal[1];
|
|
clip[2] = p->normal[2];
|
|
clip[3] = -p->dist;
|
|
|
|
pglClipPlane( GL_CLIP_PLANE0, clip );
|
|
pglEnable( GL_CLIP_PLANE0 );
|
|
}
|
|
|
|
if( RI.params & RP_FLIPFRONTFACE )
|
|
GL_FrontFace( !glState.frontFace );
|
|
|
|
if( RI.params & RP_SHADOWMAPVIEW )
|
|
{
|
|
pglShadeModel( GL_FLAT );
|
|
pglColorMask( 0, 0, 0, 0 );
|
|
pglPolygonOffset( 1, 4 );
|
|
if( prevRI.params & RP_CLIPPLANE )
|
|
pglDisable( GL_CLIP_PLANE0 );
|
|
}
|
|
|
|
GL_Cull( GL_FRONT );
|
|
GL_SetState( GLSTATE_DEPTHWRITE );
|
|
}
|
|
|
|
/*
|
|
=============
|
|
R_EndGL
|
|
=============
|
|
*/
|
|
static void R_EndGL( void )
|
|
{
|
|
if( RI.params & RP_SHADOWMAPVIEW )
|
|
{
|
|
pglPolygonOffset( -1, -2 );
|
|
pglColorMask( 1, 1, 1, 1 );
|
|
pglShadeModel( GL_SMOOTH );
|
|
if( prevRI.params & RP_CLIPPLANE )
|
|
pglEnable( GL_CLIP_PLANE0 );
|
|
}
|
|
|
|
if( RI.params & RP_FLIPFRONTFACE )
|
|
GL_FrontFace( !glState.frontFace );
|
|
|
|
if( RI.params & RP_CLIPPLANE )
|
|
pglDisable( GL_CLIP_PLANE0 );
|
|
}
|
|
|
|
|
|
/*
|
|
=============
|
|
R_CategorizeEntities
|
|
=============
|
|
*/
|
|
static void R_CategorizeEntities( void )
|
|
{
|
|
unsigned int i;
|
|
|
|
r_numnullentities = 0;
|
|
r_numbmodelentities = 0;
|
|
|
|
if( !r_drawentities->integer )
|
|
return;
|
|
|
|
for( i = 1; i < r_numEntities; i++ )
|
|
{
|
|
RI.previousentity = RI.currententity;
|
|
RI.currententity = &r_entities[i];
|
|
|
|
if( RI.currententity->rtype != RT_MODEL )
|
|
continue;
|
|
|
|
RI.currentmodel = RI.currententity->model;
|
|
if( !RI.currentmodel )
|
|
{
|
|
r_nullentities[r_numnullentities++] = RI.currententity;
|
|
continue;
|
|
}
|
|
|
|
switch( RI.currentmodel->type )
|
|
{
|
|
case mod_brush:
|
|
r_bmodelentities[r_numbmodelentities++] = RI.currententity;
|
|
break;
|
|
case mod_alias:
|
|
case mod_studio:
|
|
if( !( RI.currententity->renderfx & ( RF_NOSHADOW|RF_PLANARSHADOW ) ) )
|
|
R_AddShadowCaster( RI.currententity ); // build groups and mark shadow casters
|
|
break;
|
|
#ifdef QUAKE2_JUNK
|
|
case mod_sprite:
|
|
break;
|
|
#endif
|
|
default:
|
|
Host_Error( "%s: bad modeltype\n", RI.currentmodel->name );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
R_CullEntities
|
|
=============
|
|
*/
|
|
static void R_CullEntities( void )
|
|
{
|
|
unsigned int i;
|
|
ref_entity_t *e;
|
|
bool culled;
|
|
|
|
memset( r_entVisBits, 0, sizeof( r_entVisBits ) );
|
|
if( !r_drawentities->integer )
|
|
return;
|
|
|
|
for( i = 1; i < r_numEntities; i++ )
|
|
{
|
|
RI.previousentity = RI.currententity;
|
|
RI.currententity = e = &r_entities[i];
|
|
culled = true;
|
|
|
|
switch( e->rtype )
|
|
{
|
|
case RT_MODEL:
|
|
if( !e->model )
|
|
break;
|
|
switch( e->model->type )
|
|
{
|
|
case mod_alias:
|
|
culled = R_CullAliasModel( e );
|
|
break;
|
|
case mod_studio:
|
|
culled = R_CullSkeletalModel( e );
|
|
break;
|
|
case mod_brush:
|
|
culled = R_CullBrushModel( e );
|
|
break;
|
|
#ifdef QUAKE2_JUNK
|
|
case mod_sprite:
|
|
culled = false;
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case RT_SPRITE:
|
|
culled = ( e->radius <= 0 ) || ( e->customShader == NULL );
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if( !culled )
|
|
r_entVisBits[i>>3] |= ( 1<<( i&7 ) );
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
R_DrawNullModel
|
|
=============
|
|
*/
|
|
static void R_DrawNullModel( void )
|
|
{
|
|
pglBegin( GL_LINES );
|
|
|
|
pglColor4f( 1, 0, 0, 0.5 );
|
|
pglVertex3fv( RI.currententity->origin );
|
|
pglVertex3f( RI.currententity->origin[0] + RI.currententity->axis[0][0] * 15,
|
|
RI.currententity->origin[1] + RI.currententity->axis[0][1] * 15,
|
|
RI.currententity->origin[2] + RI.currententity->axis[0][2] * 15 );
|
|
|
|
pglColor4f( 0, 1, 0, 0.5 );
|
|
pglVertex3fv( RI.currententity->origin );
|
|
pglVertex3f( RI.currententity->origin[0] - RI.currententity->axis[1][0] * 15,
|
|
RI.currententity->origin[1] - RI.currententity->axis[1][1] * 15,
|
|
RI.currententity->origin[2] - RI.currententity->axis[1][2] * 15 );
|
|
|
|
pglColor4f( 0, 0, 1, 0.5 );
|
|
pglVertex3fv( RI.currententity->origin );
|
|
pglVertex3f( RI.currententity->origin[0] + RI.currententity->axis[2][0] * 15,
|
|
RI.currententity->origin[1] + RI.currententity->axis[2][1] * 15,
|
|
RI.currententity->origin[2] + RI.currententity->axis[2][2] * 15 );
|
|
|
|
pglEnd();
|
|
}
|
|
|
|
/*
|
|
=============
|
|
R_DrawBmodelEntities
|
|
=============
|
|
*/
|
|
static void R_DrawBmodelEntities( void )
|
|
{
|
|
int i, j;
|
|
|
|
for( i = 0; i < r_numbmodelentities; i++ )
|
|
{
|
|
RI.previousentity = RI.currententity;
|
|
RI.currententity = r_bmodelentities[i];
|
|
j = RI.currententity - r_entities;
|
|
if( r_entVisBits[j>>3] & ( 1<<( j&7 ) ) )
|
|
R_AddBrushModelToList( RI.currententity );
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
R_DrawRegularEntities
|
|
=============
|
|
*/
|
|
static void R_DrawRegularEntities( void )
|
|
{
|
|
unsigned int i;
|
|
ref_entity_t *e;
|
|
bool shadowmap = ( ( RI.params & RP_SHADOWMAPVIEW ) != 0 );
|
|
|
|
for( i = 1; i < r_numEntities; i++ )
|
|
{
|
|
RI.previousentity = RI.currententity;
|
|
RI.currententity = e = &r_entities[i];
|
|
|
|
if( shadowmap )
|
|
{
|
|
if( e->flags & RF_NOSHADOW )
|
|
continue;
|
|
if( r_entShadowBits[i] & RI.shadowGroup->bit )
|
|
goto add; // shadow caster
|
|
}
|
|
if( !( r_entVisBits[i>>3] & ( 1<<( i&7 ) ) ) )
|
|
continue;
|
|
|
|
add:
|
|
switch( e->rtype )
|
|
{
|
|
case RT_MODEL:
|
|
RI.currentmodel = e->model;
|
|
switch( RI.currentmodel->type )
|
|
{
|
|
case mod_alias:
|
|
R_AddAliasModelToList( e );
|
|
break;
|
|
case mod_studio:
|
|
R_AddSkeletalModelToList( e );
|
|
break;
|
|
#ifdef QUAKE2_JUNK
|
|
case mod_sprite:
|
|
if( !shadowmap )
|
|
R_AddSpriteModelToList( e );
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case RT_SPRITE:
|
|
if( !shadowmap )
|
|
R_AddSpritePolyToList( e );
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
R_DrawNullEntities
|
|
=============
|
|
*/
|
|
static void R_DrawNullEntities( void )
|
|
{
|
|
int i;
|
|
|
|
if( !r_numnullentities )
|
|
return;
|
|
|
|
pglDisable( GL_TEXTURE_2D );
|
|
GL_SetState( GLSTATE_SRCBLEND_SRC_ALPHA|GLSTATE_DSTBLEND_ONE_MINUS_SRC_ALPHA );
|
|
|
|
// draw non-transparent first
|
|
for( i = 0; i < r_numnullentities; i++ )
|
|
{
|
|
RI.previousentity = RI.currententity;
|
|
RI.currententity = r_nullentities[i];
|
|
|
|
if( RI.params & RP_MIRRORVIEW )
|
|
{
|
|
if( RI.currententity->flags & RF_WEAPONMODEL )
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if( RI.currententity->flags & RF_VIEWERMODEL )
|
|
continue;
|
|
}
|
|
R_DrawNullModel();
|
|
}
|
|
|
|
pglEnable( GL_TEXTURE_2D );
|
|
}
|
|
|
|
/*
|
|
=============
|
|
R_DrawEntities
|
|
=============
|
|
*/
|
|
static void R_DrawEntities( void )
|
|
{
|
|
bool shadowmap = ( ( RI.params & RP_SHADOWMAPVIEW ) != 0 );
|
|
|
|
if( !r_drawentities->integer )
|
|
return;
|
|
|
|
if( !shadowmap )
|
|
{
|
|
R_CullEntities(); // mark visible entities in r_entVisBits
|
|
|
|
R_CullShadowmapGroups();
|
|
}
|
|
|
|
// we don't mark bmodel entities in RP_SHADOWMAPVIEW, only individual surfaces
|
|
R_DrawBmodelEntities();
|
|
|
|
if( OCCLUSION_QUERIES_ENABLED( RI ) )
|
|
{
|
|
R_EndOcclusionPass();
|
|
}
|
|
|
|
if( RI.params & RP_ENVVIEW )
|
|
return;
|
|
|
|
if( !shadowmap )
|
|
R_DrawShadowmaps(); // render to depth textures, mark shadowed entities and surfaces
|
|
else if( !( RI.params & RP_WORLDSURFVISIBLE ) || ( prevRI.shadowBits & RI.shadowGroup->bit ) )
|
|
return; // we're supposed to cast shadows but there are no visible surfaces for this light, so stop
|
|
// or we've already drawn and captured textures for this group
|
|
|
|
R_DrawRegularEntities();
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
R_RenderDebugSurface
|
|
===============
|
|
*/
|
|
void R_RenderDebugSurface( void )
|
|
{
|
|
trace_t tr;
|
|
vec3_t forward;
|
|
vec3_t start, end;
|
|
|
|
if( RI.params & RP_NONVIEWERREF || RI.refdef.rdflags & RDF_NOWORLDMODEL )
|
|
return;
|
|
|
|
r_debug_surface = NULL;
|
|
if( r_speeds->integer != 4 )
|
|
return;
|
|
|
|
VectorCopy( RI.vpn, forward );
|
|
VectorCopy( RI.viewOrigin, start );
|
|
VectorMA( start, 4096, forward, end );
|
|
|
|
r_debug_surface = R_TraceLine( &tr, start, end, 0 );
|
|
if( r_debug_surface && r_debug_surface->mesh && !r_showtris->integer )
|
|
{
|
|
RI.previousentity = NULL;
|
|
RI.currententity = (ref_entity_t *)tr.gp;
|
|
|
|
R_ClearMeshList( RI.meshlist );
|
|
R_AddMeshToList( MB_MODEL, NULL, r_debug_surface->shader, r_debug_surface - r_worldbrushmodel->surfaces + 1 );
|
|
R_DrawTriangleOutlines( true, false );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
R_RenderView
|
|
|
|
RI.refdef must be set before the first call
|
|
================
|
|
*/
|
|
void R_RenderView( const ref_params_t *fd )
|
|
{
|
|
int msec = 0;
|
|
bool shadowMap = RI.params & RP_SHADOWMAPVIEW ? true : false;
|
|
|
|
RI.refdef = *fd;
|
|
|
|
R_ClearMeshList( RI.meshlist );
|
|
|
|
if( !r_worldmodel && !( RI.refdef.rdflags & RDF_NOWORLDMODEL ) )
|
|
Host_Error( "R_RenderView: NULL worldmodel\n" );
|
|
|
|
R_SetupFrame();
|
|
|
|
// we know the farclip so adjust fov before setting up the frustum
|
|
if( shadowMap )
|
|
{
|
|
R_SetupViewMatrices();
|
|
}
|
|
else if( OCCLUSION_QUERIES_ENABLED( RI ) )
|
|
{
|
|
R_SetupViewMatrices();
|
|
|
|
R_SetupGL();
|
|
|
|
R_Clear( ~( GL_STENCIL_BUFFER_BIT|GL_COLOR_BUFFER_BIT ) );
|
|
|
|
R_BeginOcclusionPass();
|
|
}
|
|
|
|
R_SetupFrustum();
|
|
|
|
if( r_speeds->integer )
|
|
msec = Sys_Milliseconds();
|
|
R_MarkLeaves();
|
|
if( r_speeds->integer )
|
|
r_mark_leaves += ( Sys_Milliseconds() - msec );
|
|
|
|
R_DrawWorld();
|
|
|
|
// we know the the farclip at this point after determining visible world leafs
|
|
if( !shadowMap )
|
|
{
|
|
R_SetupViewMatrices();
|
|
|
|
R_DrawCoronas();
|
|
|
|
if( r_speeds->integer )
|
|
msec = Sys_Milliseconds();
|
|
R_AddPolysToList();
|
|
if( r_speeds->integer )
|
|
r_add_polys += ( Sys_Milliseconds() - msec );
|
|
}
|
|
|
|
if( r_speeds->integer )
|
|
msec = Sys_Milliseconds();
|
|
R_DrawEntities();
|
|
if( r_speeds->integer )
|
|
r_add_entities += ( Sys_Milliseconds() - msec );
|
|
|
|
if( shadowMap )
|
|
{
|
|
if( !( RI.params & RP_WORLDSURFVISIBLE ) )
|
|
return; // we didn't cast shadows on anything, so stop
|
|
if( prevRI.shadowBits & RI.shadowGroup->bit )
|
|
return; // already drawn
|
|
}
|
|
|
|
if( r_speeds->integer )
|
|
msec = Sys_Milliseconds();
|
|
R_SortMeshes();
|
|
if( r_speeds->integer )
|
|
r_sort_meshes += ( Sys_Milliseconds() - msec );
|
|
|
|
R_DrawPortals();
|
|
|
|
if( r_portalonly->integer && !( RI.params & ( RP_MIRRORVIEW|RP_PORTALVIEW ) ) )
|
|
return;
|
|
|
|
R_SetupGL();
|
|
|
|
R_Clear( shadowMap ? ~( GL_STENCIL_BUFFER_BIT|GL_COLOR_BUFFER_BIT ) : ~0 );
|
|
|
|
if( r_speeds->integer )
|
|
msec = Sys_Milliseconds();
|
|
R_DrawMeshes();
|
|
if( r_speeds->integer )
|
|
r_draw_meshes += ( Sys_Milliseconds() - msec );
|
|
|
|
R_BackendCleanUpTextureUnits();
|
|
|
|
R_DrawTriangleOutlines( r_showtris->integer ? true : false, r_shownormals->integer ? true : false );
|
|
|
|
R_RenderDebugSurface ();
|
|
|
|
R_DrawPhysDebug ();
|
|
|
|
R_DrawNullEntities();
|
|
|
|
R_EndGL();
|
|
}
|
|
|
|
//=======================================================================
|
|
|
|
/*
|
|
===============
|
|
R_UpdateSwapInterval
|
|
===============
|
|
*/
|
|
static void R_UpdateSwapInterval( void )
|
|
{
|
|
if( r_swapinterval->modified )
|
|
{
|
|
r_swapinterval->modified = false;
|
|
|
|
if( !glState.stereoEnabled )
|
|
{
|
|
if( pglSwapInterval )
|
|
pglSwapInterval( r_swapinterval->integer );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_UpdateHWGamma
|
|
===============
|
|
*/
|
|
static void R_UpdateHWGamma( void )
|
|
{
|
|
int i, v;
|
|
double invGamma, div;
|
|
unsigned short gammaRamp[3*256];
|
|
|
|
if( !glState.hwGamma )
|
|
return;
|
|
|
|
invGamma = 1.0 / bound( 0.5, r_gamma->value, 3 );
|
|
div = (double)( 1 << max( 0, r_overbrightbits->integer ) ) / 255.5;
|
|
|
|
for( i = 0; i < 256; i++ )
|
|
{
|
|
v = ( int )( 65535.0 * pow( ( (double)i + 0.5 ) * div, invGamma ) + 0.5 );
|
|
gammaRamp[i] = gammaRamp[i + 256] = gammaRamp[i + 512] = ( ( unsigned short )bound( 0, v, 65535 ) );
|
|
}
|
|
|
|
GLimp_SetGammaRamp( 256, gammaRamp );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_BeginFrame
|
|
===============
|
|
*/
|
|
void R_BeginFrame( void )
|
|
{
|
|
glState.cameraSeparation = 0.0f;
|
|
|
|
if( gl_finish->integer && gl_delayfinish->integer )
|
|
{
|
|
// flush any remaining 2D bits
|
|
// R_Set2DMode( false );
|
|
|
|
// apply software gamma
|
|
// R_ApplySoftwareGamma ();
|
|
|
|
pglFinish();
|
|
|
|
GLimp_EndFrame();
|
|
}
|
|
|
|
GLimp_BeginFrame();
|
|
|
|
if( r_environment_color->modified )
|
|
{
|
|
VectorClear( mapConfig.environmentColor );
|
|
mapConfig.environmentColor[3] = 255;
|
|
|
|
if( r_environment_color->string[0] )
|
|
{
|
|
int r, g, b;
|
|
|
|
if( sscanf( r_environment_color->string, "%i %i %i", &r, &g, &b ) == 3 )
|
|
{
|
|
mapConfig.environmentColor[0] = bound( 0, r, 255 );
|
|
mapConfig.environmentColor[1] = bound( 0, g, 255 );
|
|
mapConfig.environmentColor[2] = bound( 0, b, 255 );
|
|
}
|
|
else
|
|
{
|
|
Cvar_Set( "r_environment_color", "" );
|
|
}
|
|
}
|
|
|
|
r_environment_color->modified = false;
|
|
}
|
|
|
|
if( r_clear->integer )
|
|
{
|
|
rgba_t color;
|
|
|
|
Vector4Copy( mapConfig.environmentColor, color );
|
|
pglClearColor( color[0]*( 1.0/255.0 ), color[1]*( 1.0/255.0 ), color[2]*( 1.0/255.0 ), 1 );
|
|
pglClear( GL_COLOR_BUFFER_BIT );
|
|
}
|
|
|
|
// update gamma
|
|
if( r_gamma->modified )
|
|
{
|
|
r_gamma->modified = false;
|
|
R_UpdateHWGamma();
|
|
}
|
|
|
|
// run cinematic passes on shaders
|
|
R_RunAllCinematics();
|
|
|
|
// go into 2D mode
|
|
R_Set2DMode( true );
|
|
|
|
// draw buffer stuff
|
|
if( gl_drawbuffer->modified )
|
|
{
|
|
gl_drawbuffer->modified = false;
|
|
|
|
if( glState.cameraSeparation == 0 || !glState.stereoEnabled )
|
|
{
|
|
if( com.stricmp( gl_drawbuffer->string, "GL_FRONT" ) == 0 )
|
|
pglDrawBuffer( GL_FRONT );
|
|
else
|
|
pglDrawBuffer( GL_BACK );
|
|
}
|
|
}
|
|
|
|
// texturemode stuff
|
|
if( r_texturemode->modified )
|
|
{
|
|
R_TextureMode( r_texturemode->string );
|
|
r_texturemode->modified = false;
|
|
}
|
|
|
|
// swapinterval stuff
|
|
R_UpdateSwapInterval();
|
|
}
|
|
|
|
|
|
/*
|
|
====================
|
|
R_ClearScene
|
|
====================
|
|
*/
|
|
void R_ClearScene( void )
|
|
{
|
|
r_numEntities = 1;
|
|
r_numDlights = 0;
|
|
r_numPolys = 0;
|
|
RI.previousentity = NULL;
|
|
RI.currententity = r_worldent;
|
|
RI.currentmodel = r_worldmodel;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
R_AddPolyToScene
|
|
=====================
|
|
*/
|
|
bool R_AddPolyToScene( const poly_t *poly )
|
|
{
|
|
if(( r_numPolys < MAX_POLYS ) && poly && poly->numverts )
|
|
{
|
|
poly_t *dp = &r_polys[r_numPolys++];
|
|
|
|
*dp = *poly;
|
|
if( dp->numverts > MAX_POLY_VERTS )
|
|
dp->numverts = MAX_POLY_VERTS;
|
|
dp->shader = &r_shaders[poly->shadernum];
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
R_AddLightStyleToScene
|
|
=====================
|
|
*/
|
|
void R_AddLightStyleToScene( int style, float r, float g, float b )
|
|
{
|
|
lightstyle_t *ls;
|
|
|
|
if( style < 0 || style > MAX_LIGHTSTYLES )
|
|
Host_Error( "R_AddLightStyleToScene: bad light style %i\n", style );
|
|
|
|
ls = &r_lightStyles[style];
|
|
ls->rgb[0] = max( 0, r );
|
|
ls->rgb[1] = max( 0, g );
|
|
ls->rgb[2] = max( 0, b );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_RenderScene
|
|
===============
|
|
*/
|
|
void R_RenderScene( const ref_params_t *fd )
|
|
{
|
|
// flush any remaining 2D bits
|
|
R_Set2DMode( false );
|
|
|
|
if( r_norefresh->integer )
|
|
return;
|
|
|
|
R_BackendStartFrame();
|
|
|
|
if( !( fd->rdflags & RDF_NOWORLDMODEL ) )
|
|
{
|
|
r_lastRefdef = *fd;
|
|
}
|
|
|
|
c_brush_polys = 0;
|
|
c_world_leafs = 0;
|
|
|
|
r_mark_leaves =
|
|
r_add_polys =
|
|
r_add_entities =
|
|
r_sort_meshes =
|
|
r_draw_meshes =
|
|
r_world_node = 0;
|
|
|
|
RI.params = RP_NONE;
|
|
RI.refdef = *fd;
|
|
RI.farClip = 0;
|
|
RI.clipFlags = 15;
|
|
if( r_worldmodel && !( RI.refdef.rdflags & RDF_NOWORLDMODEL ) && r_worldbrushmodel->globalfog )
|
|
{
|
|
RI.farClip = r_worldbrushmodel->globalfog->shader->fog_dist;
|
|
RI.farClip = max( r_farclip_min, RI.farClip ) + r_farclip_bias;
|
|
RI.clipFlags |= 16;
|
|
}
|
|
RI.meshlist = &r_worldlist;
|
|
RI.shadowBits = 0;
|
|
RI.shadowGroup = NULL;
|
|
|
|
// adjust field of view for widescreen
|
|
if( glState.wideScreen && !( fd->rdflags & RDF_NOFOVADJUSTMENT ) )
|
|
AdjustFov( &RI.refdef.fov_x, &RI.refdef.fov_y, glState.width, glState.height, false );
|
|
|
|
Vector4Set( RI.scissor, fd->viewport[0], glState.height - fd->viewport[3] - fd->viewport[1], fd->viewport[2], fd->viewport[3] );
|
|
Vector4Set( RI.viewport, fd->viewport[0], glState.height - fd->viewport[3] - fd->viewport[1], fd->viewport[2], fd->viewport[3] );
|
|
VectorCopy( fd->vieworg, RI.pvsOrigin );
|
|
VectorCopy( fd->vieworg, RI.lodOrigin );
|
|
|
|
if( gl_finish->integer && !gl_delayfinish->integer && !( fd->rdflags & RDF_NOWORLDMODEL ) )
|
|
pglFinish();
|
|
|
|
R_ClearShadowmaps();
|
|
|
|
R_CategorizeEntities();
|
|
|
|
R_RenderView( fd );
|
|
|
|
R_BloomBlend( fd );
|
|
|
|
R_PolyBlend();
|
|
|
|
R_BackendEndFrame();
|
|
|
|
R_Set2DMode( true );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_BeginFrame
|
|
===============
|
|
*/
|
|
void R_EndFrame( void )
|
|
{
|
|
// flush any remaining 2D bits
|
|
R_Set2DMode( false );
|
|
|
|
// cleanup texture units
|
|
R_BackendCleanUpTextureUnits();
|
|
|
|
// apply software gamma
|
|
R_ApplySoftwareGamma();
|
|
|
|
// free temporary image buffers
|
|
R_FreeImageBuffers ();
|
|
|
|
if( gl_finish->integer && gl_delayfinish->integer )
|
|
{
|
|
pglFlush();
|
|
return;
|
|
}
|
|
|
|
GLimp_EndFrame();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_SpeedsMessage
|
|
===============
|
|
*/
|
|
const char *R_SpeedsMessage( char *out, size_t size )
|
|
{
|
|
if( out )
|
|
com.strncpy( out, r_speeds_msg, size );
|
|
return out;
|
|
}
|
|
|
|
//==================================================================================
|
|
|
|
/*
|
|
=============
|
|
R_TransformToScreen_Vec3
|
|
=============
|
|
*/
|
|
void R_TransformToScreen_Vec3( vec3_t in, vec3_t out )
|
|
{
|
|
vec4_t temp, temp2;
|
|
|
|
temp[0] = in[0];
|
|
temp[1] = in[1];
|
|
temp[2] = in[2];
|
|
temp[3] = 1.0f;
|
|
Matrix4_Multiply_Vector( RI.worldviewProjectionMatrix, temp, temp2 );
|
|
|
|
if( !temp2[3] )
|
|
return;
|
|
|
|
out[0] = ( temp2[0] / temp2[3] + 1.0f ) * 0.5f * RI.refdef.viewport[2];
|
|
out[1] = ( temp2[1] / temp2[3] + 1.0f ) * 0.5f * RI.refdef.viewport[3];
|
|
out[2] = ( temp2[2] / temp2[3] + 1.0f ) * 0.5f;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
R_TransformVectorToScreen
|
|
=============
|
|
*/
|
|
void R_TransformVectorToScreen( const ref_params_t *rd, const vec3_t in, vec2_t out )
|
|
{
|
|
mat4x4_t p, m;
|
|
vec4_t temp, temp2;
|
|
|
|
if( !rd || !in || !out )
|
|
return;
|
|
|
|
temp[0] = in[0];
|
|
temp[1] = in[1];
|
|
temp[2] = in[2];
|
|
temp[3] = 1.0f;
|
|
|
|
R_SetupProjectionMatrix( rd, p );
|
|
R_SetupModelviewMatrix( rd, m );
|
|
|
|
Matrix4_Multiply_Vector( m, temp, temp2 );
|
|
Matrix4_Multiply_Vector( p, temp2, temp );
|
|
|
|
if( !temp[3] )
|
|
return;
|
|
|
|
out[0] = rd->viewport[0] + ( temp[0] / temp[3] + 1.0f ) * rd->viewport[2] * 0.5f;
|
|
out[1] = rd->viewport[1] + ( temp[1] / temp[3] + 1.0f ) * rd->viewport[3] * 0.5f;
|
|
}
|
|
|
|
//==================================================================================
|
|
|
|
/*
|
|
=============
|
|
R_TraceLine
|
|
=============
|
|
*/
|
|
msurface_t *R_TraceLine( trace_t *tr, const vec3_t start, const vec3_t end, int surfumask )
|
|
{
|
|
int i;
|
|
msurface_t *surf;
|
|
|
|
// trace against world
|
|
surf = R_TransformedTraceLine( tr, start, end, r_worldent, surfumask );
|
|
|
|
// trace against bmodels
|
|
for( i = 0; i < r_numbmodelentities; i++ )
|
|
{
|
|
trace_t t2;
|
|
msurface_t *s2;
|
|
|
|
s2 = R_TransformedTraceLine( &t2, start, end, r_bmodelentities[i], surfumask );
|
|
if( t2.fraction < tr->fraction )
|
|
{
|
|
*tr = t2; // closer impact point
|
|
surf = s2;
|
|
}
|
|
}
|
|
|
|
return surf;
|
|
}
|
|
|
|
bool R_UploadModel( const char *name, int index )
|
|
{
|
|
ref_model_t *mod;
|
|
|
|
// this array used by AddEntityToScene
|
|
mod = R_RegisterModel( name );
|
|
cl_models[index] = mod;
|
|
|
|
return (mod != NULL);
|
|
}
|
|
|
|
shader_t Mod_RegisterShader( const char *name, int shaderType )
|
|
{
|
|
ref_shader_t *src;
|
|
|
|
if( !glState.initializedMedia )
|
|
return 0;
|
|
|
|
switch( shaderType )
|
|
{
|
|
case SHADER_FONT:
|
|
case SHADER_NOMIP:
|
|
src = R_RegisterPic( name );
|
|
break;
|
|
case SHADER_SKYBOX:
|
|
case SHADER_GENERIC:
|
|
src = R_RegisterShader( name );
|
|
break;
|
|
default:
|
|
MsgDev( D_WARN, "Mod_RegisterShader: invalid shader type (%i)\n", shaderType );
|
|
return 0;
|
|
}
|
|
return src - r_shaders;
|
|
}
|
|
|
|
bool R_AddLightStyle( int stylenum, vec3_t color )
|
|
{
|
|
if( stylenum < 0 || stylenum > MAX_LIGHTSTYLES )
|
|
return false; // invalid lightstyle
|
|
|
|
R_AddLightStyleToScene( stylenum, color[0], color[1], color[2] );
|
|
return true;
|
|
}
|
|
|
|
bool R_AddGenericEntity( edict_t *pRefEntity, ref_entity_t *refent, int ed_type, float lerpfrac )
|
|
{
|
|
int i;
|
|
|
|
// check model
|
|
if( !refent->model ) return false;
|
|
|
|
switch( refent->model->type )
|
|
{
|
|
case mod_brush: break;
|
|
case mod_studio:
|
|
if( !refent->model->extradata )
|
|
return false;
|
|
refent->rtype = RT_MODEL;
|
|
break;
|
|
case mod_sprite:
|
|
if( !refent->model->extradata )
|
|
return false;
|
|
refent->rtype = RT_SPRITE;
|
|
break;
|
|
case mod_bad: // let the render drawing null model
|
|
break;
|
|
}
|
|
|
|
// setup latchedvars
|
|
VectorCopy( pRefEntity->v.oldorigin, refent->prev.origin );
|
|
VectorCopy( pRefEntity->v.oldangles, refent->prev.angles );
|
|
|
|
// do animate
|
|
if( refent->effects & EF_ANIMATE )
|
|
{
|
|
switch( refent->model->type )
|
|
{
|
|
case mod_studio:
|
|
if( pRefEntity->v.frame == -1 )
|
|
{
|
|
pRefEntity->v.frame = refent->frame = 0;
|
|
refent->sequence = pRefEntity->v.sequence;
|
|
// R_StudioResetSequenceInfo( refent, refent->model->phdr );
|
|
}
|
|
else
|
|
{
|
|
// R_StudioFrameAdvance( refent, 0 );
|
|
|
|
if( refent->m_fSequenceFinished )
|
|
{
|
|
if( refent->m_fSequenceLoops )
|
|
pRefEntity->v.frame = -1;
|
|
// hold at last frame
|
|
}
|
|
else
|
|
{
|
|
// copy current frame back to let user grab it on a client-side
|
|
pRefEntity->v.frame = refent->frame;
|
|
}
|
|
}
|
|
break;
|
|
case mod_sprite:
|
|
case mod_brush:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
refent->prev.frame = refent->frame;
|
|
refent->frame = pRefEntity->v.frame;
|
|
refent->prev.sequence = refent->sequence;
|
|
refent->prev.animtime = refent->animtime;
|
|
refent->animtime = pRefEntity->v.animtime;
|
|
refent->sequence = pRefEntity->v.sequence;
|
|
}
|
|
|
|
refent->prev.sequencetime = refent->animtime - refent->prev.animtime;
|
|
refent->weaponmodel = cl_models[pRefEntity->v.weaponmodel];
|
|
|
|
if( refent->ent_type == ED_MOVER || refent->ent_type == ED_BSPBRUSH )
|
|
{
|
|
VectorNormalize2( pRefEntity->v.movedir, refent->movedir );
|
|
}
|
|
else VectorClear( refent->movedir );
|
|
|
|
// calculate angles
|
|
if( refent->effects & EF_ROTATE )
|
|
{
|
|
// some bonus items auto-rotate
|
|
VectorSet( refent->angles, 0, anglemod( RI.refdef.time / 10), 0 );
|
|
}
|
|
else
|
|
{
|
|
// interpolate angles
|
|
for( i = 0; i < 3; i++ )
|
|
refent->angles[i] = LerpAngle( pRefEntity->v.oldangles[i], pRefEntity->v.angles[i], lerpfrac );
|
|
}
|
|
|
|
// interpolate origin
|
|
for( i = 0; i < 3; i++ )
|
|
refent->origin[i] = LerpPoint( pRefEntity->v.oldorigin[i], pRefEntity->v.origin[i], lerpfrac );
|
|
AngleVectorsFLU( refent->angles, refent->axis[0], refent->axis[1], refent->axis[2] );
|
|
VectorClear( refent->origin2 );
|
|
|
|
if(( refent->ent_type == ED_VIEWMODEL ) && ( r_lefthand->integer == 1 ))
|
|
VectorNegate( refent->axis[1], refent->axis[1] );
|
|
|
|
// copy controllers
|
|
for( i = 0; i < MAXSTUDIOCONTROLLERS; i++ )
|
|
{
|
|
refent->prev.controller[i] = refent->controller[i];
|
|
refent->controller[i] = pRefEntity->v.controller[i];
|
|
}
|
|
|
|
// copy blends
|
|
for( i = 0; i < MAXSTUDIOBLENDS; i++ )
|
|
{
|
|
refent->prev.blending[i] = refent->blending[i];
|
|
refent->blending[i] = pRefEntity->v.blending[i];
|
|
}
|
|
|
|
if( refent->ent_type == ED_CLIENT )
|
|
refent->gaitsequence = pRefEntity->v.gaitsequence;
|
|
else refent->gaitsequence = 0;
|
|
|
|
// because entity without models never added to scene
|
|
if( !refent->ent_type )
|
|
{
|
|
switch( refent->model->type )
|
|
{
|
|
case mod_brush:
|
|
refent->ent_type = ED_BSPBRUSH;
|
|
break;
|
|
case mod_studio:
|
|
case mod_sprite:
|
|
refent->ent_type = ED_NORMAL;
|
|
break;
|
|
// and ignore all other unset ents
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool R_AddPortalEntity( edict_t *pRefEntity, ref_entity_t *refent, int ed_type, float lerpfrac )
|
|
{
|
|
refent->rtype = RT_PORTALSURFACE;
|
|
|
|
VectorClear( refent->angles );
|
|
Matrix_Identity( refent->axis );
|
|
VectorCopy( pRefEntity->v.movedir, refent->movedir );
|
|
VectorCopy( pRefEntity->v.origin, refent->origin );
|
|
VectorCopy( pRefEntity->v.oldorigin, refent->origin2 );
|
|
|
|
// calculate angles
|
|
if( refent->effects & EF_ROTATE )
|
|
Matrix_Rotate( refent->axis, 5 * sin(( 0.25f + RI.refdef.time * 50 * 0.01 ) * M_PI2 ), 1, 0, 0 );
|
|
return true;
|
|
}
|
|
|
|
bool R_AddEntityToScene( edict_t *pRefEntity, int ed_type, float lerpfrac )
|
|
{
|
|
ref_entity_t *refent;
|
|
bool result = false;
|
|
|
|
if( !pRefEntity || !pRefEntity->v.modelindex )
|
|
return false; // if set to invisible, skip
|
|
if( r_numEntities >= MAX_ENTITIES ) return false;
|
|
|
|
refent = &r_entities[r_numEntities];
|
|
|
|
if( pRefEntity->v.effects & EF_NODRAW )
|
|
return true; // done
|
|
|
|
// filter ents
|
|
switch( ed_type )
|
|
{
|
|
case ED_MOVER:
|
|
case ED_PORTAL:
|
|
case ED_CLIENT:
|
|
case ED_NORMAL:
|
|
case ED_MONSTER:
|
|
case ED_BSPBRUSH:
|
|
case ED_RIGIDBODY:
|
|
case ED_VIEWMODEL: break;
|
|
default: return false;
|
|
}
|
|
|
|
// copy state to render
|
|
refent->index = pRefEntity->serialnumber;
|
|
refent->ent_type = ed_type;
|
|
refent->backlerp = 1.0f - lerpfrac;
|
|
refent->rendermode = pRefEntity->v.rendermode;
|
|
refent->body = pRefEntity->v.body;
|
|
refent->skin = pRefEntity->v.skin;
|
|
refent->scale = pRefEntity->v.scale;
|
|
refent->colormap = pRefEntity->v.colormap;
|
|
refent->effects = pRefEntity->v.effects;
|
|
refent->renderfx = pRefEntity->v.renderfx;
|
|
VectorCopy( pRefEntity->v.rendercolor, refent->rendercolor );
|
|
refent->renderamt = pRefEntity->v.renderamt;
|
|
refent->model = cl_models[pRefEntity->v.modelindex];
|
|
refent->movetype = pRefEntity->v.movetype;
|
|
refent->framerate = pRefEntity->v.framerate;
|
|
|
|
// setup rtype
|
|
switch( ed_type )
|
|
{
|
|
case ED_PORTAL:
|
|
result = R_AddPortalEntity( pRefEntity, refent, ed_type, lerpfrac );
|
|
break;
|
|
default:
|
|
result = R_AddGenericEntity( pRefEntity, refent, ed_type, lerpfrac );
|
|
break;
|
|
}
|
|
|
|
// add entity
|
|
r_numEntities++;
|
|
|
|
return result;
|
|
}
|
|
|
|
bool R_AddDynamicLight( vec3_t org, vec3_t color, float intensity )
|
|
{
|
|
dlight_t *dl;
|
|
|
|
if(( r_numDlights >= MAX_DLIGHTS ) || (intensity == 0) || ( !color[0] || !color[1] || !color[2] ))
|
|
return false;
|
|
|
|
dl = &r_dlights[r_numDlights++];
|
|
|
|
VectorCopy( org, dl->origin );
|
|
VectorCopy( color, dl->color );
|
|
dl->intensity = intensity * DLIGHT_SCALE;
|
|
dl->shader = NULL; // FIXME;
|
|
|
|
R_LightBounds( org, dl->intensity, dl->mins, dl->maxs );
|
|
|
|
return true;
|
|
}
|
|
|
|
void GL_SetColor( const void *data )
|
|
{
|
|
float *color = (float *)data;
|
|
|
|
if( color ) Vector4Copy( color, glState.draw_color );
|
|
else Vector4Set( glState.draw_color, 1.0f, 1.0f, 1.0f, 1.0f );
|
|
}
|
|
|
|
void R_DrawSetParms( shader_t handle, kRenderMode_t rendermode, int frame )
|
|
{
|
|
}
|
|
|
|
void R_DrawGetParms( int *w, int *h, int *f, int frame, shader_t shader )
|
|
{
|
|
if( !w && !h && !f ) return;
|
|
|
|
// assume error
|
|
if( w ) *w = 0;
|
|
if( h ) *h = 0;
|
|
if( f ) *f = 1;
|
|
}
|
|
|
|
void R_DrawFill( float x, float y, float w, float h )
|
|
{
|
|
}
|
|
|
|
void R_LightForPoint( const vec3_t point, vec3_t ambientLight )
|
|
{
|
|
vec4_t ambient;
|
|
|
|
R_LightForOrigin( point, NULL, ambient, NULL, 64.0f );
|
|
VectorCopy( ambient, ambientLight );
|
|
}
|
|
|
|
/*
|
|
@@@@@@@@@@@@@@@@@@@@@
|
|
CreateAPI
|
|
|
|
@@@@@@@@@@@@@@@@@@@@@
|
|
*/
|
|
render_exp_t DLLEXPORT *CreateAPI(stdlib_api_t *input, render_imp_t *engfuncs )
|
|
{
|
|
static render_exp_t re;
|
|
|
|
com = *input;
|
|
// Sys_LoadLibrary can create fake instance, to check
|
|
// api version and api size, but second argument will be 0
|
|
// and always make exception, run simply check for avoid it
|
|
if( engfuncs ) ri = *engfuncs;
|
|
|
|
// generic functions
|
|
re.api_size = sizeof(render_exp_t);
|
|
|
|
re.Init = R_Init;
|
|
re.Shutdown = R_Shutdown;
|
|
|
|
re.BeginRegistration = R_BeginRegistration;
|
|
re.RegisterModel = R_UploadModel;
|
|
re.RegisterShader = Mod_RegisterShader;
|
|
re.EndRegistration = R_EndRegistration;
|
|
|
|
re.AddLightStyle = R_AddLightStyle;
|
|
re.AddRefEntity = R_AddEntityToScene;
|
|
re.AddDynLight = R_AddDynamicLight;
|
|
re.AddPolygon = R_AddPolyToScene;
|
|
re.ClearScene = R_ClearScene;
|
|
|
|
re.BeginFrame = R_BeginFrame;
|
|
re.RenderFrame = R_RenderScene;
|
|
re.EndFrame = R_EndFrame;
|
|
|
|
re.SetColor = GL_SetColor;
|
|
re.GetParms = R_DrawGetParms;
|
|
re.SetParms = R_DrawSetParms;
|
|
re.ScrShot = VID_ScreenShot;
|
|
re.EnvShot = VID_CubemapShot;
|
|
re.LightForPoint = R_LightForPoint;
|
|
re.DrawFill = R_DrawFill;
|
|
re.DrawStretchRaw = R_DrawStretchRaw;
|
|
re.DrawStretchPic = R_DrawStretchPic;
|
|
re.GetFragments = R_GetClippedFragments;
|
|
|
|
return &re;
|
|
} |