]> icculus.org git repositories - divverent/darkplaces.git/blob - cl_screen.c
added DrawQ_Line/DrawQ_Lines/DrawQ_LineWidth patch from [515], note: buggy, needs...
[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_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_Lines (drawqueuemesh_t *mesh, int flags)
762 {
763         int size;
764         void *p;
765         drawqueue_t *dq;
766         drawqueuemesh_t *m;
767         size = sizeof(*dq);
768         size += sizeof(drawqueuemesh_t);
769         size += sizeof(int[3]) * mesh->num_triangles;
770         size += sizeof(float[3]) * mesh->num_vertices;
771         size += sizeof(float[2]) * mesh->num_vertices;
772         size += sizeof(float[4]) * mesh->num_vertices;
773         if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
774                 return;
775         dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
776         dq->size = size;
777         dq->command = DRAWQUEUE_LINES;
778         dq->flags = flags;
779         dq->color = 0;
780         dq->x = 0;
781         dq->y = 0;
782         dq->scalex = 0;
783         dq->scaley = 0;
784         p = (void *)(dq + 1);
785         m = p;p = (unsigned char*)p + sizeof(drawqueuemesh_t);
786         m->num_triangles = mesh->num_triangles;
787         m->num_vertices = mesh->num_vertices;
788         m->texture = mesh->texture;
789         m->data_element3i  = p;memcpy(m->data_element3i , mesh->data_element3i , m->num_triangles * sizeof(int[3]));p = (unsigned char*)p + m->num_triangles * sizeof(int[3]);
790         m->data_vertex3f   = p;memcpy(m->data_vertex3f  , mesh->data_vertex3f  , m->num_vertices * sizeof(float[3]));p = (unsigned char*)p + m->num_vertices * sizeof(float[3]);
791         m->data_texcoord2f = p;memcpy(m->data_texcoord2f, mesh->data_texcoord2f, m->num_vertices * sizeof(float[2]));p = (unsigned char*)p + m->num_vertices * sizeof(float[2]);
792         m->data_color4f    = p;memcpy(m->data_color4f   , mesh->data_color4f   , m->num_vertices * sizeof(float[4]));p = (unsigned char*)p + m->num_vertices * sizeof(float[4]);
793         r_refdef.drawqueuesize += dq->size;
794 }
795
796 //LordHavoc: FIXME: this is nasty!
797 void DrawQ_LineWidth (float width)
798 {
799         drawqueue_t *dq;
800         static int linewidth = 1;
801         if(width == linewidth)
802                 return;
803         linewidth = width;
804         if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
805         {
806                 Con_DPrint("DrawQueue full !\n");
807                 return;
808         }
809         dq = (void*) (r_refdef.drawqueue + r_refdef.drawqueuesize);
810         dq->size = sizeof(*dq);
811         dq->command = DRAWQUEUE_LINEWIDTH;
812         dq->x = width;
813
814         r_refdef.drawqueuesize += dq->size;
815 }
816
817 //[515]: this is old, delete
818 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
819 {
820         drawqueue_t *dq;
821         if(width > 0)
822                 DrawQ_LineWidth(width);
823         if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
824         {
825                 Con_DPrint("DrawQueue full !\n");
826                 return;
827         }
828         dq = (void*) (r_refdef.drawqueue + r_refdef.drawqueuesize);
829         dq->size = sizeof(*dq);
830         dq->command = DRAWQUEUE_LINES;
831         dq->x = x1;
832         dq->y = y1;
833         dq->scalex = x2;
834         dq->scaley = y2;
835         dq->flags = flags;
836         dq->color =  ((unsigned int) (r * 255.0f) << 24) | ((unsigned int) (g * 255.0f) << 16) | ((unsigned int) (b * 255.0f) << 8) | ((unsigned int) (alpha * 255.0f));
837
838         r_refdef.drawqueuesize += dq->size;
839 }
840
841 void DrawQ_SetClipArea(float x, float y, float width, float height)
842 {
843         drawqueue_t * dq;
844         if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
845         {
846                 Con_DPrint("DrawQueue full !\n");
847                 return;
848         }
849         dq = (drawqueue_t *) (r_refdef.drawqueue + r_refdef.drawqueuesize);
850         dq->size = sizeof(*dq);
851         dq->command = DRAWQUEUE_SETCLIP;
852         dq->x = x;
853         dq->y = y;
854         dq->scalex = width;
855         dq->scaley = height;
856         dq->flags = 0;
857         dq->color = 0;
858
859         r_refdef.drawqueuesize += dq->size;
860 }
861
862 void DrawQ_ResetClipArea(void)
863 {
864         drawqueue_t *dq;
865         if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
866         {
867                 Con_DPrint("DrawQueue full !\n");
868                 return;
869         }
870         dq = (drawqueue_t *) (r_refdef.drawqueue + r_refdef.drawqueuesize);
871         dq->size = sizeof(*dq);
872         dq->command = DRAWQUEUE_RESETCLIP;
873         dq->x = 0;
874         dq->y = 0;
875         dq->scalex = 0;
876         dq->scaley = 0;
877         dq->flags = 0;
878         dq->color = 0;
879
880         r_refdef.drawqueuesize += dq->size;
881 }
882
883 /*
884 ==================
885 SCR_ScreenShot_f
886 ==================
887 */
888 void SCR_ScreenShot_f (void)
889 {
890         static int shotnumber;
891         static char oldname[MAX_QPATH];
892         char base[MAX_QPATH];
893         char filename[MAX_QPATH];
894         unsigned char *buffer1;
895         unsigned char *buffer2;
896         unsigned char *buffer3;
897         qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
898
899         sprintf (base, "screenshots/%s", scr_screenshot_name.string);
900
901         if (strcmp (oldname, scr_screenshot_name.string))
902         {
903                 sprintf(oldname, "%s", scr_screenshot_name.string);
904                 shotnumber = 0;
905         }
906
907         // find a file name to save it to
908         for (;shotnumber < 1000000;shotnumber++)
909                 if (!FS_SysFileExists(va("%s/%s%06d.tga", fs_gamedir, base, shotnumber)) && !FS_SysFileExists(va("%s/%s%06d.jpg", fs_gamedir, base, shotnumber)))
910                         break;
911         if (shotnumber >= 1000000)
912         {
913                 Con_Print("SCR_ScreenShot_f: Couldn't create the image file\n");
914                 return;
915         }
916
917         sprintf(filename, "%s%06d.%s", base, shotnumber, jpeg ? "jpg" : "tga");
918
919         buffer1 = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 3);
920         buffer2 = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 3);
921         buffer3 = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 3 + 18);
922
923         if (SCR_ScreenShot (filename, buffer1, buffer2, buffer3, 0, 0, vid.width, vid.height, false, false, false, jpeg, true))
924                 Con_Printf("Wrote %s\n", filename);
925         else
926                 Con_Printf("unable to write %s\n", filename);
927
928         Mem_Free (buffer1);
929         Mem_Free (buffer2);
930         Mem_Free (buffer3);
931
932         shotnumber++;
933 }
934
935 typedef enum capturevideoformat_e
936 {
937         CAPTUREVIDEOFORMAT_TARGA,
938         CAPTUREVIDEOFORMAT_JPEG,
939         CAPTUREVIDEOFORMAT_RAWRGB,
940         CAPTUREVIDEOFORMAT_RAWYV12
941 }
942 capturevideoformat_t;
943
944 qboolean cl_capturevideo_active = false;
945 capturevideoformat_t cl_capturevideo_format;
946 static double cl_capturevideo_starttime = 0;
947 double cl_capturevideo_framerate = 0;
948 static int cl_capturevideo_soundrate = 0;
949 static int cl_capturevideo_frame = 0;
950 static unsigned char *cl_capturevideo_buffer = NULL;
951 static qfile_t *cl_capturevideo_videofile = NULL;
952 qfile_t *cl_capturevideo_soundfile = NULL;
953 static short cl_capturevideo_rgbtoyuvscaletable[3][3][256];
954 static unsigned char cl_capturevideo_yuvnormalizetable[3][256];
955 //static unsigned char cl_capturevideo_rgbgammatable[3][256];
956
957 void SCR_CaptureVideo_BeginVideo(void)
958 {
959         double gamma, g;
960         unsigned int i;
961         unsigned char out[44];
962         if (cl_capturevideo_active)
963                 return;
964         // soundrate is figured out on the first SoundFrame
965         cl_capturevideo_active = true;
966         cl_capturevideo_starttime = Sys_DoubleTime();
967         cl_capturevideo_framerate = bound(1, cl_capturevideo_fps.value, 1000);
968         cl_capturevideo_soundrate = 0;
969         cl_capturevideo_frame = 0;
970         cl_capturevideo_buffer = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * (3+3+3) + 18);
971         gamma = 1.0/scr_screenshot_gamma.value;
972
973         /*
974         for (i = 0;i < 256;i++)
975         {
976                 unsigned char j = (unsigned char)bound(0, 255*pow(i/255.0, gamma), 255);
977                 cl_capturevideo_rgbgammatable[0][i] = j;
978                 cl_capturevideo_rgbgammatable[1][i] = j;
979                 cl_capturevideo_rgbgammatable[2][i] = j;
980         }
981         */
982 /*
983 R = Y + 1.4075 * (Cr - 128);
984 G = Y + -0.3455 * (Cb - 128) + -0.7169 * (Cr - 128);
985 B = Y + 1.7790 * (Cb - 128);
986 Y = R *  .299 + G *  .587 + B *  .114;
987 Cb = R * -.169 + G * -.332 + B *  .500 + 128.;
988 Cr = R *  .500 + G * -.419 + B * -.0813 + 128.;
989 */
990         for (i = 0;i < 256;i++)
991         {
992                 g = 255*pow(i/255.0, gamma);
993                 // Y weights from RGB
994                 cl_capturevideo_rgbtoyuvscaletable[0][0][i] = (short)(g *  0.299);
995                 cl_capturevideo_rgbtoyuvscaletable[0][1][i] = (short)(g *  0.587);
996                 cl_capturevideo_rgbtoyuvscaletable[0][2][i] = (short)(g *  0.114);
997                 // Cb weights from RGB
998                 cl_capturevideo_rgbtoyuvscaletable[1][0][i] = (short)(g * -0.169);
999                 cl_capturevideo_rgbtoyuvscaletable[1][1][i] = (short)(g * -0.332);
1000                 cl_capturevideo_rgbtoyuvscaletable[1][2][i] = (short)(g *  0.500);
1001                 // Cr weights from RGB
1002                 cl_capturevideo_rgbtoyuvscaletable[2][0][i] = (short)(g *  0.500);
1003                 cl_capturevideo_rgbtoyuvscaletable[2][1][i] = (short)(g * -0.419);
1004                 cl_capturevideo_rgbtoyuvscaletable[2][2][i] = (short)(g * -0.0813);
1005                 // range reduction of YCbCr to valid signal range
1006                 cl_capturevideo_yuvnormalizetable[0][i] = 16 + i * (236-16) / 256;
1007                 cl_capturevideo_yuvnormalizetable[1][i] = 16 + i * (240-16) / 256;
1008                 cl_capturevideo_yuvnormalizetable[2][i] = 16 + i * (240-16) / 256;
1009         }
1010
1011         if (cl_capturevideo_rawrgb.integer)
1012         {
1013                 cl_capturevideo_format = CAPTUREVIDEOFORMAT_RAWRGB;
1014                 cl_capturevideo_videofile = FS_Open ("video/dpvideo.rgb", "wb", false, true);
1015         }
1016         else if (cl_capturevideo_rawyv12.integer)
1017         {
1018                 cl_capturevideo_format = CAPTUREVIDEOFORMAT_RAWYV12;
1019                 cl_capturevideo_videofile = FS_Open ("video/dpvideo.yv12", "wb", false, true);
1020         }
1021         else if (scr_screenshot_jpeg.integer)
1022         {
1023                 cl_capturevideo_format = CAPTUREVIDEOFORMAT_JPEG;
1024                 cl_capturevideo_videofile = NULL;
1025         }
1026         else
1027         {
1028                 cl_capturevideo_format = CAPTUREVIDEOFORMAT_TARGA;
1029                 cl_capturevideo_videofile = NULL;
1030         }
1031
1032         if (cl_capturevideo_sound.integer)
1033         {
1034                 cl_capturevideo_soundfile = FS_Open ("video/dpvideo.wav", "wb", false, true);
1035                 // wave header will be filled out when video ends
1036                 memset(out, 0, 44);
1037                 FS_Write (cl_capturevideo_soundfile, out, 44);
1038         }
1039         else
1040                 cl_capturevideo_soundfile = NULL;
1041 }
1042
1043 void SCR_CaptureVideo_EndVideo(void)
1044 {
1045         int i, n;
1046         unsigned char out[44];
1047         if (!cl_capturevideo_active)
1048                 return;
1049         cl_capturevideo_active = false;
1050
1051         if (cl_capturevideo_videofile)
1052         {
1053                 FS_Close(cl_capturevideo_videofile);
1054                 cl_capturevideo_videofile = NULL;
1055         }
1056
1057         // finish the wave file
1058         if (cl_capturevideo_soundfile)
1059         {
1060                 i = (int)FS_Tell (cl_capturevideo_soundfile);
1061                 //"RIFF", (int) unknown (chunk size), "WAVE",
1062                 //"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
1063                 //"data", (int) unknown (chunk size)
1064                 memcpy (out, "RIFF****WAVEfmt \x10\x00\x00\x00\x01\x00\x02\x00********\x04\x00\x10\0data****", 44);
1065                 // the length of the whole RIFF chunk
1066                 n = i - 8;
1067                 out[4] = (n) & 0xFF;
1068                 out[5] = (n >> 8) & 0xFF;
1069                 out[6] = (n >> 16) & 0xFF;
1070                 out[7] = (n >> 24) & 0xFF;
1071                 // rate
1072                 n = cl_capturevideo_soundrate;
1073                 out[24] = (n) & 0xFF;
1074                 out[25] = (n >> 8) & 0xFF;
1075                 out[26] = (n >> 16) & 0xFF;
1076                 out[27] = (n >> 24) & 0xFF;
1077                 // bytes per second (rate * channels * bytes per channel)
1078                 n = cl_capturevideo_soundrate * 2 * 2;
1079                 out[28] = (n) & 0xFF;
1080                 out[29] = (n >> 8) & 0xFF;
1081                 out[30] = (n >> 16) & 0xFF;
1082                 out[31] = (n >> 24) & 0xFF;
1083                 // the length of the data chunk
1084                 n = i - 44;
1085                 out[40] = (n) & 0xFF;
1086                 out[41] = (n >> 8) & 0xFF;
1087                 out[42] = (n >> 16) & 0xFF;
1088                 out[43] = (n >> 24) & 0xFF;
1089                 FS_Seek (cl_capturevideo_soundfile, 0, SEEK_SET);
1090                 FS_Write (cl_capturevideo_soundfile, out, 44);
1091                 FS_Close (cl_capturevideo_soundfile);
1092                 cl_capturevideo_soundfile = NULL;
1093         }
1094
1095         if (cl_capturevideo_buffer)
1096         {
1097                 Mem_Free (cl_capturevideo_buffer);
1098                 cl_capturevideo_buffer = NULL;
1099         }
1100
1101         cl_capturevideo_starttime = 0;
1102         cl_capturevideo_framerate = 0;
1103         cl_capturevideo_frame = 0;
1104 }
1105
1106 qboolean SCR_CaptureVideo_VideoFrame(int newframenum)
1107 {
1108         int x = 0, y = 0, width = vid.width, height = vid.height;
1109         unsigned char *b, *out;
1110         char filename[32];
1111         int outoffset = (width/2)*(height/2);
1112         //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);
1113         // speed is critical here, so do saving as directly as possible
1114         switch (cl_capturevideo_format)
1115         {
1116         case CAPTUREVIDEOFORMAT_RAWYV12:
1117                 // FIXME: width/height must be multiple of 2, enforce this?
1118                 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
1119                 CHECKGLERROR
1120                 // process one line at a time, and CbCr every other line at 2 pixel intervals
1121                 for (y = 0;y < height;y++)
1122                 {
1123                         // 1x1 Y
1124                         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++)
1125                                 *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]]];
1126                         if ((y & 1) == 0)
1127                         {
1128                                 // 2x2 Cb and Cr planes
1129 #if 1
1130                                 // low quality, no averaging
1131                                 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++)
1132                                 {
1133                                         // Cr
1134                                         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];
1135                                         // Cb
1136                                         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];
1137                                 }
1138 #else
1139                                 // high quality, averaging
1140                                 int inpitch = width*3;
1141                                 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++)
1142                                 {
1143                                         int blockr, blockg, blockb;
1144                                         blockr = (b[0] + b[3] + b[inpitch+0] + b[inpitch+3]) >> 2;
1145                                         blockg = (b[1] + b[4] + b[inpitch+1] + b[inpitch+4]) >> 2;
1146                                         blockb = (b[2] + b[5] + b[inpitch+2] + b[inpitch+5]) >> 2;
1147                                         // Cr
1148                                         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];
1149                                         // Cb
1150                                         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];
1151                                 }
1152 #endif
1153                         }
1154                 }
1155                 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
1156                         if (!FS_Write (cl_capturevideo_videofile, cl_capturevideo_buffer + width*height*3, width*height+(width/2)*(height/2)*2))
1157                                 return false;
1158                 return true;
1159         case CAPTUREVIDEOFORMAT_RAWRGB:
1160                 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
1161                 CHECKGLERROR
1162                 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
1163                         if (!FS_Write (cl_capturevideo_videofile, cl_capturevideo_buffer, width*height*3))
1164                                 return false;
1165                 return true;
1166         case CAPTUREVIDEOFORMAT_JPEG:
1167                 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
1168                 CHECKGLERROR
1169                 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
1170                 {
1171                         sprintf(filename, "video/dp%06d.jpg", cl_capturevideo_frame);
1172                         if (!JPEG_SaveImage_preflipped (filename, width, height, cl_capturevideo_buffer))
1173                                 return false;
1174                 }
1175                 return true;
1176         case CAPTUREVIDEOFORMAT_TARGA:
1177                 //return Image_WriteTGARGB_preflipped (filename, width, height, cl_capturevideo_buffer, cl_capturevideo_buffer + vid.width * vid.height * 3, );
1178                 memset (cl_capturevideo_buffer, 0, 18);
1179                 cl_capturevideo_buffer[2] = 2;          // uncompressed type
1180                 cl_capturevideo_buffer[12] = (width >> 0) & 0xFF;
1181                 cl_capturevideo_buffer[13] = (width >> 8) & 0xFF;
1182                 cl_capturevideo_buffer[14] = (height >> 0) & 0xFF;
1183                 cl_capturevideo_buffer[15] = (height >> 8) & 0xFF;
1184                 cl_capturevideo_buffer[16] = 24;        // pixel size
1185                 qglReadPixels (x, y, width, height, GL_BGR, GL_UNSIGNED_BYTE, cl_capturevideo_buffer + 18);
1186                 CHECKGLERROR
1187                 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
1188                 {
1189                         sprintf(filename, "video/dp%06d.tga", cl_capturevideo_frame);
1190                         if (!FS_WriteFile (filename, cl_capturevideo_buffer, width*height*3 + 18))
1191                                 return false;
1192                 }
1193                 return true;
1194         default:
1195                 return false;
1196         }
1197 }
1198
1199 void SCR_CaptureVideo_SoundFrame(unsigned char *bufstereo16le, size_t length, int rate)
1200 {
1201         if (!cl_capturevideo_soundfile)
1202                 return;
1203         cl_capturevideo_soundrate = rate;
1204         if (FS_Write (cl_capturevideo_soundfile, bufstereo16le, 4 * length) < (fs_offset_t)(4 * length))
1205         {
1206                 Cvar_SetValueQuick(&cl_capturevideo, 0);
1207                 Con_Printf("video sound saving failed on frame %i, out of disk space? stopping video capture.\n", cl_capturevideo_frame);
1208                 SCR_CaptureVideo_EndVideo();
1209         }
1210 }
1211
1212 void SCR_CaptureVideo(void)
1213 {
1214         int newframenum;
1215         if (cl_capturevideo.integer && r_render.integer)
1216         {
1217                 if (!cl_capturevideo_active)
1218                         SCR_CaptureVideo_BeginVideo();
1219                 if (cl_capturevideo_framerate != cl_capturevideo_fps.value)
1220                 {
1221                         Con_Printf("You can not change the video framerate while recording a video.\n");
1222                         Cvar_SetValueQuick(&cl_capturevideo_fps, cl_capturevideo_framerate);
1223                 }
1224                 if (cl_capturevideo_soundfile)
1225                 {
1226                         // preserve sound sync by duplicating frames when running slow
1227                         newframenum = (Sys_DoubleTime() - cl_capturevideo_starttime) * cl_capturevideo_framerate;
1228                 }
1229                 else
1230                         newframenum = cl_capturevideo_frame + 1;
1231                 // if falling behind more than one second, stop
1232                 if (newframenum - cl_capturevideo_frame > (int)ceil(cl_capturevideo_framerate))
1233                 {
1234                         Cvar_SetValueQuick(&cl_capturevideo, 0);
1235                         Con_Printf("video saving failed on frame %i, your machine is too slow for this capture speed.\n", cl_capturevideo_frame);
1236                         SCR_CaptureVideo_EndVideo();
1237                         return;
1238                 }
1239                 // write frames
1240                 if (!SCR_CaptureVideo_VideoFrame(newframenum))
1241                 {
1242                         Cvar_SetValueQuick(&cl_capturevideo, 0);
1243                         Con_Printf("video saving failed on frame %i, out of disk space? stopping video capture.\n", cl_capturevideo_frame);
1244                         SCR_CaptureVideo_EndVideo();
1245                 }
1246         }
1247         else if (cl_capturevideo_active)
1248                 SCR_CaptureVideo_EndVideo();
1249 }
1250
1251 /*
1252 ===============
1253 R_Envmap_f
1254
1255 Grab six views for environment mapping tests
1256 ===============
1257 */
1258 struct envmapinfo_s
1259 {
1260         float angles[3];
1261         char *name;
1262         qboolean flipx, flipy, flipdiagonaly;
1263 }
1264 envmapinfo[12] =
1265 {
1266         {{  0,   0, 0}, "rt", false, false, false},
1267         {{  0, 270, 0}, "ft", false, false, false},
1268         {{  0, 180, 0}, "lf", false, false, false},
1269         {{  0,  90, 0}, "bk", false, false, false},
1270         {{-90, 180, 0}, "up",  true,  true, false},
1271         {{ 90, 180, 0}, "dn",  true,  true, false},
1272
1273         {{  0,   0, 0}, "px",  true,  true,  true},
1274         {{  0,  90, 0}, "py", false,  true, false},
1275         {{  0, 180, 0}, "nx", false, false,  true},
1276         {{  0, 270, 0}, "ny",  true, false, false},
1277         {{-90, 180, 0}, "pz", false, false,  true},
1278         {{ 90, 180, 0}, "nz", false, false,  true}
1279 };
1280
1281 static void R_Envmap_f (void)
1282 {
1283         int j, size;
1284         char filename[MAX_QPATH], basename[MAX_QPATH];
1285         unsigned char *buffer1;
1286         unsigned char *buffer2;
1287         unsigned char *buffer3;
1288
1289         if (Cmd_Argc() != 3)
1290         {
1291                 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");
1292                 return;
1293         }
1294
1295         strlcpy (basename, Cmd_Argv(1), sizeof (basename));
1296         size = atoi(Cmd_Argv(2));
1297         if (size != 128 && size != 256 && size != 512 && size != 1024)
1298         {
1299                 Con_Print("envmap: size must be one of 128, 256, 512, or 1024\n");
1300                 return;
1301         }
1302         if (size > vid.width || size > vid.height)
1303         {
1304                 Con_Print("envmap: your resolution is not big enough to render that size\n");
1305                 return;
1306         }
1307
1308         envmap = true;
1309
1310         r_refdef.x = 0;
1311         r_refdef.y = 0;
1312         r_refdef.width = size;
1313         r_refdef.height = size;
1314
1315         r_refdef.frustum_x = tan(90 * M_PI / 360.0);
1316         r_refdef.frustum_y = tan(90 * M_PI / 360.0);
1317
1318         buffer1 = (unsigned char *)Mem_Alloc(tempmempool, size * size * 3);
1319         buffer2 = (unsigned char *)Mem_Alloc(tempmempool, size * size * 3);
1320         buffer3 = (unsigned char *)Mem_Alloc(tempmempool, size * size * 3 + 18);
1321
1322         for (j = 0;j < 12;j++)
1323         {
1324                 sprintf(filename, "env/%s%s.tga", basename, envmapinfo[j].name);
1325                 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);
1326                 R_ClearScreen();
1327                 R_Mesh_Start();
1328                 R_RenderView();
1329                 R_Mesh_Finish();
1330                 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);
1331         }
1332
1333         Mem_Free (buffer1);
1334         Mem_Free (buffer2);
1335         Mem_Free (buffer3);
1336
1337         envmap = false;
1338 }
1339
1340 //=============================================================================
1341
1342 // LordHavoc: SHOWLMP stuff
1343 #define SHOWLMP_MAXLABELS 256
1344 typedef struct showlmp_s
1345 {
1346         qboolean        isactive;
1347         float           x;
1348         float           y;
1349         char            label[32];
1350         char            pic[128];
1351 }
1352 showlmp_t;
1353
1354 showlmp_t showlmp[SHOWLMP_MAXLABELS];
1355
1356 void SHOWLMP_decodehide(void)
1357 {
1358         int i;
1359         char *lmplabel;
1360         lmplabel = MSG_ReadString();
1361         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1362                 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
1363                 {
1364                         showlmp[i].isactive = false;
1365                         return;
1366                 }
1367 }
1368
1369 void SHOWLMP_decodeshow(void)
1370 {
1371         int i, k;
1372         char lmplabel[256], picname[256];
1373         float x, y;
1374         strlcpy (lmplabel,MSG_ReadString(), sizeof (lmplabel));
1375         strlcpy (picname, MSG_ReadString(), sizeof (picname));
1376         if (gamemode == GAME_NEHAHRA) // LordHavoc: nasty old legacy junk
1377         {
1378                 x = MSG_ReadByte();
1379                 y = MSG_ReadByte();
1380         }
1381         else
1382         {
1383                 x = MSG_ReadShort();
1384                 y = MSG_ReadShort();
1385         }
1386         k = -1;
1387         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1388                 if (showlmp[i].isactive)
1389                 {
1390                         if (strcmp(showlmp[i].label, lmplabel) == 0)
1391                         {
1392                                 k = i;
1393                                 break; // drop out to replace it
1394                         }
1395                 }
1396                 else if (k < 0) // find first empty one to replace
1397                         k = i;
1398         if (k < 0)
1399                 return; // none found to replace
1400         // change existing one
1401         showlmp[k].isactive = true;
1402         strlcpy (showlmp[k].label, lmplabel, sizeof (showlmp[k].label));
1403         strlcpy (showlmp[k].pic, picname, sizeof (showlmp[k].pic));
1404         showlmp[k].x = x;
1405         showlmp[k].y = y;
1406 }
1407
1408 void SHOWLMP_drawall(void)
1409 {
1410         int i;
1411         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1412                 if (showlmp[i].isactive)
1413                         DrawQ_Pic(showlmp[i].x, showlmp[i].y, showlmp[i].pic, 0, 0, 1, 1, 1, 1, 0);
1414 }
1415
1416 void SHOWLMP_clear(void)
1417 {
1418         int i;
1419         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1420                 showlmp[i].isactive = false;
1421 }
1422
1423 void CL_SetupScreenSize(void)
1424 {
1425         float conwidth, conheight;
1426
1427         VID_UpdateGamma(false);
1428
1429         conwidth = bound(320, vid_conwidth.value, 2048);
1430         conheight = bound(200, vid_conheight.value, 1536);
1431         if (vid_conwidth.value != conwidth)
1432                 Cvar_SetValue("vid_conwidth", conwidth);
1433         if (vid_conheight.value != conheight)
1434                 Cvar_SetValue("vid_conheight", conheight);
1435
1436         vid_conwidth.integer = vid_conwidth.integer;
1437         vid_conheight.integer = vid_conheight.integer;
1438
1439         SCR_SetUpToDrawConsole();
1440 }
1441
1442 extern void R_Shadow_EditLights_DrawSelectedLightProperties(void);
1443 void CL_UpdateScreen(void)
1444 {
1445         if (!scr_initialized || !con_initialized || vid_hidden)
1446                 return;                         // not initialized yet
1447
1448         // don't allow cheats in multiplayer
1449         if (!cl.islocalgame && cl.worldmodel)
1450         {
1451                 if (r_fullbright.integer != 0)
1452                         Cvar_Set ("r_fullbright", "0");
1453                 if (r_ambient.value != 0)
1454                         Cvar_Set ("r_ambient", "0");
1455         }
1456
1457         // bound viewsize
1458         if (scr_viewsize.value < 30)
1459                 Cvar_Set ("viewsize","30");
1460         if (scr_viewsize.value > 120)
1461                 Cvar_Set ("viewsize","120");
1462
1463         // bound field of view
1464         if (scr_fov.value < 1)
1465                 Cvar_Set ("fov","1");
1466         if (scr_fov.value > 170)
1467                 Cvar_Set ("fov","170");
1468
1469         // intermission is always full screen
1470         if (cl.intermission)
1471                 sb_lines = 0;
1472         else
1473         {
1474                 if (scr_viewsize.value >= 120)
1475                         sb_lines = 0;           // no status bar at all
1476                 else if (scr_viewsize.value >= 110)
1477                         sb_lines = 24;          // no inventory
1478                 else
1479                         sb_lines = 24+16+8;
1480         }
1481
1482         r_refdef.colormask[0] = 1;
1483         r_refdef.colormask[1] = 1;
1484         r_refdef.colormask[2] = 1;
1485
1486         SCR_CaptureVideo();
1487
1488         if (cls.signon == SIGNONS)
1489                 R_TimeReport("other");
1490
1491         CL_SetupScreenSize();
1492
1493         DrawQ_Clear();
1494
1495         if (cls.signon == SIGNONS)
1496                 R_TimeReport("setup");
1497
1498         //FIXME: force menu if nothing else to look at?
1499         //if (key_dest == key_game && cls.signon != SIGNONS && cls.state == ca_disconnected)
1500
1501         if (cls.signon == SIGNONS)
1502         {
1503                 SCR_DrawNet ();
1504                 SCR_DrawTurtle ();
1505                 SCR_DrawPause ();
1506                 if (!r_letterbox.value)
1507                         Sbar_Draw();
1508                 SHOWLMP_drawall();
1509                 SCR_CheckDrawCenterString();
1510         }
1511         MR_Draw();
1512         UI_Callback_Draw();
1513         CL_DrawVideo();
1514         //ui_draw();
1515         if (cls.signon == SIGNONS)
1516         {
1517                 R_TimeReport("2d");
1518                 R_TimeReport_End();
1519                 R_TimeReport_Start();
1520         }
1521         R_Shadow_EditLights_DrawSelectedLightProperties();
1522
1523         SCR_DrawConsole();
1524
1525         SCR_DrawBrand();
1526
1527         SCR_UpdateScreen();
1528 }
1529
1530 void CL_Screen_NewMap(void)
1531 {
1532         SHOWLMP_clear();
1533 }