diff options
author | Andrey Nazarov <skuller@skuller.net> | 2007-08-14 20:18:08 +0000 |
---|---|---|
committer | Andrey Nazarov <skuller@skuller.net> | 2007-08-14 20:18:08 +0000 |
commit | f294db4ccf45f6274e65260dd6f9a2c5faa94313 (patch) | |
tree | e8cf1ba2bfe9c8417eec17faf912442f52fc4ef2 /source/gl_main.c |
Initial import of the new Q2PRO tree.
Diffstat (limited to 'source/gl_main.c')
-rw-r--r-- | source/gl_main.c | 1225 |
1 files changed, 1225 insertions, 0 deletions
diff --git a/source/gl_main.c b/source/gl_main.c new file mode 100644 index 0000000..9e4b6d7 --- /dev/null +++ b/source/gl_main.c @@ -0,0 +1,1225 @@ +/* +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" + +#ifdef USE_PNG +#include <png.h> +#endif + +#if 0 +#ifdef USE_JPEG +#ifndef USE_PNG +#include <setjmp.h> +#endif +#include <stdio.h> +#include <jpeglib.h> +#endif +#endif + + +/* declare imports for this module */ +cmdAPI_t cmd; +cvarAPI_t cvar; +fsAPI_t fs; +commonAPI_t com; +sysAPI_t sys; +videoAPI_t video; + +glRefdef_t glr; +glStatic_t gl_static; +glconfig_t gl_config; +statCounters_t c; + +int registration_sequence; + +#ifdef USE_JPEG +cvar_t *gl_screenshot_quality; +#endif +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_showtris; +cvar_t *gl_cull_nodes; +cvar_t *gl_cull_models; +cvar_t *gl_showstats; +cvar_t *gl_clear; +cvar_t *gl_novis; +cvar_t *gl_lockpvs; +cvar_t *gl_sort; +cvar_t *gl_primitives; +cvar_t *gl_sort; +cvar_t *gl_subdivide; +cvar_t *gl_fastsky; +cvar_t *gl_dynamic; +cvar_t *gl_fullbright; +cvar_t *gl_mode; +cvar_t *gl_custom_width; +cvar_t *gl_custom_height; +cvar_t *gl_hwgamma; +cvar_t *gl_fullscreen; + +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 ); + +} + +static void GL_Blend( void ) { + color_t color; + + if( glr.fd.blend[3] == 0 ) { + return; + } + + color[0] = glr.fd.blend[0] * 255; + color[1] = glr.fd.blend[1] * 255; + color[2] = glr.fd.blend[2] * 255; + color[3] = glr.fd.blend[3] * 255; + + GL_StretchPic( 0, 0, gl_config.vidWidth, gl_config.vidHeight, 0, 0, 1, 1, + color, r_whiteimage ); +} + +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 = DotProduct( origin, p->normal ) - p->dist; + if( dist < -radius ) { + return CULL_OUT; + } + if( dist <= radius ) { + cull = CULL_CLIP; + } + } + + return cull; +} + +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; + } + + 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] ); + } + + 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 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 ); + + 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] ); + } + + 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; + spriteFrame_t *frame; + image_t *image; + int idx, bits; + float alpha; + + idx = e->frame % model->numFrames; + frame = &model->sframes[idx]; + image = frame->image; + + GL_TexEnv( GL_MODULATE ); + + alpha = 1; + bits = GLS_DEFAULT; + if( e->flags & RF_TRANSLUCENT ) { + alpha = e->alpha; + 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->y, glr.viewaxis[2], point ); + VectorMA( point, frame->x, glr.viewaxis[1], point ); + qglVertex3fv( point ); + + qglTexCoord2f( 0, 0 ); + VectorMA( e->origin, frame->height - frame->y, glr.viewaxis[2], point ); + VectorMA( point, frame->x, glr.viewaxis[1], point ); + qglVertex3fv( point ); + + qglTexCoord2f( 1, 0 ); + VectorMA( e->origin, frame->height - frame->y, glr.viewaxis[2], point ); + VectorMA( point, frame->x - frame->width, glr.viewaxis[1], point ); + qglVertex3fv( point ); + + qglTexCoord2f( 1, 1 ); + VectorMA( e->origin, -frame->y, glr.viewaxis[2], point ); + VectorMA( point, frame->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; + modelType_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 ); + } + + model = GL_ModelForHandle( ent->model ); + if( !model ) { + GL_DrawNullModel(); + continue; + } + + switch( *model ) { + case MODEL_NULL: + GL_DrawNullModel(); + break; + case MODEL_BSP: + GL_DrawBspModel( ( bspSubmodel_t * )model ); + break; + case MODEL_ALIAS: + GL_DrawAliasModel( ( model_t * )model ); + break; + case MODEL_SPRITE: + GL_DrawSpriteModel( ( model_t * )model ); + break; + default: + Com_Error( ERR_FATAL, "GL_DrawEntities: bad model type: %u", *model ); + break; + } + } +} + +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; +} + +static void GL_RenderFrame( refdef_t *fd ) { + GLenum err; + + GL_Flush2D(); + + if( !r_world.name[0] && !( fd->rdflags & RDF_NOWORLDMODEL ) ) { + Com_Error( ERR_FATAL, "GL_RenderView: NULL worldmodel" ); + } + + glr.drawframe++; + + glr.fd = *fd; + glr.num_beams = 0; + + if( !gl_dynamic->integer ) { + glr.fd.num_dlights = 0; + } + + AngleVectors( glr.fd.viewangles, glr.viewaxis[0], glr.viewaxis[1], glr.viewaxis[2] ); + VectorInverse( glr.viewaxis[1] ); + + glr.scroll = -64 * ( ( glr.fd.time / 40.0f ) - ( int )( glr.fd.time / 40.0f ) ); + if( glr.scroll == 0 ) + glr.scroll = -64.0f; + + 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 ); + + GL_DrawAlphaFaces(); + + /* go back into 2D mode */ + GL_Setup2D(); + + GL_Blend(); + + while( ( err = qglGetError() ) != GL_NO_ERROR ) { + Com_EPrintf( "GL_RenderFrame: %s\n", GL_ErrorString( err ) ); + } +} + +static void GL_BeginFrame( void ) { + GLenum err; + + if( gl_log->integer ) { + QGL_LogNewFrame(); + } + + memset( &c, 0, sizeof( c ) ); + + GL_Setup2D(); + + if( gl_clear->integer ) { + qglClear( GL_COLOR_BUFFER_BIT ); + } + + while( ( err = qglGetError() ) != GL_NO_ERROR ) { + Com_EPrintf( "GL_BeginFrame: %s\n", GL_ErrorString( err ) ); + } +} + +static void GL_EndFrame( void ) { + GLenum err; + + if( gl_showstats->integer ) { + Draw_Stats(); + } + GL_Flush2D(); + + if( gl_log->modified ) { + QGL_EnableLogging( gl_log->integer ); + gl_log->modified = qfalse; + } + + while( ( err = qglGetError() ) != GL_NO_ERROR ) { + Com_EPrintf( "GL_EndFrame: %s\n", GL_ErrorString( err ) ); + } + video.EndFrame(); +} + +/* +============================================================================== + + SCREEN SHOTS + +============================================================================== +*/ + +#define SCREENSHOTS_DIRECTORY "screenshots" + +#ifdef USE_JPEG + +/* +================== +GL_ScreenShotJPEG +================== +*/ +static qboolean GL_ScreenShotJPEG( const char *filename ) { + byte *buffer; + int ret; + + buffer = fs.AllocTempMem( gl_config.vidWidth * gl_config.vidHeight * 3 ); + + qglReadPixels( 0, 0, gl_config.vidWidth, gl_config.vidHeight, GL_RGB, + GL_UNSIGNED_BYTE, buffer ); + + ret = Image_WriteJPG( filename, buffer, gl_config.vidWidth, + gl_config.vidHeight, gl_screenshot_quality->integer ); + + fs.FreeFile( buffer ); + + return ret; +} + +#endif + +/* +================== +GL_ScreenShotTGA +================== +*/ +static qboolean GL_ScreenShotTGA( const char *filename ) { + byte *buffer; + int ret; + + buffer = fs.AllocTempMem( gl_config.vidWidth * gl_config.vidHeight * 3 ); + + qglReadPixels( 0, 0, gl_config.vidWidth, gl_config.vidHeight, GL_BGR, + GL_UNSIGNED_BYTE, buffer ); + + ret = Image_WriteTGA( filename, buffer, gl_config.vidWidth, + gl_config.vidHeight ); + + fs.FreeFile( buffer ); + + return ret; +} + +/* +================== +GL_ScreenShot_f +================== +*/ +static void GL_ScreenShot_f( void ) { + char picname[MAX_QPATH]; + char checkname[MAX_QPATH]; + int i; + qboolean screenshotJPEG; + qboolean silent; + qboolean ret; + char *ext; + + if( cmd.Argc() > 3 ) { + Com_Printf( "Usage: %s [name] [silent]\n", cmd.Argv( 0 ) ); + return; + } + +#ifdef USE_JPEG + if( !Q_stricmp( cmd.Argv( 0 ), "screenshotJPEG" ) ) { + screenshotJPEG = qtrue; + ext = ".jpg"; + } else +#endif + { + screenshotJPEG = qfalse; + ext = ".tga"; + } + + silent = qfalse; + picname[0] = 0; + for( i = 1; i < cmd.Argc(); i++ ) { + if( !Q_stricmp( cmd.Argv( i ), "silent" ) ) { + silent = qtrue; + continue; + } + + if( picname[0] ) { + break; + } + + Q_strncpyz( picname, cmd.Argv( i ), sizeof( picname ) ); + + } + +// +// find a file name to save it to +// + if( !picname[0] ) { + for( i = 0; i < 1000; i++ ) { + Com_sprintf( picname, sizeof( picname ), "quake%03d%s", i, ext ); + Com_sprintf( checkname, sizeof( checkname ), SCREENSHOTS_DIRECTORY"/%s", picname ); + + if( fs.LoadFile( checkname, NULL ) == -1 ) { + break; // file doesn't exist + } + + } + + if( i == 1000 ) { + if( !silent ) { + Com_WPrintf( "Couldn't create a screenshot, all slots full\n" ); + } + return; + } + } else { + COM_DefaultExtension( picname, ext, sizeof( picname ) ); + Com_sprintf( checkname, sizeof( checkname ), SCREENSHOTS_DIRECTORY"/%s", picname ); + } + +#ifdef USE_JPEG + if( screenshotJPEG ) { + ret = GL_ScreenShotJPEG( checkname ); + } else +#endif + { + ret = GL_ScreenShotTGA( checkname ); + } + + if( silent ) { + return; + } + + if( ret ) { + Com_Printf( "Wrote %s\n", picname ); + } else { + Com_WPrintf( "Failed to write %s\n", picname ); + } + +} + + +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_ModeChanged( int width, int height, int flags, + int rowbytes, void *pixels ) +{ + gl_config.vidWidth = width; + gl_config.vidHeight = height; + gl_config.flags = flags; +} + +static void GL_Register( void ) { + cvar.Subsystem( CVAR_SYSTEM_VIDEO ); + + /* misc */ +#ifdef USE_JPEG + gl_screenshot_quality = cvar.Get( "gl_screenshot_quality", "100", + CVAR_ARCHIVE ); +#endif + gl_modulate = cvar.Get( "gl_modulate", "1", CVAR_ARCHIVE ); + gl_hwgamma = cvar.Get( "vid_hwgamma", "0", CVAR_ARCHIVE|CVAR_LATCHED ); + + /* 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_showtris = cvar.Get( "gl_showtris", "0", CVAR_CHEAT ); + gl_showstats = cvar.Get( "gl_showstats", "0", 0 ); + gl_cull_nodes = cvar.Get( "gl_cull_nodes", "1", 0 ); + gl_cull_models = cvar.Get( "gl_cull_models", "1", 0 ); + 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_primitives = cvar.Get( "gl_primitives", "0", 0 ); + gl_sort = cvar.Get( "gl_sort", "0", 0 ); + gl_subdivide = cvar.Get( "gl_subdivide", "1", 0 ); + gl_fastsky = cvar.Get( "gl_fastsky", "0", 0 ); + gl_dynamic = cvar.Get( "gl_dynamic", "2", CVAR_ARCHIVE ); + gl_fullbright = cvar.Get( "r_fullbright", "0", CVAR_CHEAT ); + + cmd.AddCommand( "screenshot", GL_ScreenShot_f ); +#ifdef USE_JPEG + cmd.AddCommand( "screenshotJPEG", GL_ScreenShot_f ); +#endif + cmd.AddCommand( "strings", GL_Strings_f ); + + cvar.Subsystem( CVAR_SYSTEM_GENERIC ); +} + +static void GL_Unregister( void ) { + cmd.RemoveCommand( "screenshot" ); +#ifdef USE_JPEG + cmd.RemoveCommand( "screenshotJPEG" ); +#endif + cmd.RemoveCommand( "strings" ); + +} + +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" ); + qglLockArraysEXT = ( PFNGLLOCKARRAYSEXTPROC )qwglGetProcAddress( "glLockArraysEXT" ); + qglUnlockArraysEXT = ( PFNGLUNLOCKARRAYSEXTPROC )qwglGetProcAddress( "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 > 1 ) { + Com_Printf( "...enabling GL_ARB_multitexture (%d texture units)\n", integer ); + qglActiveTextureARB = ( PFNGLACTIVETEXTUREARBPROC )qwglGetProcAddress( "glActiveTextureARB" ); + qglClientActiveTextureARB = ( PFNGLCLIENTACTIVETEXTUREARBPROC )qwglGetProcAddress( "glClientActiveTextureARB" ); + qglMultiTexCoord2fvARB = ( PFNGLMULTITEXCOORD2FVARBPROC )qwglGetProcAddress( "glMultiTexCoord2fvARB" ); + if( integer > MAX_TMUS ) { + integer = MAX_TMUS; + } + gl_static.numTextureUnits = integer; + } else { + Com_Printf( "...ignoring GL_ARB_multitexture,\n" + "not enough texture units supported (%d)\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\n" + "(max anisotropy is %.1f)\n", value ); + gl_config.maxAnisotropy = value; + } else { + Com_Printf( "...ignoring GL_EXT_texture_filter_anisotropic,\n" + "not enough anisotropy supported (%.1f)\n", value ); + } + } else { + Com_Printf( "GL_EXT_texture_filter_anisotropic 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; +} + +void GL_SetDefaultState( void ) { + qglDrawBuffer( GL_BACK ); + qglClearColor( 0, 0, 0, 1 ); + qglClearDepth( 1 ); + qglClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + qglEnable( GL_DEPTH_TEST ); + qglDepthFunc( GL_LEQUAL ); + qglDepthRange( 0, 1 ); + qglDepthMask( GL_TRUE ); + qglDisable( GL_BLEND ); + qglDisable( GL_ALPHA_TEST ); + qglAlphaFunc( GL_GREATER, 0.666f ); + qglHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ); + qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); + + qglEnableClientState( GL_VERTEX_ARRAY ); + qglEnableClientState( GL_TEXTURE_COORD_ARRAY ); + + GL_SelectTMU( 0 ); + qglEnable( GL_TEXTURE_2D ); + GL_Bits( GLS_DEFAULT ); +} + +static void GL_SetupRenderer( void ) { + char renderer_buffer[MAX_STRING_CHARS]; + + Q_strncpyz( 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 ) { + GL_InitImages(); + GL_InitModels(); + GL_SetDefaultState(); +} + +static qboolean GL_Init( qboolean total ) { + Com_DPrintf( "GL_Init( %i )\n", total ); + + if( !total ) { + GL_PostInit(); + return qtrue; + } + + Com_Printf( "ref_gl " VERSION ", " __DATE__ "\n" ); +#if 0 +#ifdef USE_JPEG + Com_Printf( "w/ libjpeg v%d\n", JPEG_LIB_VERSION ); +#endif +#ifdef USE_PNG + Com_Printf( "w/ libpng v" PNG_LIBPNG_VER_STRING "\n" ); +#endif +#endif + + /* initialize OS-specific parts of OpenGL */ + /* create the window and set up the context */ + if( !video.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( "Some of the required OpenGL extensions are missing\n" ); + goto fail; + } + + if( gl_hwgamma->integer && !( gl_config.flags & QVF_GAMMARAMP ) ) { + cvar.SetInteger( "vid_hwgamma", 0 ); + Com_Printf( "Hardware gamma is not supported by this video driver\n" ); + } + + GL_SetupRenderer(); + + QGL_EnableLogging( gl_log->integer ); + gl_log->modified = qfalse; + + GL_PostInit(); + + if( (( size_t )tess.vertices) & 15 ) { + Com_WPrintf( "tess.vertices not 16 byte aligned\n" ); + } + + gl_sort = cvar.Get( "gl_sort", + gl_config.renderer == GL_RENDERER_MESADRI ? "1" : "0", 0 ); + + Com_DPrintf( "Finished GL_Init\n" ); + + return qtrue; + +fail: + QGL_Shutdown(); + GL_Unregister(); + video.Shutdown(); + return qfalse; +} + +/* +=============== +R_Shutdown +=============== +*/ +void GL_Shutdown( qboolean total ) { + Com_DPrintf( "GL_Shutdown( %i )\n", total ); + + Bsp_FreeWorld(); + GL_ShutdownImages(); + GL_ShutdownModels(); + + if( !total ) { + return; + } + + /* + ** shut down OS specific OpenGL stuff like contexts, etc. + */ + video.Shutdown(); + + /* + ** shutdown our QGL subsystem + */ + QGL_Shutdown(); + + GL_Unregister(); + + memset( &gl_static, 0, sizeof( gl_static ) ); + memset( &gl_config, 0, sizeof( gl_config ) ); +} + +void GL_BeginRegistration( const char *name ) { + char fullname[MAX_QPATH]; + bspTexinfo_t *texinfo, *lastexinfo; + bspLeaf_t *leaf, *lastleaf; + bspNode_t *node, *lastnode; + int i; + + gl_static.registering = qtrue; + registration_sequence++; + + memset( &glr, 0, sizeof( glr ) ); + glr.viewcluster1 = glr.viewcluster2 = -2; + + Com_sprintf( fullname, sizeof( fullname ), "maps/%s.bsp", name ); + + /* check if the required world model was already loaded */ + if( !strcmp( r_world.name, fullname ) && + !cvar.VariableInteger( "flushmap" ) ) + { + lastexinfo = r_world.texinfos + r_world.numTexinfos; + for( texinfo = r_world.texinfos; texinfo != lastexinfo; texinfo++ ) { + texinfo->image->registration_sequence = registration_sequence; + } + lastleaf = r_world.leafs + r_world.numLeafs; + for( leaf = r_world.leafs; leaf != lastleaf; leaf++ ) { + leaf->visframe = 0; + } + lastnode = r_world.nodes + r_world.numNodes; + for( node = r_world.nodes; node != lastnode; node++ ) { + node->visframe = 0; + } + for( i = 0; i < lm.numMaps; i++ ) { + lm.lightmaps[i]->registration_sequence = registration_sequence; + } + Com_DPrintf( "GL_BeginRegistration: reused old world model\n" ); + return; + } + + Bsp_FreeWorld(); + GL_BeginPostProcessing(); + + if( !Bsp_LoadWorld( fullname ) ) { + Com_Error( ERR_DROP, "Couldn't load '%s'\n", fullname ); + } + + GL_EndPostProcessing(); +} + +void GL_EndRegistration( void ) { + R_FreeUnusedImages(); + Model_FreeUnused(); + if( scrap_dirty ) { + Scrap_Upload(); + } + gl_static.registering = qfalse; +} + +void GL_SetPalette( const byte *pal ) { + int i; + + if( pal == NULL ) { + for( i = 0; i < 256; i++ ) { + gl_static.palette[i] = d_8to24table[i]; + } + return; + } + + for( i = 0; i < 256; i++ ) { + gl_static.palette[i] = MakeColor( pal[0], pal[1], pal[2], 255 ); + pal += 3; + } +} + +void GL_GetConfig( glconfig_t *config ) { + *config = gl_config; +} + +#ifndef REF_HARD_LINKED +// this is only here so the functions in q_shared.c can link + +void Com_Printf( const char *fmt, ... ) { + va_list argptr; + char text[MAXPRINTMSG]; + + va_start( argptr, fmt ); + Q_vsnprintf( text, sizeof( text ), fmt, argptr ); + va_end( argptr ); + + com.Print( PRINT_ALL, text ); +} + +void Com_DPrintf( const char *fmt, ... ) { + va_list argptr; + char text[MAXPRINTMSG]; + + va_start( argptr, fmt ); + Q_vsnprintf( text, sizeof( text ), fmt, argptr ); + va_end( argptr ); + + com.Print( PRINT_DEVELOPER, text ); +} + +void Com_WPrintf( const char *fmt, ... ) { + va_list argptr; + char text[MAXPRINTMSG]; + + va_start( argptr, fmt ); + Q_vsnprintf( text, sizeof( text ), fmt, argptr ); + va_end( argptr ); + + com.Print( PRINT_WARNING, text ); +} + +void Com_EPrintf( const char *fmt, ... ) { + va_list argptr; + char text[MAXPRINTMSG]; + + va_start( argptr, fmt ); + Q_vsnprintf( text, sizeof( text ), fmt, argptr ); + va_end( argptr ); + + com.Print( PRINT_ERROR, text ); +} + +void Com_Error( comErrorType_t type, const char *error, ... ) { + va_list argptr; + char text[MAXPRINTMSG]; + + va_start( argptr, error ); + Q_vsnprintf( text, sizeof( text ), error, argptr ); + va_end( argptr ); + + com.Error( type, text ); +} + +#endif /* !REF_HARD_LINKED */ + +/* +================= +Ref_FillAPI +================= +*/ +static void Ref_FillAPI( refAPI_t *api ) { + api->BeginRegistration = GL_BeginRegistration; + api->RegisterModel = GL_RegisterModel; + api->RegisterSkin = R_RegisterSkin; + api->RegisterPic = R_RegisterPic; + api->RegisterFont = GL_RegisterFont; + api->SetSky = R_SetSky; + api->EndRegistration = GL_EndRegistration; + api->GetModelSize = GL_GetModelSize; + + api->RenderFrame = GL_RenderFrame; + api->LightPoint = GL_LightPoint; + + api->SetColor = Draw_SetColor; + api->SetClipRect = Draw_SetClipRect; + api->SetScale = Draw_SetScale; + api->DrawString = Draw_String; + api->DrawChar = Draw_Char; + api->DrawGetPicSize = Draw_GetPicSize; + api->DrawGetFontSize = Draw_GetFontSize; + api->DrawPic = Draw_Pic; + api->DrawStretchPicST = Draw_StretchPicST; + api->DrawStretchPic = Draw_StretchPic; + api->DrawTileClear = Draw_TileClear; + api->DrawFill = Draw_Fill; + api->DrawStretchRaw = Draw_StretchRaw; + api->DrawFillEx = Draw_FillEx; + + api->Init = GL_Init; + api->Shutdown = GL_Shutdown; + + api->CinematicSetPalette = GL_SetPalette; + api->BeginFrame = GL_BeginFrame; + api->EndFrame = GL_EndFrame; + api->ModeChanged = GL_ModeChanged; + + api->GetConfig = GL_GetConfig; +} + +/* +================= +Ref_APISetupCallback +================= +*/ +qboolean Ref_APISetupCallback( api_type_t type, void *api ) { + switch( type ) { + case API_REFRESH: + Ref_FillAPI( ( refAPI_t * )api ); + break; + default: + return qfalse; + } + + return qtrue; +} + +#ifndef REF_HARD_LINKED + +/* +@@@@@@@@@@@@@@@@@@@@@ +moduleEntry + +@@@@@@@@@@@@@@@@@@@@@ +*/ +EXPORTED void *moduleEntry( int query, void *data ) { + moduleInfo_t *info; + moduleCapability_t caps; + APISetupCallback_t callback; + + switch( query ) { + case MQ_GETINFO: + info = ( moduleInfo_t * )data; + info->api_version = MODULES_APIVERSION; + Q_strncpyz( info->fullname, "OpenGL Refresh Driver", + sizeof( info->fullname ) ); + Q_strncpyz( info->author, "Andrey Nazarov", sizeof( info->author ) ); + return ( void * )qtrue; + + case MQ_GETCAPS: + caps = MCP_REFRESH; + return ( void * )caps; + + case MQ_SETUPAPI: + if( ( callback = ( APISetupCallback_t )data ) == NULL ) { + return NULL; + } + callback( API_CMD, &cmd ); + callback( API_CVAR, &cvar ); + callback( API_FS, &fs ); + callback( API_COMMON, &com ); + callback( API_SYSTEM, &sys ); + callback( API_VIDEO_OPENGL, &video ); + + return ( void * )Ref_APISetupCallback; + + } + + /* quiet compiler warning */ + return NULL; +} + +#endif /* !REF_HARD_LINKED */ + |