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