rewrote RecursiveHullCheck, no longer gets stuck on angle changes, and is generally...
[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
90 qboolean        scr_initialized;                // ready to draw
91
92 qpic_t          *scr_ram;
93 qpic_t          *scr_net;
94 qpic_t          *scr_turtle;
95
96 int                     clearconsole;
97 int                     clearnotify;
98
99 int                     lightscalebit;
100 float           lightscale;
101
102 qboolean        scr_disabled_for_loading;
103 //qboolean      scr_drawloading;
104 //float         scr_disabled_time;
105
106 void SCR_ScreenShot_f (void);
107
108 /*
109 ===============================================================================
110
111 CENTER PRINTING
112
113 ===============================================================================
114 */
115
116 char            scr_centerstring[1024];
117 float           scr_centertime_start;   // for slow victory printing
118 float           scr_centertime_off;
119 int                     scr_center_lines;
120 int                     scr_erase_lines;
121 int                     scr_erase_center;
122
123 /*
124 ==============
125 SCR_CenterPrint
126
127 Called for important messages that should stay in the center of the screen
128 for a few moments
129 ==============
130 */
131 void SCR_CenterPrint (char *str)
132 {
133         strncpy (scr_centerstring, str, sizeof(scr_centerstring)-1);
134         scr_centertime_off = scr_centertime.value;
135         scr_centertime_start = cl.time;
136
137 // count the number of lines for centering
138         scr_center_lines = 1;
139         while (*str)
140         {
141                 if (*str == '\n')
142                         scr_center_lines++;
143                 str++;
144         }
145 }
146
147
148 void SCR_DrawCenterString (void)
149 {
150         char    *start;
151         int             l;
152         int             x, y;
153         int             remaining;
154
155 // the finale prints the characters one at a time
156         if (cl.intermission)
157                 remaining = scr_printspeed.value * (cl.time - scr_centertime_start);
158         else
159                 remaining = 9999;
160
161         scr_erase_center = 0;
162         start = scr_centerstring;
163
164         if (scr_center_lines <= 4)
165                 y = vid.conheight*0.35;
166         else
167                 y = 48;
168
169         do      
170         {
171         // scan the width of the line
172                 for (l=0 ; l<40 ; l++)
173                         if (start[l] == '\n' || !start[l])
174                                 break;
175                 x = (vid.conwidth - l*8)/2;
176                 // LordHavoc: speedup
177                 if (l > 0)
178                 {
179                         if (remaining < l)
180                                 l = remaining;
181                         Draw_String(x, y, start, l);
182                         remaining -= l;
183                         if (remaining <= 0)
184                                 return;
185                 }
186                 /*
187                 for (j=0 ; j<l ; j++, x+=8)
188                 {
189                         Draw_Character (x, y, start[j]);        
190                         if (!remaining--)
191                                 return;
192                 }
193                 */
194                         
195                 y += 8;
196
197                 while (*start && *start != '\n')
198                         start++;
199
200                 if (!*start)
201                         break;
202                 start++;                // skip the \n
203         } while (1);
204 }
205
206 void SCR_CheckDrawCenterString (void)
207 {
208         if (scr_center_lines > scr_erase_lines)
209                 scr_erase_lines = scr_center_lines;
210
211         scr_centertime_off -= host_frametime;
212
213         if (scr_centertime_off <= 0 && !cl.intermission)
214                 return;
215         if (key_dest != key_game)
216                 return;
217
218         SCR_DrawCenterString ();
219 }
220
221 //=============================================================================
222
223 /*
224 ====================
225 CalcFov
226 ====================
227 */
228 float CalcFov (float fov_x, float width, float height)
229 {
230         // calculate vision size and alter by aspect, then convert back to angle
231         return atan (height / (width / tan(fov_x/360*M_PI))) * 360 / M_PI;
232 }
233
234 /*
235 =================
236 SCR_CalcRefdef
237
238 Must be called whenever vid changes
239 Internal use only
240 =================
241 */
242 static void SCR_CalcRefdef (void)
243 {
244         float size;
245
246 //      vid.recalc_refdef = 0;
247
248 //========================================
249
250 // bound viewsize
251         if (scr_viewsize.value < 30)
252                 Cvar_Set ("viewsize","30");
253         if (scr_viewsize.value > 120)
254                 Cvar_Set ("viewsize","120");
255
256 // bound field of view
257         if (scr_fov.value < 10)
258                 Cvar_Set ("fov","10");
259         if (scr_fov.value > 170)
260                 Cvar_Set ("fov","170");
261
262 // intermission is always full screen
263         if (cl.intermission)
264         {
265                 size = 1;
266                 sb_lines = 0;
267         }
268         else
269         {
270                 if (scr_viewsize.value >= 120)
271                         sb_lines = 0;           // no status bar at all
272                 else if (scr_viewsize.value >= 110)
273                         sb_lines = 24;          // no inventory
274                 else
275                         sb_lines = 24+16+8;
276                 size = scr_viewsize.value * (1.0 / 100.0);
277         }
278
279         if (size >= 1)
280         {
281                 r_refdef.width = vid.realwidth;
282                 r_refdef.height = vid.realheight;
283                 r_refdef.x = 0;
284                 r_refdef.y = 0;
285         }
286         else
287         {
288                 r_refdef.width = vid.realwidth * size;
289                 r_refdef.height = vid.realheight * size;
290                 r_refdef.x = (vid.realwidth - r_refdef.width)/2;
291                 r_refdef.y = (vid.realheight - r_refdef.height)/2;
292         }
293
294         r_refdef.fov_x = scr_fov.value;
295         r_refdef.fov_y = CalcFov (r_refdef.fov_x, r_refdef.width, r_refdef.height);
296
297         r_refdef.width = bound(0, r_refdef.width, vid.realwidth);
298         r_refdef.height = bound(0, r_refdef.height, vid.realheight);
299         r_refdef.x = bound(0, r_refdef.x, vid.realwidth) + vid.realx;
300         r_refdef.y = bound(0, r_refdef.y, vid.realheight) + vid.realy;
301 }
302
303
304 /*
305 =================
306 SCR_SizeUp_f
307
308 Keybinding command
309 =================
310 */
311 void SCR_SizeUp_f (void)
312 {
313         Cvar_SetValue ("viewsize",scr_viewsize.value+10);
314 //      vid.recalc_refdef = 1;
315 }
316
317
318 /*
319 =================
320 SCR_SizeDown_f
321
322 Keybinding command
323 =================
324 */
325 void SCR_SizeDown_f (void)
326 {
327         Cvar_SetValue ("viewsize",scr_viewsize.value-10);
328 //      vid.recalc_refdef = 1;
329 }
330
331 //============================================================================
332
333 void gl_screen_start(void)
334 {
335         scr_ram = Draw_PicFromWad ("ram");
336         scr_net = Draw_PicFromWad ("net");
337         scr_turtle = Draw_PicFromWad ("turtle");
338 }
339
340 void gl_screen_shutdown(void)
341 {
342 }
343
344 void gl_screen_newmap(void)
345 {
346 }
347
348 /*
349 ==================
350 SCR_Init
351 ==================
352 */
353 void GL_Screen_Init (void)
354 {
355
356         Cvar_RegisterVariable (&scr_fov);
357         Cvar_RegisterVariable (&scr_viewsize);
358         Cvar_RegisterVariable (&scr_conspeed);
359         Cvar_RegisterVariable (&scr_showram);
360         Cvar_RegisterVariable (&scr_showturtle);
361         Cvar_RegisterVariable (&scr_showpause);
362         Cvar_RegisterVariable (&scr_centertime);
363         Cvar_RegisterVariable (&scr_printspeed);
364         Cvar_RegisterVariable (&showfps);
365         Cvar_RegisterVariable (&r_render);
366         Cvar_RegisterVariable (&r_brightness);
367         Cvar_RegisterVariable (&r_contrast);
368 #ifdef NORENDER
369         r_render.value = 0;
370 #endif
371
372 //
373 // register our commands
374 //
375         Cmd_AddCommand ("screenshot",SCR_ScreenShot_f);
376         Cmd_AddCommand ("sizeup",SCR_SizeUp_f);
377         Cmd_AddCommand ("sizedown",SCR_SizeDown_f);
378
379         scr_initialized = true;
380
381         R_RegisterModule("GL_Screen", gl_screen_start, gl_screen_shutdown, gl_screen_newmap);
382 }
383
384
385
386 /*
387 ==============
388 SCR_DrawRam
389 ==============
390 */
391 void SCR_DrawRam (void)
392 {
393 //      if (!scr_showram.value)
394 //              return;
395 //      Draw_Pic (32, 0, scr_ram);
396 }
397
398 /*
399 ==============
400 SCR_DrawTurtle
401 ==============
402 */
403 void SCR_DrawTurtle (void)
404 {
405         static int      count;
406         
407         if (!scr_showturtle.value)
408                 return;
409
410         if (cl.frametime < 0.1)
411         {
412                 count = 0;
413                 return;
414         }
415
416         count++;
417         if (count < 3)
418                 return;
419
420         Draw_Pic (0, 0, scr_turtle);
421 }
422
423 /*
424 ==============
425 SCR_DrawNet
426 ==============
427 */
428 void SCR_DrawNet (void)
429 {
430         if (realtime - cl.last_received_message < 0.3)
431                 return;
432         if (cls.demoplayback)
433                 return;
434
435         Draw_Pic (64, 0, scr_net);
436 }
437
438 /*
439 ==============
440 DrawPause
441 ==============
442 */
443 void SCR_DrawPause (void)
444 {
445         qpic_t  *pic;
446
447         if (!scr_showpause.value)               // turn off for screenshots
448                 return;
449
450         if (!cl.paused)
451                 return;
452
453         pic = Draw_CachePic ("gfx/pause.lmp");
454         Draw_Pic ((vid.conwidth - pic->width)/2, (vid.conheight - pic->height)/2, pic);
455 }
456
457
458
459 /*
460 ==============
461 SCR_DrawLoading
462 ==============
463 */
464 /*
465 void SCR_DrawLoading (void)
466 {
467         qpic_t  *pic;
468
469         if (!scr_drawloading)
470                 return;
471                 
472         pic = Draw_CachePic ("gfx/loading.lmp");
473         Draw_Pic ((vid.conwidth - pic->width)/2, (vid.conheight - pic->height)/2, pic);
474 }
475 */
476
477
478
479 //=============================================================================
480
481
482 /*
483 ==================
484 SCR_SetUpToDrawConsole
485 ==================
486 */
487 void SCR_SetUpToDrawConsole (void)
488 {
489         Con_CheckResize ();
490         
491         //if (scr_drawloading)
492         //      return;         // never a console with loading plaque
493
494 // decide on the height of the console
495         con_forcedup = !cl.worldmodel || cls.signon != SIGNONS;
496
497         if (con_forcedup)
498         {
499                 scr_conlines = vid.conheight;           // full screen
500                 scr_con_current = scr_conlines;
501         }
502         else if (key_dest == key_console)
503                 scr_conlines = vid.conheight/2; // half screen
504         else
505                 scr_conlines = 0;                               // none visible
506
507         if (scr_conlines < scr_con_current)
508         {
509                 scr_con_current -= scr_conspeed.value*host_realframetime;
510                 if (scr_conlines > scr_con_current)
511                         scr_con_current = scr_conlines;
512
513         }
514         else if (scr_conlines > scr_con_current)
515         {
516                 scr_con_current += scr_conspeed.value*host_realframetime;
517                 if (scr_conlines < scr_con_current)
518                         scr_con_current = scr_conlines;
519         }
520 }
521
522 /*
523 ==================
524 SCR_DrawConsole
525 ==================
526 */
527 void SCR_DrawConsole (void)
528 {
529         if (scr_con_current)
530         {
531                 Con_DrawConsole (scr_con_current, true);
532                 clearconsole = 0;
533         }
534         else
535         {
536                 if (key_dest == key_game || key_dest == key_message)
537                         Con_DrawNotify ();      // only draw notify in game
538         }
539 }
540
541
542 /*
543 ============================================================================== 
544
545                                                 SCREEN SHOTS 
546  
547 ============================================================================== 
548 */ 
549
550 /*
551 ==================
552 SCR_ScreenShot_f
553 ==================
554 */
555 void SCR_ScreenShot_f (void)
556 {
557         byte            *buffer, gamma[256];
558         char            filename[80];
559         char            checkname[MAX_OSPATH];
560         int                     i;
561 //
562 // find a file name to save it to
563 //
564         strcpy(filename,"dp0000.tga");
565
566         for (i=0 ; i<=9999 ; i++)
567         {
568                 filename[2] = (i/1000)%10 + '0';
569                 filename[3] = (i/ 100)%10 + '0';
570                 filename[4] = (i/  10)%10 + '0';
571                 filename[5] = (i/   1)%10 + '0';
572                 sprintf (checkname, "%s/%s", com_gamedir, filename);
573                 if (Sys_FileTime(checkname) == -1)
574                         break;  // file doesn't exist
575         }
576         if (i==10000)
577         {
578                 Con_Printf ("SCR_ScreenShot_f: Couldn't create a TGA file\n");
579                 return;
580         }
581
582         buffer = qmalloc(vid.realwidth*vid.realheight*3);
583         glReadPixels (vid.realx, vid.realy, vid.realwidth, vid.realheight, GL_RGB, GL_UNSIGNED_BYTE, buffer);
584
585         // apply hardware gamma to the image
586         BuildGammaTable8((lighthalf && hardwaregammasupported) ? 2.0f : 1.0f, 1, 1, 0, gamma);
587         Image_GammaRemapRGB(buffer, buffer, vid.realwidth*vid.realheight, gamma, gamma, gamma);
588
589         Image_WriteTGARGB_preflipped(filename, vid.realwidth, vid.realheight, buffer);
590
591         qfree(buffer);
592         Con_Printf ("Wrote %s\n", filename);
593 }
594
595
596 //=============================================================================
597
598
599 /*
600 ===============
601 SCR_BeginLoadingPlaque
602
603 ================
604 */
605 /*
606 void SCR_BeginLoadingPlaque (void)
607 {
608         S_StopAllSounds (true);
609
610 //      if (cls.state != ca_connected)
611 //              return;
612 //      if (cls.signon != SIGNONS)
613 //              return;
614
615 // redraw with no console and the loading plaque
616 //      Con_ClearNotify ();
617 //      scr_centertime_off = 0;
618 //      scr_con_current = 0;
619
620         scr_drawloading = true;
621         SCR_UpdateScreen ();
622
623 //      scr_disabled_for_loading = true;
624 //      scr_disabled_time = realtime;
625 }
626 */
627
628 /*
629 ===============
630 SCR_EndLoadingPlaque
631
632 ================
633 */
634 /*
635 void SCR_EndLoadingPlaque (void)
636 {
637 //      scr_disabled_for_loading = false;
638         scr_drawloading = false;
639         Con_ClearNotify ();
640 }
641 */
642
643 //=============================================================================
644
645 char    *scr_notifystring;
646
647 void SCR_DrawNotifyString (void)
648 {
649         char    *start;
650         int             l;
651         int             x, y;
652
653         start = scr_notifystring;
654
655         y = vid.conheight*0.35;
656
657         do      
658         {
659         // scan the width of the line
660                 for (l=0 ; l<40 ; l++)
661                         if (start[l] == '\n' || !start[l])
662                                 break;
663                 x = (vid.conwidth - l*8)/2;
664                 // LordHavoc: speedup
665 //              for (j=0 ; j<l ; j++, x+=8)
666 //                      Draw_Character (x, y, start[j]);
667                 Draw_String (x, y, start, l);
668
669                 y += 8;
670
671                 while (*start && *start != '\n')
672                         start++;
673
674                 if (!*start)
675                         break;
676                 start++;                // skip the \n
677         } while (1);
678 }
679
680 //=============================================================================
681
682 void DrawCrosshair(int num);
683 void GL_Set2D (void);
684
685 void GL_BrightenScreen(void)
686 {
687         float f;
688
689         if (r_brightness.value < 0.1f)
690                 Cvar_SetValue("r_brightness", 0.1f);
691         if (r_brightness.value > 5.0f)
692                 Cvar_SetValue("r_brightness", 5.0f);
693
694         if (r_contrast.value < 0.2f)
695                 Cvar_SetValue("r_contrast", 0.2f);
696         if (r_contrast.value > 1.0f)
697                 Cvar_SetValue("r_contrast", 1.0f);
698
699         if (!(lighthalf && !hardwaregammasupported) && r_brightness.value < 1.01f && r_contrast.value > 0.99f)
700                 return;
701
702         if (!r_render.value)
703                 return;
704
705         glDisable(GL_TEXTURE_2D);
706         glEnable(GL_BLEND);
707         f = r_brightness.value;
708         // only apply lighthalf using software color correction if hardware is not available (speed reasons)
709         if (lighthalf && !hardwaregammasupported)
710                 f *= 2;
711         if (f >= 1.01f)
712         {
713                 glBlendFunc (GL_DST_COLOR, GL_ONE);
714                 glBegin (GL_TRIANGLES);
715                 while (f >= 1.01f)
716                 {
717                         if (f >= 2)
718                                 glColor3f (1, 1, 1);
719                         else
720                                 glColor3f (f-1, f-1, f-1);
721                         glVertex2f (-5000, -5000);
722                         glVertex2f (10000, -5000);
723                         glVertex2f (-5000, 10000);
724                         f *= 0.5;
725                 }
726                 glEnd ();
727         }
728         if (r_contrast.value <= 0.99f)
729         {
730                 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
731                 if (lighthalf && hardwaregammasupported)
732                         glColor4f (0.5, 0.5, 0.5, 1 - r_contrast.value);
733                 else
734                         glColor4f (1, 1, 1, 1 - r_contrast.value);
735                 glBegin (GL_TRIANGLES);
736                 glVertex2f (-5000, -5000);
737                 glVertex2f (10000, -5000);
738                 glVertex2f (-5000, 10000);
739                 glEnd ();
740         }
741         glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
742
743         glEnable (GL_CULL_FACE);
744         glEnable (GL_DEPTH_TEST);
745         glDisable(GL_BLEND);
746         glEnable(GL_TEXTURE_2D);
747 }
748
749 /*
750 ==================
751 SCR_UpdateScreen
752
753 This is called every frame, and can also be called explicitly to flush
754 text to the screen.
755
756 LordHavoc: due to my rewrite of R_WorldNode, it no longer takes 256k of stack space :)
757 ==================
758 */
759 void GL_Finish(void);
760 void R_Clip_DisplayBuffer(void);
761 void SCR_UpdateScreen (void)
762 {
763         double  time1 = 0, time2;
764
765         if (r_speeds.value)
766                 time1 = Sys_DoubleTime ();
767
768         VID_UpdateGamma(false);
769
770         if (scr_disabled_for_loading)
771         {
772                 /*
773                 if (realtime - scr_disabled_time > 60)
774                 {
775                         scr_disabled_for_loading = false;
776                         Con_Printf ("load failed.\n");
777                 }
778                 else
779                 */
780                         return;
781         }
782
783         if (!scr_initialized || !con_initialized)
784                 return;                         // not initialized yet
785
786
787         GL_BeginRendering (&vid.realx, &vid.realy, &vid.realwidth, &vid.realheight);
788
789         if (gl_combine.value && !gl_combine_extension)
790                 Cvar_SetValue("gl_combine", false);
791
792         lighthalf = gl_lightmode.value;
793
794         lightscalebit = 0;
795         if (lighthalf)
796                 lightscalebit += 1;
797
798         if (gl_combine.value)
799                 lightscalebit += 2;
800
801         lightscale = 1.0f / (float) (1 << lightscalebit);
802
803         //
804         // determine size of refresh window
805         //
806         if (oldfov != scr_fov.value)
807         {
808                 oldfov = scr_fov.value;
809 //              vid.recalc_refdef = true;
810         }
811
812         if (oldscreensize != scr_viewsize.value)
813         {
814                 oldscreensize = scr_viewsize.value;
815 //              vid.recalc_refdef = true;
816         }
817
818 //      if (vid.recalc_refdef)
819                 SCR_CalcRefdef();
820
821         if (r_render.value)
822         {
823                 glClearColor(0,0,0,0);
824                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // LordHavoc: clear the screen (around the view as well)
825         }
826
827 //
828 // do 3D refresh drawing, and then update the screen
829 //
830         SCR_SetUpToDrawConsole();
831
832         V_RenderView();
833
834         GL_Set2D();
835
836         R_Clip_DisplayBuffer();
837
838         SCR_DrawRam();
839         SCR_DrawNet();
840         SCR_DrawTurtle();
841         SCR_DrawPause();
842         SCR_CheckDrawCenterString();
843         Sbar_Draw();
844         SHOWLMP_drawall();
845
846         if (crosshair.value)
847                 DrawCrosshair(crosshair.value - 1);
848
849         if (cl.intermission == 1)
850                 Sbar_IntermissionOverlay();
851         else if (cl.intermission == 2)
852                 Sbar_FinaleOverlay();
853
854         SCR_DrawConsole();
855         M_Draw();
856
857         ui_draw();
858
859 //      if (scr_drawloading)
860 //              SCR_DrawLoading();
861
862         if (showfps.value)
863         {
864                 static double currtime;
865                 double newtime;
866                 char temp[32];
867                 int calc;
868                 newtime = Sys_DoubleTime();
869                 calc = (int) ((1.0 / (newtime - currtime)) + 0.5);
870                 sprintf(temp, "%4i fps", calc);
871                 currtime = newtime;
872                 Draw_String(vid.conwidth - (8*8), vid.conheight - sb_lines - 8, temp, 9999);
873         }
874
875         if (r_speeds2.value && r_speeds2_string[0])
876         {
877                 int i, j, lines, y;
878                 lines = 1;
879                 for (i = 0;r_speeds2_string[i];i++)
880                         if (r_speeds2_string[i] == '\n')
881                                 lines++;
882                 y = vid.conheight - sb_lines - lines * 8 - 8;
883                 i = j = 0;
884                 while (r_speeds2_string[i])
885                 {
886                         j = i;
887                         while (r_speeds2_string[i] && r_speeds2_string[i] != '\n')
888                                 i++;
889                         if (i - j > 0)
890                                 Draw_String(0, y, r_speeds2_string + j, i - j);
891                         if (r_speeds2_string[i] == '\n')
892                                 i++;
893                         y += 8;
894                 }
895                 // clear so it won't reprint without renderer being called again
896                 r_speeds2_string[0] = 0;
897         }
898
899         V_UpdateBlends();
900
901         GL_BrightenScreen();
902
903         GL_Finish();
904
905         if (r_speeds.value)
906         {
907                 time2 = Sys_DoubleTime ();
908                 Con_Printf ("%3i ms  %4i wpoly %4i epoly %4i transpoly %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, currenttranspoly, c_light_polys, c_nodes, c_leafs, c_faces, c_models, c_bmodels, c_sprites, c_particles, c_dlights);
909         }
910         GL_EndRendering ();
911 }
912
913 // for profiling, this is separated
914 void GL_Finish(void)
915 {
916         if (!r_render.value)
917                 return;
918         glFinish ();
919 }
920