]> icculus.org git repositories - divverent/darkplaces.git/blob - cl_screen.c
do q1bsp lighting checks starting with + 0.125 unit Z offset to improve chances of...
[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_vieworigin[0], r_vieworigin[1], r_vieworigin[2], r_viewforward[0], r_viewforward[1], r_viewforward[2]);
468                 sprintf(r_speeds_string + strlen(r_speeds_string), "%5i entities%6i surfaces%6i triangles%5i leafs%5i portals%6i particles\n", renderstats.entities, renderstats.entities_surfaces, renderstats.entities_triangles, renderstats.world_leafs, renderstats.world_portals, renderstats.particles);
469                 sprintf(r_speeds_string + strlen(r_speeds_string), "%4i lights%4i clears%4i scissored%7i light%7i shadow%7i dynamic\n", renderstats.lights, renderstats.lights_clears, renderstats.lights_scissored, renderstats.lights_lighttriangles, renderstats.lights_shadowtriangles, renderstats.lights_dynamicshadowtriangles);
470                 if (renderstats.bloom)
471                         sprintf(r_speeds_string + strlen(r_speeds_string), "rendered%6i meshes%8i triangles bloompixels%8i copied%8i drawn\n", renderstats.meshes, renderstats.meshes_elements / 3, renderstats.bloom_copypixels, renderstats.bloom_drawpixels);
472                 else
473                         sprintf(r_speeds_string + strlen(r_speeds_string), "rendered%6i meshes%8i triangles\n", renderstats.meshes, renderstats.meshes_elements / 3);
474
475                 memset(&renderstats, 0, sizeof(renderstats));
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         envmap = true;
955
956         r_refdef.x = 0;
957         r_refdef.y = 0;
958         r_refdef.width = size;
959         r_refdef.height = size;
960
961         r_refdef.frustum_x = tan(90 * M_PI / 360.0);
962         r_refdef.frustum_y = tan(90 * M_PI / 360.0);
963
964         buffer1 = (unsigned char *)Mem_Alloc(tempmempool, size * size * 3);
965         buffer2 = (unsigned char *)Mem_Alloc(tempmempool, size * size * 3);
966         buffer3 = (unsigned char *)Mem_Alloc(tempmempool, size * size * 3 + 18);
967
968         for (j = 0;j < 12;j++)
969         {
970                 sprintf(filename, "env/%s%s.tga", basename, envmapinfo[j].name);
971                 Matrix4x4_CreateFromQuakeEntity(&r_refdef.viewentitymatrix, r_vieworigin[0], r_vieworigin[1], r_vieworigin[2], envmapinfo[j].angles[0], envmapinfo[j].angles[1], envmapinfo[j].angles[2], 1);
972                 R_ClearScreen();
973                 R_Mesh_Start();
974                 R_RenderView();
975                 R_Mesh_Finish();
976                 SCR_ScreenShot(filename, buffer1, buffer2, buffer3, 0, vid.height - (r_refdef.y + r_refdef.height), size, size, envmapinfo[j].flipx, envmapinfo[j].flipy, envmapinfo[j].flipdiagonaly, false, false);
977         }
978
979         Mem_Free (buffer1);
980         Mem_Free (buffer2);
981         Mem_Free (buffer3);
982
983         envmap = false;
984 }
985
986 //=============================================================================
987
988 // LordHavoc: SHOWLMP stuff
989 #define SHOWLMP_MAXLABELS 256
990 typedef struct showlmp_s
991 {
992         qboolean        isactive;
993         float           x;
994         float           y;
995         char            label[32];
996         char            pic[128];
997 }
998 showlmp_t;
999
1000 showlmp_t showlmp[SHOWLMP_MAXLABELS];
1001
1002 void SHOWLMP_decodehide(void)
1003 {
1004         int i;
1005         char *lmplabel;
1006         lmplabel = MSG_ReadString();
1007         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1008                 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
1009                 {
1010                         showlmp[i].isactive = false;
1011                         return;
1012                 }
1013 }
1014
1015 void SHOWLMP_decodeshow(void)
1016 {
1017         int i, k;
1018         char lmplabel[256], picname[256];
1019         float x, y;
1020         strlcpy (lmplabel,MSG_ReadString(), sizeof (lmplabel));
1021         strlcpy (picname, MSG_ReadString(), sizeof (picname));
1022         if (gamemode == GAME_NEHAHRA) // LordHavoc: nasty old legacy junk
1023         {
1024                 x = MSG_ReadByte();
1025                 y = MSG_ReadByte();
1026         }
1027         else
1028         {
1029                 x = MSG_ReadShort();
1030                 y = MSG_ReadShort();
1031         }
1032         k = -1;
1033         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1034                 if (showlmp[i].isactive)
1035                 {
1036                         if (strcmp(showlmp[i].label, lmplabel) == 0)
1037                         {
1038                                 k = i;
1039                                 break; // drop out to replace it
1040                         }
1041                 }
1042                 else if (k < 0) // find first empty one to replace
1043                         k = i;
1044         if (k < 0)
1045                 return; // none found to replace
1046         // change existing one
1047         showlmp[k].isactive = true;
1048         strlcpy (showlmp[k].label, lmplabel, sizeof (showlmp[k].label));
1049         strlcpy (showlmp[k].pic, picname, sizeof (showlmp[k].pic));
1050         showlmp[k].x = x;
1051         showlmp[k].y = y;
1052 }
1053
1054 void SHOWLMP_drawall(void)
1055 {
1056         int i;
1057         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1058                 if (showlmp[i].isactive)
1059                         DrawQ_Pic(showlmp[i].x, showlmp[i].y, Draw_CachePic(showlmp[i].pic, true), 0, 0, 1, 1, 1, 1, 0);
1060 }
1061
1062 void SHOWLMP_clear(void)
1063 {
1064         int i;
1065         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1066                 showlmp[i].isactive = false;
1067 }
1068
1069 /*
1070 ==============================================================================
1071
1072                                                 SCREEN SHOTS
1073
1074 ==============================================================================
1075 */
1076
1077 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)
1078 {
1079         int     indices[3] = {0,1,2};
1080         qboolean ret;
1081
1082         if (!r_render.integer)
1083                 return false;
1084
1085         CHECKGLERROR
1086         qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, buffer1);CHECKGLERROR
1087
1088         if (scr_screenshot_gammaboost.value != 1 && gammacorrect)
1089         {
1090                 int i;
1091                 double igamma = 1.0 / scr_screenshot_gammaboost.value;
1092                 unsigned char ramp[256];
1093                 for (i = 0;i < 256;i++)
1094                         ramp[i] = (unsigned char) (pow(i * (1.0 / 255.0), igamma) * 255.0);
1095                 for (i = 0;i < width*height*3;i++)
1096                         buffer1[i] = ramp[buffer1[i]];
1097         }
1098
1099         Image_CopyMux (buffer2, buffer1, width, height, flipx, flipy, flipdiagonal, 3, 3, indices);
1100
1101         if (jpeg)
1102                 ret = JPEG_SaveImage_preflipped (filename, width, height, buffer2);
1103         else
1104                 ret = Image_WriteTGARGB_preflipped (filename, width, height, buffer2, buffer3);
1105
1106         return ret;
1107 }
1108
1109 //=============================================================================
1110
1111 void R_ClearScreen(void)
1112 {
1113         if (r_render.integer)
1114         {
1115                 // clear to black
1116                 CHECKGLERROR
1117                 if (fogenabled)
1118                 {
1119                         qglClearColor(fogcolor[0],fogcolor[1],fogcolor[2],0);CHECKGLERROR
1120                 }
1121                 else
1122                 {
1123                         qglClearColor(0,0,0,0);CHECKGLERROR
1124                 }
1125                 qglClearDepth(1);CHECKGLERROR
1126                 if (gl_stencil)
1127                 {
1128                         // LordHavoc: we use a stencil centered around 128 instead of 0,
1129                         // to avoid clamping interfering with strange shadow volume
1130                         // drawing orders
1131                         qglClearStencil(128);CHECKGLERROR
1132                 }
1133                 // clear the screen
1134                 GL_Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | (gl_stencil ? GL_STENCIL_BUFFER_BIT : 0));
1135                 // set dithering mode
1136                 if (gl_dither.integer)
1137                 {
1138                         qglEnable(GL_DITHER);CHECKGLERROR
1139                 }
1140                 else
1141                 {
1142                         qglDisable(GL_DITHER);CHECKGLERROR
1143                 }
1144         }
1145 }
1146
1147 qboolean CL_VM_UpdateView (void);
1148 void SCR_DrawConsole (void);
1149 void R_Shadow_EditLights_DrawSelectedLightProperties(void);
1150
1151 int r_stereo_side;
1152
1153 void SCR_DrawScreen (void)
1154 {
1155         R_Mesh_Start();
1156
1157         if (r_timereport_active)
1158                 R_TimeReport("setup");
1159
1160         if (cls.signon == SIGNONS)
1161         {
1162                 float size;
1163
1164                 size = scr_viewsize.value * (1.0 / 100.0);
1165                 size = min(size, 1);
1166
1167                 if (r_stereo_sidebyside.integer)
1168                 {
1169                         r_refdef.width = (int)(vid.width * size / 2.5);
1170                         r_refdef.height = (int)(vid.height * size / 2.5 * (1 - bound(0, r_letterbox.value, 100) / 100));
1171                         r_refdef.x = (int)((vid.width - r_refdef.width * 2.5) * 0.5);
1172                         r_refdef.y = (int)((vid.height - r_refdef.height)/2);
1173                         if (r_stereo_side)
1174                                 r_refdef.x += (int)(r_refdef.width * 1.5);
1175                 }
1176                 else
1177                 {
1178                         r_refdef.width = (int)(vid.width * size);
1179                         r_refdef.height = (int)(vid.height * size * (1 - bound(0, r_letterbox.value, 100) / 100));
1180                         r_refdef.x = (int)((vid.width - r_refdef.width)/2);
1181                         r_refdef.y = (int)((vid.height - r_refdef.height)/2);
1182                 }
1183
1184                 // LordHavoc: viewzoom (zoom in for sniper rifles, etc)
1185                 // LordHavoc: this is designed to produce widescreen fov values
1186                 // when the screen is wider than 4/3 width/height aspect, to do
1187                 // this it simply assumes the requested fov is the vertical fov
1188                 // for a 4x3 display, if the ratio is not 4x3 this makes the fov
1189                 // higher/lower according to the ratio
1190                 r_refdef.frustum_y = tan(scr_fov.value * cl.viewzoom * M_PI / 360.0) * (3.0/4.0);
1191                 r_refdef.frustum_x = r_refdef.frustum_y * (float)r_refdef.width / (float)r_refdef.height / vid_pixelheight.value;
1192
1193                 r_refdef.frustum_x *= r_refdef.frustumscale_x;
1194                 r_refdef.frustum_y *= r_refdef.frustumscale_y;
1195
1196                 if(!CL_VM_UpdateView())
1197                         R_RenderView();
1198                 else
1199                         SCR_DrawConsole();
1200
1201                 if (scr_zoomwindow.integer)
1202                 {
1203                         float sizex = bound(10, scr_zoomwindow_viewsizex.value, 100) / 100.0;
1204                         float sizey = bound(10, scr_zoomwindow_viewsizey.value, 100) / 100.0;
1205                         r_refdef.width = (int)(vid.width * sizex);
1206                         r_refdef.height = (int)(vid.height * sizey);
1207                         r_refdef.x = (int)((vid.width - r_refdef.width)/2);
1208                         r_refdef.y = 0;
1209
1210                         r_refdef.frustum_y = tan(scr_zoomwindow_fov.value * cl.viewzoom * M_PI / 360.0) * (3.0/4.0);
1211                         r_refdef.frustum_x = r_refdef.frustum_y * vid_pixelheight.value * (float)r_refdef.width / (float)r_refdef.height;
1212
1213                         r_refdef.frustum_x *= r_refdef.frustumscale_x;
1214                         r_refdef.frustum_y *= r_refdef.frustumscale_y;
1215
1216                         if(!CL_VM_UpdateView())
1217                                 R_RenderView();
1218                 }
1219         }
1220
1221         if (!r_stereo_sidebyside.integer)
1222         {
1223                 r_refdef.width = vid.width;
1224                 r_refdef.height = vid.height;
1225                 r_refdef.x = 0;
1226                 r_refdef.y = 0;
1227         }
1228
1229         // draw 2D stuff
1230         DrawQ_Begin();
1231
1232         //FIXME: force menu if nothing else to look at?
1233         //if (key_dest == key_game && cls.signon != SIGNONS && cls.state == ca_disconnected)
1234
1235         if (cls.signon == SIGNONS)
1236         {
1237                 SCR_DrawNet ();
1238                 SCR_DrawTurtle ();
1239                 SCR_DrawPause ();
1240                 if (!r_letterbox.value)
1241                         Sbar_Draw();
1242                 SHOWLMP_drawall();
1243                 SCR_CheckDrawCenterString();
1244         }
1245         MR_Draw();
1246         CL_DrawVideo();
1247         R_Shadow_EditLights_DrawSelectedLightProperties();
1248
1249         if(!csqc_loaded)
1250                 SCR_DrawConsole();
1251
1252         SCR_DrawBrand();
1253
1254         SCR_DrawDownload();
1255
1256         if (r_timereport_active)
1257                 R_TimeReport("2d");
1258
1259         if (cls.signon == SIGNONS)
1260                 R_TimeReport_Frame();
1261
1262         DrawQ_Finish();
1263
1264         R_DrawGamma();
1265
1266         R_Mesh_Finish();
1267
1268         if (r_timereport_active)
1269                 R_TimeReport("meshfinish");
1270 }
1271
1272 void SCR_UpdateLoadingScreen (void)
1273 {
1274         float x, y;
1275         cachepic_t *pic;
1276         float vertex3f[12];
1277         float texcoord2f[8];
1278         // don't do anything if not initialized yet
1279         if (vid_hidden)
1280                 return;
1281         CHECKGLERROR
1282         qglViewport(0, 0, vid.width, vid.height);CHECKGLERROR
1283         //qglDisable(GL_SCISSOR_TEST);CHECKGLERROR
1284         //qglDepthMask(1);CHECKGLERROR
1285         qglColorMask(1,1,1,1);CHECKGLERROR
1286         //qglClearColor(0,0,0,0);CHECKGLERROR
1287         //qglClear(GL_COLOR_BUFFER_BIT);CHECKGLERROR
1288         //qglCullFace(GL_FRONT);CHECKGLERROR
1289         //qglDisable(GL_CULL_FACE);CHECKGLERROR
1290         //R_ClearScreen();
1291         R_Textures_Frame();
1292         GL_SetupView_Mode_Ortho(0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100);
1293         R_Mesh_Start();
1294         R_Mesh_Matrix(&identitymatrix);
1295         // draw the loading plaque
1296         pic = Draw_CachePic("gfx/loading", true);
1297         x = (vid_conwidth.integer - pic->width)/2;
1298         y = (vid_conheight.integer - pic->height)/2;
1299         GL_Color(1,1,1,1);
1300         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1301         GL_DepthTest(false);
1302         R_Mesh_VertexPointer(vertex3f);
1303         R_Mesh_ColorPointer(NULL);
1304         R_Mesh_ResetTextureState();
1305         R_Mesh_TexBind(0, R_GetTexture(pic->tex));
1306         R_Mesh_TexCoordPointer(0, 2, texcoord2f);
1307         vertex3f[2] = vertex3f[5] = vertex3f[8] = vertex3f[11] = 0;
1308         vertex3f[0] = vertex3f[9] = x;
1309         vertex3f[1] = vertex3f[4] = y;
1310         vertex3f[3] = vertex3f[6] = x + pic->width;
1311         vertex3f[7] = vertex3f[10] = y + pic->height;
1312         texcoord2f[0] = 0;texcoord2f[1] = 0;
1313         texcoord2f[2] = 1;texcoord2f[3] = 0;
1314         texcoord2f[4] = 1;texcoord2f[5] = 1;
1315         texcoord2f[6] = 0;texcoord2f[7] = 1;
1316         R_Mesh_Draw(0, 4, 2, polygonelements);
1317         R_Mesh_Finish();
1318         // refresh
1319         VID_Finish(false);
1320 }
1321
1322 void CL_UpdateScreen(void)
1323 {
1324         float conwidth, conheight;
1325
1326         if (vid_hidden)
1327                 return;
1328
1329         if (!scr_initialized || !con_initialized || vid_hidden)
1330                 return;                         // not initialized yet
1331
1332         // don't allow cheats in multiplayer
1333         if (!cl.islocalgame && cl.worldmodel)
1334         {
1335                 if (r_fullbright.integer != 0)
1336                         Cvar_Set ("r_fullbright", "0");
1337                 if (r_ambient.value != 0)
1338                         Cvar_Set ("r_ambient", "0");
1339         }
1340
1341         conwidth = bound(320, vid_conwidth.value, 2048);
1342         conheight = bound(200, vid_conheight.value, 1536);
1343         if (vid_conwidth.value != conwidth)
1344                 Cvar_SetValue("vid_conwidth", conwidth);
1345         if (vid_conheight.value != conheight)
1346                 Cvar_SetValue("vid_conheight", conheight);
1347
1348         // bound viewsize
1349         if (scr_viewsize.value < 30)
1350                 Cvar_Set ("viewsize","30");
1351         if (scr_viewsize.value > 120)
1352                 Cvar_Set ("viewsize","120");
1353
1354         // bound field of view
1355         if (scr_fov.value < 1)
1356                 Cvar_Set ("fov","1");
1357         if (scr_fov.value > 170)
1358                 Cvar_Set ("fov","170");
1359
1360         // validate r_textureunits cvar
1361         if (r_textureunits.integer > gl_textureunits)
1362                 Cvar_SetValueQuick(&r_textureunits, gl_textureunits);
1363         if (r_textureunits.integer < 1)
1364                 Cvar_SetValueQuick(&r_textureunits, 1);
1365
1366         // validate gl_combine cvar
1367         if (gl_combine.integer && !gl_combine_extension)
1368                 Cvar_SetValueQuick(&gl_combine, 0);
1369
1370         // intermission is always full screen
1371         if (cl.intermission)
1372                 sb_lines = 0;
1373         else
1374         {
1375                 if (scr_viewsize.value >= 120)
1376                         sb_lines = 0;           // no status bar at all
1377                 else if (scr_viewsize.value >= 110)
1378                         sb_lines = 24;          // no inventory
1379                 else
1380                         sb_lines = 24+16+8;
1381         }
1382
1383         r_refdef.colormask[0] = 1;
1384         r_refdef.colormask[1] = 1;
1385         r_refdef.colormask[2] = 1;
1386
1387         if (r_timereport_active)
1388                 R_TimeReport("other");
1389
1390         SCR_SetUpToDrawConsole();
1391
1392         if (r_timereport_active)
1393                 R_TimeReport("start");
1394
1395         CHECKGLERROR
1396         qglViewport(0, 0, vid.width, vid.height);CHECKGLERROR
1397         qglDisable(GL_SCISSOR_TEST);CHECKGLERROR
1398         qglDepthMask(1);CHECKGLERROR
1399         qglColorMask(1,1,1,1);CHECKGLERROR
1400         qglClearColor(0,0,0,0);CHECKGLERROR
1401         qglClear(GL_COLOR_BUFFER_BIT);CHECKGLERROR
1402
1403         if (r_timereport_active)
1404                 R_TimeReport("clear");
1405
1406         if (r_stereo_redblue.integer || r_stereo_redgreen.integer || r_stereo_redcyan.integer || r_stereo_sidebyside.integer)
1407         {
1408                 matrix4x4_t originalmatrix = r_refdef.viewentitymatrix;
1409                 r_refdef.viewentitymatrix.m[0][3] = originalmatrix.m[0][3] + r_stereo_separation.value * -0.5f * r_refdef.viewentitymatrix.m[0][1];
1410                 r_refdef.viewentitymatrix.m[1][3] = originalmatrix.m[1][3] + r_stereo_separation.value * -0.5f * r_refdef.viewentitymatrix.m[1][1];
1411                 r_refdef.viewentitymatrix.m[2][3] = originalmatrix.m[2][3] + r_stereo_separation.value * -0.5f * r_refdef.viewentitymatrix.m[2][1];
1412
1413                 if (r_stereo_sidebyside.integer)
1414                         r_stereo_side = 0;
1415
1416                 if (r_stereo_redblue.integer || r_stereo_redgreen.integer || r_stereo_redcyan.integer)
1417                 {
1418                         r_refdef.colormask[0] = 1;
1419                         r_refdef.colormask[1] = 0;
1420                         r_refdef.colormask[2] = 0;
1421                 }
1422
1423                 SCR_DrawScreen();
1424
1425                 r_refdef.viewentitymatrix.m[0][3] = originalmatrix.m[0][3] + r_stereo_separation.value * 0.5f * r_refdef.viewentitymatrix.m[0][1];
1426                 r_refdef.viewentitymatrix.m[1][3] = originalmatrix.m[1][3] + r_stereo_separation.value * 0.5f * r_refdef.viewentitymatrix.m[1][1];
1427                 r_refdef.viewentitymatrix.m[2][3] = originalmatrix.m[2][3] + r_stereo_separation.value * 0.5f * r_refdef.viewentitymatrix.m[2][1];
1428
1429                 if (r_stereo_sidebyside.integer)
1430                         r_stereo_side = 1;
1431
1432                 if (r_stereo_redblue.integer || r_stereo_redgreen.integer || r_stereo_redcyan.integer)
1433                 {
1434                         r_refdef.colormask[0] = 0;
1435                         r_refdef.colormask[1] = r_stereo_redcyan.integer || r_stereo_redgreen.integer;
1436                         r_refdef.colormask[2] = r_stereo_redcyan.integer || r_stereo_redblue.integer;
1437                 }
1438
1439                 SCR_DrawScreen();
1440
1441                 r_refdef.viewentitymatrix = originalmatrix;
1442         }
1443         else
1444                 SCR_DrawScreen();
1445
1446         SCR_CaptureVideo();
1447
1448         VID_Finish(true);
1449         if (r_timereport_active)
1450                 R_TimeReport("finish");
1451 }
1452
1453 void CL_Screen_NewMap(void)
1454 {
1455         SHOWLMP_clear();
1456 }