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