dc542a1c23859592c466f8bf2a74535d195f24a7
[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  showfps = {CVAR_SAVE, "showfps", "0"};
86 cvar_t  r_render = {0, "r_render", "1"};
87 cvar_t  r_brightness = {CVAR_SAVE, "r_brightness", "1"}; // LordHavoc: a method of operating system independent color correction
88 cvar_t  r_contrast = {CVAR_SAVE, "r_contrast", "1"}; // LordHavoc: a method of operating system independent color correction
89 cvar_t  gl_dither = {CVAR_SAVE, "gl_dither", "1"}; // whether or not to use dithering
90
91 qboolean        scr_initialized;                // ready to draw
92
93 int                     clearconsole;
94 int                     clearnotify;
95
96 int                     lightscalebit;
97 float           lightscale;
98
99 qboolean        scr_disabled_for_loading;
100 //qboolean      scr_drawloading;
101 //float         scr_disabled_time;
102
103 void SCR_ScreenShot_f (void);
104
105 /*
106 ===============================================================================
107
108 CENTER PRINTING
109
110 ===============================================================================
111 */
112
113 char            scr_centerstring[1024];
114 float           scr_centertime_start;   // for slow victory printing
115 float           scr_centertime_off;
116 int                     scr_center_lines;
117 int                     scr_erase_lines;
118 int                     scr_erase_center;
119
120 /*
121 ==============
122 SCR_CenterPrint
123
124 Called for important messages that should stay in the center of the screen
125 for a few moments
126 ==============
127 */
128 void SCR_CenterPrint (char *str)
129 {
130         strncpy (scr_centerstring, str, sizeof(scr_centerstring)-1);
131         scr_centertime_off = scr_centertime.value;
132         scr_centertime_start = cl.time;
133
134 // count the number of lines for centering
135         scr_center_lines = 1;
136         while (*str)
137         {
138                 if (*str == '\n')
139                         scr_center_lines++;
140                 str++;
141         }
142 }
143
144
145 void SCR_DrawCenterString (void)
146 {
147         char    *start;
148         int             l;
149         int             x, y;
150         int             remaining;
151
152 // the finale prints the characters one at a time
153         if (cl.intermission)
154                 remaining = scr_printspeed.value * (cl.time - scr_centertime_start);
155         else
156                 remaining = 9999;
157
158         scr_erase_center = 0;
159         start = scr_centerstring;
160
161         if (scr_center_lines <= 4)
162                 y = vid.conheight*0.35;
163         else
164                 y = 48;
165
166         do      
167         {
168         // scan the width of the line
169                 for (l=0 ; l<40 ; l++)
170                         if (start[l] == '\n' || !start[l])
171                                 break;
172                 x = (vid.conwidth - l*8)/2;
173                 // LordHavoc: speedup
174                 if (l > 0)
175                 {
176                         if (remaining < l)
177                                 l = remaining;
178                         Draw_String(x, y, start, l);
179                         remaining -= l;
180                         if (remaining <= 0)
181                                 return;
182                 }
183                 /*
184                 for (j=0 ; j<l ; j++, x+=8)
185                 {
186                         Draw_Character (x, y, start[j]);        
187                         if (!remaining--)
188                                 return;
189                 }
190                 */
191                         
192                 y += 8;
193
194                 while (*start && *start != '\n')
195                         start++;
196
197                 if (!*start)
198                         break;
199                 start++;                // skip the \n
200         } while (1);
201 }
202
203 void SCR_CheckDrawCenterString (void)
204 {
205         if (scr_center_lines > scr_erase_lines)
206                 scr_erase_lines = scr_center_lines;
207
208         scr_centertime_off -= host_frametime;
209
210         if (scr_centertime_off <= 0 && !cl.intermission)
211                 return;
212         if (key_dest != key_game)
213                 return;
214
215         SCR_DrawCenterString ();
216 }
217
218 //=============================================================================
219
220 /*
221 ====================
222 CalcFov
223 ====================
224 */
225 float CalcFov (float fov_x, float width, float height)
226 {
227         // calculate vision size and alter by aspect, then convert back to angle
228         return atan (height / (width / tan(fov_x/360*M_PI))) * 360 / M_PI;
229 }
230
231 /*
232 =================
233 SCR_CalcRefdef
234
235 Must be called whenever vid changes
236 Internal use only
237 =================
238 */
239 static void SCR_CalcRefdef (void)
240 {
241         float size;
242
243 //      vid.recalc_refdef = 0;
244
245 //========================================
246
247 // bound viewsize
248         if (scr_viewsize.value < 30)
249                 Cvar_Set ("viewsize","30");
250         if (scr_viewsize.value > 120)
251                 Cvar_Set ("viewsize","120");
252
253 // bound field of view
254         if (scr_fov.value < 10)
255                 Cvar_Set ("fov","10");
256         if (scr_fov.value > 170)
257                 Cvar_Set ("fov","170");
258
259 // intermission is always full screen
260         if (cl.intermission)
261         {
262                 size = 1;
263                 sb_lines = 0;
264         }
265         else
266         {
267                 if (scr_viewsize.value >= 120)
268                         sb_lines = 0;           // no status bar at all
269                 else if (scr_viewsize.value >= 110)
270                         sb_lines = 24;          // no inventory
271                 else
272                         sb_lines = 24+16+8;
273                 size = scr_viewsize.value * (1.0 / 100.0);
274         }
275
276         if (size >= 1)
277         {
278                 r_refdef.width = vid.realwidth;
279                 r_refdef.height = vid.realheight;
280                 r_refdef.x = 0;
281                 r_refdef.y = 0;
282         }
283         else
284         {
285                 r_refdef.width = vid.realwidth * size;
286                 r_refdef.height = vid.realheight * size;
287                 r_refdef.x = (vid.realwidth - r_refdef.width)/2;
288                 r_refdef.y = (vid.realheight - r_refdef.height)/2;
289         }
290
291         r_refdef.fov_x = scr_fov.value;
292         r_refdef.fov_y = CalcFov (r_refdef.fov_x, r_refdef.width, r_refdef.height);
293
294         r_refdef.width = bound(0, r_refdef.width, vid.realwidth);
295         r_refdef.height = bound(0, r_refdef.height, vid.realheight);
296         r_refdef.x = bound(0, r_refdef.x, vid.realwidth) + vid.realx;
297         r_refdef.y = bound(0, r_refdef.y, vid.realheight) + vid.realy;
298 }
299
300
301 /*
302 =================
303 SCR_SizeUp_f
304
305 Keybinding command
306 =================
307 */
308 void SCR_SizeUp_f (void)
309 {
310         Cvar_SetValue ("viewsize",scr_viewsize.value+10);
311 //      vid.recalc_refdef = 1;
312 }
313
314
315 /*
316 =================
317 SCR_SizeDown_f
318
319 Keybinding command
320 =================
321 */
322 void SCR_SizeDown_f (void)
323 {
324         Cvar_SetValue ("viewsize",scr_viewsize.value-10);
325 //      vid.recalc_refdef = 1;
326 }
327
328 //============================================================================
329
330 void gl_screen_start(void)
331 {
332 }
333
334 void gl_screen_shutdown(void)
335 {
336 }
337
338 void gl_screen_newmap(void)
339 {
340 }
341
342 /*
343 ==================
344 SCR_Init
345 ==================
346 */
347 static void R_Envmap_f (void);
348 void GL_Screen_Init (void)
349 {
350         Cvar_RegisterVariable (&scr_fov);
351         Cvar_RegisterVariable (&scr_viewsize);
352         Cvar_RegisterVariable (&scr_conspeed);
353         Cvar_RegisterVariable (&scr_showram);
354         Cvar_RegisterVariable (&scr_showturtle);
355         Cvar_RegisterVariable (&scr_showpause);
356         Cvar_RegisterVariable (&scr_centertime);
357         Cvar_RegisterVariable (&scr_printspeed);
358         Cvar_RegisterVariable (&showfps);
359         Cvar_RegisterVariable (&r_render);
360         Cvar_RegisterVariable (&r_brightness);
361         Cvar_RegisterVariable (&r_contrast);
362         Cvar_RegisterVariable (&gl_dither);
363 #ifdef NORENDER
364         Cvar_SetValue("r_render", 0);
365 #endif
366
367 //
368 // register our commands
369 //
370         Cmd_AddCommand ("screenshot",SCR_ScreenShot_f);
371         Cmd_AddCommand ("envmap", R_Envmap_f);
372         Cmd_AddCommand ("sizeup",SCR_SizeUp_f);
373         Cmd_AddCommand ("sizedown",SCR_SizeDown_f);
374
375         scr_initialized = true;
376
377         R_RegisterModule("GL_Screen", gl_screen_start, gl_screen_shutdown, gl_screen_newmap);
378 }
379
380
381
382 /*
383 ==============
384 SCR_DrawRam
385 ==============
386 */
387 void SCR_DrawRam (void)
388 {
389 //      if (!scr_showram.integer)
390 //              return;
391 //      Draw_Pic (32, 0, Draw_CachePic("ram"));
392 }
393
394 /*
395 ==============
396 SCR_DrawTurtle
397 ==============
398 */
399 void SCR_DrawTurtle (void)
400 {
401         static int      count;
402
403         if (!scr_showturtle.integer)
404                 return;
405
406         if (host_frametime < 0.1)
407         {
408                 count = 0;
409                 return;
410         }
411
412         count++;
413         if (count < 3)
414                 return;
415
416         Draw_Pic (0, 0, Draw_CachePic("turtle"));
417 }
418
419 /*
420 ==============
421 SCR_DrawNet
422 ==============
423 */
424 void SCR_DrawNet (void)
425 {
426         if (realtime - cl.last_received_message < 0.3)
427                 return;
428         if (cls.demoplayback)
429                 return;
430
431         Draw_Pic (64, 0, Draw_CachePic("net"));
432 }
433
434 /*
435 ==============
436 DrawPause
437 ==============
438 */
439 void SCR_DrawPause (void)
440 {
441         qpic_t  *pic;
442
443         if (!scr_showpause.integer)             // turn off for screenshots
444                 return;
445
446         if (!cl.paused)
447                 return;
448
449         pic = Draw_CachePic ("gfx/pause.lmp");
450         Draw_Pic ((vid.conwidth - pic->width)/2, (vid.conheight - pic->height)/2, pic);
451 }
452
453
454
455 /*
456 ==============
457 SCR_DrawLoading
458 ==============
459 */
460 /*
461 void SCR_DrawLoading (void)
462 {
463         qpic_t  *pic;
464
465         if (!scr_drawloading)
466                 return;
467                 
468         pic = Draw_CachePic ("gfx/loading.lmp");
469         Draw_Pic ((vid.conwidth - pic->width)/2, (vid.conheight - pic->height)/2, pic);
470 }
471 */
472
473
474
475 //=============================================================================
476
477
478 /*
479 ==================
480 SCR_SetUpToDrawConsole
481 ==================
482 */
483 void SCR_SetUpToDrawConsole (void)
484 {
485         Con_CheckResize ();
486         
487         //if (scr_drawloading)
488         //      return;         // never a console with loading plaque
489
490 // decide on the height of the console
491         con_forcedup = !cl.worldmodel || cls.signon != SIGNONS;
492
493         if (con_forcedup)
494         {
495                 scr_conlines = vid.conheight;           // full screen
496                 scr_con_current = scr_conlines;
497         }
498         else if (key_dest == key_console)
499                 scr_conlines = vid.conheight/2; // half screen
500         else
501                 scr_conlines = 0;                               // none visible
502
503         if (scr_conlines < scr_con_current)
504         {
505                 scr_con_current -= scr_conspeed.value*host_realframetime;
506                 if (scr_conlines > scr_con_current)
507                         scr_con_current = scr_conlines;
508
509         }
510         else if (scr_conlines > scr_con_current)
511         {
512                 scr_con_current += scr_conspeed.value*host_realframetime;
513                 if (scr_conlines < scr_con_current)
514                         scr_con_current = scr_conlines;
515         }
516 }
517
518 /*
519 ==================
520 SCR_DrawConsole
521 ==================
522 */
523 void SCR_DrawConsole (void)
524 {
525         if (scr_con_current)
526         {
527                 Con_DrawConsole (scr_con_current, true);
528                 clearconsole = 0;
529         }
530         else
531         {
532                 if (key_dest == key_game || key_dest == key_message)
533                         Con_DrawNotify ();      // only draw notify in game
534         }
535 }
536
537
538 /*
539 ============================================================================== 
540
541                                                 SCREEN SHOTS 
542
543 ============================================================================== 
544 */ 
545
546 /*
547 ==================
548 SCR_ScreenShot_f
549 ==================
550 */
551 void SCR_ScreenShot_f (void)
552 {
553         byte            *buffer, gamma[256];
554         char            filename[80];
555         char            checkname[MAX_OSPATH];
556         int                     i;
557 //
558 // find a file name to save it to
559 //
560         strcpy(filename,"dp0000.tga");
561
562         for (i=0 ; i<=9999 ; i++)
563         {
564                 filename[2] = (i/1000)%10 + '0';
565                 filename[3] = (i/ 100)%10 + '0';
566                 filename[4] = (i/  10)%10 + '0';
567                 filename[5] = (i/   1)%10 + '0';
568                 sprintf (checkname, "%s/%s", com_gamedir, filename);
569                 if (Sys_FileTime(checkname) == -1)
570                         break;  // file doesn't exist
571         }
572         if (i==10000)
573         {
574                 Con_Printf ("SCR_ScreenShot_f: Couldn't create a TGA file\n");
575                 return;
576         }
577
578         buffer = Mem_Alloc(tempmempool, vid.realwidth*vid.realheight*3);
579         glReadPixels (vid.realx, vid.realy, vid.realwidth, vid.realheight, GL_RGB, GL_UNSIGNED_BYTE, buffer);
580         CHECKGLERROR
581
582         // apply hardware gamma to the image
583         BuildGammaTable8((lighthalf && hardwaregammasupported) ? 2.0f : 1.0f, 1, 1, 0, gamma);
584         Image_GammaRemapRGB(buffer, buffer, vid.realwidth*vid.realheight, gamma, gamma, gamma);
585
586         Image_WriteTGARGB_preflipped(filename, vid.realwidth, vid.realheight, buffer);
587
588         Mem_Free(buffer);
589         Con_Printf ("Wrote %s\n", filename);
590 }
591
592 /*
593 ===============
594 R_Envmap_f
595
596 Grab six views for environment mapping tests
597 ===============
598 */
599 float CalcFov (float fov_x, float width, float height);
600 struct
601 {
602         float angles[3];
603         char *name;
604 }
605 envmapinfo[6] =
606 {
607         {{  0,   0, 0}, "ft"},
608         {{  0,  90, 0}, "rt"},
609         {{  0, 180, 0}, "bk"},
610         {{  0, 270, 0}, "lf"},
611         {{-90,  90, 0}, "up"},
612         {{ 90,  90, 0}, "dn"}
613 };
614 static void R_Envmap_f (void)
615 {
616         int             i, size;
617         char    filename[256];
618         char    basename[256];
619         byte    *buffer, gamma[256];
620
621         if (Cmd_Argc() != 3)
622         {
623                 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");
624                 return;
625         }
626
627         if (!r_render.integer)
628                 return;
629
630         strcpy(basename, Cmd_Argv(1));
631         size = atoi(Cmd_Argv(2));
632         if (size != 128 && size != 256 && size != 512 && size != 1024)
633         {
634                 Con_Printf("envmap: size must be one of 128, 256, 512, or 1024\n");
635                 return;
636         }
637         if (size > vid.realwidth || size > vid.realheight)
638         {
639                 Con_Printf("envmap: your resolution is not big enough to render that size\n");
640                 return;
641         }
642
643         buffer = Mem_Alloc(tempmempool, size*size*3);
644         if (buffer == NULL)
645         {
646                 Con_Printf("envmap: unable to allocate memory for image\n");
647                 return;
648         }
649
650         BuildGammaTable8((lighthalf && hardwaregammasupported) ? 2.0f : 1.0f, 1, 1, 0, gamma);
651
652 //      glDrawBuffer  (GL_FRONT);
653 //      glReadBuffer  (GL_FRONT);
654         glDrawBuffer  (GL_BACK);
655         glReadBuffer  (GL_BACK);
656         envmap = true;
657
658         r_refdef.x = 0;
659         r_refdef.y = 0;
660         r_refdef.width = size;
661         r_refdef.height = size;
662
663         r_refdef.fov_x = 90;
664         r_refdef.fov_y = 90;
665
666         for (i = 0;i < 6;i++)
667         {
668                 VectorCopy(envmapinfo[i].angles, r_refdef.viewangles);
669                 glClearColor(0,0,0,0);
670                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // LordHavoc: clear the screen (around the view as well)
671                 R_RenderView ();
672                 glReadPixels (0, 0, size, size, GL_RGB, GL_UNSIGNED_BYTE, buffer);
673                 sprintf(filename, "env/%s%s.tga", basename, envmapinfo[i].name);
674                 Image_GammaRemapRGB(buffer, buffer, size * size, gamma, gamma, gamma);
675                 Image_WriteTGARGB_preflipped(filename, size, size, buffer);
676         }
677
678         envmap = false;
679         glDrawBuffer  (GL_BACK);
680         glReadBuffer  (GL_BACK);
681
682         Mem_Free(buffer);
683
684         // cause refdef to be fixed
685 //      vid.recalc_refdef = 1;
686 }
687
688 //=============================================================================
689
690
691 /*
692 ===============
693 SCR_BeginLoadingPlaque
694
695 ================
696 */
697 /*
698 void SCR_BeginLoadingPlaque (void)
699 {
700         S_StopAllSounds (true);
701
702 //      if (cls.state != ca_connected)
703 //              return;
704 //      if (cls.signon != SIGNONS)
705 //              return;
706
707 // redraw with no console and the loading plaque
708 //      Con_ClearNotify ();
709 //      scr_centertime_off = 0;
710 //      scr_con_current = 0;
711
712         scr_drawloading = true;
713         SCR_UpdateScreen ();
714
715 //      scr_disabled_for_loading = true;
716 //      scr_disabled_time = realtime;
717 }
718 */
719
720 /*
721 ===============
722 SCR_EndLoadingPlaque
723
724 ================
725 */
726 /*
727 void SCR_EndLoadingPlaque (void)
728 {
729 //      scr_disabled_for_loading = false;
730         scr_drawloading = false;
731         Con_ClearNotify ();
732 }
733 */
734
735 //=============================================================================
736
737 char    *scr_notifystring;
738
739 void SCR_DrawNotifyString (void)
740 {
741         char    *start;
742         int             l;
743         int             x, y;
744
745         start = scr_notifystring;
746
747         y = vid.conheight*0.35;
748
749         do      
750         {
751         // scan the width of the line
752                 for (l=0 ; l<40 ; l++)
753                         if (start[l] == '\n' || !start[l])
754                                 break;
755                 x = (vid.conwidth - l*8)/2;
756                 // LordHavoc: speedup
757 //              for (j=0 ; j<l ; j++, x+=8)
758 //                      Draw_Character (x, y, start[j]);
759                 Draw_String (x, y, start, l);
760
761                 y += 8;
762
763                 while (*start && *start != '\n')
764                         start++;
765
766                 if (!*start)
767                         break;
768                 start++;                // skip the \n
769         } while (1);
770 }
771
772 //=============================================================================
773
774 void DrawCrosshair(int num);
775 void GL_Set2D (void);
776
777 void GL_BrightenScreen(void)
778 {
779         float f;
780
781         if (r_brightness.value < 0.1f)
782                 Cvar_SetValue("r_brightness", 0.1f);
783         if (r_brightness.value > 5.0f)
784                 Cvar_SetValue("r_brightness", 5.0f);
785
786         if (r_contrast.value < 0.2f)
787                 Cvar_SetValue("r_contrast", 0.2f);
788         if (r_contrast.value > 1.0f)
789                 Cvar_SetValue("r_contrast", 1.0f);
790
791         if (!(lighthalf && !hardwaregammasupported) && r_brightness.value < 1.01f && r_contrast.value > 0.99f)
792                 return;
793
794         if (!r_render.integer)
795                 return;
796
797         glDisable(GL_TEXTURE_2D);
798         CHECKGLERROR
799         glEnable(GL_BLEND);
800         CHECKGLERROR
801         f = r_brightness.value;
802         // only apply lighthalf using software color correction if hardware is not available (speed reasons)
803         if (lighthalf && !hardwaregammasupported)
804                 f *= 2;
805         if (f >= 1.01f)
806         {
807                 glBlendFunc (GL_DST_COLOR, GL_ONE);
808                 CHECKGLERROR
809                 glBegin (GL_TRIANGLES);
810                 while (f >= 1.01f)
811                 {
812                         if (f >= 2)
813                                 glColor3f (1, 1, 1);
814                         else
815                                 glColor3f (f-1, f-1, f-1);
816                         glVertex2f (-5000, -5000);
817                         glVertex2f (10000, -5000);
818                         glVertex2f (-5000, 10000);
819                         f *= 0.5;
820                 }
821                 glEnd ();
822                 CHECKGLERROR
823         }
824         if (r_contrast.value <= 0.99f)
825         {
826                 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
827                 CHECKGLERROR
828                 if (lighthalf && hardwaregammasupported)
829                         glColor4f (0.5, 0.5, 0.5, 1 - r_contrast.value);
830                 else
831                         glColor4f (1, 1, 1, 1 - r_contrast.value);
832                 CHECKGLERROR
833                 glBegin (GL_TRIANGLES);
834                 glVertex2f (-5000, -5000);
835                 glVertex2f (10000, -5000);
836                 glVertex2f (-5000, 10000);
837                 glEnd ();
838                 CHECKGLERROR
839         }
840         glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
841         CHECKGLERROR
842
843         glEnable (GL_CULL_FACE);
844         CHECKGLERROR
845         glEnable (GL_DEPTH_TEST);
846         CHECKGLERROR
847         glDisable(GL_BLEND);
848         CHECKGLERROR
849         glEnable(GL_TEXTURE_2D);
850         CHECKGLERROR
851 }
852
853 char r_speeds2_string[1024];
854 int speedstringcount, r_timereport_active;
855 double r_timereport_temp = 0, r_timereport_current = 0, r_timereport_start = 0;
856
857 void R_TimeReport(char *desc)
858 {
859         char tempbuf[256];
860         int length;
861         int t;
862
863         if (!r_timereport_active)
864                 return;
865
866         r_timereport_temp = r_timereport_current;
867         r_timereport_current = Sys_DoubleTime();
868         t = (int) ((r_timereport_current - r_timereport_temp) * 1000000.0);
869
870         sprintf(tempbuf, "%8i %s", t, desc);
871         length = strlen(tempbuf);
872         while (length < 20)
873                 tempbuf[length++] = ' ';
874         tempbuf[length] = 0;
875         if (speedstringcount + length > (vid.conwidth / 8))
876         {
877                 strcat(r_speeds2_string, "\n");
878                 speedstringcount = 0;
879         }
880         // skip the space at the beginning if it's the first on the line
881         if (speedstringcount == 0)
882         {
883                 strcat(r_speeds2_string, tempbuf + 1);
884                 speedstringcount = length - 1;
885         }
886         else
887         {
888                 strcat(r_speeds2_string, tempbuf);
889                 speedstringcount += length;
890         }
891 }
892
893 void R_TimeReport_Start(void)
894 {
895         r_timereport_active = r_speeds2.integer && cl.worldmodel && cls.state == ca_connected;
896         if (r_timereport_active)
897                 r_timereport_start = Sys_DoubleTime();
898 }
899
900 void R_TimeReport_End(void)
901 {
902         r_timereport_current = r_timereport_start;
903         R_TimeReport("total");
904 }
905
906 /*
907 ==================
908 SCR_UpdateScreen
909
910 This is called every frame, and can also be called explicitly to flush
911 text to the screen.
912
913 LordHavoc: due to my rewrite of R_WorldNode, it no longer takes 256k of stack space :)
914 ==================
915 */
916 void GL_Finish(void);
917 void R_Clip_DisplayBuffer(void);
918 void SCR_UpdateScreen (void)
919 {
920         double  time1 = 0, time2;
921
922         if (r_speeds.integer)
923                 time1 = Sys_DoubleTime ();
924
925         VID_UpdateGamma(false);
926
927         if (scr_disabled_for_loading)
928         {
929                 /*
930                 if (realtime - scr_disabled_time > 60)
931                 {
932                         scr_disabled_for_loading = false;
933                         Con_Printf ("load failed.\n");
934                 }
935                 else
936                 */
937                         return;
938         }
939
940         if (!scr_initialized || !con_initialized)
941                 return;                         // not initialized yet
942
943         r_speeds2_string[0] = 0;
944         if (r_speeds2.integer)
945         {
946                 speedstringcount = 0;
947                 sprintf(r_speeds2_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",
948                         r_origin[0] < 0 ? '-' : ' ', fabs(r_origin[0]), r_origin[1] < 0 ? '-' : ' ', fabs(r_origin[1]), r_origin[2] < 0 ? '-' : ' ', fabs(r_origin[2]), 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]), vpn[0] < 0 ? '-' : ' ', fabs(vpn[0]), vpn[1] < 0 ? '-' : ' ', fabs(vpn[1]), vpn[2] < 0 ? '-' : ' ', fabs(vpn[2]),
949                         c_brush_polys, c_light_polys, c_alias_polys, c_meshtris,
950                         c_faces, c_nodes, c_leafs,
951                         c_models, c_bmodels, c_sprites, c_particles, c_dlights);
952                 R_TimeReport_Start();
953         }
954
955         Mem_CheckSentinelsGlobal();
956         R_TimeReport("memtest");
957
958         GL_Finish();
959         GL_EndRendering ();
960
961         R_TimeReport("finish");
962
963         GL_BeginRendering (&vid.realx, &vid.realy, &vid.realwidth, &vid.realheight);
964
965         if (gl_combine.integer && !gl_combine_extension)
966                 Cvar_SetValue("gl_combine", 0);
967
968         lighthalf = gl_lightmode.integer;
969
970         lightscalebit = 0;
971         if (lighthalf)
972                 lightscalebit += 1;
973
974         if (gl_combine.integer && r_multitexture.integer)
975                 lightscalebit += 2;
976
977         lightscale = 1.0f / (float) (1 << lightscalebit);
978
979         //
980         // determine size of refresh window
981         //
982         if (oldfov != scr_fov.value)
983         {
984                 oldfov = scr_fov.value;
985 //              vid.recalc_refdef = true;
986         }
987
988         if (oldscreensize != scr_viewsize.value)
989         {
990                 oldscreensize = scr_viewsize.value;
991 //              vid.recalc_refdef = true;
992         }
993
994 //      if (vid.recalc_refdef)
995                 SCR_CalcRefdef();
996
997         R_TimeReport("calcrefdef");
998
999         if (r_render.integer)
1000         {
1001                 glClearColor(0,0,0,0);
1002                 CHECKGLERROR
1003                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // LordHavoc: clear the screen (around the view as well)
1004                 CHECKGLERROR
1005         }
1006
1007         R_TimeReport("clear");
1008
1009         if (gl_dither.integer)
1010                 glEnable(GL_DITHER);
1011         else
1012                 glDisable(GL_DITHER);
1013         CHECKGLERROR
1014
1015 //
1016 // do 3D refresh drawing, and then update the screen
1017 //
1018         SCR_SetUpToDrawConsole();
1019
1020         R_TimeReport("setupconsole");
1021
1022         V_RenderView();
1023
1024         V_UpdateBlends();
1025
1026         GL_Set2D();
1027
1028         R_Clip_DisplayBuffer();
1029
1030         SCR_DrawRam();
1031         SCR_DrawNet();
1032         SCR_DrawTurtle();
1033         SCR_DrawPause();
1034         SCR_CheckDrawCenterString();
1035         Sbar_Draw();
1036         SHOWLMP_drawall();
1037
1038         if (crosshair.integer)
1039                 DrawCrosshair(crosshair.integer - 1);
1040
1041         if (cl.intermission == 1)
1042                 Sbar_IntermissionOverlay();
1043         else if (cl.intermission == 2)
1044                 Sbar_FinaleOverlay();
1045
1046         SCR_DrawConsole();
1047         M_Draw();
1048
1049         ui_draw();
1050
1051 //      if (scr_drawloading)
1052 //              SCR_DrawLoading();
1053
1054         if (showfps.integer)
1055         {
1056                 static double currtime, frametimes[32];
1057                 double newtime, total;
1058                 char temp[32];
1059                 int calc, count, i;
1060                 static int framecycle = 0;
1061                 newtime = Sys_DoubleTime();
1062                 frametimes[framecycle] = newtime - currtime;
1063                 framecycle++;
1064                 framecycle &= 31;
1065                 total = 0;
1066                 count = 0;
1067                 for (i = 0;i < 32;i++)
1068                 {
1069                         if (frametimes[i])
1070                         {
1071                                 total += frametimes[i];
1072                                 count++;
1073                                 // limit how far back we look
1074                                 if (total >= 0.25)
1075                                         break;
1076                         }
1077                 }
1078                 if (showfps.integer == 1)
1079                         calc = (int) ((count / total) + 0.5);
1080                 else // showfps 2, rapid update
1081                         calc = (int) ((1.0 / (newtime - currtime)) + 0.5);
1082                 sprintf(temp, "%4i fps", calc);
1083                 currtime = newtime;
1084                 Draw_String(vid.conwidth - (8*8), vid.conheight - sb_lines - 8, temp, 9999);
1085         }
1086
1087         R_TimeReport("2d");
1088
1089         R_TimeReport_End();
1090
1091         if (r_speeds2_string[0] && cls.state == ca_connected && cl.worldmodel)
1092         {
1093                 int i, j, lines, y;
1094                 lines = 1;
1095                 for (i = 0;r_speeds2_string[i];i++)
1096                         if (r_speeds2_string[i] == '\n')
1097                                 lines++;
1098                 y = vid.conheight - sb_lines - lines * 8 - 8;
1099                 i = j = 0;
1100                 while (r_speeds2_string[i])
1101                 {
1102                         j = i;
1103                         while (r_speeds2_string[i] && r_speeds2_string[i] != '\n')
1104                                 i++;
1105                         if (i - j > 0)
1106                                 Draw_String(0, y, r_speeds2_string + j, i - j);
1107                         if (r_speeds2_string[i] == '\n')
1108                                 i++;
1109                         y += 8;
1110                 }
1111         }
1112
1113         GL_BrightenScreen();
1114
1115         if (r_speeds.integer)
1116         {
1117                 time2 = Sys_DoubleTime ();
1118                 Con_Printf ("%3i ms  %4i wpoly %4i epoly %6i meshtris %4i lightpoly %4i BSPnodes %4i BSPleafs %4i BSPfaces %4i models %4i bmodels %4i sprites %4i particles %3i dlights\n", (int)((time2-time1)*1000), c_brush_polys, c_alias_polys, c_meshtris, c_light_polys, c_nodes, c_leafs, c_faces, c_models, c_bmodels, c_sprites, c_particles, c_dlights);
1119         }
1120 }
1121
1122 // for profiling, this is separated
1123 void GL_Finish(void)
1124 {
1125         if (!r_render.integer)
1126                 return;
1127         glFinish ();
1128         CHECKGLERROR
1129 }
1130