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