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