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