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