]> icculus.org git repositories - divverent/darkplaces.git/blob - cl_screen.c
fix crash when entering "color" command while playing a demo
[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         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         // clear to black
1118         CHECKGLERROR
1119         if (r_refdef.fogenabled)
1120         {
1121                 qglClearColor(r_refdef.fogcolor[0],r_refdef.fogcolor[1],r_refdef.fogcolor[2],0);CHECKGLERROR
1122         }
1123         else
1124         {
1125                 qglClearColor(0,0,0,0);CHECKGLERROR
1126         }
1127         qglClearDepth(1);CHECKGLERROR
1128         if (gl_stencil)
1129         {
1130                 // LordHavoc: we use a stencil centered around 128 instead of 0,
1131                 // to avoid clamping interfering with strange shadow volume
1132                 // drawing orders
1133                 qglClearStencil(128);CHECKGLERROR
1134         }
1135         // clear the screen
1136         if (r_render.integer)
1137                 GL_Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | (gl_stencil ? GL_STENCIL_BUFFER_BIT : 0));
1138         // set dithering mode
1139         if (gl_dither.integer)
1140         {
1141                 qglEnable(GL_DITHER);CHECKGLERROR
1142         }
1143         else
1144         {
1145                 qglDisable(GL_DITHER);CHECKGLERROR
1146         }
1147 }
1148
1149 qboolean CL_VM_UpdateView (void);
1150 void SCR_DrawConsole (void);
1151 void R_Shadow_EditLights_DrawSelectedLightProperties(void);
1152
1153 int r_stereo_side;
1154
1155 void SCR_DrawScreen (void)
1156 {
1157         R_Mesh_Start();
1158
1159         if (r_timereport_active)
1160                 R_TimeReport("setup");
1161
1162         R_UpdateVariables();
1163
1164         if (cls.signon == SIGNONS)
1165         {
1166                 float size;
1167
1168                 size = scr_viewsize.value * (1.0 / 100.0);
1169                 size = min(size, 1);
1170
1171                 if (r_stereo_sidebyside.integer)
1172                 {
1173                         r_view.width = (int)(vid.width * size / 2.5);
1174                         r_view.height = (int)(vid.height * size / 2.5 * (1 - bound(0, r_letterbox.value, 100) / 100));
1175                         r_view.depth = 1;
1176                         r_view.x = (int)((vid.width - r_view.width * 2.5) * 0.5);
1177                         r_view.y = (int)((vid.height - r_view.height)/2);
1178                         r_view.z = 0;
1179                         if (r_stereo_side)
1180                                 r_view.x += (int)(r_view.width * 1.5);
1181                 }
1182                 else
1183                 {
1184                         r_view.width = (int)(vid.width * size);
1185                         r_view.height = (int)(vid.height * size * (1 - bound(0, r_letterbox.value, 100) / 100));
1186                         r_view.depth = 1;
1187                         r_view.x = (int)((vid.width - r_view.width)/2);
1188                         r_view.y = (int)((vid.height - r_view.height)/2);
1189                         r_view.z = 0;
1190                 }
1191
1192                 // LordHavoc: viewzoom (zoom in for sniper rifles, etc)
1193                 // LordHavoc: this is designed to produce widescreen fov values
1194                 // when the screen is wider than 4/3 width/height aspect, to do
1195                 // this it simply assumes the requested fov is the vertical fov
1196                 // for a 4x3 display, if the ratio is not 4x3 this makes the fov
1197                 // higher/lower according to the ratio
1198                 r_view.frustum_y = tan(scr_fov.value * cl.viewzoom * M_PI / 360.0) * (3.0/4.0);
1199                 r_view.frustum_x = r_view.frustum_y * (float)r_view.width / (float)r_view.height / vid_pixelheight.value;
1200
1201                 r_view.frustum_x *= r_refdef.frustumscale_x;
1202                 r_view.frustum_y *= r_refdef.frustumscale_y;
1203
1204                 if(!CL_VM_UpdateView())
1205                         R_RenderView();
1206                 else
1207                         SCR_DrawConsole();
1208
1209                 if (scr_zoomwindow.integer)
1210                 {
1211                         float sizex = bound(10, scr_zoomwindow_viewsizex.value, 100) / 100.0;
1212                         float sizey = bound(10, scr_zoomwindow_viewsizey.value, 100) / 100.0;
1213                         r_view.width = (int)(vid.width * sizex);
1214                         r_view.height = (int)(vid.height * sizey);
1215                         r_view.depth = 1;
1216                         r_view.x = (int)((vid.width - r_view.width)/2);
1217                         r_view.y = 0;
1218                         r_view.z = 0;
1219
1220                         r_view.frustum_y = tan(scr_zoomwindow_fov.value * cl.viewzoom * M_PI / 360.0) * (3.0/4.0);
1221                         r_view.frustum_x = r_view.frustum_y * vid_pixelheight.value * (float)r_view.width / (float)r_view.height;
1222
1223                         r_view.frustum_x *= r_refdef.frustumscale_x;
1224                         r_view.frustum_y *= r_refdef.frustumscale_y;
1225
1226                         if(!CL_VM_UpdateView())
1227                                 R_RenderView();
1228                 }
1229         }
1230
1231         if (!r_stereo_sidebyside.integer)
1232         {
1233                 r_view.width = vid.width;
1234                 r_view.height = vid.height;
1235                 r_view.depth = 1;
1236                 r_view.x = 0;
1237                 r_view.y = 0;
1238                 r_view.z = 0;
1239         }
1240
1241         // draw 2D stuff
1242         DrawQ_Begin();
1243
1244         //FIXME: force menu if nothing else to look at?
1245         //if (key_dest == key_game && cls.signon != SIGNONS && cls.state == ca_disconnected)
1246
1247         if (cls.signon == SIGNONS)
1248         {
1249                 SCR_DrawNet ();
1250                 SCR_DrawTurtle ();
1251                 SCR_DrawPause ();
1252                 if (!r_letterbox.value)
1253                         Sbar_Draw();
1254                 SHOWLMP_drawall();
1255                 SCR_CheckDrawCenterString();
1256         }
1257         MR_Draw();
1258         CL_DrawVideo();
1259         R_Shadow_EditLights_DrawSelectedLightProperties();
1260
1261         if(!csqc_loaded)
1262                 SCR_DrawConsole();
1263
1264         SCR_DrawBrand();
1265
1266         SCR_DrawDownload();
1267
1268         if (r_timereport_active)
1269                 R_TimeReport("2d");
1270
1271         if (cls.signon == SIGNONS)
1272                 R_TimeReport_Frame();
1273
1274         DrawQ_Finish();
1275
1276         R_DrawGamma();
1277
1278         R_Mesh_Finish();
1279
1280         if (r_timereport_active)
1281                 R_TimeReport("meshfinish");
1282 }
1283
1284 void SCR_UpdateLoadingScreen (void)
1285 {
1286         float x, y;
1287         cachepic_t *pic;
1288         float vertex3f[12];
1289         float texcoord2f[8];
1290         // don't do anything if not initialized yet
1291         if (vid_hidden)
1292                 return;
1293         CHECKGLERROR
1294         qglViewport(0, 0, vid.width, vid.height);CHECKGLERROR
1295         //qglDisable(GL_SCISSOR_TEST);CHECKGLERROR
1296         //qglDepthMask(1);CHECKGLERROR
1297         qglColorMask(1,1,1,1);CHECKGLERROR
1298         //qglClearColor(0,0,0,0);CHECKGLERROR
1299         //qglClear(GL_COLOR_BUFFER_BIT);CHECKGLERROR
1300         //qglCullFace(GL_FRONT);CHECKGLERROR
1301         //qglDisable(GL_CULL_FACE);CHECKGLERROR
1302         //R_ClearScreen();
1303         R_Textures_Frame();
1304         GL_SetupView_Mode_Ortho(0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100);
1305         R_Mesh_Start();
1306         R_Mesh_Matrix(&identitymatrix);
1307         // draw the loading plaque
1308         pic = Draw_CachePic("gfx/loading", true);
1309         x = (vid_conwidth.integer - pic->width)/2;
1310         y = (vid_conheight.integer - pic->height)/2;
1311         GL_Color(1,1,1,1);
1312         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1313         GL_DepthTest(false);
1314         R_Mesh_VertexPointer(vertex3f);
1315         R_Mesh_ColorPointer(NULL);
1316         R_Mesh_ResetTextureState();
1317         R_Mesh_TexBind(0, R_GetTexture(pic->tex));
1318         R_Mesh_TexCoordPointer(0, 2, texcoord2f);
1319         vertex3f[2] = vertex3f[5] = vertex3f[8] = vertex3f[11] = 0;
1320         vertex3f[0] = vertex3f[9] = x;
1321         vertex3f[1] = vertex3f[4] = y;
1322         vertex3f[3] = vertex3f[6] = x + pic->width;
1323         vertex3f[7] = vertex3f[10] = y + pic->height;
1324         texcoord2f[0] = 0;texcoord2f[1] = 0;
1325         texcoord2f[2] = 1;texcoord2f[3] = 0;
1326         texcoord2f[4] = 1;texcoord2f[5] = 1;
1327         texcoord2f[6] = 0;texcoord2f[7] = 1;
1328         R_Mesh_Draw(0, 4, 2, polygonelements);
1329         R_Mesh_Finish();
1330         // refresh
1331         VID_Finish(false);
1332 }
1333
1334 void CL_UpdateScreen(void)
1335 {
1336         float conwidth, conheight;
1337
1338         if (vid_hidden)
1339                 return;
1340
1341         if (!scr_initialized || !con_initialized || vid_hidden)
1342                 return;                         // not initialized yet
1343
1344         // don't allow cheats in multiplayer
1345         if (!cl.islocalgame && cl.worldmodel)
1346         {
1347                 if (r_fullbright.integer != 0)
1348                         Cvar_Set ("r_fullbright", "0");
1349                 if (r_ambient.value != 0)
1350                         Cvar_Set ("r_ambient", "0");
1351         }
1352
1353         conwidth = bound(320, vid_conwidth.value, 2048);
1354         conheight = bound(200, vid_conheight.value, 1536);
1355         if (vid_conwidth.value != conwidth)
1356                 Cvar_SetValue("vid_conwidth", conwidth);
1357         if (vid_conheight.value != conheight)
1358                 Cvar_SetValue("vid_conheight", conheight);
1359
1360         // bound viewsize
1361         if (scr_viewsize.value < 30)
1362                 Cvar_Set ("viewsize","30");
1363         if (scr_viewsize.value > 120)
1364                 Cvar_Set ("viewsize","120");
1365
1366         // bound field of view
1367         if (scr_fov.value < 1)
1368                 Cvar_Set ("fov","1");
1369         if (scr_fov.value > 170)
1370                 Cvar_Set ("fov","170");
1371
1372         // validate r_textureunits cvar
1373         if (r_textureunits.integer > gl_textureunits)
1374                 Cvar_SetValueQuick(&r_textureunits, gl_textureunits);
1375         if (r_textureunits.integer < 1)
1376                 Cvar_SetValueQuick(&r_textureunits, 1);
1377
1378         // validate gl_combine cvar
1379         if (gl_combine.integer && !gl_combine_extension)
1380                 Cvar_SetValueQuick(&gl_combine, 0);
1381
1382         // intermission is always full screen
1383         if (cl.intermission)
1384                 sb_lines = 0;
1385         else
1386         {
1387                 if (scr_viewsize.value >= 120)
1388                         sb_lines = 0;           // no status bar at all
1389                 else if (scr_viewsize.value >= 110)
1390                         sb_lines = 24;          // no inventory
1391                 else
1392                         sb_lines = 24+16+8;
1393         }
1394
1395         r_view.colormask[0] = 1;
1396         r_view.colormask[1] = 1;
1397         r_view.colormask[2] = 1;
1398
1399         if (r_timereport_active)
1400                 R_TimeReport("other");
1401
1402         SCR_SetUpToDrawConsole();
1403
1404         if (r_timereport_active)
1405                 R_TimeReport("start");
1406
1407         CHECKGLERROR
1408         qglViewport(0, 0, vid.width, vid.height);CHECKGLERROR
1409         qglDisable(GL_SCISSOR_TEST);CHECKGLERROR
1410         qglDepthMask(1);CHECKGLERROR
1411         qglColorMask(1,1,1,1);CHECKGLERROR
1412         qglClearColor(0,0,0,0);CHECKGLERROR
1413         qglClear(GL_COLOR_BUFFER_BIT);CHECKGLERROR
1414
1415         if (r_timereport_active)
1416                 R_TimeReport("clear");
1417
1418         if (r_stereo_redblue.integer || r_stereo_redgreen.integer || r_stereo_redcyan.integer || r_stereo_sidebyside.integer)
1419         {
1420                 matrix4x4_t originalmatrix = r_view.matrix;
1421                 r_view.matrix.m[0][3] = originalmatrix.m[0][3] + r_stereo_separation.value * -0.5f * r_view.matrix.m[0][1];
1422                 r_view.matrix.m[1][3] = originalmatrix.m[1][3] + r_stereo_separation.value * -0.5f * r_view.matrix.m[1][1];
1423                 r_view.matrix.m[2][3] = originalmatrix.m[2][3] + r_stereo_separation.value * -0.5f * r_view.matrix.m[2][1];
1424
1425                 if (r_stereo_sidebyside.integer)
1426                         r_stereo_side = 0;
1427
1428                 if (r_stereo_redblue.integer || r_stereo_redgreen.integer || r_stereo_redcyan.integer)
1429                 {
1430                         r_view.colormask[0] = 1;
1431                         r_view.colormask[1] = 0;
1432                         r_view.colormask[2] = 0;
1433                 }
1434
1435                 SCR_DrawScreen();
1436
1437                 r_view.matrix.m[0][3] = originalmatrix.m[0][3] + r_stereo_separation.value * 0.5f * r_view.matrix.m[0][1];
1438                 r_view.matrix.m[1][3] = originalmatrix.m[1][3] + r_stereo_separation.value * 0.5f * r_view.matrix.m[1][1];
1439                 r_view.matrix.m[2][3] = originalmatrix.m[2][3] + r_stereo_separation.value * 0.5f * r_view.matrix.m[2][1];
1440
1441                 if (r_stereo_sidebyside.integer)
1442                         r_stereo_side = 1;
1443
1444                 if (r_stereo_redblue.integer || r_stereo_redgreen.integer || r_stereo_redcyan.integer)
1445                 {
1446                         r_view.colormask[0] = 0;
1447                         r_view.colormask[1] = r_stereo_redcyan.integer || r_stereo_redgreen.integer;
1448                         r_view.colormask[2] = r_stereo_redcyan.integer || r_stereo_redblue.integer;
1449                 }
1450
1451                 SCR_DrawScreen();
1452
1453                 r_view.matrix = originalmatrix;
1454         }
1455         else
1456                 SCR_DrawScreen();
1457
1458         SCR_CaptureVideo();
1459
1460         VID_Finish(true);
1461         if (r_timereport_active)
1462                 R_TimeReport("finish");
1463 }
1464
1465 void CL_Screen_NewMap(void)
1466 {
1467         SHOWLMP_clear();
1468 }