]> icculus.org git repositories - divverent/darkplaces.git/blob - cl_screen.c
01ac7fa23bc6b57c47331759b877d5594d367981
[divverent/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         // save console log up to this point to log_file if it was set by configs
492         Log_Start();
493
494         Host_StartVideo();
495         S_StopAllSounds();
496         SCR_UpdateLoadingScreen();
497 }
498
499 //=============================================================================
500
501 char r_speeds_string[1024];
502 int speedstringcount, r_timereport_active;
503 double r_timereport_temp = 0, r_timereport_current = 0, r_timereport_start = 0;
504
505 void R_TimeReport(char *desc)
506 {
507         char tempbuf[256];
508         int length;
509         int t;
510
511         if (r_speeds.integer < 2 || !r_timereport_active || r_showtrispass)
512                 return;
513
514         qglFinish();
515         r_timereport_temp = r_timereport_current;
516         r_timereport_current = Sys_DoubleTime();
517         t = (int) ((r_timereport_current - r_timereport_temp) * 1000000.0);
518
519         dpsnprintf(tempbuf, sizeof(tempbuf), "%8i %-11s", t, desc);
520         length = (int)strlen(tempbuf);
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                 if (r_speeds_string[strlen(r_speeds_string)-1] == '\n')
550                         r_speeds_string[strlen(r_speeds_string)-1] = 0;
551                 lines = 1;
552                 for (i = 0;r_speeds_string[i];i++)
553                         if (r_speeds_string[i] == '\n')
554                                 lines++;
555                 y = vid_conheight.integer - sb_lines - lines * 8;
556                 i = j = 0;
557                 DrawQ_Pic(0, y, NULL, vid_conwidth.integer, lines * 8, 0, 0, 0, 0.5, 0);
558                 while (r_speeds_string[i])
559                 {
560                         j = i;
561                         while (r_speeds_string[i] && r_speeds_string[i] != '\n')
562                                 i++;
563                         if (i - j > 0)
564                                 DrawQ_String(0, y, r_speeds_string + j, i - j, 8, 8, 1, 1, 1, 1, 0);
565                         if (r_speeds_string[i] == '\n')
566                                 i++;
567                         y += 8;
568                 }
569                 r_speeds_string[0] = 0;
570                 r_timereport_active = false;
571         }
572         if (r_speeds.integer && cls.signon == SIGNONS && cls.state == ca_connected)
573         {
574                 speedstringcount = 0;
575                 r_speeds_string[0] = 0;
576                 r_timereport_active = false;
577                 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]);
578                 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);
579                 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);
580                 if (renderstats.bloom)
581                         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);
582                 else
583                         sprintf(r_speeds_string + strlen(r_speeds_string), "rendered%6i meshes%8i triangles\n", renderstats.meshes, renderstats.meshes_elements / 3);
584
585                 memset(&renderstats, 0, sizeof(renderstats));
586
587                 if (r_speeds.integer >= 2)
588                 {
589                         r_timereport_active = true;
590                         r_timereport_start = Sys_DoubleTime();
591                 }
592         }
593 }
594
595 /*
596 =================
597 SCR_SizeUp_f
598
599 Keybinding command
600 =================
601 */
602 void SCR_SizeUp_f (void)
603 {
604         Cvar_SetValue ("viewsize",scr_viewsize.value+10);
605 }
606
607
608 /*
609 =================
610 SCR_SizeDown_f
611
612 Keybinding command
613 =================
614 */
615 void SCR_SizeDown_f (void)
616 {
617         Cvar_SetValue ("viewsize",scr_viewsize.value-10);
618 }
619
620 void CL_Screen_Init(void)
621 {
622         Cvar_RegisterVariable (&scr_fov);
623         Cvar_RegisterVariable (&scr_viewsize);
624         Cvar_RegisterVariable (&scr_conspeed);
625         Cvar_RegisterVariable (&scr_conalpha);
626         Cvar_RegisterVariable (&scr_conbrightness);
627         Cvar_RegisterVariable (&scr_conforcewhiledisconnected);
628         Cvar_RegisterVariable (&scr_showram);
629         Cvar_RegisterVariable (&scr_showturtle);
630         Cvar_RegisterVariable (&scr_showpause);
631         Cvar_RegisterVariable (&scr_showbrand);
632         Cvar_RegisterVariable (&scr_centertime);
633         Cvar_RegisterVariable (&scr_printspeed);
634         Cvar_RegisterVariable (&vid_conwidth);
635         Cvar_RegisterVariable (&vid_conheight);
636         Cvar_RegisterVariable (&vid_pixelheight);
637         Cvar_RegisterVariable (&scr_screenshot_jpeg);
638         Cvar_RegisterVariable (&scr_screenshot_jpeg_quality);
639         Cvar_RegisterVariable (&scr_screenshot_gamma);
640         Cvar_RegisterVariable (&cl_capturevideo);
641         Cvar_RegisterVariable (&cl_capturevideo_sound);
642         Cvar_RegisterVariable (&cl_capturevideo_fps);
643         Cvar_RegisterVariable (&cl_capturevideo_rawrgb);
644         Cvar_RegisterVariable (&cl_capturevideo_rawyv12);
645         Cvar_RegisterVariable (&r_textshadow);
646         Cvar_RegisterVariable (&r_letterbox);
647
648         Cmd_AddCommand ("sizeup",SCR_SizeUp_f, "increase view size (increases viewsize cvar)");
649         Cmd_AddCommand ("sizedown",SCR_SizeDown_f, "decrease view size (decreases viewsize cvar)");
650         Cmd_AddCommand ("screenshot",SCR_ScreenShot_f, "takes a screenshot of the next rendered frame");
651         Cmd_AddCommand ("envmap", R_Envmap_f, "render a cubemap (skybox) of the current scene");
652
653         scr_initialized = true;
654 }
655
656 void DrawQ_Clear(void)
657 {
658         r_refdef.drawqueuesize = 0;
659 }
660
661 static int picelements[6] = {0, 1, 2, 0, 2, 3};
662 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
663 {
664         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);
665 }
666
667 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)
668 {
669         int size, len;
670         drawqueue_t *dq;
671         char *out;
672         if (alpha < (1.0f / 255.0f))
673                 return;
674         if (maxlen < 1)
675                 len = (int)strlen(string);
676         else
677                 for (len = 0;len < maxlen && string[len];len++);
678         for (;len > 0 && string[0] == ' ';string++, x += scalex, len--);
679         for (;len > 0 && string[len - 1] == ' ';len--);
680         if (len < 1)
681                 return;
682         if (x >= vid_conwidth.integer || y >= vid_conheight.integer || x < (-scalex * len) || y < (-scaley))
683                 return;
684         size = sizeof(*dq) + ((len + 1 + 3) & ~3);
685         if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
686                 return;
687         red = bound(0, red, 1);
688         green = bound(0, green, 1);
689         blue = bound(0, blue, 1);
690         alpha = bound(0, alpha, 1);
691         dq = (drawqueue_t *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
692         dq->size = size;
693         dq->command = DRAWQUEUE_STRING;
694         dq->flags = flags;
695         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));
696         dq->x = x;
697         dq->y = y;
698         dq->scalex = scalex;
699         dq->scaley = scaley;
700         out = (char *)(dq + 1);
701         memcpy(out, string, len);
702         out[len] = 0;
703         r_refdef.drawqueuesize += dq->size;
704 }
705
706 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)
707 {
708         if (r_textshadow.integer)
709                 DrawQ_String_Real(x+scalex*0.25,y+scaley*0.25,string,maxlen,scalex,scaley,0,0,0,alpha*0.8,flags);
710
711         DrawQ_String_Real(x,y,string,maxlen,scalex,scaley,red,green,blue,alpha,flags);
712 }
713
714 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)
715 {
716         float floats[36];
717         drawqueuemesh_t mesh;
718         memset(&mesh, 0, sizeof(mesh));
719         if (pic)
720         {
721                 if (width == 0)
722                         width = pic->width;
723                 if (height == 0)
724                         height = pic->height;
725                 mesh.texture = pic->tex;
726         }
727         mesh.num_triangles = 2;
728         mesh.num_vertices = 4;
729         mesh.data_element3i = picelements;
730         mesh.data_vertex3f = floats;
731         mesh.data_texcoord2f = floats + 12;
732         mesh.data_color4f = floats + 20;
733         memset(floats, 0, sizeof(floats));
734         mesh.data_vertex3f[0] = mesh.data_vertex3f[9] = x;
735         mesh.data_vertex3f[1] = mesh.data_vertex3f[4] = y;
736         mesh.data_vertex3f[3] = mesh.data_vertex3f[6] = x + width;
737         mesh.data_vertex3f[7] = mesh.data_vertex3f[10] = y + height;
738         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;
739         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;
740         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;
741         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;
742         DrawQ_Mesh (&mesh, flags);
743 }
744
745 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
746 {
747         int size;
748         void *p;
749         drawqueue_t *dq;
750         drawqueuemesh_t *m;
751         size = sizeof(*dq);
752         size += sizeof(drawqueuemesh_t);
753         size += sizeof(int[3]) * mesh->num_triangles;
754         size += sizeof(float[3]) * mesh->num_vertices;
755         size += sizeof(float[2]) * mesh->num_vertices;
756         size += sizeof(float[4]) * mesh->num_vertices;
757         if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
758                 return;
759         dq = (drawqueue_t *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
760         dq->size = size;
761         dq->command = DRAWQUEUE_MESH;
762         dq->flags = flags;
763         dq->color = 0;
764         dq->x = 0;
765         dq->y = 0;
766         dq->scalex = 0;
767         dq->scaley = 0;
768         p = (void *)(dq + 1);
769         m = (drawqueuemesh_t *)p;p = (unsigned char*)p + sizeof(drawqueuemesh_t);
770         m->num_triangles = mesh->num_triangles;
771         m->num_vertices = mesh->num_vertices;
772         m->texture = mesh->texture;
773         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]);
774         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]);
775         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]);
776         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]);
777         r_refdef.drawqueuesize += dq->size;
778 }
779
780 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
781 {
782         int size;
783         void *p;
784         drawqueue_t *dq;
785         drawqueuemesh_t *m;
786         size = sizeof(*dq);
787         size += sizeof(drawqueuemesh_t);
788         size += sizeof(int[3]) * mesh->num_triangles;
789         size += sizeof(float[3]) * mesh->num_vertices;
790         size += sizeof(float[2]) * mesh->num_vertices;
791         size += sizeof(float[4]) * mesh->num_vertices;
792         if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
793                 return;
794         dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
795         dq->size = size;
796         dq->command = DRAWQUEUE_LINES;
797         dq->flags = flags;
798         dq->color = 0;
799         dq->x = 0;
800         dq->y = 0;
801         dq->scalex = 0;
802         dq->scaley = 0;
803         p = (void *)(dq + 1);
804         m = p;p = (unsigned char*)p + sizeof(drawqueuemesh_t);
805         m->num_triangles = mesh->num_triangles;
806         m->num_vertices = mesh->num_vertices;
807         m->texture = mesh->texture;
808         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]);
809         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]);
810         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]);
811         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]);
812         r_refdef.drawqueuesize += dq->size;
813 }
814
815 //LordHavoc: FIXME: this is nasty!
816 void DrawQ_LineWidth (float width)
817 {
818         drawqueue_t *dq;
819         static int linewidth = 1;
820         if(width == linewidth)
821                 return;
822         linewidth = width;
823         if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
824         {
825                 Con_DPrint("DrawQueue full !\n");
826                 return;
827         }
828         dq = (void*) (r_refdef.drawqueue + r_refdef.drawqueuesize);
829         dq->size = sizeof(*dq);
830         dq->command = DRAWQUEUE_LINEWIDTH;
831         dq->x = width;
832
833         r_refdef.drawqueuesize += dq->size;
834 }
835
836 //[515]: this is old, delete
837 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
838 {
839         drawqueue_t *dq;
840         if(width > 0)
841                 DrawQ_LineWidth(width);
842         if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
843         {
844                 Con_DPrint("DrawQueue full !\n");
845                 return;
846         }
847         dq = (void*) (r_refdef.drawqueue + r_refdef.drawqueuesize);
848         dq->size = sizeof(*dq);
849         dq->command = DRAWQUEUE_LINES;
850         dq->x = x1;
851         dq->y = y1;
852         dq->scalex = x2;
853         dq->scaley = y2;
854         dq->flags = flags;
855         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));
856
857         r_refdef.drawqueuesize += dq->size;
858 }
859
860 void DrawQ_SetClipArea(float x, float y, float width, float height)
861 {
862         drawqueue_t * dq;
863         if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
864         {
865                 Con_DPrint("DrawQueue full !\n");
866                 return;
867         }
868         dq = (drawqueue_t *) (r_refdef.drawqueue + r_refdef.drawqueuesize);
869         dq->size = sizeof(*dq);
870         dq->command = DRAWQUEUE_SETCLIP;
871         dq->x = x;
872         dq->y = y;
873         dq->scalex = width;
874         dq->scaley = height;
875         dq->flags = 0;
876         dq->color = 0;
877
878         r_refdef.drawqueuesize += dq->size;
879 }
880
881 void DrawQ_ResetClipArea(void)
882 {
883         drawqueue_t *dq;
884         if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
885         {
886                 Con_DPrint("DrawQueue full !\n");
887                 return;
888         }
889         dq = (drawqueue_t *) (r_refdef.drawqueue + r_refdef.drawqueuesize);
890         dq->size = sizeof(*dq);
891         dq->command = DRAWQUEUE_RESETCLIP;
892         dq->x = 0;
893         dq->y = 0;
894         dq->scalex = 0;
895         dq->scaley = 0;
896         dq->flags = 0;
897         dq->color = 0;
898
899         r_refdef.drawqueuesize += dq->size;
900 }
901
902 /*
903 ==================
904 SCR_ScreenShot_f
905 ==================
906 */
907 void SCR_ScreenShot_f (void)
908 {
909         static int shotnumber;
910         static char oldname[MAX_QPATH];
911         char base[MAX_QPATH];
912         char filename[MAX_QPATH];
913         unsigned char *buffer1;
914         unsigned char *buffer2;
915         unsigned char *buffer3;
916         qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
917
918         sprintf (base, "screenshots/%s", scr_screenshot_name.string);
919
920         if (strcmp (oldname, scr_screenshot_name.string))
921         {
922                 sprintf(oldname, "%s", scr_screenshot_name.string);
923                 shotnumber = 0;
924         }
925
926         // find a file name to save it to
927         for (;shotnumber < 1000000;shotnumber++)
928                 if (!FS_SysFileExists(va("%s/%s%06d.tga", fs_gamedir, base, shotnumber)) && !FS_SysFileExists(va("%s/%s%06d.jpg", fs_gamedir, base, shotnumber)))
929                         break;
930         if (shotnumber >= 1000000)
931         {
932                 Con_Print("SCR_ScreenShot_f: Couldn't create the image file\n");
933                 return;
934         }
935
936         sprintf(filename, "%s%06d.%s", base, shotnumber, jpeg ? "jpg" : "tga");
937
938         buffer1 = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 3);
939         buffer2 = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 3);
940         buffer3 = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 3 + 18);
941
942         if (SCR_ScreenShot (filename, buffer1, buffer2, buffer3, 0, 0, vid.width, vid.height, false, false, false, jpeg, true))
943                 Con_Printf("Wrote %s\n", filename);
944         else
945                 Con_Printf("unable to write %s\n", filename);
946
947         Mem_Free (buffer1);
948         Mem_Free (buffer2);
949         Mem_Free (buffer3);
950
951         shotnumber++;
952 }
953
954 typedef enum capturevideoformat_e
955 {
956         CAPTUREVIDEOFORMAT_TARGA,
957         CAPTUREVIDEOFORMAT_JPEG,
958         CAPTUREVIDEOFORMAT_RAWRGB,
959         CAPTUREVIDEOFORMAT_RAWYV12
960 }
961 capturevideoformat_t;
962
963 qboolean cl_capturevideo_active = false;
964 capturevideoformat_t cl_capturevideo_format;
965 static double cl_capturevideo_starttime = 0;
966 double cl_capturevideo_framerate = 0;
967 static int cl_capturevideo_soundrate = 0;
968 static int cl_capturevideo_frame = 0;
969 static unsigned char *cl_capturevideo_buffer = NULL;
970 static qfile_t *cl_capturevideo_videofile = NULL;
971 qfile_t *cl_capturevideo_soundfile = NULL;
972 static short cl_capturevideo_rgbtoyuvscaletable[3][3][256];
973 static unsigned char cl_capturevideo_yuvnormalizetable[3][256];
974 //static unsigned char cl_capturevideo_rgbgammatable[3][256];
975
976 void SCR_CaptureVideo_BeginVideo(void)
977 {
978         double gamma, g;
979         unsigned int i;
980         unsigned char out[44];
981         if (cl_capturevideo_active)
982                 return;
983         // soundrate is figured out on the first SoundFrame
984         cl_capturevideo_active = true;
985         cl_capturevideo_starttime = Sys_DoubleTime();
986         cl_capturevideo_framerate = bound(1, cl_capturevideo_fps.value, 1000);
987         cl_capturevideo_soundrate = 0;
988         cl_capturevideo_frame = 0;
989         cl_capturevideo_buffer = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * (3+3+3) + 18);
990         gamma = 1.0/scr_screenshot_gamma.value;
991
992         /*
993         for (i = 0;i < 256;i++)
994         {
995                 unsigned char j = (unsigned char)bound(0, 255*pow(i/255.0, gamma), 255);
996                 cl_capturevideo_rgbgammatable[0][i] = j;
997                 cl_capturevideo_rgbgammatable[1][i] = j;
998                 cl_capturevideo_rgbgammatable[2][i] = j;
999         }
1000         */
1001 /*
1002 R = Y + 1.4075 * (Cr - 128);
1003 G = Y + -0.3455 * (Cb - 128) + -0.7169 * (Cr - 128);
1004 B = Y + 1.7790 * (Cb - 128);
1005 Y = R *  .299 + G *  .587 + B *  .114;
1006 Cb = R * -.169 + G * -.332 + B *  .500 + 128.;
1007 Cr = R *  .500 + G * -.419 + B * -.0813 + 128.;
1008 */
1009         for (i = 0;i < 256;i++)
1010         {
1011                 g = 255*pow(i/255.0, gamma);
1012                 // Y weights from RGB
1013                 cl_capturevideo_rgbtoyuvscaletable[0][0][i] = (short)(g *  0.299);
1014                 cl_capturevideo_rgbtoyuvscaletable[0][1][i] = (short)(g *  0.587);
1015                 cl_capturevideo_rgbtoyuvscaletable[0][2][i] = (short)(g *  0.114);
1016                 // Cb weights from RGB
1017                 cl_capturevideo_rgbtoyuvscaletable[1][0][i] = (short)(g * -0.169);
1018                 cl_capturevideo_rgbtoyuvscaletable[1][1][i] = (short)(g * -0.332);
1019                 cl_capturevideo_rgbtoyuvscaletable[1][2][i] = (short)(g *  0.500);
1020                 // Cr weights from RGB
1021                 cl_capturevideo_rgbtoyuvscaletable[2][0][i] = (short)(g *  0.500);
1022                 cl_capturevideo_rgbtoyuvscaletable[2][1][i] = (short)(g * -0.419);
1023                 cl_capturevideo_rgbtoyuvscaletable[2][2][i] = (short)(g * -0.0813);
1024                 // range reduction of YCbCr to valid signal range
1025                 cl_capturevideo_yuvnormalizetable[0][i] = 16 + i * (236-16) / 256;
1026                 cl_capturevideo_yuvnormalizetable[1][i] = 16 + i * (240-16) / 256;
1027                 cl_capturevideo_yuvnormalizetable[2][i] = 16 + i * (240-16) / 256;
1028         }
1029
1030         if (cl_capturevideo_rawrgb.integer)
1031         {
1032                 cl_capturevideo_format = CAPTUREVIDEOFORMAT_RAWRGB;
1033                 cl_capturevideo_videofile = FS_Open ("video/dpvideo.rgb", "wb", false, true);
1034         }
1035         else if (cl_capturevideo_rawyv12.integer)
1036         {
1037                 cl_capturevideo_format = CAPTUREVIDEOFORMAT_RAWYV12;
1038                 cl_capturevideo_videofile = FS_Open ("video/dpvideo.yv12", "wb", false, true);
1039         }
1040         else if (scr_screenshot_jpeg.integer)
1041         {
1042                 cl_capturevideo_format = CAPTUREVIDEOFORMAT_JPEG;
1043                 cl_capturevideo_videofile = NULL;
1044         }
1045         else
1046         {
1047                 cl_capturevideo_format = CAPTUREVIDEOFORMAT_TARGA;
1048                 cl_capturevideo_videofile = NULL;
1049         }
1050
1051         if (cl_capturevideo_sound.integer)
1052         {
1053                 cl_capturevideo_soundfile = FS_Open ("video/dpvideo.wav", "wb", false, true);
1054                 // wave header will be filled out when video ends
1055                 memset(out, 0, 44);
1056                 FS_Write (cl_capturevideo_soundfile, out, 44);
1057         }
1058         else
1059                 cl_capturevideo_soundfile = NULL;
1060 }
1061
1062 void SCR_CaptureVideo_EndVideo(void)
1063 {
1064         int i, n;
1065         unsigned char out[44];
1066         if (!cl_capturevideo_active)
1067                 return;
1068         cl_capturevideo_active = false;
1069
1070         if (cl_capturevideo_videofile)
1071         {
1072                 FS_Close(cl_capturevideo_videofile);
1073                 cl_capturevideo_videofile = NULL;
1074         }
1075
1076         // finish the wave file
1077         if (cl_capturevideo_soundfile)
1078         {
1079                 i = (int)FS_Tell (cl_capturevideo_soundfile);
1080                 //"RIFF", (int) unknown (chunk size), "WAVE",
1081                 //"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
1082                 //"data", (int) unknown (chunk size)
1083                 memcpy (out, "RIFF****WAVEfmt \x10\x00\x00\x00\x01\x00\x02\x00********\x04\x00\x10\0data****", 44);
1084                 // the length of the whole RIFF chunk
1085                 n = i - 8;
1086                 out[4] = (n) & 0xFF;
1087                 out[5] = (n >> 8) & 0xFF;
1088                 out[6] = (n >> 16) & 0xFF;
1089                 out[7] = (n >> 24) & 0xFF;
1090                 // rate
1091                 n = cl_capturevideo_soundrate;
1092                 out[24] = (n) & 0xFF;
1093                 out[25] = (n >> 8) & 0xFF;
1094                 out[26] = (n >> 16) & 0xFF;
1095                 out[27] = (n >> 24) & 0xFF;
1096                 // bytes per second (rate * channels * bytes per channel)
1097                 n = cl_capturevideo_soundrate * 2 * 2;
1098                 out[28] = (n) & 0xFF;
1099                 out[29] = (n >> 8) & 0xFF;
1100                 out[30] = (n >> 16) & 0xFF;
1101                 out[31] = (n >> 24) & 0xFF;
1102                 // the length of the data chunk
1103                 n = i - 44;
1104                 out[40] = (n) & 0xFF;
1105                 out[41] = (n >> 8) & 0xFF;
1106                 out[42] = (n >> 16) & 0xFF;
1107                 out[43] = (n >> 24) & 0xFF;
1108                 FS_Seek (cl_capturevideo_soundfile, 0, SEEK_SET);
1109                 FS_Write (cl_capturevideo_soundfile, out, 44);
1110                 FS_Close (cl_capturevideo_soundfile);
1111                 cl_capturevideo_soundfile = NULL;
1112         }
1113
1114         if (cl_capturevideo_buffer)
1115         {
1116                 Mem_Free (cl_capturevideo_buffer);
1117                 cl_capturevideo_buffer = NULL;
1118         }
1119
1120         cl_capturevideo_starttime = 0;
1121         cl_capturevideo_framerate = 0;
1122         cl_capturevideo_frame = 0;
1123 }
1124
1125 qboolean SCR_CaptureVideo_VideoFrame(int newframenum)
1126 {
1127         int x = 0, y = 0, width = vid.width, height = vid.height;
1128         unsigned char *b, *out;
1129         char filename[32];
1130         int outoffset = (width/2)*(height/2);
1131         //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);
1132         // speed is critical here, so do saving as directly as possible
1133         switch (cl_capturevideo_format)
1134         {
1135         case CAPTUREVIDEOFORMAT_RAWYV12:
1136                 // FIXME: width/height must be multiple of 2, enforce this?
1137                 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
1138                 CHECKGLERROR
1139                 // process one line at a time, and CbCr every other line at 2 pixel intervals
1140                 for (y = 0;y < height;y++)
1141                 {
1142                         // 1x1 Y
1143                         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++)
1144                                 *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]]];
1145                         if ((y & 1) == 0)
1146                         {
1147                                 // 2x2 Cb and Cr planes
1148 #if 1
1149                                 // low quality, no averaging
1150                                 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++)
1151                                 {
1152                                         // Cr
1153                                         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];
1154                                         // Cb
1155                                         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];
1156                                 }
1157 #else
1158                                 // high quality, averaging
1159                                 int inpitch = width*3;
1160                                 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++)
1161                                 {
1162                                         int blockr, blockg, blockb;
1163                                         blockr = (b[0] + b[3] + b[inpitch+0] + b[inpitch+3]) >> 2;
1164                                         blockg = (b[1] + b[4] + b[inpitch+1] + b[inpitch+4]) >> 2;
1165                                         blockb = (b[2] + b[5] + b[inpitch+2] + b[inpitch+5]) >> 2;
1166                                         // Cr
1167                                         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];
1168                                         // Cb
1169                                         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];
1170                                 }
1171 #endif
1172                         }
1173                 }
1174                 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
1175                         if (!FS_Write (cl_capturevideo_videofile, cl_capturevideo_buffer + width*height*3, width*height+(width/2)*(height/2)*2))
1176                                 return false;
1177                 return true;
1178         case CAPTUREVIDEOFORMAT_RAWRGB:
1179                 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
1180                 CHECKGLERROR
1181                 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
1182                         if (!FS_Write (cl_capturevideo_videofile, cl_capturevideo_buffer, width*height*3))
1183                                 return false;
1184                 return true;
1185         case CAPTUREVIDEOFORMAT_JPEG:
1186                 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
1187                 CHECKGLERROR
1188                 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
1189                 {
1190                         sprintf(filename, "video/dp%06d.jpg", cl_capturevideo_frame);
1191                         if (!JPEG_SaveImage_preflipped (filename, width, height, cl_capturevideo_buffer))
1192                                 return false;
1193                 }
1194                 return true;
1195         case CAPTUREVIDEOFORMAT_TARGA:
1196                 //return Image_WriteTGARGB_preflipped (filename, width, height, cl_capturevideo_buffer, cl_capturevideo_buffer + vid.width * vid.height * 3, );
1197                 memset (cl_capturevideo_buffer, 0, 18);
1198                 cl_capturevideo_buffer[2] = 2;          // uncompressed type
1199                 cl_capturevideo_buffer[12] = (width >> 0) & 0xFF;
1200                 cl_capturevideo_buffer[13] = (width >> 8) & 0xFF;
1201                 cl_capturevideo_buffer[14] = (height >> 0) & 0xFF;
1202                 cl_capturevideo_buffer[15] = (height >> 8) & 0xFF;
1203                 cl_capturevideo_buffer[16] = 24;        // pixel size
1204                 qglReadPixels (x, y, width, height, GL_BGR, GL_UNSIGNED_BYTE, cl_capturevideo_buffer + 18);
1205                 CHECKGLERROR
1206                 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
1207                 {
1208                         sprintf(filename, "video/dp%06d.tga", cl_capturevideo_frame);
1209                         if (!FS_WriteFile (filename, cl_capturevideo_buffer, width*height*3 + 18))
1210                                 return false;
1211                 }
1212                 return true;
1213         default:
1214                 return false;
1215         }
1216 }
1217
1218 void SCR_CaptureVideo_SoundFrame(unsigned char *bufstereo16le, size_t length, int rate)
1219 {
1220         if (!cl_capturevideo_soundfile)
1221                 return;
1222         cl_capturevideo_soundrate = rate;
1223         if (FS_Write (cl_capturevideo_soundfile, bufstereo16le, 4 * length) < (fs_offset_t)(4 * length))
1224         {
1225                 Cvar_SetValueQuick(&cl_capturevideo, 0);
1226                 Con_Printf("video sound saving failed on frame %i, out of disk space? stopping video capture.\n", cl_capturevideo_frame);
1227                 SCR_CaptureVideo_EndVideo();
1228         }
1229 }
1230
1231 void SCR_CaptureVideo(void)
1232 {
1233         int newframenum;
1234         if (cl_capturevideo.integer && r_render.integer)
1235         {
1236                 if (!cl_capturevideo_active)
1237                         SCR_CaptureVideo_BeginVideo();
1238                 if (cl_capturevideo_framerate != cl_capturevideo_fps.value)
1239                 {
1240                         Con_Printf("You can not change the video framerate while recording a video.\n");
1241                         Cvar_SetValueQuick(&cl_capturevideo_fps, cl_capturevideo_framerate);
1242                 }
1243                 if (cl_capturevideo_soundfile)
1244                 {
1245                         // preserve sound sync by duplicating frames when running slow
1246                         newframenum = (Sys_DoubleTime() - cl_capturevideo_starttime) * cl_capturevideo_framerate;
1247                 }
1248                 else
1249                         newframenum = cl_capturevideo_frame + 1;
1250                 // if falling behind more than one second, stop
1251                 if (newframenum - cl_capturevideo_frame > (int)ceil(cl_capturevideo_framerate))
1252                 {
1253                         Cvar_SetValueQuick(&cl_capturevideo, 0);
1254                         Con_Printf("video saving failed on frame %i, your machine is too slow for this capture speed.\n", cl_capturevideo_frame);
1255                         SCR_CaptureVideo_EndVideo();
1256                         return;
1257                 }
1258                 // write frames
1259                 if (!SCR_CaptureVideo_VideoFrame(newframenum))
1260                 {
1261                         Cvar_SetValueQuick(&cl_capturevideo, 0);
1262                         Con_Printf("video saving failed on frame %i, out of disk space? stopping video capture.\n", cl_capturevideo_frame);
1263                         SCR_CaptureVideo_EndVideo();
1264                 }
1265         }
1266         else if (cl_capturevideo_active)
1267                 SCR_CaptureVideo_EndVideo();
1268 }
1269
1270 /*
1271 ===============
1272 R_Envmap_f
1273
1274 Grab six views for environment mapping tests
1275 ===============
1276 */
1277 struct envmapinfo_s
1278 {
1279         float angles[3];
1280         char *name;
1281         qboolean flipx, flipy, flipdiagonaly;
1282 }
1283 envmapinfo[12] =
1284 {
1285         {{  0,   0, 0}, "rt", false, false, false},
1286         {{  0, 270, 0}, "ft", false, false, false},
1287         {{  0, 180, 0}, "lf", false, false, false},
1288         {{  0,  90, 0}, "bk", false, false, false},
1289         {{-90, 180, 0}, "up",  true,  true, false},
1290         {{ 90, 180, 0}, "dn",  true,  true, false},
1291
1292         {{  0,   0, 0}, "px",  true,  true,  true},
1293         {{  0,  90, 0}, "py", false,  true, false},
1294         {{  0, 180, 0}, "nx", false, false,  true},
1295         {{  0, 270, 0}, "ny",  true, false, false},
1296         {{-90, 180, 0}, "pz", false, false,  true},
1297         {{ 90, 180, 0}, "nz", false, false,  true}
1298 };
1299
1300 static void R_Envmap_f (void)
1301 {
1302         int j, size;
1303         char filename[MAX_QPATH], basename[MAX_QPATH];
1304         unsigned char *buffer1;
1305         unsigned char *buffer2;
1306         unsigned char *buffer3;
1307
1308         if (Cmd_Argc() != 3)
1309         {
1310                 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");
1311                 return;
1312         }
1313
1314         strlcpy (basename, Cmd_Argv(1), sizeof (basename));
1315         size = atoi(Cmd_Argv(2));
1316         if (size != 128 && size != 256 && size != 512 && size != 1024)
1317         {
1318                 Con_Print("envmap: size must be one of 128, 256, 512, or 1024\n");
1319                 return;
1320         }
1321         if (size > vid.width || size > vid.height)
1322         {
1323                 Con_Print("envmap: your resolution is not big enough to render that size\n");
1324                 return;
1325         }
1326
1327         envmap = true;
1328
1329         r_refdef.x = 0;
1330         r_refdef.y = 0;
1331         r_refdef.width = size;
1332         r_refdef.height = size;
1333
1334         r_refdef.frustum_x = tan(90 * M_PI / 360.0);
1335         r_refdef.frustum_y = tan(90 * M_PI / 360.0);
1336
1337         buffer1 = (unsigned char *)Mem_Alloc(tempmempool, size * size * 3);
1338         buffer2 = (unsigned char *)Mem_Alloc(tempmempool, size * size * 3);
1339         buffer3 = (unsigned char *)Mem_Alloc(tempmempool, size * size * 3 + 18);
1340
1341         for (j = 0;j < 12;j++)
1342         {
1343                 sprintf(filename, "env/%s%s.tga", basename, envmapinfo[j].name);
1344                 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);
1345                 R_ClearScreen();
1346                 R_Mesh_Start();
1347                 R_RenderView();
1348                 R_Mesh_Finish();
1349                 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);
1350         }
1351
1352         Mem_Free (buffer1);
1353         Mem_Free (buffer2);
1354         Mem_Free (buffer3);
1355
1356         envmap = false;
1357 }
1358
1359 //=============================================================================
1360
1361 // LordHavoc: SHOWLMP stuff
1362 #define SHOWLMP_MAXLABELS 256
1363 typedef struct showlmp_s
1364 {
1365         qboolean        isactive;
1366         float           x;
1367         float           y;
1368         char            label[32];
1369         char            pic[128];
1370 }
1371 showlmp_t;
1372
1373 showlmp_t showlmp[SHOWLMP_MAXLABELS];
1374
1375 void SHOWLMP_decodehide(void)
1376 {
1377         int i;
1378         char *lmplabel;
1379         lmplabel = MSG_ReadString();
1380         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1381                 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
1382                 {
1383                         showlmp[i].isactive = false;
1384                         return;
1385                 }
1386 }
1387
1388 void SHOWLMP_decodeshow(void)
1389 {
1390         int i, k;
1391         char lmplabel[256], picname[256];
1392         float x, y;
1393         strlcpy (lmplabel,MSG_ReadString(), sizeof (lmplabel));
1394         strlcpy (picname, MSG_ReadString(), sizeof (picname));
1395         if (gamemode == GAME_NEHAHRA) // LordHavoc: nasty old legacy junk
1396         {
1397                 x = MSG_ReadByte();
1398                 y = MSG_ReadByte();
1399         }
1400         else
1401         {
1402                 x = MSG_ReadShort();
1403                 y = MSG_ReadShort();
1404         }
1405         k = -1;
1406         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1407                 if (showlmp[i].isactive)
1408                 {
1409                         if (strcmp(showlmp[i].label, lmplabel) == 0)
1410                         {
1411                                 k = i;
1412                                 break; // drop out to replace it
1413                         }
1414                 }
1415                 else if (k < 0) // find first empty one to replace
1416                         k = i;
1417         if (k < 0)
1418                 return; // none found to replace
1419         // change existing one
1420         showlmp[k].isactive = true;
1421         strlcpy (showlmp[k].label, lmplabel, sizeof (showlmp[k].label));
1422         strlcpy (showlmp[k].pic, picname, sizeof (showlmp[k].pic));
1423         showlmp[k].x = x;
1424         showlmp[k].y = y;
1425 }
1426
1427 void SHOWLMP_drawall(void)
1428 {
1429         int i;
1430         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1431                 if (showlmp[i].isactive)
1432                         DrawQ_Pic(showlmp[i].x, showlmp[i].y, Draw_CachePic(showlmp[i].pic, false), 0, 0, 1, 1, 1, 1, 0);
1433 }
1434
1435 void SHOWLMP_clear(void)
1436 {
1437         int i;
1438         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1439                 showlmp[i].isactive = false;
1440 }
1441
1442 void CL_SetupScreenSize(void)
1443 {
1444         float conwidth, conheight;
1445
1446         VID_UpdateGamma(false);
1447
1448         conwidth = bound(320, vid_conwidth.value, 2048);
1449         conheight = bound(200, vid_conheight.value, 1536);
1450         if (vid_conwidth.value != conwidth)
1451                 Cvar_SetValue("vid_conwidth", conwidth);
1452         if (vid_conheight.value != conheight)
1453                 Cvar_SetValue("vid_conheight", conheight);
1454
1455         vid_conwidth.integer = vid_conwidth.integer;
1456         vid_conheight.integer = vid_conheight.integer;
1457
1458         SCR_SetUpToDrawConsole();
1459 }
1460
1461 extern void R_Shadow_EditLights_DrawSelectedLightProperties(void);
1462 void CL_UpdateScreen(void)
1463 {
1464         if (!scr_initialized || !con_initialized || vid_hidden)
1465                 return;                         // not initialized yet
1466
1467         // don't allow cheats in multiplayer
1468         if (!cl.islocalgame && cl.worldmodel)
1469         {
1470                 if (r_fullbright.integer != 0)
1471                         Cvar_Set ("r_fullbright", "0");
1472                 if (r_ambient.value != 0)
1473                         Cvar_Set ("r_ambient", "0");
1474         }
1475
1476         // bound viewsize
1477         if (scr_viewsize.value < 30)
1478                 Cvar_Set ("viewsize","30");
1479         if (scr_viewsize.value > 120)
1480                 Cvar_Set ("viewsize","120");
1481
1482         // bound field of view
1483         if (scr_fov.value < 1)
1484                 Cvar_Set ("fov","1");
1485         if (scr_fov.value > 170)
1486                 Cvar_Set ("fov","170");
1487
1488         // intermission is always full screen
1489         if (cl.intermission)
1490                 sb_lines = 0;
1491         else
1492         {
1493                 if (scr_viewsize.value >= 120)
1494                         sb_lines = 0;           // no status bar at all
1495                 else if (scr_viewsize.value >= 110)
1496                         sb_lines = 24;          // no inventory
1497                 else
1498                         sb_lines = 24+16+8;
1499         }
1500
1501         r_refdef.colormask[0] = 1;
1502         r_refdef.colormask[1] = 1;
1503         r_refdef.colormask[2] = 1;
1504
1505         SCR_CaptureVideo();
1506
1507         if (r_timereport_active)
1508                 R_TimeReport("other");
1509
1510         CL_SetupScreenSize();
1511
1512         DrawQ_Clear();
1513
1514         if (r_timereport_active)
1515                 R_TimeReport("setup");
1516
1517         //FIXME: force menu if nothing else to look at?
1518         //if (key_dest == key_game && cls.signon != SIGNONS && cls.state == ca_disconnected)
1519
1520         if (cls.signon == SIGNONS)
1521         {
1522                 SCR_DrawNet ();
1523                 SCR_DrawTurtle ();
1524                 SCR_DrawPause ();
1525                 if (!r_letterbox.value)
1526                         Sbar_Draw();
1527                 SHOWLMP_drawall();
1528                 SCR_CheckDrawCenterString();
1529         }
1530         MR_Draw();
1531         CL_DrawVideo();
1532         if (cls.signon == SIGNONS)
1533         {
1534                 if (r_timereport_active)
1535                         R_TimeReport("2d");
1536                 R_TimeReport_Frame();
1537         }
1538         R_Shadow_EditLights_DrawSelectedLightProperties();
1539
1540         if(!csqc_loaded)
1541                 SCR_DrawConsole();
1542
1543         SCR_DrawBrand();
1544
1545         SCR_DrawDownload();
1546
1547         SCR_UpdateScreen();
1548 }
1549
1550 void CL_Screen_NewMap(void)
1551 {
1552         SHOWLMP_clear();
1553 }