xash3d-fwgs/r_main.c

1697 lines
38 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 "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 *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_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;
/*
================
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];
}
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;
}
/*
================
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;
}
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++;
if( 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, 1920*1080*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 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( gEngfuncs.Host_IsQuakeCompatible( ))
{
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 || gEngfuncs.GetWaterLevel() < 3 || !RI.drawWorld || !RI.viewleaf )
{
if( RI.cached_waterlevel == 3 )
{
// in some cases waterlevel jumps from 3 to 1. Catch it
RI.cached_waterlevel = gEngfuncs.GetWaterLevel();
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 = gEngfuncs.GetWaterLevel();
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;
tr.blend = 1.0f;
// GL_CheckForErrors();
// 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;
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;
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 0
/*
=============
R_DrawBEntitiesOnList
=============
*/
void R_DrawBEntitiesOnList (void)
{
int i, clipflags;
vec3_t oldorigin;
vec3_t mins, maxs;
float minmaxs[6];
mnode_t *topnode;
if (!r_drawentities->value)
return;
VectorCopy (modelorg, oldorigin);
insubmodel = true;
r_dlightframecount = r_framecount;
for (i=0 ; i<r_newrefdef.num_entities ; i++)
{
currententity = &r_newrefdef.entities[i];
currentmodel = currententity->model;
if (!currentmodel)
continue;
if (currentmodel->nummodelsurfaces == 0)
continue; // clip brush only
if ( currententity->flags & RF_BEAM )
continue;
if (currentmodel->type != mod_brush)
continue;
// see if the bounding box lets us trivially reject, also sets
// trivial accept status
RotatedBBox (currentmodel->mins, currentmodel->maxs,
currententity->angles, mins, maxs);
VectorAdd (mins, currententity->origin, minmaxs);
VectorAdd (maxs, currententity->origin, (minmaxs+3));
clipflags = R_BmodelCheckBBox (minmaxs);
if (clipflags == BMODEL_FULLY_CLIPPED)
continue; // off the edge of the screen
topnode = R_FindTopnode (minmaxs, minmaxs+3);
if (!topnode)
continue; // no part in a visible leaf
VectorCopy (currententity->origin, r_entorigin);
VectorSubtract (r_origin, r_entorigin, modelorg);
r_pcurrentvertbase = currentmodel->vertexes;
// FIXME: stop transforming twice
R_RotateBmodel ();
// calculate dynamic lighting for bmodel
R_PushDlights (currentmodel);
if (topnode->contents == CONTENTS_NODE)
{
// not a leaf; has to be clipped to the world BSP
r_clipflags = clipflags;
R_DrawSolidClippedSubmodelPolygons (currentmodel, topnode);
}
else
{
// falls entirely in one leaf, so we just put all the
// edges in the edge list and let 1/z sorting handle
// drawing order
R_DrawSubmodelPolygons (currentmodel, clipflags, topnode);
}
// 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
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_PushDlights();
R_SetupFrustum();
R_SetupFrame();
// 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;
// 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;
r_cnumsurfs = sw_maxsurfs->value;
if (r_cnumsurfs <= MINSURFACES)
r_cnumsurfs = MINSURFACES;
if (r_cnumsurfs > NUMSTACKSURFACES)
{
surfaces = malloc (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" );
// 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, "");
//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" );
vid.width = 1920;
vid.height = 1080;
vid.rowbytes = 1920; // rowpixels
vid.buffer = Mem_Malloc( r_temppool, 1920*1080*sizeof( pixel_t ) );
if( !gEngfuncs.R_Init_Video( REF_GL )) // request GL context
{
gEngfuncs.R_Free_Video();
gEngfuncs.Host_Error( "Can't initialize video subsystem\nProbably driver was not installed" );
return false;
}
R_InitImages();
// init draw stack
tr.draw_list = &tr.draw_stack[0];
tr.draw_stack_pos = 0;
return true;
}
void R_Shutdown()
{
R_ShutdownImages();
gEngfuncs.R_Free_Video();
}