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