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