/* * Copyright (C) 1997-2001 Id Software, Inc. * * 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. * * ======================================================================= * * Refresher setup and main part of the frame generation * * ======================================================================= */ #include "header/local.h" #define NUM_BEAM_SEGS 6 void R_Clear ( void ); void R_BeginRegistration ( char *map ); struct model_s *R_RegisterModel ( char *name ); struct image_s *R_RegisterSkin ( char *name ); void R_SetSky ( char *name, float rotate, vec3_t axis ); void R_EndRegistration ( void ); void R_RenderFrame ( refdef_t *fd ); struct image_s *Draw_FindPic ( char *name ); void Draw_Pic ( int x, int y, char *name ); void Draw_Char ( int x, int y, int c ); void Draw_TileClear ( int x, int y, int w, int h, char *name ); void Draw_Fill ( int x, int y, int w, int h, int c ); void Draw_FadeScreen ( void ); viddef_t vid; refimport_t ri; int QGL_TEXTURE0, QGL_TEXTURE1; model_t *r_worldmodel; float gldepthmin, gldepthmax; glconfig_t gl_config; glstate_t gl_state; image_t *r_notexture; /* use for bad textures */ image_t *r_particletexture; /* little dot for particles */ entity_t *currententity; model_t *currentmodel; cplane_t frustum [ 4 ]; int r_visframecount; /* bumped when going to a new PVS */ int r_framecount; /* used for dlight push checking */ int c_brush_polys, c_alias_polys; float v_blend [ 4 ]; /* final blending color */ void R_Strings ( void ); /* view origin */ vec3_t vup; vec3_t vpn; vec3_t vright; vec3_t r_origin; float r_world_matrix [ 16 ]; float r_base_world_matrix [ 16 ]; /* screen size info */ refdef_t r_newrefdef; int r_viewcluster, r_viewcluster2, r_oldviewcluster, r_oldviewcluster2; extern qboolean have_stencil; unsigned r_rawpalette [ 256 ]; cvar_t *gl_norefresh; cvar_t *gl_drawentities; cvar_t *gl_drawworld; cvar_t *gl_speeds; cvar_t *gl_fullbright; cvar_t *gl_novis; cvar_t *gl_nocull; cvar_t *gl_lerpmodels; cvar_t *gl_lefthand; cvar_t *gl_farsee; cvar_t *gl_lightlevel; cvar_t *gl_overbrightbits; cvar_t *gl_nosubimage; cvar_t *gl_allow_software; cvar_t *gl_vertex_arrays; cvar_t *gl_particle_min_size; cvar_t *gl_particle_max_size; cvar_t *gl_particle_size; cvar_t *gl_particle_att_a; cvar_t *gl_particle_att_b; cvar_t *gl_particle_att_c; cvar_t *gl_ext_swapinterval; cvar_t *gl_ext_palettedtexture; cvar_t *gl_ext_multitexture; cvar_t *gl_ext_pointparameters; cvar_t *gl_ext_compiled_vertex_array; cvar_t *gl_ext_mtexcombine; cvar_t *gl_log; cvar_t *gl_bitdepth; cvar_t *gl_drawbuffer; cvar_t *gl_driver; cvar_t *gl_lightmap; cvar_t *gl_shadows; cvar_t *gl_stencilshadow; cvar_t *gl_mode; cvar_t *gl_customwidth; cvar_t *gl_customheight; cvar_t *gl_dynamic; cvar_t *gl_modulate; cvar_t *gl_nobind; cvar_t *gl_round_down; cvar_t *gl_picmip; cvar_t *gl_skymip; cvar_t *gl_showtris; cvar_t *gl_ztrick; cvar_t *gl_finish; cvar_t *gl_clear; cvar_t *gl_cull; cvar_t *gl_polyblend; cvar_t *gl_flashblend; cvar_t *gl_playermip; cvar_t *gl_saturatelighting; cvar_t *gl_swapinterval; cvar_t *gl_texturemode; cvar_t *gl_texturealphamode; cvar_t *gl_texturesolidmode; cvar_t *gl_anisotropic; cvar_t *gl_anisotropic_avail; cvar_t *gl_lockpvs; cvar_t *vid_fullscreen; cvar_t *vid_gamma; cvar_t *vid_ref; /* * Returns true if the box is completely outside the frustom */ qboolean R_CullBox ( vec3_t mins, vec3_t maxs ) { int i; if ( gl_nocull->value ) { return ( false ); } for ( i = 0; i < 4; i++ ) { if ( BOX_ON_PLANE_SIDE( mins, maxs, &frustum [ i ] ) == 2 ) { return ( true ); } } return ( false ); } void R_RotateForEntity ( entity_t *e ) { qglTranslatef( e->origin [ 0 ], e->origin [ 1 ], e->origin [ 2 ] ); qglRotatef( e->angles [ 1 ], 0, 0, 1 ); qglRotatef( -e->angles [ 0 ], 0, 1, 0 ); qglRotatef( -e->angles [ 2 ], 1, 0, 0 ); } void R_DrawSpriteModel ( entity_t *e ) { float alpha = 1.0F; vec3_t point; dsprframe_t *frame; float *up, *right; dsprite_t *psprite; /* don't even bother culling, because it's just a single * polygon without a surface cache */ psprite = (dsprite_t *) currentmodel->extradata; e->frame %= psprite->numframes; frame = &psprite->frames [ e->frame ]; { /* normal sprite */ up = vup; right = vright; } if ( e->flags & RF_TRANSLUCENT ) { alpha = e->alpha; } if ( alpha != 1.0F ) { qglEnable( GL_BLEND ); } qglColor4f( 1, 1, 1, alpha ); R_Bind( currentmodel->skins [ e->frame ]->texnum ); R_TexEnv( GL_MODULATE ); if ( alpha == 1.0 ) { qglEnable( GL_ALPHA_TEST ); } else { qglDisable( GL_ALPHA_TEST ); } qglBegin( GL_QUADS ); qglTexCoord2f( 0, 1 ); VectorMA( e->origin, -frame->origin_y, up, point ); VectorMA( point, -frame->origin_x, right, point ); qglVertex3fv( point ); qglTexCoord2f( 0, 0 ); VectorMA( e->origin, frame->height - frame->origin_y, up, point ); VectorMA( point, -frame->origin_x, right, point ); qglVertex3fv( point ); qglTexCoord2f( 1, 0 ); VectorMA( e->origin, frame->height - frame->origin_y, up, point ); VectorMA( point, frame->width - frame->origin_x, right, point ); qglVertex3fv( point ); qglTexCoord2f( 1, 1 ); VectorMA( e->origin, -frame->origin_y, up, point ); VectorMA( point, frame->width - frame->origin_x, right, point ); qglVertex3fv( point ); qglEnd(); qglDisable( GL_ALPHA_TEST ); R_TexEnv( GL_REPLACE ); if ( alpha != 1.0F ) { qglDisable( GL_BLEND ); } qglColor4f( 1, 1, 1, 1 ); } void R_DrawNullModel ( void ) { vec3_t shadelight; int i; if ( currententity->flags & RF_FULLBRIGHT ) { shadelight [ 0 ] = shadelight [ 1 ] = shadelight [ 2 ] = 1.0F; } else { R_LightPoint( currententity->origin, shadelight ); } qglPushMatrix(); R_RotateForEntity( currententity ); qglDisable( GL_TEXTURE_2D ); qglColor3fv( shadelight ); qglBegin( GL_TRIANGLE_FAN ); qglVertex3f( 0, 0, -16 ); for ( i = 0; i <= 4; i++ ) { qglVertex3f( 16 * cos( i * M_PI / 2 ), 16 * sin( i * M_PI / 2 ), 0 ); } qglEnd(); qglBegin( GL_TRIANGLE_FAN ); qglVertex3f( 0, 0, 16 ); for ( i = 4; i >= 0; i-- ) { qglVertex3f( 16 * cos( i * M_PI / 2 ), 16 * sin( i * M_PI / 2 ), 0 ); } qglEnd(); qglColor3f( 1, 1, 1 ); qglPopMatrix(); qglEnable( GL_TEXTURE_2D ); } void R_DrawEntitiesOnList ( void ) { int i; if ( !gl_drawentities->value ) { return; } /* draw non-transparent first */ for ( i = 0; i < r_newrefdef.num_entities; i++ ) { currententity = &r_newrefdef.entities [ i ]; if ( currententity->flags & RF_TRANSLUCENT ) { continue; /* solid */ } if ( currententity->flags & RF_BEAM ) { R_DrawBeam( currententity ); } else { currentmodel = currententity->model; if ( !currentmodel ) { R_DrawNullModel(); continue; } switch ( currentmodel->type ) { case mod_alias: R_DrawAliasModel( currententity ); break; case mod_brush: R_DrawBrushModel( currententity ); break; case mod_sprite: R_DrawSpriteModel( currententity ); break; default: ri.Sys_Error( ERR_DROP, "Bad modeltype" ); break; } } } /* draw transparent entities * we could sort these if it ever * becomes a problem... */ qglDepthMask( 0 ); /* no z writes */ for ( i = 0; i < r_newrefdef.num_entities; i++ ) { currententity = &r_newrefdef.entities [ i ]; if ( !( currententity->flags & RF_TRANSLUCENT ) ) { continue; /* solid */ } if ( currententity->flags & RF_BEAM ) { R_DrawBeam( currententity ); } else { currentmodel = currententity->model; if ( !currentmodel ) { R_DrawNullModel(); continue; } switch ( currentmodel->type ) { case mod_alias: R_DrawAliasModel( currententity ); break; case mod_brush: R_DrawBrushModel( currententity ); break; case mod_sprite: R_DrawSpriteModel( currententity ); break; default: ri.Sys_Error( ERR_DROP, "Bad modeltype" ); break; } } } qglDepthMask( 1 ); /* back to writing */ } void R_DrawParticles2 ( int num_particles, const particle_t particles[], const unsigned colortable [ 768 ] ) { const particle_t *p; int i; vec3_t up, right; float scale; byte color [ 4 ]; R_Bind( r_particletexture->texnum ); qglDepthMask( GL_FALSE ); /* no z buffering */ qglEnable( GL_BLEND ); R_TexEnv( GL_MODULATE ); qglBegin( GL_TRIANGLES ); VectorScale( vup, 1.5, up ); VectorScale( vright, 1.5, right ); for ( p = particles, i = 0; i < num_particles; i++, p++ ) { /* hack a scale up to keep particles from disapearing */ scale = ( p->origin [ 0 ] - r_origin [ 0 ] ) * vpn [ 0 ] + ( p->origin [ 1 ] - r_origin [ 1 ] ) * vpn [ 1 ] + ( p->origin [ 2 ] - r_origin [ 2 ] ) * vpn [ 2 ]; if ( scale < 20 ) { scale = 1; } else { scale = 1 + scale * 0.004; } *(int *) color = colortable [ p->color ]; color [ 3 ] = p->alpha * 255; qglColor4ubv( color ); qglTexCoord2f( 0.0625, 0.0625 ); qglVertex3fv( p->origin ); qglTexCoord2f( 1.0625, 0.0625 ); qglVertex3f( p->origin [ 0 ] + up [ 0 ] * scale, p->origin [ 1 ] + up [ 1 ] * scale, p->origin [ 2 ] + up [ 2 ] * scale ); qglTexCoord2f( 0.0625, 1.0625 ); qglVertex3f( p->origin [ 0 ] + right [ 0 ] * scale, p->origin [ 1 ] + right [ 1 ] * scale, p->origin [ 2 ] + right [ 2 ] * scale ); } qglEnd(); qglDisable( GL_BLEND ); qglColor4f( 1, 1, 1, 1 ); qglDepthMask( 1 ); /* back to normal Z buffering */ R_TexEnv( GL_REPLACE ); } void R_DrawParticles ( void ) { if ( gl_ext_pointparameters->value && qglPointParameterfEXT ) { int i; unsigned char color [ 4 ]; const particle_t *p; qglDepthMask( GL_FALSE ); qglEnable( GL_BLEND ); qglDisable( GL_TEXTURE_2D ); qglPointSize( gl_particle_size->value ); qglBegin( GL_POINTS ); for ( i = 0, p = r_newrefdef.particles; i < r_newrefdef.num_particles; i++, p++ ) { *(int *) color = d_8to24table [ p->color & 0xFF ]; color [ 3 ] = p->alpha * 255; qglColor4ubv( color ); qglVertex3fv( p->origin ); } qglEnd(); qglDisable( GL_BLEND ); qglColor4f( 1.0F, 1.0F, 1.0F, 1.0F ); qglDepthMask( GL_TRUE ); qglEnable( GL_TEXTURE_2D ); } else { R_DrawParticles2( r_newrefdef.num_particles, r_newrefdef.particles, d_8to24table ); } } void R_PolyBlend ( void ) { if ( !gl_polyblend->value ) { return; } if ( !v_blend [ 3 ] ) { return; } qglDisable( GL_ALPHA_TEST ); qglEnable( GL_BLEND ); qglDisable( GL_DEPTH_TEST ); qglDisable( GL_TEXTURE_2D ); qglLoadIdentity(); qglRotatef( -90, 1, 0, 0 ); /* put Z going up */ qglRotatef( 90, 0, 0, 1 ); /* put Z going up */ qglColor4fv( v_blend ); qglBegin( GL_QUADS ); qglVertex3f( 10, 100, 100 ); qglVertex3f( 10, -100, 100 ); qglVertex3f( 10, -100, -100 ); qglVertex3f( 10, 100, -100 ); qglEnd(); qglDisable( GL_BLEND ); qglEnable( GL_TEXTURE_2D ); qglEnable( GL_ALPHA_TEST ); qglColor4f( 1, 1, 1, 1 ); } int R_SignbitsForPlane ( cplane_t *out ) { int bits, j; /* for fast box on planeside test */ bits = 0; for ( j = 0; j < 3; j++ ) { if ( out->normal [ j ] < 0 ) { bits |= 1 << j; } } return ( bits ); } void R_SetFrustum ( void ) { int i; /* rotate VPN right by FOV_X/2 degrees */ RotatePointAroundVector( frustum [ 0 ].normal, vup, vpn, -( 90 - r_newrefdef.fov_x / 2 ) ); /* rotate VPN left by FOV_X/2 degrees */ RotatePointAroundVector( frustum [ 1 ].normal, vup, vpn, 90 - r_newrefdef.fov_x / 2 ); /* rotate VPN up by FOV_X/2 degrees */ RotatePointAroundVector( frustum [ 2 ].normal, vright, vpn, 90 - r_newrefdef.fov_y / 2 ); /* rotate VPN down by FOV_X/2 degrees */ RotatePointAroundVector( frustum [ 3 ].normal, vright, vpn, -( 90 - r_newrefdef.fov_y / 2 ) ); for ( i = 0; i < 4; i++ ) { frustum [ i ].type = PLANE_ANYZ; frustum [ i ].dist = DotProduct( r_origin, frustum [ i ].normal ); frustum [ i ].signbits = R_SignbitsForPlane( &frustum [ i ] ); } } void R_SetupFrame ( void ) { int i; mleaf_t *leaf; r_framecount++; /* build the transformation matrix for the given view angles */ VectorCopy( r_newrefdef.vieworg, r_origin ); AngleVectors( r_newrefdef.viewangles, vpn, vright, vup ); /* current viewcluster */ if ( !( r_newrefdef.rdflags & RDF_NOWORLDMODEL ) ) { r_oldviewcluster = r_viewcluster; r_oldviewcluster2 = r_viewcluster2; leaf = Mod_PointInLeaf( r_origin, r_worldmodel ); r_viewcluster = r_viewcluster2 = leaf->cluster; /* check above and below so crossing solid water doesn't draw wrong */ if ( !leaf->contents ) { /* look down a bit */ vec3_t temp; VectorCopy( r_origin, temp ); temp [ 2 ] -= 16; leaf = Mod_PointInLeaf( temp, r_worldmodel ); if ( !( leaf->contents & CONTENTS_SOLID ) && ( leaf->cluster != r_viewcluster2 ) ) { r_viewcluster2 = leaf->cluster; } } else { /* look up a bit */ vec3_t temp; VectorCopy( r_origin, temp ); temp [ 2 ] += 16; leaf = Mod_PointInLeaf( temp, r_worldmodel ); if ( !( leaf->contents & CONTENTS_SOLID ) && ( leaf->cluster != r_viewcluster2 ) ) { r_viewcluster2 = leaf->cluster; } } } for ( i = 0; i < 4; i++ ) { v_blend [ i ] = r_newrefdef.blend [ i ]; } c_brush_polys = 0; c_alias_polys = 0; /* clear out the portion of the screen that the NOWORLDMODEL defines */ if ( r_newrefdef.rdflags & RDF_NOWORLDMODEL ) { qglEnable( GL_SCISSOR_TEST ); qglClearColor( 0.3, 0.3, 0.3, 1 ); qglScissor( r_newrefdef.x, vid.height - r_newrefdef.height - r_newrefdef.y, r_newrefdef.width, r_newrefdef.height ); qglClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); qglClearColor( 1, 0, 0.5, 0.5 ); qglDisable( GL_SCISSOR_TEST ); } } void R_MYgluPerspective ( GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar ) { GLdouble xmin, xmax, ymin, ymax; ymax = zNear * tan( fovy * M_PI / 360.0 ); ymin = -ymax; xmin = ymin * aspect; xmax = ymax * aspect; xmin += -( 2 * gl_state.camera_separation ) / zNear; xmax += -( 2 * gl_state.camera_separation ) / zNear; qglFrustum( xmin, xmax, ymin, ymax, zNear, zFar ); } void R_SetupGL ( void ) { float screenaspect; int x, x2, y2, y, w, h; /* set up viewport */ x = floor( r_newrefdef.x * vid.width / vid.width ); x2 = ceil( ( r_newrefdef.x + r_newrefdef.width ) * vid.width / vid.width ); y = floor( vid.height - r_newrefdef.y * vid.height / vid.height ); y2 = ceil( vid.height - ( r_newrefdef.y + r_newrefdef.height ) * vid.height / vid.height ); w = x2 - x; h = y - y2; qglViewport( x, y2, w, h ); /* set up projection matrix */ screenaspect = (float) r_newrefdef.width / r_newrefdef.height; qglMatrixMode( GL_PROJECTION ); qglLoadIdentity(); if (gl_farsee->value == 0) { R_MYgluPerspective( r_newrefdef.fov_y, screenaspect, 4, 4096 ); } else { R_MYgluPerspective( r_newrefdef.fov_y, screenaspect, 4, 8192 ); } qglCullFace( GL_FRONT ); qglMatrixMode( GL_MODELVIEW ); qglLoadIdentity(); qglRotatef( -90, 1, 0, 0 ); /* put Z going up */ qglRotatef( 90, 0, 0, 1 ); /* put Z going up */ qglRotatef( -r_newrefdef.viewangles [ 2 ], 1, 0, 0 ); qglRotatef( -r_newrefdef.viewangles [ 0 ], 0, 1, 0 ); qglRotatef( -r_newrefdef.viewangles [ 1 ], 0, 0, 1 ); qglTranslatef( -r_newrefdef.vieworg [ 0 ], -r_newrefdef.vieworg [ 1 ], -r_newrefdef.vieworg [ 2 ] ); qglGetFloatv( GL_MODELVIEW_MATRIX, r_world_matrix ); /* set drawing parms */ if ( gl_cull->value ) { qglEnable( GL_CULL_FACE ); } else { qglDisable( GL_CULL_FACE ); } qglDisable( GL_BLEND ); qglDisable( GL_ALPHA_TEST ); qglEnable( GL_DEPTH_TEST ); } void R_Clear ( void ) { if ( gl_ztrick->value ) { static int trickframe; if ( gl_clear->value ) { qglClear( GL_COLOR_BUFFER_BIT ); } trickframe++; if ( trickframe & 1 ) { gldepthmin = 0; gldepthmax = 0.49999; qglDepthFunc( GL_LEQUAL ); } else { gldepthmin = 1; gldepthmax = 0.5; qglDepthFunc( GL_GEQUAL ); } } else { if ( gl_clear->value ) { qglClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); } else { qglClear( GL_DEPTH_BUFFER_BIT ); } gldepthmin = 0; gldepthmax = 1; qglDepthFunc( GL_LEQUAL ); } qglDepthRange( gldepthmin, gldepthmax ); /* stencilbuffer shadows */ if ( gl_shadows->value && have_stencil && gl_stencilshadow->value ) { qglClearStencil( 1 ); qglClear( GL_STENCIL_BUFFER_BIT ); } } void R_Flash ( void ) { R_PolyBlend(); } /* * r_newrefdef must be set before the first call */ void R_RenderView ( refdef_t *fd ) { if ( gl_norefresh->value ) { return; } r_newrefdef = *fd; if ( !r_worldmodel && !( r_newrefdef.rdflags & RDF_NOWORLDMODEL ) ) { ri.Sys_Error( ERR_DROP, "R_RenderView: NULL worldmodel" ); } if ( gl_speeds->value ) { c_brush_polys = 0; c_alias_polys = 0; } R_PushDlights(); if ( gl_finish->value ) { qglFinish(); } R_SetupFrame(); R_SetFrustum(); R_SetupGL(); R_MarkLeaves(); /* done here so we know if we're in water */ R_DrawWorld(); R_DrawEntitiesOnList(); R_RenderDlights(); R_DrawParticles(); R_DrawAlphaSurfaces(); R_Flash(); if ( gl_speeds->value ) { ri.Con_Printf( PRINT_ALL, "%4i wpoly %4i epoly %i tex %i lmaps\n", c_brush_polys, c_alias_polys, c_visible_textures, c_visible_lightmaps ); } } void R_SetGL2D ( void ) { /* set 2D virtual screen size */ qglViewport( 0, 0, vid.width, vid.height ); qglMatrixMode( GL_PROJECTION ); qglLoadIdentity(); qglOrtho( 0, vid.width, vid.height, 0, -99999, 99999 ); qglMatrixMode( GL_MODELVIEW ); qglLoadIdentity(); qglDisable( GL_DEPTH_TEST ); qglDisable( GL_CULL_FACE ); qglDisable( GL_BLEND ); qglEnable( GL_ALPHA_TEST ); qglColor4f( 1, 1, 1, 1 ); } void R_SetLightLevel ( void ) { vec3_t shadelight; if ( r_newrefdef.rdflags & RDF_NOWORLDMODEL ) { return; } /* save off light value for server to look at */ R_LightPoint( r_newrefdef.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 ] ) { gl_lightlevel->value = 150 * shadelight [ 0 ]; } else { gl_lightlevel->value = 150 * shadelight [ 2 ]; } } else { if ( shadelight [ 1 ] > shadelight [ 2 ] ) { gl_lightlevel->value = 150 * shadelight [ 1 ]; } else { gl_lightlevel->value = 150 * shadelight [ 2 ]; } } } void R_RenderFrame ( refdef_t *fd ) { R_RenderView( fd ); R_SetLightLevel(); R_SetGL2D(); } void R_Register ( void ) { gl_lefthand = ri.Cvar_Get( "hand", "0", CVAR_USERINFO | CVAR_ARCHIVE ); gl_farsee = ri.Cvar_Get( "gl_farsee", "0", CVAR_LATCH | CVAR_ARCHIVE ); gl_norefresh = ri.Cvar_Get( "gl_norefresh", "0", 0 ); gl_fullbright = ri.Cvar_Get( "gl_fullbright", "0", 0 ); gl_drawentities = ri.Cvar_Get( "gl_drawentities", "1", 0 ); gl_drawworld = ri.Cvar_Get( "gl_drawworld", "1", 0 ); gl_novis = ri.Cvar_Get( "gl_novis", "0", 0 ); gl_nocull = ri.Cvar_Get( "gl_nocull", "0", 0 ); gl_lerpmodels = ri.Cvar_Get( "gl_lerpmodels", "1", 0 ); gl_speeds = ri.Cvar_Get( "gl_speeds", "0", 0 ); gl_lightlevel = ri.Cvar_Get( "gl_lightlevel", "0", 0 ); gl_overbrightbits = ri.Cvar_Get( "gl_overbrightbits", "2", CVAR_ARCHIVE ); gl_nosubimage = ri.Cvar_Get( "gl_nosubimage", "0", 0 ); gl_allow_software = ri.Cvar_Get( "gl_allow_software", "0", 0 ); gl_particle_min_size = ri.Cvar_Get( "gl_particle_min_size", "2", CVAR_ARCHIVE ); gl_particle_max_size = ri.Cvar_Get( "gl_particle_max_size", "40", CVAR_ARCHIVE ); gl_particle_size = ri.Cvar_Get( "gl_particle_size", "40", CVAR_ARCHIVE ); gl_particle_att_a = ri.Cvar_Get( "gl_particle_att_a", "0.01", CVAR_ARCHIVE ); gl_particle_att_b = ri.Cvar_Get( "gl_particle_att_b", "0.0", CVAR_ARCHIVE ); gl_particle_att_c = ri.Cvar_Get( "gl_particle_att_c", "0.01", CVAR_ARCHIVE ); gl_modulate = ri.Cvar_Get( "gl_modulate", "1", CVAR_ARCHIVE ); gl_log = ri.Cvar_Get( "gl_log", "0", 0 ); gl_bitdepth = ri.Cvar_Get( "gl_bitdepth", "0", 0 ); gl_mode = ri.Cvar_Get( "gl_mode", "4", CVAR_ARCHIVE ); gl_lightmap = ri.Cvar_Get( "gl_lightmap", "0", 0 ); gl_shadows = ri.Cvar_Get( "gl_shadows", "0", CVAR_ARCHIVE ); gl_stencilshadow = ri.Cvar_Get( "gl_stencilshadow", "0", CVAR_ARCHIVE ); gl_dynamic = ri.Cvar_Get( "gl_dynamic", "1", 0 ); gl_nobind = ri.Cvar_Get( "gl_nobind", "0", 0 ); gl_round_down = ri.Cvar_Get( "gl_round_down", "1", 0 ); gl_picmip = ri.Cvar_Get( "gl_picmip", "0", 0 ); gl_skymip = ri.Cvar_Get( "gl_skymip", "0", 0 ); gl_showtris = ri.Cvar_Get( "gl_showtris", "0", 0 ); gl_ztrick = ri.Cvar_Get( "gl_ztrick", "0", 0 ); gl_finish = ri.Cvar_Get( "gl_finish", "0", CVAR_ARCHIVE ); gl_clear = ri.Cvar_Get( "gl_clear", "0", 0 ); gl_cull = ri.Cvar_Get( "gl_cull", "1", 0 ); gl_polyblend = ri.Cvar_Get( "gl_polyblend", "1", 0 ); gl_flashblend = ri.Cvar_Get( "gl_flashblend", "0", 0 ); gl_playermip = ri.Cvar_Get( "gl_playermip", "0", 0 ); gl_driver = ri.Cvar_Get( "gl_driver", "libGL.so.1", CVAR_ARCHIVE ); gl_texturemode = ri.Cvar_Get( "gl_texturemode", "GL_LINEAR_MIPMAP_NEAREST", CVAR_ARCHIVE ); gl_texturealphamode = ri.Cvar_Get( "gl_texturealphamode", "default", CVAR_ARCHIVE ); gl_texturesolidmode = ri.Cvar_Get( "gl_texturesolidmode", "default", CVAR_ARCHIVE ); gl_anisotropic = ri.Cvar_Get( "gl_anisotropic", "0", CVAR_ARCHIVE ); gl_anisotropic_avail = ri.Cvar_Get( "gl_anisotropic_avail", "0", 0 ); gl_lockpvs = ri.Cvar_Get( "gl_lockpvs", "0", 0 ); gl_vertex_arrays = ri.Cvar_Get( "gl_vertex_arrays", "0", CVAR_ARCHIVE ); gl_ext_swapinterval = ri.Cvar_Get( "gl_ext_swapinterval", "1", CVAR_ARCHIVE ); gl_ext_palettedtexture = ri.Cvar_Get( "gl_ext_palettedtexture", "0", CVAR_ARCHIVE ); gl_ext_multitexture = ri.Cvar_Get( "gl_ext_multitexture", "1", CVAR_ARCHIVE ); gl_ext_pointparameters = ri.Cvar_Get( "gl_ext_pointparameters", "1", CVAR_ARCHIVE ); gl_ext_compiled_vertex_array = ri.Cvar_Get( "gl_ext_compiled_vertex_array", "1", CVAR_ARCHIVE ); gl_ext_mtexcombine = ri.Cvar_Get( "gl_ext_mtexcombine", "1", CVAR_ARCHIVE ); gl_drawbuffer = ri.Cvar_Get( "gl_drawbuffer", "GL_BACK", 0 ); gl_swapinterval = ri.Cvar_Get( "gl_swapinterval", "1", CVAR_ARCHIVE ); gl_saturatelighting = ri.Cvar_Get( "gl_saturatelighting", "0", 0 ); vid_fullscreen = ri.Cvar_Get( "vid_fullscreen", "0", CVAR_ARCHIVE ); vid_gamma = ri.Cvar_Get( "vid_gamma", "1.0", CVAR_ARCHIVE ); vid_ref = ri.Cvar_Get( "vid_ref", "soft", CVAR_ARCHIVE ); gl_customwidth = ri.Cvar_Get( "gl_customwidth", "1024", CVAR_ARCHIVE ); gl_customheight = ri.Cvar_Get( "gl_customheight", "768", CVAR_ARCHIVE ); ri.Cmd_AddCommand( "imagelist", R_ImageList_f ); ri.Cmd_AddCommand( "screenshot", R_ScreenShot ); ri.Cmd_AddCommand( "modellist", Mod_Modellist_f ); ri.Cmd_AddCommand( "gl_strings", R_Strings ); } qboolean R_SetMode ( void ) { rserr_t err; qboolean fullscreen; fullscreen = vid_fullscreen->value; vid_fullscreen->modified = false; gl_mode->modified = false; /* a bit hackish approach to enable custom resolutions: * Glimp_SetMode needs these values set for mode -1 */ vid.width = gl_customwidth->value; vid.height = gl_customheight->value; if ( ( err = GLimp_SetMode( &vid.width, &vid.height, gl_mode->value, fullscreen ) ) == rserr_ok ) { if ( gl_mode->value == -1 ) { gl_state.prev_mode = 3; /* safe default for custom mode */ } else { gl_state.prev_mode = gl_mode->value; } } else { if ( err == rserr_invalid_fullscreen ) { ri.Cvar_SetValue( "vid_fullscreen", 0 ); vid_fullscreen->modified = false; ri.Con_Printf( PRINT_ALL, "ref_gl::R_SetMode() - fullscreen unavailable in this mode\n" ); if ( ( err = GLimp_SetMode( &vid.width, &vid.height, gl_mode->value, false ) ) == rserr_ok ) { return ( true ); } } else if ( err == rserr_invalid_mode ) { ri.Cvar_SetValue( "gl_mode", gl_state.prev_mode ); gl_mode->modified = false; ri.Con_Printf( PRINT_ALL, "ref_gl::R_SetMode() - invalid mode\n" ); } /* try setting it back to something safe */ if ( ( err = GLimp_SetMode( &vid.width, &vid.height, gl_state.prev_mode, false ) ) != rserr_ok ) { ri.Con_Printf( PRINT_ALL, "ref_gl::R_SetMode() - could not revert to safe mode\n" ); return ( false ); } } return ( true ); } int R_Init ( void *hinstance, void *hWnd ) { char renderer_buffer [ 1000 ]; char vendor_buffer [ 1000 ]; int err; int j; extern float r_turbsin [ 256 ]; for ( j = 0; j < 256; j++ ) { r_turbsin [ j ] *= 0.5; } ri.Con_Printf( PRINT_ALL, "Refresh: " REF_VERSION "\n" ); Draw_GetPalette(); R_Register(); /* initialize our QGL dynamic bindings */ if ( !QGL_Init( gl_driver->string ) ) { QGL_Shutdown(); ri.Con_Printf( PRINT_ALL, "ref_gl::R_Init() - could not load \"%s\"\n", gl_driver->string ); return ( -1 ); } /* initialize OS-specific parts of OpenGL */ if ( !GLimp_Init() ) { QGL_Shutdown(); return ( -1 ); } /* set our "safe" modes */ gl_state.prev_mode = 3; /* create the window and set up the context */ if ( !R_SetMode() ) { QGL_Shutdown(); ri.Con_Printf( PRINT_ALL, "ref_gl::R_Init() - could not R_SetMode()\n" ); return ( -1 ); } ri.Vid_MenuInit(); /* get our various GL strings */ ri.Con_Printf( PRINT_ALL, "\nOpenGL setting:\n", gl_config.vendor_string ); gl_config.vendor_string = (char *) qglGetString( GL_VENDOR ); ri.Con_Printf( PRINT_ALL, "GL_VENDOR: %s\n", gl_config.vendor_string ); gl_config.renderer_string = (char *) qglGetString( GL_RENDERER ); ri.Con_Printf( PRINT_ALL, "GL_RENDERER: %s\n", gl_config.renderer_string ); gl_config.version_string = (char *) qglGetString( GL_VERSION ); ri.Con_Printf( PRINT_ALL, "GL_VERSION: %s\n", gl_config.version_string ); gl_config.extensions_string = (char *) qglGetString( GL_EXTENSIONS ); ri.Con_Printf( PRINT_ALL, "GL_EXTENSIONS: %s\n", gl_config.extensions_string ); strncpy( renderer_buffer, gl_config.renderer_string, sizeof ( renderer_buffer ) ); renderer_buffer [ sizeof ( renderer_buffer ) - 1 ] = 0; strlwr( renderer_buffer ); strncpy( vendor_buffer, gl_config.vendor_string, sizeof ( vendor_buffer ) ); vendor_buffer [ sizeof ( vendor_buffer ) - 1 ] = 0; strlwr( vendor_buffer ); ri.Cvar_Set( "scr_drawall", "0" ); gl_config.allow_cds = true; ri.Con_Printf( PRINT_ALL, "\n\nProbing for OpenGL extensions:\n" ); /* grab extensions */ if ( strstr( gl_config.extensions_string, "GL_EXT_compiled_vertex_array" ) || strstr( gl_config.extensions_string, "GL_SGI_compiled_vertex_array" ) ) { ri.Con_Printf( PRINT_ALL, "...enabling GL_EXT_compiled_vertex_array\n" ); qglLockArraysEXT = (void *) qwglGetProcAddress( "glLockArraysEXT" ); qglUnlockArraysEXT = (void *) qwglGetProcAddress( "glUnlockArraysEXT" ); } else { ri.Con_Printf( PRINT_ALL, "...GL_EXT_compiled_vertex_array not found\n" ); } if ( strstr( gl_config.extensions_string, "GL_EXT_point_parameters" ) ) { if ( gl_ext_pointparameters->value ) { qglPointParameterfEXT = ( void (APIENTRY *) ( GLenum, GLfloat ) )qwglGetProcAddress( "glPointParameterfEXT" ); qglPointParameterfvEXT = ( void (APIENTRY *) ( GLenum, const GLfloat * ) )qwglGetProcAddress( "glPointParameterfvEXT" ); ri.Con_Printf( PRINT_ALL, "...using GL_EXT_point_parameters\n" ); } else { ri.Con_Printf( PRINT_ALL, "...ignoring GL_EXT_point_parameters\n" ); } } else { ri.Con_Printf( PRINT_ALL, "...GL_EXT_point_parameters not found\n" ); } if ( !qglColorTableEXT && strstr( gl_config.extensions_string, "GL_EXT_paletted_texture" ) && strstr( gl_config.extensions_string, "GL_EXT_shared_texture_palette" ) ) { if ( gl_ext_palettedtexture->value ) { ri.Con_Printf( PRINT_ALL, "...using GL_EXT_shared_texture_palette\n" ); qglColorTableEXT = ( void (APIENTRY *) ( GLenum, GLenum, GLsizei, GLenum, GLenum, const GLvoid * ) )qwglGetProcAddress( "glColorTableEXT" ); } else { ri.Con_Printf( PRINT_ALL, "...ignoring GL_EXT_shared_texture_palette\n" ); } } else { ri.Con_Printf( PRINT_ALL, "...GL_EXT_shared_texture_palette not found\n" ); } if ( strstr( gl_config.extensions_string, "GL_ARB_multitexture" ) ) { if ( gl_ext_multitexture->value ) { ri.Con_Printf( PRINT_ALL, "...using GL_ARB_multitexture\n" ); qglMTexCoord2fSGIS = (void *) qwglGetProcAddress( "glMultiTexCoord2fARB" ); qglActiveTextureARB = (void *) qwglGetProcAddress( "glActiveTextureARB" ); qglClientActiveTextureARB = (void *) qwglGetProcAddress( "glClientActiveTextureARB" ); QGL_TEXTURE0 = GL_TEXTURE0_ARB; QGL_TEXTURE1 = GL_TEXTURE1_ARB; } else { ri.Con_Printf( PRINT_ALL, "...ignoring GL_ARB_multitexture\n" ); } } else { ri.Con_Printf( PRINT_ALL, "...GL_ARB_multitexture not found\n" ); } if ( strstr( gl_config.extensions_string, "GL_SGIS_multitexture" ) ) { if ( qglActiveTextureARB ) { ri.Con_Printf( PRINT_ALL, "...GL_SGIS_multitexture deprecated in favor of ARB_multitexture\n" ); } else if ( gl_ext_multitexture->value ) { ri.Con_Printf( PRINT_ALL, "...using GL_SGIS_multitexture\n" ); qglMTexCoord2fSGIS = (void *) qwglGetProcAddress( "glMTexCoord2fSGIS" ); qglSelectTextureSGIS = (void *) qwglGetProcAddress( "glSelectTextureSGIS" ); QGL_TEXTURE0 = GL_TEXTURE0_SGIS; QGL_TEXTURE1 = GL_TEXTURE1_SGIS; } else { ri.Con_Printf( PRINT_ALL, "...ignoring GL_SGIS_multitexture\n" ); } } else { ri.Con_Printf( PRINT_ALL, "...GL_SGIS_multitexture not found\n" ); } gl_config.anisotropic = false; if ( strstr(gl_config.extensions_string,"GL_EXT_texture_filter_anisotropic") ) { ri.Con_Printf (PRINT_ALL,"...using GL_EXT_texture_filter_anisotropic\n" ); gl_config.anisotropic = true; qglGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gl_config.max_anisotropy); ri.Cvar_SetValue("gl_anisotropic_avail", gl_config.max_anisotropy); } else { ri.Con_Printf (PRINT_ALL,"..GL_EXT_texture_filter_anisotropic not found\n" ); gl_config.anisotropic = false; gl_config.max_anisotropy = 0.0; ri.Cvar_SetValue("gl_anisotropic_avail", 0.0); } gl_config.mtexcombine = false; if ( strstr( gl_config.extensions_string, "GL_ARB_texture_env_combine" ) ) { if ( gl_ext_mtexcombine->value ) { Com_Printf( "...using GL_ARB_texture_env_combine\n" ); gl_config.mtexcombine = true; } else { Com_Printf( "...ignoring GL_ARB_texture_env_combine\n" ); } } else { Com_Printf( "...GL_ARB_texture_env_combine not found\n" ); } if ( !gl_config.mtexcombine ) { if ( strstr( gl_config.extensions_string, "GL_EXT_texture_env_combine" ) ) { if ( gl_ext_mtexcombine->value ) { Com_Printf( "...using GL_EXT_texture_env_combine\n" ); gl_config.mtexcombine = true; } else { Com_Printf( "...ignoring GL_EXT_texture_env_combine\n" ); } } else { Com_Printf( "...GL_EXT_texture_env_combine not found\n" ); } } R_SetDefaultState(); R_InitImages(); Mod_Init(); R_InitParticleTexture(); Draw_InitLocal(); err = qglGetError(); if ( err != GL_NO_ERROR ) { ri.Con_Printf( PRINT_ALL, "glGetError() = 0x%x\n", err ); } return ( true ); } void R_Shutdown ( void ) { ri.Cmd_RemoveCommand( "modellist" ); ri.Cmd_RemoveCommand( "screenshot" ); ri.Cmd_RemoveCommand( "imagelist" ); ri.Cmd_RemoveCommand( "gl_strings" ); Mod_FreeAll(); R_ShutdownImages(); /* shut down OS specific OpenGL stuff like contexts, etc. */ GLimp_Shutdown(); /* shutdown our QGL subsystem */ QGL_Shutdown(); } extern void UpdateHardwareGamma (); void R_BeginFrame ( float camera_separation ) { gl_state.camera_separation = camera_separation; /* change modes if necessary */ if ( gl_mode->modified || vid_fullscreen->modified ) { cvar_t *ref; ref = ri.Cvar_Get( "vid_ref", "gl", 0 ); ref->modified = true; } if ( gl_log->modified ) { GLimp_EnableLogging( gl_log->value ); gl_log->modified = false; } if ( gl_log->value ) { GLimp_LogNewFrame(); } if ( vid_gamma->modified ) { vid_gamma->modified = false; if ( gl_state.hwgamma ) { UpdateHardwareGamma(); } } /* go into 2D mode */ qglViewport( 0, 0, vid.width, vid.height ); qglMatrixMode( GL_PROJECTION ); qglLoadIdentity(); qglOrtho( 0, vid.width, vid.height, 0, -99999, 99999 ); qglMatrixMode( GL_MODELVIEW ); qglLoadIdentity(); qglDisable( GL_DEPTH_TEST ); qglDisable( GL_CULL_FACE ); qglDisable( GL_BLEND ); qglEnable( GL_ALPHA_TEST ); qglColor4f( 1, 1, 1, 1 ); /* draw buffer stuff */ if ( gl_drawbuffer->modified ) { gl_drawbuffer->modified = false; if ( ( gl_state.camera_separation == 0 ) || !gl_state.stereo_enabled ) { if ( Q_stricmp( gl_drawbuffer->string, "GL_FRONT" ) == 0 ) { qglDrawBuffer( GL_FRONT ); } else { qglDrawBuffer( GL_BACK ); } } } /* texturemode stuff */ if ( gl_texturemode->modified ) { R_TextureMode( gl_texturemode->string ); gl_texturemode->modified = false; } if ( gl_texturealphamode->modified ) { R_TextureAlphaMode( gl_texturealphamode->string ); gl_texturealphamode->modified = false; } if ( gl_texturesolidmode->modified ) { R_TextureSolidMode( gl_texturesolidmode->string ); gl_texturesolidmode->modified = false; } /* swapinterval stuff */ R_UpdateSwapInterval(); /* clear screen if desired */ R_Clear(); } void R_SetPalette ( const unsigned char *palette ) { int i; byte *rp = (byte *) r_rawpalette; if ( palette ) { for ( i = 0; i < 256; i++ ) { rp [ i * 4 + 0 ] = palette [ i * 3 + 0 ]; rp [ i * 4 + 1 ] = palette [ i * 3 + 1 ]; rp [ i * 4 + 2 ] = palette [ i * 3 + 2 ]; rp [ i * 4 + 3 ] = 0xff; } } else { for ( i = 0; i < 256; i++ ) { rp [ i * 4 + 0 ] = LittleLong( d_8to24table [ i ] ) & 0xff; rp [ i * 4 + 1 ] = ( LittleLong( d_8to24table [ i ] ) >> 8 ) & 0xff; rp [ i * 4 + 2 ] = ( LittleLong( d_8to24table [ i ] ) >> 16 ) & 0xff; rp [ i * 4 + 3 ] = 0xff; } } R_SetTexturePalette( r_rawpalette ); qglClearColor( 0, 0, 0, 0 ); qglClear( GL_COLOR_BUFFER_BIT ); qglClearColor( 1, 0, 0.5, 0.5 ); } /* R_DrawBeam */ void R_DrawBeam ( entity_t *e ) { int i; float r, g, b; vec3_t perpvec; vec3_t direction, normalized_direction; vec3_t start_points [ NUM_BEAM_SEGS ], end_points [ NUM_BEAM_SEGS ]; vec3_t oldorigin, origin; oldorigin [ 0 ] = e->oldorigin [ 0 ]; oldorigin [ 1 ] = e->oldorigin [ 1 ]; oldorigin [ 2 ] = e->oldorigin [ 2 ]; origin [ 0 ] = e->origin [ 0 ]; origin [ 1 ] = e->origin [ 1 ]; origin [ 2 ] = e->origin [ 2 ]; normalized_direction [ 0 ] = direction [ 0 ] = oldorigin [ 0 ] - origin [ 0 ]; normalized_direction [ 1 ] = direction [ 1 ] = oldorigin [ 1 ] - origin [ 1 ]; normalized_direction [ 2 ] = direction [ 2 ] = oldorigin [ 2 ] - origin [ 2 ]; if ( VectorNormalize( normalized_direction ) == 0 ) { return; } PerpendicularVector( perpvec, normalized_direction ); VectorScale( perpvec, e->frame / 2, perpvec ); for ( i = 0; i < 6; i++ ) { RotatePointAroundVector( start_points [ i ], normalized_direction, perpvec, ( 360.0 / NUM_BEAM_SEGS ) * i ); VectorAdd( start_points [ i ], origin, start_points [ i ] ); VectorAdd( start_points [ i ], direction, end_points [ i ] ); } qglDisable( GL_TEXTURE_2D ); qglEnable( GL_BLEND ); qglDepthMask( GL_FALSE ); r = ( LittleLong( d_8to24table [ e->skinnum & 0xFF ] ) ) & 0xFF; g = ( LittleLong( d_8to24table [ e->skinnum & 0xFF ] ) >> 8 ) & 0xFF; b = ( LittleLong( d_8to24table [ e->skinnum & 0xFF ] ) >> 16 ) & 0xFF; r *= 1 / 255.0F; g *= 1 / 255.0F; b *= 1 / 255.0F; qglColor4f( r, g, b, e->alpha ); qglBegin( GL_TRIANGLE_STRIP ); for ( i = 0; i < NUM_BEAM_SEGS; i++ ) { qglVertex3fv( start_points [ i ] ); qglVertex3fv( end_points [ i ] ); qglVertex3fv( start_points [ ( i + 1 ) % NUM_BEAM_SEGS ] ); qglVertex3fv( end_points [ ( i + 1 ) % NUM_BEAM_SEGS ] ); } qglEnd(); qglEnable( GL_TEXTURE_2D ); qglDisable( GL_BLEND ); qglDepthMask( GL_TRUE ); } refexport_t R_GetRefAPI ( refimport_t rimp ) { refexport_t re; ri = rimp; re.api_version = API_VERSION; re.BeginRegistration = R_BeginRegistration; re.RegisterModel = R_RegisterModel; re.RegisterSkin = R_RegisterSkin; re.RegisterPic = Draw_FindPic; re.SetSky = R_SetSky; re.EndRegistration = R_EndRegistration; re.RenderFrame = R_RenderFrame; re.DrawGetPicSize = Draw_GetPicSize; re.DrawPic = Draw_Pic; re.DrawStretchPic = Draw_StretchPic; re.DrawChar = Draw_Char; re.DrawTileClear = Draw_TileClear; re.DrawFill = Draw_Fill; re.DrawFadeScreen = Draw_FadeScreen; re.DrawStretchRaw = Draw_StretchRaw; re.Init = R_Init; re.Shutdown = R_Shutdown; re.CinematicSetPalette = R_SetPalette; re.BeginFrame = R_BeginFrame; re.EndFrame = GLimp_EndFrame; re.AppActivate = NULL; Swap_Init(); return ( re ); } /* * this is only here so the functions in * q_shared.c can link */ void Sys_Error ( char *error, ... ) { va_list argptr; char text [ 1024 ]; va_start( argptr, error ); vsprintf( text, error, argptr ); va_end( argptr ); ri.Sys_Error( ERR_FATAL, "%s", text ); } void Com_Printf ( char *fmt, ... ) { va_list argptr; char text [ 1024 ]; va_start( argptr, fmt ); vsprintf( text, fmt, argptr ); va_end( argptr ); ri.Con_Printf( PRINT_ALL, "%s", text ); }