6eaf808f9835abfc0b5e408d389490ef8900a7c3
[xonotic/darkplaces.git] / gl_screen.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 // screen.c -- master for refresh, status bar, console, chat, notify, etc
22
23 #include "quakedef.h"
24
25 /*
26
27 background clear
28 rendering
29 turtle/net/ram icons
30 sbar
31 centerprint / slow centerprint
32 notify lines
33 intermission / finale overlay
34 loading plaque
35 console
36 menu
37
38 required background clears
39 required update regions
40
41
42 syncronous draw mode or async
43 One off screen buffer, with updates either copied or xblited
44 Need to double buffer?
45
46
47 async draw will require the refresh area to be cleared, because it will be
48 xblited, but sync draw can just ignore it.
49
50 sync
51 draw
52
53 CenterPrint ()
54 SlowPrint ()
55 Screen_Update ();
56 Con_Printf ();
57
58 net
59 turn off messages option
60
61 the refresh is always rendered, unless the console is full screen
62
63
64 console is:
65         notify lines
66         half
67         full
68
69
70 */
71
72
73 float   scr_con_current;
74 float   scr_conlines;           // lines of console to display
75
76 float   oldscreensize, oldfov;
77 cvar_t  scr_viewsize = {CVAR_SAVE, "viewsize","100"};
78 cvar_t  scr_fov = {CVAR_SAVE, "fov","90"};      // 10 - 170
79 cvar_t  scr_conspeed = {CVAR_SAVE, "scr_conspeed","900"}; // LordHavoc: quake used 300
80 cvar_t  scr_centertime = {0, "scr_centertime","2"};
81 cvar_t  scr_showram = {CVAR_SAVE, "showram","1"};
82 cvar_t  scr_showturtle = {CVAR_SAVE, "showturtle","0"};
83 cvar_t  scr_showpause = {CVAR_SAVE, "showpause","1"};
84 cvar_t  scr_printspeed = {0, "scr_printspeed","8"};
85 cvar_t  r_render = {0, "r_render", "1"};
86 cvar_t  r_brightness = {CVAR_SAVE, "r_brightness", "1"}; // LordHavoc: a method of operating system independent color correction
87 cvar_t  r_contrast = {CVAR_SAVE, "r_contrast", "1"}; // LordHavoc: a method of operating system independent color correction
88 cvar_t  gl_dither = {CVAR_SAVE, "gl_dither", "1"}; // whether or not to use dithering
89
90 qboolean        scr_initialized;                // ready to draw
91
92 int                     clearconsole;
93 int                     clearnotify;
94
95 int                     lightscalebit;
96 float           lightscale;
97
98 qboolean        scr_disabled_for_loading;
99 //qboolean      scr_drawloading;
100 //float         scr_disabled_time;
101
102 void SCR_ScreenShot_f (void);
103
104 /*
105 ===============================================================================
106
107 CENTER PRINTING
108
109 ===============================================================================
110 */
111
112 char            scr_centerstring[1024];
113 float           scr_centertime_start;   // for slow victory printing
114 float           scr_centertime_off;
115 int                     scr_center_lines;
116 int                     scr_erase_lines;
117 int                     scr_erase_center;
118
119 /*
120 ==============
121 SCR_CenterPrint
122
123 Called for important messages that should stay in the center of the screen
124 for a few moments
125 ==============
126 */
127 void SCR_CenterPrint (char *str)
128 {
129         strncpy (scr_centerstring, str, sizeof(scr_centerstring)-1);
130         scr_centertime_off = scr_centertime.value;
131         scr_centertime_start = cl.time;
132
133 // count the number of lines for centering
134         scr_center_lines = 1;
135         while (*str)
136         {
137                 if (*str == '\n')
138                         scr_center_lines++;
139                 str++;
140         }
141 }
142
143
144 void SCR_DrawCenterString (void)
145 {
146         char    *start;
147         int             l;
148         int             x, y;
149         int             remaining;
150
151 // the finale prints the characters one at a time
152         if (cl.intermission)
153                 remaining = scr_printspeed.value * (cl.time - scr_centertime_start);
154         else
155                 remaining = 9999;
156
157         scr_erase_center = 0;
158         start = scr_centerstring;
159
160         if (scr_center_lines <= 4)
161                 y = vid.conheight*0.35;
162         else
163                 y = 48;
164
165         do      
166         {
167         // scan the width of the line
168                 for (l=0 ; l<40 ; l++)
169                         if (start[l] == '\n' || !start[l])
170                                 break;
171                 x = (vid.conwidth - l*8)/2;
172                 if (l > 0)
173                 {
174                         if (remaining < l)
175                                 l = remaining;
176                         DrawQ_String(x, y, start, l, 8, 8, 1, 1, 1, 1, 0);
177                         remaining -= l;
178                         if (remaining <= 0)
179                                 return;
180                 }
181
182                 y += 8;
183
184                 while (*start && *start != '\n')
185                         start++;
186
187                 if (!*start)
188                         break;
189                 start++;                // skip the \n
190         } while (1);
191 }
192
193 void SCR_CheckDrawCenterString (void)
194 {
195         if (scr_center_lines > scr_erase_lines)
196                 scr_erase_lines = scr_center_lines;
197
198         scr_centertime_off -= host_frametime;
199
200         if (scr_centertime_off <= 0 && !cl.intermission)
201                 return;
202         if (key_dest != key_game)
203                 return;
204
205         SCR_DrawCenterString ();
206 }
207
208 //=============================================================================
209
210 /*
211 ====================
212 CalcFov
213 ====================
214 */
215 float CalcFov (float fov_x, float width, float height)
216 {
217         // calculate vision size and alter by aspect, then convert back to angle
218         return atan (height / (width / tan(fov_x/360*M_PI))) * 360 / M_PI;
219 }
220
221 /*
222 =================
223 SCR_CalcRefdef
224
225 Must be called whenever vid changes
226 Internal use only
227 =================
228 */
229 static void SCR_CalcRefdef (void)
230 {
231         float size;
232
233 //      vid.recalc_refdef = 0;
234
235 //========================================
236
237 // bound viewsize
238         if (scr_viewsize.value < 30)
239                 Cvar_Set ("viewsize","30");
240         if (scr_viewsize.value > 120)
241                 Cvar_Set ("viewsize","120");
242
243 // bound field of view
244         if (scr_fov.value < 10)
245                 Cvar_Set ("fov","10");
246         if (scr_fov.value > 170)
247                 Cvar_Set ("fov","170");
248
249 // intermission is always full screen
250         if (cl.intermission)
251         {
252                 size = 1;
253                 sb_lines = 0;
254         }
255         else
256         {
257                 if (scr_viewsize.value >= 120)
258                         sb_lines = 0;           // no status bar at all
259                 else if (scr_viewsize.value >= 110)
260                         sb_lines = 24;          // no inventory
261                 else
262                         sb_lines = 24+16+8;
263                 size = scr_viewsize.value * (1.0 / 100.0);
264         }
265
266         if (size >= 1)
267         {
268                 r_refdef.width = vid.realwidth;
269                 r_refdef.height = vid.realheight;
270                 r_refdef.x = 0;
271                 r_refdef.y = 0;
272         }
273         else
274         {
275                 r_refdef.width = vid.realwidth * size;
276                 r_refdef.height = vid.realheight * size;
277                 r_refdef.x = (vid.realwidth - r_refdef.width)/2;
278                 r_refdef.y = (vid.realheight - r_refdef.height)/2;
279         }
280
281         r_refdef.fov_x = scr_fov.value;
282         r_refdef.fov_y = CalcFov (r_refdef.fov_x, r_refdef.width, r_refdef.height);
283
284         r_refdef.width = bound(0, r_refdef.width, vid.realwidth);
285         r_refdef.height = bound(0, r_refdef.height, vid.realheight);
286         r_refdef.x = bound(0, r_refdef.x, vid.realwidth) + vid.realx;
287         r_refdef.y = bound(0, r_refdef.y, vid.realheight) + vid.realy;
288 }
289
290
291 /*
292 =================
293 SCR_SizeUp_f
294
295 Keybinding command
296 =================
297 */
298 void SCR_SizeUp_f (void)
299 {
300         Cvar_SetValue ("viewsize",scr_viewsize.value+10);
301 }
302
303
304 /*
305 =================
306 SCR_SizeDown_f
307
308 Keybinding command
309 =================
310 */
311 void SCR_SizeDown_f (void)
312 {
313         Cvar_SetValue ("viewsize",scr_viewsize.value-10);
314 }
315
316 //============================================================================
317
318 void gl_screen_start(void)
319 {
320 }
321
322 void gl_screen_shutdown(void)
323 {
324 }
325
326 void gl_screen_newmap(void)
327 {
328 }
329
330 /*
331 ==================
332 SCR_Init
333 ==================
334 */
335 static void R_Envmap_f (void);
336 void GL_Screen_Init (void)
337 {
338         Cvar_RegisterVariable (&scr_fov);
339         Cvar_RegisterVariable (&scr_viewsize);
340         Cvar_RegisterVariable (&scr_conspeed);
341         Cvar_RegisterVariable (&scr_showram);
342         Cvar_RegisterVariable (&scr_showturtle);
343         Cvar_RegisterVariable (&scr_showpause);
344         Cvar_RegisterVariable (&scr_centertime);
345         Cvar_RegisterVariable (&scr_printspeed);
346         Cvar_RegisterVariable (&r_render);
347         Cvar_RegisterVariable (&r_brightness);
348         Cvar_RegisterVariable (&r_contrast);
349         Cvar_RegisterVariable (&gl_dither);
350 #ifdef NORENDER
351         Cvar_SetValue("r_render", 0);
352 #endif
353
354 //
355 // register our commands
356 //
357         Cmd_AddCommand ("screenshot",SCR_ScreenShot_f);
358         Cmd_AddCommand ("envmap", R_Envmap_f);
359         Cmd_AddCommand ("sizeup",SCR_SizeUp_f);
360         Cmd_AddCommand ("sizedown",SCR_SizeDown_f);
361
362         scr_initialized = true;
363
364         R_RegisterModule("GL_Screen", gl_screen_start, gl_screen_shutdown, gl_screen_newmap);
365 }
366
367
368
369 /*
370 ==============
371 SCR_DrawRam
372 ==============
373 */
374 void SCR_DrawRam (void)
375 {
376 //      if (!scr_showram.integer)
377 //              return;
378 //      DrawQ_Pic (32, 0, "ram", 0, 0, 1, 1, 1, 1, 0);
379 }
380
381 /*
382 ==============
383 SCR_DrawTurtle
384 ==============
385 */
386 void SCR_DrawTurtle (void)
387 {
388         static int      count;
389
390         if (cls.state != ca_connected)
391                 return;
392
393         if (!scr_showturtle.integer)
394                 return;
395
396         if (host_frametime < 0.1)
397         {
398                 count = 0;
399                 return;
400         }
401
402         count++;
403         if (count < 3)
404                 return;
405
406         DrawQ_Pic (0, 0, "turtle", 0, 0, 1, 1, 1, 1, 0);
407 }
408
409 /*
410 ==============
411 SCR_DrawNet
412 ==============
413 */
414 void SCR_DrawNet (void)
415 {
416         if (cls.state != ca_connected)
417                 return;
418         if (realtime - cl.last_received_message < 0.3)
419                 return;
420         if (cls.demoplayback)
421                 return;
422
423         DrawQ_Pic (64, 0, "net", 0, 0, 1, 1, 1, 1, 0);
424 }
425
426 /*
427 ==============
428 DrawPause
429 ==============
430 */
431 void SCR_DrawPause (void)
432 {
433         cachepic_t      *pic;
434
435         if (cls.state != ca_connected)
436                 return;
437
438         if (!scr_showpause.integer)             // turn off for screenshots
439                 return;
440
441         if (!cl.paused)
442                 return;
443
444         pic = Draw_CachePic ("gfx/pause.lmp");
445         DrawQ_Pic ((vid.conwidth - pic->width)/2, (vid.conheight - pic->height)/2, "gfx/pause.lmp", 0, 0, 1, 1, 1, 1, 0);
446 }
447
448
449
450 /*
451 ==============
452 SCR_DrawLoading
453 ==============
454 */
455 /*
456 void SCR_DrawLoading (void)
457 {
458         cachepic_t      *pic;
459
460         if (!scr_drawloading)
461                 return;
462
463         pic = Draw_CachePic ("gfx/loading.lmp");
464         DrawQ_Pic ((vid.conwidth - pic->width)/2, (vid.conheight - pic->height)/2, "gfx/loading.lmp", 0, 0, 1, 1, 1, 1, 0);
465 }
466 */
467
468
469
470 //=============================================================================
471
472
473 /*
474 ==================
475 SCR_SetUpToDrawConsole
476 ==================
477 */
478 void SCR_SetUpToDrawConsole (void)
479 {
480         Con_CheckResize ();
481
482 // decide on the height of the console
483         con_forcedup = !cl.worldmodel || cls.signon != SIGNONS;
484
485         if (con_forcedup)
486         {
487                 scr_conlines = vid.conheight;           // full screen
488                 scr_con_current = scr_conlines;
489         }
490         else if (key_dest == key_console)
491                 scr_conlines = vid.conheight/2; // half screen
492         else
493                 scr_conlines = 0;                               // none visible
494
495         if (scr_conlines < scr_con_current)
496         {
497                 scr_con_current -= scr_conspeed.value*host_realframetime;
498                 if (scr_conlines > scr_con_current)
499                         scr_con_current = scr_conlines;
500
501         }
502         else if (scr_conlines > scr_con_current)
503         {
504                 scr_con_current += scr_conspeed.value*host_realframetime;
505                 if (scr_conlines < scr_con_current)
506                         scr_con_current = scr_conlines;
507         }
508 }
509
510 /*
511 ==================
512 SCR_DrawConsole
513 ==================
514 */
515 void SCR_DrawConsole (void)
516 {
517         if (scr_con_current)
518         {
519                 Con_DrawConsole (scr_con_current);
520                 clearconsole = 0;
521         }
522         else
523         {
524                 if (key_dest == key_game || key_dest == key_message)
525                         Con_DrawNotify ();      // only draw notify in game
526         }
527 }
528
529
530 /*
531 ==============================================================================
532
533                                                 SCREEN SHOTS
534
535 ==============================================================================
536 */
537
538 /*
539 ==================
540 SCR_ScreenShot_f
541 ==================
542 */
543 void SCR_ScreenShot_f (void)
544 {
545         byte            *buffer, gamma[256];
546         char            filename[80];
547         char            checkname[MAX_OSPATH];
548         int                     i;
549 //
550 // find a file name to save it to
551 //
552         strcpy(filename,"dp0000.tga");
553
554         for (i=0 ; i<=9999 ; i++)
555         {
556                 filename[2] = (i/1000)%10 + '0';
557                 filename[3] = (i/ 100)%10 + '0';
558                 filename[4] = (i/  10)%10 + '0';
559                 filename[5] = (i/   1)%10 + '0';
560                 sprintf (checkname, "%s/%s", com_gamedir, filename);
561                 if (Sys_FileTime(checkname) == -1)
562                         break;  // file doesn't exist
563         }
564         if (i==10000)
565         {
566                 Con_Printf ("SCR_ScreenShot_f: Couldn't create a TGA file\n");
567                 return;
568         }
569
570         buffer = Mem_Alloc(tempmempool, vid.realwidth*vid.realheight*3);
571         glReadPixels (vid.realx, vid.realy, vid.realwidth, vid.realheight, GL_RGB, GL_UNSIGNED_BYTE, buffer);
572         CHECKGLERROR
573
574         // apply hardware gamma to the image
575         BuildGammaTable8((lighthalf && hardwaregammasupported) ? 2.0f : 1.0f, 1, 1, 0, gamma);
576         Image_GammaRemapRGB(buffer, buffer, vid.realwidth*vid.realheight, gamma, gamma, gamma);
577
578         Image_WriteTGARGB_preflipped(filename, vid.realwidth, vid.realheight, buffer);
579
580         Mem_Free(buffer);
581         Con_Printf ("Wrote %s\n", filename);
582 }
583
584 /*
585 ===============
586 R_Envmap_f
587
588 Grab six views for environment mapping tests
589 ===============
590 */
591 float CalcFov (float fov_x, float width, float height);
592 struct
593 {
594         float angles[3];
595         char *name;
596 }
597 envmapinfo[6] =
598 {
599         {{  0,   0, 0}, "ft"},
600         {{  0,  90, 0}, "rt"},
601         {{  0, 180, 0}, "bk"},
602         {{  0, 270, 0}, "lf"},
603         {{-90,  90, 0}, "up"},
604         {{ 90,  90, 0}, "dn"}
605 };
606 static void R_Envmap_f (void)
607 {
608         int             i, size;
609         char    filename[256];
610         char    basename[256];
611         byte    *buffer, gamma[256];
612
613         if (Cmd_Argc() != 3)
614         {
615                 Con_Printf ("envmap <basename> <size>: save out 6 cubic environment map images, usable with loadsky, note that size must one of 128, 256, 512, or 1024 and can't be bigger than your current resolution\n");
616                 return;
617         }
618
619         if (!r_render.integer)
620                 return;
621
622         strcpy(basename, Cmd_Argv(1));
623         size = atoi(Cmd_Argv(2));
624         if (size != 128 && size != 256 && size != 512 && size != 1024)
625         {
626                 Con_Printf("envmap: size must be one of 128, 256, 512, or 1024\n");
627                 return;
628         }
629         if (size > vid.realwidth || size > vid.realheight)
630         {
631                 Con_Printf("envmap: your resolution is not big enough to render that size\n");
632                 return;
633         }
634
635         buffer = Mem_Alloc(tempmempool, size*size*3);
636         if (buffer == NULL)
637         {
638                 Con_Printf("envmap: unable to allocate memory for image\n");
639                 return;
640         }
641
642         BuildGammaTable8((lighthalf && hardwaregammasupported) ? 2.0f : 1.0f, 1, 1, 0, gamma);
643
644 //      glDrawBuffer  (GL_FRONT);
645 //      glReadBuffer  (GL_FRONT);
646         glDrawBuffer  (GL_BACK);
647         glReadBuffer  (GL_BACK);
648         envmap = true;
649
650         r_refdef.x = 0;
651         r_refdef.y = 0;
652         r_refdef.width = size;
653         r_refdef.height = size;
654
655         r_refdef.fov_x = 90;
656         r_refdef.fov_y = 90;
657
658         for (i = 0;i < 6;i++)
659         {
660                 VectorCopy(envmapinfo[i].angles, r_refdef.viewangles);
661                 glClearColor(0,0,0,0);
662                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // LordHavoc: clear the screen (around the view as well)
663                 R_RenderView ();
664                 glReadPixels (0, 0, size, size, GL_RGB, GL_UNSIGNED_BYTE, buffer);
665                 sprintf(filename, "env/%s%s.tga", basename, envmapinfo[i].name);
666                 Image_GammaRemapRGB(buffer, buffer, size * size, gamma, gamma, gamma);
667                 Image_WriteTGARGB_preflipped(filename, size, size, buffer);
668         }
669
670         envmap = false;
671         glDrawBuffer  (GL_BACK);
672         glReadBuffer  (GL_BACK);
673
674         Mem_Free(buffer);
675
676         // cause refdef to be fixed
677 //      vid.recalc_refdef = 1;
678 }
679
680 //=============================================================================
681
682
683 /*
684 ===============
685 SCR_BeginLoadingPlaque
686
687 ================
688 */
689 /*
690 void SCR_BeginLoadingPlaque (void)
691 {
692         S_StopAllSounds (true);
693
694 //      if (cls.state != ca_connected)
695 //              return;
696 //      if (cls.signon != SIGNONS)
697 //              return;
698
699 // redraw with no console and the loading plaque
700 //      Con_ClearNotify ();
701 //      scr_centertime_off = 0;
702 //      scr_con_current = 0;
703
704         scr_drawloading = true;
705         SCR_UpdateScreen ();
706
707 //      scr_disabled_for_loading = true;
708 //      scr_disabled_time = realtime;
709 }
710 */
711
712 /*
713 ===============
714 SCR_EndLoadingPlaque
715
716 ================
717 */
718 /*
719 void SCR_EndLoadingPlaque (void)
720 {
721 //      scr_disabled_for_loading = false;
722         scr_drawloading = false;
723         Con_ClearNotify ();
724 }
725 */
726
727 //=============================================================================
728
729 char    *scr_notifystring;
730
731 void SCR_DrawNotifyString (void)
732 {
733         char    *start;
734         int             l;
735         int             x, y;
736
737         start = scr_notifystring;
738
739         y = vid.conheight*0.35;
740
741         do      
742         {
743         // scan the width of the line
744                 for (l=0 ; l<40 ; l++)
745                         if (start[l] == '\n' || !start[l])
746                                 break;
747                 x = (vid.conwidth - l*8)/2;
748                 DrawQ_String (x, y, start, l, 8, 8, 1, 1, 1, 1, 0);
749
750                 y += 8;
751
752                 while (*start && *start != '\n')
753                         start++;
754
755                 if (!*start)
756                         break;
757                 start++;                // skip the \n
758         }
759         while (1);
760 }
761
762 //=============================================================================
763
764 void DrawCrosshair(int num);
765
766 char r_speeds_string[1024];
767 int speedstringcount, r_timereport_active;
768 double r_timereport_temp = 0, r_timereport_current = 0, r_timereport_start = 0;
769
770 void R_TimeReport(char *desc)
771 {
772         char tempbuf[256];
773         int length;
774         int t;
775
776         if (!r_timereport_active)
777                 return;
778
779         r_timereport_temp = r_timereport_current;
780         r_timereport_current = Sys_DoubleTime();
781         t = (int) ((r_timereport_current - r_timereport_temp) * 1000000.0);
782
783         sprintf(tempbuf, "%8i %s", t, desc);
784         length = strlen(tempbuf);
785         while (length < 20)
786                 tempbuf[length++] = ' ';
787         tempbuf[length] = 0;
788         if (speedstringcount + length > (vid.conwidth / 8))
789         {
790                 strcat(r_speeds_string, "\n");
791                 speedstringcount = 0;
792         }
793         // skip the space at the beginning if it's the first on the line
794         if (speedstringcount == 0)
795         {
796                 strcat(r_speeds_string, tempbuf + 1);
797                 speedstringcount = length - 1;
798         }
799         else
800         {
801                 strcat(r_speeds_string, tempbuf);
802                 speedstringcount += length;
803         }
804 }
805
806 void R_TimeReport_Start(void)
807 {
808         r_timereport_active = r_speeds.integer && cl.worldmodel && cls.state == ca_connected;
809         r_speeds_string[0] = 0;
810         if (r_timereport_active)
811         {
812                 speedstringcount = 0;
813                 AngleVectors (r_refdef.viewangles, vpn, NULL, NULL);
814                 //sprintf(r_speeds_string, "org:'%c%6.2f %c%6.2f %c%6.2f' ang:'%c%3.0f %c%3.0f %c%3.0f' dir:'%c%2.3f %c%2.3f %c%2.3f'\n%6i walls %6i dlitwalls %7i modeltris %7i meshtris\nBSP: %6i faces %6i nodes %6i leafs\n%4i models %4i bmodels %4i sprites %5i particles %3i dlights\n",
815                 //      r_refdef.vieworg[0] < 0 ? '-' : ' ', fabs(r_refdef.vieworg[0]), r_refdef.vieworg[1] < 0 ? '-' : ' ', fabs(r_refdef.vieworg[1]), r_refdef.vieworg[2] < 0 ? '-' : ' ', fabs(r_refdef.vieworg[2]),
816                 //      r_refdef.viewangles[0] < 0 ? '-' : ' ', fabs(r_refdef.viewangles[0]), r_refdef.viewangles[1] < 0 ? '-' : ' ', fabs(r_refdef.viewangles[1]), r_refdef.viewangles[2] < 0 ? '-' : ' ', fabs(r_refdef.viewangles[2]),
817                 //      vpn[0] < 0 ? '-' : ' ', fabs(vpn[0]), vpn[1] < 0 ? '-' : ' ', fabs(vpn[1]), vpn[2] < 0 ? '-' : ' ', fabs(vpn[2]),
818                 sprintf(r_speeds_string, "org:'%+8.2f %+8.2f %+8.2f' ang:'%+4.0f %+4.0f %+4.0f' dir:'%+2.3f %+2.3f %+2.3f'\n%6i walls %6i dlitwalls %7i modeltris %7i meshtris\nBSP: %6i faces %6i nodes %6i leafs\n%4i models %4i bmodels %4i sprites %5i particles %3i dlights\n",
819                         r_refdef.vieworg[0], r_refdef.vieworg[1], r_refdef.vieworg[2],
820                         r_refdef.viewangles[0], r_refdef.viewangles[1], r_refdef.viewangles[2],
821                         vpn[0], vpn[1], vpn[2],
822                         c_brush_polys, c_light_polys, c_alias_polys, c_meshtris,
823                         c_faces, c_nodes, c_leafs,
824                         c_models, c_bmodels, c_sprites, c_particles, c_dlights);
825
826                 c_brush_polys = 0;
827                 c_alias_polys = 0;
828                 c_light_polys = 0;
829                 c_faces = 0;
830                 c_nodes = 0;
831                 c_leafs = 0;
832                 c_models = 0;
833                 c_bmodels = 0;
834                 c_sprites = 0;
835                 c_particles = 0;
836         //      c_dlights = 0;
837
838                 r_timereport_start = Sys_DoubleTime();
839         }
840 }
841
842 void R_TimeReport_End(void)
843 {
844         r_timereport_current = r_timereport_start;
845         R_TimeReport("total");
846
847         if (r_timereport_active)
848         {
849                 int i, j, lines, y;
850                 lines = 1;
851                 for (i = 0;r_speeds_string[i];i++)
852                         if (r_speeds_string[i] == '\n')
853                                 lines++;
854                 y = vid.conheight - sb_lines - lines * 8/* - 8*/;
855                 i = j = 0;
856                 DrawQ_Fill(0, y, vid.conwidth, lines * 8, 0, 0, 0, 0.5, 0);
857                 while (r_speeds_string[i])
858                 {
859                         j = i;
860                         while (r_speeds_string[i] && r_speeds_string[i] != '\n')
861                                 i++;
862                         if (i - j > 0)
863                                 DrawQ_String(0, y, r_speeds_string + j, i - j, 8, 8, 1, 1, 1, 1, 0);
864                         if (r_speeds_string[i] == '\n')
865                                 i++;
866                         y += 8;
867                 }
868         }
869 }
870
871 /*
872 ==================
873 SCR_UpdateScreen
874
875 This is called every frame, and can also be called explicitly to flush
876 text to the screen.
877
878 LordHavoc: due to my rewrite of R_WorldNode, it no longer takes 256k of stack space :)
879 ==================
880 */
881 void SCR_UpdateScreen (void)
882 {
883         VID_UpdateGamma(false);
884
885         if (scr_disabled_for_loading)
886                 return;
887
888         if (!scr_initialized || !con_initialized)
889                 return;                         // not initialized yet
890
891         //Mem_CheckSentinelsGlobal();
892         //R_TimeReport("memtest");
893
894         R_TimeReport("other");
895
896         glFinish ();
897         CHECKGLERROR
898
899         GL_EndRendering ();
900
901         R_TimeReport("finish");
902
903         GL_BeginRendering (&vid.realx, &vid.realy, &vid.realwidth, &vid.realheight);
904
905         if (gl_combine.integer && !gl_combine_extension)
906                 Cvar_SetValue("gl_combine", 0);
907
908         lighthalf = gl_lightmode.integer;
909
910         lightscalebit = 0;
911         if (lighthalf)
912                 lightscalebit += 1;
913
914         if (gl_combine.integer && r_multitexture.integer)
915                 lightscalebit += 2;
916
917         lightscale = 1.0f / (float) (1 << lightscalebit);
918
919         R_TimeReport("setup");
920
921         // determine size of refresh window
922         SCR_CalcRefdef();
923
924         R_TimeReport("calcrefdef");
925
926         if (r_render.integer)
927         {
928                 glClearColor(0,0,0,0);
929                 CHECKGLERROR
930                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // LordHavoc: clear the screen (around the view as well)
931                 CHECKGLERROR
932                 if (gl_dither.integer)
933                         glEnable(GL_DITHER);
934                 else
935                         glDisable(GL_DITHER);
936                 CHECKGLERROR
937         }
938
939         SCR_SetUpToDrawConsole();
940
941         R_TimeReport("clear");
942
943         if (scr_conlines < vid.conheight)
944                 R_RenderView();
945
946         SCR_DrawRam();
947         SCR_DrawNet();
948         SCR_DrawTurtle();
949         SCR_DrawPause();
950         SCR_CheckDrawCenterString();
951         Sbar_Draw();
952         SHOWLMP_drawall();
953
954         SCR_DrawConsole();
955         M_Draw();
956
957         ui_draw();
958
959         R_TimeReport("2d");
960
961         R_TimeReport_End();
962
963         // draw 2D stuff
964         R_DrawQueue();
965
966         R_TimeReport_Start();
967 }