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