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