]> icculus.org git repositories - divverent/darkplaces.git/blob - cl_screen.c
rewrote timing code, now a much better and very different sleeping method, no longer...
[divverent/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", "0", "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 = (int)(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 = (int)(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 -= cl.realframetime;
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 (cl.realframetime < 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", true), 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", true), 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*cl.realframetime;
360                         if (scr_con_current < conlines)
361                                 scr_con_current = conlines;
362                 }
363                 else if (scr_con_current < conlines)
364                 {
365                         scr_con_current += scr_conspeed.value*cl.realframetime;
366                         if (scr_con_current > conlines)
367                                 scr_con_current = conlines;
368                 }
369         }
370         else
371                 scr_con_current = conlines;
372 }
373
374 /*
375 ==================
376 SCR_DrawConsole
377 ==================
378 */
379 void SCR_DrawConsole (void)
380 {
381         if (key_consoleactive & KEY_CONSOLEACTIVE_FORCED)
382         {
383                 // full screen
384                 Con_DrawConsole (vid_conheight.integer);
385         }
386         else if (scr_con_current)
387                 Con_DrawConsole ((int)scr_con_current);
388         else
389         {
390                 con_vislines = 0;
391                 if (key_dest == key_game || key_dest == key_message)
392                         Con_DrawNotify ();      // only draw notify in game
393         }
394 }
395
396 /*
397 ===============
398 SCR_BeginLoadingPlaque
399
400 ================
401 */
402 void SCR_BeginLoadingPlaque (void)
403 {
404         // save console log up to this point to log_file if it was set by configs
405         Log_Start();
406
407         Host_StartVideo();
408         S_StopAllSounds();
409         SCR_UpdateLoadingScreen();
410 }
411
412 //=============================================================================
413
414 char r_speeds_string[1024];
415 int speedstringcount, r_timereport_active;
416 double r_timereport_temp = 0, r_timereport_current = 0, r_timereport_start = 0;
417
418 void R_TimeReport(char *desc)
419 {
420         char tempbuf[256];
421         int length;
422         int t;
423
424         if (r_speeds.integer < 2 || !r_timereport_active)
425                 return;
426
427         qglFinish();
428         r_timereport_temp = r_timereport_current;
429         r_timereport_current = Sys_DoubleTime();
430         t = (int) ((r_timereport_current - r_timereport_temp) * 1000000.0 + 0.5);
431
432         dpsnprintf(tempbuf, sizeof(tempbuf), "%8i %-11s", t, desc);
433         length = (int)strlen(tempbuf);
434         if (speedstringcount + length > (vid_conwidth.integer / 8))
435         {
436                 strlcat(r_speeds_string, "\n", sizeof(r_speeds_string));
437                 speedstringcount = 0;
438         }
439         strlcat(r_speeds_string, tempbuf, sizeof(r_speeds_string));
440         speedstringcount += length;
441 }
442
443 void R_TimeReport_Frame(void)
444 {
445         int i, j, lines, y;
446
447         if (r_speeds_string[0])
448         {
449                 if (r_timereport_active)
450                 {
451                         r_timereport_current = r_timereport_start;
452                         R_TimeReport("total");
453                 }
454
455                 if (r_speeds_string[strlen(r_speeds_string)-1] == '\n')
456                         r_speeds_string[strlen(r_speeds_string)-1] = 0;
457                 lines = 1;
458                 for (i = 0;r_speeds_string[i];i++)
459                         if (r_speeds_string[i] == '\n')
460                                 lines++;
461                 y = vid_conheight.integer - sb_lines - lines * 8;
462                 i = j = 0;
463                 DrawQ_Pic(0, y, NULL, vid_conwidth.integer, lines * 8, 0, 0, 0, 0.5, 0);
464                 while (r_speeds_string[i])
465                 {
466                         j = i;
467                         while (r_speeds_string[i] && r_speeds_string[i] != '\n')
468                                 i++;
469                         if (i - j > 0)
470                                 DrawQ_String(0, y, r_speeds_string + j, i - j, 8, 8, 1, 1, 1, 1, 0);
471                         if (r_speeds_string[i] == '\n')
472                                 i++;
473                         y += 8;
474                 }
475                 r_speeds_string[0] = 0;
476                 r_timereport_active = false;
477         }
478         if (r_speeds.integer && cls.signon == SIGNONS && cls.state == ca_connected)
479         {
480                 speedstringcount = 0;
481                 r_speeds_string[0] = 0;
482                 r_timereport_active = false;
483                 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]);
484                 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);
485                 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);
486                 if (renderstats.bloom)
487                         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);
488                 else
489                         sprintf(r_speeds_string + strlen(r_speeds_string), "rendered%6i meshes%8i triangles\n", renderstats.meshes, renderstats.meshes_elements / 3);
490
491                 memset(&renderstats, 0, sizeof(renderstats));
492
493                 if (r_speeds.integer >= 2)
494                 {
495                         r_timereport_active = true;
496                         r_timereport_start = r_timereport_current = Sys_DoubleTime();
497                 }
498         }
499 }
500
501 /*
502 =================
503 SCR_SizeUp_f
504
505 Keybinding command
506 =================
507 */
508 void SCR_SizeUp_f (void)
509 {
510         Cvar_SetValue ("viewsize",scr_viewsize.value+10);
511 }
512
513
514 /*
515 =================
516 SCR_SizeDown_f
517
518 Keybinding command
519 =================
520 */
521 void SCR_SizeDown_f (void)
522 {
523         Cvar_SetValue ("viewsize",scr_viewsize.value-10);
524 }
525
526 void CL_Screen_Init(void)
527 {
528         Cvar_RegisterVariable (&scr_fov);
529         Cvar_RegisterVariable (&scr_viewsize);
530         Cvar_RegisterVariable (&scr_conspeed);
531         Cvar_RegisterVariable (&scr_conalpha);
532         Cvar_RegisterVariable (&scr_conbrightness);
533         Cvar_RegisterVariable (&scr_conforcewhiledisconnected);
534         Cvar_RegisterVariable (&scr_menuforcewhiledisconnected);
535         Cvar_RegisterVariable (&scr_showram);
536         Cvar_RegisterVariable (&scr_showturtle);
537         Cvar_RegisterVariable (&scr_showpause);
538         Cvar_RegisterVariable (&scr_showbrand);
539         Cvar_RegisterVariable (&scr_centertime);
540         Cvar_RegisterVariable (&scr_printspeed);
541         Cvar_RegisterVariable (&vid_conwidth);
542         Cvar_RegisterVariable (&vid_conheight);
543         Cvar_RegisterVariable (&vid_pixelheight);
544         Cvar_RegisterVariable (&scr_screenshot_jpeg);
545         Cvar_RegisterVariable (&scr_screenshot_jpeg_quality);
546         Cvar_RegisterVariable (&scr_screenshot_gamma);
547         Cvar_RegisterVariable (&cl_capturevideo);
548         Cvar_RegisterVariable (&cl_capturevideo_sound);
549         Cvar_RegisterVariable (&cl_capturevideo_fps);
550         Cvar_RegisterVariable (&cl_capturevideo_rawrgb);
551         Cvar_RegisterVariable (&cl_capturevideo_rawyv12);
552         Cvar_RegisterVariable (&r_letterbox);
553         Cvar_RegisterVariable(&r_stereo_separation);
554         Cvar_RegisterVariable(&r_stereo_sidebyside);
555         Cvar_RegisterVariable(&r_stereo_redblue);
556         Cvar_RegisterVariable(&r_stereo_redcyan);
557         Cvar_RegisterVariable(&r_stereo_redgreen);
558         Cvar_RegisterVariable(&scr_zoomwindow);
559         Cvar_RegisterVariable(&scr_zoomwindow_viewsizex);
560         Cvar_RegisterVariable(&scr_zoomwindow_viewsizey);
561         Cvar_RegisterVariable(&scr_zoomwindow_fov);
562
563         Cmd_AddCommand ("sizeup",SCR_SizeUp_f, "increase view size (increases viewsize cvar)");
564         Cmd_AddCommand ("sizedown",SCR_SizeDown_f, "decrease view size (decreases viewsize cvar)");
565         Cmd_AddCommand ("screenshot",SCR_ScreenShot_f, "takes a screenshot of the next rendered frame");
566         Cmd_AddCommand ("envmap", R_Envmap_f, "render a cubemap (skybox) of the current scene");
567
568         scr_initialized = true;
569 }
570
571 /*
572 ==================
573 SCR_ScreenShot_f
574 ==================
575 */
576 void SCR_ScreenShot_f (void)
577 {
578         static int shotnumber;
579         static char oldname[MAX_QPATH];
580         char base[MAX_QPATH];
581         char filename[MAX_QPATH];
582         unsigned char *buffer1;
583         unsigned char *buffer2;
584         unsigned char *buffer3;
585         qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
586
587         sprintf (base, "screenshots/%s", scr_screenshot_name.string);
588
589         if (strcmp (oldname, scr_screenshot_name.string))
590         {
591                 sprintf(oldname, "%s", scr_screenshot_name.string);
592                 shotnumber = 0;
593         }
594
595         // find a file name to save it to
596         for (;shotnumber < 1000000;shotnumber++)
597                 if (!FS_SysFileExists(va("%s/%s%06d.tga", fs_gamedir, base, shotnumber)) && !FS_SysFileExists(va("%s/%s%06d.jpg", fs_gamedir, base, shotnumber)))
598                         break;
599         if (shotnumber >= 1000000)
600         {
601                 Con_Print("SCR_ScreenShot_f: Couldn't create the image file\n");
602                 return;
603         }
604
605         sprintf(filename, "%s%06d.%s", base, shotnumber, jpeg ? "jpg" : "tga");
606
607         buffer1 = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 3);
608         buffer2 = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 3);
609         buffer3 = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 3 + 18);
610
611         if (SCR_ScreenShot (filename, buffer1, buffer2, buffer3, 0, 0, vid.width, vid.height, false, false, false, jpeg, true))
612                 Con_Printf("Wrote %s\n", filename);
613         else
614                 Con_Printf("unable to write %s\n", filename);
615
616         Mem_Free (buffer1);
617         Mem_Free (buffer2);
618         Mem_Free (buffer3);
619
620         shotnumber++;
621 }
622
623 void SCR_CaptureVideo_BeginVideo(void)
624 {
625         double gamma, g;
626         unsigned int i;
627         unsigned char out[44];
628         if (cls.capturevideo_active)
629                 return;
630         // soundrate is figured out on the first SoundFrame
631         cls.capturevideo_active = true;
632         cls.capturevideo_starttime = Sys_DoubleTime();
633         cls.capturevideo_framerate = bound(1, cl_capturevideo_fps.value, 1000);
634         cls.capturevideo_soundrate = 0;
635         cls.capturevideo_frame = 0;
636         cls.capturevideo_buffer = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * (3+3+3) + 18);
637         gamma = 1.0/scr_screenshot_gamma.value;
638
639         /*
640         for (i = 0;i < 256;i++)
641         {
642                 unsigned char j = (unsigned char)bound(0, 255*pow(i/255.0, gamma), 255);
643                 cls.capturevideo_rgbgammatable[0][i] = j;
644                 cls.capturevideo_rgbgammatable[1][i] = j;
645                 cls.capturevideo_rgbgammatable[2][i] = j;
646         }
647         */
648 /*
649 R = Y + 1.4075 * (Cr - 128);
650 G = Y + -0.3455 * (Cb - 128) + -0.7169 * (Cr - 128);
651 B = Y + 1.7790 * (Cb - 128);
652 Y = R *  .299 + G *  .587 + B *  .114;
653 Cb = R * -.169 + G * -.332 + B *  .500 + 128.;
654 Cr = R *  .500 + G * -.419 + B * -.0813 + 128.;
655 */
656         for (i = 0;i < 256;i++)
657         {
658                 g = 255*pow(i/255.0, gamma);
659                 // Y weights from RGB
660                 cls.capturevideo_rgbtoyuvscaletable[0][0][i] = (short)(g *  0.299);
661                 cls.capturevideo_rgbtoyuvscaletable[0][1][i] = (short)(g *  0.587);
662                 cls.capturevideo_rgbtoyuvscaletable[0][2][i] = (short)(g *  0.114);
663                 // Cb weights from RGB
664                 cls.capturevideo_rgbtoyuvscaletable[1][0][i] = (short)(g * -0.169);
665                 cls.capturevideo_rgbtoyuvscaletable[1][1][i] = (short)(g * -0.332);
666                 cls.capturevideo_rgbtoyuvscaletable[1][2][i] = (short)(g *  0.500);
667                 // Cr weights from RGB
668                 cls.capturevideo_rgbtoyuvscaletable[2][0][i] = (short)(g *  0.500);
669                 cls.capturevideo_rgbtoyuvscaletable[2][1][i] = (short)(g * -0.419);
670                 cls.capturevideo_rgbtoyuvscaletable[2][2][i] = (short)(g * -0.0813);
671                 // range reduction of YCbCr to valid signal range
672                 cls.capturevideo_yuvnormalizetable[0][i] = 16 + i * (236-16) / 256;
673                 cls.capturevideo_yuvnormalizetable[1][i] = 16 + i * (240-16) / 256;
674                 cls.capturevideo_yuvnormalizetable[2][i] = 16 + i * (240-16) / 256;
675         }
676
677         if (cl_capturevideo_rawrgb.integer)
678         {
679                 cls.capturevideo_format = CAPTUREVIDEOFORMAT_RAWRGB;
680                 cls.capturevideo_videofile = FS_Open ("video/dpvideo.rgb", "wb", false, true);
681         }
682         else if (cl_capturevideo_rawyv12.integer)
683         {
684                 cls.capturevideo_format = CAPTUREVIDEOFORMAT_RAWYV12;
685                 cls.capturevideo_videofile = FS_Open ("video/dpvideo.yv12", "wb", false, true);
686         }
687         else if (scr_screenshot_jpeg.integer)
688         {
689                 cls.capturevideo_format = CAPTUREVIDEOFORMAT_JPEG;
690                 cls.capturevideo_videofile = NULL;
691         }
692         else
693         {
694                 cls.capturevideo_format = CAPTUREVIDEOFORMAT_TARGA;
695                 cls.capturevideo_videofile = NULL;
696         }
697
698         if (cl_capturevideo_sound.integer)
699         {
700                 cls.capturevideo_soundfile = FS_Open ("video/dpvideo.wav", "wb", false, true);
701                 // wave header will be filled out when video ends
702                 memset(out, 0, 44);
703                 FS_Write (cls.capturevideo_soundfile, out, 44);
704         }
705         else
706                 cls.capturevideo_soundfile = NULL;
707 }
708
709 void SCR_CaptureVideo_EndVideo(void)
710 {
711         int i, n;
712         unsigned char out[44];
713         if (!cls.capturevideo_active)
714                 return;
715         cls.capturevideo_active = false;
716
717         if (cls.capturevideo_videofile)
718         {
719                 FS_Close(cls.capturevideo_videofile);
720                 cls.capturevideo_videofile = NULL;
721         }
722
723         // finish the wave file
724         if (cls.capturevideo_soundfile)
725         {
726                 i = (int)FS_Tell (cls.capturevideo_soundfile);
727                 //"RIFF", (int) unknown (chunk size), "WAVE",
728                 //"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
729                 //"data", (int) unknown (chunk size)
730                 memcpy (out, "RIFF****WAVEfmt \x10\x00\x00\x00\x01\x00\x02\x00********\x04\x00\x10\0data****", 44);
731                 // the length of the whole RIFF chunk
732                 n = i - 8;
733                 out[4] = (n) & 0xFF;
734                 out[5] = (n >> 8) & 0xFF;
735                 out[6] = (n >> 16) & 0xFF;
736                 out[7] = (n >> 24) & 0xFF;
737                 // rate
738                 n = cls.capturevideo_soundrate;
739                 out[24] = (n) & 0xFF;
740                 out[25] = (n >> 8) & 0xFF;
741                 out[26] = (n >> 16) & 0xFF;
742                 out[27] = (n >> 24) & 0xFF;
743                 // bytes per second (rate * channels * bytes per channel)
744                 n = cls.capturevideo_soundrate * 2 * 2;
745                 out[28] = (n) & 0xFF;
746                 out[29] = (n >> 8) & 0xFF;
747                 out[30] = (n >> 16) & 0xFF;
748                 out[31] = (n >> 24) & 0xFF;
749                 // the length of the data chunk
750                 n = i - 44;
751                 out[40] = (n) & 0xFF;
752                 out[41] = (n >> 8) & 0xFF;
753                 out[42] = (n >> 16) & 0xFF;
754                 out[43] = (n >> 24) & 0xFF;
755                 FS_Seek (cls.capturevideo_soundfile, 0, SEEK_SET);
756                 FS_Write (cls.capturevideo_soundfile, out, 44);
757                 FS_Close (cls.capturevideo_soundfile);
758                 cls.capturevideo_soundfile = NULL;
759         }
760
761         if (cls.capturevideo_buffer)
762         {
763                 Mem_Free (cls.capturevideo_buffer);
764                 cls.capturevideo_buffer = NULL;
765         }
766
767         cls.capturevideo_starttime = 0;
768         cls.capturevideo_framerate = 0;
769         cls.capturevideo_frame = 0;
770 }
771
772 qboolean SCR_CaptureVideo_VideoFrame(int newframenum)
773 {
774         int x = 0, y = 0, width = vid.width, height = vid.height;
775         unsigned char *b, *out;
776         char filename[32];
777         int outoffset = (width/2)*(height/2);
778         //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);
779         // speed is critical here, so do saving as directly as possible
780         switch (cls.capturevideo_format)
781         {
782         case CAPTUREVIDEOFORMAT_RAWYV12:
783                 // FIXME: width/height must be multiple of 2, enforce this?
784                 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cls.capturevideo_buffer);
785                 CHECKGLERROR
786                 // process one line at a time, and CbCr every other line at 2 pixel intervals
787                 for (y = 0;y < height;y++)
788                 {
789                         // 1x1 Y
790                         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++)
791                                 *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]]];
792                         if ((y & 1) == 0)
793                         {
794                                 // 2x2 Cb and Cr planes
795 #if 1
796                                 // low quality, no averaging
797                                 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++)
798                                 {
799                                         // Cr
800                                         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];
801                                         // Cb
802                                         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];
803                                 }
804 #else
805                                 // high quality, averaging
806                                 int inpitch = width*3;
807                                 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++)
808                                 {
809                                         int blockr, blockg, blockb;
810                                         blockr = (b[0] + b[3] + b[inpitch+0] + b[inpitch+3]) >> 2;
811                                         blockg = (b[1] + b[4] + b[inpitch+1] + b[inpitch+4]) >> 2;
812                                         blockb = (b[2] + b[5] + b[inpitch+2] + b[inpitch+5]) >> 2;
813                                         // Cr
814                                         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];
815                                         // Cb
816                                         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];
817                                 }
818 #endif
819                         }
820                 }
821                 for (;cls.capturevideo_frame < newframenum;cls.capturevideo_frame++)
822                         if (!FS_Write (cls.capturevideo_videofile, cls.capturevideo_buffer + width*height*3, width*height+(width/2)*(height/2)*2))
823                                 return false;
824                 return true;
825         case CAPTUREVIDEOFORMAT_RAWRGB:
826                 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cls.capturevideo_buffer);
827                 CHECKGLERROR
828                 for (;cls.capturevideo_frame < newframenum;cls.capturevideo_frame++)
829                         if (!FS_Write (cls.capturevideo_videofile, cls.capturevideo_buffer, width*height*3))
830                                 return false;
831                 return true;
832         case CAPTUREVIDEOFORMAT_JPEG:
833                 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cls.capturevideo_buffer);
834                 CHECKGLERROR
835                 for (;cls.capturevideo_frame < newframenum;cls.capturevideo_frame++)
836                 {
837                         sprintf(filename, "video/dp%06d.jpg", cls.capturevideo_frame);
838                         if (!JPEG_SaveImage_preflipped (filename, width, height, cls.capturevideo_buffer))
839                                 return false;
840                 }
841                 return true;
842         case CAPTUREVIDEOFORMAT_TARGA:
843                 //return Image_WriteTGARGB_preflipped (filename, width, height, cls.capturevideo_buffer, cls.capturevideo_buffer + vid.width * vid.height * 3, );
844                 memset (cls.capturevideo_buffer, 0, 18);
845                 cls.capturevideo_buffer[2] = 2;         // uncompressed type
846                 cls.capturevideo_buffer[12] = (width >> 0) & 0xFF;
847                 cls.capturevideo_buffer[13] = (width >> 8) & 0xFF;
848                 cls.capturevideo_buffer[14] = (height >> 0) & 0xFF;
849                 cls.capturevideo_buffer[15] = (height >> 8) & 0xFF;
850                 cls.capturevideo_buffer[16] = 24;       // pixel size
851                 qglReadPixels (x, y, width, height, GL_BGR, GL_UNSIGNED_BYTE, cls.capturevideo_buffer + 18);
852                 CHECKGLERROR
853                 for (;cls.capturevideo_frame < newframenum;cls.capturevideo_frame++)
854                 {
855                         sprintf(filename, "video/dp%06d.tga", cls.capturevideo_frame);
856                         if (!FS_WriteFile (filename, cls.capturevideo_buffer, width*height*3 + 18))
857                                 return false;
858                 }
859                 return true;
860         default:
861                 return false;
862         }
863 }
864
865 void SCR_CaptureVideo_SoundFrame(unsigned char *bufstereo16le, size_t length, int rate)
866 {
867         if (!cls.capturevideo_soundfile)
868                 return;
869         cls.capturevideo_soundrate = rate;
870         if (FS_Write (cls.capturevideo_soundfile, bufstereo16le, 4 * length) < (fs_offset_t)(4 * length))
871         {
872                 Cvar_SetValueQuick(&cl_capturevideo, 0);
873                 Con_Printf("video sound saving failed on frame %i, out of disk space? stopping video capture.\n", cls.capturevideo_frame);
874                 SCR_CaptureVideo_EndVideo();
875         }
876 }
877
878 void SCR_CaptureVideo(void)
879 {
880         int newframenum;
881         if (cl_capturevideo.integer && r_render.integer)
882         {
883                 if (!cls.capturevideo_active)
884                         SCR_CaptureVideo_BeginVideo();
885                 if (cls.capturevideo_framerate != cl_capturevideo_fps.value)
886                 {
887                         Con_Printf("You can not change the video framerate while recording a video.\n");
888                         Cvar_SetValueQuick(&cl_capturevideo_fps, cls.capturevideo_framerate);
889                 }
890                 if (cls.capturevideo_soundfile)
891                 {
892                         // preserve sound sync by duplicating frames when running slow
893                         newframenum = (int)((Sys_DoubleTime() - cls.capturevideo_starttime) * cls.capturevideo_framerate);
894                 }
895                 else
896                         newframenum = cls.capturevideo_frame + 1;
897                 // if falling behind more than one second, stop
898                 if (newframenum - cls.capturevideo_frame > (int)ceil(cls.capturevideo_framerate))
899                 {
900                         Cvar_SetValueQuick(&cl_capturevideo, 0);
901                         Con_Printf("video saving failed on frame %i, your machine is too slow for this capture speed.\n", cls.capturevideo_frame);
902                         SCR_CaptureVideo_EndVideo();
903                         return;
904                 }
905                 // write frames
906                 if (!SCR_CaptureVideo_VideoFrame(newframenum))
907                 {
908                         Cvar_SetValueQuick(&cl_capturevideo, 0);
909                         Con_Printf("video saving failed on frame %i, out of disk space? stopping video capture.\n", cls.capturevideo_frame);
910                         SCR_CaptureVideo_EndVideo();
911                 }
912         }
913         else if (cls.capturevideo_active)
914                 SCR_CaptureVideo_EndVideo();
915 }
916
917 /*
918 ===============
919 R_Envmap_f
920
921 Grab six views for environment mapping tests
922 ===============
923 */
924 struct envmapinfo_s
925 {
926         float angles[3];
927         char *name;
928         qboolean flipx, flipy, flipdiagonaly;
929 }
930 envmapinfo[12] =
931 {
932         {{  0,   0, 0}, "rt", false, false, false},
933         {{  0, 270, 0}, "ft", false, false, false},
934         {{  0, 180, 0}, "lf", false, false, false},
935         {{  0,  90, 0}, "bk", false, false, false},
936         {{-90, 180, 0}, "up",  true,  true, false},
937         {{ 90, 180, 0}, "dn",  true,  true, false},
938
939         {{  0,   0, 0}, "px",  true,  true,  true},
940         {{  0,  90, 0}, "py", false,  true, false},
941         {{  0, 180, 0}, "nx", false, false,  true},
942         {{  0, 270, 0}, "ny",  true, false, false},
943         {{-90, 180, 0}, "pz", false, false,  true},
944         {{ 90, 180, 0}, "nz", false, false,  true}
945 };
946
947 static void R_Envmap_f (void)
948 {
949         int j, size;
950         char filename[MAX_QPATH], basename[MAX_QPATH];
951         unsigned char *buffer1;
952         unsigned char *buffer2;
953         unsigned char *buffer3;
954
955         if (Cmd_Argc() != 3)
956         {
957                 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");
958                 return;
959         }
960
961         strlcpy (basename, Cmd_Argv(1), sizeof (basename));
962         size = atoi(Cmd_Argv(2));
963         if (size != 128 && size != 256 && size != 512 && size != 1024)
964         {
965                 Con_Print("envmap: size must be one of 128, 256, 512, or 1024\n");
966                 return;
967         }
968         if (size > vid.width || size > vid.height)
969         {
970                 Con_Print("envmap: your resolution is not big enough to render that size\n");
971                 return;
972         }
973
974         envmap = true;
975
976         r_refdef.x = 0;
977         r_refdef.y = 0;
978         r_refdef.width = size;
979         r_refdef.height = size;
980
981         r_refdef.frustum_x = tan(90 * M_PI / 360.0);
982         r_refdef.frustum_y = tan(90 * M_PI / 360.0);
983
984         buffer1 = (unsigned char *)Mem_Alloc(tempmempool, size * size * 3);
985         buffer2 = (unsigned char *)Mem_Alloc(tempmempool, size * size * 3);
986         buffer3 = (unsigned char *)Mem_Alloc(tempmempool, size * size * 3 + 18);
987
988         for (j = 0;j < 12;j++)
989         {
990                 sprintf(filename, "env/%s%s.tga", basename, envmapinfo[j].name);
991                 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);
992                 R_ClearScreen();
993                 R_Mesh_Start();
994                 R_RenderView();
995                 R_Mesh_Finish();
996                 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);
997         }
998
999         Mem_Free (buffer1);
1000         Mem_Free (buffer2);
1001         Mem_Free (buffer3);
1002
1003         envmap = false;
1004 }
1005
1006 //=============================================================================
1007
1008 // LordHavoc: SHOWLMP stuff
1009 #define SHOWLMP_MAXLABELS 256
1010 typedef struct showlmp_s
1011 {
1012         qboolean        isactive;
1013         float           x;
1014         float           y;
1015         char            label[32];
1016         char            pic[128];
1017 }
1018 showlmp_t;
1019
1020 showlmp_t showlmp[SHOWLMP_MAXLABELS];
1021
1022 void SHOWLMP_decodehide(void)
1023 {
1024         int i;
1025         char *lmplabel;
1026         lmplabel = MSG_ReadString();
1027         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1028                 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
1029                 {
1030                         showlmp[i].isactive = false;
1031                         return;
1032                 }
1033 }
1034
1035 void SHOWLMP_decodeshow(void)
1036 {
1037         int i, k;
1038         char lmplabel[256], picname[256];
1039         float x, y;
1040         strlcpy (lmplabel,MSG_ReadString(), sizeof (lmplabel));
1041         strlcpy (picname, MSG_ReadString(), sizeof (picname));
1042         if (gamemode == GAME_NEHAHRA) // LordHavoc: nasty old legacy junk
1043         {
1044                 x = MSG_ReadByte();
1045                 y = MSG_ReadByte();
1046         }
1047         else
1048         {
1049                 x = MSG_ReadShort();
1050                 y = MSG_ReadShort();
1051         }
1052         k = -1;
1053         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1054                 if (showlmp[i].isactive)
1055                 {
1056                         if (strcmp(showlmp[i].label, lmplabel) == 0)
1057                         {
1058                                 k = i;
1059                                 break; // drop out to replace it
1060                         }
1061                 }
1062                 else if (k < 0) // find first empty one to replace
1063                         k = i;
1064         if (k < 0)
1065                 return; // none found to replace
1066         // change existing one
1067         showlmp[k].isactive = true;
1068         strlcpy (showlmp[k].label, lmplabel, sizeof (showlmp[k].label));
1069         strlcpy (showlmp[k].pic, picname, sizeof (showlmp[k].pic));
1070         showlmp[k].x = x;
1071         showlmp[k].y = y;
1072 }
1073
1074 void SHOWLMP_drawall(void)
1075 {
1076         int i;
1077         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1078                 if (showlmp[i].isactive)
1079                         DrawQ_Pic(showlmp[i].x, showlmp[i].y, Draw_CachePic(showlmp[i].pic, true), 0, 0, 1, 1, 1, 1, 0);
1080 }
1081
1082 void SHOWLMP_clear(void)
1083 {
1084         int i;
1085         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1086                 showlmp[i].isactive = false;
1087 }
1088
1089 /*
1090 ==============================================================================
1091
1092                                                 SCREEN SHOTS
1093
1094 ==============================================================================
1095 */
1096
1097 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)
1098 {
1099         int     indices[3] = {0,1,2};
1100         qboolean ret;
1101
1102         if (!r_render.integer)
1103                 return false;
1104
1105         qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, buffer1);
1106         CHECKGLERROR
1107
1108         if (scr_screenshot_gamma.value != 1 && gammacorrect)
1109         {
1110                 int i;
1111                 double igamma = 1.0 / scr_screenshot_gamma.value;
1112                 unsigned char ramp[256];
1113                 for (i = 0;i < 256;i++)
1114                         ramp[i] = (unsigned char) (pow(i * (1.0 / 255.0), igamma) * 255.0);
1115                 for (i = 0;i < width*height*3;i++)
1116                         buffer1[i] = ramp[buffer1[i]];
1117         }
1118
1119         Image_CopyMux (buffer2, buffer1, width, height, flipx, flipy, flipdiagonal, 3, 3, indices);
1120
1121         if (jpeg)
1122                 ret = JPEG_SaveImage_preflipped (filename, width, height, buffer2);
1123         else
1124                 ret = Image_WriteTGARGB_preflipped (filename, width, height, buffer2, buffer3);
1125
1126         return ret;
1127 }
1128
1129 //=============================================================================
1130
1131 void R_ClearScreen(void)
1132 {
1133         if (r_render.integer)
1134         {
1135                 // clear to black
1136                 if (fogenabled)
1137                         qglClearColor(fogcolor[0],fogcolor[1],fogcolor[2],0);
1138                 else
1139                         qglClearColor(0,0,0,0);
1140                 CHECKGLERROR
1141                 qglClearDepth(1);CHECKGLERROR
1142                 if (gl_stencil)
1143                 {
1144                         // LordHavoc: we use a stencil centered around 128 instead of 0,
1145                         // to avoid clamping interfering with strange shadow volume
1146                         // drawing orders
1147                         qglClearStencil(128);CHECKGLERROR
1148                 }
1149                 // clear the screen
1150                 GL_Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | (gl_stencil ? GL_STENCIL_BUFFER_BIT : 0));
1151                 // set dithering mode
1152                 if (gl_dither.integer)
1153                 {
1154                         qglEnable(GL_DITHER);CHECKGLERROR
1155                 }
1156                 else
1157                 {
1158                         qglDisable(GL_DITHER);CHECKGLERROR
1159                 }
1160         }
1161 }
1162
1163 qboolean CL_VM_UpdateView (void);
1164 void SCR_DrawConsole (void);
1165 void R_Shadow_EditLights_DrawSelectedLightProperties(void);
1166
1167 int r_stereo_side;
1168
1169 void SCR_DrawScreen (void)
1170 {
1171         R_Mesh_Start();
1172
1173         if (r_timereport_active)
1174                 R_TimeReport("setup");
1175
1176         if (cls.signon == SIGNONS)
1177         {
1178                 float size;
1179
1180                 size = scr_viewsize.value * (1.0 / 100.0);
1181                 size = min(size, 1);
1182
1183                 if (r_stereo_sidebyside.integer)
1184                 {
1185                         r_refdef.width = (int)(vid.width * size / 2.5);
1186                         r_refdef.height = (int)(vid.height * size / 2.5 * (1 - bound(0, r_letterbox.value, 100) / 100));
1187                         r_refdef.x = (int)((vid.width - r_refdef.width * 2.5) * 0.5);
1188                         r_refdef.y = (int)((vid.height - r_refdef.height)/2);
1189                         if (r_stereo_side)
1190                                 r_refdef.x += (int)(r_refdef.width * 1.5);
1191                 }
1192                 else
1193                 {
1194                         r_refdef.width = (int)(vid.width * size);
1195                         r_refdef.height = (int)(vid.height * size * (1 - bound(0, r_letterbox.value, 100) / 100));
1196                         r_refdef.x = (int)((vid.width - r_refdef.width)/2);
1197                         r_refdef.y = (int)((vid.height - r_refdef.height)/2);
1198                 }
1199
1200                 // LordHavoc: viewzoom (zoom in for sniper rifles, etc)
1201                 // LordHavoc: this is designed to produce widescreen fov values
1202                 // when the screen is wider than 4/3 width/height aspect, to do
1203                 // this it simply assumes the requested fov is the vertical fov
1204                 // for a 4x3 display, if the ratio is not 4x3 this makes the fov
1205                 // higher/lower according to the ratio
1206                 r_refdef.frustum_y = tan(scr_fov.value * cl.viewzoom * M_PI / 360.0) * (3.0/4.0);
1207                 r_refdef.frustum_x = r_refdef.frustum_y * (float)r_refdef.width / (float)r_refdef.height / vid_pixelheight.value;
1208
1209                 r_refdef.frustum_x *= r_refdef.frustumscale_x;
1210                 r_refdef.frustum_y *= r_refdef.frustumscale_y;
1211
1212                 if(!CL_VM_UpdateView())
1213                         R_RenderView();
1214                 else
1215                         SCR_DrawConsole();
1216
1217                 if (scr_zoomwindow.integer)
1218                 {
1219                         float sizex = bound(10, scr_zoomwindow_viewsizex.value, 100) / 100.0;
1220                         float sizey = bound(10, scr_zoomwindow_viewsizey.value, 100) / 100.0;
1221                         r_refdef.width = (int)(vid.width * sizex);
1222                         r_refdef.height = (int)(vid.height * sizey);
1223                         r_refdef.x = (int)((vid.width - r_refdef.width)/2);
1224                         r_refdef.y = 0;
1225
1226                         r_refdef.frustum_y = tan(scr_zoomwindow_fov.value * cl.viewzoom * M_PI / 360.0) * (3.0/4.0);
1227                         r_refdef.frustum_x = r_refdef.frustum_y * vid_pixelheight.value * (float)r_refdef.width / (float)r_refdef.height;
1228
1229                         r_refdef.frustum_x *= r_refdef.frustumscale_x;
1230                         r_refdef.frustum_y *= r_refdef.frustumscale_y;
1231
1232                         if(!CL_VM_UpdateView())
1233                                 R_RenderView();
1234                 }
1235         }
1236
1237         if (!r_stereo_sidebyside.integer)
1238         {
1239                 r_refdef.width = vid.width;
1240                 r_refdef.height = vid.height;
1241                 r_refdef.x = 0;
1242                 r_refdef.y = 0;
1243         }
1244
1245         // draw 2D stuff
1246         DrawQ_Begin();
1247
1248         //FIXME: force menu if nothing else to look at?
1249         //if (key_dest == key_game && cls.signon != SIGNONS && cls.state == ca_disconnected)
1250
1251         if (cls.signon == SIGNONS)
1252         {
1253                 SCR_DrawNet ();
1254                 SCR_DrawTurtle ();
1255                 SCR_DrawPause ();
1256                 if (!r_letterbox.value)
1257                         Sbar_Draw();
1258                 SHOWLMP_drawall();
1259                 SCR_CheckDrawCenterString();
1260         }
1261         MR_Draw();
1262         CL_DrawVideo();
1263         R_Shadow_EditLights_DrawSelectedLightProperties();
1264
1265         if(!csqc_loaded)
1266                 SCR_DrawConsole();
1267
1268         SCR_DrawBrand();
1269
1270         SCR_DrawDownload();
1271
1272         if (r_timereport_active)
1273                 R_TimeReport("2d");
1274
1275         if (cls.signon == SIGNONS)
1276                 R_TimeReport_Frame();
1277
1278         DrawQ_Finish();
1279
1280         R_DrawGamma();
1281
1282         R_Mesh_Finish();
1283
1284         if (r_timereport_active)
1285                 R_TimeReport("meshfinish");
1286 }
1287
1288 void SCR_UpdateLoadingScreen (void)
1289 {
1290         float x, y;
1291         cachepic_t *pic;
1292         float vertex3f[12];
1293         float texcoord2f[8];
1294         // don't do anything if not initialized yet
1295         if (vid_hidden)
1296                 return;
1297         qglViewport(0, 0, vid.width, vid.height);
1298         //qglDisable(GL_SCISSOR_TEST);
1299         //qglDepthMask(1);
1300         qglColorMask(1,1,1,1);
1301         //qglClearColor(0,0,0,0);
1302         //qglClear(GL_COLOR_BUFFER_BIT);
1303         //qglCullFace(GL_FRONT);
1304         //qglDisable(GL_CULL_FACE);
1305         //R_ClearScreen();
1306         R_Textures_Frame();
1307         GL_SetupView_Mode_Ortho(0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100);
1308         R_Mesh_Start();
1309         R_Mesh_Matrix(&identitymatrix);
1310         // draw the loading plaque
1311         pic = Draw_CachePic("gfx/loading", true);
1312         x = (vid_conwidth.integer - pic->width)/2;
1313         y = (vid_conheight.integer - pic->height)/2;
1314         GL_Color(1,1,1,1);
1315         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1316         GL_DepthTest(false);
1317         R_Mesh_VertexPointer(vertex3f);
1318         R_Mesh_ColorPointer(NULL);
1319         R_Mesh_ResetTextureState();
1320         R_Mesh_TexBind(0, R_GetTexture(pic->tex));
1321         R_Mesh_TexCoordPointer(0, 2, texcoord2f);
1322         vertex3f[2] = vertex3f[5] = vertex3f[8] = vertex3f[11] = 0;
1323         vertex3f[0] = vertex3f[9] = x;
1324         vertex3f[1] = vertex3f[4] = y;
1325         vertex3f[3] = vertex3f[6] = x + pic->width;
1326         vertex3f[7] = vertex3f[10] = y + pic->height;
1327         texcoord2f[0] = 0;texcoord2f[1] = 0;
1328         texcoord2f[2] = 1;texcoord2f[3] = 0;
1329         texcoord2f[4] = 1;texcoord2f[5] = 1;
1330         texcoord2f[6] = 0;texcoord2f[7] = 1;
1331         R_Mesh_Draw(0, 4, 2, polygonelements);
1332         R_Mesh_Finish();
1333         // refresh
1334         VID_Finish(false);
1335 }
1336
1337 void CL_UpdateScreen(void)
1338 {
1339         float conwidth, conheight;
1340
1341         if (vid_hidden)
1342                 return;
1343
1344         if (!scr_initialized || !con_initialized || vid_hidden)
1345                 return;                         // not initialized yet
1346
1347         // don't allow cheats in multiplayer
1348         if (!cl.islocalgame && cl.worldmodel)
1349         {
1350                 if (r_fullbright.integer != 0)
1351                         Cvar_Set ("r_fullbright", "0");
1352                 if (r_ambient.value != 0)
1353                         Cvar_Set ("r_ambient", "0");
1354         }
1355
1356         conwidth = bound(320, vid_conwidth.value, 2048);
1357         conheight = bound(200, vid_conheight.value, 1536);
1358         if (vid_conwidth.value != conwidth)
1359                 Cvar_SetValue("vid_conwidth", conwidth);
1360         if (vid_conheight.value != conheight)
1361                 Cvar_SetValue("vid_conheight", conheight);
1362
1363         // bound viewsize
1364         if (scr_viewsize.value < 30)
1365                 Cvar_Set ("viewsize","30");
1366         if (scr_viewsize.value > 120)
1367                 Cvar_Set ("viewsize","120");
1368
1369         // bound field of view
1370         if (scr_fov.value < 1)
1371                 Cvar_Set ("fov","1");
1372         if (scr_fov.value > 170)
1373                 Cvar_Set ("fov","170");
1374
1375         // validate r_textureunits cvar
1376         if (r_textureunits.integer > gl_textureunits)
1377                 Cvar_SetValueQuick(&r_textureunits, gl_textureunits);
1378         if (r_textureunits.integer < 1)
1379                 Cvar_SetValueQuick(&r_textureunits, 1);
1380
1381         // validate gl_combine cvar
1382         if (gl_combine.integer && !gl_combine_extension)
1383                 Cvar_SetValueQuick(&gl_combine, 0);
1384
1385         // intermission is always full screen
1386         if (cl.intermission)
1387                 sb_lines = 0;
1388         else
1389         {
1390                 if (scr_viewsize.value >= 120)
1391                         sb_lines = 0;           // no status bar at all
1392                 else if (scr_viewsize.value >= 110)
1393                         sb_lines = 24;          // no inventory
1394                 else
1395                         sb_lines = 24+16+8;
1396         }
1397
1398         r_refdef.colormask[0] = 1;
1399         r_refdef.colormask[1] = 1;
1400         r_refdef.colormask[2] = 1;
1401
1402         if (r_timereport_active)
1403                 R_TimeReport("other");
1404
1405         SCR_SetUpToDrawConsole();
1406
1407         if (r_timereport_active)
1408                 R_TimeReport("start");
1409
1410         CHECKGLERROR
1411         qglViewport(0, 0, vid.width, vid.height);
1412         qglDisable(GL_SCISSOR_TEST);
1413         qglDepthMask(1);
1414         qglColorMask(1,1,1,1);
1415         qglClearColor(0,0,0,0);
1416         qglClear(GL_COLOR_BUFFER_BIT);
1417         CHECKGLERROR
1418
1419         if (r_timereport_active)
1420                 R_TimeReport("clear");
1421
1422         if (r_stereo_redblue.integer || r_stereo_redgreen.integer || r_stereo_redcyan.integer || r_stereo_sidebyside.integer)
1423         {
1424                 matrix4x4_t originalmatrix = r_refdef.viewentitymatrix;
1425                 r_refdef.viewentitymatrix.m[0][3] = originalmatrix.m[0][3] + r_stereo_separation.value * -0.5f * r_refdef.viewentitymatrix.m[0][1];
1426                 r_refdef.viewentitymatrix.m[1][3] = originalmatrix.m[1][3] + r_stereo_separation.value * -0.5f * r_refdef.viewentitymatrix.m[1][1];
1427                 r_refdef.viewentitymatrix.m[2][3] = originalmatrix.m[2][3] + r_stereo_separation.value * -0.5f * r_refdef.viewentitymatrix.m[2][1];
1428
1429                 if (r_stereo_sidebyside.integer)
1430                         r_stereo_side = 0;
1431
1432                 if (r_stereo_redblue.integer || r_stereo_redgreen.integer || r_stereo_redcyan.integer)
1433                 {
1434                         r_refdef.colormask[0] = 1;
1435                         r_refdef.colormask[1] = 0;
1436                         r_refdef.colormask[2] = 0;
1437                 }
1438
1439                 SCR_DrawScreen();
1440
1441                 r_refdef.viewentitymatrix.m[0][3] = originalmatrix.m[0][3] + r_stereo_separation.value * 0.5f * r_refdef.viewentitymatrix.m[0][1];
1442                 r_refdef.viewentitymatrix.m[1][3] = originalmatrix.m[1][3] + r_stereo_separation.value * 0.5f * r_refdef.viewentitymatrix.m[1][1];
1443                 r_refdef.viewentitymatrix.m[2][3] = originalmatrix.m[2][3] + r_stereo_separation.value * 0.5f * r_refdef.viewentitymatrix.m[2][1];
1444
1445                 if (r_stereo_sidebyside.integer)
1446                         r_stereo_side = 1;
1447
1448                 if (r_stereo_redblue.integer || r_stereo_redgreen.integer || r_stereo_redcyan.integer)
1449                 {
1450                         r_refdef.colormask[0] = 0;
1451                         r_refdef.colormask[1] = r_stereo_redcyan.integer || r_stereo_redgreen.integer;
1452                         r_refdef.colormask[2] = r_stereo_redcyan.integer || r_stereo_redblue.integer;
1453                 }
1454
1455                 SCR_DrawScreen();
1456
1457                 r_refdef.viewentitymatrix = originalmatrix;
1458         }
1459         else
1460                 SCR_DrawScreen();
1461
1462         SCR_CaptureVideo();
1463
1464         VID_Finish(true);
1465         if (r_timereport_active)
1466                 R_TimeReport("finish");
1467 }
1468
1469 void CL_Screen_NewMap(void)
1470 {
1471         SHOWLMP_clear();
1472 }