/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code 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. Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // cl_scrn.c -- master for refresh, status bar, console, chat, notify, etc #include "client.h" #include "cl_ui.h" qboolean scr_initialized; // ready to draw stereoFrame_t s_scr_stereoFrame; cvar_t *cl_timegraph; cvar_t *cl_debuggraph; cvar_t *cl_graphheight; cvar_t *cl_graphscale; cvar_t *cl_graphshift; /* ================ SCR_DrawNamedPic Coordinates are 640*480 virtual values ================= */ void SCR_DrawNamedPic( float x, float y, float width, float height, const char *picname ) { qhandle_t hShader; assert( width != 0 ); hShader = re.RegisterShader( picname ); SCR_AdjustFrom640( &x, &y, &width, &height ); re.DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader ); } /* ================ SCR_AdjustFrom640 Adjusted for resolution and screen aspect ratio ================ */ void SCR_AdjustFrom640( float *x, float *y, float *w, float *h ) { float xscale; float yscale; #if 0 // adjust for wide screens if ( cls.glconfig.vidWidth * 480 > cls.glconfig.vidHeight * 640 ) { *x += 0.5 * ( cls.glconfig.vidWidth - ( cls.glconfig.vidHeight * 640 / 480 ) ); } #endif // scale for screen sizes xscale = cls.glconfig.vidWidth / 640.0; yscale = cls.glconfig.vidHeight / 480.0; if ( x ) { *x *= xscale; } if ( y ) { *y *= yscale; } if ( w ) { *w *= xscale; } if ( h ) { *h *= yscale; } } /* ================ SCR_FillRect Coordinates are 640*480 virtual values ================= */ void SCR_FillRect( float x, float y, float width, float height, const float *color ) { re.SetColor( color ); SCR_AdjustFrom640( &x, &y, &width, &height ); re.DrawStretchPic( x, y, width, height, 0, 0, 0, 0, cls.whiteShader ); re.SetColor( NULL ); } /* ================ SCR_DrawPic Coordinates are 640*480 virtual values ================= */ void SCR_DrawPic( float x, float y, float width, float height, qhandle_t hShader ) { SCR_AdjustFrom640( &x, &y, &width, &height ); re.DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader ); } /* ** SCR_DrawChar ** chars are drawn at 640*480 virtual screen size */ static void SCR_DrawChar( int x, int y, float size, int ch ) { int row, col; float frow, fcol; float ax, ay, aw, ah; ch &= 255; if ( ch == ' ' ) { return; } if ( y < -size ) { return; } ax = x; ay = y; aw = size; ah = size; SCR_AdjustFrom640( &ax, &ay, &aw, &ah ); row = ch>>4; col = ch&15; frow = row*0.0625; fcol = col*0.0625; size = 0.0625; re.DrawStretchPic( ax, ay, aw, ah, fcol, frow, fcol + size, frow + size, cls.charSetShader ); } /* ** SCR_DrawSmallChar ** small chars are drawn at native screen resolution */ void SCR_DrawSmallChar( int x, int y, int ch ) { int row, col; float frow, fcol; float size; ch &= 255; if ( ch == ' ' ) { return; } if ( y < -SMALLCHAR_HEIGHT ) { return; } row = ch>>4; col = ch&15; frow = row*0.0625; fcol = col*0.0625; size = 0.0625; re.DrawStretchPic( x, y, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, fcol, frow, fcol + size, frow + size, cls.charSetShader ); } /* ================== SCR_DrawBigString[Color] Draws a multi-colored string with a drop shadow, optionally forcing to a fixed color. Coordinates are at 640 by 480 virtual resolution ================== */ void SCR_DrawStringExt( int x, int y, float size, const char *string, float *setColor, qboolean forceColor, qboolean noColorEscape ) { vec4_t color; const char *s; int xx; // draw the drop shadow color[0] = color[1] = color[2] = 0; color[3] = setColor[3]; re.SetColor( color ); s = string; xx = x; while ( *s ) { if ( !noColorEscape && Q_IsColorString( s ) ) { s += 2; continue; } SCR_DrawChar( xx+2, y+2, size, *s ); xx += size; s++; } // draw the colored text s = string; xx = x; re.SetColor( setColor ); while ( *s ) { if ( Q_IsColorString( s ) ) { if ( !forceColor ) { Com_Memcpy( color, g_color_table[ColorIndex(*(s+1))], sizeof( color ) ); color[3] = setColor[3]; re.SetColor( color ); } if ( !noColorEscape ) { s += 2; continue; } } SCR_DrawChar( xx, y, size, *s ); xx += size; s++; } re.SetColor( NULL ); } void SCR_DrawBigString( int x, int y, const char *s, float alpha, qboolean noColorEscape ) { float color[4]; color[0] = color[1] = color[2] = 1.0; color[3] = alpha; SCR_DrawStringExt( x, y, BIGCHAR_WIDTH, s, color, qfalse, noColorEscape ); } void SCR_DrawBigStringColor( int x, int y, const char *s, vec4_t color, qboolean noColorEscape ) { SCR_DrawStringExt( x, y, BIGCHAR_WIDTH, s, color, qtrue, noColorEscape ); } /* ================== SCR_DrawSmallString[Color] Draws a multi-colored string with a drop shadow, optionally forcing to a fixed color. ================== */ void SCR_DrawSmallStringExt( int x, int y, const char *string, float *setColor, qboolean forceColor, qboolean noColorEscape ) { vec4_t color; const char *s; int xx; // draw the colored text s = string; xx = x; re.SetColor( setColor ); while ( *s ) { if ( Q_IsColorString( s ) ) { if ( !forceColor ) { Com_Memcpy( color, g_color_table[ColorIndex(*(s+1))], sizeof( color ) ); color[3] = setColor[3]; re.SetColor( color ); } if ( !noColorEscape ) { s += 2; continue; } } SCR_DrawSmallChar( xx, y, *s ); xx += SMALLCHAR_WIDTH; s++; } re.SetColor( NULL ); } /* ** SCR_Strlen -- skips color escape codes */ static int SCR_Strlen( const char *str ) { const char *s = str; int count = 0; while ( *s ) { if ( Q_IsColorString( s ) ) { s += 2; } else { count++; s++; } } return count; } /* ** SCR_GetBigStringWidth */ int SCR_GetBigStringWidth( const char *str ) { return SCR_Strlen( str ) * BIGCHAR_WIDTH; } //=============================================================================== /* ================= SCR_DrawDemoRecording ================= */ void SCR_DrawDemoRecording( void ) { char string[1024]; int pos; if ( !clc.demorecording ) { return; } if ( clc.spDemoRecording ) { return; } pos = FS_FTell( clc.demofile ); Com_sprintf( string, sizeof( string ), "RECORDING %s: %ik", clc.demoName, pos / 1024 ); SCR_DrawStringExt( 320 - strlen( string ) * 4, 20, 8, string, g_color_table[7], qtrue, qfalse ); } /* =============================================================================== DEBUG GRAPH =============================================================================== */ static int current; static float values[1024]; /* ============== SCR_DebugGraph ============== */ void SCR_DebugGraph (float value) { values[current] = value; current = (current + 1) % ARRAY_LEN(values); } /* ============== SCR_DrawDebugGraph ============== */ void SCR_DrawDebugGraph (void) { int a, x, y, w, i, h; float v; // // draw the graph // w = cls.glconfig.vidWidth; x = 0; y = cls.glconfig.vidHeight; re.SetColor( g_color_table[0] ); re.DrawStretchPic(x, y - cl_graphheight->integer, w, cl_graphheight->integer, 0, 0, 0, 0, cls.whiteShader ); re.SetColor( NULL ); for (a=0 ; ainteger + cl_graphshift->integer; if (v < 0) v += cl_graphheight->integer * (1+(int)(-v / cl_graphheight->integer)); h = (int)v % cl_graphheight->integer; re.DrawStretchPic( x+w-1-a, y - h, 1, h, 0, 0, 0, 0, cls.whiteShader ); } } //============================================================================= /* ================== SCR_Init ================== */ void SCR_Init( void ) { cl_timegraph = Cvar_Get ("timegraph", "0", CVAR_CHEAT); cl_debuggraph = Cvar_Get ("debuggraph", "0", CVAR_CHEAT); cl_graphheight = Cvar_Get ("graphheight", "32", CVAR_CHEAT); cl_graphscale = Cvar_Get ("graphscale", "1", CVAR_CHEAT); cl_graphshift = Cvar_Get ("graphshift", "0", CVAR_CHEAT); scr_initialized = qtrue; } //======================================================= /* ================== SCR_DrawScreenField This will be called twice if rendering in stereo mode ================== */ void SCR_DrawScreenField( void ) { // wide aspect ratio screens need to have the sides cleared // unless they are displaying game renderings if ( clc.state != CA_ACTIVE && clc.state != CA_CINEMATIC ) { if ( cls.glconfig.vidWidth * 480 > cls.glconfig.vidHeight * 640 ) { re.SetColor( g_color_table[0] ); //re.DrawStretchPic( 0, 0, cls.glconfig.vidWidth, cls.glconfig.vidHeight, 0, 0, 0, 0, cls.whiteShader ); re.DrawBox( 0, 0, cls.glconfig.vidHeight, cls.glconfig.vidWidth ); re.SetColor( NULL ); } } switch( clc.state ) { case CA_UNINITIALIZED: Com_Error( ERR_FATAL, "SCR_DrawScreenField: clc.state == CA_UNINITIALIZED" ); break; case CA_LOADING: case CA_PRIMED: // draw the game information screen and loading progress CL_CGameRendering(s_scr_stereoFrame); // also draw the connection information, so it doesn't // flash away too briefly on local or lan games // refresh to update the time case CA_CONNECTING: case CA_CHALLENGING: case CA_CONNECTED: // connecting clients will only show the connection dialog // refresh to update the time UI_DrawConnect(); break; case CA_CINEMATIC: SCR_DrawCinematic(); break; case CA_ACTIVE: CL_CGameRendering(s_scr_stereoFrame); break; default: break; } } /* ================== UpdateStereoSide ================== */ void UpdateStereoSide( stereoFrame_t s ) { s_scr_stereoFrame = s; re.BeginFrame( s ); if( clc.state == CA_CINEMATIC ) { SCR_DrawCinematic(); } UI_Update(); } /* ================== SCR_SimpleUpdateScreen ================== */ void SCR_SimpleUpdateScreen( void ) { // if running in stereo, we need to draw the frame twice if( cls.glconfig.stereoEnabled ) { UpdateStereoSide( STEREO_LEFT ); UpdateStereoSide( STEREO_RIGHT ); } else { UpdateStereoSide( STEREO_CENTER ); } if( com_speeds->integer ) { re.EndFrame( &time_frontend, &time_backend ); } else { re.EndFrame( NULL, NULL ); } } /* ================== SCR_UpdateScreen This is called every frame, and can also be called explicitly to flush text to the screen. ================== */ void SCR_UpdateScreen( void ) { static qboolean screen_recursive; if ( !scr_initialized ) { return; // not initialized yet } if (screen_recursive) { // already called return; } screen_recursive = qtrue; CL_StartHunkUsers(qfalse); SCR_SimpleUpdateScreen(); // set the fps value if( fps->integer && clc.state == CA_ACTIVE ) { static int belowlastplaytime = 0; static qboolean belowframerate = qfalse; if( currentfps > 19.8 ) { belowframerate = qfalse; } else if( !belowframerate ) { if( currentfps < 18.0 ) { belowlastplaytime = 10000; belowframerate = qtrue; } } if( belowframerate && cls.realtime > belowlastplaytime ) { belowlastplaytime = cls.realtime + 4000; } } screen_recursive = qfalse; }