1391 lines
34 KiB
C
1391 lines
34 KiB
C
//=======================================================================
|
|
// Copyright XashXT Group 2007 ©
|
|
// r_main.c - opengl render core
|
|
//=======================================================================
|
|
|
|
#include "r_local.h"
|
|
#include "mathlib.h"
|
|
#include "matrixlib.h"
|
|
#include "const.h"
|
|
|
|
render_imp_t ri;
|
|
stdlib_api_t com;
|
|
|
|
byte *r_temppool;
|
|
matrix4x4 r_worldMatrix;
|
|
matrix4x4 r_entityMatrix;
|
|
|
|
gl_matrix gl_projectionMatrix;
|
|
gl_matrix gl_entityMatrix;
|
|
gl_matrix gl_textureMatrix;
|
|
cplane_t r_frustum[4];
|
|
float r_frameTime;
|
|
mesh_t r_solidMeshes[MAX_MESHES];
|
|
int r_numSolidMeshes;
|
|
mesh_t r_transMeshes[MAX_MESHES];
|
|
int r_numTransMeshes;
|
|
ref_entity_t *r_nullModels[MAX_ENTITIES];
|
|
rmodel_t *cl_models[MAX_MODELS]; // client replacement modeltable
|
|
lightstyle_t r_lightStyles[MAX_LIGHTSTYLES];
|
|
ref_entity_t r_entities[MAX_ENTITIES];
|
|
int r_numEntities;
|
|
dlight_t r_dlights[MAX_DLIGHTS];
|
|
int r_numDLights;
|
|
particle_t r_particles[MAX_PARTICLES];
|
|
int r_numParticles;
|
|
poly_t r_polys[MAX_POLYS];
|
|
int r_numPolys;
|
|
polyVert_t r_polyVerts[MAX_POLY_VERTS];
|
|
int r_numPolyVerts;
|
|
int r_numNullModels;
|
|
refdef_t r_refdef;
|
|
refstats_t r_stats;
|
|
byte *r_framebuffer; // pause frame buffer
|
|
float r_pause_alpha;
|
|
glconfig_t gl_config;
|
|
glstate_t gl_state;
|
|
|
|
// view matrix
|
|
vec3_t r_forward;
|
|
vec3_t r_right;
|
|
vec3_t r_up;
|
|
vec3_t r_origin;
|
|
|
|
cvar_t *r_check_errors;
|
|
cvar_t *r_himodels;
|
|
cvar_t *r_norefresh;
|
|
cvar_t *r_novis;
|
|
cvar_t *r_nocull;
|
|
cvar_t *r_nobind;
|
|
cvar_t *r_drawworld;
|
|
cvar_t *r_drawentities;
|
|
cvar_t *r_drawparticles;
|
|
cvar_t *r_drawpolys;
|
|
cvar_t *r_fullbright;
|
|
cvar_t *r_lightmap;
|
|
cvar_t *r_lockpvs;
|
|
cvar_t *r_frontbuffer;
|
|
cvar_t *r_showcluster;
|
|
cvar_t *r_showtris;
|
|
cvar_t *r_shownormals;
|
|
cvar_t *r_showtangentspace;
|
|
cvar_t *r_showmodelbounds;
|
|
cvar_t *r_showshadowvolumes;
|
|
cvar_t *r_offsetfactor;
|
|
cvar_t *r_offsetunits;
|
|
cvar_t *r_debugsort;
|
|
cvar_t *r_speeds;
|
|
cvar_t *r_singleshader;
|
|
cvar_t *r_skipbackend;
|
|
cvar_t *r_skipfrontend;
|
|
cvar_t *r_swapInterval;
|
|
cvar_t *r_vertexbuffers;
|
|
cvar_t *r_mode;
|
|
cvar_t *r_testmode;
|
|
cvar_t *r_fullscreen;
|
|
cvar_t *r_minimap;
|
|
cvar_t *r_minimap_size;
|
|
cvar_t *r_minimap_zoom;
|
|
cvar_t *r_minimap_style;
|
|
cvar_t *r_pause;
|
|
cvar_t *r_width;
|
|
cvar_t *r_height;
|
|
cvar_t *r_refreshrate;
|
|
cvar_t *r_bitdepth;
|
|
cvar_t *r_overbrightbits;
|
|
cvar_t *r_shadows;
|
|
cvar_t *r_caustics;
|
|
cvar_t *r_dynamiclights;
|
|
cvar_t *r_modulate;
|
|
cvar_t *r_ambientscale;
|
|
cvar_t *r_directedscale;
|
|
cvar_t *r_intensity;
|
|
cvar_t *r_texturebits;
|
|
cvar_t *r_texturefilter;
|
|
cvar_t *r_texturefilteranisotropy;
|
|
cvar_t *r_texturelodbias;
|
|
cvar_t *r_max_normal_texsize;
|
|
cvar_t *r_max_texsize;
|
|
cvar_t *r_round_down;
|
|
cvar_t *r_detailtextures;
|
|
cvar_t *r_compress_normal_textures;
|
|
cvar_t *r_compress_textures;
|
|
cvar_t *r_lefthand;
|
|
cvar_t *r_bloom;
|
|
cvar_t *r_bloom_alpha;
|
|
cvar_t *r_bloom_diamond_size;
|
|
cvar_t *r_bloom_intensity;
|
|
cvar_t *r_bloom_darken;
|
|
cvar_t *r_bloom_sample_size;
|
|
cvar_t *r_bloom_fast_sample;
|
|
cvar_t *r_motionblur_intens;
|
|
cvar_t *r_motionblur;
|
|
cvar_t *r_mirroralpha;
|
|
cvar_t *r_interpolate;
|
|
cvar_t *r_physbdebug;
|
|
cvar_t *r_pause_bw;
|
|
cvar_t *r_lightlevel; // FIXME: This is a HACK to get the client's light level
|
|
|
|
cvar_t *gl_finish;
|
|
cvar_t *gl_clear;
|
|
cvar_t *vid_gamma;
|
|
|
|
|
|
/*
|
|
=================
|
|
R_CullBox
|
|
|
|
Returns true if the box is completely outside the frustum
|
|
=================
|
|
*/
|
|
bool R_CullBox( const vec3_t mins, const vec3_t maxs, int clipFlags )
|
|
{
|
|
cplane_t *plane;
|
|
int i;
|
|
|
|
if( r_nocull->integer )
|
|
return false;
|
|
|
|
for( i = 0, plane = r_frustum; i < 4; i++, plane++ )
|
|
{
|
|
if(!(clipFlags & (1<<i))) continue;
|
|
if( BoxOnPlaneSide( mins, maxs, plane ) == SIDE_ON )
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_CullSphere
|
|
|
|
Returns true if the sphere is completely outside the frustum
|
|
=================
|
|
*/
|
|
bool R_CullSphere( const vec3_t origin, float radius, int clipFlags )
|
|
{
|
|
cplane_t *plane;
|
|
int i;
|
|
|
|
if( r_nocull->integer )
|
|
return false;
|
|
|
|
for( i = 0, plane = r_frustum; i < 4; i++, plane++ )
|
|
{
|
|
if(!(clipFlags & (1<<i)))
|
|
continue;
|
|
if( DotProduct( origin, plane->normal ) - plane->dist <= -radius )
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_RotateForEntity
|
|
=================
|
|
*/
|
|
void R_RotateForEntity( ref_entity_t *e )
|
|
{
|
|
matrix4x4 rotMatrix;
|
|
#if 0
|
|
// classic slow version (used for debug)
|
|
Matrix4x4_LoadIdentity( rotMatrix );
|
|
Matrix4x4_ConcatTranslate( rotMatrix, e->origin[0], e->origin[1], e->origin[2] );
|
|
Matrix4x4_ConcatRotate( rotMatrix, e->angles[1], 0, 0, 1 );
|
|
Matrix4x4_ConcatRotate( rotMatrix, -e->angles[0], 0, 1, 0 );
|
|
Matrix4x4_ConcatRotate( rotMatrix, -e->angles[2], 1, 0, 0 );
|
|
Matrix4x4_Concat( r_entityMatrix, r_worldMatrix, rotMatrix );
|
|
#else
|
|
Matrix4x4_FromVectors( rotMatrix, e->axis[0], e->axis[1], e->axis[2], vec3_origin );
|
|
Matrix4x4_SetOrigin( rotMatrix, e->origin[0], e->origin[1], e->origin[2] );
|
|
Matrix4x4_Concat( r_entityMatrix, r_worldMatrix, rotMatrix );
|
|
#endif
|
|
GL_LoadMatrix( r_entityMatrix );
|
|
}
|
|
|
|
|
|
// =====================================================================
|
|
|
|
/*
|
|
=================
|
|
R_DrawBeam
|
|
=================
|
|
*/
|
|
void R_DrawBeam( void )
|
|
{
|
|
vec3_t axis[3];
|
|
float length;
|
|
int i;
|
|
|
|
// find orientation vectors
|
|
VectorSubtract( r_refdef.vieworg, m_pCurrentEntity->origin, axis[0] );
|
|
VectorSubtract( m_pCurrentEntity->prev.origin, m_pCurrentEntity->origin, axis[1] );// FIXME
|
|
|
|
CrossProduct( axis[0], axis[1], axis[2] );
|
|
VectorNormalizeFast( axis[2] );
|
|
|
|
// find normal
|
|
CrossProduct( axis[1], axis[2], axis[0] );
|
|
VectorNormalizeFast( axis[0] );
|
|
|
|
// scale by radius
|
|
VectorScale( axis[2], m_pCurrentEntity->frame / 2, axis[2] );
|
|
|
|
// find segment length
|
|
length = VectorLength( axis[1] ) / m_pCurrentEntity->prev.frame;
|
|
|
|
// draw it
|
|
RB_CheckMeshOverflow( 6, 4 );
|
|
|
|
for( i = 2; i < 4; i++ )
|
|
{
|
|
ref.indexArray[ref.numIndex++] = ref.numVertex + 0;
|
|
ref.indexArray[ref.numIndex++] = ref.numVertex + i-1;
|
|
ref.indexArray[ref.numIndex++] = ref.numVertex + i;
|
|
}
|
|
|
|
ref.vertsArray[ref.numVertex+0].point[0] = m_pCurrentEntity->origin[0] + axis[2][0];
|
|
ref.vertsArray[ref.numVertex+0].point[1] = m_pCurrentEntity->origin[1] + axis[2][1];
|
|
ref.vertsArray[ref.numVertex+0].point[2] = m_pCurrentEntity->origin[2] + axis[2][2];
|
|
ref.vertsArray[ref.numVertex+1].point[0] = m_pCurrentEntity->prev.origin[0] + axis[2][0];
|
|
ref.vertsArray[ref.numVertex+1].point[1] = m_pCurrentEntity->prev.origin[1] + axis[2][1];
|
|
ref.vertsArray[ref.numVertex+1].point[2] = m_pCurrentEntity->prev.origin[2] + axis[2][2];
|
|
ref.vertsArray[ref.numVertex+2].point[0] = m_pCurrentEntity->prev.origin[0] - axis[2][0];
|
|
ref.vertsArray[ref.numVertex+2].point[1] = m_pCurrentEntity->prev.origin[1] - axis[2][1];
|
|
ref.vertsArray[ref.numVertex+2].point[2] = m_pCurrentEntity->prev.origin[2] - axis[2][2];
|
|
ref.vertsArray[ref.numVertex+3].point[0] = m_pCurrentEntity->origin[0] - axis[2][0];
|
|
ref.vertsArray[ref.numVertex+3].point[1] = m_pCurrentEntity->origin[1] - axis[2][1];
|
|
ref.vertsArray[ref.numVertex+3].point[2] = m_pCurrentEntity->origin[2] - axis[2][2];
|
|
|
|
ref.vertsArray[ref.numVertex+0].stcoord[0] = 0;
|
|
ref.vertsArray[ref.numVertex+0].stcoord[1] = 0;
|
|
ref.vertsArray[ref.numVertex+1].stcoord[0] = length;
|
|
ref.vertsArray[ref.numVertex+1].stcoord[1] = 0;
|
|
ref.vertsArray[ref.numVertex+2].stcoord[0] = length;
|
|
ref.vertsArray[ref.numVertex+2].stcoord[1] = 1;
|
|
ref.vertsArray[ref.numVertex+3].stcoord[0] = 0;
|
|
ref.vertsArray[ref.numVertex+3].stcoord[1] = 1;
|
|
|
|
for( i = 0; i < 4; i++ )
|
|
{
|
|
ref.vertsArray[ref.numVertex].normal[0] = axis[0][0];
|
|
ref.vertsArray[ref.numVertex].normal[1] = axis[0][1];
|
|
ref.vertsArray[ref.numVertex].normal[2] = axis[0][2];
|
|
ref.vertsArray[ref.numVertex].color[0] = m_pCurrentEntity->rendercolor[0];
|
|
ref.vertsArray[ref.numVertex].color[1] = m_pCurrentEntity->rendercolor[1];
|
|
ref.vertsArray[ref.numVertex].color[2] = m_pCurrentEntity->rendercolor[2];
|
|
ref.vertsArray[ref.numVertex].color[3] = m_pCurrentEntity->renderamt;
|
|
ref.numVertex++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_AddBeamToList
|
|
=================
|
|
*/
|
|
static void R_AddBeamToList( ref_entity_t *entity )
|
|
{
|
|
R_AddMeshToList( MESH_BEAM, NULL, entity->shader, entity, 0 );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_AddEntitiesToList
|
|
=================
|
|
*/
|
|
static void R_AddEntitiesToList( void )
|
|
{
|
|
ref_entity_t *entity;
|
|
rmodel_t *model;
|
|
int i;
|
|
|
|
if( !r_drawentities->integer || r_numEntities == 1 )
|
|
return;
|
|
|
|
r_stats.numEntities += (r_numEntities - 1);
|
|
|
|
for( i = 1, entity = &r_entities[1]; i < r_numEntities; i++, entity++ )
|
|
{
|
|
switch( entity->ent_type )
|
|
{
|
|
case ED_NORMAL:
|
|
case ED_CLIENT:
|
|
case ED_BSPBRUSH:
|
|
case ED_VIEWMODEL:
|
|
model = m_pRenderModel = entity->model;
|
|
if( !model || model->type == mod_bad )
|
|
{
|
|
r_nullModels[r_numNullModels++] = entity;
|
|
break;
|
|
}
|
|
|
|
switch( model->type )
|
|
{
|
|
case mod_world:
|
|
case mod_brush:
|
|
R_AddBrushModelToList( entity );
|
|
break;
|
|
case mod_studio:
|
|
R_AddStudioModelToList( entity );
|
|
break;
|
|
case mod_sprite:
|
|
R_AddSpriteModelToList( entity );
|
|
break;
|
|
default:
|
|
Host_Error( "R_AddEntitiesToList: bad model type (%i)\n", model->type );
|
|
}
|
|
break;
|
|
case ED_BEAM:
|
|
R_AddBeamToList( entity );
|
|
break;
|
|
default:
|
|
Host_Error( "R_AddEntitiesToList: bad entity type (%i)\n", entity->ent_type );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_DrawNullModels
|
|
=================
|
|
*/
|
|
static void R_DrawNullModels( void )
|
|
{
|
|
ref_entity_t *entity;
|
|
vec3_t points[3];
|
|
int i;
|
|
|
|
if (!r_numNullModels)
|
|
return;
|
|
|
|
GL_LoadMatrix( r_worldMatrix );
|
|
|
|
// Set the state
|
|
GL_Enable(GL_CULL_FACE);
|
|
GL_Disable(GL_POLYGON_OFFSET_FILL);
|
|
GL_Disable(GL_VERTEX_PROGRAM_ARB);
|
|
GL_Disable(GL_FRAGMENT_PROGRAM_ARB);
|
|
GL_Disable(GL_ALPHA_TEST);
|
|
GL_Enable(GL_BLEND);
|
|
GL_Enable(GL_DEPTH_TEST);
|
|
|
|
GL_CullFace(GL_FRONT);
|
|
GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
GL_DepthFunc(GL_LEQUAL);
|
|
GL_DepthMask(GL_FALSE);
|
|
|
|
// Draw them
|
|
for( i = 0; i < r_numNullModels; i++ )
|
|
{
|
|
entity = r_nullModels[i];
|
|
|
|
VectorMA( entity->origin, 15, entity->axis[0], points[0] );
|
|
VectorMA( entity->origin, -15, entity->axis[1], points[1] );
|
|
VectorMA( entity->origin, 15, entity->axis[2], points[2] );
|
|
|
|
pglBegin( GL_LINES );
|
|
|
|
pglColor4f( 1.0f, 0.0f, 0.0f, 0.5f );
|
|
pglVertex3fv( entity->origin );
|
|
pglVertex3fv( points[0] );
|
|
|
|
pglColor4f( 0, 1.0f, 0, 0.5f );
|
|
pglVertex3fv( entity->origin );
|
|
pglVertex3fv( points[1] );
|
|
|
|
pglColor4f( 0, 0, 1.0f, 0.5f );
|
|
pglVertex3fv( entity->origin );
|
|
pglVertex3fv( points[2] );
|
|
|
|
pglEnd();
|
|
}
|
|
r_numNullModels = 0;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_DrawParticle
|
|
=================
|
|
*/
|
|
void R_DrawParticle( void )
|
|
{
|
|
particle_t *particle = m_pRenderMesh->mesh;
|
|
vec3_t axis[3];
|
|
int i;
|
|
|
|
// Draw it
|
|
RB_CheckMeshOverflow( 6, 4 );
|
|
|
|
for( i = 2; i < 4; i++ )
|
|
{
|
|
ref.indexArray[ref.numIndex++] = ref.numVertex + 0;
|
|
ref.indexArray[ref.numIndex++] = ref.numVertex + i-1;
|
|
ref.indexArray[ref.numIndex++] = ref.numVertex + i;
|
|
}
|
|
|
|
if( particle->length != 1 )
|
|
{
|
|
// find orientation vectors
|
|
VectorSubtract( r_refdef.vieworg, particle->origin, axis[0] );
|
|
VectorSubtract( particle->old_origin, particle->origin, axis[1] );
|
|
CrossProduct( axis[0], axis[1], axis[2] );
|
|
|
|
VectorNormalizeFast( axis[1] );
|
|
VectorNormalizeFast( axis[2] );
|
|
|
|
// find normal
|
|
CrossProduct( axis[1], axis[2], axis[0] );
|
|
VectorNormalizeFast( axis[0] );
|
|
|
|
VectorMA( particle->origin, -particle->length, axis[1], particle->old_origin );
|
|
VectorScale( axis[2], particle->radius, axis[2] );
|
|
|
|
ref.vertsArray[ref.numVertex+0].point[0] = particle->old_origin[0] + axis[2][0];
|
|
ref.vertsArray[ref.numVertex+0].point[1] = particle->old_origin[1] + axis[2][1];
|
|
ref.vertsArray[ref.numVertex+0].point[2] = particle->old_origin[2] + axis[2][2];
|
|
ref.vertsArray[ref.numVertex+1].point[0] = particle->origin[0] + axis[2][0];
|
|
ref.vertsArray[ref.numVertex+1].point[1] = particle->origin[1] + axis[2][1];
|
|
ref.vertsArray[ref.numVertex+1].point[2] = particle->origin[2] + axis[2][2];
|
|
ref.vertsArray[ref.numVertex+2].point[0] = particle->origin[0] - axis[2][0];
|
|
ref.vertsArray[ref.numVertex+2].point[1] = particle->origin[1] - axis[2][1];
|
|
ref.vertsArray[ref.numVertex+2].point[2] = particle->origin[2] - axis[2][2];
|
|
ref.vertsArray[ref.numVertex+3].point[0] = particle->old_origin[0] - axis[2][0];
|
|
ref.vertsArray[ref.numVertex+3].point[1] = particle->old_origin[1] - axis[2][1];
|
|
ref.vertsArray[ref.numVertex+3].point[2] = particle->old_origin[2] - axis[2][2];
|
|
}
|
|
else
|
|
{
|
|
if( particle->rotation )
|
|
{
|
|
// Rotate it around its normal
|
|
RotatePointAroundVector( axis[1], r_forward, r_right, particle->rotation );
|
|
CrossProduct( r_forward, axis[1], axis[2] );
|
|
|
|
// The normal should point at the viewer
|
|
VectorNegate( r_forward, axis[0] );
|
|
|
|
// Scale the axes by radius
|
|
VectorScale( axis[1], particle->radius, axis[1] );
|
|
VectorScale( axis[2], particle->radius, axis[2] );
|
|
}
|
|
else
|
|
{
|
|
// the normal should point at the viewer
|
|
VectorNegate( r_forward, axis[0] );
|
|
|
|
// scale the axes by radius
|
|
VectorScale( r_right, particle->radius, axis[1] );
|
|
VectorScale( r_up, particle->radius, axis[2] );
|
|
}
|
|
|
|
ref.vertsArray[ref.numVertex+0].point[0] = particle->origin[0] + axis[1][0] + axis[2][0];
|
|
ref.vertsArray[ref.numVertex+0].point[1] = particle->origin[1] + axis[1][1] + axis[2][1];
|
|
ref.vertsArray[ref.numVertex+0].point[2] = particle->origin[2] + axis[1][2] + axis[2][2];
|
|
ref.vertsArray[ref.numVertex+1].point[0] = particle->origin[0] - axis[1][0] + axis[2][0];
|
|
ref.vertsArray[ref.numVertex+1].point[1] = particle->origin[1] - axis[1][1] + axis[2][1];
|
|
ref.vertsArray[ref.numVertex+1].point[2] = particle->origin[2] - axis[1][2] + axis[2][2];
|
|
ref.vertsArray[ref.numVertex+2].point[0] = particle->origin[0] - axis[1][0] - axis[2][0];
|
|
ref.vertsArray[ref.numVertex+2].point[1] = particle->origin[1] - axis[1][1] - axis[2][1];
|
|
ref.vertsArray[ref.numVertex+2].point[2] = particle->origin[2] - axis[1][2] - axis[2][2];
|
|
ref.vertsArray[ref.numVertex+3].point[0] = particle->origin[0] + axis[1][0] - axis[2][0];
|
|
ref.vertsArray[ref.numVertex+3].point[1] = particle->origin[1] + axis[1][1] - axis[2][1];
|
|
ref.vertsArray[ref.numVertex+3].point[2] = particle->origin[2] + axis[1][2] - axis[2][2];
|
|
}
|
|
|
|
ref.vertsArray[ref.numVertex+0].stcoord[0] = 0;
|
|
ref.vertsArray[ref.numVertex+0].stcoord[1] = 0;
|
|
ref.vertsArray[ref.numVertex+1].stcoord[0] = 1;
|
|
ref.vertsArray[ref.numVertex+1].stcoord[1] = 0;
|
|
ref.vertsArray[ref.numVertex+2].stcoord[0] = 1;
|
|
ref.vertsArray[ref.numVertex+2].stcoord[1] = 1;
|
|
ref.vertsArray[ref.numVertex+3].stcoord[0] = 0;
|
|
ref.vertsArray[ref.numVertex+3].stcoord[1] = 1;
|
|
|
|
for( i = 0; i < 4; i++ )
|
|
{
|
|
ref.vertsArray[ref.numVertex].normal[0] = axis[0][0];
|
|
ref.vertsArray[ref.numVertex].normal[1] = axis[0][1];
|
|
ref.vertsArray[ref.numVertex].normal[2] = axis[0][2];
|
|
Vector4Copy( particle->modulate, ref.vertsArray[ref.numVertex].color );
|
|
ref.numVertex++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_AddParticlesToList
|
|
=================
|
|
*/
|
|
static void R_AddParticlesToList( void )
|
|
{
|
|
particle_t *particle;
|
|
vec3_t vec;
|
|
int i;
|
|
|
|
if( !r_drawparticles->integer || !r_numParticles )
|
|
return;
|
|
|
|
r_stats.numParticles += r_numParticles;
|
|
|
|
for (i = 0, particle = r_particles; i < r_numParticles; i++, particle++ )
|
|
{
|
|
// cull
|
|
if( !r_nocull->integer )
|
|
{
|
|
VectorSubtract( particle->origin, r_refdef.vieworg, vec );
|
|
VectorNormalizeFast( vec );
|
|
|
|
if( DotProduct( vec, r_forward ) < 0 )
|
|
continue;
|
|
}
|
|
|
|
// add it
|
|
R_AddMeshToList( MESH_PARTICLE, particle, particle->shader, r_worldEntity, 0 );
|
|
}
|
|
}
|
|
|
|
// =====================================================================
|
|
|
|
|
|
/*
|
|
=================
|
|
R_QSortMeshes
|
|
=================
|
|
*/
|
|
static void R_QSortMeshes( mesh_t *meshes, int numMeshes )
|
|
{
|
|
static mesh_t tmp;
|
|
static int stack[4096];
|
|
int depth = 0;
|
|
int L, R, l, r, median;
|
|
uint pivot;
|
|
|
|
if( !numMeshes ) return;
|
|
|
|
L = 0;
|
|
R = numMeshes - 1;
|
|
|
|
start:
|
|
l = L;
|
|
r = R;
|
|
|
|
median = (L + R) >> 1;
|
|
|
|
if( meshes[L].sortKey > meshes[median].sortKey )
|
|
{
|
|
if( meshes[L].sortKey < meshes[R].sortKey )
|
|
median = L;
|
|
}
|
|
else if( meshes[R].sortKey < meshes[median].sortKey )
|
|
median = R;
|
|
|
|
pivot = meshes[median].sortKey;
|
|
|
|
while( l < r )
|
|
{
|
|
while( meshes[l].sortKey < pivot )
|
|
l++;
|
|
while( meshes[r].sortKey > pivot )
|
|
r--;
|
|
|
|
if( l <= r )
|
|
{
|
|
tmp = meshes[r];
|
|
meshes[r] = meshes[l];
|
|
meshes[l] = tmp;
|
|
|
|
l++;
|
|
r--;
|
|
}
|
|
}
|
|
|
|
if((L < r) && (depth < 4096))
|
|
{
|
|
stack[depth++] = l;
|
|
stack[depth++] = R;
|
|
R = r;
|
|
goto start;
|
|
}
|
|
|
|
if( l < R )
|
|
{
|
|
L = l;
|
|
goto start;
|
|
}
|
|
|
|
if( depth )
|
|
{
|
|
R = stack[--depth];
|
|
L = stack[--depth];
|
|
goto start;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_ISortMeshes
|
|
=================
|
|
*/
|
|
static void R_ISortMeshes( mesh_t *meshes, int numMeshes )
|
|
{
|
|
static mesh_t tmp;
|
|
int i, j;
|
|
|
|
if( !numMeshes ) return;
|
|
|
|
for( i = 1; i < numMeshes; i++ )
|
|
{
|
|
tmp = meshes[i];
|
|
j = i - 1;
|
|
|
|
while((j >= 0) && (meshes[j].sortKey > tmp.sortKey))
|
|
{
|
|
meshes[j+1] = meshes[j];
|
|
j--;
|
|
}
|
|
|
|
if( i != j+1 ) meshes[j+1] = tmp;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_AddMeshToList
|
|
|
|
Calculates sort key and stores info used for sorting and batching.
|
|
All 3D geometry passes this function.
|
|
=================
|
|
*/
|
|
void R_AddMeshToList( meshType_t meshType, void *mesh, ref_shader_t *shader, ref_entity_t *entity, int infoKey )
|
|
{
|
|
mesh_t *m;
|
|
|
|
if( shader->sort <= SORT_DECAL )
|
|
{
|
|
if( r_numSolidMeshes == MAX_MESHES )
|
|
Host_Error( "R_AddMeshToList: MAX_MESHES hit\n" );
|
|
m = &r_solidMeshes[r_numSolidMeshes++];
|
|
}
|
|
else
|
|
{
|
|
if( r_numTransMeshes == MAX_MESHES )
|
|
Host_Error( "R_AddMeshToList: MAX_MESHES hit\n" );
|
|
m = &r_transMeshes[r_numTransMeshes++];
|
|
}
|
|
|
|
m->sortKey = (shader->sort<<28) | (shader->index<<18) | ((entity - r_entities)<<8) | (infoKey);
|
|
m->meshType = meshType;
|
|
m->mesh = mesh;
|
|
}
|
|
|
|
|
|
// =====================================================================
|
|
|
|
|
|
/*
|
|
=================
|
|
R_SetFrustum
|
|
=================
|
|
*/
|
|
static void R_SetFrustum( void )
|
|
{
|
|
int i;
|
|
|
|
// build the transformation matrix for the given view angles
|
|
VectorCopy( r_refdef.vieworg, r_origin );
|
|
AnglesToAxis( r_refdef.viewangles );
|
|
|
|
RotatePointAroundVector( r_frustum[0].normal, r_up, r_forward, -(90 - r_refdef.fov_x / 2));
|
|
RotatePointAroundVector( r_frustum[1].normal, r_up, r_forward, 90 - r_refdef.fov_x / 2);
|
|
RotatePointAroundVector( r_frustum[2].normal, r_right, r_forward, 90 - r_refdef.fov_y / 2);
|
|
RotatePointAroundVector( r_frustum[3].normal, r_right, r_forward, -(90 - r_refdef.fov_y / 2));
|
|
|
|
for( i = 0; i < 4; i++ )
|
|
{
|
|
r_frustum[i].dist = DotProduct( r_refdef.vieworg, r_frustum[i].normal );
|
|
PlaneClassify( &r_frustum[i] );
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_SetFarClip
|
|
=================
|
|
*/
|
|
static float R_SetFarClip( void )
|
|
{
|
|
float farDist, dirDist, worldDist = 0;
|
|
int i;
|
|
|
|
if( r_refdef.rdflags & RDF_NOWORLDMODEL)
|
|
return 4096.0;
|
|
|
|
dirDist = DotProduct( r_refdef.vieworg, r_forward );
|
|
farDist = dirDist + 256.0;
|
|
|
|
for( i = 0; i < 3; i++ )
|
|
{
|
|
if( r_forward[i] < 0 )
|
|
worldDist += (r_worldMins[i] * r_forward[i]);
|
|
else worldDist += (r_worldMaxs[i] * r_forward[i]);
|
|
}
|
|
|
|
if( farDist < worldDist ) farDist = worldDist;
|
|
return farDist - dirDist + 256.0;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_SetMatrices
|
|
=================
|
|
*/
|
|
static void R_SetMatrices( void )
|
|
{
|
|
float xMax, xMin, yMax, yMin;
|
|
float xDiv, yDiv, zDiv;
|
|
float zNear, zFar;
|
|
|
|
zNear = 4.0;
|
|
zFar = R_SetFarClip();
|
|
|
|
xMax = zNear * tan(r_refdef.fov_x * M_PI / 360.0);
|
|
xMin = -xMax;
|
|
|
|
yMax = zNear * tan(r_refdef.fov_y * M_PI / 360.0);
|
|
yMin = -yMax;
|
|
|
|
xDiv = 1.0 / (xMax - xMin);
|
|
yDiv = 1.0 / (yMax - yMin);
|
|
zDiv = 1.0 / (zFar - zNear);
|
|
|
|
gl_projectionMatrix[ 0] = (2.0 * zNear) * xDiv;
|
|
gl_projectionMatrix[ 1] = 0.0;
|
|
gl_projectionMatrix[ 2] = 0.0;
|
|
gl_projectionMatrix[ 3] = 0.0;
|
|
gl_projectionMatrix[ 4] = 0.0;
|
|
gl_projectionMatrix[ 5] = (2.0 * zNear) * yDiv;
|
|
gl_projectionMatrix[ 6] = 0.0;
|
|
gl_projectionMatrix[ 7] = 0.0;
|
|
gl_projectionMatrix[ 8] = (xMax + xMin) * xDiv;
|
|
gl_projectionMatrix[ 9] = (yMax + yMin) * yDiv;
|
|
gl_projectionMatrix[10] = -(zNear + zFar) * zDiv;
|
|
gl_projectionMatrix[11] = -1.0;
|
|
gl_projectionMatrix[12] = 0.0;
|
|
gl_projectionMatrix[13] = 0.0;
|
|
gl_projectionMatrix[14] = -(2.0 * zNear * zFar) * zDiv;
|
|
gl_projectionMatrix[15] = 0.0;
|
|
|
|
#if 0
|
|
// classic slow version (used for debug)
|
|
Matrix4x4_LoadIdentity( r_worldMatrix );
|
|
Matrix4x4_ConcatRotate( r_worldMatrix, -90, 1, 0, 0 ); // put Z going up
|
|
Matrix4x4_ConcatRotate( r_worldMatrix, 90, 0, 0, 1 ); // put Z going up
|
|
Matrix4x4_ConcatRotate( r_worldMatrix, -r_refdef.viewangles[2], 1, 0, 0 );
|
|
Matrix4x4_ConcatRotate( r_worldMatrix, -r_refdef.viewangles[0], 0, 1, 0 );
|
|
Matrix4x4_ConcatRotate( r_worldMatrix, -r_refdef.viewangles[1], 0, 0, 1 );
|
|
Matrix4x4_ConcatTranslate( r_worldMatrix, -r_refdef.vieworg[0], -r_refdef.vieworg[1], -r_refdef.vieworg[2] );
|
|
#else
|
|
Matrix4x4_CreateModelview_FromAxis( r_worldMatrix, r_forward, r_right, r_up, r_origin );
|
|
#endif
|
|
gl_textureMatrix[ 0] = r_right[0];
|
|
gl_textureMatrix[ 1] = -r_right[1];
|
|
gl_textureMatrix[ 2] = -r_right[2];
|
|
gl_textureMatrix[ 3] = 0.0;
|
|
gl_textureMatrix[ 4] = -r_up[0];
|
|
gl_textureMatrix[ 5] = r_up[1];
|
|
gl_textureMatrix[ 6] = r_up[2];
|
|
gl_textureMatrix[ 7] = 0.0;
|
|
gl_textureMatrix[ 8] = r_forward[0];
|
|
gl_textureMatrix[ 9] = -r_forward[1];
|
|
gl_textureMatrix[10] = -r_forward[2];
|
|
gl_textureMatrix[11] = 0.0;
|
|
gl_textureMatrix[12] = 0.0;
|
|
gl_textureMatrix[13] = 0.0;
|
|
gl_textureMatrix[14] = 0.0;
|
|
gl_textureMatrix[15] = 1.0;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_RenderView
|
|
=================
|
|
*/
|
|
void R_RenderView( const refdef_t *fd )
|
|
{
|
|
if( r_skipfrontend->integer )
|
|
return;
|
|
|
|
r_numSolidMeshes = 0;
|
|
r_numTransMeshes = 0;
|
|
|
|
// copy the areamask data over and note if it has changed, which
|
|
// will force a reset of the visible leafs even if the view hasn't moved
|
|
r_areabitsChanged = false;
|
|
if(!(r_refdef.rdflags & RDF_NOWORLDMODEL ))
|
|
{
|
|
int i, areaDiff = 0;
|
|
|
|
// compare the area bits
|
|
for( i = 0; i < MAX_MAP_AREA_BYTES / 4; i++ )
|
|
{
|
|
areaDiff |= ((int *)r_refdef.areabits)[i] ^ ((int *)fd->areabits)[i];
|
|
((int *)r_refdef.areabits)[i] = ((int *)fd->areabits)[i];
|
|
}
|
|
|
|
// a door just opened or something
|
|
if( areaDiff ) r_areabitsChanged = true;
|
|
}
|
|
|
|
r_refdef = *fd;
|
|
// set up frustum
|
|
R_SetFrustum();
|
|
|
|
// build mesh lists
|
|
R_AddWorldToList();
|
|
R_AddEntitiesToList();
|
|
R_AddParticlesToList();
|
|
|
|
// sort mesh lists
|
|
R_QSortMeshes( r_solidMeshes, r_numSolidMeshes );
|
|
R_ISortMeshes( r_transMeshes, r_numTransMeshes );
|
|
|
|
// set up matrices
|
|
R_SetMatrices();
|
|
|
|
// go into 3D mode
|
|
GL_Setup3D();
|
|
|
|
// render everything
|
|
RB_RenderMeshes( r_solidMeshes, r_numSolidMeshes );
|
|
|
|
// R_RenderShadows();
|
|
|
|
RB_RenderMeshes( r_transMeshes, r_numTransMeshes );
|
|
|
|
// finish up
|
|
R_DrawNullModels();
|
|
|
|
RB_DebugGraphics();
|
|
R_BloomBlend( fd );
|
|
}
|
|
|
|
void R_DrawPauseScreen( void )
|
|
{
|
|
// don't apply post effects for custom window
|
|
if(r_refdef.rdflags & RDF_NOWORLDMODEL)
|
|
return;
|
|
|
|
if( !r_pause_bw->integer )
|
|
return;
|
|
|
|
if( r_pause->modified )
|
|
{
|
|
// reset saturation value
|
|
if( !r_pause->value )
|
|
r_pause_alpha = 0.0f;
|
|
r_pause->modified = false;
|
|
}
|
|
if( !r_pause->value ) return;
|
|
if( r_pause_alpha < 1.0f ) r_pause_alpha += 0.03f;
|
|
|
|
if( r_pause_alpha <= 1.0f || r_lefthand->modified )
|
|
{
|
|
int k = r_pause_alpha * 255.0f;
|
|
int i, s, r, g, b;
|
|
|
|
pglFlush();
|
|
pglReadPixels(0, 0, r_width->integer, r_height->integer, GL_RGB, GL_UNSIGNED_BYTE, r_framebuffer);
|
|
for (i = 0; i < r_width->integer * r_height->integer * 3; i+=3)
|
|
{
|
|
r = r_framebuffer[i+0];
|
|
g = r_framebuffer[i+1];
|
|
b = r_framebuffer[i+2];
|
|
s = (r + 2 * g + b) * k>>2; // simply bw recomputing
|
|
r_framebuffer[i+0] = (r*(255-k)+s)>>8;
|
|
r_framebuffer[i+1] = (g*(255-k)+s)>>8;
|
|
r_framebuffer[i+2] = (b*(255-k)+s)>>8;
|
|
}
|
|
r_lefthand->modified = false;
|
|
}
|
|
// set custom orthogonal mode
|
|
pglMatrixMode(GL_PROJECTION);
|
|
pglLoadIdentity ();
|
|
pglOrtho(0, r_width->integer, 0, r_height->integer, 0, 1.0f);
|
|
pglMatrixMode(GL_MODELVIEW);
|
|
pglLoadIdentity ();
|
|
|
|
pglDisable(GL_TEXTURE_2D);
|
|
pglRasterPos2f(0, 0);
|
|
pglDrawPixels(r_width->integer, r_height->integer, GL_RGB, GL_UNSIGNED_BYTE, r_framebuffer);
|
|
pglFlush();
|
|
pglEnable(GL_TEXTURE_2D);
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_ClearScene
|
|
=================
|
|
*/
|
|
void R_ClearScene( void )
|
|
{
|
|
r_numEntities = 1;
|
|
r_numDLights = 0;
|
|
r_numParticles = 0;
|
|
r_numPolys = 0;
|
|
r_numPolyVerts = 0;
|
|
}
|
|
|
|
/*
|
|
====================
|
|
R_SetLightLevel
|
|
|
|
HACKHACK
|
|
====================
|
|
*/
|
|
void R_SetLightLevel( void )
|
|
{
|
|
vec3_t shadelight;
|
|
|
|
if( r_refdef.rdflags & RDF_NOWORLDMODEL )
|
|
return;
|
|
|
|
// save off light value for server to look at (BIG HACK!)
|
|
|
|
R_LightForPoint( r_refdef.vieworg, shadelight );
|
|
|
|
// pick the greatest component, which should be the same
|
|
// as the mono value returned by software
|
|
if( shadelight[0] > shadelight[1] )
|
|
{
|
|
if( shadelight[0] > shadelight[2] )
|
|
r_lightlevel->value = 150 * shadelight[0];
|
|
else r_lightlevel->value = 150 * shadelight[2];
|
|
}
|
|
else
|
|
{
|
|
if( shadelight[1] > shadelight[2] )
|
|
r_lightlevel->value = 150 * shadelight[1];
|
|
else r_lightlevel->value = 150 * shadelight[2];
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_AddEntityToScene
|
|
=================
|
|
*/
|
|
static bool R_AddEntityToScene( entity_state_t *s1, entity_state_t *s2, float lerpfrac )
|
|
{
|
|
ref_entity_t *refent;
|
|
int i;
|
|
|
|
if( !s1 || !s1->model.index ) return false; // if set to invisible, skip
|
|
if( r_numEntities >= MAX_ENTITIES ) return false;
|
|
|
|
refent = &r_entities[r_numEntities];
|
|
if( !s2 ) s2 = s1; // no lerping state
|
|
|
|
// copy state to render
|
|
refent->frame = s1->model.frame;
|
|
refent->index = s1->number;
|
|
refent->ent_type = s1->ed_type;
|
|
refent->backlerp = 1.0f - lerpfrac;
|
|
refent->renderamt = s1->renderamt;
|
|
refent->body = s1->model.body;
|
|
refent->sequence = s1->model.sequence;
|
|
refent->movetype = s1->movetype;
|
|
refent->scale = s1->model.scale ? s1->model.scale : 1.0f;
|
|
refent->colormap = s1->model.colormap;
|
|
refent->framerate = s1->model.framerate;
|
|
refent->effects = s1->effects;
|
|
refent->animtime = s1->model.animtime;
|
|
VectorCopy( s1->rendercolor, refent->rendercolor );
|
|
|
|
// setup latchedvars
|
|
refent->prev.frame = s2->model.frame;
|
|
refent->prev.animtime = s2->model.animtime;
|
|
VectorCopy( s2->origin, refent->prev.origin );
|
|
VectorCopy( s2->angles, refent->prev.angles );
|
|
refent->prev.sequence = s2->model.sequence;
|
|
|
|
// interpolate origin
|
|
for( i = 0; i < 3; i++ )
|
|
refent->origin[i] = LerpPoint( s2->origin[i], s1->origin[i], lerpfrac );
|
|
|
|
// set skin
|
|
refent->skin = s1->model.skin;
|
|
refent->model = cl_models[s1->model.index];
|
|
refent->weaponmodel = cl_models[s1->pmodel.index];
|
|
refent->renderfx = s1->renderfx;
|
|
refent->prev.sequencetime = s1->model.animtime - s2->model.animtime;
|
|
|
|
// calculate angles
|
|
if( refent->effects & EF_ROTATE )
|
|
{
|
|
// some bonus items auto-rotate
|
|
VectorSet( refent->angles, 0, anglemod( r_refdef.time / 10), 0 );
|
|
}
|
|
else
|
|
{
|
|
// interpolate angles
|
|
for( i = 0; i < 3; i++ )
|
|
refent->angles[i] = LerpAngle( s2->angles[i], s1->angles[i], lerpfrac );
|
|
}
|
|
|
|
AnglesToAxisPrivate( refent->angles, refent->axis );
|
|
|
|
// copy controllers
|
|
for( i = 0; i < MAXSTUDIOCONTROLLERS; i++ )
|
|
{
|
|
refent->controller[i] = s1->model.controller[i];
|
|
refent->prev.controller[i] = s2->model.controller[i];
|
|
}
|
|
|
|
// copy blends
|
|
for( i = 0; i < MAXSTUDIOBLENDS; i++ )
|
|
{
|
|
refent->blending[i] = s1->model.blending[i];
|
|
refent->prev.blending[i] = s2->model.blending[i];
|
|
}
|
|
|
|
if( refent->ent_type == ED_CLIENT )
|
|
{
|
|
// only draw from mirrors
|
|
refent->renderfx |= RF_PLAYERMODEL;
|
|
refent->gaitsequence = s1->model.gaitsequence;
|
|
}
|
|
|
|
// because entity without models never added to scene
|
|
if( !refent->ent_type )
|
|
{
|
|
switch( refent->model->type )
|
|
{
|
|
case mod_brush:
|
|
refent->ent_type = ED_BSPBRUSH;
|
|
break;
|
|
case mod_studio:
|
|
case mod_sprite:
|
|
refent->ent_type = ED_NORMAL;
|
|
break;
|
|
// and ignore all other unset ents
|
|
}
|
|
}
|
|
|
|
if( !refent->shader ) refent->shader = tr.defaultShader;
|
|
|
|
// add entity
|
|
r_numEntities++;
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_AddLightToScene
|
|
=================
|
|
*/
|
|
static bool R_AddDynamicLight( vec3_t org, vec3_t color, float intensity )
|
|
{
|
|
dlight_t *dl;
|
|
|
|
if( r_numDLights >= MAX_DLIGHTS )
|
|
return false;
|
|
|
|
dl = &r_dlights[r_numDLights];
|
|
VectorCopy( org, dl->origin );
|
|
VectorCopy( color, dl->color );
|
|
dl->intensity = intensity;
|
|
r_numDLights++;
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_AddParticleToScene
|
|
=================
|
|
*/
|
|
bool R_AddParticleToScene( const vec3_t origin, float alpha, int color )
|
|
{
|
|
particle_t *p;
|
|
|
|
if( r_numParticles >= MAX_PARTICLES )
|
|
return false;
|
|
|
|
p = &r_particles[r_numParticles];
|
|
|
|
p->shader = tr.defaultShader;
|
|
VectorCopy( origin, p->origin );
|
|
VectorCopy( origin, p->old_origin );
|
|
p->radius = 5;
|
|
p->length = alpha;
|
|
p->rotation = 0;
|
|
Vector4Set( p->modulate, 1.0f, 1.0f, 1.0f, 1.0f );
|
|
r_numParticles++;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool R_AddLightStyle( int style, vec3_t color )
|
|
{
|
|
lightstyle_t *ls;
|
|
|
|
if( style < 0 || style > MAX_LIGHTSTYLES )
|
|
return false; // invalid lightstyle
|
|
|
|
ls = &r_lightStyles[style];
|
|
ls->white = color[0] + color[1] + color[2];
|
|
VectorCopy( color, ls->rgb );
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_RenderFrame
|
|
=================
|
|
*/
|
|
void R_RenderFrame( refdef_t *rd )
|
|
{
|
|
if( r_norefresh->integer )
|
|
return;
|
|
|
|
r_refdef = *rd;
|
|
|
|
if(!(r_refdef.rdflags & RDF_NOWORLDMODEL ))
|
|
{
|
|
if( !r_worldModel ) Host_Error( "R_RenderScene: NULL worldmodel\n" );
|
|
}
|
|
|
|
// Make sure all 2D stuff is flushed
|
|
RB_RenderMesh();
|
|
|
|
// render view
|
|
R_RenderView( rd );
|
|
|
|
// go into 2D mode
|
|
GL_Setup2D();
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_BeginFrame
|
|
=================
|
|
*/
|
|
void R_BeginFrame( void )
|
|
{
|
|
// clear r_speeds statistics
|
|
Mem_Set(&r_stats, 0, sizeof(refstats_t));
|
|
|
|
if( vid_gamma->modified )
|
|
{
|
|
vid_gamma->modified = false;
|
|
GL_UpdateGammaRamp();
|
|
}
|
|
|
|
// update texture parameters
|
|
if( r_texturefilter->modified || r_texturefilteranisotropy->modified || r_texturelodbias->modified )
|
|
R_SetTextureParameters();
|
|
|
|
// Set draw buffer
|
|
if( r_frontbuffer->integer )
|
|
pglDrawBuffer( GL_FRONT );
|
|
else pglDrawBuffer( GL_BACK );
|
|
|
|
// clear screen if desired
|
|
if( gl_clear->integer )
|
|
{
|
|
GL_DepthMask( GL_TRUE );
|
|
|
|
pglClearColor( 1.0, 0.0, 0.5, 0.5 );
|
|
pglClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
|
|
}
|
|
|
|
// Go into 2D mode
|
|
GL_Setup2D();
|
|
|
|
// check for errors
|
|
if( r_check_errors->integer ) R_CheckForErrors();
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_EndFrame
|
|
=================
|
|
*/
|
|
void R_EndFrame( void )
|
|
{
|
|
// make sure all 2D stuff is flushed
|
|
RB_RenderMesh();
|
|
|
|
// Swap the buffers
|
|
if( !r_frontbuffer->integer )
|
|
{
|
|
if( !pwglSwapBuffers( glw_state.hDC ) )
|
|
Sys_Break("R_EndFrame() - SwapBuffers() failed!\n" );
|
|
}
|
|
|
|
// print r_speeds statistics
|
|
if( r_speeds->integer )
|
|
{
|
|
switch( r_speeds->integer )
|
|
{
|
|
case 1:
|
|
Msg("%i/%i shaders/stages %i meshes %i leafs %i verts %i/%i tris\n", r_stats.numShaders, r_stats.numStages, r_stats.numMeshes, r_stats.numLeafs, r_stats.numVertices, (r_stats.numIndices / 3), (r_stats.totalIndices / 3));
|
|
break;
|
|
case 2:
|
|
Msg("%i entities %i dlights %i particles %i polys\n", r_stats.numEntities, r_stats.numDLights, r_stats.numParticles, r_stats.numPolys);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// check for errors
|
|
if( r_check_errors->integer ) R_CheckForErrors();
|
|
}
|
|
|
|
bool R_UploadModel( const char *name, int index )
|
|
{
|
|
rmodel_t *mod;
|
|
|
|
// this array used by AddEntityToScene
|
|
mod = R_RegisterModel( name );
|
|
cl_models[index] = mod;
|
|
|
|
return (mod != NULL);
|
|
}
|
|
|
|
bool R_UploadImage( const char *unused, int index )
|
|
{
|
|
mipTex_t *texture;
|
|
|
|
// nothing to load
|
|
if( !r_worldModel ) return false;
|
|
m_pLoadModel = r_worldModel;
|
|
texture = m_pLoadModel->shaders + index;
|
|
|
|
// this is not actually needed
|
|
if( texture->flags & (SURF_SKY||SURF_SKYROOM|SURF_NODRAW))
|
|
return true;
|
|
|
|
if( !m_pLoadModel->lightMaps )
|
|
texture->flags |= SURF_NOLIGHTMAP;
|
|
|
|
// now all pointers are valid
|
|
texture->shader = r_shaders[R_FindShader( texture->name, SHADER_TEXTURE, texture->flags )];
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_PrecachePic
|
|
|
|
prefetching 2d graphics
|
|
=================
|
|
*/
|
|
bool R_PrecachePic( const char *name )
|
|
{
|
|
if( R_FindTexture(va( "gfx/%s", name ), NULL, 0, TF_STATIC|TF_NOPICMIP, TF_LINEAR, TW_REPEAT ))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_Init
|
|
=================
|
|
*/
|
|
bool R_Init( void )
|
|
{
|
|
GL_InitBackend();
|
|
|
|
// create the window and set up the context
|
|
if(!R_Init_OpenGL())
|
|
{
|
|
R_Free_OpenGL();
|
|
return false;
|
|
}
|
|
|
|
GL_InitExtensions();
|
|
RB_InitBackend();
|
|
|
|
R_InitTextures();
|
|
R_InitPrograms();
|
|
R_InitShaders();
|
|
R_InitModels();
|
|
R_CheckForErrors();
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_Shutdown
|
|
=================
|
|
*/
|
|
void R_Shutdown( void )
|
|
{
|
|
R_ShutdownModels();
|
|
R_ShutdownShaders();
|
|
R_ShutdownPrograms();
|
|
R_ShutdownTextures();
|
|
|
|
RB_ShutdownBackend();
|
|
GL_ShutdownBackend();
|
|
|
|
// shut down OS specific OpenGL stuff like contexts, etc.
|
|
R_Free_OpenGL();
|
|
}
|
|
|
|
/*
|
|
@@@@@@@@@@@@@@@@@@@@@
|
|
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.Init = R_Init;
|
|
re.Shutdown = R_Shutdown;
|
|
|
|
re.BeginRegistration = R_BeginRegistration;
|
|
re.RegisterModel = R_UploadModel;
|
|
re.RegisterImage = R_UploadImage;
|
|
re.PrecacheImage = R_PrecachePic;
|
|
re.SetSky = R_SetupSky;
|
|
re.EndRegistration = R_EndRegistration;
|
|
|
|
re.AddLightStyle = R_AddLightStyle;
|
|
re.AddRefEntity = R_AddEntityToScene;
|
|
re.AddDynLight = R_AddDynamicLight;
|
|
re.AddParticle = R_AddParticleToScene;
|
|
re.ClearScene = R_ClearScene;
|
|
|
|
re.BeginFrame = R_BeginFrame;
|
|
re.RenderFrame = R_RenderFrame;
|
|
re.EndFrame = R_EndFrame;
|
|
|
|
re.SetColor = GL_SetColor;
|
|
re.ScrShot = VID_ScreenShot;
|
|
re.DrawFill = R_DrawFill;
|
|
re.DrawStretchRaw = R_DrawStretchRaw;
|
|
re.DrawStretchPic = R_DrawStretchPic;
|
|
|
|
// get rid of this
|
|
re.DrawGetPicSize = R_GetPicSize;
|
|
|
|
return &re;
|
|
} |