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