mirror of
https://github.com/FWGS/xash3d-fwgs
synced 2024-11-25 19:30:08 +01:00
2085 lines
48 KiB
C
2085 lines
48 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", 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;
|
|
}
|
|
|