1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4
5 This file is part of Quake III Arena source code.
6
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 ===========================================================================
21 */
22 // cl_scrn.c -- master for refresh, status bar, console, chat, notify, etc
23
24 #include "client.h"
25
26 qboolean scr_initialized; // ready to draw
27
28 cvar_t *cl_timegraph;
29 cvar_t *cl_debuggraph;
30 cvar_t *cl_graphheight;
31 cvar_t *cl_graphscale;
32 cvar_t *cl_graphshift;
33
34 /*
35 ================
36 SCR_DrawNamedPic
37
38 Coordinates are 640*480 virtual values
39 =================
40 */
SCR_DrawNamedPic(float x,float y,float width,float height,const char * picname)41 void SCR_DrawNamedPic( float x, float y, float width, float height, const char *picname ) {
42 qhandle_t hShader;
43
44 assert( width != 0 );
45
46 hShader = re.RegisterShader( picname );
47 SCR_AdjustFrom640( &x, &y, &width, &height );
48 re.DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader );
49 }
50
51
52 /*
53 ================
54 SCR_AdjustFrom640
55
56 Adjusted for resolution and screen aspect ratio
57 ================
58 */
SCR_AdjustFrom640(float * x,float * y,float * w,float * h)59 void SCR_AdjustFrom640( float *x, float *y, float *w, float *h ) {
60 float xscale;
61 float yscale;
62
63 #if 0
64 // adjust for wide screens
65 if ( cls.glconfig.vidWidth * 480 > cls.glconfig.vidHeight * 640 ) {
66 *x += 0.5 * ( cls.glconfig.vidWidth - ( cls.glconfig.vidHeight * 640 / 480 ) );
67 }
68 #endif
69
70 // scale for screen sizes
71 xscale = cls.glconfig.vidWidth / 640.0;
72 yscale = cls.glconfig.vidHeight / 480.0;
73 if ( x ) {
74 *x *= xscale;
75 }
76 if ( y ) {
77 *y *= yscale;
78 }
79 if ( w ) {
80 *w *= xscale;
81 }
82 if ( h ) {
83 *h *= yscale;
84 }
85 }
86
87 /*
88 ================
89 SCR_FillRect
90
91 Coordinates are 640*480 virtual values
92 =================
93 */
SCR_FillRect(float x,float y,float width,float height,const float * color)94 void SCR_FillRect( float x, float y, float width, float height, const float *color ) {
95 re.SetColor( color );
96
97 SCR_AdjustFrom640( &x, &y, &width, &height );
98 re.DrawStretchPic( x, y, width, height, 0, 0, 0, 0, cls.whiteShader );
99
100 re.SetColor( NULL );
101 }
102
103
104 /*
105 ================
106 SCR_DrawPic
107
108 Coordinates are 640*480 virtual values
109 =================
110 */
SCR_DrawPic(float x,float y,float width,float height,qhandle_t hShader)111 void SCR_DrawPic( float x, float y, float width, float height, qhandle_t hShader ) {
112 SCR_AdjustFrom640( &x, &y, &width, &height );
113 re.DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader );
114 }
115
116
117
118 /*
119 ** SCR_DrawChar
120 ** chars are drawn at 640*480 virtual screen size
121 */
SCR_DrawChar(int x,int y,float size,int ch)122 static void SCR_DrawChar( int x, int y, float size, int ch ) {
123 int row, col;
124 float frow, fcol;
125 float ax, ay, aw, ah;
126
127 ch &= 255;
128
129 if ( ch == ' ' ) {
130 return;
131 }
132
133 if ( y < -size ) {
134 return;
135 }
136
137 ax = x;
138 ay = y;
139 aw = size;
140 ah = size;
141 SCR_AdjustFrom640( &ax, &ay, &aw, &ah );
142
143 row = ch>>4;
144 col = ch&15;
145
146 frow = row*0.0625;
147 fcol = col*0.0625;
148 size = 0.0625;
149
150 re.DrawStretchPic( ax, ay, aw, ah,
151 fcol, frow,
152 fcol + size, frow + size,
153 cls.charSetShader );
154 }
155
156 /*
157 ** SCR_DrawSmallChar
158 ** small chars are drawn at native screen resolution
159 */
SCR_DrawSmallChar(int x,int y,int ch)160 void SCR_DrawSmallChar( int x, int y, int ch ) {
161 int row, col;
162 float frow, fcol;
163 float size;
164
165 ch &= 255;
166
167 if ( ch == ' ' ) {
168 return;
169 }
170
171 if ( y < -SMALLCHAR_HEIGHT ) {
172 return;
173 }
174
175 row = ch>>4;
176 col = ch&15;
177
178 frow = row*0.0625;
179 fcol = col*0.0625;
180 size = 0.0625;
181
182 re.DrawStretchPic( x, y, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT,
183 fcol, frow,
184 fcol + size, frow + size,
185 cls.charSetShader );
186 }
187
188
189 /*
190 ==================
191 SCR_DrawBigString[Color]
192
193 Draws a multi-colored string with a drop shadow, optionally forcing
194 to a fixed color.
195
196 Coordinates are at 640 by 480 virtual resolution
197 ==================
198 */
SCR_DrawStringExt(int x,int y,float size,const char * string,float * setColor,qboolean forceColor,qboolean noColorEscape)199 void SCR_DrawStringExt( int x, int y, float size, const char *string, float *setColor, qboolean forceColor,
200 qboolean noColorEscape ) {
201 vec4_t color;
202 const char *s;
203 int xx;
204
205 // draw the drop shadow
206 color[0] = color[1] = color[2] = 0;
207 color[3] = setColor[3];
208 re.SetColor( color );
209 s = string;
210 xx = x;
211 while ( *s ) {
212 if ( !noColorEscape && Q_IsColorString( s ) ) {
213 s += 2;
214 continue;
215 }
216 SCR_DrawChar( xx+2, y+2, size, *s );
217 xx += size;
218 s++;
219 }
220
221
222 // draw the colored text
223 s = string;
224 xx = x;
225 re.SetColor( setColor );
226 while ( *s ) {
227 if ( !noColorEscape && Q_IsColorString( s ) ) {
228 if ( !forceColor ) {
229 Com_Memcpy( color, g_color_table[ColorIndex(*(s+1))], sizeof( color ) );
230 color[3] = setColor[3];
231 re.SetColor( color );
232 }
233 s += 2;
234 continue;
235 }
236 SCR_DrawChar( xx, y, size, *s );
237 xx += size;
238 s++;
239 }
240 re.SetColor( NULL );
241 }
242
243
SCR_DrawBigString(int x,int y,const char * s,float alpha,qboolean noColorEscape)244 void SCR_DrawBigString( int x, int y, const char *s, float alpha, qboolean noColorEscape ) {
245 float color[4];
246
247 color[0] = color[1] = color[2] = 1.0;
248 color[3] = alpha;
249 SCR_DrawStringExt( x, y, BIGCHAR_WIDTH, s, color, qfalse, noColorEscape );
250 }
251
SCR_DrawBigStringColor(int x,int y,const char * s,vec4_t color,qboolean noColorEscape)252 void SCR_DrawBigStringColor( int x, int y, const char *s, vec4_t color, qboolean noColorEscape ) {
253 SCR_DrawStringExt( x, y, BIGCHAR_WIDTH, s, color, qtrue, noColorEscape );
254 }
255
256
257 /*
258 ==================
259 SCR_DrawSmallString[Color]
260
261 Draws a multi-colored string with a drop shadow, optionally forcing
262 to a fixed color.
263 ==================
264 */
SCR_DrawSmallStringExt(int x,int y,const char * string,float * setColor,qboolean forceColor,qboolean noColorEscape)265 void SCR_DrawSmallStringExt( int x, int y, const char *string, float *setColor, qboolean forceColor,
266 qboolean noColorEscape ) {
267 vec4_t color;
268 const char *s;
269 int xx;
270
271 // draw the colored text
272 s = string;
273 xx = x;
274 re.SetColor( setColor );
275 while ( *s ) {
276 if ( !noColorEscape && Q_IsColorString( s ) ) {
277 if ( !forceColor ) {
278 Com_Memcpy( color, g_color_table[ColorIndex(*(s+1))], sizeof( color ) );
279 color[3] = setColor[3];
280 re.SetColor( color );
281 }
282 s += 2;
283 continue;
284 }
285 SCR_DrawSmallChar( xx, y, *s );
286 xx += SMALLCHAR_WIDTH;
287 s++;
288 }
289 re.SetColor( NULL );
290 }
291
292
293
294 /*
295 ** SCR_Strlen -- skips color escape codes
296 */
SCR_Strlen(const char * str)297 static int SCR_Strlen( const char *str ) {
298 const char *s = str;
299 int count = 0;
300
301 while ( *s ) {
302 if ( Q_IsColorString( s ) ) {
303 s += 2;
304 } else {
305 count++;
306 s++;
307 }
308 }
309
310 return count;
311 }
312
313 /*
314 ** SCR_GetBigStringWidth
315 */
SCR_GetBigStringWidth(const char * str)316 int SCR_GetBigStringWidth( const char *str ) {
317 return SCR_Strlen( str ) * 16;
318 }
319
320
321 //===============================================================================
322
323 /*
324 =================
325 SCR_DrawDemoRecording
326 =================
327 */
SCR_DrawDemoRecording(void)328 void SCR_DrawDemoRecording( void ) {
329 char string[1024];
330 int pos;
331
332 if ( !clc.demorecording ) {
333 return;
334 }
335 if ( clc.spDemoRecording ) {
336 return;
337 }
338
339 pos = FS_FTell( clc.demofile );
340 sprintf( string, "RECORDING %s: %ik", clc.demoName, pos / 1024 );
341
342 SCR_DrawStringExt( 320 - strlen( string ) * 4, 20, 8, string, g_color_table[7], qtrue, qfalse );
343 }
344
345
346 #ifdef USE_VOIP
347 /*
348 =================
349 SCR_DrawVoipMeter
350 =================
351 */
SCR_DrawVoipMeter(void)352 void SCR_DrawVoipMeter( void ) {
353 char buffer[16];
354 char string[256];
355 int limit, i;
356
357 if (!cl_voipShowMeter->integer)
358 return; // player doesn't want to show meter at all.
359 else if (!cl_voipSend->integer)
360 return; // not recording at the moment.
361 else if (cls.state != CA_ACTIVE)
362 return; // not connected to a server.
363 else if (!cl_connectedToVoipServer)
364 return; // server doesn't support VoIP.
365 else if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive"))
366 return; // single player game.
367 else if (clc.demoplaying)
368 return; // playing back a demo.
369 else if (!cl_voip->integer)
370 return; // client has VoIP support disabled.
371
372 limit = (int) (clc.voipPower * 10.0f);
373 if (limit > 10)
374 limit = 10;
375
376 for (i = 0; i < limit; i++)
377 buffer[i] = '*';
378 while (i < 10)
379 buffer[i++] = ' ';
380 buffer[i] = '\0';
381
382 sprintf( string, "VoIP: [%s]", buffer );
383 SCR_DrawStringExt( 320 - strlen( string ) * 4, 10, 8, string, g_color_table[7], qtrue, qfalse );
384 }
385 #endif
386
387
388
389
390 /*
391 ===============================================================================
392
393 DEBUG GRAPH
394
395 ===============================================================================
396 */
397
398 typedef struct
399 {
400 float value;
401 int color;
402 } graphsamp_t;
403
404 static int current;
405 static graphsamp_t values[1024];
406
407 /*
408 ==============
409 SCR_DebugGraph
410 ==============
411 */
SCR_DebugGraph(float value,int color)412 void SCR_DebugGraph (float value, int color)
413 {
414 values[current&1023].value = value;
415 values[current&1023].color = color;
416 current++;
417 }
418
419 /*
420 ==============
421 SCR_DrawDebugGraph
422 ==============
423 */
SCR_DrawDebugGraph(void)424 void SCR_DrawDebugGraph (void)
425 {
426 int a, x, y, w, i, h;
427 float v;
428 int color;
429
430 //
431 // draw the graph
432 //
433 w = cls.glconfig.vidWidth;
434 x = 0;
435 y = cls.glconfig.vidHeight;
436 re.SetColor( g_color_table[0] );
437 re.DrawStretchPic(x, y - cl_graphheight->integer,
438 w, cl_graphheight->integer, 0, 0, 0, 0, cls.whiteShader );
439 re.SetColor( NULL );
440
441 for (a=0 ; a<w ; a++)
442 {
443 i = (current-1-a+1024) & 1023;
444 v = values[i].value;
445 color = values[i].color;
446 v = v * cl_graphscale->integer + cl_graphshift->integer;
447
448 if (v < 0)
449 v += cl_graphheight->integer * (1+(int)(-v / cl_graphheight->integer));
450 h = (int)v % cl_graphheight->integer;
451 re.DrawStretchPic( x+w-1-a, y - h, 1, h, 0, 0, 0, 0, cls.whiteShader );
452 }
453 }
454
455 //=============================================================================
456
457 /*
458 ==================
459 SCR_Init
460 ==================
461 */
SCR_Init(void)462 void SCR_Init( void ) {
463 cl_timegraph = Cvar_Get ("timegraph", "0", CVAR_CHEAT);
464 cl_debuggraph = Cvar_Get ("debuggraph", "0", CVAR_CHEAT);
465 cl_graphheight = Cvar_Get ("graphheight", "32", CVAR_CHEAT);
466 cl_graphscale = Cvar_Get ("graphscale", "1", CVAR_CHEAT);
467 cl_graphshift = Cvar_Get ("graphshift", "0", CVAR_CHEAT);
468
469 scr_initialized = qtrue;
470 }
471
472
473 //=======================================================
474
475 /*
476 ==================
477 SCR_DrawScreenField
478
479 This will be called twice if rendering in stereo mode
480 ==================
481 */
SCR_DrawScreenField(stereoFrame_t stereoFrame)482 void SCR_DrawScreenField( stereoFrame_t stereoFrame ) {
483 re.BeginFrame( stereoFrame );
484
485 // wide aspect ratio screens need to have the sides cleared
486 // unless they are displaying game renderings
487 if ( cls.state != CA_ACTIVE && cls.state != CA_CINEMATIC ) {
488 if ( cls.glconfig.vidWidth * 480 > cls.glconfig.vidHeight * 640 ) {
489 re.SetColor( g_color_table[0] );
490 re.DrawStretchPic( 0, 0, cls.glconfig.vidWidth, cls.glconfig.vidHeight, 0, 0, 0, 0, cls.whiteShader );
491 re.SetColor( NULL );
492 }
493 }
494
495 // if the menu is going to cover the entire screen, we
496 // don't need to render anything under it
497 if ( uivm && !VM_Call( uivm, UI_IS_FULLSCREEN )) {
498 switch( cls.state ) {
499 default:
500 Com_Error( ERR_FATAL, "SCR_DrawScreenField: bad cls.state" );
501 break;
502 case CA_CINEMATIC:
503 SCR_DrawCinematic();
504 break;
505 case CA_DISCONNECTED:
506 // force menu up
507 S_StopAllSounds();
508 VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_MAIN );
509 break;
510 case CA_CONNECTING:
511 case CA_CHALLENGING:
512 case CA_CONNECTED:
513 // connecting clients will only show the connection dialog
514 // refresh to update the time
515 VM_Call( uivm, UI_REFRESH, cls.realtime );
516 VM_Call( uivm, UI_DRAW_CONNECT_SCREEN, qfalse );
517 break;
518 case CA_LOADING:
519 case CA_PRIMED:
520 // draw the game information screen and loading progress
521 CL_CGameRendering(stereoFrame);
522
523 // also draw the connection information, so it doesn't
524 // flash away too briefly on local or lan games
525 // refresh to update the time
526 VM_Call( uivm, UI_REFRESH, cls.realtime );
527 VM_Call( uivm, UI_DRAW_CONNECT_SCREEN, qtrue );
528 break;
529 case CA_ACTIVE:
530 // always supply STEREO_CENTER as vieworg offset is now done by the engine.
531 CL_CGameRendering(stereoFrame);
532 SCR_DrawDemoRecording();
533 #ifdef USE_VOIP
534 SCR_DrawVoipMeter();
535 #endif
536 break;
537 }
538 }
539
540 // the menu draws next
541 if ( Key_GetCatcher( ) & KEYCATCH_UI && uivm ) {
542 VM_Call( uivm, UI_REFRESH, cls.realtime );
543 }
544
545 // console draws next
546 Con_DrawConsole ();
547
548 // debug graph can be drawn on top of anything
549 if ( cl_debuggraph->integer || cl_timegraph->integer || cl_debugMove->integer ) {
550 SCR_DrawDebugGraph ();
551 }
552 }
553
554 /*
555 ==================
556 SCR_UpdateScreen
557
558 This is called every frame, and can also be called explicitly to flush
559 text to the screen.
560 ==================
561 */
SCR_UpdateScreen(void)562 void SCR_UpdateScreen( void ) {
563 static int recursive;
564
565 if ( !scr_initialized ) {
566 return; // not initialized yet
567 }
568
569 if ( ++recursive > 2 ) {
570 Com_Error( ERR_FATAL, "SCR_UpdateScreen: recursively called" );
571 }
572 recursive = 1;
573
574 // If there is no VM, there are also no rendering commands issued. Stop the renderer in
575 // that case.
576 if( uivm || com_dedicated->integer )
577 {
578 // if running in stereo, we need to draw the frame twice
579 if ( cls.glconfig.stereoEnabled || Cvar_VariableIntegerValue("r_anaglyphMode")) {
580 SCR_DrawScreenField( STEREO_LEFT );
581 SCR_DrawScreenField( STEREO_RIGHT );
582 } else {
583 SCR_DrawScreenField( STEREO_CENTER );
584 }
585
586 if ( com_speeds->integer ) {
587 re.EndFrame( &time_frontend, &time_backend );
588 } else {
589 re.EndFrame( NULL, NULL );
590 }
591 }
592
593 recursive = 0;
594 }
595
596