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 allways rendered, unless the console is full screen
73 int glx, gly, glwidth, glheight;
75 // only the refresh window will be updated unless these variables are flagged
77 int scr_copyeverything;
79 float scr_con_current;
80 float scr_conlines; // lines of console to display
82 float oldscreensize, oldfov;
83 cvar_t scr_viewsize = {"viewsize","100", true};
84 cvar_t scr_fov = {"fov","90"}; // 10 - 170
85 cvar_t scr_conspeed = {"scr_conspeed","300"};
86 cvar_t scr_centertime = {"scr_centertime","2"};
87 cvar_t scr_showram = {"showram","1"};
88 cvar_t scr_showturtle = {"showturtle","0"};
89 cvar_t scr_showpause = {"showpause","1"};
90 cvar_t scr_printspeed = {"scr_printspeed","8"};
91 cvar_t gl_triplebuffer = {"gl_triplebuffer", "1", true };
92 cvar_t showfps = {"showfps", "0", true};
94 extern cvar_t crosshair;
96 qboolean scr_initialized; // ready to draw
109 extern viddef_t vid; // global video state
113 qboolean scr_disabled_for_loading;
114 qboolean scr_drawloading;
115 float scr_disabled_time;
117 void SCR_ScreenShot_f (void);
120 ===============================================================================
124 ===============================================================================
127 char scr_centerstring[1024];
128 float scr_centertime_start; // for slow victory printing
129 float scr_centertime_off;
130 int scr_center_lines;
132 int scr_erase_center;
138 Called for important messages that should stay in the center of the screen
142 void SCR_CenterPrint (char *str)
144 strncpy (scr_centerstring, str, sizeof(scr_centerstring)-1);
145 scr_centertime_off = scr_centertime.value;
146 scr_centertime_start = cl.time;
148 // count the number of lines for centering
149 scr_center_lines = 1;
159 void SCR_DrawCenterString (void)
166 // the finale prints the characters one at a time
168 remaining = scr_printspeed.value * (cl.time - scr_centertime_start);
172 scr_erase_center = 0;
173 start = scr_centerstring;
175 if (scr_center_lines <= 4)
182 // scan the width of the line
183 for (l=0 ; l<40 ; l++)
184 if (start[l] == '\n' || !start[l])
186 x = (vid.width - l*8)/2;
187 // LordHavoc: speedup
192 Draw_String(x, y, start, l);
198 for (j=0 ; j<l ; j++, x+=8)
200 Draw_Character (x, y, start[j]);
208 while (*start && *start != '\n')
213 start++; // skip the \n
217 void SCR_CheckDrawCenterString (void)
220 if (scr_center_lines > scr_erase_lines)
221 scr_erase_lines = scr_center_lines;
223 scr_centertime_off -= host_frametime;
225 if (scr_centertime_off <= 0 && !cl.intermission)
227 if (key_dest != key_game)
230 SCR_DrawCenterString ();
233 //=============================================================================
240 float CalcFov (float fov_x, float width, float height)
245 if (fov_x < 1 || fov_x > 179)
246 Sys_Error ("Bad fov: %f", fov_x);
248 x = width/tan(fov_x/360*M_PI);
261 Must be called whenever vid changes
265 static void SCR_CalcRefdef (void)
269 qboolean full = false;
272 scr_fullupdate = 0; // force a background redraw
273 vid.recalc_refdef = 0;
275 // force the status bar to redraw
278 //========================================
281 if (scr_viewsize.value < 30)
282 Cvar_Set ("viewsize","30");
283 if (scr_viewsize.value > 120)
284 Cvar_Set ("viewsize","120");
286 // bound field of view
287 if (scr_fov.value < 10)
288 Cvar_Set ("fov","10");
289 if (scr_fov.value > 170)
290 Cvar_Set ("fov","170");
292 // intermission is always full screen
296 size = scr_viewsize.value;
299 sb_lines = 0; // no status bar at all
300 else if (size >= 110)
301 sb_lines = 24; // no inventory
305 if (scr_viewsize.value >= 100.0)
311 size = scr_viewsize.value;
320 // LordHavoc: always fullyscreen rendering
321 h = vid.height/* - sb_lines*/;
323 r_refdef.vrect.width = vid.width * size;
324 if (r_refdef.vrect.width < 96)
326 size = 96.0 / r_refdef.vrect.width;
327 r_refdef.vrect.width = 96; // min for icons
330 r_refdef.vrect.height = vid.height * size;
331 //if (r_refdef.vrect.height > vid.height - sb_lines)
332 // r_refdef.vrect.height = vid.height - sb_lines;
333 if (r_refdef.vrect.height > (int) vid.height)
334 r_refdef.vrect.height = vid.height;
335 r_refdef.vrect.x = (vid.width - r_refdef.vrect.width)/2;
337 r_refdef.vrect.y = 0;
339 r_refdef.vrect.y = (h - r_refdef.vrect.height)/2;
341 r_refdef.fov_x = scr_fov.value;
342 r_refdef.fov_y = CalcFov (r_refdef.fov_x, r_refdef.vrect.width, r_refdef.vrect.height);
344 scr_vrect = r_refdef.vrect;
355 void SCR_SizeUp_f (void)
357 Cvar_SetValue ("viewsize",scr_viewsize.value+10);
358 vid.recalc_refdef = 1;
369 void SCR_SizeDown_f (void)
371 Cvar_SetValue ("viewsize",scr_viewsize.value-10);
372 vid.recalc_refdef = 1;
375 //============================================================================
385 Cvar_RegisterVariable (&scr_fov);
386 Cvar_RegisterVariable (&scr_viewsize);
387 Cvar_RegisterVariable (&scr_conspeed);
388 Cvar_RegisterVariable (&scr_showram);
389 Cvar_RegisterVariable (&scr_showturtle);
390 Cvar_RegisterVariable (&scr_showpause);
391 Cvar_RegisterVariable (&scr_centertime);
392 Cvar_RegisterVariable (&scr_printspeed);
393 Cvar_RegisterVariable (&gl_triplebuffer);
394 Cvar_RegisterVariable (&showfps);
397 // register our commands
399 Cmd_AddCommand ("screenshot",SCR_ScreenShot_f);
400 Cmd_AddCommand ("sizeup",SCR_SizeUp_f);
401 Cmd_AddCommand ("sizedown",SCR_SizeDown_f);
403 scr_ram = Draw_PicFromWad ("ram");
404 scr_net = Draw_PicFromWad ("net");
405 scr_turtle = Draw_PicFromWad ("turtle");
407 scr_initialized = true;
417 void SCR_DrawRam (void)
419 if (!scr_showram.value)
425 Draw_Pic (scr_vrect.x+32, scr_vrect.y, scr_ram);
433 void SCR_DrawTurtle (void)
437 if (!scr_showturtle.value)
440 if (host_frametime < 0.1)
450 Draw_Pic (scr_vrect.x, scr_vrect.y, scr_turtle);
458 void SCR_DrawNet (void)
460 if (realtime - cl.last_received_message < 0.3)
462 if (cls.demoplayback)
465 Draw_Pic (scr_vrect.x+64, scr_vrect.y, scr_net);
473 void SCR_DrawPause (void)
477 if (!scr_showpause.value) // turn off for screenshots
483 pic = Draw_CachePic ("gfx/pause.lmp");
484 Draw_Pic ( (vid.width - pic->width)/2,
485 (vid.height - 48 - pic->height)/2, pic);
495 void SCR_DrawLoading (void)
499 if (!scr_drawloading)
502 pic = Draw_CachePic ("gfx/loading.lmp");
503 Draw_Pic ( (vid.width - pic->width)/2,
504 (vid.height - 48 - pic->height)/2, pic);
509 //=============================================================================
514 SCR_SetUpToDrawConsole
517 void SCR_SetUpToDrawConsole (void)
521 //if (scr_drawloading)
522 // return; // never a console with loading plaque
524 // decide on the height of the console
525 con_forcedup = !cl.worldmodel || cls.signon != SIGNONS;
529 scr_conlines = vid.height; // full screen
530 scr_con_current = scr_conlines;
532 else if (key_dest == key_console)
533 scr_conlines = vid.height/2; // half screen
535 scr_conlines = 0; // none visible
537 if (scr_conlines < scr_con_current)
539 scr_con_current -= scr_conspeed.value*host_frametime;
540 if (scr_conlines > scr_con_current)
541 scr_con_current = scr_conlines;
544 else if (scr_conlines > scr_con_current)
546 scr_con_current += scr_conspeed.value*host_frametime;
547 if (scr_conlines < scr_con_current)
548 scr_con_current = scr_conlines;
552 if (clearconsole++ < vid.numpages)
556 else if (clearnotify++ < vid.numpages)
569 void SCR_DrawConsole (void)
573 scr_copyeverything = 1;
574 Con_DrawConsole (scr_con_current, true);
579 if (key_dest == key_game || key_dest == key_message)
580 Con_DrawNotify (); // only draw notify in game
586 ==============================================================================
590 ==============================================================================
593 typedef struct _TargaHeader {
594 unsigned char id_length, colormap_type, image_type;
595 unsigned short colormap_index, colormap_length;
596 unsigned char colormap_size;
597 unsigned short x_origin, y_origin, width, height;
598 unsigned char pixel_size, attributes;
607 void SCR_ScreenShot_f (void)
611 char checkname[MAX_OSPATH];
614 // find a file name to save it to
616 strcpy(pcxname,"dp0000.tga");
618 for (i=0 ; i<=9999 ; i++)
620 pcxname[2] = (i/1000)%10 + '0';
621 pcxname[3] = (i/ 100)%10 + '0';
622 pcxname[4] = (i/ 10)%10 + '0';
623 pcxname[5] = (i/ 1)%10 + '0';
624 sprintf (checkname, "%s/%s", com_gamedir, pcxname);
625 if (Sys_FileTime(checkname) == -1)
626 break; // file doesn't exist
630 Con_Printf ("SCR_ScreenShot_f: Couldn't create a TGA file\n");
635 buffer = malloc(glwidth*glheight*3 + 18);
636 memset (buffer, 0, 18);
637 buffer[2] = 2; // uncompressed type
638 buffer[12] = glwidth&255;
639 buffer[13] = glwidth>>8;
640 buffer[14] = glheight&255;
641 buffer[15] = glheight>>8;
642 buffer[16] = 24; // pixel size
644 glReadPixels (glx, gly, glwidth, glheight, GL_RGB, GL_UNSIGNED_BYTE, buffer+18 );
647 c = 18+glwidth*glheight*3;
648 for (i=18 ; i<c ; i+=3)
651 buffer[i] = buffer[i+2];
654 COM_WriteFile (pcxname, buffer, glwidth*glheight*3 + 18 );
657 Con_Printf ("Wrote %s\n", pcxname);
661 //=============================================================================
666 SCR_BeginLoadingPlaque
670 void SCR_BeginLoadingPlaque (void)
672 S_StopAllSounds (true);
674 if (cls.state != ca_connected)
676 if (cls.signon != SIGNONS)
679 // redraw with no console and the loading plaque
681 scr_centertime_off = 0;
684 scr_drawloading = true;
688 scr_drawloading = false;
690 scr_disabled_for_loading = true;
691 scr_disabled_time = realtime;
701 void SCR_EndLoadingPlaque (void)
703 scr_disabled_for_loading = false;
708 //=============================================================================
710 char *scr_notifystring;
711 qboolean scr_drawdialog;
713 void SCR_DrawNotifyString (void)
719 start = scr_notifystring;
725 // scan the width of the line
726 for (l=0 ; l<40 ; l++)
727 if (start[l] == '\n' || !start[l])
729 x = (vid.width - l*8)/2;
730 // LordHavoc: speedup
731 // for (j=0 ; j<l ; j++, x+=8)
732 // Draw_Character (x, y, start[j]);
733 Draw_String (x, y, start, l);
737 while (*start && *start != '\n')
742 start++; // skip the \n
750 Displays a text string in the center of the screen and waits for a Y or N
754 int SCR_ModalMessage (char *text)
756 if (cls.state == ca_dedicated)
759 scr_notifystring = text;
761 // draw a fresh screen
763 scr_drawdialog = true;
765 scr_drawdialog = false;
767 S_ClearBuffer (); // so dma doesn't loop current sound
771 key_count = -1; // wait for a key down and up
772 Sys_SendKeyEvents ();
773 } while (key_lastpress != 'y' && key_lastpress != 'n' && key_lastpress != K_ESCAPE);
778 return key_lastpress == 'y';
782 //=============================================================================
788 Brings the console down and fades the palettes back to normal
791 void SCR_BringDownConsole (void)
795 scr_centertime_off = 0;
797 for (i=0 ; i<20 && scr_conlines != scr_con_current ; i++)
800 cl.cshifts[0].percent = 0; // no area contents palette on next frame
801 VID_SetPalette (host_basepal);
804 void GL_Set2D (void);
806 extern void SHOWLMP_drawall();
807 extern cvar_t contrast;
808 extern cvar_t brightness;
809 extern cvar_t gl_lightmode;
811 void GL_BrightenScreen()
814 glDisable(GL_TEXTURE_2D);
816 f = brightness.value = bound(1.0f, brightness.value, 5.0f);
819 glBlendFunc (GL_DST_COLOR, GL_ONE);
820 glBegin (GL_TRIANGLES);
826 glColor3f (f-1, f-1, f-1);
827 glVertex2f (-5000, -5000);
828 glVertex2f (10000, -5000);
829 glVertex2f (-5000, 10000);
834 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
835 contrast.value = bound(0.2, contrast.value, 1.0);
836 if (contrast.value < 1.0f)
838 glBegin (GL_TRIANGLES);
839 glColor4f (1, 1, 1, 1-contrast.value);
840 glVertex2f (-5000, -5000);
841 glVertex2f (10000, -5000);
842 glVertex2f (-5000, 10000);
846 glEnable (GL_CULL_FACE);
847 glEnable (GL_DEPTH_TEST);
849 glEnable(GL_TEXTURE_2D);
856 This is called every frame, and can also be called explicitly to flush
859 WARNING: be very careful calling this from elsewhere, because the refresh
860 needs almost the entire 256k of stack space!
863 extern cvar_t gl_vertexarrays;
864 extern qboolean gl_arrays;
867 void SCR_UpdateScreen (void)
869 double time1 = 0, time2;
873 time1 = Sys_FloatTime ();
880 gl_vertexarrays.value = 0;
882 vid.numpages = 2 + gl_triplebuffer.value;
885 scr_copyeverything = 0;
887 if (scr_disabled_for_loading)
889 if (realtime - scr_disabled_time > 60)
891 scr_disabled_for_loading = false;
892 Con_Printf ("load failed.\n");
898 if (!scr_initialized || !con_initialized)
899 return; // not initialized yet
902 GL_BeginRendering (&glx, &gly, &glwidth, &glheight);
905 // determine size of refresh window
907 if (oldfov != scr_fov.value)
909 oldfov = scr_fov.value;
910 vid.recalc_refdef = true;
913 if (oldscreensize != scr_viewsize.value)
915 oldscreensize = scr_viewsize.value;
916 vid.recalc_refdef = true;
919 if (vid.recalc_refdef)
922 glClearColor(0,0,0,0);
923 glClear (GL_COLOR_BUFFER_BIT); // LordHavoc: clear the screen (around the view as well)
926 // do 3D refresh drawing, and then update the screen
928 SCR_SetUpToDrawConsole ();
937 // Draw_FadeScreen ();
938 SCR_DrawNotifyString ();
939 scr_copyeverything = true;
941 else if (scr_drawloading)
946 else if (cl.intermission == 1 && key_dest == key_game)
948 Sbar_IntermissionOverlay ();
950 else if (cl.intermission == 2 && key_dest == key_game)
952 Sbar_FinaleOverlay ();
953 SCR_CheckDrawCenterString ();
958 Draw_Character (scr_vrect.x + scr_vrect.width/2, scr_vrect.y + scr_vrect.height/2, '+');
964 SCR_CheckDrawCenterString ();
973 static double currtime;
977 newtime = Sys_FloatTime();
978 calc = (int) (100.0 / (newtime - currtime));
979 sprintf(temp, "% 4i.%02i fps", calc / 100, calc % 100);
981 Draw_String(vid.width - (12*8), 0, temp, 9999);
992 time2 = Sys_FloatTime ();
993 Con_Printf ("%3i ms %4i wpoly %4i epoly %4i transpoly %4i BSPnodes\n", (int)((time2-time1)*1000), c_brush_polys, c_alias_polys, currenttranspoly, c_nodes);
998 // for profiling, this is seperated