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