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