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