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