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.
1968 lines
46 KiB
1968 lines
46 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 "mathlib.h" |
|
#include "library.h" |
|
//#include "beamdef.h" |
|
//#include "particledef.h" |
|
#include "entity_types.h" |
|
#include "mod_local.h" |
|
|
|
#define IsLiquidContents( cnt ) ( cnt == CONTENTS_WATER || cnt == CONTENTS_SLIME || cnt == CONTENTS_LAVA ) |
|
|
|
float gldepthmin, gldepthmax; |
|
ref_instance_t RI; |
|
|
|
|
|
// quake defines. will be refactored |
|
|
|
// view origin |
|
// |
|
vec3_t vup, base_vup; |
|
vec3_t vpn, base_vpn; |
|
vec3_t vright, base_vright; |
|
vec3_t r_origin; |
|
|
|
// |
|
// screen size info |
|
// |
|
float xcenter, ycenter; |
|
float xscale, yscale; |
|
float xscaleinv, yscaleinv; |
|
float xscaleshrink, yscaleshrink; |
|
float aliasxscale, aliasyscale, aliasxcenter, aliasycenter; |
|
|
|
int r_screenwidth; |
|
|
|
float verticalFieldOfView; |
|
float xOrigin, yOrigin; |
|
|
|
mplane_t screenedge[4]; |
|
|
|
|
|
// |
|
// refresh flags |
|
// |
|
int r_framecount = 1; // so frame counts initialized to 0 don't match |
|
int r_visframecount; |
|
int d_spanpixcount; |
|
int r_polycount; |
|
int r_drawnpolycount; |
|
int r_wholepolycount; |
|
|
|
int *pfrustum_indexes[4]; |
|
int r_frustum_indexes[4*6]; |
|
|
|
mleaf_t *r_viewleaf; |
|
int r_viewcluster, r_oldviewcluster; |
|
|
|
cvar_t *r_lefthand; |
|
cvar_t *sw_aliasstats; |
|
cvar_t *sw_allow_modex; |
|
cvar_t *sw_clearcolor; |
|
cvar_t *sw_drawflat; |
|
cvar_t *sw_draworder; |
|
cvar_t *sw_maxedges; |
|
cvar_t *sw_maxsurfs; |
|
cvar_t *sw_mode; |
|
cvar_t *sw_reportedgeout; |
|
cvar_t *sw_reportsurfout; |
|
cvar_t *sw_stipplealpha; |
|
cvar_t *sw_surfcacheoverride; |
|
cvar_t *sw_waterwarp; |
|
cvar_t *sw_texfilt; |
|
|
|
cvar_t *r_drawworld; |
|
cvar_t *r_drawentities; |
|
cvar_t *r_dspeeds; |
|
cvar_t *r_fullbright; |
|
cvar_t *r_lerpmodels; |
|
cvar_t *r_novis; |
|
cvar_t *r_lightmap; |
|
cvar_t *r_dynamic; |
|
|
|
cvar_t *r_speeds; |
|
cvar_t *r_lightlevel; //FIXME HACK |
|
|
|
cvar_t *vid_fullscreen; |
|
cvar_t *vid_gamma; |
|
|
|
//PGM |
|
cvar_t *sw_lockpvs; |
|
//PGM |
|
|
|
|
|
mleaf_t *r_viewleaf; |
|
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; |
|
|
|
qboolean r_dowarp; |
|
|
|
mvertex_t *r_pcurrentvertbase; |
|
|
|
int c_surf; |
|
int r_maxsurfsseen, r_maxedgesseen, r_cnumsurfs; |
|
qboolean r_surfsonstack; |
|
int r_clipflags; |
|
byte r_warpbuffer[WARP_WIDTH * WARP_HEIGHT]; |
|
int r_numallocatededges; |
|
|
|
float r_aliasuvscale = 1.0; |
|
|
|
/* |
|
================ |
|
R_ConcatRotations |
|
================ |
|
*/ |
|
void R_ConcatRotations (float in1[3][3], float in2[3][3], float out[3][3]) |
|
{ |
|
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + |
|
in1[0][2] * in2[2][0]; |
|
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + |
|
in1[0][2] * in2[2][1]; |
|
out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + |
|
in1[0][2] * in2[2][2]; |
|
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + |
|
in1[1][2] * in2[2][0]; |
|
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + |
|
in1[1][2] * in2[2][1]; |
|
out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + |
|
in1[1][2] * in2[2][2]; |
|
out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + |
|
in1[2][2] * in2[2][0]; |
|
out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + |
|
in1[2][2] * in2[2][1]; |
|
out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + |
|
in1[2][2] * in2[2][2]; |
|
} |
|
/* |
|
================ |
|
R_ConcatTransforms |
|
================ |
|
*/ |
|
void R_ConcatTransforms (float in1[3][4], float in2[3][4], float out[3][4]) |
|
{ |
|
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + |
|
in1[0][2] * in2[2][0]; |
|
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + |
|
in1[0][2] * in2[2][1]; |
|
out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + |
|
in1[0][2] * in2[2][2]; |
|
out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] + |
|
in1[0][2] * in2[2][3] + in1[0][3]; |
|
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + |
|
in1[1][2] * in2[2][0]; |
|
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + |
|
in1[1][2] * in2[2][1]; |
|
out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + |
|
in1[1][2] * in2[2][2]; |
|
out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] + |
|
in1[1][2] * in2[2][3] + in1[1][3]; |
|
out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + |
|
in1[2][2] * in2[2][0]; |
|
out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + |
|
in1[2][2] * in2[2][1]; |
|
out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + |
|
in1[2][2] * in2[2][2]; |
|
out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] + |
|
in1[2][2] * in2[2][3] + in1[2][3]; |
|
} |
|
|
|
|
|
|
|
|
|
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 R_AllowFog( qboolean allowed ) |
|
{ |
|
} |
|
|
|
/* |
|
=============== |
|
R_OpaqueEntity |
|
|
|
Opaque entity can be brush or studio model but sprite |
|
=============== |
|
*/ |
|
static qboolean R_OpaqueEntity( cl_entity_t *ent ) |
|
{ |
|
if( R_GetEntityRenderMode( ent ) == kRenderNormal ) |
|
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->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( 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 0 |
|
|
|
/* |
|
=============== |
|
R_WorldToScreen |
|
|
|
Convert a given point from world into screen space |
|
Returns true if we behind to screen |
|
=============== |
|
*/ |
|
int R_WorldToScreen( const vec3_t point, vec3_t screen ) |
|
{ |
|
matrix4x4 worldToScreen; |
|
qboolean behind; |
|
float w; |
|
|
|
if( !point || !screen ) |
|
return true; |
|
|
|
Matrix4x4_Copy( worldToScreen, RI.worldviewProjectionMatrix ); |
|
screen[0] = worldToScreen[0][0] * point[0] + worldToScreen[0][1] * point[1] + worldToScreen[0][2] * point[2] + worldToScreen[0][3]; |
|
screen[1] = worldToScreen[1][0] * point[0] + worldToScreen[1][1] * point[1] + worldToScreen[1][2] * point[2] + worldToScreen[1][3]; |
|
w = worldToScreen[3][0] * point[0] + worldToScreen[3][1] * point[1] + worldToScreen[3][2] * point[2] + worldToScreen[3][3]; |
|
screen[2] = 0.0f; // just so we have something valid here |
|
|
|
if( w < 0.001f ) |
|
{ |
|
screen[0] *= 100000; |
|
screen[1] *= 100000; |
|
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 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 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 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 R_ClearScene( void ) |
|
{ |
|
tr.draw_list->num_solid_entities = 0; |
|
tr.draw_list->num_trans_entities = 0; |
|
tr.draw_list->num_beam_entities = 0; |
|
|
|
// clear the scene befor start new frame |
|
if( gEngfuncs.drawFuncs->R_ClearScene != NULL ) |
|
gEngfuncs.drawFuncs->R_ClearScene(); |
|
|
|
} |
|
|
|
/* |
|
=============== |
|
R_AddEntity |
|
=============== |
|
*/ |
|
qboolean 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 ) && gEngfuncs.CL_FxBlend( clent ) <= 0 ) |
|
return true; // invisible |
|
|
|
if( type == ET_FRAGMENTED ) |
|
r_stats.c_client_ents++; |
|
|
|
// debug: mark all solid |
|
if( true ) // R_OpaqueEntity( clent )) |
|
{ |
|
// 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 ) |
|
{ |
|
AngleVectors( RI.viewangles, RI.vforward, RI.vright, RI.vup ); |
|
#if 0 |
|
ref_overview_t *ov = gEngfuncs.GetOverviewParms(); |
|
|
|
if( RP_NORMALPASS() && ( gEngfuncs.GetWaterLevel() >= 3 )) |
|
{ |
|
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 0 |
|
GLdouble xMin, xMax, yMin, yMax, zNear, zFar; |
|
|
|
if( RI.drawOrtho ) |
|
{ |
|
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 = max( 256.0f, RI.farClip ); |
|
|
|
yMax = zNear * tan( RI.fov_y * M_PI / 360.0 ); |
|
yMin = -yMax; |
|
|
|
xMax = zNear * tan( RI.fov_x * M_PI / 360.0 ); |
|
xMin = -xMax; |
|
|
|
Matrix4x4_CreateProjection( m, xMax, xMin, yMax, yMin, zNear, zFar ); |
|
#endif |
|
} |
|
|
|
/* |
|
============= |
|
R_SetupModelviewMatrix |
|
============= |
|
*/ |
|
static void R_SetupModelviewMatrix( matrix4x4 m ) |
|
{ |
|
#if 0 |
|
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* ), 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; |
|
} |
|
|
|
/* |
|
============= |
|
R_CheckFog |
|
|
|
check for underwater fog |
|
Using backward recursion to find waterline leaf |
|
from underwater leaf (idea: XaeroX) |
|
============= |
|
*/ |
|
static void R_CheckFog( void ) |
|
{ |
|
cl_entity_t *ent; |
|
image_t *tex; |
|
int i, cnt, count; |
|
|
|
// quake global fog |
|
if( ENGINE_GET_PARM( PARM_QUAKE_COMPATIBLE )) |
|
{ |
|
if( !MOVEVARS->fog_settings ) |
|
{ |
|
// if( pglIsEnabled( GL_FOG )) |
|
// pglDisable( GL_FOG ); |
|
RI.fogEnabled = false; |
|
return; |
|
} |
|
|
|
// quake-style global fog |
|
RI.fogColor[0] = ((MOVEVARS->fog_settings & 0xFF000000) >> 24) / 255.0f; |
|
RI.fogColor[1] = ((MOVEVARS->fog_settings & 0xFF0000) >> 16) / 255.0f; |
|
RI.fogColor[2] = ((MOVEVARS->fog_settings & 0xFF00) >> 8) / 255.0f; |
|
RI.fogDensity = ((MOVEVARS->fog_settings & 0xFF) / 255.0f) * 0.01f; |
|
RI.fogStart = RI.fogEnd = 0.0f; |
|
RI.fogColor[3] = 1.0f; |
|
RI.fogCustom = false; |
|
RI.fogEnabled = true; |
|
RI.fogSkybox = true; |
|
return; |
|
} |
|
|
|
RI.fogEnabled = false; |
|
|
|
if( RI.onlyClientDraw || ENGINE_GET_PARM( PARM_WATER_LEVEL ) < 3 || !RI.drawWorld || !RI.viewleaf ) |
|
{ |
|
if( RI.cached_waterlevel == 3 ) |
|
{ |
|
// in some cases waterlevel jumps from 3 to 1. Catch it |
|
RI.cached_waterlevel = ENGINE_GET_PARM( PARM_WATER_LEVEL ); |
|
RI.cached_contents = CONTENTS_EMPTY; |
|
//if( !RI.fogCustom ) pglDisable( GL_FOG ); |
|
} |
|
return; |
|
} |
|
|
|
ent = gEngfuncs.CL_GetWaterEntity( RI.vieworg ); |
|
if( ent && ent->model && ent->model->type == mod_brush && ent->curstate.skin < 0 ) |
|
cnt = ent->curstate.skin; |
|
else cnt = RI.viewleaf->contents; |
|
|
|
RI.cached_waterlevel = ENGINE_GET_PARM( PARM_WATER_LEVEL ); |
|
|
|
if( !IsLiquidContents( RI.cached_contents ) && IsLiquidContents( cnt )) |
|
{ |
|
tex = NULL; |
|
|
|
// check for water texture |
|
if( ent && ent->model && ent->model->type == mod_brush ) |
|
{ |
|
msurface_t *surf; |
|
|
|
count = ent->model->nummodelsurfaces; |
|
|
|
for( i = 0, surf = &ent->model->surfaces[ent->model->firstmodelsurface]; i < count; i++, surf++ ) |
|
{ |
|
if( surf->flags & SURF_DRAWTURB && surf->texinfo && surf->texinfo->texture ) |
|
{ |
|
tex = R_GetTexture( surf->texinfo->texture->gl_texturenum ); |
|
RI.cached_contents = ent->curstate.skin; |
|
break; |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
tex = R_RecursiveFindWaterTexture( RI.viewleaf->parent, NULL, false ); |
|
if( tex ) RI.cached_contents = RI.viewleaf->contents; |
|
} |
|
|
|
if( !tex ) return; // no valid fogs |
|
|
|
// copy fog params |
|
RI.fogColor[0] = tex->fogParams[0] / 255.0f; |
|
RI.fogColor[1] = tex->fogParams[1] / 255.0f; |
|
RI.fogColor[2] = tex->fogParams[2] / 255.0f; |
|
RI.fogDensity = tex->fogParams[3] * 0.000025f; |
|
RI.fogStart = RI.fogEnd = 0.0f; |
|
RI.fogColor[3] = 1.0f; |
|
RI.fogCustom = false; |
|
RI.fogEnabled = true; |
|
RI.fogSkybox = true; |
|
} |
|
else |
|
{ |
|
RI.fogCustom = false; |
|
RI.fogEnabled = true; |
|
RI.fogSkybox = true; |
|
} |
|
} |
|
|
|
/* |
|
============= |
|
R_CheckGLFog |
|
|
|
special condition for Spirit 1.9 |
|
that used direct calls of glFog-functions |
|
============= |
|
*/ |
|
static void R_CheckGLFog( void ) |
|
{ |
|
#ifdef HACKS_RELATED_HLMODS |
|
if(( !RI.fogEnabled && !RI.fogCustom ) && pglIsEnabled( GL_FOG ) && VectorIsNull( RI.fogColor )) |
|
{ |
|
// fill the fog color from GL-state machine |
|
pglGetFloatv( GL_FOG_COLOR, RI.fogColor ); |
|
RI.fogSkybox = true; |
|
} |
|
#endif |
|
} |
|
|
|
/* |
|
============= |
|
R_DrawFog |
|
|
|
============= |
|
*/ |
|
void R_DrawFog( void ) |
|
{ |
|
if( !RI.fogEnabled ) return; |
|
#if 0 |
|
pglEnable( GL_FOG ); |
|
if( gEngfuncs.Host_IsQuakeCompatible( )) |
|
pglFogi( GL_FOG_MODE, GL_EXP2 ); |
|
else pglFogi( GL_FOG_MODE, GL_EXP ); |
|
pglFogf( GL_FOG_DENSITY, RI.fogDensity ); |
|
pglFogfv( GL_FOG_COLOR, RI.fogColor ); |
|
pglHint( GL_FOG_HINT, GL_NICEST ); |
|
#endif |
|
} |
|
|
|
/* |
|
============= |
|
R_DrawEntitiesOnList |
|
============= |
|
*/ |
|
void R_DrawEntitiesOnList( void ) |
|
{ |
|
int i; |
|
extern int d_aflatcolor; |
|
d_aflatcolor = 0; |
|
tr.blend = 1.0f; |
|
// GL_CheckForErrors(); |
|
// HACK: setup world transform |
|
void R_AliasSetUpTransform (void); |
|
RI.currententity = gEngfuncs.GetEntityByIndex(0); |
|
R_AliasSetUpTransform(); |
|
extern void (*d_pdrawspans)(void *); |
|
extern void R_PolysetFillSpans8 ( void * ); |
|
d_pdrawspans = R_PolysetFillSpans8; |
|
// 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_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(); |
|
|
|
// 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(); |
|
|
|
// 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 = gEngfuncs.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_DrawStudioModel( RI.currententity ); |
|
break; |
|
case mod_sprite: |
|
// 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 |
|
|
|
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 = pfrustum_indexes[i]; |
|
|
|
rejectpt[0] = minmaxs[pindex[0]]; |
|
rejectpt[1] = minmaxs[pindex[1]]; |
|
rejectpt[2] = minmaxs[pindex[2]]; |
|
|
|
d = DotProduct (rejectpt, view_clipplanes[i].normal); |
|
d -= 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, view_clipplanes[i].normal); |
|
d -= 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 != r_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 (modelorg, oldorigin); |
|
insubmodel = true; |
|
//r_dlightframecount = r_framecount; |
|
|
|
for( i = 0; i < tr.draw_list->num_solid_entities && !RI.onlyClientDraw; i++ ) |
|
{ |
|
int k; |
|
RI.currententity = tr.draw_list->solid_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 (r_origin, r_entorigin, 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 ); |
|
|
|
if( l->die < gpGlobals->time || !l->radius ) |
|
continue; |
|
|
|
/*VectorCopy( l->origin, oldorigin ); // save lightorigin |
|
Matrix4x4_VectorITransform( RI.objectMatrix, l->origin, origin_l ); |
|
VectorCopy( origin_l, l->origin ); // move light in bmodel space |
|
R_MarkLights( l, 1<<k, clmodel->nodes + clmodel->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 (base_vpn, vpn); |
|
VectorCopy (base_vup, vup); |
|
VectorCopy (base_vright, vright); |
|
VectorCopy (oldorigin, modelorg); |
|
R_TransformFrustum (); |
|
} |
|
|
|
insubmodel = 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 *) |
|
(((long)&ledges[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); |
|
} |
|
|
|
if (r_surfsonstack) |
|
{ |
|
surfaces = (surf_t *) |
|
(((long)&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 (); |
|
|
|
// 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; |
|
|
|
r_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 == r_visframecount) |
|
break; |
|
node->visframe = r_visframecount; |
|
node = node->parent; |
|
} while (node); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
#endif |
|
|
|
/* |
|
================ |
|
R_RenderScene |
|
|
|
R_SetupRefParams must be called right before |
|
================ |
|
*/ |
|
void 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++; |
|
|
|
|
|
R_SetupFrustum(); |
|
R_SetupFrame(); |
|
|
|
R_PushDlights(); |
|
// R_SetupGL( true ); |
|
R_Clear( ~0 ); |
|
|
|
R_MarkLeaves(); |
|
R_DrawFog (); |
|
// R_PushDlights (r_worldmodel); ?? |
|
R_CheckGLFog(); |
|
//R_DrawWorld(); |
|
R_EdgeDrawing (); |
|
R_CheckFog(); |
|
|
|
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 R_BeginFrame( qboolean clearScene ) |
|
{ |
|
|
|
if( R_DoResetGamma( )) |
|
{ |
|
gEngfuncs.BuildGammaTable( 1.8f, 0.0f ); |
|
// glConfig.softwareGammaUpdate = true; |
|
// GL_RebuildLightmaps(); |
|
// glConfig.softwareGammaUpdate = false; |
|
|
|
// next frame will be restored gamma |
|
// SetBits( vid_brightness->flags, FCVAR_CHANGED ); |
|
// SetBits( vid_gamma->flags, FCVAR_CHANGED ); |
|
} |
|
else if( FBitSet( vid_gamma->flags, FCVAR_CHANGED ) || FBitSet( vid_brightness->flags, FCVAR_CHANGED )) |
|
{ |
|
// gEngfuncs.BuildGammaTable( vid_gamma->value, vid_brightness->value ); |
|
//glConfig.softwareGammaUpdate = true; |
|
// GL_RebuildLightmaps(); |
|
//glConfig.softwareGammaUpdate = false; |
|
} |
|
|
|
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 ); |
|
RI.farClip = 0; |
|
|
|
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 |
|
=============== |
|
*/ |
|
int R_RenderFrame( const ref_viewpass_t *rvp ) |
|
{ |
|
if( r_norefresh->value ) |
|
return 1; |
|
|
|
// prevent cache overrun |
|
if( gpGlobals->height > vid.height || gpGlobals->width > vid.width ) |
|
return 1; |
|
|
|
// 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 1; |
|
} |
|
} |
|
|
|
tr.fCustomRendering = false; |
|
if( !RI.onlyClientDraw ) |
|
R_RunViewmodelEvents(); |
|
|
|
tr.realframecount++; // right called after viewmodel events |
|
R_RenderScene(); |
|
|
|
return 1; |
|
} |
|
|
|
/* |
|
=============== |
|
R_EndFrame |
|
=============== |
|
*/ |
|
void R_EndFrame( void ) |
|
{ |
|
// flush any remaining 2D bits |
|
R_Set2DMode( false ); |
|
|
|
// blit pixels with GL until engine supports REF_SOFT context |
|
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 R_NewMap (void) |
|
{ |
|
r_viewcluster = -1; |
|
|
|
D_FlushCaches( true ); |
|
|
|
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_maxedgesseen = 0; |
|
r_maxsurfsseen = 0; |
|
|
|
r_numallocatededges = sw_maxedges->value; |
|
|
|
if (r_numallocatededges < MINEDGES) |
|
r_numallocatededges = MINEDGES; |
|
|
|
if (r_numallocatededges <= NUMSTACKEDGES) |
|
{ |
|
auxedges = NULL; |
|
} |
|
else |
|
{ |
|
auxedges = malloc (r_numallocatededges * sizeof(edge_t)); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
qboolean R_Init() |
|
{ |
|
gl_emboss_scale = gEngfuncs.Cvar_Get( "gl_emboss_scale", "0", FCVAR_ARCHIVE|FCVAR_LATCH, "fake bumpmapping scale" ); |
|
vid_gamma = gEngfuncs.pfnGetCvarPointer( "gamma", 0 ); |
|
r_norefresh = gEngfuncs.Cvar_Get( "r_norefresh", "0", 0, "disable 3D rendering (use with caution)" ); |
|
r_drawentities = gEngfuncs.Cvar_Get( "r_drawentities", "1", FCVAR_CHEAT, "render entities" ); |
|
vid_brightness = gEngfuncs.pfnGetCvarPointer( "brightness", 0 ); |
|
r_fullbright = gEngfuncs.Cvar_Get( "r_fullbright", "0", FCVAR_CHEAT, "disable lightmaps, get fullbright for entities" ); |
|
sw_mipcap = gEngfuncs.Cvar_Get( "r_fullbright", "0", FCVAR_CHEAT, "disable lightmaps, get fullbright for entities" ); |
|
|
|
r_dynamic = gEngfuncs.Cvar_Get( "r_dynamic", "1", FCVAR_ARCHIVE, "allow dynamic lighting (dlights, lightstyles)" ); |
|
r_lightmap = gEngfuncs.Cvar_Get( "r_lightmap", "0", FCVAR_CHEAT, "lightmap debugging tool" ); |
|
|
|
// sw_aliasstats = ri.Cvar_Get ("sw_polymodelstats", "0", 0); |
|
// sw_allow_modex = ri.Cvar_Get( "sw_allow_modex", "1", CVAR_ARCHIVE ); |
|
sw_clearcolor = gEngfuncs.Cvar_Get ("sw_clearcolor", "2", 0, "screen clear color"); |
|
sw_drawflat = gEngfuncs.Cvar_Get ("sw_drawflat", "0", 0, ""); |
|
sw_draworder = gEngfuncs.Cvar_Get ("sw_draworder", "0", 0, ""); |
|
sw_maxedges = gEngfuncs.Cvar_Get ("sw_maxedges", "32", 0, ""); |
|
sw_maxsurfs = gEngfuncs.Cvar_Get ("sw_maxsurfs", "0", 0, ""); |
|
sw_mipscale = gEngfuncs.Cvar_Get ("sw_mipscale", "1", 0, ""); |
|
sw_reportedgeout = gEngfuncs.Cvar_Get ("sw_reportedgeout", "0", 0, ""); |
|
sw_reportsurfout = gEngfuncs.Cvar_Get ("sw_reportsurfout", "0", 0, ""); |
|
sw_stipplealpha = gEngfuncs.Cvar_Get( "sw_stipplealpha", "0", FCVAR_ARCHIVE, "" ); |
|
sw_surfcacheoverride = gEngfuncs.Cvar_Get ("sw_surfcacheoverride", "0", 0, ""); |
|
sw_waterwarp = gEngfuncs.Cvar_Get ("sw_waterwarp", "1", 0, ""); |
|
sw_mode = gEngfuncs.Cvar_Get( "sw_mode", "0", FCVAR_ARCHIVE, ""); |
|
sw_texfilt = gEngfuncs.Cvar_Get ("sw_texfilt", "0", 0, "texture dither"); |
|
//r_lefthand = ri.Cvar_Get( "hand", "0", FCVAR_USERINFO | FCVAR_ARCHIVE ); |
|
// r_speeds = ri.Cvar_Get ("r_speeds", "0", 0); |
|
|
|
//r_drawworld = ri.Cvar_Get ("r_drawworld", "1", 0); |
|
//r_dspeeds = ri.Cvar_Get ("r_dspeeds", "0", 0); |
|
// r_lightlevel = ri.Cvar_Get ("r_lightlevel", "0", 0); |
|
//r_lerpmodels = ri.Cvar_Get( "r_lerpmodels", "1", 0 ); |
|
r_novis = gEngfuncs.Cvar_Get( "r_novis", "0", 0, "" ); |
|
|
|
// create the window and set up the context |
|
r_temppool = Mem_AllocPool( "ref_sw zone" ); |
|
|
|
if( !gEngfuncs.R_Init_Video( REF_SOFTWARE )) // request GL context |
|
{ |
|
gEngfuncs.R_Free_Video(); |
|
|
|
gEngfuncs.Host_Error( "Can't initialize video subsystem\nProbably driver was not installed" ); |
|
return false; |
|
} |
|
|
|
R_InitBlit(); |
|
|
|
R_InitImages(); |
|
// init draw stack |
|
tr.draw_list = &tr.draw_stack[0]; |
|
tr.draw_stack_pos = 0; |
|
RI.yOrigin = YCENTERING; |
|
RI.xOrigin = XCENTERING; |
|
view_clipplanes[0].leftedge = true; |
|
view_clipplanes[1].rightedge = true; |
|
view_clipplanes[1].leftedge = view_clipplanes[2].leftedge =view_clipplanes[3].leftedge = false; |
|
view_clipplanes[0].rightedge = view_clipplanes[2].rightedge = view_clipplanes[3].rightedge = false; |
|
R_StudioInit(); |
|
|
|
return true; |
|
} |
|
|
|
void R_Shutdown() |
|
{ |
|
R_ShutdownImages(); |
|
gEngfuncs.R_Free_Video(); |
|
} |
|
|
|
|