You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2084 lines
48 KiB
2084 lines
48 KiB
/* |
|
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", 0, ""); |
|
CVAR_DEFINE_AUTO( sw_draworder, "0", 0, ""); |
|
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, "1", 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; |
|
} |
|
#if 0 |
|
/* |
|
================ |
|
R_GetEntityRenderMode |
|
|
|
check for texture flags |
|
================ |
|
*/ |
|
int R_GetEntityRenderMode( cl_entity_t *ent ) |
|
{ |
|
int i, opaque, trans; |
|
mstudiotexture_t *ptexture; |
|
cl_entity_t *oldent; |
|
model_t *model; |
|
studiohdr_t *phdr; |
|
|
|
oldent = RI.currententity; |
|
RI.currententity = ent; |
|
return ent->curstate.rendermode; |
|
} |
|
#endif |
|
|
|
void GAME_EXPORT R_AllowFog( qboolean allowed ) |
|
{ |
|
} |
|
|
|
/* |
|
=============== |
|
R_OpaqueEntity |
|
|
|
Opaque entity can be brush or studio model but sprite |
|
=============== |
|
*/ |
|
static qboolean R_OpaqueEntity( cl_entity_t *ent ) |
|
{ |
|
int rendermode = R_GetEntityRenderMode( ent ); |
|
|
|
if( rendermode == kRenderNormal ) |
|
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; |
|
} |
|
|
|
#if 1 |
|
|
|
/* |
|
=============== |
|
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 ); |
|
} |
|
|
|
#endif |
|
|
|
/* |
|
=============== |
|
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 ) |
|
{ |
|
int bits; |
|
#if 0 |
|
if( gEngfuncs.CL_IsDevOverviewMode( )) |
|
pglClearColor( 0.0f, 1.0f, 0.0f, 1.0f ); // green background (Valve rules) |
|
else pglClearColor( 0.5f, 0.5f, 0.5f, 1.0f ); |
|
|
|
bits = GL_DEPTH_BUFFER_BIT; |
|
|
|
if( glState.stencilEnabled ) |
|
bits |= GL_STENCIL_BUFFER_BIT; |
|
|
|
bits &= bitMask; |
|
|
|
pglClear( bits ); |
|
|
|
// change ordering for overview |
|
if( RI.drawOrtho ) |
|
{ |
|
gldepthmin = 1.0f; |
|
gldepthmax = 0.0f; |
|
} |
|
else |
|
{ |
|
gldepthmin = 0.0f; |
|
gldepthmax = 1.0f; |
|
} |
|
|
|
pglDepthFunc( GL_LEQUAL ); |
|
pglDepthRange( gldepthmin, gldepthmax ); |
|
#endif |
|
memset( vid.buffer, 0, vid.width * vid.height *2); |
|
} |
|
|
|
//============================================================================= |
|
/* |
|
=============== |
|
R_GetFarClip |
|
=============== |
|
*/ |
|
static float R_GetFarClip( void ) |
|
{ |
|
if( WORLDMODEL && RI.drawWorld ) |
|
return MOVEVARS->zmax * 1.73f; |
|
return 2048.0f; |
|
} |
|
|
|
/* |
|
=============== |
|
R_SetupFrustum |
|
=============== |
|
*/ |
|
void R_SetupFrustum( void ) |
|
{ |
|
#if 1 |
|
//ref_overview_t *ov = gEngfuncs.GetOverviewParms(); |
|
|
|
/*if( RP_NORMALPASS() && ( ENGINE_GET_PARM( PARM_WATER_LEVEL ) >= 3 ) && ENGINE_GET_PARM( PARM_QUAKE_COMPATIBLE )) |
|
{ |
|
RI.fov_x = atan( tan( DEG2RAD( RI.fov_x ) / 2 ) * ( 0.97 + sin( gpGlobals->time * 1.5 ) * 0.03 )) * 2 / (M_PI / 180.0); |
|
RI.fov_y = atan( tan( DEG2RAD( RI.fov_y ) / 2 ) * ( 1.03 - sin( gpGlobals->time * 1.5 ) * 0.03 )) * 2 / (M_PI / 180.0); |
|
}*/ |
|
|
|
// build the transformation matrix for the given view angles |
|
AngleVectors( RI.viewangles, RI.vforward, RI.vright, RI.vup ); |
|
|
|
//if( !r_lockfrustum->value ) |
|
{ |
|
VectorCopy( RI.vieworg, RI.cullorigin ); |
|
VectorCopy( RI.vforward, RI.cull_vforward ); |
|
VectorCopy( RI.vright, RI.cull_vright ); |
|
VectorCopy( RI.vup, RI.cull_vup ); |
|
} |
|
|
|
// if( RI.drawOrtho ) |
|
// GL_FrustumInitOrtho( &RI.frustum, ov->xLeft, ov->xRight, ov->yTop, ov->yBottom, ov->zNear, ov->zFar ); |
|
// else GL_FrustumInitProj( &RI.frustum, 0.0f, R_GetFarClip(), RI.fov_x, RI.fov_y ); // NOTE: we ignore nearplane here (mirrors only) |
|
#endif |
|
} |
|
|
|
/* |
|
============= |
|
R_SetupProjectionMatrix |
|
============= |
|
*/ |
|
static void R_SetupProjectionMatrix( matrix4x4 m ) |
|
{ |
|
#if 1 |
|
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 ); |
|
#endif |
|
} |
|
|
|
/* |
|
============= |
|
R_SetupModelviewMatrix |
|
============= |
|
*/ |
|
static void R_SetupModelviewMatrix( matrix4x4 m ) |
|
{ |
|
#if 1 |
|
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] ); |
|
#endif |
|
} |
|
|
|
/* |
|
============= |
|
R_LoadIdentity |
|
============= |
|
*/ |
|
void R_LoadIdentity( void ) |
|
{ |
|
#if 0 |
|
if( tr.modelviewIdentity ) return; |
|
|
|
Matrix4x4_LoadIdentity( RI.objectMatrix ); |
|
Matrix4x4_Copy( RI.modelviewMatrix, RI.worldviewMatrix ); |
|
|
|
pglMatrixMode( GL_MODELVIEW ); |
|
GL_LoadMatrix( RI.modelviewMatrix ); |
|
tr.modelviewIdentity = true; |
|
#endif |
|
} |
|
|
|
/* |
|
============= |
|
R_RotateForEntity |
|
============= |
|
*/ |
|
void R_RotateForEntity( cl_entity_t *e ) |
|
{ |
|
#if 0 |
|
float scale = 1.0f; |
|
|
|
if( e == gEngfuncs.GetEntityByIndex( 0 ) ) |
|
{ |
|
R_LoadIdentity(); |
|
return; |
|
} |
|
|
|
if( e->model->type != mod_brush && e->curstate.scale > 0.0f ) |
|
scale = e->curstate.scale; |
|
|
|
Matrix4x4_CreateFromEntity( RI.objectMatrix, e->angles, e->origin, scale ); |
|
Matrix4x4_ConcatTransforms( RI.modelviewMatrix, RI.worldviewMatrix, RI.objectMatrix ); |
|
|
|
pglMatrixMode( GL_MODELVIEW ); |
|
GL_LoadMatrix( RI.modelviewMatrix ); |
|
tr.modelviewIdentity = false; |
|
#endif |
|
} |
|
|
|
/* |
|
============= |
|
R_TranslateForEntity |
|
============= |
|
*/ |
|
void R_TranslateForEntity( cl_entity_t *e ) |
|
{ |
|
#if 0 |
|
float scale = 1.0f; |
|
|
|
if( e == gEngfuncs.GetEntityByIndex( 0 ) ) |
|
{ |
|
R_LoadIdentity(); |
|
return; |
|
} |
|
|
|
if( e->model->type != mod_brush && e->curstate.scale > 0.0f ) |
|
scale = e->curstate.scale; |
|
|
|
Matrix4x4_CreateFromEntity( RI.objectMatrix, vec3_origin, e->origin, scale ); |
|
Matrix4x4_ConcatTransforms( RI.modelviewMatrix, RI.worldviewMatrix, RI.objectMatrix ); |
|
|
|
pglMatrixMode( GL_MODELVIEW ); |
|
GL_LoadMatrix( RI.modelviewMatrix ); |
|
tr.modelviewIdentity = false; |
|
#endif |
|
} |
|
|
|
/* |
|
=============== |
|
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(); |
|
} |
|
#if 0 |
|
|
|
/* |
|
============= |
|
R_SetupGL |
|
============= |
|
*/ |
|
void R_SetupGL( qboolean set_gl_state ) |
|
{ |
|
R_SetupModelviewMatrix( RI.worldviewMatrix ); |
|
R_SetupProjectionMatrix( RI.projectionMatrix ); |
|
|
|
Matrix4x4_Concat( RI.worldviewProjectionMatrix, RI.projectionMatrix, RI.worldviewMatrix ); |
|
|
|
if( !set_gl_state ) return; |
|
|
|
if( RP_NORMALPASS( )) |
|
{ |
|
int x, x2, y, y2; |
|
|
|
// set up viewport (main, playersetup) |
|
x = floor( RI.viewport[0] * gpGlobals->width / gpGlobals->width ); |
|
x2 = ceil(( RI.viewport[0] + RI.viewport[2] ) * gpGlobals->width / gpGlobals->width ); |
|
y = floor( gpGlobals->height - RI.viewport[1] * gpGlobals->height / gpGlobals->height ); |
|
y2 = ceil( gpGlobals->height - ( RI.viewport[1] + RI.viewport[3] ) * gpGlobals->height / gpGlobals->height ); |
|
|
|
pglViewport( x, y2, x2 - x, y - y2 ); |
|
} |
|
else |
|
{ |
|
// envpass, mirrorpass |
|
pglViewport( RI.viewport[0], RI.viewport[1], RI.viewport[2], RI.viewport[3] ); |
|
} |
|
|
|
pglMatrixMode( GL_PROJECTION ); |
|
GL_LoadMatrix( RI.projectionMatrix ); |
|
|
|
pglMatrixMode( GL_MODELVIEW ); |
|
GL_LoadMatrix( RI.worldviewMatrix ); |
|
|
|
if( FBitSet( RI.params, RP_CLIPPLANE )) |
|
{ |
|
GLdouble clip[4]; |
|
mplane_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 ); |
|
} |
|
|
|
GL_Cull( GL_FRONT ); |
|
|
|
pglDisable( GL_BLEND ); |
|
pglDisable( GL_ALPHA_TEST ); |
|
pglColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); |
|
} |
|
|
|
/* |
|
============= |
|
R_EndGL |
|
============= |
|
*/ |
|
static void R_EndGL( void ) |
|
{ |
|
if( RI.params & RP_CLIPPLANE ) |
|
pglDisable( GL_CLIP_PLANE0 ); |
|
} |
|
|
|
#endif |
|
|
|
/* |
|
============= |
|
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; |
|
} |
|
|
|
extern void R_PolysetFillSpans8 ( void * ); |
|
extern void R_PolysetDrawSpansConstant8_33( void *pspanpackage); |
|
extern void R_PolysetDrawSpans8_33( void *pspanpackage); |
|
|
|
/* |
|
============= |
|
R_DrawEntitiesOnList |
|
============= |
|
*/ |
|
void R_DrawEntitiesOnList( void ) |
|
{ |
|
extern void (*d_pdrawspans)(void *); |
|
int i; |
|
//extern int d_aflatcolor; |
|
//d_aflatcolor = 0; |
|
tr.blend = 1.0f; |
|
// GL_CheckForErrors(); |
|
//RI.currententity = gEngfuncs.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 ); |
|
#if 0 |
|
// gradient debug (for colormap testing) |
|
{finalvert_t fv[3]; |
|
void R_AliasSetUpTransform (void); |
|
extern void (*d_pdrawspans)(void *); |
|
extern void R_PolysetFillSpans8 ( void * ); |
|
d_pdrawspans = R_PolysetFillSpans8; |
|
//RI.currententity = gEngfuncs.GetEntityByIndex(0); |
|
R_AliasSetUpTransform(); |
|
image_t *image = R_GetTexture(GL_LoadTexture("gfx/env/desertbk", NULL, 0, 0)); |
|
r_affinetridesc.pskin = image->pixels[0]; |
|
r_affinetridesc.skinwidth = image->width; |
|
r_affinetridesc.skinheight = image->height; |
|
R_SetupFinalVert( &fv[0], 0, -50, 50, 31 << 8, 0, 0); |
|
R_SetupFinalVert( &fv[1], 0, 50, 50, 0 << 8, image->width, 0); |
|
R_SetupFinalVert( &fv[2], 0, 0, 0, 0 << 8, image->width/2, image->height); |
|
R_RenderTriangle( &fv[0], &fv[1], &fv[2] ); |
|
} |
|
#endif |
|
|
|
|
|
break; |
|
default: |
|
break; |
|
} |
|
} |
|
|
|
// GL_CheckForErrors(); |
|
|
|
// quake-specific feature |
|
// R_DrawAlphaTextureChains(); |
|
|
|
// GL_CheckForErrors(); |
|
|
|
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; |
|
} |
|
} |
|
|
|
// GL_CheckForErrors(); |
|
|
|
if( !RI.onlyClientDraw ) |
|
{ |
|
gEngfuncs.CL_DrawEFX( tr.frametime, false ); |
|
} |
|
|
|
// GL_CheckForErrors(); |
|
|
|
if( RI.drawWorld ) |
|
gEngfuncs.pfnDrawNormalTriangles(); |
|
|
|
// GL_CheckForErrors(); |
|
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; |
|
} |
|
} |
|
|
|
// GL_CheckForErrors(); |
|
|
|
if( RI.drawWorld ) |
|
{ |
|
// pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); |
|
gEngfuncs.pfnDrawTransparentTriangles (); |
|
} |
|
|
|
// GL_CheckForErrors(); |
|
|
|
if( !RI.onlyClientDraw ) |
|
{ |
|
R_AllowFog( false ); |
|
gEngfuncs.CL_DrawEFX( tr.frametime, true ); |
|
R_AllowFog( true ); |
|
} |
|
|
|
//GL_CheckForErrors(); |
|
|
|
// pglDisable( GL_BLEND ); // Trinity Render issues |
|
GL_SetRenderMode(kRenderNormal); |
|
R_SetUpWorldTransform(); |
|
if( !RI.onlyClientDraw ) |
|
R_DrawViewModel(); |
|
gEngfuncs.CL_ExtraUpdate(); |
|
|
|
//GL_CheckForErrors(); |
|
} |
|
|
|
#if 1 |
|
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 |
|
=================== |
|
*/ |
|
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 |
|
============= |
|
*/ |
|
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; |
|
//r_dlightframecount = r_framecount; |
|
|
|
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 ( currententity->flags & RF_BEAM ) |
|
//continue; |
|
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); |
|
#if 0 |
|
mins[0] = mins[0] - 100; |
|
mins[1] = mins[1] - 100; |
|
mins[2] = mins[2] - 100; |
|
maxs[0] = maxs[0] + 100; |
|
maxs[1] = maxs[1] + 100; |
|
maxs[2] = maxs[2] + 100; |
|
#endif |
|
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 |
|
// this will reset RI.currententity, do we need this? |
|
//R_PushDlights (); |
|
/*if (clmodel->firstmodelsurface != 0) |
|
{ |
|
for (k=0 ; k<r_refdef2.numDlights ; k++) |
|
{ |
|
R_MarkLights (&r_refdef2.dlights[k], 1<<k, |
|
clmodel->nodes + clmodel->firstnode); |
|
} |
|
}*/ |
|
|
|
// calculate dynamic lighting for bmodel |
|
for( k = 0; k < MAX_DLIGHTS; k++ ) |
|
{ |
|
dlight_t *l = gEngfuncs.GetDynamicLight( k ); |
|
vec3_t origin_l, oldorigin; |
|
|
|
if( l->die < gpGlobals->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*/ |
|
//R_MarkLights( l, 1<<k, RI.currentmodel->nodes + RI.currentmodel->hulls[0].firstclipnode ); |
|
} |
|
|
|
// RI.currentmodel = tr.draw_list->solid_entities[i]->model; |
|
// RI.currententity = tr.draw_list->solid_entities[i]; |
|
RI.currententity->topnode = topnode; |
|
//ASSERT( RI.currentmodel == tr.draw_list->solid_entities[i]->model ); |
|
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 |
|
//ASSERT( RI.currentmodel == tr.draw_list->solid_entities[i]->model ); |
|
|
|
|
|
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; |
|
//r_dlightframecount = r_framecount; |
|
|
|
if (!RI.currentmodel) |
|
return; |
|
if (RI.currentmodel->nummodelsurfaces == 0) |
|
return; // clip brush only |
|
//if ( currententity->flags & RF_BEAM ) |
|
//continue; |
|
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); |
|
#if 0 |
|
mins[0] = mins[0] - 100; |
|
mins[1] = mins[1] - 100; |
|
mins[2] = mins[2] - 100; |
|
maxs[0] = maxs[0] + 100; |
|
maxs[1] = maxs[1] + 100; |
|
maxs[2] = maxs[2] + 100; |
|
#endif |
|
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 |
|
// this will reset RI.currententity, do we need this? |
|
//R_PushDlights (); |
|
/*if (clmodel->firstmodelsurface != 0) |
|
{ |
|
for (k=0 ; k<r_refdef2.numDlights ; k++) |
|
{ |
|
R_MarkLights (&r_refdef2.dlights[k], 1<<k, |
|
clmodel->nodes + clmodel->firstnode); |
|
} |
|
}*/ |
|
|
|
// calculate dynamic lighting for bmodel |
|
for( k = 0; k < MAX_DLIGHTS; k++ ) |
|
{ |
|
dlight_t *l = gEngfuncs.GetDynamicLight( k ); |
|
vec3_t origin_l, oldorigin; |
|
|
|
if( l->die < gpGlobals->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*/ |
|
//R_MarkLights( l, 1<<k, RI.currentmodel->nodes + RI.currentmodel->hulls[0].firstclipnode ); |
|
} |
|
|
|
// RI.currentmodel = tr.draw_list->solid_entities[i]->model; |
|
// RI.currententity = tr.draw_list->solid_entities[i]; |
|
RI.currententity->topnode = topnode; |
|
//ASSERT( RI.currentmodel == tr.draw_list->solid_entities[i]->model ); |
|
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 |
|
//ASSERT( RI.currentmodel == tr.draw_list->solid_entities[i]->model ); |
|
|
|
|
|
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; |
|
} |
|
|
|
#endif |
|
|
|
/* |
|
================ |
|
R_EdgeDrawing |
|
================ |
|
*/ |
|
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 (); |
|
} |
|
|
|
#if 0 |
|
/* |
|
=============== |
|
R_MarkLeaves |
|
|
|
Mark the leaves and nodes that are in the PVS for the current leaf |
|
=============== |
|
*/ |
|
void R_MarkLeaves( void ) |
|
{ |
|
qboolean novis = false; |
|
qboolean force = false; |
|
mleaf_t *leaf = NULL; |
|
mnode_t *node; |
|
vec3_t test; |
|
int i; |
|
|
|
if( !RI.drawWorld ) return; |
|
|
|
/*if( FBitSet( r_novis->flags, FCVAR_CHANGED ) || tr.fResetVis ) |
|
{ |
|
// force recalc viewleaf |
|
ClearBits( r_novis->flags, FCVAR_CHANGED ); |
|
tr.fResetVis = false; |
|
RI.viewleaf = NULL; |
|
}*/ |
|
|
|
VectorCopy( RI.pvsorigin, test ); |
|
|
|
if( RI.viewleaf != NULL ) |
|
{ |
|
// merge two leafs that can be a crossed-line contents |
|
if( RI.viewleaf->contents == CONTENTS_EMPTY ) |
|
{ |
|
VectorSet( test, RI.pvsorigin[0], RI.pvsorigin[1], RI.pvsorigin[2] - 16.0f ); |
|
leaf = gEngfuncs.Mod_PointInLeaf( test, WORLDMODEL->nodes ); |
|
} |
|
else |
|
{ |
|
VectorSet( test, RI.pvsorigin[0], RI.pvsorigin[1], RI.pvsorigin[2] + 16.0f ); |
|
leaf = gEngfuncs.Mod_PointInLeaf( test, WORLDMODEL->nodes ); |
|
} |
|
|
|
if(( leaf->contents != CONTENTS_SOLID ) && ( RI.viewleaf != leaf )) |
|
force = true; |
|
} |
|
|
|
if( RI.viewleaf == RI.oldviewleaf && RI.viewleaf != NULL && !force ) |
|
return; |
|
|
|
// development aid to let you run around |
|
// and see exactly where the pvs ends |
|
//if( sw_lockpvs->value ) return; |
|
|
|
RI.oldviewleaf = RI.viewleaf; |
|
tr.visframecount++; |
|
|
|
if( RI.drawOrtho || !RI.viewleaf || !WORLDMODEL->visdata ) |
|
novis = true; |
|
|
|
gEngfuncs.R_FatPVS( RI.pvsorigin, REFPVS_RADIUS, RI.visbytes, FBitSet( RI.params, RP_OLDVIEWLEAF ), novis ); |
|
if( force && !novis ) gEngfuncs.R_FatPVS( test, REFPVS_RADIUS, RI.visbytes, true, novis ); |
|
|
|
for( i = 0; i < WORLDMODEL->numleafs; i++ ) |
|
{ |
|
if( CHECKVISBIT( RI.visbytes, i )) |
|
{ |
|
node = (mnode_t *)&WORLDMODEL->leafs[i+1]; |
|
do |
|
{ |
|
if( node->visframe == tr.visframecount ) |
|
break; |
|
node->visframe = tr.visframecount; |
|
node = node->parent; |
|
} while( node ); |
|
} |
|
} |
|
} |
|
#else |
|
/* |
|
=============== |
|
R_MarkLeaves |
|
=============== |
|
*/ |
|
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); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
/* |
|
================ |
|
R_RenderScene |
|
|
|
R_SetupRefParams must be called right before |
|
================ |
|
*/ |
|
void GAME_EXPORT R_RenderScene( void ) |
|
{ |
|
if( !WORLDMODEL && RI.drawWorld ) |
|
gEngfuncs.Host_Error( "R_RenderView: NULL worldmodel\n" ); |
|
|
|
// frametime is valid only for normal pass |
|
if( RP_NORMALPASS( )) |
|
tr.frametime = gpGlobals->time - gpGlobals->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(); |
|
} |
|
|
|
/* |
|
=============== |
|
R_DoResetGamma |
|
|
|
gamma will be reset for |
|
some type of screenshots |
|
=============== |
|
*/ |
|
qboolean R_DoResetGamma( void ) |
|
{ |
|
// FIXME: this looks ugly. apply the backward gamma changes to the output image |
|
return false; |
|
#if 0 |
|
switch( cls.scrshot_action ) |
|
{ |
|
case scrshot_normal: |
|
if( CL_IsDevOverviewMode( )) |
|
return true; |
|
return false; |
|
case scrshot_snapshot: |
|
if( CL_IsDevOverviewMode( )) |
|
return true; |
|
return false; |
|
case scrshot_plaque: |
|
case scrshot_savegame: |
|
case scrshot_envshot: |
|
case scrshot_skyshot: |
|
case scrshot_mapshot: |
|
return true; |
|
default: |
|
return false; |
|
} |
|
#endif |
|
} |
|
|
|
/* |
|
=============== |
|
R_BeginFrame |
|
=============== |
|
*/ |
|
void GAME_EXPORT R_BeginFrame( qboolean clearScene ) |
|
{ |
|
#if 0 // unused |
|
if( R_DoResetGamma( )) |
|
{ |
|
gEngfuncs.BuildGammaTable( 1.8f, 0.0f ); |
|
D_FlushCaches( ); |
|
|
|
// next frame will be restored gamma |
|
SetBits( vid_brightness->flags, FCVAR_CHANGED ); |
|
SetBits( vid_gamma->flags, FCVAR_CHANGED ); |
|
} |
|
else |
|
#endif |
|
if( FBitSet( vid_gamma->flags, FCVAR_CHANGED ) || FBitSet( vid_brightness->flags, FCVAR_CHANGED )) |
|
{ |
|
gEngfuncs.BuildGammaTable( vid_gamma->value, vid_brightness->value ); |
|
|
|
D_FlushCaches( ); |
|
// next frame will be restored gamma |
|
ClearBits( vid_brightness->flags, FCVAR_CHANGED ); |
|
ClearBits( vid_gamma->flags, FCVAR_CHANGED ); |
|
} |
|
|
|
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 |
|
================ |
|
*/ |
|
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 ); |
|
|
|
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; |
|
} |
|
|
|
R_InitBlit( glblit ); |
|
|
|
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( gpGlobals->time * 2 + offset ); |
|
break; |
|
case kRenderFxPulseFastWide: |
|
blend = e->curstate.renderamt + 0x40 * sin( gpGlobals->time * 8 + offset ); |
|
break; |
|
case kRenderFxPulseSlow: |
|
blend = e->curstate.renderamt + 0x10 * sin( gpGlobals->time * 2 + offset ); |
|
break; |
|
case kRenderFxPulseFast: |
|
blend = e->curstate.renderamt + 0x10 * sin( gpGlobals->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( gpGlobals->time * 4 + offset ); |
|
if( blend < 0 ) blend = 0; |
|
else blend = e->curstate.renderamt; |
|
break; |
|
case kRenderFxStrobeFast: |
|
blend = 20 * sin( gpGlobals->time * 16 + offset ); |
|
if( blend < 0 ) blend = 0; |
|
else blend = e->curstate.renderamt; |
|
break; |
|
case kRenderFxStrobeFaster: |
|
blend = 20 * sin( gpGlobals->time * 36 + offset ); |
|
if( blend < 0 ) blend = 0; |
|
else blend = e->curstate.renderamt; |
|
break; |
|
case kRenderFxFlickerSlow: |
|
blend = 20 * (sin( gpGlobals->time * 2 ) + sin( gpGlobals->time * 17 + offset )); |
|
if( blend < 0 ) blend = 0; |
|
else blend = e->curstate.renderamt; |
|
break; |
|
case kRenderFxFlickerFast: |
|
blend = 20 * (sin( gpGlobals->time * 16 ) + sin( gpGlobals->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; |
|
} |
|
|
|
|