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