summaryrefslogtreecommitdiff
path: root/src/gl_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gl_main.c')
-rw-r--r--src/gl_main.c1009
1 files changed, 1009 insertions, 0 deletions
diff --git a/src/gl_main.c b/src/gl_main.c
new file mode 100644
index 0000000..c4cb998
--- /dev/null
+++ b/src/gl_main.c
@@ -0,0 +1,1009 @@
+/*
+Copyright (C) 2003-2006 Andrey Nazarov
+
+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.
+
+*/
+
+/*
+ * gl_main.c
+ *
+ */
+
+#include "gl_local.h"
+
+glRefdef_t glr;
+glStatic_t gl_static;
+glconfig_t gl_config;
+statCounters_t c;
+
+int registration_sequence;
+
+cvar_t *gl_partscale;
+#if USE_JPG
+cvar_t *gl_screenshot_quality;
+#endif
+#if USE_PNG
+cvar_t *gl_screenshot_compression;
+#endif
+cvar_t *gl_celshading;
+cvar_t *gl_znear;
+cvar_t *gl_zfar;
+cvar_t *gl_modulate;
+cvar_t *gl_log;
+cvar_t *gl_drawworld;
+cvar_t *gl_drawentities;
+cvar_t *gl_drawsky;
+cvar_t *gl_showtris;
+cvar_t *gl_showorigins;
+cvar_t *gl_cull_nodes;
+cvar_t *gl_cull_models;
+#ifdef _DEBUG
+cvar_t *gl_showstats;
+#endif
+cvar_t *gl_bind;
+cvar_t *gl_clear;
+cvar_t *gl_novis;
+cvar_t *gl_lockpvs;
+cvar_t *gl_lightmap;
+#if USE_DLIGHTS
+cvar_t *gl_dynamic;
+#endif
+cvar_t *gl_polyblend;
+cvar_t *gl_fullbright;
+cvar_t *gl_hwgamma;
+cvar_t *gl_fullscreen;
+cvar_t *gl_showerrors;
+cvar_t *gl_fragment_program;
+cvar_t *gl_vertex_buffer_object;
+
+static void GL_SetupFrustum( void ) {
+ cplane_t *f;
+ vec3_t forward, left, up;
+ vec_t fovSin, fovCos, angle;
+
+ angle = DEG2RAD( glr.fd.fov_x / 2 );
+ fovSin = sin( angle );
+ fovCos = cos( angle );
+
+ VectorScale( glr.viewaxis[0], fovSin, forward );
+ VectorScale( glr.viewaxis[1], fovCos, left );
+
+ /* right side */
+ f = &glr.frustumPlanes[0];
+ VectorAdd( forward, left, f->normal );
+ f->dist = DotProduct( glr.fd.vieworg, f->normal );
+ SetPlaneSignbits( f );
+ SetPlaneType( f );
+
+ /* left side */
+ f = &glr.frustumPlanes[1];
+ VectorSubtract( forward, left, f->normal );
+ f->dist = DotProduct( glr.fd.vieworg, f->normal );
+ SetPlaneSignbits( f );
+ SetPlaneType( f );
+
+ angle = DEG2RAD( glr.fd.fov_y / 2 );
+ fovSin = sin( angle );
+ fovCos = cos( angle );
+
+ VectorScale( glr.viewaxis[0], fovSin, forward );
+ VectorScale( glr.viewaxis[2], fovCos, up );
+
+ /* up side */
+ f = &glr.frustumPlanes[2];
+ VectorAdd( forward, up, f->normal );
+ f->dist = DotProduct( glr.fd.vieworg, f->normal );
+ SetPlaneSignbits( f );
+ SetPlaneType( f );
+
+ /* down side */
+ f = &glr.frustumPlanes[3];
+ VectorSubtract( forward, up, f->normal );
+ f->dist = DotProduct( glr.fd.vieworg, f->normal );
+ SetPlaneSignbits( f );
+ SetPlaneType( f );
+}
+
+glCullResult_t GL_CullBox( vec3_t bounds[2] ) {
+ int i, bits;
+ glCullResult_t cull;
+
+ if( !gl_cull_models->integer ) {
+ return CULL_IN;
+ }
+
+ cull = CULL_IN;
+ for( i = 0; i < 4; i++ ) {
+ bits = BoxOnPlaneSide( bounds[0], bounds[1], &glr.frustumPlanes[i] );
+ if( bits == BOX_BEHIND ) {
+ return CULL_OUT;
+ }
+ if( bits != BOX_INFRONT ) {
+ cull = CULL_CLIP;
+ }
+ }
+
+ return cull;
+}
+
+glCullResult_t GL_CullSphere( const vec3_t origin, float radius ) {
+ float dist;
+ cplane_t *p;
+ int i;
+ glCullResult_t cull;
+
+ if( !gl_cull_models->integer ) {
+ return CULL_IN;
+ }
+
+ cull = CULL_IN;
+ for( i = 0, p = glr.frustumPlanes; i < 4; i++, p++ ) {
+ dist = PlaneDiff( origin, p );
+ if( dist < -radius ) {
+ return CULL_OUT;
+ }
+ if( dist <= radius ) {
+ cull = CULL_CLIP;
+ }
+ }
+
+ return cull;
+}
+
+static inline void make_box_points( const vec3_t origin,
+ vec3_t bounds[2],
+ vec3_t points[8] )
+{
+ int i;
+
+ for( i = 0; i < 8; i++ ) {
+ VectorCopy( origin, points[i] );
+ VectorMA( points[i], bounds[(i>>0)&1][0], glr.entaxis[0], points[i] );
+ VectorMA( points[i], bounds[(i>>1)&1][1], glr.entaxis[1], points[i] );
+ VectorMA( points[i], bounds[(i>>2)&1][2], glr.entaxis[2], points[i] );
+ }
+
+}
+
+glCullResult_t GL_CullLocalBox( const vec3_t origin, vec3_t bounds[2] ) {
+ vec3_t points[8];
+ cplane_t *p;
+ int i, j;
+ vec_t dot;
+ qboolean infront;
+ glCullResult_t cull;
+
+ if( !gl_cull_models->integer ) {
+ return CULL_IN;
+ }
+
+ make_box_points( origin, bounds, points );
+
+ cull = CULL_IN;
+ for( i = 0, p = glr.frustumPlanes; i < 4; i++, p++ ) {
+ infront = qfalse;
+ for( j = 0; j < 8; j++ ) {
+ dot = DotProduct( points[j], p->normal );
+ if( dot >= p->dist ) {
+ infront = qtrue;
+ if( cull == CULL_CLIP ) {
+ break;
+ }
+ } else {
+ cull = CULL_CLIP;
+ if( infront ) {
+ break;
+ }
+ }
+ }
+ if( !infront ) {
+ return CULL_OUT;
+ }
+ }
+
+ return cull;
+}
+
+void GL_DrawBox( const vec3_t origin, vec3_t bounds[2] ) {
+ static const int indices[2][4] = {
+ { 0, 1, 3, 2 },
+ { 4, 5, 7, 6 }
+ };
+ vec3_t points[8];
+ int i, j;
+
+ qglDisable( GL_TEXTURE_2D );
+ GL_TexEnv( GL_REPLACE );
+ qglDisable( GL_DEPTH_TEST );
+ qglColor4f( 1, 1, 1, 1 );
+
+ make_box_points( origin, bounds, points );
+
+ for( i = 0; i < 2; i++ ) {
+ qglBegin( GL_LINE_LOOP );
+ for( j = 0; j < 4; j++ ) {
+ qglVertex3fv( points[ indices[i][j] ] );
+ }
+ qglEnd();
+ }
+
+ qglBegin( GL_LINES );
+ for( i = 0; i < 4; i++ ) {
+ qglVertex3fv( points[ i ] );
+ qglVertex3fv( points[ i + 4 ] );
+ }
+ qglEnd();
+
+ qglEnable( GL_DEPTH_TEST );
+ qglEnable( GL_TEXTURE_2D );
+}
+
+static void GL_DrawSpriteModel( model_t *model ) {
+ vec3_t point;
+ entity_t *e = glr.ent;
+ mspriteframe_t *frame;
+ image_t *image;
+ int bits;
+ float alpha;
+
+ frame = &model->spriteframes[e->frame % model->numframes];
+ image = frame->image;
+
+ GL_TexEnv( GL_MODULATE );
+
+ alpha = ( e->flags & RF_TRANSLUCENT ) ? e->alpha : 1.0f;
+
+ bits = GLS_DEPTHMASK_FALSE;
+ if( alpha == 1.0f ) {
+ if( image->flags & if_transparent ) {
+ if( image->flags & if_paletted ) {
+ bits |= GLS_ALPHATEST_ENABLE;
+ } else {
+ bits |= GLS_BLEND_BLEND;
+ }
+ }
+ } else {
+ bits |= GLS_BLEND_BLEND;
+ }
+ GL_Bits( bits );
+
+ qglColor4f( 1, 1, 1, alpha );
+
+ GL_BindTexture( image->texnum );
+
+ qglBegin( GL_QUADS );
+
+ qglTexCoord2f( 0, 1 );
+ VectorMA( e->origin, -frame->origin_y, glr.viewaxis[2], point );
+ VectorMA( point, frame->origin_x, glr.viewaxis[1], point );
+ qglVertex3fv( point );
+
+ qglTexCoord2f( 0, 0 );
+ VectorMA( e->origin, frame->height - frame->origin_y, glr.viewaxis[2], point );
+ VectorMA( point, frame->origin_x, glr.viewaxis[1], point );
+ qglVertex3fv( point );
+
+ qglTexCoord2f( 1, 0 );
+ VectorMA( e->origin, frame->height - frame->origin_y, glr.viewaxis[2], point );
+ VectorMA( point, frame->origin_x - frame->width, glr.viewaxis[1], point );
+ qglVertex3fv( point );
+
+ qglTexCoord2f( 1, 1 );
+ VectorMA( e->origin, -frame->origin_y, glr.viewaxis[2], point );
+ VectorMA( point, frame->origin_x - frame->width, glr.viewaxis[1], point );
+ qglVertex3fv( point );
+
+ qglEnd();
+}
+
+static void GL_DrawNullModel( void ) {
+ vec3_t point;
+
+ qglDisable( GL_TEXTURE_2D );
+ //qglDisable( GL_DEPTH_TEST );
+ qglBegin( GL_LINES );
+
+ qglColor3f( 1, 0, 0 );
+ qglVertex3fv( glr.ent->origin );
+ VectorMA( glr.ent->origin, 16, glr.entaxis[0], point );
+ qglVertex3fv( point );
+
+ qglColor3f( 0, 1, 0 );
+ qglVertex3fv( glr.ent->origin );
+ VectorMA( glr.ent->origin, 16, glr.entaxis[1], point );
+ qglVertex3fv( point );
+
+ qglColor3f( 0, 0, 1 );
+ qglVertex3fv( glr.ent->origin );
+ VectorMA( glr.ent->origin, 16, glr.entaxis[2], point );
+ qglVertex3fv( point );
+
+ qglEnd();
+ //qglEnable( GL_DEPTH_TEST );
+ qglEnable( GL_TEXTURE_2D );
+}
+
+static void GL_DrawEntities( int mask ) {
+ entity_t *ent, *last;
+ model_t *model;
+
+ if( !gl_drawentities->integer ) {
+ return;
+ }
+
+ last = glr.fd.entities + glr.fd.num_entities;
+ for( ent = glr.fd.entities; ent != last; ent++ ) {
+ if( ent->flags & RF_BEAM ) {
+ /* beams are drawn elsewhere in single batch */
+ glr.num_beams++;
+ continue;
+ }
+ if( ( ent->flags & RF_TRANSLUCENT ) != mask ) {
+ continue;
+ }
+
+ glr.ent = ent;
+ if( ent->angles[0] || ent->angles[1] || ent->angles[2] ) {
+ glr.entrotated = qtrue;
+ AngleVectors( ent->angles, glr.entaxis[0], glr.entaxis[1], glr.entaxis[2] );
+ VectorInverse( glr.entaxis[1] );
+ } else {
+ glr.entrotated = qfalse;
+ VectorSet( glr.entaxis[0], 1, 0, 0 );
+ VectorSet( glr.entaxis[1], 0, 1, 0 );
+ VectorSet( glr.entaxis[2], 0, 0, 1 );
+ }
+
+ // inline BSP model
+ if( ent->model & 0x80000000 ) {
+ bsp_t *bsp = gl_static.world.cache;
+ int index = ~ent->model;
+
+ if( !bsp ) {
+ Com_Error( ERR_DROP, "%s: inline model without world",
+ __func__ );
+ }
+
+ if( index < 1 || index >= bsp->nummodels ) {
+ Com_Error( ERR_DROP, "%s: inline model %d out of range",
+ __func__, index );
+ }
+
+ GL_DrawBspModel( &bsp->models[index] );
+ continue;
+ }
+
+ model = MOD_ForHandle( ent->model );
+ if( !model ) {
+ GL_DrawNullModel();
+ continue;
+ }
+
+ if( model->frames ) {
+ GL_DrawAliasModel( model );
+ } else if( model->spriteframes ) {
+ GL_DrawSpriteModel( model );
+ } else {
+ Com_Error( ERR_FATAL, "%s: bad model type", __func__ );
+ }
+
+ if( gl_showorigins->integer ) {
+ GL_DrawNullModel();
+ }
+
+ }
+}
+
+static char *GL_ErrorString( GLenum err ) {
+ char *str;
+
+#define MapError( x ) case x: str = #x; break;
+
+ switch( err ) {
+ MapError( GL_NO_ERROR )
+ MapError( GL_INVALID_ENUM )
+ MapError( GL_INVALID_VALUE )
+ MapError( GL_INVALID_OPERATION )
+ MapError( GL_STACK_OVERFLOW )
+ MapError( GL_STACK_UNDERFLOW )
+ MapError( GL_OUT_OF_MEMORY )
+ default: str = "UNKNOWN ERROR";
+ }
+
+#undef MapError
+
+ return str;
+}
+
+void GL_ShowErrors( const char *func ) {
+ GLenum err;
+
+ if( gl_showerrors->integer ) {
+ while( ( err = qglGetError() ) != GL_NO_ERROR ) {
+ Com_EPrintf( "%s: %s\n", func, GL_ErrorString( err ) );
+ }
+ }
+}
+
+void R_RenderFrame( refdef_t *fd ) {
+ GL_Flush2D();
+
+ if( !gl_static.world.cache && !( fd->rdflags & RDF_NOWORLDMODEL ) ) {
+ Com_Error( ERR_FATAL, "GL_RenderView: NULL worldmodel" );
+ }
+
+ glr.drawframe++;
+
+ glr.fd = *fd;
+ glr.num_beams = 0;
+
+#if USE_DLIGHTS
+ if( !gl_dynamic->integer ) {
+ glr.fd.num_dlights = 0;
+ }
+#endif
+
+ GL_Setup3D();
+
+ if( gl_cull_nodes->integer ) {
+ GL_SetupFrustum();
+ }
+
+ if( !( glr.fd.rdflags & RDF_NOWORLDMODEL ) && gl_drawworld->integer ) {
+ GL_DrawWorld();
+ }
+
+ GL_DrawEntities( 0 );
+
+ GL_DrawBeams();
+
+ GL_DrawParticles();
+
+ GL_DrawEntities( RF_TRANSLUCENT );
+
+ if( !( glr.fd.rdflags & RDF_NOWORLDMODEL ) && gl_drawworld->integer ) {
+ GL_DrawAlphaFaces();
+ }
+
+ /* go back into 2D mode */
+ GL_Setup2D();
+
+ if( gl_polyblend->integer && glr.fd.blend[3] != 0 ) {
+ GL_Blend();
+ }
+
+ GL_ShowErrors( __func__ );
+}
+
+void R_BeginFrame( void ) {
+ if( gl_log->integer ) {
+ QGL_LogNewFrame();
+ }
+
+ memset( &c, 0, sizeof( c ) );
+
+ GL_Setup2D();
+
+ if( gl_clear->integer ) {
+ qglClear( GL_COLOR_BUFFER_BIT );
+ }
+
+ GL_ShowErrors( __func__ );
+}
+
+void R_EndFrame( void ) {
+#ifdef _DEBUG
+ if( gl_showstats->integer ) {
+ Draw_Stats();
+ }
+#endif
+ GL_Flush2D();
+
+ if( gl_log->modified ) {
+ QGL_EnableLogging( gl_log->integer );
+ gl_log->modified = qfalse;
+ }
+
+ GL_ShowErrors( __func__ );
+
+ VID_EndFrame();
+
+// qglFinish();
+}
+
+/*
+==============================================================================
+
+ SCREEN SHOTS
+
+==============================================================================
+*/
+
+#if USE_TGA || USE_JPG || USE_PNG
+static void make_screenshot( const char *ext, img_save_t func, GLenum format, int param ) {
+ char buffer[MAX_OSPATH];
+ byte *pixels;
+ qerror_t ret;
+ qhandle_t f;
+ int i;
+
+ if( Cmd_Argc() > 1 ) {
+ f = FS_EasyOpenFile( buffer, sizeof( buffer ), FS_MODE_WRITE,
+ SCREENSHOTS_DIRECTORY "/", Cmd_Argv( 1 ), ext );
+ if( !f ) {
+ return;
+ }
+ } else {
+ // find a file name to save it to
+ for( i = 0; i < 1000; i++ ) {
+ Q_snprintf( buffer, sizeof( buffer ), SCREENSHOTS_DIRECTORY "/quake%03d%s", i, ext );
+ ret = FS_FOpenFile( buffer, &f, FS_MODE_WRITE|FS_FLAG_EXCL );
+ if( f ) {
+ break;
+ }
+ if( ret != Q_ERR_EXIST ) {
+ Com_EPrintf( "Couldn't exclusively open %s for writing: %s\n",
+ buffer, Q_ErrorString( ret ) );
+ return;
+ }
+ }
+
+ if( i == 1000 ) {
+ Com_EPrintf( "All screenshot slots are full.\n" );
+ return;
+ }
+ }
+
+ pixels = FS_AllocTempMem( gl_config.vidWidth * gl_config.vidHeight * 3 );
+
+ qglReadPixels( 0, 0, gl_config.vidWidth, gl_config.vidHeight, format,
+ GL_UNSIGNED_BYTE, pixels );
+
+ ret = func( f, buffer, pixels, gl_config.vidWidth, gl_config.vidHeight, param );
+
+ FS_FreeFile( pixels );
+
+ FS_FCloseFile( f );
+
+ if( ret < 0 ) {
+ Com_EPrintf( "Couldn't write %s: %s\n", buffer, Q_ErrorString( ret ) );
+ } else {
+ Com_Printf( "Wrote %s\n", buffer );
+ }
+}
+#endif
+
+/*
+==================
+GL_ScreenShot_f
+==================
+*/
+static void GL_ScreenShot_f( void ) {
+#if USE_TGA
+ if( Cmd_Argc() > 2 ) {
+ Com_Printf( "Usage: %s [name]\n", Cmd_Argv( 0 ) );
+ return;
+ }
+ make_screenshot( ".tga", IMG_SaveTGA, GL_BGR, 0 );
+#else
+ Com_Printf( "Couldn't create screenshot due to no TGA support linked in.\n" );
+#endif
+}
+
+#if USE_JPG
+static void GL_ScreenShotJPG_f( void ) {
+ int quality;
+
+ if( Cmd_Argc() > 3 ) {
+ Com_Printf( "Usage: %s [name] [quality]\n", Cmd_Argv( 0 ) );
+ return;
+ }
+
+ if( Cmd_Argc() > 2 ) {
+ quality = atoi( Cmd_Argv( 2 ) );
+ } else {
+ quality = gl_screenshot_quality->integer;
+ }
+
+ make_screenshot( ".jpg", IMG_SaveJPG, GL_RGB, quality );
+}
+#endif
+
+#if USE_PNG
+static void GL_ScreenShotPNG_f( void ) {
+ int compression;
+
+ if( Cmd_Argc() > 3 ) {
+ Com_Printf( "Usage: %s [name] [compression]\n", Cmd_Argv( 0 ) );
+ return;
+ }
+
+ if( Cmd_Argc() > 2 ) {
+ compression = atoi( Cmd_Argv( 2 ) );
+ } else {
+ compression = gl_screenshot_compression->integer;
+ }
+
+ make_screenshot( ".png", IMG_SavePNG, GL_RGB, compression );
+}
+#endif
+
+static void GL_Strings_f( void ) {
+ Com_Printf( "GL_VENDOR: %s\n", gl_config.vendorString );
+ Com_Printf( "GL_RENDERER: %s\n", gl_config.rendererString );
+ Com_Printf( "GL_VERSION: %s\n", gl_config.versionString );
+ Com_Printf( "GL_EXTENSIONS: %s\n", gl_config.extensionsString );
+}
+
+// ==============================================================================
+
+static void GL_Register( void ) {
+ /* misc */
+ gl_partscale = Cvar_Get( "gl_partscale", "2", 0 );
+#if USE_JPG
+ gl_screenshot_quality = Cvar_Get( "gl_screenshot_quality", "100", 0 );
+#endif
+#if USE_PNG
+ gl_screenshot_compression = Cvar_Get( "gl_screenshot_compression", "6", 0 );
+#endif
+ gl_celshading = Cvar_Get( "gl_celshading", "0", 0 );
+ gl_modulate = Cvar_Get( "gl_modulate", "1", CVAR_ARCHIVE );
+ gl_hwgamma = Cvar_Get( "vid_hwgamma", "0", CVAR_ARCHIVE|CVAR_REFRESH );
+
+ /* development variables */
+ gl_znear = Cvar_Get( "gl_znear", "2", CVAR_CHEAT );
+ gl_zfar = Cvar_Get( "gl_zfar", "16384", 0 );
+ gl_log = Cvar_Get( "gl_log", "0", 0 );
+ gl_drawworld = Cvar_Get( "gl_drawworld", "1", CVAR_CHEAT );
+ gl_drawentities = Cvar_Get( "gl_drawentities", "1", CVAR_CHEAT );
+ gl_drawsky = Cvar_Get( "gl_drawsky", "1", 0 );
+ gl_showtris = Cvar_Get( "gl_showtris", "0", CVAR_CHEAT );
+ gl_showorigins = Cvar_Get( "gl_showorigins", "0", CVAR_CHEAT );
+#ifdef _DEBUG
+ gl_showstats = Cvar_Get( "gl_showstats", "0", 0 );
+#endif
+ gl_cull_nodes = Cvar_Get( "gl_cull_nodes", "1", 0 );
+ gl_cull_models = Cvar_Get( "gl_cull_models", "1", 0 );
+ gl_bind = Cvar_Get( "gl_bind", "1", CVAR_CHEAT );
+ gl_clear = Cvar_Get( "gl_clear", "0", 0 );
+ gl_novis = Cvar_Get( "gl_novis", "0", 0 );
+ gl_lockpvs = Cvar_Get( "gl_lockpvs", "0", CVAR_CHEAT );
+ gl_lightmap = Cvar_Get( "gl_lightmap", "0", CVAR_CHEAT );
+#if USE_DLIGHTS
+ gl_dynamic = Cvar_Get( "gl_dynamic", "2", CVAR_ARCHIVE );
+#endif
+ gl_polyblend = Cvar_Get( "gl_polyblend", "1", 0 );
+ gl_fullbright = Cvar_Get( "r_fullbright", "0", CVAR_CHEAT );
+ gl_showerrors = Cvar_Get( "gl_showerrors", "1", 0 );
+ gl_fragment_program = Cvar_Get( "gl_fragment_program", "0", CVAR_REFRESH );
+ gl_vertex_buffer_object = Cvar_Get( "gl_vertex_buffer_object", "0", CVAR_REFRESH );
+
+ Cmd_AddCommand( "screenshot", GL_ScreenShot_f );
+#if USE_JPG
+ Cmd_AddCommand( "screenshotjpg", GL_ScreenShotJPG_f );
+#else
+ Cmd_AddCommand( "screenshotjpg", GL_ScreenShot_f );
+#endif
+#if USE_PNG
+ Cmd_AddCommand( "screenshotpng", GL_ScreenShotPNG_f );
+#else
+ Cmd_AddCommand( "screenshotpng", GL_ScreenShot_f );
+#endif
+ Cmd_AddCommand( "strings", GL_Strings_f );
+}
+
+static void GL_Unregister( void ) {
+ Cmd_RemoveCommand( "screenshot" );
+ Cmd_RemoveCommand( "screenshotjpg" );
+ Cmd_RemoveCommand( "screenshotpng" );
+ Cmd_RemoveCommand( "strings" );
+}
+
+#define GPA( x ) do { q ## x = ( void * )qglGetProcAddress( #x ); } while( 0 )
+
+static qboolean GL_SetupExtensions( void ) {
+ const char *extensions;
+ int integer;
+ float value;
+
+ extensions = gl_config.extensionsString;
+ if( strstr( extensions, "GL_EXT_compiled_vertex_array" ) ) {
+ Com_Printf( "...enabling GL_EXT_compiled_vertex_array\n" );
+ GPA( glLockArraysEXT );
+ GPA( glUnlockArraysEXT );
+ } else {
+ Com_Printf( "GL_EXT_compiled_vertex_array not found\n" );
+ }
+
+ gl_static.numTextureUnits = 1;
+ if( strstr( extensions, "GL_ARB_multitexture" ) ) {
+ qglGetIntegerv( GL_MAX_TEXTURE_UNITS_ARB, &integer );
+ if( integer >= 2 ) {
+ Com_Printf( "...enabling GL_ARB_multitexture (%d TMUs)\n", integer );
+ GPA( glActiveTextureARB );
+ GPA( glClientActiveTextureARB );
+ if( integer > MAX_TMUS ) {
+ integer = MAX_TMUS;
+ }
+ gl_static.numTextureUnits = integer;
+ } else {
+ Com_Printf( "...ignoring GL_ARB_multitexture,\n"
+ "%d TMU is not enough\n", integer );
+ }
+ } else {
+ Com_Printf( "GL_ARB_multitexture not found\n" );
+ }
+
+ gl_config.maxAnisotropy = 1;
+ if( strstr( extensions, "GL_EXT_texture_filter_anisotropic" ) ) {
+ qglGetFloatv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &value );
+ if( value >= 2 ) {
+ Com_Printf( "...enabling GL_EXT_texture_filter_anisotropic (%d max)\n", ( int )value );
+ gl_config.maxAnisotropy = value;
+ } else {
+ Com_Printf( "...ignoring GL_EXT_texture_filter_anisotropic,\n"
+ "%d anisotropy is not enough\n", ( int )value );
+ }
+ } else {
+ Com_Printf( "GL_EXT_texture_filter_anisotropic not found\n" );
+ }
+
+ if( strstr( extensions, "GL_ARB_fragment_program" ) ) {
+ if( gl_fragment_program->integer ) {
+ Com_Printf( "...enabling GL_ARB_fragment_program\n" );
+ GPA( glProgramStringARB );
+ GPA( glBindProgramARB );
+ GPA( glDeleteProgramsARB );
+ GPA( glGenProgramsARB );
+ GPA( glProgramEnvParameter4fvARB );
+ GPA( glProgramLocalParameter4fvARB );
+ } else {
+ Com_Printf( "...ignoring GL_ARB_fragment_program\n" );
+ }
+ } else {
+ Com_Printf( "GL_ARB_fragment_program not found\n" );
+ }
+
+ if( strstr( extensions, "GL_ARB_vertex_buffer_object" ) ) {
+ if( gl_vertex_buffer_object->integer ) {
+ Com_Printf( "...enabling GL_ARB_vertex_buffer_object\n" );
+ GPA( glBindBufferARB );
+ GPA( glDeleteBuffersARB );
+ GPA( glGenBuffersARB );
+ GPA( glIsBufferARB );
+ GPA( glBufferDataARB );
+ GPA( glBufferSubDataARB );
+ GPA( glGetBufferSubDataARB );
+ GPA( glMapBufferARB );
+ GPA( glUnmapBufferARB );
+ GPA( glGetBufferParameterivARB );
+ GPA( glGetBufferPointervARB );
+ } else {
+ Com_Printf( "...ignoring GL_ARB_vertex_buffer_object\n" );
+ }
+ } else {
+ Com_Printf( "GL_ARB_vertex_buffer_object not found\n" );
+ }
+
+ if( !qglActiveTextureARB ) {
+ return qfalse;
+ }
+
+ qglGetIntegerv( GL_MAX_TEXTURE_SIZE, &gl_static.maxTextureSize );
+ if( gl_static.maxTextureSize > MAX_TEXTURE_SIZE ) {
+ gl_static.maxTextureSize = MAX_TEXTURE_SIZE;
+ }
+
+ return qtrue;
+}
+
+#undef GPA
+
+static void GL_IdentifyRenderer( void ) {
+ char renderer_buffer[MAX_STRING_CHARS];
+
+ Q_strlcpy( renderer_buffer, gl_config.rendererString,
+ sizeof( renderer_buffer ) );
+ Q_strlwr( renderer_buffer );
+
+ if( strstr( renderer_buffer, "voodoo" ) ) {
+ if( !strstr( renderer_buffer, "rush" ) ) {
+ gl_config.renderer = GL_RENDERER_VOODOO;
+ } else {
+ gl_config.renderer = GL_RENDERER_VOODOO_RUSH;
+ }
+ } else if( strstr( renderer_buffer, "permedia" ) ) {
+ gl_config.renderer = GL_RENDERER_PERMEDIA2;
+ } else if ( strstr( renderer_buffer, "glint" ) ) {
+ gl_config.renderer = GL_RENDERER_GLINT;
+ } else if( strstr( renderer_buffer, "gdi" ) ) {
+ gl_config.renderer = GL_RENDERER_MCD;
+ } else if( strstr( renderer_buffer, "glzicd" ) ) {
+ gl_config.renderer = GL_RENDERER_INTERGRAPH;
+ } else if( strstr( renderer_buffer, "pcx2" ) ) {
+ gl_config.renderer = GL_RENDERER_POWERVR;
+ } else if( strstr( renderer_buffer, "verite" ) ) {
+ gl_config.renderer = GL_RENDERER_RENDITION;
+ } else if( strstr( renderer_buffer, "mesa dri" ) ) {
+ gl_config.renderer = GL_RENDERER_MESADRI;
+ } else {
+ gl_config.renderer = GL_RENDERER_OTHER;
+ }
+}
+
+static void GL_PostInit( void ) {
+ registration_sequence = 1;
+
+ GL_InitImages();
+ MOD_Init();
+ GL_SetDefaultState();
+}
+
+// ==============================================================================
+
+/*
+===============
+R_Init
+===============
+*/
+qboolean R_Init( qboolean total ) {
+ Com_DPrintf( "GL_Init( %i )\n", total );
+
+ if( !total ) {
+ GL_PostInit();
+ return qtrue;
+ }
+
+ Com_Printf( "ref_gl " VERSION ", " __DATE__ "\n" );
+
+ // initialize OS-specific parts of OpenGL
+ // create the window and set up the context
+ if( !VID_Init() ) {
+ return qfalse;
+ }
+
+ GL_Register();
+
+ // initialize our QGL dynamic bindings
+ QGL_Init();
+
+#define GET_STRING( x ) ( const char * )qglGetString( x )
+
+ gl_config.vendorString = GET_STRING( GL_VENDOR );
+ gl_config.rendererString = GET_STRING( GL_RENDERER );
+ gl_config.versionString = GET_STRING( GL_VERSION );
+ gl_config.extensionsString = GET_STRING( GL_EXTENSIONS );
+
+ if( !gl_config.extensionsString || !gl_config.extensionsString[0] ) {
+ Com_EPrintf( "No OpenGL extensions found, check your drivers\n" );
+ goto fail;
+ }
+
+ if( !GL_SetupExtensions() ) {
+ Com_EPrintf( "Required OpenGL extensions are missing\n" );
+ goto fail;
+ }
+
+ if( gl_hwgamma->integer && !( gl_config.flags & QVF_GAMMARAMP ) ) {
+ Cvar_Set( "vid_hwgamma", "0" );
+ Com_Printf( "Hardware gamma is not supported by this video driver\n" );
+ }
+
+ GL_IdentifyRenderer();
+
+ QGL_EnableLogging( gl_log->integer );
+ gl_log->modified = qfalse;
+
+ GL_PostInit();
+
+ GL_InitPrograms();
+
+ if( (( size_t )tess.vertices) & 15 ) {
+ Com_WPrintf( "tess.vertices not 16 byte aligned\n" );
+ }
+
+ Com_DPrintf( "Finished GL_Init\n" );
+
+ return qtrue;
+
+fail:
+ QGL_Shutdown();
+ GL_Unregister();
+ VID_Shutdown();
+ return qfalse;
+}
+
+/*
+===============
+R_Shutdown
+===============
+*/
+void R_Shutdown( qboolean total ) {
+ Com_DPrintf( "GL_Shutdown( %i )\n", total );
+
+ GL_FreeWorld();
+ GL_ShutdownImages();
+ MOD_Shutdown();
+
+ if( !total ) {
+ return;
+ }
+
+ GL_ShutdownPrograms();
+
+ // shut down OS specific OpenGL stuff like contexts, etc.
+ VID_Shutdown();
+
+ // shutdown our QGL subsystem
+ QGL_Shutdown();
+
+ GL_Unregister();
+
+ memset( &gl_static, 0, sizeof( gl_static ) );
+ memset( &gl_config, 0, sizeof( gl_config ) );
+}
+
+/*
+===============
+R_BeginRegistration
+===============
+*/
+void R_BeginRegistration( const char *name ) {
+ char fullname[MAX_QPATH];
+
+ gl_static.registering = qtrue;
+ registration_sequence++;
+
+ memset( &glr, 0, sizeof( glr ) );
+ glr.viewcluster1 = glr.viewcluster2 = -2;
+
+ Q_concat( fullname, sizeof( fullname ), "maps/", name, ".bsp", NULL );
+ GL_LoadWorld( fullname );
+}
+
+/*
+===============
+R_EndRegistration
+===============
+*/
+void R_EndRegistration( void ) {
+ IMG_FreeUnused();
+ MOD_FreeUnused();
+ Scrap_Upload();
+ gl_static.registering = qfalse;
+}
+
+/*
+===============
+R_ModeChanged
+===============
+*/
+void R_ModeChanged( int width, int height, int flags, int rowbytes, void *pixels ) {
+ gl_config.vidWidth = width & ~7;
+ gl_config.vidHeight = height & ~1;
+ gl_config.flags = flags;
+}
+
+/*
+===============
+R_GetConfig
+===============
+*/
+void R_GetConfig( glconfig_t *config ) {
+ *config = gl_config;
+}
+