2
0
mirror of https://github.com/FWGS/xash3d-fwgs synced 2024-11-25 11:19:59 +01:00
xash3d-fwgs/ref/soft/r_main.c

1699 lines
39 KiB
C

/*
gl_rmain.c - renderer main loop
Copyright (C) 2010 Uncle Mike
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 3 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.
*/
#include "r_local.h"
#include "xash3d_mathlib.h"
#include "library.h"
// #include "beamdef.h"
// #include "particledef.h"
#include "entity_types.h"
#include "mod_local.h"
int r_cnumsurfs;
#define IsLiquidContents( cnt ) ( cnt == CONTENTS_WATER || cnt == CONTENTS_SLIME || cnt == CONTENTS_LAVA )
ref_instance_t RI;
// quake defines. will be refactored
// view origin
//
//
// screen size info
//
float xcenter, ycenter;
float xscale, yscale;
float xscaleinv, yscaleinv;
// float xscaleshrink, yscaleshrink;
float aliasxscale, aliasyscale, aliasxcenter, aliasycenter;
int r_screenwidth;
//
// refresh flags
//
// int d_spanpixcount;
// int r_polycount;
// int r_drawnpolycount;
// int r_wholepolycount;
int r_viewcluster, r_oldviewcluster;
CVAR_DEFINE_AUTO( sw_clearcolor, "48999", 0, "screen clear color" );
CVAR_DEFINE_AUTO( sw_drawflat, "0", FCVAR_CHEAT, "" );
CVAR_DEFINE_AUTO( sw_draworder, "0", FCVAR_CHEAT, "" );
CVAR_DEFINE_AUTO( sw_maxedges, "32", 0, "" );
static CVAR_DEFINE_AUTO( sw_maxsurfs, "0", 0, "" );
CVAR_DEFINE_AUTO( sw_mipscale, "1", FCVAR_GLCONFIG, "nothing" );
CVAR_DEFINE_AUTO( sw_mipcap, "0", FCVAR_GLCONFIG, "nothing" );
CVAR_DEFINE_AUTO( sw_surfcacheoverride, "0", 0, "" );
static CVAR_DEFINE_AUTO( sw_waterwarp, "1", FCVAR_GLCONFIG, "nothing" );
static CVAR_DEFINE_AUTO( sw_notransbrushes, "0", FCVAR_GLCONFIG, "do not apply transparency to water/glasses (faster)" );
CVAR_DEFINE_AUTO( sw_noalphabrushes, "0", FCVAR_GLCONFIG, "do not draw brush holes (faster)" );
CVAR_DEFINE_AUTO( r_traceglow, "0", FCVAR_GLCONFIG, "cull flares behind models" );
CVAR_DEFINE_AUTO( sw_texfilt, "0", FCVAR_GLCONFIG, "texture dither" );
static CVAR_DEFINE_AUTO( r_novis, "0", 0, "" );
DEFINE_ENGINE_SHARED_CVAR_LIST()
int r_viewcluster, r_oldviewcluster;
float d_sdivzstepu, d_tdivzstepu, d_zistepu;
float d_sdivzstepv, d_tdivzstepv, d_zistepv;
float d_sdivzorigin, d_tdivzorigin, d_ziorigin;
fixed16_t sadjust, tadjust, bbextents, bbextentt;
pixel_t *cacheblock;
int cachewidth;
pixel_t *d_viewbuffer;
short *d_pzbuffer;
unsigned int d_zrowbytes;
unsigned int d_zwidth;
mvertex_t *r_pcurrentvertbase;
// int c_surf;
qboolean r_surfsonstack;
int r_clipflags;
byte r_warpbuffer[WARP_WIDTH * WARP_HEIGHT];
int r_numallocatededges;
float r_aliasuvscale = 1.0;
static int R_RankForRenderMode( int rendermode )
{
switch( rendermode )
{
case kRenderTransTexture:
return 1; // draw second
case kRenderTransAdd:
return 2; // draw third
case kRenderGlow:
return 3; // must be last!
}
return 0;
}
void GAME_EXPORT R_AllowFog( qboolean allowed )
{
}
/*
===============
R_OpaqueEntity
Opaque entity can be brush or studio model but sprite
===============
*/
qboolean R_OpaqueEntity( cl_entity_t *ent )
{
int rendermode = R_GetEntityRenderMode( ent );
if( rendermode == kRenderNormal )
{
switch( ent->curstate.renderfx )
{
case kRenderFxNone:
case kRenderFxDeadPlayer:
case kRenderFxLightMultiplier:
case kRenderFxExplode:
return true;
}
}
if( sw_notransbrushes.value && ent->model && ent->model->type == mod_brush && rendermode == kRenderTransTexture )
return true;
if( sw_noalphabrushes.value && ent->model && ent->model->type == mod_brush && rendermode == kRenderTransAlpha )
return true;
return false;
}
/*
===============
R_TransEntityCompare
Sorting translucent entities by rendermode then by distance
===============
*/
static int R_TransEntityCompare( const cl_entity_t **a, const cl_entity_t **b )
{
cl_entity_t *ent1, *ent2;
vec3_t vecLen, org;
float dist1, dist2;
int rendermode1;
int rendermode2;
ent1 = (cl_entity_t *)*a;
ent2 = (cl_entity_t *)*b;
rendermode1 = R_GetEntityRenderMode( ent1 );
rendermode2 = R_GetEntityRenderMode( ent2 );
// sort by distance
if(( ent1->model && ent1->model->type != mod_brush ) || rendermode1 != kRenderTransAlpha )
{
VectorAverage( ent1->model->mins, ent1->model->maxs, org );
VectorAdd( ent1->origin, org, org );
VectorSubtract( RI.vieworg, org, vecLen );
dist1 = DotProduct( vecLen, vecLen );
}
else
dist1 = 1000000000;
if(( ent1->model && ent2->model->type != mod_brush ) || rendermode2 != kRenderTransAlpha )
{
VectorAverage( ent2->model->mins, ent2->model->maxs, org );
VectorAdd( ent2->origin, org, org );
VectorSubtract( RI.vieworg, org, vecLen );
dist2 = DotProduct( vecLen, vecLen );
}
else
dist2 = 1000000000;
if( dist1 > dist2 )
return -1;
if( dist1 < dist2 )
return 1;
// then sort by rendermode
if( R_RankForRenderMode( rendermode1 ) > R_RankForRenderMode( rendermode2 ))
return 1;
if( R_RankForRenderMode( rendermode1 ) < R_RankForRenderMode( rendermode2 ))
return -1;
return 0;
}
/*
===============
R_WorldToScreen
Convert a given point from world into screen space
Returns true if we behind to screen
===============
*/
int R_WorldToScreen( const vec3_t point, vec3_t screen )
{
matrix4x4 worldToScreen;
qboolean behind;
float w;
if( !point || !screen )
return true;
Matrix4x4_Copy( worldToScreen, RI.worldviewProjectionMatrix );
screen[0] = worldToScreen[0][0] * point[0] + worldToScreen[0][1] * point[1] + worldToScreen[0][2] * point[2] + worldToScreen[0][3];
screen[1] = worldToScreen[1][0] * point[0] + worldToScreen[1][1] * point[1] + worldToScreen[1][2] * point[2] + worldToScreen[1][3];
w = worldToScreen[3][0] * point[0] + worldToScreen[3][1] * point[1] + worldToScreen[3][2] * point[2] + worldToScreen[3][3];
screen[2] = 0.0f; // just so we have something valid here
if( w < 0.001f )
{
behind = true;
}
else
{
float invw = 1.0f / w;
screen[0] *= invw;
screen[1] *= invw;
behind = false;
}
return behind;
}
/*
===============
R_ScreenToWorld
Convert a given point from screen into world space
===============
*/
void GAME_EXPORT R_ScreenToWorld( const vec3_t screen, vec3_t point )
{
matrix4x4 screenToWorld;
float w;
if( !point || !screen )
return;
Matrix4x4_Invert_Full( screenToWorld, RI.worldviewProjectionMatrix );
point[0] = screen[0] * screenToWorld[0][0] + screen[1] * screenToWorld[0][1] + screen[2] * screenToWorld[0][2] + screenToWorld[0][3];
point[1] = screen[0] * screenToWorld[1][0] + screen[1] * screenToWorld[1][1] + screen[2] * screenToWorld[1][2] + screenToWorld[1][3];
point[2] = screen[0] * screenToWorld[2][0] + screen[1] * screenToWorld[2][1] + screen[2] * screenToWorld[2][2] + screenToWorld[2][3];
w = screen[0] * screenToWorld[3][0] + screen[1] * screenToWorld[3][1] + screen[2] * screenToWorld[3][2] + screenToWorld[3][3];
if( w != 0.0f )
VectorScale( point, ( 1.0f / w ), point );
}
/*
===============
R_PushScene
===============
*/
void GAME_EXPORT R_PushScene( void )
{
if( ++tr.draw_stack_pos >= MAX_DRAW_STACK )
gEngfuncs.Host_Error( "draw stack overflow\n" );
tr.draw_list = &tr.draw_stack[tr.draw_stack_pos];
}
/*
===============
R_PopScene
===============
*/
void GAME_EXPORT R_PopScene( void )
{
if( --tr.draw_stack_pos < 0 )
gEngfuncs.Host_Error( "draw stack underflow\n" );
tr.draw_list = &tr.draw_stack[tr.draw_stack_pos];
}
/*
===============
R_ClearScene
===============
*/
void GAME_EXPORT R_ClearScene( void )
{
tr.draw_list->num_solid_entities = 0;
tr.draw_list->num_trans_entities = 0;
tr.draw_list->num_beam_entities = 0;
tr.draw_list->num_edge_entities = 0;
// clear the scene befor start new frame
if( gEngfuncs.drawFuncs->R_ClearScene != NULL )
gEngfuncs.drawFuncs->R_ClearScene();
}
/*
===============
R_AddEntity
===============
*/
qboolean GAME_EXPORT R_AddEntity( struct cl_entity_s *clent, int type )
{
if( !r_drawentities->value )
return false; // not allow to drawing
if( !clent || !clent->model )
return false; // if set to invisible, skip
if( FBitSet( clent->curstate.effects, EF_NODRAW ))
return false; // done
if( !R_ModelOpaque( clent->curstate.rendermode ) && CL_FxBlend( clent ) <= 0 )
return true; // invisible
if( type == ET_FRAGMENTED )
r_stats.c_client_ents++;
if( R_OpaqueEntity( clent ))
{
if( clent->model->type == mod_brush )
{
if( tr.draw_list->num_edge_entities >= MAX_VISIBLE_PACKET )
return false;
tr.draw_list->edge_entities[tr.draw_list->num_edge_entities] = clent;
tr.draw_list->num_edge_entities++;
return true;
}
// opaque
if( tr.draw_list->num_solid_entities >= MAX_VISIBLE_PACKET )
return false;
tr.draw_list->solid_entities[tr.draw_list->num_solid_entities] = clent;
tr.draw_list->num_solid_entities++;
}
else
{
// translucent
if( tr.draw_list->num_trans_entities >= MAX_VISIBLE_PACKET )
return false;
tr.draw_list->trans_entities[tr.draw_list->num_trans_entities] = clent;
tr.draw_list->num_trans_entities++;
}
return true;
}
/*
=============
R_Clear
=============
*/
static void R_Clear( int bitMask )
{
memset( vid.buffer, 0, vid.width * vid.height * 2 );
}
// =============================================================================
/*
===============
R_GetFarClip
===============
*/
static float R_GetFarClip( void )
{
if( WORLDMODEL && RI.drawWorld )
return tr.movevars->zmax * 1.73f;
return 2048.0f;
}
/*
===============
R_SetupFrustum
===============
*/
void R_SetupFrustum( void )
{
// build the transformation matrix for the given view angles
AngleVectors( RI.viewangles, RI.vforward, RI.vright, RI.vup );
{
VectorCopy( RI.vieworg, RI.cullorigin );
VectorCopy( RI.vforward, RI.cull_vforward );
VectorCopy( RI.vright, RI.cull_vright );
VectorCopy( RI.vup, RI.cull_vup );
}
}
/*
=============
R_SetupProjectionMatrix
=============
*/
static void R_SetupProjectionMatrix( matrix4x4 m )
{
float xMin, xMax, yMin, yMax, zNear, zFar;
if( RI.drawOrtho )
{
const ref_overview_t *ov = gEngfuncs.GetOverviewParms();
Matrix4x4_CreateOrtho( m, ov->xLeft, ov->xRight, ov->yTop, ov->yBottom, ov->zNear, ov->zFar );
return;
}
RI.farClip = R_GetFarClip();
zNear = 4.0f;
zFar = Q_max( 256.0f, RI.farClip );
yMax = zNear * tan( RI.fov_y * M_PI_F / 360.0f );
yMin = -yMax;
xMax = zNear * tan( RI.fov_x * M_PI_F / 360.0f );
xMin = -xMax;
Matrix4x4_CreateProjection( m, xMax, xMin, yMax, yMin, zNear, zFar );
}
/*
=============
R_SetupModelviewMatrix
=============
*/
static void R_SetupModelviewMatrix( matrix4x4 m )
{
Matrix4x4_CreateModelview( m );
Matrix4x4_ConcatRotate( m, -RI.viewangles[2], 1, 0, 0 );
Matrix4x4_ConcatRotate( m, -RI.viewangles[0], 0, 1, 0 );
Matrix4x4_ConcatRotate( m, -RI.viewangles[1], 0, 0, 1 );
Matrix4x4_ConcatTranslate( m, -RI.vieworg[0], -RI.vieworg[1], -RI.vieworg[2] );
}
/*
=============
R_LoadIdentity
=============
*/
void R_LoadIdentity( void )
{
}
/*
=============
R_RotateForEntity
=============
*/
void R_RotateForEntity( cl_entity_t *e )
{
}
/*
=============
R_TranslateForEntity
=============
*/
void R_TranslateForEntity( cl_entity_t *e )
{
}
/*
===============
R_FindViewLeaf
===============
*/
void R_FindViewLeaf( void )
{
RI.oldviewleaf = RI.viewleaf;
RI.viewleaf = gEngfuncs.Mod_PointInLeaf( RI.pvsorigin, WORLDMODEL->nodes );
}
/*
===============
R_SetupFrame
===============
*/
static void R_SetupFrame( void )
{
// setup viewplane dist
RI.viewplanedist = DotProduct( RI.vieworg, RI.vforward );
// if( !gl_nosort->value )
{
// sort translucents entities by rendermode and distance
qsort( tr.draw_list->trans_entities, tr.draw_list->num_trans_entities, sizeof( cl_entity_t * ), (void *)R_TransEntityCompare );
}
// current viewleaf
if( RI.drawWorld )
{
RI.isSkyVisible = false; // unknown at this moment
R_FindViewLeaf();
}
// setup twice until globals fully refactored
R_SetupFrameQ();
}
/*
=============
R_RecursiveFindWaterTexture
using to find source waterleaf with
watertexture to grab fog values from it
=============
*/
static image_t *R_RecursiveFindWaterTexture( const mnode_t *node, const mnode_t *ignore, qboolean down )
{
image_t *tex = NULL;
// assure the initial node is not null
// we could check it here, but we would rather check it
// outside the call to get rid of one additional recursion level
Assert( node != NULL );
// ignore solid nodes
if( node->contents == CONTENTS_SOLID )
return NULL;
if( node->contents < 0 )
{
mleaf_t *pleaf;
msurface_t **mark;
int i, c;
// ignore non-liquid leaves
if( node->contents != CONTENTS_WATER && node->contents != CONTENTS_LAVA && node->contents != CONTENTS_SLIME )
return NULL;
// find texture
pleaf = (mleaf_t *)node;
mark = pleaf->firstmarksurface;
c = pleaf->nummarksurfaces;
for( i = 0; i < c; i++, mark++ )
{
if(( *mark )->flags & SURF_DRAWTURB && ( *mark )->texinfo && ( *mark )->texinfo->texture )
return R_GetTexture(( *mark )->texinfo->texture->gl_texturenum );
}
// texture not found
return NULL;
}
// this is a regular node
// traverse children
if( node->children[0] && ( node->children[0] != ignore ))
{
tex = R_RecursiveFindWaterTexture( node->children[0], node, true );
if( tex )
return tex;
}
if( node->children[1] && ( node->children[1] != ignore ))
{
tex = R_RecursiveFindWaterTexture( node->children[1], node, true );
if( tex )
return tex;
}
// for down recursion, return immediately
if( down )
return NULL;
// texture not found, step up if any
if( node->parent )
return R_RecursiveFindWaterTexture( node->parent, node, false );
// top-level node, bail out
return NULL;
}
/*
=============
R_DrawEntitiesOnList
=============
*/
static void R_DrawEntitiesOnList( void )
{
int i;
// extern int d_aflatcolor;
// d_aflatcolor = 0;
tr.blend = 1.0f;
// GL_CheckForErrors();
// RI.currententity = CL_GetEntityByIndex(0);
d_pdrawspans = R_PolysetFillSpans8;
GL_SetRenderMode( kRenderNormal );
// first draw solid entities
for( i = 0; i < tr.draw_list->num_solid_entities && !RI.onlyClientDraw; i++ )
{
RI.currententity = tr.draw_list->solid_entities[i];
RI.currentmodel = RI.currententity->model;
// d_aflatcolor += 500;
Assert( RI.currententity != NULL );
Assert( RI.currentmodel != NULL );
switch( RI.currentmodel->type )
{
case mod_brush:
R_DrawBrushModel( RI.currententity );
break;
case mod_alias:
// R_DrawAliasModel( RI.currententity );
break;
case mod_studio:
R_SetUpWorldTransform();
R_DrawStudioModel( RI.currententity );
break;
default:
break;
}
}
R_SetUpWorldTransform();
// draw sprites seperately, because of alpha blending
for( i = 0; i < tr.draw_list->num_solid_entities && !RI.onlyClientDraw; i++ )
{
RI.currententity = tr.draw_list->solid_entities[i];
RI.currentmodel = RI.currententity->model;
Assert( RI.currententity != NULL );
Assert( RI.currentmodel != NULL );
switch( RI.currentmodel->type )
{
case mod_sprite:
R_DrawSpriteModel( RI.currententity );
break;
}
}
if( !RI.onlyClientDraw )
{
gEngfuncs.CL_DrawEFX( tr.frametime, false );
}
if( RI.drawWorld )
gEngfuncs.pfnDrawNormalTriangles();
d_pdrawspans = R_PolysetDrawSpans8_33;
// then draw translucent entities
for( i = 0; i < tr.draw_list->num_trans_entities && !RI.onlyClientDraw; i++ )
{
RI.currententity = tr.draw_list->trans_entities[i];
RI.currentmodel = RI.currententity->model;
// handle studiomodels with custom rendermodes on texture
if( RI.currententity->curstate.rendermode != kRenderNormal )
tr.blend = CL_FxBlend( RI.currententity ) / 255.0f;
else
tr.blend = 1.0f; // draw as solid but sorted by distance
if( tr.blend <= 0.0f )
continue;
Assert( RI.currententity != NULL );
Assert( RI.currentmodel != NULL );
switch( RI.currentmodel->type )
{
case mod_brush:
R_DrawBrushModel( RI.currententity );
break;
case mod_alias:
// R_DrawAliasModel( RI.currententity );
break;
case mod_studio:
R_SetUpWorldTransform();
R_DrawStudioModel( RI.currententity );
break;
case mod_sprite:
R_SetUpWorldTransform();
R_DrawSpriteModel( RI.currententity );
break;
default:
break;
}
}
if( RI.drawWorld )
{
gEngfuncs.pfnDrawTransparentTriangles();
}
if( !RI.onlyClientDraw )
{
R_AllowFog( false );
gEngfuncs.CL_DrawEFX( tr.frametime, true );
R_AllowFog( true );
}
GL_SetRenderMode( kRenderNormal );
R_SetUpWorldTransform();
if( !RI.onlyClientDraw )
R_DrawViewModel();
gEngfuncs.CL_ExtraUpdate();
}
qboolean insubmodel;
/*
=============
R_BmodelCheckBBox
=============
*/
int R_BmodelCheckBBox( float *minmaxs )
{
int i, *pindex, clipflags;
vec3_t acceptpt, rejectpt;
float d;
clipflags = 0;
for( i = 0; i < 4; i++ )
{
// generate accept and reject points
// FIXME: do with fast look-ups or integer tests based on the sign bit
// of the floating point values
pindex = qfrustum.pfrustum_indexes[i];
rejectpt[0] = minmaxs[pindex[0]];
rejectpt[1] = minmaxs[pindex[1]];
rejectpt[2] = minmaxs[pindex[2]];
d = DotProduct( rejectpt, qfrustum.view_clipplanes[i].normal );
d -= qfrustum.view_clipplanes[i].dist;
if( d <= 0 )
return BMODEL_FULLY_CLIPPED;
acceptpt[0] = minmaxs[pindex[3 + 0]];
acceptpt[1] = minmaxs[pindex[3 + 1]];
acceptpt[2] = minmaxs[pindex[3 + 2]];
d = DotProduct( acceptpt, qfrustum.view_clipplanes[i].normal );
d -= qfrustum.view_clipplanes[i].dist;
if( d <= 0 )
clipflags |= ( 1 << i );
}
return clipflags;
}
/*
===================
R_FindTopNode
===================
*/
static mnode_t *R_FindTopnode( vec3_t mins, vec3_t maxs )
{
mplane_t *splitplane;
int sides;
mnode_t *node;
node = WORLDMODEL->nodes;
while( 1 )
{
if( node->visframe != tr.visframecount )
return NULL; // not visible at all
if( node->contents < 0 )
{
if( node->contents != CONTENTS_SOLID )
return node; // we've reached a non-solid leaf, so it's
// visible and not BSP clipped
return NULL; // in solid, so not visible
}
splitplane = node->plane;
sides = BOX_ON_PLANE_SIDE( mins, maxs, splitplane );
if( sides == 3 )
return node; // this is the splitter
// not split yet; recurse down the contacted side
if( sides & 1 )
node = node->children[0];
else
node = node->children[1];
}
}
/*
=============
RotatedBBox
Returns an axially aligned box that contains the input box at the given rotation
=============
*/
void RotatedBBox( vec3_t mins, vec3_t maxs, vec3_t angles, vec3_t tmins, vec3_t tmaxs )
{
vec3_t tmp, v;
int i, j;
vec3_t forward, right, up;
if( !angles[0] && !angles[1] && !angles[2] )
{
VectorCopy( mins, tmins );
VectorCopy( maxs, tmaxs );
return;
}
for( i = 0; i < 3; i++ )
{
tmins[i] = 99999;
tmaxs[i] = -99999;
}
AngleVectors( angles, forward, right, up );
for( i = 0; i < 8; i++ )
{
if( i & 1 )
tmp[0] = mins[0];
else
tmp[0] = maxs[0];
if( i & 2 )
tmp[1] = mins[1];
else
tmp[1] = maxs[1];
if( i & 4 )
tmp[2] = mins[2];
else
tmp[2] = maxs[2];
VectorScale( forward, tmp[0], v );
VectorMA( v, -tmp[1], right, v );
VectorMA( v, tmp[2], up, v );
for( j = 0; j < 3; j++ )
{
if( v[j] < tmins[j] )
tmins[j] = v[j];
if( v[j] > tmaxs[j] )
tmaxs[j] = v[j];
}
}
}
/*
=============
R_DrawBEntitiesOnList
=============
*/
static void R_DrawBEntitiesOnList( void )
{
int i, clipflags;
vec3_t oldorigin;
vec3_t mins, maxs;
float minmaxs[6];
mnode_t *topnode;
VectorCopy( tr.modelorg, oldorigin );
insubmodel = true;
for( i = 0; i < tr.draw_list->num_edge_entities && !RI.onlyClientDraw; i++ )
{
int k;
RI.currententity = tr.draw_list->edge_entities[i];
RI.currentmodel = RI.currententity->model;
if( !RI.currentmodel )
continue;
if( RI.currentmodel->nummodelsurfaces == 0 )
continue; // clip brush only
if( RI.currentmodel->type != mod_brush )
continue;
// see if the bounding box lets us trivially reject, also sets
// trivial accept status
RotatedBBox( RI.currentmodel->mins, RI.currentmodel->maxs,
RI.currententity->angles, mins, maxs );
VectorAdd( mins, RI.currententity->origin, minmaxs );
VectorAdd( maxs, RI.currententity->origin, ( minmaxs + 3 ));
clipflags = R_BmodelCheckBBox( minmaxs );
if( clipflags == BMODEL_FULLY_CLIPPED )
continue; // off the edge of the screen
// clipflags = 0;
topnode = R_FindTopnode( minmaxs, minmaxs + 3 );
if( !topnode )
continue; // no part in a visible leaf
VectorCopy( RI.currententity->origin, r_entorigin );
VectorSubtract( RI.vieworg, r_entorigin, tr.modelorg );
// VectorSubtract (r_origin, RI.currententity->origin, modelorg);
r_pcurrentvertbase = RI.currentmodel->vertexes;
// FIXME: stop transforming twice
R_RotateBmodel();
// calculate dynamic lighting for bmodel
for( k = 0; k < MAX_DLIGHTS; k++ )
{
dlight_t *l = &tr.dlights[k];
vec3_t origin_l, oldorigin;
if( l->die < gp_cl->time || !l->radius )
continue;
VectorCopy( l->origin, oldorigin ); // save lightorigin
Matrix4x4_CreateFromEntity( RI.objectMatrix, RI.currententity->angles, RI.currententity->origin, 1 );
Matrix4x4_VectorITransform( RI.objectMatrix, l->origin, origin_l );
VectorCopy( origin_l, l->origin ); // move light in bmodel space
R_MarkLights( l, 1 << k, RI.currentmodel->nodes + RI.currentmodel->hulls[0].firstclipnode );
VectorCopy( oldorigin, l->origin ); // restore lightorigin
}
RI.currententity->topnode = topnode;
if( topnode->contents >= 0 )
{
// not a leaf; has to be clipped to the world BSP
r_clipflags = clipflags;
R_DrawSolidClippedSubmodelPolygons( RI.currentmodel, topnode );
}
else
{
// falls entirely in one leaf, so we just put all the
// edges in the edge list and let 1/z sorting handle
// drawing order
R_DrawSubmodelPolygons( RI.currentmodel, clipflags, topnode );
}
RI.currententity->topnode = NULL;
// put back world rotation and frustum clipping
// FIXME: R_RotateBmodel should just work off base_vxx
VectorCopy( RI.base_vpn, RI.vforward );
VectorCopy( RI.base_vup, RI.vup );
VectorCopy( RI.base_vright, RI.vright );
VectorCopy( oldorigin, tr.modelorg );
R_TransformFrustum();
}
insubmodel = false;
}
extern qboolean alphaspans;
/*
=============
R_DrawBEntitiesOnList
=============
*/
void R_DrawBrushModel( cl_entity_t *pent )
{
int i, clipflags;
vec3_t oldorigin;
vec3_t mins, maxs;
float minmaxs[6];
mnode_t *topnode;
int k;
edge_t ledges[NUMSTACKEDGES
+ (( CACHE_SIZE - 1 ) / sizeof( edge_t )) + 1];
surf_t lsurfs[NUMSTACKSURFACES
+ (( CACHE_SIZE - 1 ) / sizeof( surf_t )) + 1];
if( !RI.drawWorld )
return;
if( auxedges )
{
r_edges = auxedges;
}
else
{
r_edges = (edge_t *)
(((uintptr_t)&ledges[0] + CACHE_SIZE - 1 ) & ~( CACHE_SIZE - 1 ));
}
if( r_surfsonstack )
{
surfaces = (surf_t *)(((uintptr_t)&lsurfs[0] + CACHE_SIZE - 1 ) & ~( CACHE_SIZE - 1 ));
surf_max = &surfaces[r_cnumsurfs];
// surface 0 doesn't really exist; it's just a dummy because index 0
// is used to indicate no edge attached to surface
memset( &surfaces[0], 0, sizeof( surf_t ));
surfaces--;
R_SurfacePatch();
}
R_BeginEdgeFrame();
VectorCopy( tr.modelorg, oldorigin );
insubmodel = true;
if( !RI.currentmodel )
return;
if( RI.currentmodel->nummodelsurfaces == 0 )
return; // clip brush only
if( RI.currentmodel->type != mod_brush )
return;
// see if the bounding box lets us trivially reject, also sets
// trivial accept status
RotatedBBox( RI.currentmodel->mins, RI.currentmodel->maxs,
RI.currententity->angles, mins, maxs );
VectorAdd( mins, RI.currententity->origin, minmaxs );
VectorAdd( maxs, RI.currententity->origin, ( minmaxs + 3 ));
clipflags = R_BmodelCheckBBox( minmaxs );
if( clipflags == BMODEL_FULLY_CLIPPED )
return; // off the edge of the screen
// clipflags = 0;
topnode = R_FindTopnode( minmaxs, minmaxs + 3 );
if( !topnode )
return; // no part in a visible leaf
alphaspans = true;
VectorCopy( RI.currententity->origin, r_entorigin );
VectorSubtract( RI.vieworg, r_entorigin, tr.modelorg );
// VectorSubtract (r_origin, RI.currententity->origin, modelorg);
r_pcurrentvertbase = RI.currentmodel->vertexes;
// FIXME: stop transforming twice
R_RotateBmodel();
// calculate dynamic lighting for bmodel
for( k = 0; k < MAX_DLIGHTS; k++ )
{
dlight_t *l = &tr.dlights[k];
vec3_t origin_l, oldorigin;
if( l->die < gp_cl->time || !l->radius )
continue;
VectorCopy( l->origin, oldorigin ); // save lightorigin
Matrix4x4_CreateFromEntity( RI.objectMatrix, RI.currententity->angles, RI.currententity->origin, 1 );
Matrix4x4_VectorITransform( RI.objectMatrix, l->origin, origin_l );
tr.modelviewIdentity = false;
VectorCopy( origin_l, l->origin ); // move light in bmodel space
R_MarkLights( l, 1 << k, RI.currentmodel->nodes + RI.currentmodel->hulls[0].firstclipnode );
VectorCopy( oldorigin, l->origin ); // restore lightorigin*/
}
RI.currententity->topnode = topnode;
if( topnode->contents >= 0 )
{
// not a leaf; has to be clipped to the world BSP
r_clipflags = clipflags;
R_DrawSolidClippedSubmodelPolygons( RI.currentmodel, topnode );
}
else
{
// falls entirely in one leaf, so we just put all the
// edges in the edge list and let 1/z sorting handle
// drawing order
R_DrawSubmodelPolygons( RI.currentmodel, clipflags, topnode );
}
RI.currententity->topnode = NULL;
// put back world rotation and frustum clipping
// FIXME: R_RotateBmodel should just work off base_vxx
VectorCopy( RI.base_vpn, RI.vforward );
VectorCopy( RI.base_vup, RI.vup );
VectorCopy( RI.base_vright, RI.vright );
VectorCopy( oldorigin, tr.modelorg );
R_TransformFrustum();
insubmodel = false;
R_ScanEdges();
alphaspans = false;
}
/*
================
R_EdgeDrawing
================
*/
static void R_EdgeDrawing( void )
{
edge_t ledges[NUMSTACKEDGES
+ (( CACHE_SIZE - 1 ) / sizeof( edge_t )) + 1];
surf_t lsurfs[NUMSTACKSURFACES
+ (( CACHE_SIZE - 1 ) / sizeof( surf_t )) + 1];
if( !RI.drawWorld )
return;
if( auxedges )
{
r_edges = auxedges;
}
else
{
r_edges = (edge_t *)
(((uintptr_t)&ledges[0] + CACHE_SIZE - 1 ) & ~( CACHE_SIZE - 1 ));
}
if( r_surfsonstack )
{
surfaces = (surf_t *)(((uintptr_t)&lsurfs + CACHE_SIZE - 1 ) & ~( CACHE_SIZE - 1 ));
surf_max = &surfaces[r_cnumsurfs];
// surface 0 doesn't really exist; it's just a dummy because index 0
// is used to indicate no edge attached to surface
memset( surfaces, 0, sizeof( surf_t ));
surfaces--;
R_SurfacePatch();
}
R_BeginEdgeFrame();
// this will prepare edges
R_RenderWorld();
// move brushes to separate list to merge with edges?
R_DrawBEntitiesOnList();
// display all edges
R_ScanEdges();
}
/*
===============
R_MarkLeaves
===============
*/
static void R_MarkLeaves( void )
{
byte *vis;
mnode_t *node;
int i;
if( r_oldviewcluster == r_viewcluster && !r_novis.value && r_viewcluster != -1 )
return;
tr.visframecount++;
r_oldviewcluster = r_viewcluster;
gEngfuncs.R_FatPVS( RI.pvsorigin, REFPVS_RADIUS, RI.visbytes, FBitSet( RI.params, RP_OLDVIEWLEAF ), false );
vis = RI.visbytes;
for( i = 0; i < WORLDMODEL->numleafs; i++ )
{
if( vis[i >> 3] & ( 1 << ( i & 7 )))
{
node = (mnode_t *) &WORLDMODEL->leafs[i + 1];
do
{
if( node->visframe == tr.visframecount )
break;
node->visframe = tr.visframecount;
node = node->parent;
}
while( node );
}
}
}
/*
================
R_RenderScene
R_SetupRefParams must be called right before
================
*/
void GAME_EXPORT R_RenderScene( void )
{
if( !WORLDMODEL && RI.drawWorld )
gEngfuncs.Host_Error( "%s: NULL worldmodel\n", __func__ );
// frametime is valid only for normal pass
if( RP_NORMALPASS( ))
tr.frametime = gp_cl->time - gp_cl->oldtime;
else
tr.frametime = 0.0;
// begin a new frame
tr.framecount++;
if( tr.map_unload )
{
D_FlushCaches();
tr.map_unload = false;
}
R_SetupFrustum();
R_SetupFrame();
R_PushDlights();
R_SetupModelviewMatrix( RI.worldviewMatrix );
R_SetupProjectionMatrix( RI.projectionMatrix );
Matrix4x4_Concat( RI.worldviewProjectionMatrix, RI.projectionMatrix, RI.worldviewMatrix );
tr.modelviewIdentity = true;
// R_SetupGL( true );
// R_Clear( ~0 );
R_MarkLeaves();
// R_PushDlights (r_worldmodel); ??
// R_DrawWorld();
R_EdgeDrawing();
gEngfuncs.CL_ExtraUpdate(); // don't let sound get messed up if going slow
R_DrawEntitiesOnList();
// R_DrawWaterSurfaces();
// R_EndGL();
}
void R_GammaChanged( qboolean do_reset_gamma )
{
if( do_reset_gamma ) // unused
return;
D_FlushCaches( );
}
/*
===============
R_BeginFrame
===============
*/
void GAME_EXPORT R_BeginFrame( qboolean clearScene )
{
R_Set2DMode( true );
// draw buffer stuff
// pglDrawBuffer( GL_BACK );
// update texture parameters
// if( FBitSet( gl_texture_nearest->flags|gl_lightmap_nearest->flags|gl_texture_anisotropy->flags|gl_texture_lodbias->flags, FCVAR_CHANGED ))
// R_SetTextureParameters();
gEngfuncs.CL_ExtraUpdate();
}
/*
===============
R_SetupRefParams
set initial params for renderer
===============
*/
void R_SetupRefParams( const ref_viewpass_t *rvp )
{
RI.params = RP_NONE;
RI.drawWorld = FBitSet( rvp->flags, RF_DRAW_WORLD );
RI.onlyClientDraw = FBitSet( rvp->flags, RF_ONLY_CLIENTDRAW );
if( !FBitSet( rvp->flags, RF_DRAW_CUBEMAP ))
RI.drawOrtho = FBitSet( rvp->flags, RF_DRAW_OVERVIEW );
else
RI.drawOrtho = false;
// setup viewport
RI.viewport[0] = rvp->viewport[0];
RI.viewport[1] = rvp->viewport[1];
RI.viewport[2] = rvp->viewport[2];
RI.viewport[3] = rvp->viewport[3];
// calc FOV
RI.fov_x = rvp->fov_x;
RI.fov_y = rvp->fov_y;
VectorCopy( rvp->vieworigin, RI.vieworg );
VectorCopy( rvp->viewangles, RI.viewangles );
VectorCopy( rvp->vieworigin, RI.pvsorigin );
}
/*
===============
R_RenderFrame
===============
*/
void GAME_EXPORT R_RenderFrame( const ref_viewpass_t *rvp )
{
if( r_norefresh->value )
return;
// prevent cache overrun
if( gpGlobals->height > vid.height || gpGlobals->width > vid.width )
return;
// setup the initial render params
R_SetupRefParams( rvp );
// completely override rendering
if( gEngfuncs.drawFuncs->GL_RenderFrame != NULL )
{
tr.fCustomRendering = true;
if( gEngfuncs.drawFuncs->GL_RenderFrame( rvp ))
{
// R_GatherPlayerLight();
tr.realframecount++;
tr.fResetVis = true;
return;
}
}
tr.fCustomRendering = false;
if( !RI.onlyClientDraw )
R_RunViewmodelEvents();
tr.realframecount++; // right called after viewmodel events
R_RenderScene();
return;
}
/*
===============
R_EndFrame
===============
*/
void GAME_EXPORT R_EndFrame( void )
{
// flush any remaining 2D bits
R_Set2DMode( false );
// blit pixels
R_BlitScreen();
}
/*
===============
R_DrawCubemapView
===============
*/
void R_DrawCubemapView( const vec3_t origin, const vec3_t angles, int size )
{
ref_viewpass_t rvp;
// basic params
rvp.flags = rvp.viewentity = 0;
SetBits( rvp.flags, RF_DRAW_WORLD );
SetBits( rvp.flags, RF_DRAW_CUBEMAP );
rvp.viewport[0] = rvp.viewport[1] = 0;
rvp.viewport[2] = rvp.viewport[3] = size;
rvp.fov_x = rvp.fov_y = 90.0f; // this is a final fov value
// setup origin & angles
VectorCopy( origin, rvp.vieworigin );
VectorCopy( angles, rvp.viewangles );
R_RenderFrame( &rvp );
RI.viewleaf = NULL; // force markleafs next frame
}
/*
===============
R_NewMap
===============
*/
void GAME_EXPORT R_NewMap( void )
{
int i;
model_t *world = WORLDMODEL;
r_viewcluster = -1;
tr.draw_list->num_solid_entities = 0;
tr.draw_list->num_trans_entities = 0;
tr.draw_list->num_beam_entities = 0;
tr.draw_list->num_edge_entities = 0;
R_ClearDecals(); // clear all level decals
R_StudioResetPlayerModels();
r_cnumsurfs = sw_maxsurfs.value;
if( r_cnumsurfs <= MINSURFACES )
r_cnumsurfs = MINSURFACES;
if( r_cnumsurfs > NUMSTACKSURFACES )
{
surfaces = Mem_Calloc( r_temppool, r_cnumsurfs * sizeof( surf_t ));
surface_p = surfaces;
surf_max = &surfaces[r_cnumsurfs];
r_surfsonstack = false;
// surface 0 doesn't really exist; it's just a dummy because index 0
// is used to indicate no edge attached to surface
surfaces--;
R_SurfacePatch();
}
else
{
r_surfsonstack = true;
}
r_numallocatededges = sw_maxedges.value;
if( r_numallocatededges < MINEDGES )
r_numallocatededges = MINEDGES;
if( r_numallocatededges <= NUMSTACKEDGES )
{
auxedges = NULL;
}
else
{
auxedges = Mem_Malloc( r_temppool, r_numallocatededges * sizeof( edge_t ));
}
// clear out efrags in case the level hasn't been reloaded
for( i = 0; i < world->numleafs; i++ )
world->leafs[i + 1].efrags = NULL;
tr.sample_size = gEngfuncs.Mod_SampleSizeForFace( &world->surfaces[0] );
for( i = 1; i < world->numsurfaces; i++ )
{
int sample_size = gEngfuncs.Mod_SampleSizeForFace( &world->surfaces[i] );
if( sample_size != tr.sample_size )
{
tr.sample_size = -1;
break;
}
}
tr.sample_bits = -1;
if( tr.sample_size != -1 )
{
uint sample_pot;
tr.sample_bits = 0;
for( sample_pot = 1; sample_pot < tr.sample_size; sample_pot <<= 1, tr.sample_bits++ )
;
}
gEngfuncs.Con_Printf( "Map sample size is %d\n", tr.sample_size );
}
/*
================
R_InitTurb
================
*/
static void R_InitTurb( void )
{
int i;
for( i = 0; i < 1280; i++ )
{
sintable[i] = AMP + sin( i * 3.14159 * 2 / CYCLE ) * AMP;
intsintable[i] = AMP2 + sin( i * 3.14159 * 2 / CYCLE ) * AMP2; // AMP2, not 20
blanktable[i] = 0; // PGM
}
}
qboolean GAME_EXPORT R_Init( void )
{
qboolean glblit = false;
RETRIEVE_ENGINE_SHARED_CVAR_LIST();
gEngfuncs.Cvar_RegisterVariable( &sw_clearcolor );
gEngfuncs.Cvar_RegisterVariable( &sw_drawflat );
gEngfuncs.Cvar_RegisterVariable( &sw_draworder );
gEngfuncs.Cvar_RegisterVariable( &sw_maxedges );
gEngfuncs.Cvar_RegisterVariable( &sw_maxsurfs );
gEngfuncs.Cvar_RegisterVariable( &sw_mipscale );
gEngfuncs.Cvar_RegisterVariable( &sw_mipcap );
gEngfuncs.Cvar_RegisterVariable( &sw_surfcacheoverride );
gEngfuncs.Cvar_RegisterVariable( &sw_waterwarp );
gEngfuncs.Cvar_RegisterVariable( &sw_notransbrushes );
gEngfuncs.Cvar_RegisterVariable( &sw_noalphabrushes );
gEngfuncs.Cvar_RegisterVariable( &r_traceglow );
#ifndef DISABLE_TEXFILTER
gEngfuncs.Cvar_RegisterVariable( &sw_texfilt );
#endif
gEngfuncs.Cvar_RegisterVariable( &r_novis );
gEngfuncs.Cvar_RegisterVariable( &r_studio_sort_textures );
r_temppool = Mem_AllocPool( "ref_soft zone" );
glblit = !!gEngfuncs.Sys_CheckParm( "-glblit" );
// create the window and set up the context
if( !glblit && !gEngfuncs.R_Init_Video( REF_SOFTWARE )) // request software blitter
{
gEngfuncs.R_Free_Video();
gEngfuncs.Con_Printf( "failed to initialize software blitter, fallback to glblit\n" );
glblit = true;
}
if( glblit && !gEngfuncs.R_Init_Video( REF_GL )) // request GL context
{
gEngfuncs.R_Free_Video();
return false;
}
// see R_ProcessEntData for tr.entities initialization
tr.movevars = (movevars_t *)ENGINE_GET_PARM( PARM_GET_MOVEVARS_PTR );
tr.palette = (color24 *)ENGINE_GET_PARM( PARM_GET_PALETTE_PTR );
tr.viewent = (cl_entity_t *)ENGINE_GET_PARM( PARM_GET_VIEWENT_PTR );
tr.texgammatable = (byte *)ENGINE_GET_PARM( PARM_GET_TEXGAMMATABLE_PTR );
tr.lightgammatable = (uint *)ENGINE_GET_PARM( PARM_GET_LIGHTGAMMATABLE_PTR );
tr.screengammatable = (uint *)ENGINE_GET_PARM( PARM_GET_SCREENGAMMATABLE_PTR );
tr.lineargammatable = (uint *)ENGINE_GET_PARM( PARM_GET_LINEARGAMMATABLE_PTR );
tr.dlights = (dlight_t *)ENGINE_GET_PARM( PARM_GET_DLIGHTS_PTR );
tr.elights = (dlight_t *)ENGINE_GET_PARM( PARM_GET_ELIGHTS_PTR );
if( !R_InitBlit( glblit ))
{
gEngfuncs.R_Free_Video();
return false;
}
R_InitImages();
// init draw stack
tr.draw_list = &tr.draw_stack[0];
tr.draw_stack_pos = 0;
qfrustum.view_clipplanes[0].leftedge = true;
qfrustum.view_clipplanes[1].rightedge = true;
qfrustum.view_clipplanes[1].leftedge = qfrustum.view_clipplanes[2].leftedge = qfrustum.view_clipplanes[3].leftedge = false;
qfrustum.view_clipplanes[0].rightedge = qfrustum.view_clipplanes[2].rightedge = qfrustum.view_clipplanes[3].rightedge = false;
R_StudioInit();
R_SpriteInit();
R_InitTurb();
GL_InitRandomTable();
return true;
}
void GAME_EXPORT R_Shutdown( void )
{
R_ShutdownImages();
gEngfuncs.R_Free_Video();
}
/*
===============
CL_FxBlend
===============
*/
int CL_FxBlend( cl_entity_t *e )
{
int blend = 0;
float offset, dist;
vec3_t tmp;
offset = ((int)e->index ) * 363.0f; // Use ent index to de-sync these fx
switch( e->curstate.renderfx )
{
case kRenderFxPulseSlowWide:
blend = e->curstate.renderamt + 0x40 * sin( gp_cl->time * 2 + offset );
break;
case kRenderFxPulseFastWide:
blend = e->curstate.renderamt + 0x40 * sin( gp_cl->time * 8 + offset );
break;
case kRenderFxPulseSlow:
blend = e->curstate.renderamt + 0x10 * sin( gp_cl->time * 2 + offset );
break;
case kRenderFxPulseFast:
blend = e->curstate.renderamt + 0x10 * sin( gp_cl->time * 8 + offset );
break;
case kRenderFxFadeSlow:
if( RP_NORMALPASS( ))
{
if( e->curstate.renderamt > 0 )
e->curstate.renderamt -= 1;
else
e->curstate.renderamt = 0;
}
blend = e->curstate.renderamt;
break;
case kRenderFxFadeFast:
if( RP_NORMALPASS( ))
{
if( e->curstate.renderamt > 3 )
e->curstate.renderamt -= 4;
else
e->curstate.renderamt = 0;
}
blend = e->curstate.renderamt;
break;
case kRenderFxSolidSlow:
if( RP_NORMALPASS( ))
{
if( e->curstate.renderamt < 255 )
e->curstate.renderamt += 1;
else
e->curstate.renderamt = 255;
}
blend = e->curstate.renderamt;
break;
case kRenderFxSolidFast:
if( RP_NORMALPASS( ))
{
if( e->curstate.renderamt < 252 )
e->curstate.renderamt += 4;
else
e->curstate.renderamt = 255;
}
blend = e->curstate.renderamt;
break;
case kRenderFxStrobeSlow:
blend = 20 * sin( gp_cl->time * 4 + offset );
if( blend < 0 )
blend = 0;
else
blend = e->curstate.renderamt;
break;
case kRenderFxStrobeFast:
blend = 20 * sin( gp_cl->time * 16 + offset );
if( blend < 0 )
blend = 0;
else
blend = e->curstate.renderamt;
break;
case kRenderFxStrobeFaster:
blend = 20 * sin( gp_cl->time * 36 + offset );
if( blend < 0 )
blend = 0;
else
blend = e->curstate.renderamt;
break;
case kRenderFxFlickerSlow:
blend = 20 * ( sin( gp_cl->time * 2 ) + sin( gp_cl->time * 17 + offset ));
if( blend < 0 )
blend = 0;
else
blend = e->curstate.renderamt;
break;
case kRenderFxFlickerFast:
blend = 20 * ( sin( gp_cl->time * 16 ) + sin( gp_cl->time * 23 + offset ));
if( blend < 0 )
blend = 0;
else
blend = e->curstate.renderamt;
break;
case kRenderFxHologram:
case kRenderFxDistort:
VectorCopy( e->origin, tmp );
VectorSubtract( tmp, RI.vieworg, tmp );
dist = DotProduct( tmp, RI.vforward );
// turn off distance fade
if( e->curstate.renderfx == kRenderFxDistort )
dist = 1;
if( dist <= 0 )
{
blend = 0;
}
else
{
e->curstate.renderamt = 180;
if( dist <= 100 )
blend = e->curstate.renderamt;
else
blend = (int) (( 1.0f - ( dist - 100 ) * ( 1.0f / 400.0f )) * e->curstate.renderamt );
blend += gEngfuncs.COM_RandomLong( -32, 31 );
}
break;
default:
blend = e->curstate.renderamt;
break;
}
blend = bound( 0, blend, 255 );
return blend;
}