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