]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - cl_screen.c
dde64f3cfc4603d4140a4c0eed02b63e521f8dff
[xonotic/darkplaces.git] / cl_screen.c
1
2 #include "quakedef.h"
3 #include "cl_video.h"
4 #include "image.h"
5 #include "jpeg.h"
6 #include "cl_collision.h"
7 #include "csprogs.h"
8
9 cvar_t scr_viewsize = {CVAR_SAVE, "viewsize","100", "how large the view should be, 110 disables inventory bar, 120 disables status bar"};
10 cvar_t scr_fov = {CVAR_SAVE, "fov","90", "field of vision, 1-170 degrees, default 90, some players use 110-130"};       // 1 - 170
11 cvar_t scr_conspeed = {CVAR_SAVE, "scr_conspeed","900", "speed of console open/close"}; // LordHavoc: quake used 300
12 cvar_t scr_conalpha = {CVAR_SAVE, "scr_conalpha", "1", "opacity of console background"};
13 cvar_t scr_conbrightness = {CVAR_SAVE, "scr_conbrightness", "0.2", "brightness of console background (0 = black, 1 = image)"};
14 cvar_t scr_conforcewhiledisconnected = {0, "scr_conforcewhiledisconnected", "1", "forces fullscreen console while disconnected"};
15 cvar_t scr_menuforcewhiledisconnected = {0, "scr_menuforcewhiledisconnected", "1", "forces menu while disconnected"};
16 cvar_t scr_centertime = {0, "scr_centertime","2", "how long centerprint messages show"};
17 cvar_t scr_showram = {CVAR_SAVE, "showram","1", "show ram icon if low on surface cache memory (not used)"};
18 cvar_t scr_showturtle = {CVAR_SAVE, "showturtle","0", "show turtle icon when framerate is too low (not used)"};
19 cvar_t scr_showpause = {CVAR_SAVE, "showpause","1", "show pause icon when game is paused"};
20 cvar_t scr_showbrand = {0, "showbrand","0", "shows gfx/brand.tga in a corner of the screen (different values select different positions, including centered)"};
21 cvar_t scr_printspeed = {0, "scr_printspeed","8", "speed of intermission printing (episode end texts)"};
22 cvar_t vid_conwidth = {CVAR_SAVE, "vid_conwidth", "640", "virtual width of 2D graphics system"};
23 cvar_t vid_conheight = {CVAR_SAVE, "vid_conheight", "480", "virtual height of 2D graphics system"};
24 cvar_t vid_pixelheight = {CVAR_SAVE, "vid_pixelheight", "1", "adjusts vertical field of vision to account for non-square pixels (1280x1024 on a CRT monitor for example)"};
25 cvar_t scr_screenshot_jpeg = {CVAR_SAVE, "scr_screenshot_jpeg","1", "save jpeg instead of targa"};
26 cvar_t scr_screenshot_jpeg_quality = {CVAR_SAVE, "scr_screenshot_jpeg_quality","0.9", "image quality of saved jpeg"};
27 cvar_t scr_screenshot_gamma = {CVAR_SAVE, "scr_screenshot_gamma","2.2", "gamma correction on saved screenshots and videos, 1.0 saves unmodified images"};
28 // scr_screenshot_name is defined in fs.c
29 cvar_t cl_capturevideo = {0, "cl_capturevideo", "0", "enables saving of video to a file or files (default is .tga files, if scr_screenshot_jpeg is on it saves .jpg files (VERY SLOW), if any rawrgb or rawyv12 are on it saves those formats instead, note that scr_screenshot_gamma affects the brightness of the output)"};
30 cvar_t cl_capturevideo_sound = {0, "cl_capturevideo_sound", "0", "enables saving of sound to a .wav file (warning: this requires exact sync, if your hard drive can't keep up it will abort, if your graphics can't keep up it will save duplicate frames to maintain sound sync)"};
31 cvar_t cl_capturevideo_fps = {0, "cl_capturevideo_fps", "30", "how many frames per second to save (29.97 for NTSC, 30 for typical PC video, 15 can be useful)"};
32 cvar_t cl_capturevideo_rawrgb = {0, "cl_capturevideo_rawrgb", "0", "saves a single .rgb video file containing raw RGB images (you'll need special processing tools to encode this to something more useful)"};
33 cvar_t cl_capturevideo_rawyv12 = {0, "cl_capturevideo_rawyv12", "0", "saves a single .yv12 video file containing raw YV12 (luma plane, then half resolution chroma planes, first chroma blue then chroma red, this is the format used internally by many encoders, some tools can read it directly)"};
34 cvar_t r_letterbox = {0, "r_letterbox", "0", "reduces vertical height of view to simulate a letterboxed movie effect (can be used by mods for cutscenes)"};
35 cvar_t r_stereo_separation = {0, "r_stereo_separation", "4", "separation of eyes in the world (try negative values too)"};
36 cvar_t r_stereo_sidebyside = {0, "r_stereo_sidebyside", "0", "side by side views (for those who can't afford glasses but can afford eye strain)"};
37 cvar_t r_stereo_redblue = {0, "r_stereo_redblue", "0", "red/blue anaglyph stereo glasses (note: most of these glasses are actually red/cyan, try that one too)"};
38 cvar_t r_stereo_redcyan = {0, "r_stereo_redcyan", "0", "red/cyan anaglyph stereo glasses, the kind given away at drive-in movies like Creature From The Black Lagoon In 3D"};
39 cvar_t r_stereo_redgreen = {0, "r_stereo_redgreen", "0", "red/green anaglyph stereo glasses (for those who don't mind yellow)"};
40 cvar_t scr_zoomwindow = {CVAR_SAVE, "scr_zoomwindow", "0", "displays a zoomed in overlay window"};
41 cvar_t scr_zoomwindow_viewsizex = {CVAR_SAVE, "scr_zoomwindow_viewsizex", "20", "horizontal viewsize of zoom window"};
42 cvar_t scr_zoomwindow_viewsizey = {CVAR_SAVE, "scr_zoomwindow_viewsizey", "20", "vertical viewsize of zoom window"};
43 cvar_t scr_zoomwindow_fov = {CVAR_SAVE, "scr_zoomwindow_fov", "20", "fov of zoom window"};
44
45
46 int jpeg_supported = false;
47
48 qboolean        scr_initialized;                // ready to draw
49
50 float           scr_con_current;
51
52 extern int      con_vislines;
53
54 void DrawCrosshair(int num);
55 static void SCR_ScreenShot_f (void);
56 static void R_Envmap_f (void);
57
58 // backend
59 void R_ClearScreen(void);
60
61 /*
62 ===============================================================================
63
64 CENTER PRINTING
65
66 ===============================================================================
67 */
68
69 char            scr_centerstring[MAX_INPUTLINE];
70 float           scr_centertime_start;   // for slow victory printing
71 float           scr_centertime_off;
72 int                     scr_center_lines;
73 int                     scr_erase_lines;
74 int                     scr_erase_center;
75
76 /*
77 ==============
78 SCR_CenterPrint
79
80 Called for important messages that should stay in the center of the screen
81 for a few moments
82 ==============
83 */
84 void SCR_CenterPrint(char *str)
85 {
86         strlcpy (scr_centerstring, str, sizeof (scr_centerstring));
87         scr_centertime_off = scr_centertime.value;
88         scr_centertime_start = cl.time;
89
90 // count the number of lines for centering
91         scr_center_lines = 1;
92         while (*str)
93         {
94                 if (*str == '\n')
95                         scr_center_lines++;
96                 str++;
97         }
98 }
99
100
101 void SCR_DrawCenterString (void)
102 {
103         char    *start;
104         int             l;
105         int             x, y;
106         int             remaining;
107         int             color;
108
109 // the finale prints the characters one at a time
110         if (cl.intermission)
111                 remaining = scr_printspeed.value * (cl.time - scr_centertime_start);
112         else
113                 remaining = 9999;
114
115         scr_erase_center = 0;
116         start = scr_centerstring;
117
118         if (remaining < 1)
119                 return;
120
121         if (scr_center_lines <= 4)
122                 y = vid_conheight.integer*0.35;
123         else
124                 y = 48;
125
126         color = -1;
127         do
128         {
129         // scan the width of the line
130                 for (l=0 ; l<vid_conwidth.integer/8 ; l++)
131                         if (start[l] == '\n' || !start[l])
132                                 break;
133                 x = (vid_conwidth.integer - l*8)/2;
134                 if (l > 0)
135                 {
136                         if (remaining < l)
137                                 l = remaining;
138                         DrawQ_ColoredString(x, y, start, l, 8, 8, 1, 1, 1, 1, 0, &color);
139                         remaining -= l;
140                         if (remaining <= 0)
141                                 return;
142                 }
143
144                 y += 8;
145
146                 while (*start && *start != '\n')
147                         start++;
148
149                 if (!*start)
150                         break;
151                 start++;                // skip the \n
152         } while (1);
153 }
154
155 void SCR_CheckDrawCenterString (void)
156 {
157         if (scr_center_lines > scr_erase_lines)
158                 scr_erase_lines = scr_center_lines;
159
160         scr_centertime_off -= host_frametime;
161
162         // don't draw if this is a normal stats-screen intermission,
163         // only if it is not an intermission, or a finale intermission
164         if (cl.intermission == 1)
165                 return;
166         if (scr_centertime_off <= 0 && !cl.intermission)
167                 return;
168         if (key_dest != key_game)
169                 return;
170
171         SCR_DrawCenterString ();
172 }
173
174 /*
175 ==============
176 SCR_DrawTurtle
177 ==============
178 */
179 void SCR_DrawTurtle (void)
180 {
181         static int      count;
182
183         if (cls.state != ca_connected)
184                 return;
185
186         if (!scr_showturtle.integer)
187                 return;
188
189         if (host_frametime < 0.1)
190         {
191                 count = 0;
192                 return;
193         }
194
195         count++;
196         if (count < 3)
197                 return;
198
199         DrawQ_Pic (0, 0, Draw_CachePic("gfx/turtle", false), 0, 0, 1, 1, 1, 1, 0);
200 }
201
202 /*
203 ==============
204 SCR_DrawNet
205 ==============
206 */
207 void SCR_DrawNet (void)
208 {
209         if (cls.state != ca_connected)
210                 return;
211         if (realtime - cl.last_received_message < 0.3)
212                 return;
213         if (cls.demoplayback)
214                 return;
215
216         DrawQ_Pic (64, 0, Draw_CachePic("gfx/net", false), 0, 0, 1, 1, 1, 1, 0);
217 }
218
219 /*
220 ==============
221 DrawPause
222 ==============
223 */
224 void SCR_DrawPause (void)
225 {
226         cachepic_t      *pic;
227
228         if (cls.state != ca_connected)
229                 return;
230
231         if (!scr_showpause.integer)             // turn off for screenshots
232                 return;
233
234         if (!cl.paused)
235                 return;
236
237         pic = Draw_CachePic ("gfx/pause", true);
238         DrawQ_Pic ((vid_conwidth.integer - pic->width)/2, (vid_conheight.integer - pic->height)/2, pic, 0, 0, 1, 1, 1, 1, 0);
239 }
240
241 /*
242 ==============
243 SCR_DrawBrand
244 ==============
245 */
246 void SCR_DrawBrand (void)
247 {
248         cachepic_t      *pic;
249         float           x, y;
250
251         if (!scr_showbrand.value)
252                 return;
253
254         pic = Draw_CachePic ("gfx/brand", true);
255
256         switch ((int)scr_showbrand.value)
257         {
258         case 1: // bottom left
259                 x = 0;
260                 y = vid_conheight.integer - pic->height;
261                 break;
262         case 2: // bottom centre
263                 x = (vid_conwidth.integer - pic->width) / 2;
264                 y = vid_conheight.integer - pic->height;
265                 break;
266         case 3: // bottom right
267                 x = vid_conwidth.integer - pic->width;
268                 y = vid_conheight.integer - pic->height;
269                 break;
270         case 4: // centre right
271                 x = vid_conwidth.integer - pic->width;
272                 y = (vid_conheight.integer - pic->height) / 2;
273                 break;
274         case 5: // top right
275                 x = vid_conwidth.integer - pic->width;
276                 y = 0;
277                 break;
278         case 6: // top centre
279                 x = (vid_conwidth.integer - pic->width) / 2;
280                 y = 0;
281                 break;
282         case 7: // top left
283                 x = 0;
284                 y = 0;
285                 break;
286         case 8: // centre left
287                 x = 0;
288                 y = (vid_conheight.integer - pic->height) / 2;
289                 break;
290         default:
291                 return;
292         }
293
294         DrawQ_Pic (x, y, pic, 0, 0, 1, 1, 1, 1, 0);
295 }
296
297 /*
298 ==============
299 SCR_DrawDownload
300 ==============
301 */
302 static void SCR_DrawDownload(void)
303 {
304         int len;
305         float x, y;
306         float size = 8;
307         char temp[256];
308         if (!cls.qw_downloadname[0])
309                 return;
310         dpsnprintf(temp, sizeof(temp), "Downloading %s ...  %3i%%\n", cls.qw_downloadname, cls.qw_downloadpercent);
311         len = (int)strlen(temp);
312         x = (vid_conwidth.integer - len*size) / 2;
313         y = vid_conheight.integer - size;
314         DrawQ_Pic(0, y, NULL, vid_conwidth.integer, size, 0, 0, 0, 0.5, 0);
315         DrawQ_String(x, y, temp, len, size, size, 1, 1, 1, 1, 0);
316 }
317
318 //=============================================================================
319
320
321 /*
322 ==================
323 SCR_SetUpToDrawConsole
324 ==================
325 */
326 void SCR_SetUpToDrawConsole (void)
327 {
328         // lines of console to display
329         float conlines;
330         static int framecounter = 0;
331
332         Con_CheckResize ();
333
334         if (scr_menuforcewhiledisconnected.integer && key_dest == key_game && cls.state == ca_disconnected)
335         {
336                 if (framecounter >= 2)
337                         MR_ToggleMenu_f();
338                 else
339                         framecounter++;
340         }
341         else
342                 framecounter = 0;
343
344         if (scr_conforcewhiledisconnected.integer && key_dest == key_game && cls.signon != SIGNONS)
345                 key_consoleactive |= KEY_CONSOLEACTIVE_FORCED;
346         else
347                 key_consoleactive &= ~KEY_CONSOLEACTIVE_FORCED;
348
349 // decide on the height of the console
350         if (key_consoleactive & KEY_CONSOLEACTIVE_USER)
351                 conlines = vid_conheight.integer/2;     // half screen
352         else
353                 conlines = 0;                           // none visible
354
355         if (scr_conspeed.value)
356         {
357                 if (scr_con_current > conlines)
358                 {
359                         scr_con_current -= scr_conspeed.value*host_realframetime;
360                         if (scr_con_current < conlines)
361                                 scr_con_current = conlines;
362
363                 }
364                 else if (scr_con_current < conlines)
365                 {
366                         scr_con_current += scr_conspeed.value*host_realframetime;
367                         if (scr_con_current > conlines)
368                                 scr_con_current = conlines;
369                 }
370         }
371         else
372                 scr_con_current = conlines;
373 }
374
375 /*
376 ==================
377 SCR_DrawConsole
378 ==================
379 */
380 void SCR_DrawConsole (void)
381 {
382         if (key_consoleactive & KEY_CONSOLEACTIVE_FORCED)
383         {
384                 // full screen
385                 Con_DrawConsole (vid_conheight.integer);
386         }
387         else if (scr_con_current)
388                 Con_DrawConsole (scr_con_current);
389         else
390         {
391                 con_vislines = 0;
392                 if (key_dest == key_game || key_dest == key_message)
393                         Con_DrawNotify ();      // only draw notify in game
394         }
395 }
396
397 /*
398 ===============
399 SCR_BeginLoadingPlaque
400
401 ================
402 */
403 void SCR_BeginLoadingPlaque (void)
404 {
405         // save console log up to this point to log_file if it was set by configs
406         Log_Start();
407
408         Host_StartVideo();
409         S_StopAllSounds();
410         SCR_UpdateLoadingScreen();
411 }
412
413 //=============================================================================
414
415 char r_speeds_string[1024];
416 int speedstringcount, r_timereport_active;
417 double r_timereport_temp = 0, r_timereport_current = 0, r_timereport_start = 0;
418
419 void R_TimeReport(char *desc)
420 {
421         char tempbuf[256];
422         int length;
423         int t;
424
425         if (r_speeds.integer < 2 || !r_timereport_active || r_showtrispass)
426                 return;
427
428         qglFinish();
429         r_timereport_temp = r_timereport_current;
430         r_timereport_current = Sys_DoubleTime();
431         t = (int) ((r_timereport_current - r_timereport_temp) * 1000000.0 + 0.5);
432
433         dpsnprintf(tempbuf, sizeof(tempbuf), "%8i %-11s", t, desc);
434         length = (int)strlen(tempbuf);
435         if (speedstringcount + length > (vid_conwidth.integer / 8))
436         {
437                 strlcat(r_speeds_string, "\n", sizeof(r_speeds_string));
438                 speedstringcount = 0;
439         }
440         strlcat(r_speeds_string, tempbuf, sizeof(r_speeds_string));
441         speedstringcount += length;
442 }
443
444 void R_TimeReport_Frame(void)
445 {
446         int i, j, lines, y;
447
448         if (r_speeds_string[0])
449         {
450                 if (r_timereport_active)
451                 {
452                         r_timereport_current = r_timereport_start;
453                         R_TimeReport("total");
454                 }
455
456                 if (r_speeds_string[strlen(r_speeds_string)-1] == '\n')
457                         r_speeds_string[strlen(r_speeds_string)-1] = 0;
458                 lines = 1;
459                 for (i = 0;r_speeds_string[i];i++)
460                         if (r_speeds_string[i] == '\n')
461                                 lines++;
462                 y = vid_conheight.integer - sb_lines - lines * 8;
463                 i = j = 0;
464                 DrawQ_Pic(0, y, NULL, vid_conwidth.integer, lines * 8, 0, 0, 0, 0.5, 0);
465                 while (r_speeds_string[i])
466                 {
467                         j = i;
468                         while (r_speeds_string[i] && r_speeds_string[i] != '\n')
469                                 i++;
470                         if (i - j > 0)
471                                 DrawQ_String(0, y, r_speeds_string + j, i - j, 8, 8, 1, 1, 1, 1, 0);
472                         if (r_speeds_string[i] == '\n')
473                                 i++;
474                         y += 8;
475                 }
476                 r_speeds_string[0] = 0;
477                 r_timereport_active = false;
478         }
479         if (r_speeds.integer && cls.signon == SIGNONS && cls.state == ca_connected)
480         {
481                 speedstringcount = 0;
482                 r_speeds_string[0] = 0;
483                 r_timereport_active = false;
484                 sprintf(r_speeds_string + strlen(r_speeds_string), "org:'%+8.2f %+8.2f %+8.2f' dir:'%+2.3f %+2.3f %+2.3f'\n", r_vieworigin[0], r_vieworigin[1], r_vieworigin[2], r_viewforward[0], r_viewforward[1], r_viewforward[2]);
485                 sprintf(r_speeds_string + strlen(r_speeds_string), "%5i entities%6i surfaces%6i triangles%5i leafs%5i portals%6i particles\n", renderstats.entities, renderstats.entities_surfaces, renderstats.entities_triangles, renderstats.world_leafs, renderstats.world_portals, renderstats.particles);
486                 sprintf(r_speeds_string + strlen(r_speeds_string), "%4i lights%4i clears%4i scissored%7i light%7i shadow%7i dynamic\n", renderstats.lights, renderstats.lights_clears, renderstats.lights_scissored, renderstats.lights_lighttriangles, renderstats.lights_shadowtriangles, renderstats.lights_dynamicshadowtriangles);
487                 if (renderstats.bloom)
488                         sprintf(r_speeds_string + strlen(r_speeds_string), "rendered%6i meshes%8i triangles bloompixels%8i copied%8i drawn\n", renderstats.meshes, renderstats.meshes_elements / 3, renderstats.bloom_copypixels, renderstats.bloom_drawpixels);
489                 else
490                         sprintf(r_speeds_string + strlen(r_speeds_string), "rendered%6i meshes%8i triangles\n", renderstats.meshes, renderstats.meshes_elements / 3);
491
492                 memset(&renderstats, 0, sizeof(renderstats));
493
494                 if (r_speeds.integer >= 2)
495                 {
496                         r_timereport_active = true;
497                         r_timereport_start = r_timereport_current = Sys_DoubleTime();
498                 }
499         }
500 }
501
502 /*
503 =================
504 SCR_SizeUp_f
505
506 Keybinding command
507 =================
508 */
509 void SCR_SizeUp_f (void)
510 {
511         Cvar_SetValue ("viewsize",scr_viewsize.value+10);
512 }
513
514
515 /*
516 =================
517 SCR_SizeDown_f
518
519 Keybinding command
520 =================
521 */
522 void SCR_SizeDown_f (void)
523 {
524         Cvar_SetValue ("viewsize",scr_viewsize.value-10);
525 }
526
527 void CL_Screen_Init(void)
528 {
529         Cvar_RegisterVariable (&scr_fov);
530         Cvar_RegisterVariable (&scr_viewsize);
531         Cvar_RegisterVariable (&scr_conspeed);
532         Cvar_RegisterVariable (&scr_conalpha);
533         Cvar_RegisterVariable (&scr_conbrightness);
534         Cvar_RegisterVariable (&scr_conforcewhiledisconnected);
535         Cvar_RegisterVariable (&scr_menuforcewhiledisconnected);
536         Cvar_RegisterVariable (&scr_showram);
537         Cvar_RegisterVariable (&scr_showturtle);
538         Cvar_RegisterVariable (&scr_showpause);
539         Cvar_RegisterVariable (&scr_showbrand);
540         Cvar_RegisterVariable (&scr_centertime);
541         Cvar_RegisterVariable (&scr_printspeed);
542         Cvar_RegisterVariable (&vid_conwidth);
543         Cvar_RegisterVariable (&vid_conheight);
544         Cvar_RegisterVariable (&vid_pixelheight);
545         Cvar_RegisterVariable (&scr_screenshot_jpeg);
546         Cvar_RegisterVariable (&scr_screenshot_jpeg_quality);
547         Cvar_RegisterVariable (&scr_screenshot_gamma);
548         Cvar_RegisterVariable (&cl_capturevideo);
549         Cvar_RegisterVariable (&cl_capturevideo_sound);
550         Cvar_RegisterVariable (&cl_capturevideo_fps);
551         Cvar_RegisterVariable (&cl_capturevideo_rawrgb);
552         Cvar_RegisterVariable (&cl_capturevideo_rawyv12);
553         Cvar_RegisterVariable (&r_letterbox);
554         Cvar_RegisterVariable(&r_stereo_separation);
555         Cvar_RegisterVariable(&r_stereo_sidebyside);
556         Cvar_RegisterVariable(&r_stereo_redblue);
557         Cvar_RegisterVariable(&r_stereo_redcyan);
558         Cvar_RegisterVariable(&r_stereo_redgreen);
559         Cvar_RegisterVariable(&scr_zoomwindow);
560         Cvar_RegisterVariable(&scr_zoomwindow_viewsizex);
561         Cvar_RegisterVariable(&scr_zoomwindow_viewsizey);
562         Cvar_RegisterVariable(&scr_zoomwindow_fov);
563
564         Cmd_AddCommand ("sizeup",SCR_SizeUp_f, "increase view size (increases viewsize cvar)");
565         Cmd_AddCommand ("sizedown",SCR_SizeDown_f, "decrease view size (decreases viewsize cvar)");
566         Cmd_AddCommand ("screenshot",SCR_ScreenShot_f, "takes a screenshot of the next rendered frame");
567         Cmd_AddCommand ("envmap", R_Envmap_f, "render a cubemap (skybox) of the current scene");
568
569         scr_initialized = true;
570 }
571
572 /*
573 ==================
574 SCR_ScreenShot_f
575 ==================
576 */
577 void SCR_ScreenShot_f (void)
578 {
579         static int shotnumber;
580         static char oldname[MAX_QPATH];
581         char base[MAX_QPATH];
582         char filename[MAX_QPATH];
583         unsigned char *buffer1;
584         unsigned char *buffer2;
585         unsigned char *buffer3;
586         qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
587
588         sprintf (base, "screenshots/%s", scr_screenshot_name.string);
589
590         if (strcmp (oldname, scr_screenshot_name.string))
591         {
592                 sprintf(oldname, "%s", scr_screenshot_name.string);
593                 shotnumber = 0;
594         }
595
596         // find a file name to save it to
597         for (;shotnumber < 1000000;shotnumber++)
598                 if (!FS_SysFileExists(va("%s/%s%06d.tga", fs_gamedir, base, shotnumber)) && !FS_SysFileExists(va("%s/%s%06d.jpg", fs_gamedir, base, shotnumber)))
599                         break;
600         if (shotnumber >= 1000000)
601         {
602                 Con_Print("SCR_ScreenShot_f: Couldn't create the image file\n");
603                 return;
604         }
605
606         sprintf(filename, "%s%06d.%s", base, shotnumber, jpeg ? "jpg" : "tga");
607
608         buffer1 = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 3);
609         buffer2 = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 3);
610         buffer3 = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 3 + 18);
611
612         if (SCR_ScreenShot (filename, buffer1, buffer2, buffer3, 0, 0, vid.width, vid.height, false, false, false, jpeg, true))
613                 Con_Printf("Wrote %s\n", filename);
614         else
615                 Con_Printf("unable to write %s\n", filename);
616
617         Mem_Free (buffer1);
618         Mem_Free (buffer2);
619         Mem_Free (buffer3);
620
621         shotnumber++;
622 }
623
624 void SCR_CaptureVideo_BeginVideo(void)
625 {
626         double gamma, g;
627         unsigned int i;
628         unsigned char out[44];
629         if (cls.capturevideo_active)
630                 return;
631         // soundrate is figured out on the first SoundFrame
632         cls.capturevideo_active = true;
633         cls.capturevideo_starttime = Sys_DoubleTime();
634         cls.capturevideo_framerate = bound(1, cl_capturevideo_fps.value, 1000);
635         cls.capturevideo_soundrate = 0;
636         cls.capturevideo_frame = 0;
637         cls.capturevideo_buffer = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * (3+3+3) + 18);
638         gamma = 1.0/scr_screenshot_gamma.value;
639
640         /*
641         for (i = 0;i < 256;i++)
642         {
643                 unsigned char j = (unsigned char)bound(0, 255*pow(i/255.0, gamma), 255);
644                 cls.capturevideo_rgbgammatable[0][i] = j;
645                 cls.capturevideo_rgbgammatable[1][i] = j;
646                 cls.capturevideo_rgbgammatable[2][i] = j;
647         }
648         */
649 /*
650 R = Y + 1.4075 * (Cr - 128);
651 G = Y + -0.3455 * (Cb - 128) + -0.7169 * (Cr - 128);
652 B = Y + 1.7790 * (Cb - 128);
653 Y = R *  .299 + G *  .587 + B *  .114;
654 Cb = R * -.169 + G * -.332 + B *  .500 + 128.;
655 Cr = R *  .500 + G * -.419 + B * -.0813 + 128.;
656 */
657         for (i = 0;i < 256;i++)
658         {
659                 g = 255*pow(i/255.0, gamma);
660                 // Y weights from RGB
661                 cls.capturevideo_rgbtoyuvscaletable[0][0][i] = (short)(g *  0.299);
662                 cls.capturevideo_rgbtoyuvscaletable[0][1][i] = (short)(g *  0.587);
663                 cls.capturevideo_rgbtoyuvscaletable[0][2][i] = (short)(g *  0.114);
664                 // Cb weights from RGB
665                 cls.capturevideo_rgbtoyuvscaletable[1][0][i] = (short)(g * -0.169);
666                 cls.capturevideo_rgbtoyuvscaletable[1][1][i] = (short)(g * -0.332);
667                 cls.capturevideo_rgbtoyuvscaletable[1][2][i] = (short)(g *  0.500);
668                 // Cr weights from RGB
669                 cls.capturevideo_rgbtoyuvscaletable[2][0][i] = (short)(g *  0.500);
670                 cls.capturevideo_rgbtoyuvscaletable[2][1][i] = (short)(g * -0.419);
671                 cls.capturevideo_rgbtoyuvscaletable[2][2][i] = (short)(g * -0.0813);
672                 // range reduction of YCbCr to valid signal range
673                 cls.capturevideo_yuvnormalizetable[0][i] = 16 + i * (236-16) / 256;
674                 cls.capturevideo_yuvnormalizetable[1][i] = 16 + i * (240-16) / 256;
675                 cls.capturevideo_yuvnormalizetable[2][i] = 16 + i * (240-16) / 256;
676         }
677
678         if (cl_capturevideo_rawrgb.integer)
679         {
680                 cls.capturevideo_format = CAPTUREVIDEOFORMAT_RAWRGB;
681                 cls.capturevideo_videofile = FS_Open ("video/dpvideo.rgb", "wb", false, true);
682         }
683         else if (cl_capturevideo_rawyv12.integer)
684         {
685                 cls.capturevideo_format = CAPTUREVIDEOFORMAT_RAWYV12;
686                 cls.capturevideo_videofile = FS_Open ("video/dpvideo.yv12", "wb", false, true);
687         }
688         else if (scr_screenshot_jpeg.integer)
689         {
690                 cls.capturevideo_format = CAPTUREVIDEOFORMAT_JPEG;
691                 cls.capturevideo_videofile = NULL;
692         }
693         else
694         {
695                 cls.capturevideo_format = CAPTUREVIDEOFORMAT_TARGA;
696                 cls.capturevideo_videofile = NULL;
697         }
698
699         if (cl_capturevideo_sound.integer)
700         {
701                 cls.capturevideo_soundfile = FS_Open ("video/dpvideo.wav", "wb", false, true);
702                 // wave header will be filled out when video ends
703                 memset(out, 0, 44);
704                 FS_Write (cls.capturevideo_soundfile, out, 44);
705         }
706         else
707                 cls.capturevideo_soundfile = NULL;
708 }
709
710 void SCR_CaptureVideo_EndVideo(void)
711 {
712         int i, n;
713         unsigned char out[44];
714         if (!cls.capturevideo_active)
715                 return;
716         cls.capturevideo_active = false;
717
718         if (cls.capturevideo_videofile)
719         {
720                 FS_Close(cls.capturevideo_videofile);
721                 cls.capturevideo_videofile = NULL;
722         }
723
724         // finish the wave file
725         if (cls.capturevideo_soundfile)
726         {
727                 i = (int)FS_Tell (cls.capturevideo_soundfile);
728                 //"RIFF", (int) unknown (chunk size), "WAVE",
729                 //"fmt ", (int) 16 (chunk size), (short) format 1 (uncompressed PCM), (short) 2 channels, (int) unknown rate, (int) unknown bytes per second, (short) 4 bytes per sample (channels * bytes per channel), (short) 16 bits per channel
730                 //"data", (int) unknown (chunk size)
731                 memcpy (out, "RIFF****WAVEfmt \x10\x00\x00\x00\x01\x00\x02\x00********\x04\x00\x10\0data****", 44);
732                 // the length of the whole RIFF chunk
733                 n = i - 8;
734                 out[4] = (n) & 0xFF;
735                 out[5] = (n >> 8) & 0xFF;
736                 out[6] = (n >> 16) & 0xFF;
737                 out[7] = (n >> 24) & 0xFF;
738                 // rate
739                 n = cls.capturevideo_soundrate;
740                 out[24] = (n) & 0xFF;
741                 out[25] = (n >> 8) & 0xFF;
742                 out[26] = (n >> 16) & 0xFF;
743                 out[27] = (n >> 24) & 0xFF;
744                 // bytes per second (rate * channels * bytes per channel)
745                 n = cls.capturevideo_soundrate * 2 * 2;
746                 out[28] = (n) & 0xFF;
747                 out[29] = (n >> 8) & 0xFF;
748                 out[30] = (n >> 16) & 0xFF;
749                 out[31] = (n >> 24) & 0xFF;
750                 // the length of the data chunk
751                 n = i - 44;
752                 out[40] = (n) & 0xFF;
753                 out[41] = (n >> 8) & 0xFF;
754                 out[42] = (n >> 16) & 0xFF;
755                 out[43] = (n >> 24) & 0xFF;
756                 FS_Seek (cls.capturevideo_soundfile, 0, SEEK_SET);
757                 FS_Write (cls.capturevideo_soundfile, out, 44);
758                 FS_Close (cls.capturevideo_soundfile);
759                 cls.capturevideo_soundfile = NULL;
760         }
761
762         if (cls.capturevideo_buffer)
763         {
764                 Mem_Free (cls.capturevideo_buffer);
765                 cls.capturevideo_buffer = NULL;
766         }
767
768         cls.capturevideo_starttime = 0;
769         cls.capturevideo_framerate = 0;
770         cls.capturevideo_frame = 0;
771 }
772
773 qboolean SCR_CaptureVideo_VideoFrame(int newframenum)
774 {
775         int x = 0, y = 0, width = vid.width, height = vid.height;
776         unsigned char *b, *out;
777         char filename[32];
778         int outoffset = (width/2)*(height/2);
779         //return SCR_ScreenShot(filename, cls.capturevideo_buffer, cls.capturevideo_buffer + vid.width * vid.height * 3, cls.capturevideo_buffer + vid.width * vid.height * 6, 0, 0, vid.width, vid.height, false, false, false, jpeg, true);
780         // speed is critical here, so do saving as directly as possible
781         switch (cls.capturevideo_format)
782         {
783         case CAPTUREVIDEOFORMAT_RAWYV12:
784                 // FIXME: width/height must be multiple of 2, enforce this?
785                 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cls.capturevideo_buffer);
786                 CHECKGLERROR
787                 // process one line at a time, and CbCr every other line at 2 pixel intervals
788                 for (y = 0;y < height;y++)
789                 {
790                         // 1x1 Y
791                         for (b = cls.capturevideo_buffer + (height-1-y)*width*3, out = cls.capturevideo_buffer + width*height*3 + y*width, x = 0;x < width;x++, b += 3, out++)
792                                 *out = cls.capturevideo_yuvnormalizetable[0][cls.capturevideo_rgbtoyuvscaletable[0][0][b[0]] + cls.capturevideo_rgbtoyuvscaletable[0][1][b[1]] + cls.capturevideo_rgbtoyuvscaletable[0][2][b[2]]];
793                         if ((y & 1) == 0)
794                         {
795                                 // 2x2 Cb and Cr planes
796 #if 1
797                                 // low quality, no averaging
798                                 for (b = cls.capturevideo_buffer + (height-2-y)*width*3, out = cls.capturevideo_buffer + width*height*3 + width*height + (y/2)*(width/2), x = 0;x < width/2;x++, b += 6, out++)
799                                 {
800                                         // Cr
801                                         out[0        ] = cls.capturevideo_yuvnormalizetable[2][cls.capturevideo_rgbtoyuvscaletable[2][0][b[0]] + cls.capturevideo_rgbtoyuvscaletable[2][1][b[1]] + cls.capturevideo_rgbtoyuvscaletable[2][2][b[2]] + 128];
802                                         // Cb
803                                         out[outoffset] = cls.capturevideo_yuvnormalizetable[1][cls.capturevideo_rgbtoyuvscaletable[1][0][b[0]] + cls.capturevideo_rgbtoyuvscaletable[1][1][b[1]] + cls.capturevideo_rgbtoyuvscaletable[1][2][b[2]] + 128];
804                                 }
805 #else
806                                 // high quality, averaging
807                                 int inpitch = width*3;
808                                 for (b = cls.capturevideo_buffer + (height-2-y)*width*3, out = cls.capturevideo_buffer + width*height*3 + width*height + (y/2)*(width/2), x = 0;x < width/2;x++, b += 6, out++)
809                                 {
810                                         int blockr, blockg, blockb;
811                                         blockr = (b[0] + b[3] + b[inpitch+0] + b[inpitch+3]) >> 2;
812                                         blockg = (b[1] + b[4] + b[inpitch+1] + b[inpitch+4]) >> 2;
813                                         blockb = (b[2] + b[5] + b[inpitch+2] + b[inpitch+5]) >> 2;
814                                         // Cr
815                                         out[0        ] = cls.capturevideo_yuvnormalizetable[2][cls.capturevideo_rgbtoyuvscaletable[2][0][blockr] + cls.capturevideo_rgbtoyuvscaletable[2][1][blockg] + cls.capturevideo_rgbtoyuvscaletable[2][2][blockb] + 128];
816                                         // Cb
817                                         out[outoffset] = cls.capturevideo_yuvnormalizetable[1][cls.capturevideo_rgbtoyuvscaletable[1][0][blockr] + cls.capturevideo_rgbtoyuvscaletable[1][1][blockg] + cls.capturevideo_rgbtoyuvscaletable[1][2][blockb] + 128];
818                                 }
819 #endif
820                         }
821                 }
822                 for (;cls.capturevideo_frame < newframenum;cls.capturevideo_frame++)
823                         if (!FS_Write (cls.capturevideo_videofile, cls.capturevideo_buffer + width*height*3, width*height+(width/2)*(height/2)*2))
824                                 return false;
825                 return true;
826         case CAPTUREVIDEOFORMAT_RAWRGB:
827                 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cls.capturevideo_buffer);
828                 CHECKGLERROR
829                 for (;cls.capturevideo_frame < newframenum;cls.capturevideo_frame++)
830                         if (!FS_Write (cls.capturevideo_videofile, cls.capturevideo_buffer, width*height*3))
831                                 return false;
832                 return true;
833         case CAPTUREVIDEOFORMAT_JPEG:
834                 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cls.capturevideo_buffer);
835                 CHECKGLERROR
836                 for (;cls.capturevideo_frame < newframenum;cls.capturevideo_frame++)
837                 {
838                         sprintf(filename, "video/dp%06d.jpg", cls.capturevideo_frame);
839                         if (!JPEG_SaveImage_preflipped (filename, width, height, cls.capturevideo_buffer))
840                                 return false;
841                 }
842                 return true;
843         case CAPTUREVIDEOFORMAT_TARGA:
844                 //return Image_WriteTGARGB_preflipped (filename, width, height, cls.capturevideo_buffer, cls.capturevideo_buffer + vid.width * vid.height * 3, );
845                 memset (cls.capturevideo_buffer, 0, 18);
846                 cls.capturevideo_buffer[2] = 2;         // uncompressed type
847                 cls.capturevideo_buffer[12] = (width >> 0) & 0xFF;
848                 cls.capturevideo_buffer[13] = (width >> 8) & 0xFF;
849                 cls.capturevideo_buffer[14] = (height >> 0) & 0xFF;
850                 cls.capturevideo_buffer[15] = (height >> 8) & 0xFF;
851                 cls.capturevideo_buffer[16] = 24;       // pixel size
852                 qglReadPixels (x, y, width, height, GL_BGR, GL_UNSIGNED_BYTE, cls.capturevideo_buffer + 18);
853                 CHECKGLERROR
854                 for (;cls.capturevideo_frame < newframenum;cls.capturevideo_frame++)
855                 {
856                         sprintf(filename, "video/dp%06d.tga", cls.capturevideo_frame);
857                         if (!FS_WriteFile (filename, cls.capturevideo_buffer, width*height*3 + 18))
858                                 return false;
859                 }
860                 return true;
861         default:
862                 return false;
863         }
864 }
865
866 void SCR_CaptureVideo_SoundFrame(unsigned char *bufstereo16le, size_t length, int rate)
867 {
868         if (!cls.capturevideo_soundfile)
869                 return;
870         cls.capturevideo_soundrate = rate;
871         if (FS_Write (cls.capturevideo_soundfile, bufstereo16le, 4 * length) < (fs_offset_t)(4 * length))
872         {
873                 Cvar_SetValueQuick(&cl_capturevideo, 0);
874                 Con_Printf("video sound saving failed on frame %i, out of disk space? stopping video capture.\n", cls.capturevideo_frame);
875                 SCR_CaptureVideo_EndVideo();
876         }
877 }
878
879 void SCR_CaptureVideo(void)
880 {
881         int newframenum;
882         if (cl_capturevideo.integer && r_render.integer)
883         {
884                 if (!cls.capturevideo_active)
885                         SCR_CaptureVideo_BeginVideo();
886                 if (cls.capturevideo_framerate != cl_capturevideo_fps.value)
887                 {
888                         Con_Printf("You can not change the video framerate while recording a video.\n");
889                         Cvar_SetValueQuick(&cl_capturevideo_fps, cls.capturevideo_framerate);
890                 }
891                 if (cls.capturevideo_soundfile)
892                 {
893                         // preserve sound sync by duplicating frames when running slow
894                         newframenum = (Sys_DoubleTime() - cls.capturevideo_starttime) * cls.capturevideo_framerate;
895                 }
896                 else
897                         newframenum = cls.capturevideo_frame + 1;
898                 // if falling behind more than one second, stop
899                 if (newframenum - cls.capturevideo_frame > (int)ceil(cls.capturevideo_framerate))
900                 {
901                         Cvar_SetValueQuick(&cl_capturevideo, 0);
902                         Con_Printf("video saving failed on frame %i, your machine is too slow for this capture speed.\n", cls.capturevideo_frame);
903                         SCR_CaptureVideo_EndVideo();
904                         return;
905                 }
906                 // write frames
907                 if (!SCR_CaptureVideo_VideoFrame(newframenum))
908                 {
909                         Cvar_SetValueQuick(&cl_capturevideo, 0);
910                         Con_Printf("video saving failed on frame %i, out of disk space? stopping video capture.\n", cls.capturevideo_frame);
911                         SCR_CaptureVideo_EndVideo();
912                 }
913         }
914         else if (cls.capturevideo_active)
915                 SCR_CaptureVideo_EndVideo();
916 }
917
918 /*
919 ===============
920 R_Envmap_f
921
922 Grab six views for environment mapping tests
923 ===============
924 */
925 struct envmapinfo_s
926 {
927         float angles[3];
928         char *name;
929         qboolean flipx, flipy, flipdiagonaly;
930 }
931 envmapinfo[12] =
932 {
933         {{  0,   0, 0}, "rt", false, false, false},
934         {{  0, 270, 0}, "ft", false, false, false},
935         {{  0, 180, 0}, "lf", false, false, false},
936         {{  0,  90, 0}, "bk", false, false, false},
937         {{-90, 180, 0}, "up",  true,  true, false},
938         {{ 90, 180, 0}, "dn",  true,  true, false},
939
940         {{  0,   0, 0}, "px",  true,  true,  true},
941         {{  0,  90, 0}, "py", false,  true, false},
942         {{  0, 180, 0}, "nx", false, false,  true},
943         {{  0, 270, 0}, "ny",  true, false, false},
944         {{-90, 180, 0}, "pz", false, false,  true},
945         {{ 90, 180, 0}, "nz", false, false,  true}
946 };
947
948 static void R_Envmap_f (void)
949 {
950         int j, size;
951         char filename[MAX_QPATH], basename[MAX_QPATH];
952         unsigned char *buffer1;
953         unsigned char *buffer2;
954         unsigned char *buffer3;
955
956         if (Cmd_Argc() != 3)
957         {
958                 Con_Print("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");
959                 return;
960         }
961
962         strlcpy (basename, Cmd_Argv(1), sizeof (basename));
963         size = atoi(Cmd_Argv(2));
964         if (size != 128 && size != 256 && size != 512 && size != 1024)
965         {
966                 Con_Print("envmap: size must be one of 128, 256, 512, or 1024\n");
967                 return;
968         }
969         if (size > vid.width || size > vid.height)
970         {
971                 Con_Print("envmap: your resolution is not big enough to render that size\n");
972                 return;
973         }
974
975         envmap = true;
976
977         r_refdef.x = 0;
978         r_refdef.y = 0;
979         r_refdef.width = size;
980         r_refdef.height = size;
981
982         r_refdef.frustum_x = tan(90 * M_PI / 360.0);
983         r_refdef.frustum_y = tan(90 * M_PI / 360.0);
984
985         buffer1 = (unsigned char *)Mem_Alloc(tempmempool, size * size * 3);
986         buffer2 = (unsigned char *)Mem_Alloc(tempmempool, size * size * 3);
987         buffer3 = (unsigned char *)Mem_Alloc(tempmempool, size * size * 3 + 18);
988
989         for (j = 0;j < 12;j++)
990         {
991                 sprintf(filename, "env/%s%s.tga", basename, envmapinfo[j].name);
992                 Matrix4x4_CreateFromQuakeEntity(&r_refdef.viewentitymatrix, r_vieworigin[0], r_vieworigin[1], r_vieworigin[2], envmapinfo[j].angles[0], envmapinfo[j].angles[1], envmapinfo[j].angles[2], 1);
993                 R_ClearScreen();
994                 R_Mesh_Start();
995                 R_RenderView();
996                 R_Mesh_Finish();
997                 SCR_ScreenShot(filename, buffer1, buffer2, buffer3, 0, vid.height - (r_refdef.y + r_refdef.height), size, size, envmapinfo[j].flipx, envmapinfo[j].flipy, envmapinfo[j].flipdiagonaly, false, false);
998         }
999
1000         Mem_Free (buffer1);
1001         Mem_Free (buffer2);
1002         Mem_Free (buffer3);
1003
1004         envmap = false;
1005 }
1006
1007 //=============================================================================
1008
1009 // LordHavoc: SHOWLMP stuff
1010 #define SHOWLMP_MAXLABELS 256
1011 typedef struct showlmp_s
1012 {
1013         qboolean        isactive;
1014         float           x;
1015         float           y;
1016         char            label[32];
1017         char            pic[128];
1018 }
1019 showlmp_t;
1020
1021 showlmp_t showlmp[SHOWLMP_MAXLABELS];
1022
1023 void SHOWLMP_decodehide(void)
1024 {
1025         int i;
1026         char *lmplabel;
1027         lmplabel = MSG_ReadString();
1028         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1029                 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
1030                 {
1031                         showlmp[i].isactive = false;
1032                         return;
1033                 }
1034 }
1035
1036 void SHOWLMP_decodeshow(void)
1037 {
1038         int i, k;
1039         char lmplabel[256], picname[256];
1040         float x, y;
1041         strlcpy (lmplabel,MSG_ReadString(), sizeof (lmplabel));
1042         strlcpy (picname, MSG_ReadString(), sizeof (picname));
1043         if (gamemode == GAME_NEHAHRA) // LordHavoc: nasty old legacy junk
1044         {
1045                 x = MSG_ReadByte();
1046                 y = MSG_ReadByte();
1047         }
1048         else
1049         {
1050                 x = MSG_ReadShort();
1051                 y = MSG_ReadShort();
1052         }
1053         k = -1;
1054         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1055                 if (showlmp[i].isactive)
1056                 {
1057                         if (strcmp(showlmp[i].label, lmplabel) == 0)
1058                         {
1059                                 k = i;
1060                                 break; // drop out to replace it
1061                         }
1062                 }
1063                 else if (k < 0) // find first empty one to replace
1064                         k = i;
1065         if (k < 0)
1066                 return; // none found to replace
1067         // change existing one
1068         showlmp[k].isactive = true;
1069         strlcpy (showlmp[k].label, lmplabel, sizeof (showlmp[k].label));
1070         strlcpy (showlmp[k].pic, picname, sizeof (showlmp[k].pic));
1071         showlmp[k].x = x;
1072         showlmp[k].y = y;
1073 }
1074
1075 void SHOWLMP_drawall(void)
1076 {
1077         int i;
1078         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1079                 if (showlmp[i].isactive)
1080                         DrawQ_Pic(showlmp[i].x, showlmp[i].y, Draw_CachePic(showlmp[i].pic, false), 0, 0, 1, 1, 1, 1, 0);
1081 }
1082
1083 void SHOWLMP_clear(void)
1084 {
1085         int i;
1086         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1087                 showlmp[i].isactive = false;
1088 }
1089
1090 /*
1091 ==============================================================================
1092
1093                                                 SCREEN SHOTS
1094
1095 ==============================================================================
1096 */
1097
1098 qboolean SCR_ScreenShot(char *filename, unsigned char *buffer1, unsigned char *buffer2, unsigned char *buffer3, int x, int y, int width, int height, qboolean flipx, qboolean flipy, qboolean flipdiagonal, qboolean jpeg, qboolean gammacorrect)
1099 {
1100         int     indices[3] = {0,1,2};
1101         qboolean ret;
1102
1103         if (!r_render.integer)
1104                 return false;
1105
1106         qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, buffer1);
1107         CHECKGLERROR
1108
1109         if (scr_screenshot_gamma.value != 1 && gammacorrect)
1110         {
1111                 int i;
1112                 double igamma = 1.0 / scr_screenshot_gamma.value;
1113                 unsigned char ramp[256];
1114                 for (i = 0;i < 256;i++)
1115                         ramp[i] = (unsigned char) (pow(i * (1.0 / 255.0), igamma) * 255.0);
1116                 for (i = 0;i < width*height*3;i++)
1117                         buffer1[i] = ramp[buffer1[i]];
1118         }
1119
1120         Image_CopyMux (buffer2, buffer1, width, height, flipx, flipy, flipdiagonal, 3, 3, indices);
1121
1122         if (jpeg)
1123                 ret = JPEG_SaveImage_preflipped (filename, width, height, buffer2);
1124         else
1125                 ret = Image_WriteTGARGB_preflipped (filename, width, height, buffer2, buffer3);
1126
1127         return ret;
1128 }
1129
1130 //=============================================================================
1131
1132 void R_ClearScreen(void)
1133 {
1134         if (r_render.integer)
1135         {
1136                 // clear to black
1137                 if (fogenabled)
1138                         qglClearColor(fogcolor[0],fogcolor[1],fogcolor[2],0);
1139                 else
1140                         qglClearColor(0,0,0,0);
1141                 CHECKGLERROR
1142                 qglClearDepth(1);CHECKGLERROR
1143                 if (gl_stencil)
1144                 {
1145                         // LordHavoc: we use a stencil centered around 128 instead of 0,
1146                         // to avoid clamping interfering with strange shadow volume
1147                         // drawing orders
1148                         qglClearStencil(128);CHECKGLERROR
1149                 }
1150                 // clear the screen
1151                 GL_Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | (gl_stencil ? GL_STENCIL_BUFFER_BIT : 0));
1152                 // set dithering mode
1153                 if (gl_dither.integer)
1154                 {
1155                         qglEnable(GL_DITHER);CHECKGLERROR
1156                 }
1157                 else
1158                 {
1159                         qglDisable(GL_DITHER);CHECKGLERROR
1160                 }
1161         }
1162 }
1163
1164 qboolean CL_VM_UpdateView (void);
1165 void SCR_DrawConsole (void);
1166 void R_Shadow_EditLights_DrawSelectedLightProperties(void);
1167
1168 int r_stereo_side;
1169
1170 void SCR_DrawScreen (void)
1171 {
1172         R_Mesh_Start();
1173
1174         if (r_timereport_active)
1175                 R_TimeReport("setup");
1176
1177         if (cls.signon == SIGNONS)
1178         {
1179                 float size;
1180
1181                 size = scr_viewsize.value * (1.0 / 100.0);
1182                 size = min(size, 1);
1183
1184                 if (r_stereo_sidebyside.integer)
1185                 {
1186                         r_refdef.width = vid.width * size / 2.5;
1187                         r_refdef.height = vid.height * size / 2.5 * (1 - bound(0, r_letterbox.value, 100) / 100);
1188                         r_refdef.x = (vid.width - r_refdef.width * 2.5) * 0.5;
1189                         r_refdef.y = (vid.height - r_refdef.height)/2;
1190                         if (r_stereo_side)
1191                                 r_refdef.x += r_refdef.width * 1.5;
1192                 }
1193                 else
1194                 {
1195                         r_refdef.width = vid.width * size;
1196                         r_refdef.height = vid.height * size * (1 - bound(0, r_letterbox.value, 100) / 100);
1197                         r_refdef.x = (vid.width - r_refdef.width)/2;
1198                         r_refdef.y = (vid.height - r_refdef.height)/2;
1199                 }
1200
1201                 // LordHavoc: viewzoom (zoom in for sniper rifles, etc)
1202                 // LordHavoc: this is designed to produce widescreen fov values
1203                 // when the screen is wider than 4/3 width/height aspect, to do
1204                 // this it simply assumes the requested fov is the vertical fov
1205                 // for a 4x3 display, if the ratio is not 4x3 this makes the fov
1206                 // higher/lower according to the ratio
1207                 r_refdef.frustum_y = tan(scr_fov.value * cl.viewzoom * M_PI / 360.0) * (3.0/4.0);
1208                 r_refdef.frustum_x = r_refdef.frustum_y * (float)r_refdef.width / (float)r_refdef.height / vid_pixelheight.value;
1209
1210                 r_refdef.frustum_x *= r_refdef.frustumscale_x;
1211                 r_refdef.frustum_y *= r_refdef.frustumscale_y;
1212
1213                 if(!CL_VM_UpdateView())
1214                         R_RenderView();
1215                 else
1216                         SCR_DrawConsole();
1217
1218                 if (scr_zoomwindow.integer)
1219                 {
1220                         float sizex = bound(10, scr_zoomwindow_viewsizex.value, 100) / 100.0;
1221                         float sizey = bound(10, scr_zoomwindow_viewsizey.value, 100) / 100.0;
1222                         r_refdef.width = vid.width * sizex;
1223                         r_refdef.height = vid.height * sizey;
1224                         r_refdef.x = (vid.width - r_refdef.width)/2;
1225                         r_refdef.y = 0;
1226
1227                         r_refdef.frustum_y = tan(scr_zoomwindow_fov.value * cl.viewzoom * M_PI / 360.0) * (3.0/4.0);
1228                         r_refdef.frustum_x = r_refdef.frustum_y * vid_pixelheight.value * (float)r_refdef.width / (float)r_refdef.height;
1229
1230                         r_refdef.frustum_x *= r_refdef.frustumscale_x;
1231                         r_refdef.frustum_y *= r_refdef.frustumscale_y;
1232
1233                         if(!CL_VM_UpdateView())
1234                                 R_RenderView();
1235                 }
1236         }
1237
1238         if (!r_stereo_sidebyside.integer)
1239         {
1240                 r_refdef.width = vid.width;
1241                 r_refdef.height = vid.height;
1242                 r_refdef.x = 0;
1243                 r_refdef.y = 0;
1244         }
1245
1246         // draw 2D stuff
1247         DrawQ_Begin();
1248
1249         //FIXME: force menu if nothing else to look at?
1250         //if (key_dest == key_game && cls.signon != SIGNONS && cls.state == ca_disconnected)
1251
1252         if (cls.signon == SIGNONS)
1253         {
1254                 SCR_DrawNet ();
1255                 SCR_DrawTurtle ();
1256                 SCR_DrawPause ();
1257                 if (!r_letterbox.value)
1258                         Sbar_Draw();
1259                 SHOWLMP_drawall();
1260                 SCR_CheckDrawCenterString();
1261         }
1262         MR_Draw();
1263         CL_DrawVideo();
1264         R_Shadow_EditLights_DrawSelectedLightProperties();
1265
1266         if(!csqc_loaded)
1267                 SCR_DrawConsole();
1268
1269         SCR_DrawBrand();
1270
1271         SCR_DrawDownload();
1272
1273         if (r_timereport_active)
1274                 R_TimeReport("2d");
1275
1276         if (cls.signon == SIGNONS)
1277                 R_TimeReport_Frame();
1278
1279         DrawQ_Finish();
1280
1281         R_DrawGamma();
1282
1283         R_Mesh_Finish();
1284
1285         if (r_timereport_active)
1286                 R_TimeReport("meshfinish");
1287 }
1288
1289 void SCR_UpdateLoadingScreen (void)
1290 {
1291         float x, y;
1292         cachepic_t *pic;
1293         rmeshstate_t m;
1294         float vertex3f[12];
1295         float texcoord2f[8];
1296         // don't do anything if not initialized yet
1297         if (vid_hidden)
1298                 return;
1299         r_showtrispass = 0;
1300         VID_UpdateGamma(false);
1301         qglViewport(0, 0, vid.width, vid.height);
1302         //qglDisable(GL_SCISSOR_TEST);
1303         //qglDepthMask(1);
1304         qglColorMask(1,1,1,1);
1305         //qglClearColor(0,0,0,0);
1306         //qglClear(GL_COLOR_BUFFER_BIT);
1307         //qglCullFace(GL_FRONT);
1308         //qglDisable(GL_CULL_FACE);
1309         //R_ClearScreen();
1310         R_Textures_Frame();
1311         GL_SetupView_Mode_Ortho(0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100);
1312         R_Mesh_Start();
1313         R_Mesh_Matrix(&identitymatrix);
1314         // draw the loading plaque
1315         pic = Draw_CachePic("gfx/loading", false);
1316         x = (vid_conwidth.integer - pic->width)/2;
1317         y = (vid_conheight.integer - pic->height)/2;
1318         GL_Color(1,1,1,1);
1319         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1320         GL_DepthTest(false);
1321         memset(&m, 0, sizeof(m));
1322         m.pointer_vertex = vertex3f;
1323         m.pointer_texcoord[0] = texcoord2f;
1324         m.tex[0] = R_GetTexture(pic->tex);
1325         R_Mesh_State(&m);
1326         vertex3f[2] = vertex3f[5] = vertex3f[8] = vertex3f[11] = 0;
1327         vertex3f[0] = vertex3f[9] = x;
1328         vertex3f[1] = vertex3f[4] = y;
1329         vertex3f[3] = vertex3f[6] = x + pic->width;
1330         vertex3f[7] = vertex3f[10] = y + pic->height;
1331         texcoord2f[0] = 0;texcoord2f[1] = 0;
1332         texcoord2f[2] = 1;texcoord2f[3] = 0;
1333         texcoord2f[4] = 1;texcoord2f[5] = 1;
1334         texcoord2f[6] = 0;texcoord2f[7] = 1;
1335         R_Mesh_Draw(0, 4, 2, polygonelements);
1336         R_Mesh_Finish();
1337         // refresh
1338         VID_Finish(false);
1339 }
1340
1341 void CL_UpdateScreen(void)
1342 {
1343         float conwidth, conheight;
1344
1345         if (vid_hidden)
1346                 return;
1347
1348         if (!scr_initialized || !con_initialized || vid_hidden)
1349                 return;                         // not initialized yet
1350
1351         // don't allow cheats in multiplayer
1352         if (!cl.islocalgame && cl.worldmodel)
1353         {
1354                 if (r_fullbright.integer != 0)
1355                         Cvar_Set ("r_fullbright", "0");
1356                 if (r_ambient.value != 0)
1357                         Cvar_Set ("r_ambient", "0");
1358         }
1359
1360         conwidth = bound(320, vid_conwidth.value, 2048);
1361         conheight = bound(200, vid_conheight.value, 1536);
1362         if (vid_conwidth.value != conwidth)
1363                 Cvar_SetValue("vid_conwidth", conwidth);
1364         if (vid_conheight.value != conheight)
1365                 Cvar_SetValue("vid_conheight", conheight);
1366
1367         // bound viewsize
1368         if (scr_viewsize.value < 30)
1369                 Cvar_Set ("viewsize","30");
1370         if (scr_viewsize.value > 120)
1371                 Cvar_Set ("viewsize","120");
1372
1373         // bound field of view
1374         if (scr_fov.value < 1)
1375                 Cvar_Set ("fov","1");
1376         if (scr_fov.value > 170)
1377                 Cvar_Set ("fov","170");
1378
1379         // validate r_textureunits cvar
1380         if (r_textureunits.integer > gl_textureunits)
1381                 Cvar_SetValueQuick(&r_textureunits, gl_textureunits);
1382         if (r_textureunits.integer < 1)
1383                 Cvar_SetValueQuick(&r_textureunits, 1);
1384
1385         // validate gl_combine cvar
1386         if (gl_combine.integer && !gl_combine_extension)
1387                 Cvar_SetValueQuick(&gl_combine, 0);
1388
1389         // intermission is always full screen
1390         if (cl.intermission)
1391                 sb_lines = 0;
1392         else
1393         {
1394                 if (scr_viewsize.value >= 120)
1395                         sb_lines = 0;           // no status bar at all
1396                 else if (scr_viewsize.value >= 110)
1397                         sb_lines = 24;          // no inventory
1398                 else
1399                         sb_lines = 24+16+8;
1400         }
1401
1402         r_refdef.colormask[0] = 1;
1403         r_refdef.colormask[1] = 1;
1404         r_refdef.colormask[2] = 1;
1405
1406         if (r_timereport_active)
1407                 R_TimeReport("other");
1408
1409         VID_UpdateGamma(false);
1410
1411         SCR_SetUpToDrawConsole();
1412
1413         if (r_timereport_active)
1414                 R_TimeReport("start");
1415
1416         r_showtrispass = 0;
1417
1418         CHECKGLERROR
1419         qglViewport(0, 0, vid.width, vid.height);
1420         qglDisable(GL_SCISSOR_TEST);
1421         qglDepthMask(1);
1422         qglColorMask(1,1,1,1);
1423         qglClearColor(0,0,0,0);
1424         qglClear(GL_COLOR_BUFFER_BIT);
1425         CHECKGLERROR
1426
1427         if (r_timereport_active)
1428                 R_TimeReport("clear");
1429
1430         if (r_stereo_redblue.integer || r_stereo_redgreen.integer || r_stereo_redcyan.integer || r_stereo_sidebyside.integer)
1431         {
1432                 matrix4x4_t originalmatrix = r_refdef.viewentitymatrix;
1433                 r_refdef.viewentitymatrix.m[0][3] = originalmatrix.m[0][3] + r_stereo_separation.value * -0.5f * r_refdef.viewentitymatrix.m[0][1];
1434                 r_refdef.viewentitymatrix.m[1][3] = originalmatrix.m[1][3] + r_stereo_separation.value * -0.5f * r_refdef.viewentitymatrix.m[1][1];
1435                 r_refdef.viewentitymatrix.m[2][3] = originalmatrix.m[2][3] + r_stereo_separation.value * -0.5f * r_refdef.viewentitymatrix.m[2][1];
1436
1437                 if (r_stereo_sidebyside.integer)
1438                         r_stereo_side = 0;
1439
1440                 if (r_stereo_redblue.integer || r_stereo_redgreen.integer || r_stereo_redcyan.integer)
1441                 {
1442                         r_refdef.colormask[0] = 1;
1443                         r_refdef.colormask[1] = 0;
1444                         r_refdef.colormask[2] = 0;
1445                 }
1446
1447                 SCR_DrawScreen();
1448
1449                 r_refdef.viewentitymatrix.m[0][3] = originalmatrix.m[0][3] + r_stereo_separation.value * 0.5f * r_refdef.viewentitymatrix.m[0][1];
1450                 r_refdef.viewentitymatrix.m[1][3] = originalmatrix.m[1][3] + r_stereo_separation.value * 0.5f * r_refdef.viewentitymatrix.m[1][1];
1451                 r_refdef.viewentitymatrix.m[2][3] = originalmatrix.m[2][3] + r_stereo_separation.value * 0.5f * r_refdef.viewentitymatrix.m[2][1];
1452
1453                 if (r_stereo_sidebyside.integer)
1454                         r_stereo_side = 1;
1455
1456                 if (r_stereo_redblue.integer || r_stereo_redgreen.integer || r_stereo_redcyan.integer)
1457                 {
1458                         r_refdef.colormask[0] = 0;
1459                         r_refdef.colormask[1] = r_stereo_redcyan.integer || r_stereo_redgreen.integer;
1460                         r_refdef.colormask[2] = r_stereo_redcyan.integer || r_stereo_redblue.integer;
1461                 }
1462
1463                 SCR_DrawScreen();
1464
1465                 r_refdef.viewentitymatrix = originalmatrix;
1466         }
1467         else
1468                 SCR_DrawScreen();
1469
1470         SCR_CaptureVideo();
1471
1472         VID_Finish(true);
1473         if (r_timereport_active)
1474                 R_TimeReport("finish");
1475 }
1476
1477 void CL_Screen_NewMap(void)
1478 {
1479         SHOWLMP_clear();
1480 }