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