2 Copyright (C) 1996-1997 Id Software, Inc.
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.
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.
13 See the GNU General Public License for more details.
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.
21 // screen.c -- master for refresh, status bar, console, chat, notify, etc
31 centerprint / slow centerprint
33 intermission / finale overlay
38 required background clears
39 required update regions
42 syncronous draw mode or async
43 One off screen buffer, with updates either copied or xblited
44 Need to double buffer?
47 async draw will require the refresh area to be cleared, because it will be
48 xblited, but sync draw can just ignore it.
59 turn off messages option
61 the refresh is always rendered, unless the console is full screen
73 float scr_con_current;
74 float scr_conlines; // lines of console to display
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
90 qboolean scr_initialized; // ready to draw
98 qboolean scr_disabled_for_loading;
99 //qboolean scr_drawloading;
100 //float scr_disabled_time;
102 void SCR_ScreenShot_f (void);
105 ===============================================================================
109 ===============================================================================
112 char scr_centerstring[1024];
113 float scr_centertime_start; // for slow victory printing
114 float scr_centertime_off;
115 int scr_center_lines;
117 int scr_erase_center;
123 Called for important messages that should stay in the center of the screen
127 void SCR_CenterPrint (char *str)
129 strncpy (scr_centerstring, str, sizeof(scr_centerstring)-1);
130 scr_centertime_off = scr_centertime.value;
131 scr_centertime_start = cl.time;
133 // count the number of lines for centering
134 scr_center_lines = 1;
144 void SCR_DrawCenterString (void)
151 // the finale prints the characters one at a time
153 remaining = scr_printspeed.value * (cl.time - scr_centertime_start);
157 scr_erase_center = 0;
158 start = scr_centerstring;
160 if (scr_center_lines <= 4)
161 y = vid.conheight*0.35;
167 // scan the width of the line
168 for (l=0 ; l<40 ; l++)
169 if (start[l] == '\n' || !start[l])
171 x = (vid.conwidth - l*8)/2;
176 DrawQ_String(x, y, start, l, 8, 8, 1, 1, 1, 1, 0);
184 while (*start && *start != '\n')
189 start++; // skip the \n
193 void SCR_CheckDrawCenterString (void)
195 if (scr_center_lines > scr_erase_lines)
196 scr_erase_lines = scr_center_lines;
198 scr_centertime_off -= host_frametime;
200 if (scr_centertime_off <= 0 && !cl.intermission)
202 if (key_dest != key_game)
205 SCR_DrawCenterString ();
208 //=============================================================================
215 float CalcFov (float fov_x, float width, float height)
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;
225 Must be called whenever vid changes
229 static void SCR_CalcRefdef (void)
233 // vid.recalc_refdef = 0;
235 //========================================
238 if (scr_viewsize.value < 30)
239 Cvar_Set ("viewsize","30");
240 if (scr_viewsize.value > 120)
241 Cvar_Set ("viewsize","120");
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");
249 // intermission is always full screen
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
263 size = scr_viewsize.value * (1.0 / 100.0);
268 r_refdef.width = vid.realwidth;
269 r_refdef.height = vid.realheight;
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;
281 r_refdef.fov_x = scr_fov.value;
282 r_refdef.fov_y = CalcFov (r_refdef.fov_x, r_refdef.width, r_refdef.height);
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;
298 void SCR_SizeUp_f (void)
300 Cvar_SetValue ("viewsize",scr_viewsize.value+10);
311 void SCR_SizeDown_f (void)
313 Cvar_SetValue ("viewsize",scr_viewsize.value-10);
316 //============================================================================
318 void gl_screen_start(void)
322 void gl_screen_shutdown(void)
326 void gl_screen_newmap(void)
335 static void R_Envmap_f (void);
336 void GL_Screen_Init (void)
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);
351 Cvar_SetValue("r_render", 0);
355 // register our commands
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);
362 scr_initialized = true;
364 R_RegisterModule("GL_Screen", gl_screen_start, gl_screen_shutdown, gl_screen_newmap);
374 void SCR_DrawRam (void)
376 // if (!scr_showram.integer)
378 // DrawQ_Pic (32, 0, "ram", 0, 0, 1, 1, 1, 1, 0);
386 void SCR_DrawTurtle (void)
390 if (cls.state != ca_connected)
393 if (!scr_showturtle.integer)
396 if (host_frametime < 0.1)
406 DrawQ_Pic (0, 0, "turtle", 0, 0, 1, 1, 1, 1, 0);
414 void SCR_DrawNet (void)
416 if (cls.state != ca_connected)
418 if (realtime - cl.last_received_message < 0.3)
420 if (cls.demoplayback)
423 DrawQ_Pic (64, 0, "net", 0, 0, 1, 1, 1, 1, 0);
431 void SCR_DrawPause (void)
435 if (cls.state != ca_connected)
438 if (!scr_showpause.integer) // turn off for screenshots
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);
456 void SCR_DrawLoading (void)
460 if (!scr_drawloading)
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);
470 //=============================================================================
475 SCR_SetUpToDrawConsole
478 void SCR_SetUpToDrawConsole (void)
482 // decide on the height of the console
483 con_forcedup = !cl.worldmodel || cls.signon != SIGNONS;
487 scr_conlines = vid.conheight; // full screen
488 scr_con_current = scr_conlines;
490 else if (key_dest == key_console)
491 scr_conlines = vid.conheight/2; // half screen
493 scr_conlines = 0; // none visible
495 if (scr_conlines < scr_con_current)
497 scr_con_current -= scr_conspeed.value*host_realframetime;
498 if (scr_conlines > scr_con_current)
499 scr_con_current = scr_conlines;
502 else if (scr_conlines > scr_con_current)
504 scr_con_current += scr_conspeed.value*host_realframetime;
505 if (scr_conlines < scr_con_current)
506 scr_con_current = scr_conlines;
515 void SCR_DrawConsole (void)
519 Con_DrawConsole (scr_con_current);
524 if (key_dest == key_game || key_dest == key_message)
525 Con_DrawNotify (); // only draw notify in game
531 ==============================================================================
535 ==============================================================================
543 void SCR_ScreenShot_f (void)
545 byte *buffer, gamma[256];
547 char checkname[MAX_OSPATH];
550 // find a file name to save it to
552 strcpy(filename,"dp0000.tga");
554 for (i=0 ; i<=9999 ; i++)
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
566 Con_Printf ("SCR_ScreenShot_f: Couldn't create a TGA file\n");
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);
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);
578 Image_WriteTGARGB_preflipped(filename, vid.realwidth, vid.realheight, buffer);
581 Con_Printf ("Wrote %s\n", filename);
588 Grab six views for environment mapping tests
591 float CalcFov (float fov_x, float width, float height);
601 {{ 0, 180, 0}, "bk"},
602 {{ 0, 270, 0}, "lf"},
603 {{-90, 90, 0}, "up"},
606 static void R_Envmap_f (void)
611 byte *buffer, gamma[256];
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");
619 if (!r_render.integer)
622 strcpy(basename, Cmd_Argv(1));
623 size = atoi(Cmd_Argv(2));
624 if (size != 128 && size != 256 && size != 512 && size != 1024)
626 Con_Printf("envmap: size must be one of 128, 256, 512, or 1024\n");
629 if (size > vid.realwidth || size > vid.realheight)
631 Con_Printf("envmap: your resolution is not big enough to render that size\n");
635 buffer = Mem_Alloc(tempmempool, size*size*3);
638 Con_Printf("envmap: unable to allocate memory for image\n");
642 BuildGammaTable8((lighthalf && hardwaregammasupported) ? 2.0f : 1.0f, 1, 1, 0, gamma);
644 // glDrawBuffer (GL_FRONT);
645 // glReadBuffer (GL_FRONT);
646 glDrawBuffer (GL_BACK);
647 glReadBuffer (GL_BACK);
652 r_refdef.width = size;
653 r_refdef.height = size;
658 for (i = 0;i < 6;i++)
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)
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);
671 glDrawBuffer (GL_BACK);
672 glReadBuffer (GL_BACK);
676 // cause refdef to be fixed
677 // vid.recalc_refdef = 1;
680 //=============================================================================
685 SCR_BeginLoadingPlaque
690 void SCR_BeginLoadingPlaque (void)
692 S_StopAllSounds (true);
694 // if (cls.state != ca_connected)
696 // if (cls.signon != SIGNONS)
699 // redraw with no console and the loading plaque
700 // Con_ClearNotify ();
701 // scr_centertime_off = 0;
702 // scr_con_current = 0;
704 scr_drawloading = true;
707 // scr_disabled_for_loading = true;
708 // scr_disabled_time = realtime;
719 void SCR_EndLoadingPlaque (void)
721 // scr_disabled_for_loading = false;
722 scr_drawloading = false;
727 //=============================================================================
729 char *scr_notifystring;
731 void SCR_DrawNotifyString (void)
737 start = scr_notifystring;
739 y = vid.conheight*0.35;
743 // scan the width of the line
744 for (l=0 ; l<40 ; l++)
745 if (start[l] == '\n' || !start[l])
747 x = (vid.conwidth - l*8)/2;
748 DrawQ_String (x, y, start, l, 8, 8, 1, 1, 1, 1, 0);
752 while (*start && *start != '\n')
757 start++; // skip the \n
762 //=============================================================================
764 void DrawCrosshair(int num);
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;
770 void R_TimeReport(char *desc)
776 if (!r_timereport_active)
779 r_timereport_temp = r_timereport_current;
780 r_timereport_current = Sys_DoubleTime();
781 t = (int) ((r_timereport_current - r_timereport_temp) * 1000000.0);
783 sprintf(tempbuf, "%8i %s", t, desc);
784 length = strlen(tempbuf);
786 tempbuf[length++] = ' ';
788 if (speedstringcount + length > (vid.conwidth / 8))
790 strcat(r_speeds_string, "\n");
791 speedstringcount = 0;
793 // skip the space at the beginning if it's the first on the line
794 if (speedstringcount == 0)
796 strcat(r_speeds_string, tempbuf + 1);
797 speedstringcount = length - 1;
801 strcat(r_speeds_string, tempbuf);
802 speedstringcount += length;
806 void R_TimeReport_Start(void)
808 r_timereport_active = r_speeds.integer && cl.worldmodel && cls.state == ca_connected;
809 r_speeds_string[0] = 0;
810 if (r_timereport_active)
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);
838 r_timereport_start = Sys_DoubleTime();
842 void R_TimeReport_End(void)
844 r_timereport_current = r_timereport_start;
845 R_TimeReport("total");
847 if (r_timereport_active)
851 for (i = 0;r_speeds_string[i];i++)
852 if (r_speeds_string[i] == '\n')
854 y = vid.conheight - sb_lines - lines * 8/* - 8*/;
856 DrawQ_Fill(0, y, vid.conwidth, lines * 8, 0, 0, 0, 0.5, 0);
857 while (r_speeds_string[i])
860 while (r_speeds_string[i] && r_speeds_string[i] != '\n')
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')
875 This is called every frame, and can also be called explicitly to flush
878 LordHavoc: due to my rewrite of R_WorldNode, it no longer takes 256k of stack space :)
881 void SCR_UpdateScreen (void)
883 VID_UpdateGamma(false);
885 if (scr_disabled_for_loading)
888 if (!scr_initialized || !con_initialized)
889 return; // not initialized yet
891 //Mem_CheckSentinelsGlobal();
892 //R_TimeReport("memtest");
894 R_TimeReport("other");
901 R_TimeReport("finish");
903 GL_BeginRendering (&vid.realx, &vid.realy, &vid.realwidth, &vid.realheight);
905 if (gl_combine.integer && !gl_combine_extension)
906 Cvar_SetValue("gl_combine", 0);
908 lighthalf = gl_lightmode.integer;
914 if (gl_combine.integer && r_multitexture.integer)
917 lightscale = 1.0f / (float) (1 << lightscalebit);
919 R_TimeReport("setup");
921 // determine size of refresh window
924 R_TimeReport("calcrefdef");
926 if (r_render.integer)
928 glClearColor(0,0,0,0);
930 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // LordHavoc: clear the screen (around the view as well)
932 if (gl_dither.integer)
935 glDisable(GL_DITHER);
939 SCR_SetUpToDrawConsole();
941 R_TimeReport("clear");
943 if (scr_conlines < vid.conheight)
950 SCR_CheckDrawCenterString();
966 R_TimeReport_Start();