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 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
91 qboolean scr_initialized; // ready to draw
99 qboolean scr_disabled_for_loading;
100 //qboolean scr_drawloading;
101 //float scr_disabled_time;
103 void SCR_ScreenShot_f (void);
106 ===============================================================================
110 ===============================================================================
113 char scr_centerstring[1024];
114 float scr_centertime_start; // for slow victory printing
115 float scr_centertime_off;
116 int scr_center_lines;
118 int scr_erase_center;
124 Called for important messages that should stay in the center of the screen
128 void SCR_CenterPrint (char *str)
130 strncpy (scr_centerstring, str, sizeof(scr_centerstring)-1);
131 scr_centertime_off = scr_centertime.value;
132 scr_centertime_start = cl.time;
134 // count the number of lines for centering
135 scr_center_lines = 1;
145 void SCR_DrawCenterString (void)
152 // the finale prints the characters one at a time
154 remaining = scr_printspeed.value * (cl.time - scr_centertime_start);
158 scr_erase_center = 0;
159 start = scr_centerstring;
161 if (scr_center_lines <= 4)
162 y = vid.conheight*0.35;
168 // scan the width of the line
169 for (l=0 ; l<40 ; l++)
170 if (start[l] == '\n' || !start[l])
172 x = (vid.conwidth - l*8)/2;
173 // LordHavoc: speedup
178 Draw_String(x, y, start, l);
184 for (j=0 ; j<l ; j++, x+=8)
186 Draw_Character (x, y, start[j]);
194 while (*start && *start != '\n')
199 start++; // skip the \n
203 void SCR_CheckDrawCenterString (void)
205 if (scr_center_lines > scr_erase_lines)
206 scr_erase_lines = scr_center_lines;
208 scr_centertime_off -= host_frametime;
210 if (scr_centertime_off <= 0 && !cl.intermission)
212 if (key_dest != key_game)
215 SCR_DrawCenterString ();
218 //=============================================================================
225 float CalcFov (float fov_x, float width, float height)
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;
235 Must be called whenever vid changes
239 static void SCR_CalcRefdef (void)
243 // vid.recalc_refdef = 0;
245 //========================================
248 if (scr_viewsize.value < 30)
249 Cvar_Set ("viewsize","30");
250 if (scr_viewsize.value > 120)
251 Cvar_Set ("viewsize","120");
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");
259 // intermission is always full screen
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
273 size = scr_viewsize.value * (1.0 / 100.0);
278 r_refdef.width = vid.realwidth;
279 r_refdef.height = vid.realheight;
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;
291 r_refdef.fov_x = scr_fov.value;
292 r_refdef.fov_y = CalcFov (r_refdef.fov_x, r_refdef.width, r_refdef.height);
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;
308 void SCR_SizeUp_f (void)
310 Cvar_SetValue ("viewsize",scr_viewsize.value+10);
311 // vid.recalc_refdef = 1;
322 void SCR_SizeDown_f (void)
324 Cvar_SetValue ("viewsize",scr_viewsize.value-10);
325 // vid.recalc_refdef = 1;
328 //============================================================================
330 void gl_screen_start(void)
334 void gl_screen_shutdown(void)
338 void gl_screen_newmap(void)
347 static void R_Envmap_f (void);
348 void GL_Screen_Init (void)
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);
364 Cvar_SetValue("r_render", 0);
368 // register our commands
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);
375 scr_initialized = true;
377 R_RegisterModule("GL_Screen", gl_screen_start, gl_screen_shutdown, gl_screen_newmap);
387 void SCR_DrawRam (void)
389 // if (!scr_showram.integer)
391 // Draw_Pic (32, 0, Draw_CachePic("ram"));
399 void SCR_DrawTurtle (void)
403 if (!scr_showturtle.integer)
406 if (host_frametime < 0.1)
416 Draw_Pic (0, 0, Draw_CachePic("turtle"));
424 void SCR_DrawNet (void)
426 if (realtime - cl.last_received_message < 0.3)
428 if (cls.demoplayback)
431 Draw_Pic (64, 0, Draw_CachePic("net"));
439 void SCR_DrawPause (void)
443 if (!scr_showpause.integer) // turn off for screenshots
449 pic = Draw_CachePic ("gfx/pause.lmp");
450 Draw_Pic ((vid.conwidth - pic->width)/2, (vid.conheight - pic->height)/2, pic);
461 void SCR_DrawLoading (void)
465 if (!scr_drawloading)
468 pic = Draw_CachePic ("gfx/loading.lmp");
469 Draw_Pic ((vid.conwidth - pic->width)/2, (vid.conheight - pic->height)/2, pic);
475 //=============================================================================
480 SCR_SetUpToDrawConsole
483 void SCR_SetUpToDrawConsole (void)
487 //if (scr_drawloading)
488 // return; // never a console with loading plaque
490 // decide on the height of the console
491 con_forcedup = !cl.worldmodel || cls.signon != SIGNONS;
495 scr_conlines = vid.conheight; // full screen
496 scr_con_current = scr_conlines;
498 else if (key_dest == key_console)
499 scr_conlines = vid.conheight/2; // half screen
501 scr_conlines = 0; // none visible
503 if (scr_conlines < scr_con_current)
505 scr_con_current -= scr_conspeed.value*host_realframetime;
506 if (scr_conlines > scr_con_current)
507 scr_con_current = scr_conlines;
510 else if (scr_conlines > scr_con_current)
512 scr_con_current += scr_conspeed.value*host_realframetime;
513 if (scr_conlines < scr_con_current)
514 scr_con_current = scr_conlines;
523 void SCR_DrawConsole (void)
527 Con_DrawConsole (scr_con_current, true);
532 if (key_dest == key_game || key_dest == key_message)
533 Con_DrawNotify (); // only draw notify in game
539 ==============================================================================
543 ==============================================================================
551 void SCR_ScreenShot_f (void)
553 byte *buffer, gamma[256];
555 char checkname[MAX_OSPATH];
558 // find a file name to save it to
560 strcpy(filename,"dp0000.tga");
562 for (i=0 ; i<=9999 ; i++)
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
574 Con_Printf ("SCR_ScreenShot_f: Couldn't create a TGA file\n");
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);
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);
586 Image_WriteTGARGB_preflipped(filename, vid.realwidth, vid.realheight, buffer);
589 Con_Printf ("Wrote %s\n", filename);
596 Grab six views for environment mapping tests
599 float CalcFov (float fov_x, float width, float height);
609 {{ 0, 180, 0}, "bk"},
610 {{ 0, 270, 0}, "lf"},
611 {{-90, 90, 0}, "up"},
614 static void R_Envmap_f (void)
619 byte *buffer, gamma[256];
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");
627 if (!r_render.integer)
630 strcpy(basename, Cmd_Argv(1));
631 size = atoi(Cmd_Argv(2));
632 if (size != 128 && size != 256 && size != 512 && size != 1024)
634 Con_Printf("envmap: size must be one of 128, 256, 512, or 1024\n");
637 if (size > vid.realwidth || size > vid.realheight)
639 Con_Printf("envmap: your resolution is not big enough to render that size\n");
643 buffer = Mem_Alloc(tempmempool, size*size*3);
646 Con_Printf("envmap: unable to allocate memory for image\n");
650 BuildGammaTable8((lighthalf && hardwaregammasupported) ? 2.0f : 1.0f, 1, 1, 0, gamma);
652 // glDrawBuffer (GL_FRONT);
653 // glReadBuffer (GL_FRONT);
654 glDrawBuffer (GL_BACK);
655 glReadBuffer (GL_BACK);
660 r_refdef.width = size;
661 r_refdef.height = size;
666 for (i = 0;i < 6;i++)
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)
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);
679 glDrawBuffer (GL_BACK);
680 glReadBuffer (GL_BACK);
684 // cause refdef to be fixed
685 // vid.recalc_refdef = 1;
688 //=============================================================================
693 SCR_BeginLoadingPlaque
698 void SCR_BeginLoadingPlaque (void)
700 S_StopAllSounds (true);
702 // if (cls.state != ca_connected)
704 // if (cls.signon != SIGNONS)
707 // redraw with no console and the loading plaque
708 // Con_ClearNotify ();
709 // scr_centertime_off = 0;
710 // scr_con_current = 0;
712 scr_drawloading = true;
715 // scr_disabled_for_loading = true;
716 // scr_disabled_time = realtime;
727 void SCR_EndLoadingPlaque (void)
729 // scr_disabled_for_loading = false;
730 scr_drawloading = false;
735 //=============================================================================
737 char *scr_notifystring;
739 void SCR_DrawNotifyString (void)
745 start = scr_notifystring;
747 y = vid.conheight*0.35;
751 // scan the width of the line
752 for (l=0 ; l<40 ; l++)
753 if (start[l] == '\n' || !start[l])
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);
763 while (*start && *start != '\n')
768 start++; // skip the \n
772 //=============================================================================
774 void DrawCrosshair(int num);
775 void GL_Set2D (void);
777 void GL_BrightenScreen(void)
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);
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);
791 if (!(lighthalf && !hardwaregammasupported) && r_brightness.value < 1.01f && r_contrast.value > 0.99f)
794 if (!r_render.integer)
797 glDisable(GL_TEXTURE_2D);
801 f = r_brightness.value;
802 // only apply lighthalf using software color correction if hardware is not available (speed reasons)
803 if (lighthalf && !hardwaregammasupported)
807 glBlendFunc (GL_DST_COLOR, GL_ONE);
809 glBegin (GL_TRIANGLES);
815 glColor3f (f-1, f-1, f-1);
816 glVertex2f (-5000, -5000);
817 glVertex2f (10000, -5000);
818 glVertex2f (-5000, 10000);
824 if (r_contrast.value <= 0.99f)
826 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
828 if (lighthalf && hardwaregammasupported)
829 glColor4f (0.5, 0.5, 0.5, 1 - r_contrast.value);
831 glColor4f (1, 1, 1, 1 - r_contrast.value);
833 glBegin (GL_TRIANGLES);
834 glVertex2f (-5000, -5000);
835 glVertex2f (10000, -5000);
836 glVertex2f (-5000, 10000);
840 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
843 glEnable (GL_CULL_FACE);
845 glEnable (GL_DEPTH_TEST);
849 glEnable(GL_TEXTURE_2D);
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;
857 void R_TimeReport(char *desc)
863 if (!r_timereport_active)
866 r_timereport_temp = r_timereport_current;
867 r_timereport_current = Sys_DoubleTime();
868 t = (int) ((r_timereport_current - r_timereport_temp) * 1000000.0);
870 sprintf(tempbuf, "%8i %s", t, desc);
871 length = strlen(tempbuf);
873 tempbuf[length++] = ' ';
875 if (speedstringcount + length > (vid.conwidth / 8))
877 strcat(r_speeds2_string, "\n");
878 speedstringcount = 0;
880 // skip the space at the beginning if it's the first on the line
881 if (speedstringcount == 0)
883 strcat(r_speeds2_string, tempbuf + 1);
884 speedstringcount = length - 1;
888 strcat(r_speeds2_string, tempbuf);
889 speedstringcount += length;
893 void R_TimeReport_Start(void)
895 r_timereport_active = r_speeds2.integer && cl.worldmodel && cls.state == ca_connected;
896 if (r_timereport_active)
897 r_timereport_start = Sys_DoubleTime();
900 void R_TimeReport_End(void)
902 r_timereport_current = r_timereport_start;
903 R_TimeReport("total");
910 This is called every frame, and can also be called explicitly to flush
913 LordHavoc: due to my rewrite of R_WorldNode, it no longer takes 256k of stack space :)
916 void GL_Finish(void);
917 void R_Clip_DisplayBuffer(void);
918 void SCR_UpdateScreen (void)
920 double time1 = 0, time2;
922 if (r_speeds.integer)
923 time1 = Sys_DoubleTime ();
925 VID_UpdateGamma(false);
927 if (scr_disabled_for_loading)
930 if (realtime - scr_disabled_time > 60)
932 scr_disabled_for_loading = false;
933 Con_Printf ("load failed.\n");
940 if (!scr_initialized || !con_initialized)
941 return; // not initialized yet
943 r_speeds2_string[0] = 0;
944 if (r_speeds2.integer)
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();
955 Mem_CheckSentinelsGlobal();
956 R_TimeReport("memtest");
961 R_TimeReport("finish");
963 GL_BeginRendering (&vid.realx, &vid.realy, &vid.realwidth, &vid.realheight);
965 if (gl_combine.integer && !gl_combine_extension)
966 Cvar_SetValue("gl_combine", 0);
968 lighthalf = gl_lightmode.integer;
974 if (gl_combine.integer && r_multitexture.integer)
977 lightscale = 1.0f / (float) (1 << lightscalebit);
980 // determine size of refresh window
982 if (oldfov != scr_fov.value)
984 oldfov = scr_fov.value;
985 // vid.recalc_refdef = true;
988 if (oldscreensize != scr_viewsize.value)
990 oldscreensize = scr_viewsize.value;
991 // vid.recalc_refdef = true;
994 // if (vid.recalc_refdef)
997 R_TimeReport("calcrefdef");
999 if (r_render.integer)
1001 glClearColor(0,0,0,0);
1003 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // LordHavoc: clear the screen (around the view as well)
1007 R_TimeReport("clear");
1009 if (gl_dither.integer)
1010 glEnable(GL_DITHER);
1012 glDisable(GL_DITHER);
1016 // do 3D refresh drawing, and then update the screen
1018 SCR_SetUpToDrawConsole();
1020 R_TimeReport("setupconsole");
1028 R_Clip_DisplayBuffer();
1034 SCR_CheckDrawCenterString();
1038 if (crosshair.integer)
1039 DrawCrosshair(crosshair.integer - 1);
1041 if (cl.intermission == 1)
1042 Sbar_IntermissionOverlay();
1043 else if (cl.intermission == 2)
1044 Sbar_FinaleOverlay();
1051 // if (scr_drawloading)
1052 // SCR_DrawLoading();
1054 if (showfps.integer)
1056 static double currtime, frametimes[32];
1057 double newtime, total;
1060 static int framecycle = 0;
1061 newtime = Sys_DoubleTime();
1062 frametimes[framecycle] = newtime - currtime;
1067 for (i = 0;i < 32;i++)
1071 total += frametimes[i];
1073 // limit how far back we look
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);
1084 Draw_String(vid.conwidth - (8*8), vid.conheight - sb_lines - 8, temp, 9999);
1091 if (r_speeds2_string[0] && cls.state == ca_connected && cl.worldmodel)
1095 for (i = 0;r_speeds2_string[i];i++)
1096 if (r_speeds2_string[i] == '\n')
1098 y = vid.conheight - sb_lines - lines * 8 - 8;
1100 while (r_speeds2_string[i])
1103 while (r_speeds2_string[i] && r_speeds2_string[i] != '\n')
1106 Draw_String(0, y, r_speeds2_string + j, i - j);
1107 if (r_speeds2_string[i] == '\n')
1113 GL_BrightenScreen();
1115 if (r_speeds.integer)
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);
1122 // for profiling, this is separated
1123 void GL_Finish(void)
1125 if (!r_render.integer)