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