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