This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/vid_gl/r_main.c

2751 lines
67 KiB
C

/*
Copyright (C) 1997-2001 Id Software, Inc.
Copyright (C) 2002-2007 Victor Luchits
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 2
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// r_main.c
#include <stdio.h> // sscanf
#include "r_local.h"
#include "clgame_api.h"
#include "tmpent_def.h"
#include "effects_api.h"
#include "mathlib.h"
#include "matrix_lib.h"
render_imp_t ri;
stdlib_api_t com;
ref_model_t *r_worldmodel;
mbrushmodel_t *r_worldbrushmodel;
float gldepthmin, gldepthmax;
mapconfig_t mapConfig;
refinst_t RI, prevRI;
ref_params_t r_lastRefdef;
static int r_numnullentities;
static ref_entity_t *r_nullentities[MAX_ENTITIES];
ref_model_t *cl_models[MAX_MODELS]; // client replacement modeltable
static int r_numbmodelentities;
static ref_entity_t *r_bmodelentities[MAX_MODELS];
static byte r_entVisBits[MAX_EDICTS/8];
int r_pvsframecount; // bumped when going to a new PVS
int r_framecount; // used for dlight push checking
int r_framecount2; // used bonestransform checking
int c_brush_polys, c_world_leafs;
double r_mark_leaves, r_world_node;
double r_add_polys, r_add_entities;
double r_sort_meshes, r_draw_meshes;
msurface_t *r_debug_surface;
char r_speeds_msg[MAX_RSPEEDSMSGSIZE];
uint r_numEntities;
ref_entity_t r_entities[MAX_ENTITIES];
ref_entity_t *r_worldent = &r_entities[0];
uint r_numDlights;
dlight_t r_dlights[MAX_DLIGHTS];
uint r_numPolys;
poly_t r_polys[MAX_POLYS];
lightstyle_t r_lightStyles[MAX_LIGHTSTYLES];
mleaf_t *r_viewleaf, *r_oldviewleaf;
mleaf_t *r_viewleaf2, *r_oldviewleaf2;
float r_farclip_min, r_farclip_bias = 64.0f;
/*
=================
GL_Cull
=================
*/
void GL_Cull( int cull )
{
if( glState.faceCull == cull )
return;
if( !cull )
{
pglDisable( GL_CULL_FACE );
glState.faceCull = 0;
return;
}
if( !glState.faceCull )
pglEnable( GL_CULL_FACE );
pglCullFace( cull );
glState.faceCull = cull;
}
/*
=================
GL_SetState
=================
*/
void GL_SetState( int state )
{
int diff;
if( glState.in2DMode )
state |= GLSTATE_NO_DEPTH_TEST;
if( state & GLSTATE_NO_DEPTH_TEST )
state &= ~( GLSTATE_DEPTHWRITE|GLSTATE_DEPTHFUNC_EQ );
diff = glState.flags ^ state;
if( !diff )
return;
if( diff & ( GLSTATE_BLEND_MTEX|GLSTATE_SRCBLEND_MASK|GLSTATE_DSTBLEND_MASK ) )
{
if( state & ( GLSTATE_SRCBLEND_MASK|GLSTATE_DSTBLEND_MASK ) )
{
int blendsrc, blenddst;
switch( state & GLSTATE_SRCBLEND_MASK )
{
case GLSTATE_SRCBLEND_ZERO:
blendsrc = GL_ZERO;
break;
case GLSTATE_SRCBLEND_DST_COLOR:
blendsrc = GL_DST_COLOR;
break;
case GLSTATE_SRCBLEND_ONE_MINUS_DST_COLOR:
blendsrc = GL_ONE_MINUS_DST_COLOR;
break;
case GLSTATE_SRCBLEND_SRC_ALPHA:
blendsrc = GL_SRC_ALPHA;
break;
case GLSTATE_SRCBLEND_ONE_MINUS_SRC_ALPHA:
blendsrc = GL_ONE_MINUS_SRC_ALPHA;
break;
case GLSTATE_SRCBLEND_DST_ALPHA:
blendsrc = GL_DST_ALPHA;
break;
case GLSTATE_SRCBLEND_ONE_MINUS_DST_ALPHA:
blendsrc = GL_ONE_MINUS_DST_ALPHA;
break;
default:
case GLSTATE_SRCBLEND_ONE:
blendsrc = GL_ONE;
break;
}
switch( state & GLSTATE_DSTBLEND_MASK )
{
case GLSTATE_DSTBLEND_ONE:
blenddst = GL_ONE;
break;
case GLSTATE_DSTBLEND_SRC_COLOR:
blenddst = GL_SRC_COLOR;
break;
case GLSTATE_DSTBLEND_ONE_MINUS_SRC_COLOR:
blenddst = GL_ONE_MINUS_SRC_COLOR;
break;
case GLSTATE_DSTBLEND_SRC_ALPHA:
blenddst = GL_SRC_ALPHA;
break;
case GLSTATE_DSTBLEND_ONE_MINUS_SRC_ALPHA:
blenddst = GL_ONE_MINUS_SRC_ALPHA;
break;
case GLSTATE_DSTBLEND_DST_ALPHA:
blenddst = GL_DST_ALPHA;
break;
case GLSTATE_DSTBLEND_ONE_MINUS_DST_ALPHA:
blenddst = GL_ONE_MINUS_DST_ALPHA;
break;
default:
case GLSTATE_DSTBLEND_ZERO:
blenddst = GL_ZERO;
break;
}
if( state & GLSTATE_BLEND_MTEX )
{
if( glState.currentEnvModes[glState.activeTMU] != GL_REPLACE )
pglEnable( GL_BLEND );
else
pglDisable( GL_BLEND );
}
else
{
pglEnable( GL_BLEND );
}
pglBlendFunc( blendsrc, blenddst );
}
else
{
pglDisable( GL_BLEND );
}
}
if( diff & GLSTATE_ALPHAFUNC )
{
if( state & GLSTATE_ALPHAFUNC )
{
if( !( glState.flags & GLSTATE_ALPHAFUNC ) )
pglEnable( GL_ALPHA_TEST );
if( state & GLSTATE_AFUNC_GT0 )
pglAlphaFunc( GL_GREATER, 0 );
else if( state & GLSTATE_AFUNC_LT128 )
pglAlphaFunc( GL_LESS, 0.5f );
else
pglAlphaFunc( GL_GEQUAL, 0.5f );
}
else
{
pglDisable( GL_ALPHA_TEST );
}
}
if( diff & GLSTATE_DEPTHFUNC_EQ )
{
if( state & GLSTATE_DEPTHFUNC_EQ )
pglDepthFunc( GL_EQUAL );
else
pglDepthFunc( GL_LEQUAL );
}
if( diff & GLSTATE_DEPTHWRITE )
{
if( state & GLSTATE_DEPTHWRITE )
pglDepthMask( GL_TRUE );
else
pglDepthMask( GL_FALSE );
}
if( diff & GLSTATE_NO_DEPTH_TEST )
{
if( state & GLSTATE_NO_DEPTH_TEST )
pglDisable( GL_DEPTH_TEST );
else
pglEnable( GL_DEPTH_TEST );
}
if( diff & GLSTATE_OFFSET_FILL )
{
if( state & GLSTATE_OFFSET_FILL )
pglEnable( GL_POLYGON_OFFSET_FILL );
else
pglDisable( GL_POLYGON_OFFSET_FILL );
}
glState.flags = state;
}
/*
=================
GL_FrontFace
=================
*/
void GL_FrontFace( int front )
{
pglFrontFace( front ? GL_CW : GL_CCW );
glState.frontFace = front;
}
/*
=============
R_TransformEntityBBox
=============
*/
void R_TransformEntityBBox( ref_entity_t *e, vec3_t mins, vec3_t maxs, vec3_t bbox[8], bool local )
{
int i;
vec3_t axis[3], tmp;
if( e == r_worldent ) local = false;
if( local ) Matrix3x3_Transpose( axis, e->axis ); // switch row-column order
// rotate local bounding box and compute the full bounding box
for( i = 0; i < 8; i++ )
{
vec_t *corner = bbox[i];
corner[0] = (( i & 1 ) ? mins[0] : maxs[0] );
corner[1] = (( i & 2 ) ? mins[1] : maxs[1] );
corner[2] = (( i & 4 ) ? mins[2] : maxs[2] );
if( local )
{
Matrix3x3_Transform( axis, corner, tmp );
VectorAdd( tmp, e->origin, corner );
}
}
}
/*
=============
R_LoadIdentity
=============
*/
void R_LoadIdentity( void )
{
if( tr.modelviewIdentity ) return;
Matrix4x4_LoadIdentity( RI.objectMatrix );
Matrix4x4_Copy( RI.modelviewMatrix, RI.worldviewMatrix );
GL_LoadMatrix( RI.modelviewMatrix );
tr.modelviewIdentity = true;
}
/*
=============
R_RotateForEntity
=============
*/
void R_RotateForEntity( ref_entity_t *e )
{
if( e == r_worldent )
{
R_LoadIdentity();
return;
}
Matrix4x4_FromMatrix3x3( RI.objectMatrix, e->axis, e->scale );
if( e->movetype == MOVETYPE_FOLLOW && e->parent && !VectorIsNull( e->origin2 ))
Matrix4x4_SetOrigin( RI.objectMatrix, e->origin2[0], e->origin2[1], e->origin2[2] );
else Matrix4x4_SetOrigin( RI.objectMatrix, e->origin[0], e->origin[1], e->origin[2] );
Matrix4x4_ConcatTransforms( RI.modelviewMatrix, RI.worldviewMatrix, RI.objectMatrix );
GL_LoadMatrix( RI.modelviewMatrix );
tr.modelviewIdentity = false;
}
/*
=============
R_TranslateForEntity
=============
*/
void R_TranslateForEntity( ref_entity_t *e )
{
if( e == r_worldent )
{
R_LoadIdentity();
return;
}
Matrix4x4_LoadIdentity( RI.objectMatrix );
if( e->movetype == MOVETYPE_FOLLOW && e->parent && !VectorIsNull( e->origin2 ))
Matrix4x4_SetOrigin( RI.objectMatrix, e->origin2[0], e->origin2[1], e->origin2[2] );
else Matrix4x4_SetOrigin( RI.objectMatrix, e->origin[0], e->origin[1], e->origin[2] );
Matrix4x4_ConcatScale( RI.objectMatrix, e->scale );
Matrix4x4_ConcatTransforms( RI.modelviewMatrix, RI.worldviewMatrix, RI.objectMatrix );
GL_LoadMatrix( RI.modelviewMatrix );
tr.modelviewIdentity = false;
}
/*
=============
R_FogForSphere
=============
*/
mfog_t *R_FogForSphere( const vec3_t centre, const float radius )
{
int i, j;
mfog_t *fog;
cplane_t *plane;
if( !r_worldmodel || ( RI.refdef.flags & RDF_NOWORLDMODEL ) || !r_worldbrushmodel->numfogs )
return NULL;
if( RI.params & RP_SHADOWMAPVIEW )
return NULL;
if( r_worldbrushmodel->globalfog )
return r_worldbrushmodel->globalfog;
fog = r_worldbrushmodel->fogs;
for( i = 0; i < r_worldbrushmodel->numfogs; i++, fog++ )
{
if( !fog->shader )
continue;
plane = fog->planes;
for( j = 0; j < fog->numplanes; j++, plane++ )
{
// if completely in front of face, no intersection
if( PlaneDiff( centre, plane ) > radius )
break;
}
if( j == fog->numplanes )
return fog;
}
return NULL;
}
/*
=============
R_CompletelyFogged
=============
*/
bool R_CompletelyFogged( mfog_t *fog, vec3_t origin, float radius )
{
// note that fog->distanceToEye < 0 is always true if
// globalfog is not NULL and we're inside the world boundaries
if( fog && fog->shader && RI.fog_dist_to_eye[fog-r_worldbrushmodel->fogs] < 0 )
{
float vpnDist = (( RI.viewOrigin[0] - origin[0] ) * RI.vpn[0] + ( RI.viewOrigin[1] - origin[1] ) * RI.vpn[1] + ( RI.viewOrigin[2] - origin[2] ) * RI.vpn[2] );
return (( vpnDist + radius ) / fog->shader->fog_dist ) < -1;
}
return false;
}
/*
=============================================================
CUSTOM COLORS
=============================================================
*/
static rgba_t r_customColors[NUM_CUSTOMCOLORS];
/*
=================
R_InitCustomColors
=================
*/
void R_InitCustomColors( void )
{
Mem_Set( r_customColors, 255, sizeof( r_customColors ));
}
/*
=================
R_SetCustomColor
=================
*/
void R_SetCustomColor( int num, int r, int g, int b )
{
if( num < 0 || num >= NUM_CUSTOMCOLORS )
return;
Vector4Set( r_customColors[num], (byte)r, (byte)g, (byte)b, 255 );
}
/*
=================
R_GetCustomColor
=================
*/
int R_GetCustomColor( int num )
{
if( num < 0 || num >= NUM_CUSTOMCOLORS )
return 0xFFFFFFFF; // white color
return *(int *)r_customColors[num];
}
/*
=============================================================
SPRITE MODELS AND FLARES
=============================================================
*/
static vec4_t spr_xyz[4] = { {0,0,0,1}, {0,0,0,1}, {0,0,0,1}, {0,0,0,1} };
static vec2_t spr_st[4] = { {1, 0}, {1, 1}, {0,1}, {0, 0} };
static rgba_t spr_color[4];
static mesh_t spr_mesh = { 4, 6, spr_xyz, spr_xyz, NULL, NULL, spr_st, NULL, spr_color, NULL, NULL };
/*
=================
R_PushSprite
=================
*/
bool R_PushSprite( const meshbuffer_t *mb, int type, float right, float left, float up, float down )
{
int i, features;
vec3_t v_forward, v_right, v_up;
ref_entity_t *e = RI.currententity;
float angle, sr, cr;
ref_shader_t *shader;
vec3_t point, origin;
// don't touch entity origin in case we doesn't have updates
VectorCopy( e->origin, origin );
switch( type )
{
case SPR_ORIENTED:
VectorCopy( e->axis[0], v_forward );
VectorCopy( e->axis[1], v_right );
VectorCopy( e->axis[2], v_up );
VectorScale( v_forward, 0.01f, v_forward ); // to avoid z-fighting
VectorSubtract( origin, v_forward, origin );
break;
case SPR_FACING_UPRIGHT:
VectorSet( v_right, origin[1] - RI.viewOrigin[1], -(origin[0] - RI.viewOrigin[0]), 0 );
VectorSet( v_up, 0, 0, 1 );
VectorNormalize( v_right );
break;
case SPR_FWD_PARALLEL_UPRIGHT:
VectorSet( v_right, RI.vpn[1], -RI.vpn[0], 0 );
VectorSet( v_up, 0, 0, 1 );
break;
case SPR_FWD_PARALLEL_ORIENTED:
angle = e->angles[ROLL] * (M_PI * 2.0f/360.0f);
sr = com.sin( angle );
cr = com.cos( angle );
for( i = 0; i < 3; i++ )
{
v_right[i] = (RI.vright[i] * cr + RI.vup[i] * sr);
v_up[i] = RI.vright[i] * -sr + RI.vup[i] * cr;
}
break;
case SPR_FWD_PARALLEL: // normal sprite
default:
if( e->customShader )
angle = e->angles[PITCH];
else angle = e->angles[ROLL]; // for support rotating muzzleflashes
if( angle != 0.0f )
{
RotatePointAroundVector( v_right, RI.vpn, RI.vright, angle );
CrossProduct( RI.vpn, v_right, v_up );
}
else
{
VectorCopy( RI.vright, v_right );
VectorCopy( RI.vup, v_up );
}
break;
}
VectorScale( v_up, down, point );
VectorMA( point, -left, v_right, spr_xyz[0] );
VectorMA( point, -right, v_right, spr_xyz[3] );
VectorScale( v_up, up, point );
VectorMA( point, -left, v_right, spr_xyz[1] );
VectorMA( point, -right, v_right, spr_xyz[2] );
MB_NUM2SHADER( mb->shaderkey, shader );
// the code below is disgusting, but some q3a shaders use 'rgbgen vertex'
// and 'alphagen vertex' for effects instead of 'rgbgen entity' and 'alphagen entity'
if( shader->features & MF_COLORS )
{
for( i = 0; i < 4; i++ )
Vector4Set( spr_color[i], e->rendercolor[0], e->rendercolor[1], e->rendercolor[2], e->renderamt );
}
features = MF_NOCULL | MF_TRIFAN | shader->features;
if( r_shownormals->integer )
features |= MF_NORMALS;
if( shader->flags & SHADER_ENTITY_MERGABLE )
{
for( i = 0; i < 4; i++ )
VectorAdd( spr_xyz[i], origin, spr_xyz[i] );
R_PushMesh( &spr_mesh, features );
return false;
}
R_PushMesh( &spr_mesh, MF_NONBATCHED | features );
return true;
}
/*
=================
R_PushFlareSurf
=================
*/
static void R_PushFlareSurf( const meshbuffer_t *mb )
{
int i;
vec4_t color;
vec3_t origin, point, v;
float radius = r_flaresize->value, colorscale, depth;
float up = radius, down = -radius, left = -radius, right = radius;
mbrushmodel_t *bmodel = ( mbrushmodel_t * )RI.currentmodel->extradata;
msurface_t *surf = &bmodel->surfaces[mb->infokey - 1];
ref_shader_t *shader;
if( !r_flares->integer ) return;
if( RI.currentmodel != r_worldmodel )
{
Matrix3x3_Transform( RI.currententity->axis, surf->origin, origin );
VectorAdd( origin, RI.currententity->origin, origin );
}
else VectorCopy( surf->origin, origin );
R_TransformToScreen_Vec3( origin, v );
if( v[0] < RI.refdef.viewport[0] || v[0] > RI.refdef.viewport[0] + RI.refdef.viewport[2] )
return;
if( v[1] < RI.refdef.viewport[1] || v[1] > RI.refdef.viewport[1] + RI.refdef.viewport[3] )
return;
pglReadPixels((int)( v[0] ), (int)( v[1] ), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth );
if( depth + 1e-4 < v[2] ) return; // occluded
VectorCopy( origin, origin );
VectorMA( origin, down, RI.vup, point );
VectorMA( point, -left, RI.vright, spr_xyz[0] );
VectorMA( point, -right, RI.vright, spr_xyz[3] );
VectorMA( origin, up, RI.vup, point );
VectorMA( point, -left, RI.vright, spr_xyz[1] );
VectorMA( point, -right, RI.vright, spr_xyz[2] );
colorscale = 255.0 / r_flarefade->value;
Vector4Set( color, surf->color[0] * colorscale, surf->color[1] * colorscale, surf->color[2] * colorscale, 255 );
for( i = 0; i < 4; i++ )
color[i] = bound( 0, color[i], 255 );
for( i = 0; i < 4; i++ )
Vector4Copy( color, spr_color[i] );
MB_NUM2SHADER( mb->shaderkey, shader );
R_PushMesh( &spr_mesh, MF_NOCULL | MF_TRIFAN | shader->features );
}
/*
=================
R_PushCorona
=================
*/
static void R_PushCorona( const meshbuffer_t *mb )
{
int i;
vec4_t color;
vec3_t origin, point;
dlight_t *light = r_dlights + ( -mb->infokey - 1 );
float radius = light->intensity, colorscale;
float up = radius, down = -radius, left = -radius, right = radius;
ref_shader_t *shader;
VectorCopy( light->origin, origin );
VectorMA( origin, down, RI.vup, point );
VectorMA( point, -left, RI.vright, spr_xyz[0] );
VectorMA( point, -right, RI.vright, spr_xyz[3] );
VectorMA( origin, up, RI.vup, point );
VectorMA( point, -left, RI.vright, spr_xyz[1] );
VectorMA( point, -right, RI.vright, spr_xyz[2] );
colorscale = 255.0 * bound( 0, r_coronascale->value, 1.0 );
Vector4Set( color, light->color[0] * colorscale, light->color[1] * colorscale, light->color[2] * colorscale, 255 );
for( i = 0; i < 4; i++ )
color[i] = bound( 0, color[i], 255 );
for( i = 0; i < 4; i++ )
Vector4Copy( color, spr_color[i] );
MB_NUM2SHADER( mb->shaderkey, shader );
R_PushMesh( &spr_mesh, MF_NOCULL | MF_TRIFAN | shader->features );
}
/*
=================
R_PushSpritePoly
=================
*/
bool R_PushSpritePoly( const meshbuffer_t *mb )
{
ref_entity_t *e = RI.currententity;
if( ( mb->sortkey & 3 ) == MB_CORONA )
{
R_PushCorona( mb );
return false;
}
if( mb->infokey > 0 )
{
R_PushFlareSurf( mb );
return false;
}
return R_PushSprite( mb, -1, -e->radius, e->radius, e->radius, -e->radius );
}
/*
=================
R_AddSpriteModelToList
=================
*/
static void R_AddSpriteModelToList( ref_entity_t *e )
{
mspriteframe_t *frame;
msprite_t *psprite;
ref_model_t *model = e->model;
ref_shader_t *shader;
float dist;
meshbuffer_t *mb;
if(!( psprite = (( msprite_t* )model->extradata )))
return;
dist = (e->origin[0] - RI.viewOrigin[0]) * RI.vpn[0] + (e->origin[1] - RI.viewOrigin[1]) * RI.vpn[1] + (e->origin[2] - RI.viewOrigin[2]) * RI.vpn[2];
if( dist < 0 ) return; // cull it because we don't want to sort unneeded things
if( R_SpriteOccluded( e )) return;
frame = R_GetSpriteFrame( e->model, e->lerp->curstate.frame, e->angles[YAW] );
if( e->customShader ) shader = e->customShader;
else shader = &r_shaders[frame->shader];
if( RI.refdef.flags & (RDF_PORTALINVIEW|RDF_SKYPORTALINVIEW) || ( RI.params & RP_SKYPORTALVIEW ))
{
if( R_VisCullSphere( e->origin, frame->radius ))
return;
}
mb = R_AddMeshToList( MB_MODEL, R_FogForSphere( e->origin, frame->radius ), shader, -1 );
if( mb ) mb->shaderkey |= ( bound( 1, 0x4000 - (uint)dist, 0x4000 - 1 )<<12 );
}
/*
=================
R_AddSpritePolyToList
=================
*/
static void R_AddSpritePolyToList( ref_entity_t *e )
{
float dist;
meshbuffer_t *mb;
dist = (e->origin[0] - RI.refdef.vieworg[0]) * RI.vpn[0] + (e->origin[1] - RI.refdef.vieworg[1]) * RI.vpn[1] + (e->origin[2] - RI.refdef.vieworg[2]) * RI.vpn[2];
if( dist < 0 ) return; // cull it because we don't want to sort unneeded things
if( RI.refdef.flags & ( RDF_PORTALINVIEW|RDF_SKYPORTALINVIEW ) || ( RI.params & RP_SKYPORTALVIEW ) )
{
if( R_VisCullSphere( e->origin, e->radius ) )
return;
}
mb = R_AddMeshToList( MB_SPRITE, R_FogForSphere( e->origin, e->radius ), e->customShader, -1 );
if( mb ) mb->shaderkey |= ( bound( 1, 0x4000 - (unsigned int)dist, 0x4000 - 1 ) << 12 );
}
/*
=================
R_SpriteOverflow
=================
*/
bool R_SpriteOverflow( void )
{
return R_MeshOverflow( &spr_mesh );
}
//==================================================================================
/*
============
R_PolyBlend
============
*/
static void R_PolyBlend( void )
{
if( !r_polyblend->integer )
return;
if( RI.refdef.blend[3] < 0.01f )
return;
pglMatrixMode( GL_PROJECTION );
pglLoadIdentity();
pglOrtho( 0, 1, 1, 0, -99999, 99999 );
pglMatrixMode( GL_MODELVIEW );
pglLoadIdentity();
GL_Cull( 0 );
GL_SetState( GLSTATE_NO_DEPTH_TEST|GLSTATE_SRCBLEND_SRC_ALPHA|GLSTATE_DSTBLEND_ONE_MINUS_SRC_ALPHA );
pglDisable( GL_TEXTURE_2D );
pglColor4fv( RI.refdef.blend );
pglBegin( GL_TRIANGLES );
pglVertex2f( -5, -5 );
pglVertex2f( 10, -5 );
pglVertex2f( -5, 10 );
pglEnd();
pglEnable( GL_TEXTURE_2D );
pglColor4f( 1, 1, 1, 1 );
}
/*
===============
R_ApplySoftwareGamma
===============
*/
static void R_ApplySoftwareGamma( void )
{
double f, div;
// apply software gamma
if( !r_ignorehwgamma->integer )
return;
pglMatrixMode( GL_PROJECTION );
pglLoadIdentity();
pglOrtho( 0, 1, 1, 0, -99999, 99999 );
pglMatrixMode( GL_MODELVIEW );
pglLoadIdentity();
GL_Cull( 0 );
GL_SetState( GLSTATE_NO_DEPTH_TEST | GLSTATE_SRCBLEND_DST_COLOR | GLSTATE_DSTBLEND_ONE );
pglDisable( GL_TEXTURE_2D );
if( r_overbrightbits->integer > 0 )
div = 0.5 * (double)( 1 << r_overbrightbits->integer );
else
div = 0.5;
f = div + r_gamma->value;
f = bound( 0.1f, f, 5.0f );
pglBegin( GL_TRIANGLES );
while( f >= 1.01f )
{
if( f >= 2 )
pglColor4f( 1.0f, 1.0f, 1.0f, 1.0f );
else
pglColor4f( f - 1.0f, f - 1.0f, f - 1.0f, 1.0f );
pglVertex2f( -5, -5 );
pglVertex2f( 10, -5 );
pglVertex2f( -5, 10 );
f *= 0.5;
}
pglEnd();
pglEnable( GL_TEXTURE_2D );
pglColor4f( 1, 1, 1, 1 );
}
//=============================================================================
/*
===============
R_SetupFrustum
===============
*/
static void R_SetupFrustum( void )
{
int i;
vec3_t farPoint;
// 0 - left
// 1 - right
// 2 - down
// 3 - up
// 4 - farclip
// rotate RI.vpn right by FOV_X/2 degrees
RotatePointAroundVector( RI.frustum[0].normal, RI.vup, RI.vpn, -( 90-RI.refdef.fov_x / 2 ) );
// rotate RI.vpn left by FOV_X/2 degrees
RotatePointAroundVector( RI.frustum[1].normal, RI.vup, RI.vpn, 90-RI.refdef.fov_x / 2 );
// rotate RI.vpn up by FOV_X/2 degrees
RotatePointAroundVector( RI.frustum[2].normal, RI.vright, RI.vpn, 90-RI.refdef.fov_y / 2 );
// rotate RI.vpn down by FOV_X/2 degrees
RotatePointAroundVector( RI.frustum[3].normal, RI.vright, RI.vpn, -( 90 - RI.refdef.fov_y / 2 ) );
// negate forward vector
VectorNegate( RI.vpn, RI.frustum[4].normal );
for( i = 0; i < 4; i++ )
{
RI.frustum[i].type = PLANE_NONAXIAL;
RI.frustum[i].dist = DotProduct( RI.viewOrigin, RI.frustum[i].normal );
RI.frustum[i].signbits = SignbitsForPlane( RI.frustum[i].normal );
}
VectorMA( RI.viewOrigin, RI.farClip, RI.vpn, farPoint );
RI.frustum[i].type = PLANE_NONAXIAL;
RI.frustum[i].dist = DotProduct( farPoint, RI.frustum[i].normal );
RI.frustum[i].signbits = SignbitsForPlane( RI.frustum[i].normal );
}
/*
===============
R_FarClip
===============
*/
static float R_FarClip( void )
{
float farclip_dist;
if( r_worldmodel && !( RI.refdef.flags & RDF_NOWORLDMODEL ))
{
int i;
float dist;
vec3_t tmp;
farclip_dist = 0;
for( i = 0; i < 8; i++ )
{
tmp[0] = ( ( i & 1 ) ? RI.visMins[0] : RI.visMaxs[0] );
tmp[1] = ( ( i & 2 ) ? RI.visMins[1] : RI.visMaxs[1] );
tmp[2] = ( ( i & 4 ) ? RI.visMins[2] : RI.visMaxs[2] );
dist = VectorDistance2( tmp, RI.viewOrigin );
farclip_dist = max( farclip_dist, dist );
}
farclip_dist = com.sqrt( farclip_dist );
farclip_dist = max( RI.refdef.zFar, farclip_dist );
if( r_worldbrushmodel->globalfog )
{
float fogdist = r_worldbrushmodel->globalfog->shader->fog_dist;
if( farclip_dist > fogdist )
farclip_dist = fogdist;
else
RI.clipFlags &= ~16;
}
}
else
{
farclip_dist = 2048;
}
return max( r_farclip_min, farclip_dist ) + r_farclip_bias;
}
/*
=============
R_SetupProjectionMatrix
=============
*/
static void R_SetupProjectionMatrix( const ref_params_t *rd, matrix4x4 m )
{
GLdouble xMin, xMax, yMin, yMax, zNear, zFar;
if( rd->flags & RDF_NOWORLDMODEL )
RI.farClip = 2048;
else
RI.farClip = R_FarClip();
zNear = Z_NEAR;
zFar = RI.farClip;
yMax = zNear *tan( rd->fov_y *M_PI / 360.0 );
yMin = -yMax;
xMax = zNear *tan( rd->fov_x *M_PI / 360.0 );
xMin = -xMax;
Matrix4x4_CreateProjection( m, xMax, xMin, yMax, yMin, zNear, zFar );
}
/*
=============
R_SetupModelviewMatrix
=============
*/
void R_SetupModelviewMatrix( const ref_params_t *rd, matrix4x4 m )
{
#if 0
Matrix4x4_LoadIdentity( m );
Matrix4x4_ConcatRotate( m, -90, 1, 0, 0 );
Matrix4x4_ConcatRotate( m, 90, 0, 0, 1 );
#else
Matrix4x4_CreateModelview( m );
#endif
Matrix4x4_ConcatRotate( m, -rd->viewangles[2], 1, 0, 0 );
Matrix4x4_ConcatRotate( m, -rd->viewangles[0], 0, 1, 0 );
Matrix4x4_ConcatRotate( m, -rd->viewangles[1], 0, 0, 1 );
Matrix4x4_ConcatTranslate( m, -rd->vieworg[0], -rd->vieworg[1], -rd->vieworg[2] );
}
/*
===============
R_SetupFrame
===============
*/
static void R_SetupFrame( void )
{
// build the transformation matrix for the given view angles
VectorCopy( RI.refdef.vieworg, RI.viewOrigin );
AngleVectors( RI.refdef.viewangles, RI.viewAxis[0], RI.viewAxis[1], RI.viewAxis[2] );
RI.vpn = RI.viewAxis[0];
RI.vright = RI.viewAxis[1];
RI.vup = RI.viewAxis[2];
if( RI.params & RP_SHADOWMAPVIEW )
return;
r_framecount++;
// current viewleaf
if(!( RI.refdef.flags & RDF_NOWORLDMODEL ))
{
mleaf_t *leaf;
vec3_t tmp;
VectorCopy( r_worldmodel->mins, RI.visMins );
VectorCopy( r_worldmodel->maxs, RI.visMaxs );
if(!( RI.params & RP_OLDVIEWLEAF ))
{
r_oldviewleaf = r_viewleaf;
r_oldviewleaf2 = r_viewleaf2;
leaf = Mod_PointInLeaf( RI.pvsOrigin, r_worldmodel );
r_viewleaf2 = r_viewleaf = leaf;
// check above and below so crossing solid water doesn't draw wrong
if( leaf->contents == CONTENTS_EMPTY )
{
// look down a bit
VectorCopy( RI.pvsOrigin, tmp );
tmp[2] -= 16;
leaf = Mod_PointInLeaf( tmp, r_worldmodel );
if(( leaf->contents != CONTENTS_SOLID ) && ( leaf != r_viewleaf2 ))
r_viewleaf2 = leaf;
}
else
{
// look up a bit
VectorCopy( RI.pvsOrigin, tmp );
tmp[2] += 16;
leaf = Mod_PointInLeaf( tmp, r_worldmodel );
if(( leaf->contents != CONTENTS_SOLID ) && ( leaf != r_viewleaf2 ))
r_viewleaf2 = leaf;
}
}
}
}
/*
===============
R_SetupViewMatrices
===============
*/
static void R_SetupViewMatrices( void )
{
R_SetupModelviewMatrix( &RI.refdef, RI.worldviewMatrix );
if( RI.params & RP_SHADOWMAPVIEW )
{
int i;
float x1, x2, y1, y2;
int ix1, ix2, iy1, iy2;
int sizex = RI.refdef.viewport[2], sizey = RI.refdef.viewport[3];
int diffx, diffy;
shadowGroup_t *group = RI.shadowGroup;
R_SetupProjectionMatrix( &RI.refdef, RI.projectionMatrix );
Matrix4x4_Concat( RI.worldviewProjectionMatrix, RI.projectionMatrix, RI.worldviewMatrix );
// compute optimal fov to increase depth precision (so that shadow group objects are
// as close to the nearplane as possible)
// note that it's suboptimal to use bbox calculated in worldspace (FIXME)
x1 = y1 = 999999;
x2 = y2 = -999999;
for( i = 0; i < 8; i++ )
{
vec3_t v, tmp;
// compute and rotate a full bounding box
tmp[0] = ( ( i & 1 ) ? group->mins[0] : group->maxs[0] );
tmp[1] = ( ( i & 2 ) ? group->mins[1] : group->maxs[1] );
tmp[2] = ( ( i & 4 ) ? group->mins[2] : group->maxs[2] );
// transform to screen
R_TransformToScreen_Vec3( tmp, v );
x1 = min( x1, v[0] ); y1 = min( y1, v[1] );
x2 = max( x2, v[0] ); y2 = max( y2, v[1] );
}
// give it 1 pixel gap on both sides
ix1 = x1 - 1.0f; ix2 = x2 + 1.0f;
iy1 = y1 - 1.0f; iy2 = y2 + 1.0f;
diffx = sizex - min( ix1, sizex - ix2 ) * 2;
diffy = sizey - min( iy1, sizey - iy2 ) * 2;
// adjust fov
RI.refdef.fov_x = 2 * RAD2DEG( atan( (float)diffx / (float)sizex ) );
RI.refdef.fov_y = 2 * RAD2DEG( atan( (float)diffy / (float)sizey ) );
}
R_SetupProjectionMatrix( &RI.refdef, RI.projectionMatrix );
if( RI.params & RP_MIRRORVIEW ) RI.projectionMatrix[0][0] = -RI.projectionMatrix[0][0];
Matrix4x4_Concat( RI.worldviewProjectionMatrix, RI.projectionMatrix, RI.worldviewMatrix );
}
/*
=============
R_Clear
=============
*/
static void R_Clear( int bitMask )
{
int bits;
bits = GL_DEPTH_BUFFER_BIT;
if( !( RI.refdef.flags & RDF_NOWORLDMODEL ) && r_fastsky->integer )
bits |= GL_COLOR_BUFFER_BIT;
if( glState.stencilEnabled && ( r_shadows->integer >= SHADOW_PLANAR ) )
bits |= GL_STENCIL_BUFFER_BIT;
bits &= bitMask;
if( bits & GL_STENCIL_BUFFER_BIT )
pglClearStencil( 128 );
if( bits & GL_COLOR_BUFFER_BIT )
{
byte *color = r_worldmodel && !( RI.refdef.flags & RDF_NOWORLDMODEL ) && r_worldbrushmodel->globalfog ?
r_worldbrushmodel->globalfog->shader->fog_color : mapConfig.environmentColor;
pglClearColor( (float)color[0]*( 1.0/255.0 ), (float)color[1]*( 1.0/255.0 ), (float)color[2]*( 1.0/255.0 ), 1 );
}
pglClear( bits );
gldepthmin = 0;
gldepthmax = 1;
pglDepthRange( gldepthmin, gldepthmax );
}
/*
=============
R_SetupGL
=============
*/
static void R_SetupGL( void )
{
pglScissor( RI.scissor[0], RI.scissor[1], RI.scissor[2], RI.scissor[3] );
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( RI.params & RP_CLIPPLANE )
{
GLdouble clip[4];
cplane_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 );
}
if( RI.params & RP_FLIPFRONTFACE )
GL_FrontFace( !glState.frontFace );
if( RI.params & RP_SHADOWMAPVIEW )
{
pglShadeModel( GL_FLAT );
pglColorMask( 0, 0, 0, 0 );
pglPolygonOffset( 1, 4 );
if( prevRI.params & RP_CLIPPLANE )
pglDisable( GL_CLIP_PLANE0 );
}
GL_Cull( GL_FRONT );
GL_SetState( GLSTATE_DEPTHWRITE );
}
/*
=============
R_EndGL
=============
*/
static void R_EndGL( void )
{
if( RI.params & RP_SHADOWMAPVIEW )
{
pglPolygonOffset( -1, -2 );
pglColorMask( 1, 1, 1, 1 );
pglShadeModel( GL_SMOOTH );
if( prevRI.params & RP_CLIPPLANE )
pglEnable( GL_CLIP_PLANE0 );
}
if( RI.params & RP_FLIPFRONTFACE )
GL_FrontFace( !glState.frontFace );
if( RI.params & RP_CLIPPLANE )
pglDisable( GL_CLIP_PLANE0 );
}
/*
=============
R_CategorizeEntities
=============
*/
static void R_CategorizeEntities( void )
{
uint i, j;
edict_t *world;
r_numnullentities = 0;
r_numbmodelentities = 0;
if( !r_drawentities->integer )
return;
// apply global waveheight to world entity
world = ri.GetClientEdict( 0 );
if( world ) r_worldent->waveHeight = world->v.scale * 16.0f;
for( i = 1; i < r_numEntities; i++ )
{
RI.previousentity = RI.currententity;
RI.currententity = &r_entities[i];
if( RI.currententity->rtype != RT_MODEL )
continue;
RI.currentmodel = RI.currententity->model;
if( !RI.currentmodel )
{
r_nullentities[r_numnullentities++] = RI.currententity;
continue;
}
// setup entity parents
if( RI.currententity->movetype == MOVETYPE_FOLLOW && !RI.currententity->parent )
{
edict_t *pEdict = ri.GetClientEdict( RI.currententity->index );
if( RI.currententity->ent_type == ED_TEMPENTITY )
{
// index it's a pointer to parent
if( pEdict && !pEdict->free )
{
for( j = 1; j < r_numEntities; j++ )
{
// we can hit himself before than find real parent
if( RI.currententity == r_entities + j ) continue;
if( r_entities[j].index == pEdict->serialnumber )
{
RI.currententity->parent = r_entities + j;
break;
}
}
}
}
else
{
if( pEdict && pEdict->v.aiment )
{
for( j = 1; j < r_numEntities; j++ )
{
if( r_entities[j].index == pEdict->v.aiment->serialnumber )
{
RI.currententity->parent = r_entities + j;
break;
}
}
}
}
}
switch( RI.currentmodel->type )
{
case mod_brush:
case mod_world:
r_bmodelentities[r_numbmodelentities++] = RI.currententity;
break;
case mod_studio:
if(!( RI.currententity->flags & (EF_NOSHADOW|EF_PLANARSHADOW)))
R_AddShadowCaster( RI.currententity ); // build groups and mark shadow casters
break;
case mod_sprite:
break;
default:
Host_Error( "%s: bad modeltype\n", RI.currentmodel->name );
break;
}
}
}
/*
=============
R_CullEntities
=============
*/
static void R_CullEntities( void )
{
uint i;
ref_entity_t *e;
bool culled;
Mem_Set( r_entVisBits, 0, sizeof( r_entVisBits ));
if( !r_drawentities->integer )
return;
for( i = 1; i < r_numEntities; i++ )
{
RI.previousentity = RI.currententity;
RI.currententity = e = &r_entities[i];
culled = true;
switch( e->rtype )
{
case RT_MODEL:
if( !e->model ) break;
switch( e->model->type )
{
case mod_world:
case mod_brush:
culled = R_CullBrushModel( e );
break;
case mod_studio:
culled = R_CullStudioModel( e );
break;
case mod_sprite:
culled = R_CullSpriteModel( e );
break;
default: break;
}
break;
case RT_SPRITE:
culled = ( e->radius <= 0 ) || ( e->customShader == NULL );
break;
case RT_NONE:
default: break;
}
if( !culled ) r_entVisBits[i>>3] |= ( 1<<( i&7 ));
}
}
/*
=============
R_DrawNullModel
=============
*/
static void R_DrawNullModel( void )
{
pglBegin( GL_LINES );
pglColor4f( 1, 0, 0, 0.5 );
pglVertex3fv( RI.currententity->origin );
pglVertex3f( RI.currententity->origin[0] + RI.currententity->axis[0][0] * 15,
RI.currententity->origin[1] + RI.currententity->axis[0][1] * 15,
RI.currententity->origin[2] + RI.currententity->axis[0][2] * 15 );
pglColor4f( 0, 1, 0, 0.5 );
pglVertex3fv( RI.currententity->origin );
pglVertex3f( RI.currententity->origin[0] - RI.currententity->axis[1][0] * 15,
RI.currententity->origin[1] - RI.currententity->axis[1][1] * 15,
RI.currententity->origin[2] - RI.currententity->axis[1][2] * 15 );
pglColor4f( 0, 0, 1, 0.5 );
pglVertex3fv( RI.currententity->origin );
pglVertex3f( RI.currententity->origin[0] + RI.currententity->axis[2][0] * 15,
RI.currententity->origin[1] + RI.currententity->axis[2][1] * 15,
RI.currententity->origin[2] + RI.currententity->axis[2][2] * 15 );
pglEnd();
}
/*
=============
R_DrawBmodelEntities
=============
*/
static void R_DrawBmodelEntities( void )
{
int i, j;
for( i = 0; i < r_numbmodelentities; i++ )
{
RI.previousentity = RI.currententity;
RI.currententity = r_bmodelentities[i];
j = RI.currententity - r_entities;
if( r_entVisBits[j>>3] & ( 1<<( j & 7 ))) R_AddBrushModelToList( RI.currententity );
}
}
/*
=============
R_DrawRegularEntities
=============
*/
static void R_DrawRegularEntities( void )
{
uint i;
ref_entity_t *e;
bool shadowmap = (( RI.params & RP_SHADOWMAPVIEW ) != 0 );
for( i = 1; i < r_numEntities; i++ )
{
RI.previousentity = RI.currententity;
RI.currententity = e = &r_entities[i];
if( shadowmap )
{
if( e->flags & EF_NOSHADOW )
continue;
if( r_entShadowBits[i] & RI.shadowGroup->bit )
goto add; // shadow caster
}
if(!( r_entVisBits[i>>3] & ( 1<<( i & 7 ))))
continue;
add:
switch( e->rtype )
{
case RT_MODEL:
RI.currentmodel = e->model;
switch( RI.currentmodel->type )
{
case mod_studio:
R_AddStudioModelToList( e );
break;
case mod_sprite:
if( !shadowmap )
R_AddSpriteModelToList( e );
break;
default: break;
}
break;
case RT_SPRITE:
if( !shadowmap )
R_AddSpritePolyToList( e );
break;
case RT_NONE:
default: break;
}
}
}
/*
=============
R_DrawNullEntities
=============
*/
static void R_DrawNullEntities( void )
{
int i;
if( !r_numnullentities )
return;
pglDisable( GL_TEXTURE_2D );
GL_SetState( GLSTATE_SRCBLEND_SRC_ALPHA|GLSTATE_DSTBLEND_ONE_MINUS_SRC_ALPHA );
// draw non-transparent first
for( i = 0; i < r_numnullentities; i++ )
{
RI.previousentity = RI.currententity;
RI.currententity = r_nullentities[i];
if( RI.params & RP_MIRRORVIEW )
{
if( RI.currententity->ent_type == ED_VIEWMODEL )
continue;
}
else
{
if( RP_LOCALCLIENT( RI.currententity ))
continue;
}
R_DrawNullModel();
}
pglEnable( GL_TEXTURE_2D );
}
/*
=============
R_DrawEntities
=============
*/
static void R_DrawEntities( void )
{
bool shadowmap = (( RI.params & RP_SHADOWMAPVIEW ) != 0 );
if( r_drawentities->integer <= 0 )
return;
if( !shadowmap )
{
R_CullEntities(); // mark visible entities in r_entVisBits
R_CullShadowmapGroups();
}
// we don't mark bmodel entities in RP_SHADOWMAPVIEW, only individual surfaces
R_DrawBmodelEntities();
if( OCCLUSION_QUERIES_ENABLED( RI ))
{
R_EndOcclusionPass();
}
if( RI.params & RP_ENVVIEW )
return;
if( !shadowmap ) R_DrawShadowmaps(); // render to depth textures, mark shadowed entities and surfaces
else if(!( RI.params & RP_WORLDSURFVISIBLE ) || ( prevRI.shadowBits & RI.shadowGroup->bit ))
return; // we're supposed to cast shadows but there are no visible surfaces for this light, so stop
// or we've already drawn and captured textures for this group
R_DrawRegularEntities();
}
/*
===============
R_RenderDebugSurface
===============
*/
void R_RenderDebugSurface( void )
{
trace_t tr;
vec3_t forward;
vec3_t start, end;
if( RI.params & RP_NONVIEWERREF || RI.refdef.flags & RDF_NOWORLDMODEL )
return;
r_debug_surface = NULL;
if( r_speeds->integer != 4 )
return;
VectorCopy( RI.vpn, forward );
VectorCopy( RI.viewOrigin, start );
VectorMA( start, 4096, forward, end );
r_debug_surface = R_TraceLine( &tr, start, end );
if( r_debug_surface && r_debug_surface->mesh && !gl_wireframe->integer )
{
RI.previousentity = NULL;
RI.currententity = (ref_entity_t *)tr.pHit;
R_ClearMeshList( RI.meshlist );
R_AddMeshToList( MB_MODEL, NULL, r_debug_surface->shader, r_debug_surface - r_worldbrushmodel->surfaces + 1 );
R_DrawTriangleOutlines( true, false );
}
}
/*
================
R_RenderView
RI.refdef must be set before the first call
================
*/
void R_RenderView( const ref_params_t *fd )
{
double starttime;
bool shadowMap = RI.params & RP_SHADOWMAPVIEW ? true : false;
RI.refdef = *fd;
R_ClearMeshList( RI.meshlist );
if( !r_worldmodel && !( RI.refdef.flags & RDF_NOWORLDMODEL ))
Host_Error( "R_RenderView: NULL worldmodel\n" );
R_SetupFrame();
r_framecount2++;
// we know the farclip so adjust fov before setting up the frustum
if( shadowMap )
{
R_SetupViewMatrices();
}
else if( OCCLUSION_QUERIES_ENABLED( RI ))
{
R_SetupViewMatrices();
R_SetupGL();
R_Clear( ~( GL_STENCIL_BUFFER_BIT|GL_COLOR_BUFFER_BIT ));
R_BeginOcclusionPass();
}
R_SetupFrustum();
if( r_speeds->integer )
starttime = Sys_DoubleTime();
R_MarkLeaves();
if( r_speeds->integer )
r_mark_leaves += ( Sys_DoubleTime() - starttime );
R_DrawWorld();
// we know the the farclip at this point after determining visible world leafs
if( !shadowMap )
{
R_SetupViewMatrices();
R_DrawCoronas();
if( r_speeds->integer )
starttime = Sys_DoubleTime();
R_AddPolysToList();
if( r_speeds->integer )
r_add_polys += ( Sys_DoubleTime() - starttime );
}
if( r_speeds->integer )
starttime = Sys_DoubleTime();
R_DrawEntities();
if( r_speeds->integer )
r_add_entities += ( Sys_DoubleTime() - starttime );
if( shadowMap )
{
if(!( RI.params & RP_WORLDSURFVISIBLE ))
return; // we didn't cast shadows on anything, so stop
if( prevRI.shadowBits & RI.shadowGroup->bit )
return; // already drawn
}
if( r_speeds->integer )
starttime = Sys_DoubleTime();
R_SortMeshes();
if( r_speeds->integer )
r_sort_meshes += ( Sys_DoubleTime() - starttime );
R_DrawPortals();
if( r_portalonly->integer && !( RI.params & ( RP_MIRRORVIEW|RP_PORTALVIEW )))
return;
R_SetupGL();
R_Clear( shadowMap ? ~( GL_STENCIL_BUFFER_BIT|GL_COLOR_BUFFER_BIT ) : ~0 );
if( r_speeds->integer )
starttime = Sys_DoubleTime();
R_DrawMeshes();
if( r_speeds->integer )
r_draw_meshes += ( Sys_DoubleTime() - starttime );
R_BackendCleanUpTextureUnits();
R_DrawTriangleOutlines( gl_wireframe->integer ? true : false, r_shownormals->integer ? true : false );
R_RenderDebugSurface ();
R_DrawEntitiesDebug ();
R_DrawPhysDebug ();
R_DrawNullEntities();
R_EndGL();
}
//=======================================================================
/*
===============
R_BeginFrame
===============
*/
void R_BeginFrame( bool clearScene )
{
if( gl_finish->integer && gl_delayfinish->integer )
{
// flush any remaining 2D bits
// R_Set2DMode( false );
// apply software gamma
// R_ApplySoftwareGamma ();
pglFinish();
R_CheckForErrors ();
// Swap the buffers
if( !r_frontbuffer->integer )
{
if( !pwglSwapBuffers( glw_state.hDC ))
Sys_Break( "R_EndFrame() - SwapBuffers() failed!\n" );
}
}
if( r_colorbits->modified )
{
r_colorbits->modified = false;
}
if( r_environment_color->modified )
{
VectorClear( mapConfig.environmentColor );
mapConfig.environmentColor[3] = 255;
if( r_environment_color->string[0] )
{
int r, g, b;
if( sscanf( r_environment_color->string, "%i %i %i", &r, &g, &b ) == 3 )
{
mapConfig.environmentColor[0] = bound( 0, r, 255 );
mapConfig.environmentColor[1] = bound( 0, g, 255 );
mapConfig.environmentColor[2] = bound( 0, b, 255 );
}
else
{
Cvar_Set( "r_environment_color", "" );
}
}
r_environment_color->modified = false;
}
if( gl_clear->integer && clearScene )
{
rgba_t color;
Vector4Copy( mapConfig.environmentColor, color );
pglClearColor( color[0]*( 1.0/255.0 ), color[1]*( 1.0/255.0 ), color[2]*( 1.0/255.0 ), 1 );
pglClear( GL_COLOR_BUFFER_BIT );
}
// update gamma
if( r_gamma->modified )
{
r_gamma->modified = false;
GL_UpdateGammaRamp();
}
// run cinematic passes on shaders
R_RunAllCinematics();
// go into 2D mode
R_Set2DMode( true );
// draw buffer stuff
if( r_frontbuffer->modified )
{
r_frontbuffer->modified = false;
if( r_frontbuffer->integer )
pglDrawBuffer( GL_FRONT );
else pglDrawBuffer( GL_BACK );
}
// texturemode stuff
// update texture parameters
if( gl_texturemode->modified || gl_texture_anisotropy->modified || gl_texture_lodbias ->modified )
R_SetTextureParameters();
// swapinterval stuff
GL_UpdateSwapInterval();
}
/*
====================
R_ClearScene
====================
*/
void R_ClearScene( void )
{
r_numEntities = 1; // worldmodel
r_numDlights = 0;
r_numPolys = 0;
RI.previousentity = NULL;
RI.currententity = r_worldent;
RI.currentmodel = r_worldmodel;
}
/*
=====================
R_AddPolyToScene
=====================
*/
bool R_AddPolyToScene( const poly_t *poly )
{
if(( r_numPolys < MAX_POLYS ) && poly && poly->numverts )
{
poly_t *dp = &r_polys[r_numPolys];
ref_shader_t *shader;
*dp = *poly;
if( dp->numverts > MAX_POLY_VERTS )
dp->numverts = MAX_POLY_VERTS;
if( poly->shadernum < 0 || poly->shadernum > MAX_SHADERS )
return false;
if( !(shader = &r_shaders[poly->shadernum] ))
return false;
dp->shader = shader;
r_numPolys++;
return true;
}
return false;
}
/*
=====================
R_AddLightStyleToScene
=====================
*/
void R_AddLightStyleToScene( int style, float r, float g, float b )
{
lightstyle_t *ls;
if( !r_worldmodel || !r_worldbrushmodel->lightdata )
return; // don't apply lightstyles when no lighting info
if( style < 0 || style >= MAX_LIGHTSTYLES )
Host_Error( "R_SetLightStyle: bad light style %i\n", style );
ls = &r_lightStyles[style];
ls->rgb[0] = max( 0, r );
ls->rgb[1] = max( 0, g );
ls->rgb[2] = max( 0, b );
ls->white = ls->rgb[0] + ls->rgb[1] + ls->rgb[2];
}
/*
===============
R_RenderScene
===============
*/
void R_RenderScene( const ref_params_t *fd )
{
// flush any remaining 2D bits
R_Set2DMode( false );
if( r_norefresh->integer )
return;
R_BackendStartFrame();
if(!( fd->flags & RDF_NOWORLDMODEL ))
{
r_lastRefdef = *fd;
}
c_brush_polys = 0;
c_world_leafs = 0;
r_mark_leaves = r_add_polys = r_add_entities = r_sort_meshes = r_draw_meshes = r_world_node = 0;
RI.params = RP_NONE;
RI.refdef = *fd;
RI.farClip = 0;
RI.clipFlags = 15;
RI.lerpFrac = ri.GetLerpFrac();
if( r_worldmodel && !( RI.refdef.flags & RDF_NOWORLDMODEL ) && r_worldbrushmodel->globalfog )
{
RI.farClip = r_worldbrushmodel->globalfog->shader->fog_dist;
RI.farClip = max( r_farclip_min, RI.farClip ) + r_farclip_bias;
RI.clipFlags |= 16;
}
RI.meshlist = &r_worldlist;
RI.shadowBits = 0;
RI.shadowGroup = NULL;
// adjust field of view for widescreen
if( glState.wideScreen && !( fd->flags & RDF_NOFOVADJUSTMENT ))
AdjustFov( &RI.refdef.fov_x, &RI.refdef.fov_y, glState.width, glState.height, false );
Vector4Set( RI.scissor, fd->viewport[0], glState.height - fd->viewport[3] - fd->viewport[1], fd->viewport[2], fd->viewport[3] );
Vector4Set( RI.viewport, fd->viewport[0], glState.height - fd->viewport[3] - fd->viewport[1], fd->viewport[2], fd->viewport[3] );
VectorCopy( fd->vieworg, RI.pvsOrigin );
if( gl_finish->integer && !gl_delayfinish->integer && !( fd->flags & RDF_NOWORLDMODEL ))
pglFinish();
R_ClearShadowmaps();
R_CategorizeEntities();
R_RenderView( fd );
R_BloomBlend( fd );
R_PolyBlend();
R_BackendEndFrame();
R_Set2DMode( true );
R_ShowTextures();
}
/*
===============
R_EndFrame
===============
*/
void R_EndFrame( void )
{
// flush any remaining 2D bits
R_Set2DMode( false );
// cleanup texture units
R_BackendCleanUpTextureUnits();
// apply software gamma
R_ApplySoftwareGamma();
if( gl_finish->integer && gl_delayfinish->integer )
{
pglFlush();
return;
}
R_CheckForErrors ();
// Swap the buffers
if( !r_frontbuffer->integer )
{
if( !pwglSwapBuffers( glw_state.hDC ))
Sys_Break( "R_EndFrame() - SwapBuffers() failed!\n" );
}
}
/*
===============
R_SpeedsMessage
===============
*/
static bool R_SpeedsMessage( char *out, size_t size )
{
if( r_speeds->integer <= 0 ) return false;
if( !out || !size ) return false;
com.strncpy( out, r_speeds_msg, size );
return true;
}
//==================================================================================
int R_ComputeFxBlend( ref_entity_t *e )
{
int blend = 0, renderAmt;
float offset, dist;
edict_t *m_pEntity = ri.GetClientEdict( e->index );
vec3_t tmp;
offset = ((int)e->index ) * 363.0f; // Use ent index to de-sync these fx
if( e->ent_type == ED_TEMPENTITY )
{
renderAmt = e->renderamt;
}
else if( m_pEntity && !m_pEntity->free )
{
renderAmt = m_pEntity->v.renderamt;
}
else renderAmt = 255;
switch( e->renderfx )
{
case kRenderFxPulseSlowWide:
blend = renderAmt + 0x40 * com.sin( RI.refdef.time * 2 + offset );
break;
case kRenderFxPulseFastWide:
blend = renderAmt + 0x40 * com.sin( RI.refdef.time * 8 + offset );
break;
case kRenderFxPulseSlow:
blend = renderAmt + 0x10 * com.sin( RI.refdef.time * 2 + offset );
break;
case kRenderFxPulseFast:
blend = renderAmt + 0x10 * com.sin( RI.refdef.time * 8 + offset );
break;
// JAY: HACK for now -- not time based
case kRenderFxFadeSlow:
if( renderAmt > 0 )
renderAmt -= 1;
else renderAmt = 0;
blend = renderAmt;
break;
case kRenderFxFadeFast:
if( renderAmt > 3 )
renderAmt -= 4;
else renderAmt = 0;
blend = renderAmt;
break;
case kRenderFxSolidSlow:
if( renderAmt < 255 )
renderAmt += 1;
else renderAmt = 255;
blend = renderAmt;
break;
case kRenderFxSolidFast:
if( renderAmt < 252 )
renderAmt += 4;
else renderAmt = 255;
blend = renderAmt;
break;
case kRenderFxStrobeSlow:
blend = 20 * com.sin( RI.refdef.time * 4 + offset );
if( blend < 0 ) blend = 0;
else blend = renderAmt;
break;
case kRenderFxStrobeFast:
blend = 20 * com.sin( RI.refdef.time * 16 + offset );
if( blend < 0 ) blend = 0;
else blend = renderAmt;
break;
case kRenderFxStrobeFaster:
blend = 20 * com.sin( RI.refdef.time * 36 + offset );
if( blend < 0 ) blend = 0;
else blend = renderAmt;
break;
case kRenderFxFlickerSlow:
blend = 20 * (com.sin( RI.refdef.time * 2 ) + com.sin( RI.refdef.time * 17 + offset ));
if( blend < 0 ) blend = 0;
else blend = renderAmt;
break;
case kRenderFxFlickerFast:
blend = 20 * (com.sin( RI.refdef.time * 16 ) + com.sin( RI.refdef.time * 23 + offset ));
if( blend < 0 ) blend = 0;
else blend = renderAmt;
break;
case kRenderFxHologram:
case kRenderFxDistort:
VectorCopy( e->origin, tmp );
VectorSubtract( tmp, RI.refdef.vieworg, tmp );
dist = DotProduct( tmp, RI.refdef.forward );
// Turn off distance fade
if( e->renderfx == kRenderFxDistort )
dist = 1;
if( dist <= 0 )
{
blend = 0;
}
else
{
renderAmt = 180;
if( dist <= 100 ) blend = renderAmt;
else blend = (int) ((1.0f - ( dist - 100 ) * ( 1.0f / 400.0f )) * renderAmt );
blend += Com_RandomLong( -32, 31 );
}
break;
case kRenderFxNone:
case kRenderFxClampMinScale:
default:
if( e->rendermode == kRenderNormal )
blend = 255;
else blend = renderAmt;
break;
}
// store value back into client entity, some effects requires this
if( m_pEntity )
{
m_pEntity->v.renderamt = renderAmt;
// NOTE: never pass sprites with rendercolor '0 0 0' it's a stupid Valve Hammer Editor bug
if(( !e->rendercolor[0] && !e->rendercolor[1] && !e->rendercolor[2] ))
VectorSet( e->rendercolor, 255, 255, 255 );
}
blend = bound( 0, blend, 255 );
return blend;
}
/*
=============
R_TransformToScreen_Vec3
=============
*/
bool R_TransformToScreen_Vec3( const vec3_t in, vec3_t out )
{
vec4_t temp, temp2;
temp[0] = in[0];
temp[1] = in[1];
temp[2] = in[2];
temp[3] = 1.0f;
Matrix4x4_ConcatVector( RI.worldviewProjectionMatrix, temp, temp2 );
if( !temp2[3] ) return true; // Z-clipped
out[0] = (temp2[0] / temp2[3] + 1.0f) * 0.5f * RI.refdef.viewport[2];
out[1] = (temp2[1] / temp2[3] + 1.0f) * 0.5f * RI.refdef.viewport[3];
out[2] = (temp2[2] / temp2[3] + 1.0f) * 0.5f;
return false;
}
/*
=============
R_TransformVectorToScreen
=============
*/
void R_TransformVectorToScreen( const ref_params_t *rd, const vec3_t in, vec2_t out )
{
matrix4x4 p, m;
vec4_t temp, temp2;
if( !rd || !in || !out )
return;
temp[0] = in[0];
temp[1] = in[1];
temp[2] = in[2];
temp[3] = 1.0f;
R_SetupProjectionMatrix( rd, p );
R_SetupModelviewMatrix( rd, m );
Matrix4x4_ConcatVector( m, temp, temp2 );
Matrix4x4_ConcatVector( p, temp2, temp );
if( !temp[3] ) return;
out[0] = rd->viewport[0] + ( temp[0] / temp[3] + 1.0f ) * rd->viewport[2] * 0.5f;
out[1] = rd->viewport[1] + ( temp[1] / temp[3] + 1.0f ) * rd->viewport[3] * 0.5f;
}
/*
=============
R_ScreenTransform
transform world position into screen
=============
*/
bool R_ScreenTransform( const vec3_t point, vec3_t pClip )
{
matrix4x4 worldToScreen;
bool behind;
float w;
Matrix4x4_Copy( worldToScreen, RI.worldviewProjectionMatrix );
pClip[0] = worldToScreen[0][0] * point[0] + worldToScreen[0][1] * point[1] + worldToScreen[0][2] * point[2] + worldToScreen[0][3];
pClip[1] = worldToScreen[1][0] * point[0] + worldToScreen[1][1] * point[1] + worldToScreen[1][2] * point[2] + worldToScreen[1][3];
// z = worldToScreen[2][0] * point[0] + worldToScreen[2][1] * point[1] + worldToScreen[2][2] * point[2] + worldToScreen[2][3];
w = worldToScreen[3][0] * point[0] + worldToScreen[3][1] * point[1] + worldToScreen[3][2] * point[2] + worldToScreen[3][3];
// Just so we have something valid here
pClip[2] = 0.0f;
if( w < 0.001f )
{
behind = true;
pClip[0] *= 100000;
pClip[1] *= 100000;
}
else
{
float invw = 1.0f / w;
behind = false;
pClip[0] *= invw;
pClip[1] *= invw;
}
return behind;
}
static void R_ScreenToWorld( const float *screen, float *world )
{
// FIXME: implement
}
static bool R_WorldToScreen( const float *world, float *screen )
{
return R_ScreenTransform( world, screen );
}
//==================================================================================
/*
=============
R_TraceLine
=============
*/
msurface_t *R_TraceLine( trace_t *tr, const vec3_t start, const vec3_t end )
{
int i;
msurface_t *surf;
// trace against world
surf = R_TransformedTraceLine( tr, start, end, r_worldent );
// trace against bmodels
for( i = 0; i < r_numbmodelentities; i++ )
{
trace_t t2;
msurface_t *s2;
s2 = R_TransformedTraceLine( &t2, start, end, r_bmodelentities[i] );
if( t2.flFraction < tr->flFraction )
{
*tr = t2; // closer impact point
surf = s2;
}
}
return surf;
}
bool R_UploadModel( const char *name, int index )
{
ref_model_t *mod;
// this array used by AddEntityToScene
mod = R_RegisterModel( name );
cl_models[index] = mod;
return (mod != NULL);
}
shader_t Mod_RegisterShader( const char *name, int shaderType )
{
ref_shader_t *src;
if( !glState.initializedMedia )
return 0;
switch( shaderType )
{
case SHADER_FONT:
case SHADER_NOMIP:
src = R_LoadShader( name, shaderType, false, TF_CLAMP|TF_NOMIPMAP|TF_NOPICMIP, SHADER_INVALID );
break;
case SHADER_DECAL:
case SHADER_GENERIC:
src = R_LoadShader( name, shaderType, false, 0, SHADER_INVALID );
break;
case SHADER_SKY:
src = R_SetupSky( name );
break;
default:
MsgDev( D_WARN, "Mod_RegisterShader: invalid shader type (%i)\n", shaderType );
return 0;
}
return src - r_shaders;
}
byte *Mod_GetCurrentVis( void )
{
return Mod_LeafPVS( r_viewleaf, r_worldmodel );
}
bool Mod_CullBox( const vec3_t mins, const vec3_t maxs )
{
return R_CullBox( mins, maxs, 15 );
}
shader_t R_GetSpriteTexture( int spriteIndex, int spriteFrame )
{
ref_model_t *pSpriteModel;
mspriteframe_t *pSpriteFrame;
if( spriteIndex <= 0 || spriteIndex >= MAX_MODELS )
return tr.defaultShader->shadernum; // assume error
pSpriteModel = cl_models[spriteIndex];
if( !pSpriteModel || pSpriteModel->type != mod_sprite )
{
MsgDev( D_ERROR, "R_GetSpriteTexture: invalid Sprite %d\n", spriteIndex );
return tr.defaultShader->shadernum;
}
// okay, at this point we have valid sprite model
pSpriteFrame = R_GetSpriteFrame( pSpriteModel, spriteFrame, 0.0f );
return pSpriteFrame->shader;
}
bool R_AddLightStyle( int stylenum, vec3_t color )
{
if( stylenum < 0 || stylenum > MAX_LIGHTSTYLES )
return false; // invalid lightstyle
R_AddLightStyleToScene( stylenum, color[0], color[1], color[2] );
return true;
}
bool R_AddGenericEntity( edict_t *pRefEntity, ref_entity_t *refent )
{
// check model
if( !refent->model ) return false;
// get pointer to lerping frame data
refent->lerp = ri.GetLerpFrame( refent->index );
if( refent->lerp == NULL )
{
MsgDev( D_ERROR, "Rejected entity %i (model %s) -- no lerpframe data\n", refent->index, refent->model->name );
return false;
}
refent->rtype = RT_MODEL;
// setup light origin
VectorCopy( pRefEntity->v.origin, refent->lightingOrigin );
refent->lightingOrigin[2] += 1;
switch( refent->model->type )
{
case mod_world:
case mod_brush:
if( !refent->model->extradata )
return false;
refent->waveHeight = refent->scale * 16.0f;
refent->scale = 1.0f; // ignore scale for brush models
refent->frame = pRefEntity->v.frame; // brush properly animating
break;
case mod_studio:
case mod_sprite:
if( !refent->model->extradata )
return false;
refent->frame = 0.0f; // keep clear to prevent randomly change skins :-)
refent->lerp->curstate.frame = pRefEntity->v.frame;
break;
case mod_bad: // let the render drawing null model
break;
}
// calculate angles
if( refent->flags & EF_ROTATE )
{
// some bonus items auto-rotate
VectorSet( refent->angles, 0, anglemod( RI.refdef.time / 10), 0 );
}
else VectorCopy( pRefEntity->v.angles, refent->angles );
VectorCopy( pRefEntity->v.origin, refent->origin );
Matrix3x3_FromAngles( refent->angles, refent->axis );
VectorClear( refent->origin2 );
if( refent->ent_type == ED_CLIENT )
{
refent->gaitsequence = pRefEntity->v.gaitsequence;
refent->flags |= EF_OCCLUSIONTEST;
refent->lightingOrigin[2] += refent->model->maxs[2] - 2; // drop shadow to floor
}
else refent->gaitsequence = 0;
if( refent->ent_type == ED_MOVER || refent->ent_type == ED_BSPBRUSH )
{
VectorNormalize2( pRefEntity->v.movedir, refent->movedir );
}
else VectorClear( refent->movedir );
if( refent->ent_type == ED_VIEWMODEL )
{
if( r_lefthand->integer == 1 )
VectorNegate( refent->axis[1], refent->axis[1] );
}
if( refent->model->type == mod_studio )
{
R_StudioAllocExtradata( pRefEntity, refent );
}
else
{
// entity has changed model, so no reason to keep this data
if( refent->extradata ) Mem_EmptyPool( refent->mempool );
refent->extradata = NULL;
}
// because entity without models never added to scene
if( !refent->ent_type )
{
switch( refent->model->type )
{
case mod_brush:
case mod_world:
refent->ent_type = ED_BSPBRUSH;
break;
case mod_studio:
case mod_sprite:
refent->ent_type = ED_NORMAL;
break;
// and ignore all other unset ents
}
}
return true;
}
bool R_AddPortalEntity( edict_t *pRefEntity, ref_entity_t *refent )
{
refent->rtype = RT_PORTALSURFACE;
VectorClear( refent->angles );
VectorCopy( pRefEntity->v.movedir, refent->movedir );
VectorCopy( pRefEntity->v.origin, refent->origin );
VectorCopy( pRefEntity->v.oldorigin, refent->origin2 );
if( refent->flags & EF_ROTATE )
{
float phase = pRefEntity->v.frame;
float speed = (pRefEntity->v.framerate ? pRefEntity->v.framerate : 50.0f);
refent->angles[ROLL] = 5 * com.sin(( phase + RI.refdef.time * speed * 0.01f ) * M_PI2);
}
// calculate angles
Matrix3x3_FromAngles( refent->angles, refent->axis );
return true;
}
bool R_AddEntityToScene( edict_t *pRefEntity, int ed_type, shader_t customShader )
{
ref_entity_t *refent;
ref_shader_t *shader;
bool result = false;
if( !pRefEntity || pRefEntity->v.modelindex <= 0 || pRefEntity->v.modelindex >= MAX_MODELS )
return false; // if set to invisible, skip
if( r_numEntities >= MAX_ENTITIES )
{
MsgDev( D_ERROR, "R_AddEntityToScene: too many visible entities\n" );
return false;
}
refent = &r_entities[r_numEntities];
if( pRefEntity->v.effects & EF_NODRAW && ed_type != ED_PORTAL )
return true; // done
if( pRefEntity->v.rendermode != kRenderNormal && pRefEntity->v.renderamt <= 0.0f )
return true; // done
// filter ents
switch( ed_type )
{
case ED_MOVER:
case ED_PORTAL:
case ED_CLIENT:
case ED_NORMAL:
case ED_MONSTER:
case ED_BSPBRUSH:
case ED_RIGIDBODY:
case ED_VIEWMODEL: break;
default: return false;
}
// ignore env_sprite flares if supposed
if( !r_spriteflares->integer && pRefEntity->v.rendermode == kRenderGlow )
{
if( cl_models[pRefEntity->v.modelindex]->type == mod_sprite && !pRefEntity->v.renderfx )
return true; // only sprite flares with variable size supposed to be ignored
// because we don't want ignore laserspot and other like things
}
// copy state to render
refent->index = pRefEntity->serialnumber;
refent->ent_type = ed_type;
refent->rendermode = pRefEntity->v.rendermode;
VectorCopy( pRefEntity->v.rendercolor, refent->rendercolor );
refent->body = pRefEntity->v.body;
refent->skin = pRefEntity->v.skin;
refent->scale = pRefEntity->v.scale;
refent->colormap = pRefEntity->v.colormap;
refent->flags = pRefEntity->v.effects;
refent->renderfx = pRefEntity->v.renderfx;
refent->renderamt = pRefEntity->v.renderamt;
refent->model = cl_models[pRefEntity->v.modelindex];
refent->movetype = pRefEntity->v.movetype;
refent->framerate = pRefEntity->v.framerate;
refent->parent = NULL;
refent->lerp = NULL;
if( pRefEntity->v.rendermode == kRenderGlow && !pRefEntity->v.renderfx )
refent->flags |= EF_OCCLUSIONTEST;
// setup custom shader
if( customShader >= 0 && customShader < MAX_SHADERS && (shader = &r_shaders[customShader]))
refent->customShader = shader;
else refent->customShader = NULL;
refent->rtype = RT_MODEL;
// setup rtype
switch( ed_type )
{
case ED_PORTAL:
result = R_AddPortalEntity( pRefEntity, refent );
break;
default:
result = R_AddGenericEntity( pRefEntity, refent );
break;
}
if( result ) r_numEntities++;
refent->renderamt = R_ComputeFxBlend( refent );
if( refent->index == VIEWENT_INDEX )
{
// run events here to prevent
// de-synchronize muzzleflashes movement
R_StudioRunEvents( refent );
}
// never adding child entity without parent
// only studio models can have attached childrens
if( result && refent->model->type == mod_studio && pRefEntity->v.weaponmodel && (refent->renderfx != kRenderFxDeadPlayer))
{
edict_t FollowEntity = *pRefEntity;
// create attached entity
FollowEntity.v.modelindex = pRefEntity->v.weaponmodel;
FollowEntity.v.movetype = MOVETYPE_FOLLOW;
FollowEntity.v.weaponmodel = 0;
if( R_AddEntityToScene( &FollowEntity, ED_NORMAL, customShader ))
r_entities[r_numEntities-1].parent = refent; // set parent
}
return result;
}
bool R_AddTeEntToScene( TEMPENTITY *pTempEntity, int ed_type, shader_t customShader )
{
ref_entity_t *refent;
ref_shader_t *shader;
vec3_t center;
if( !pTempEntity || pTempEntity->modelindex <= 0 || pTempEntity->modelindex >= MAX_MODELS )
return false; // if set to invisible, skip
if( r_numEntities >= MAX_ENTITIES ) return false;
refent = &r_entities[r_numEntities];
if( pTempEntity->flags & FTENT_NOMODEL )
return true; // done
// filter ents
switch( ed_type )
{
case ED_TEMPENTITY: break;
default: return false;
}
// ignore env_sprite flares if supposed
if( !r_spriteflares->integer && pTempEntity->renderMode == kRenderGlow )
{
if( cl_models[pTempEntity->modelindex]->type == mod_sprite && !pTempEntity->renderFX )
return true; // only sprite flares with variable size supposed to be ignored
// because we don't want ignore laserspot and other like things
}
// copy state to render
if( pTempEntity->clientIndex != 0 )
refent->index = pTempEntity->clientIndex;
else refent->index = NULLENT_INDEX;
refent->ent_type = ed_type;
refent->rendermode = pTempEntity->renderMode;
refent->body = pTempEntity->body;
refent->skin = pTempEntity->skin;
refent->scale = pTempEntity->m_flSpriteScale;
refent->renderfx = pTempEntity->renderFX;
refent->renderamt = pTempEntity->renderAmt;
VectorCopy( pTempEntity->renderColor, refent->rendercolor );
refent->model = cl_models[pTempEntity->modelindex];
refent->framerate = pTempEntity->m_flFrameRate;
refent->movetype = MOVETYPE_NOCLIP;
refent->flags = EF_NOSHADOW;
refent->gaitsequence = 0;
refent->parent = NULL;
refent->lerp = NULL;
refent->renderamt = R_ComputeFxBlend( refent );
// attached entity (can attach sprites to models)
if( pTempEntity->m_iAttachment && cl_models[pTempEntity->modelindex]->type == mod_sprite )
{
// set attachment right
refent->colormap = ((refent->colormap & 0xFF00)>>8) | pTempEntity->m_iAttachment;
refent->movetype = MOVETYPE_FOLLOW;
}
// check model
if( !refent->model ) return false;
switch( refent->model->type )
{
case mod_bad:
case mod_world:
case mod_brush:
return false;
case mod_studio:
case mod_sprite:
if( !refent->model->extradata )
return false;
break;
}
refent->lerp = pTempEntity->pvEngineData; // setup lerpframe data
if( refent->lerp == NULL )
{
MsgDev( D_ERROR, "Rejected temp entity (model %s) -- no lerpframe data\n", refent->model->name );
return false;
}
// setup custom shader
if( customShader >= 0 && customShader < MAX_SHADERS && (shader = &r_shaders[customShader]))
refent->customShader = shader;
else refent->customShader = NULL;
refent->rtype = RT_MODEL;
refent->lerp->curstate.frame = pTempEntity->m_flFrame;
// setup light origin
if( refent->model ) VectorAverage( refent->model->mins, refent->model->maxs, center );
else VectorClear( center );
VectorAdd( pTempEntity->origin, center, refent->lightingOrigin );
VectorClear( refent->movedir );
VectorCopy( pTempEntity->angles, refent->angles );
VectorCopy( pTempEntity->origin, refent->origin );
Matrix3x3_FromAngles( refent->angles, refent->axis );
VectorClear( refent->origin2 );
if( refent->model->type == mod_studio )
{
R_StudioAllocTentExtradata( pTempEntity, refent );
}
else
{
// entity has changed model, so no reason to keep this data
if( refent->extradata ) Mem_EmptyPool( refent->mempool );
refent->extradata = NULL;
}
r_numEntities++; // added
return true;
}
bool R_AddDynamicLight( const void *dlight )
{
dlight_t *dl, *src = (dlight_t *)dlight;
ref_shader_t *shader;
if(( r_numDlights >= MAX_DLIGHTS ) || (!src) || (src->intensity == 0) || ( VectorIsNull( src->color )))
return false;
if( src->texture < 0 || src->texture > MAX_SHADERS || !(shader = &r_shaders[src->texture])->name)
shader = NULL;
dl = &r_dlights[r_numDlights++];
VectorCopy( src->origin, dl->origin );
VectorCopy( src->color, dl->color );
Vector2Copy( src->cone, dl->cone );
dl->intensity = src->intensity * 0.5f;
dl->shader = shader;
R_LightBounds( dl->origin, dl->intensity, dl->mins, dl->maxs );
return true;
}
/*
@@@@@@@@@@@@@@@@@@@@@
CreateAPI
@@@@@@@@@@@@@@@@@@@@@
*/
render_exp_t DLLEXPORT *CreateAPI(stdlib_api_t *input, render_imp_t *engfuncs )
{
static render_exp_t re;
com = *input;
// Sys_LoadLibrary can create fake instance, to check
// api version and api size, but second argument will be 0
// and always make exception, run simply check for avoid it
if( engfuncs ) ri = *engfuncs;
// generic functions
re.api_size = sizeof( render_exp_t );
re.com_size = sizeof( stdlib_api_t );
re.Init = R_Init;
re.Shutdown = R_Shutdown;
re.BeginRegistration = R_BeginRegistration;
re.RegisterModel = R_UploadModel;
re.RegisterShader = Mod_RegisterShader;
re.EndRegistration = R_EndRegistration;
re.FreeShader = Mod_FreeShader;
re.AddLightStyle = R_AddLightStyle;
re.AddRefEntity = R_AddEntityToScene;
re.AddTmpEntity = R_AddTeEntToScene;
re.AddDynLight = R_AddDynamicLight;
re.AddPolygon = R_AddPolyToScene;
re.ClearScene = R_ClearScene;
re.BeginFrame = R_BeginFrame;
re.RenderFrame = R_RenderScene;
re.EndFrame = R_EndFrame;
re.RenderMode = Tri_RenderMode;
re.TexCoord2f = Tri_TexCoord2f;
re.Normal3f = Tri_Normal3f;
re.Vertex3f = Tri_Vertex3f;
re.Color4ub = Tri_Color4ub;
re.CullFace = Tri_CullFace;
re.Disable = Tri_Disable;
re.Enable = Tri_Enable;
re.Begin = Tri_Begin;
re.Bind = Tri_Bind;
re.End = Tri_End;
re.Fog = Tri_Fog;
re.SetColor = R_DrawSetColor;
re.GetParms = R_DrawGetParms;
re.SetParms = R_DrawSetParms;
re.ScrShot = VID_ScreenShot;
re.EnvShot = VID_CubemapShot;
re.LightForPoint = R_LightForPoint;
re.DrawStretchRaw = R_DrawStretchRaw;
re.DrawStretchPic = R_DrawStretchPic;
re.GetSpriteTexture = R_GetSpriteTexture;
re.GetFragments = R_GetClippedFragments;
re.ScreenToWorld = R_ScreenToWorld;
re.WorldToScreen = R_WorldToScreen;
re.RSpeedsMessage = R_SpeedsMessage;
re.CullBox = Mod_CullBox;
re.Support = GL_Support;
re.GetCurrentVis = Mod_GetCurrentVis;
re.RestoreGamma = R_RestoreGamma;
return &re;
}