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