openmohaa/code/client/cl_scrn.cpp
smallmodel 51afcd7b96 Fix loading screen being incorrectly displayed
This fixes an issue where some part of the loading screen would not be drawn correctly, due to the screen being updated recursively through UI_Update()
2024-09-27 17:59:03 +02:00

560 lines
12 KiB
C++

/*
===========================================================================
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 ; a<w ; a++)
{
i = (ARRAY_LEN(values)+current-1-(a % ARRAY_LEN(values))) % ARRAY_LEN(values);
v = values[i];
v = v * cl_graphscale->integer + 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;
}