6 #include "cl_collision.h"
10 // we have to include snd_main.h here only to get access to snd_renderbuffer->format.speed when writing the AVI headers
13 cvar_t scr_viewsize = {CVAR_SAVE, "viewsize","100", "how large the view should be, 110 disables inventory bar, 120 disables status bar"};
14 cvar_t scr_fov = {CVAR_SAVE, "fov","90", "field of vision, 1-170 degrees, default 90, some players use 110-130"}; // 1 - 170
15 cvar_t scr_conalpha = {CVAR_SAVE, "scr_conalpha", "1", "opacity of console background"};
16 cvar_t scr_conbrightness = {CVAR_SAVE, "scr_conbrightness", "1", "brightness of console background (0 = black, 1 = image)"};
17 cvar_t scr_conforcewhiledisconnected = {0, "scr_conforcewhiledisconnected", "1", "forces fullscreen console while disconnected"};
18 cvar_t scr_menuforcewhiledisconnected = {0, "scr_menuforcewhiledisconnected", "0", "forces menu while disconnected"};
19 cvar_t scr_centertime = {0, "scr_centertime","2", "how long centerprint messages show"};
20 cvar_t scr_showram = {CVAR_SAVE, "showram","1", "show ram icon if low on surface cache memory (not used)"};
21 cvar_t scr_showturtle = {CVAR_SAVE, "showturtle","0", "show turtle icon when framerate is too low (not used)"};
22 cvar_t scr_showpause = {CVAR_SAVE, "showpause","1", "show pause icon when game is paused"};
23 cvar_t scr_showbrand = {0, "showbrand","0", "shows gfx/brand.tga in a corner of the screen (different values select different positions, including centered)"};
24 cvar_t scr_printspeed = {0, "scr_printspeed","8", "speed of intermission printing (episode end texts)"};
25 cvar_t vid_conwidth = {CVAR_SAVE, "vid_conwidth", "640", "virtual width of 2D graphics system"};
26 cvar_t vid_conheight = {CVAR_SAVE, "vid_conheight", "480", "virtual height of 2D graphics system"};
27 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)"};
28 cvar_t scr_screenshot_jpeg = {CVAR_SAVE, "scr_screenshot_jpeg","1", "save jpeg instead of targa"};
29 cvar_t scr_screenshot_jpeg_quality = {CVAR_SAVE, "scr_screenshot_jpeg_quality","0.9", "image quality of saved jpeg"};
30 cvar_t scr_screenshot_gammaboost = {CVAR_SAVE, "scr_screenshot_gammaboost","1", "gamma correction on saved screenshots and videos, 1.0 saves unmodified images"};
31 // scr_screenshot_name is defined in fs.c
32 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 or avi_i420 cvars are on it saves those formats instead, note that scr_screenshot_gammaboost affects the brightness of the output)"};
33 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)"};
34 cvar_t cl_capturevideo_rawrgb = {0, "cl_capturevideo_rawrgb", "0", "saves a single .rgb video file containing uncompressed RGB images (you'll need special processing tools to encode this to something more useful)"};
35 cvar_t cl_capturevideo_rawyv12 = {0, "cl_capturevideo_rawyv12", "0", "saves a single .yv12 video file containing uncompressed YV12 images (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)"};
36 cvar_t cl_capturevideo_avi_i420 = {0, "cl_capturevideo_avi_i420", "1", "saves a single .avi video file containing uncompressed I420 images and PCM sound"};
37 cvar_t cl_capturevideo_number = {CVAR_SAVE, "cl_capturevideo_number", "1", "number to append to video filename, incremented each time a capture begins"};
38 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)"};
39 cvar_t r_stereo_separation = {0, "r_stereo_separation", "4", "separation of eyes in the world (try negative values too)"};
40 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)"};
41 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)"};
42 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"};
43 cvar_t r_stereo_redgreen = {0, "r_stereo_redgreen", "0", "red/green anaglyph stereo glasses (for those who don't mind yellow)"};
44 cvar_t scr_zoomwindow = {CVAR_SAVE, "scr_zoomwindow", "0", "displays a zoomed in overlay window"};
45 cvar_t scr_zoomwindow_viewsizex = {CVAR_SAVE, "scr_zoomwindow_viewsizex", "20", "horizontal viewsize of zoom window"};
46 cvar_t scr_zoomwindow_viewsizey = {CVAR_SAVE, "scr_zoomwindow_viewsizey", "20", "vertical viewsize of zoom window"};
47 cvar_t scr_zoomwindow_fov = {CVAR_SAVE, "scr_zoomwindow_fov", "20", "fov of zoom window"};
50 int jpeg_supported = false;
52 qboolean scr_initialized; // ready to draw
54 float scr_con_current;
56 extern int con_vislines;
58 static void SCR_ScreenShot_f (void);
59 static void R_Envmap_f (void);
62 void R_ClearScreen(void);
65 ===============================================================================
69 ===============================================================================
72 char scr_centerstring[MAX_INPUTLINE];
73 float scr_centertime_start; // for slow victory printing
74 float scr_centertime_off;
83 Called for important messages that should stay in the center of the screen
87 void SCR_CenterPrint(char *str)
89 strlcpy (scr_centerstring, str, sizeof (scr_centerstring));
90 scr_centertime_off = scr_centertime.value;
91 scr_centertime_start = cl.time;
93 // count the number of lines for centering
104 void SCR_DrawCenterString (void)
112 // the finale prints the characters one at a time
114 remaining = (int)(scr_printspeed.value * (cl.time - scr_centertime_start));
118 scr_erase_center = 0;
119 start = scr_centerstring;
124 if (scr_center_lines <= 4)
125 y = (int)(vid_conheight.integer*0.35);
132 // scan the number of characters on the line, not counting color codes
134 for (l=0 ; l<vid_conwidth.integer/8 ; l++)
136 if (start[l] == '\n' || !start[l])
138 // color codes add no visible characters, so don't count them
139 if (start[l] == STRING_COLOR_TAG && (start[l+1] >= '0' && start[l+1] <= '9'))
144 x = (vid_conwidth.integer - chars*8)/2;
149 DrawQ_ColoredString(x, y, start, l, 8, 8, 1, 1, 1, 1, 0, &color);
157 while (*start && *start != '\n')
162 start++; // skip the \n
166 void SCR_CheckDrawCenterString (void)
168 if (scr_center_lines > scr_erase_lines)
169 scr_erase_lines = scr_center_lines;
171 scr_centertime_off -= cl.realframetime;
173 // don't draw if this is a normal stats-screen intermission,
174 // only if it is not an intermission, or a finale intermission
175 if (cl.intermission == 1)
177 if (scr_centertime_off <= 0 && !cl.intermission)
179 if (key_dest != key_game)
182 SCR_DrawCenterString ();
190 void SCR_DrawTurtle (void)
194 if (cls.state != ca_connected)
197 if (!scr_showturtle.integer)
200 if (cl.realframetime < 0.1)
210 DrawQ_Pic (0, 0, Draw_CachePic("gfx/turtle", true), 0, 0, 1, 1, 1, 1, 0);
218 void SCR_DrawNet (void)
220 if (cls.state != ca_connected)
222 if (realtime - cl.last_received_message < 0.3)
224 if (cls.demoplayback)
227 DrawQ_Pic (64, 0, Draw_CachePic("gfx/net", true), 0, 0, 1, 1, 1, 1, 0);
235 void SCR_DrawPause (void)
239 if (cls.state != ca_connected)
242 if (!scr_showpause.integer) // turn off for screenshots
248 pic = Draw_CachePic ("gfx/pause", true);
249 DrawQ_Pic ((vid_conwidth.integer - pic->width)/2, (vid_conheight.integer - pic->height)/2, pic, 0, 0, 1, 1, 1, 1, 0);
257 void SCR_DrawBrand (void)
262 if (!scr_showbrand.value)
265 pic = Draw_CachePic ("gfx/brand", true);
267 switch ((int)scr_showbrand.value)
269 case 1: // bottom left
271 y = vid_conheight.integer - pic->height;
273 case 2: // bottom centre
274 x = (vid_conwidth.integer - pic->width) / 2;
275 y = vid_conheight.integer - pic->height;
277 case 3: // bottom right
278 x = vid_conwidth.integer - pic->width;
279 y = vid_conheight.integer - pic->height;
281 case 4: // centre right
282 x = vid_conwidth.integer - pic->width;
283 y = (vid_conheight.integer - pic->height) / 2;
286 x = vid_conwidth.integer - pic->width;
289 case 6: // top centre
290 x = (vid_conwidth.integer - pic->width) / 2;
297 case 8: // centre left
299 y = (vid_conheight.integer - pic->height) / 2;
305 DrawQ_Pic (x, y, pic, 0, 0, 1, 1, 1, 1, 0);
313 static int SCR_DrawQWDownload(int offset)
319 if (!cls.qw_downloadname[0])
321 dpsnprintf(temp, sizeof(temp), "Downloading %s ... %3i%%\n", cls.qw_downloadname, cls.qw_downloadpercent);
322 len = (int)strlen(temp);
323 x = (vid_conwidth.integer - len*size) / 2;
324 y = vid_conheight.integer - size - offset;
325 DrawQ_Pic(0, y, NULL, vid_conwidth.integer, size, 0, 0, 0, 0.5, 0);
326 DrawQ_String(x, y, temp, len, size, size, 1, 1, 1, 1, 0);
335 static int SCR_DrawCurlDownload(int offset)
342 Curl_downloadinfo_t *downinfo;
346 downinfo = Curl_GetDownloadInfo(&nDownloads, &addinfo);
350 y = vid_conheight.integer - size * nDownloads - offset;
354 len = (int)strlen(addinfo);
355 x = (vid_conwidth.integer - len*size) / 2;
356 DrawQ_Pic(0, y - size, NULL, vid_conwidth.integer, size, 1, 1, 1, 0.8, 0);
357 DrawQ_String(x, y - size, addinfo, len, size, size, 0, 0, 0, 1, 0);
360 for(i = 0; i != nDownloads; ++i)
362 if(downinfo[i].queued)
363 dpsnprintf(temp, sizeof(temp), "Still in queue: %s\n", downinfo[i].filename);
364 else if(downinfo[i].progress <= 0)
365 dpsnprintf(temp, sizeof(temp), "Downloading %s ... ???.?%% @ %.1f KiB/s\n", downinfo[i].filename, downinfo[i].speed / 1024.0);
367 dpsnprintf(temp, sizeof(temp), "Downloading %s ... %5.1f%% @ %.1f KiB/s\n", downinfo[i].filename, 100.0 * downinfo[i].progress, downinfo[i].speed / 1024.0);
368 len = (int)strlen(temp);
369 x = (vid_conwidth.integer - len*size) / 2;
370 DrawQ_Pic(0, y + i * size, NULL, vid_conwidth.integer, size, 0, 0, 0, 0.8, 0);
371 DrawQ_String(x, y + i * size, temp, len, size, size, 1, 1, 1, 1, 0);
376 return 8 * (nDownloads + (addinfo ? 1 : 0));
384 static void SCR_DrawDownload()
387 offset += SCR_DrawQWDownload(offset);
388 offset += SCR_DrawCurlDownload(offset);
391 //=============================================================================
395 SCR_SetUpToDrawConsole
398 void SCR_SetUpToDrawConsole (void)
400 // lines of console to display
402 static int framecounter = 0;
406 if (scr_menuforcewhiledisconnected.integer && key_dest == key_game && cls.state == ca_disconnected)
408 if (framecounter >= 2)
416 if (scr_conforcewhiledisconnected.integer && key_dest == key_game && cls.signon != SIGNONS)
417 key_consoleactive |= KEY_CONSOLEACTIVE_FORCED;
419 key_consoleactive &= ~KEY_CONSOLEACTIVE_FORCED;
421 // decide on the height of the console
422 if (key_consoleactive & KEY_CONSOLEACTIVE_USER)
423 conlines = vid_conheight.integer/2; // half screen
425 conlines = 0; // none visible
427 scr_con_current = conlines;
435 void SCR_DrawConsole (void)
437 if (key_consoleactive & KEY_CONSOLEACTIVE_FORCED)
440 Con_DrawConsole (vid_conheight.integer);
442 else if (scr_con_current)
443 Con_DrawConsole ((int)scr_con_current);
447 if ((key_dest == key_game || key_dest == key_message) && !r_letterbox.value)
448 Con_DrawNotify (); // only draw notify in game
454 SCR_BeginLoadingPlaque
458 void SCR_BeginLoadingPlaque (void)
460 // save console log up to this point to log_file if it was set by configs
465 SCR_UpdateLoadingScreen();
468 //=============================================================================
470 char r_speeds_string[1024];
471 int speedstringcount, r_timereport_active;
472 double r_timereport_temp = 0, r_timereport_current = 0, r_timereport_start = 0;
474 void R_TimeReport(char *desc)
480 if (r_speeds.integer < 2 || !r_timereport_active)
484 qglFinish();CHECKGLERROR
485 r_timereport_temp = r_timereport_current;
486 r_timereport_current = Sys_DoubleTime();
487 t = (int) ((r_timereport_current - r_timereport_temp) * 1000000.0 + 0.5);
489 dpsnprintf(tempbuf, sizeof(tempbuf), "%8i %-11s", t, desc);
490 length = (int)strlen(tempbuf);
491 if (speedstringcount + length > (vid_conwidth.integer / 8))
493 strlcat(r_speeds_string, "\n", sizeof(r_speeds_string));
494 speedstringcount = 0;
496 strlcat(r_speeds_string, tempbuf, sizeof(r_speeds_string));
497 speedstringcount += length;
500 void R_TimeReport_Frame(void)
504 if (r_speeds_string[0])
506 if (r_timereport_active)
508 r_timereport_current = r_timereport_start;
509 R_TimeReport("total");
512 if (r_speeds_string[strlen(r_speeds_string)-1] == '\n')
513 r_speeds_string[strlen(r_speeds_string)-1] = 0;
515 for (i = 0;r_speeds_string[i];i++)
516 if (r_speeds_string[i] == '\n')
518 y = vid_conheight.integer - sb_lines - lines * 8;
520 DrawQ_Pic(0, y, NULL, vid_conwidth.integer, lines * 8, 0, 0, 0, 0.5, 0);
521 while (r_speeds_string[i])
524 while (r_speeds_string[i] && r_speeds_string[i] != '\n')
527 DrawQ_String(0, y, r_speeds_string + j, i - j, 8, 8, 1, 1, 1, 1, 0);
528 if (r_speeds_string[i] == '\n')
532 r_speeds_string[0] = 0;
533 r_timereport_active = false;
535 if (r_speeds.integer && cls.signon == SIGNONS && cls.state == ca_connected)
537 speedstringcount = 0;
538 r_speeds_string[0] = 0;
539 r_timereport_active = false;
540 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]);
541 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);
542 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);
543 if (r_refdef.stats.bloom)
544 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);
546 sprintf(r_speeds_string + strlen(r_speeds_string), "rendered%6i meshes%8i triangles\n", r_refdef.stats.meshes, r_refdef.stats.meshes_elements / 3);
548 memset(&r_refdef.stats, 0, sizeof(r_refdef.stats));
550 if (r_speeds.integer >= 2)
552 r_timereport_active = true;
553 r_timereport_start = r_timereport_current = Sys_DoubleTime();
565 void SCR_SizeUp_f (void)
567 Cvar_SetValue ("viewsize",scr_viewsize.value+10);
578 void SCR_SizeDown_f (void)
580 Cvar_SetValue ("viewsize",scr_viewsize.value-10);
583 void CL_Screen_Init(void)
585 Cvar_RegisterVariable (&scr_fov);
586 Cvar_RegisterVariable (&scr_viewsize);
587 Cvar_RegisterVariable (&scr_conalpha);
588 Cvar_RegisterVariable (&scr_conbrightness);
589 Cvar_RegisterVariable (&scr_conforcewhiledisconnected);
590 Cvar_RegisterVariable (&scr_menuforcewhiledisconnected);
591 Cvar_RegisterVariable (&scr_showram);
592 Cvar_RegisterVariable (&scr_showturtle);
593 Cvar_RegisterVariable (&scr_showpause);
594 Cvar_RegisterVariable (&scr_showbrand);
595 Cvar_RegisterVariable (&scr_centertime);
596 Cvar_RegisterVariable (&scr_printspeed);
597 Cvar_RegisterVariable (&vid_conwidth);
598 Cvar_RegisterVariable (&vid_conheight);
599 Cvar_RegisterVariable (&vid_pixelheight);
600 Cvar_RegisterVariable (&scr_screenshot_jpeg);
601 Cvar_RegisterVariable (&scr_screenshot_jpeg_quality);
602 Cvar_RegisterVariable (&scr_screenshot_gammaboost);
603 Cvar_RegisterVariable (&cl_capturevideo);
604 Cvar_RegisterVariable (&cl_capturevideo_fps);
605 Cvar_RegisterVariable (&cl_capturevideo_rawrgb);
606 Cvar_RegisterVariable (&cl_capturevideo_rawyv12);
607 Cvar_RegisterVariable (&cl_capturevideo_avi_i420);
608 Cvar_RegisterVariable (&cl_capturevideo_number);
609 Cvar_RegisterVariable (&r_letterbox);
610 Cvar_RegisterVariable(&r_stereo_separation);
611 Cvar_RegisterVariable(&r_stereo_sidebyside);
612 Cvar_RegisterVariable(&r_stereo_redblue);
613 Cvar_RegisterVariable(&r_stereo_redcyan);
614 Cvar_RegisterVariable(&r_stereo_redgreen);
615 Cvar_RegisterVariable(&scr_zoomwindow);
616 Cvar_RegisterVariable(&scr_zoomwindow_viewsizex);
617 Cvar_RegisterVariable(&scr_zoomwindow_viewsizey);
618 Cvar_RegisterVariable(&scr_zoomwindow_fov);
620 Cmd_AddCommand ("sizeup",SCR_SizeUp_f, "increase view size (increases viewsize cvar)");
621 Cmd_AddCommand ("sizedown",SCR_SizeDown_f, "decrease view size (decreases viewsize cvar)");
622 Cmd_AddCommand ("screenshot",SCR_ScreenShot_f, "takes a screenshot of the next rendered frame");
623 Cmd_AddCommand ("envmap", R_Envmap_f, "render a cubemap (skybox) of the current scene");
625 scr_initialized = true;
633 void SCR_ScreenShot_f (void)
635 static int shotnumber;
636 static char oldname[MAX_QPATH];
637 char base[MAX_QPATH];
638 char filename[MAX_QPATH];
639 unsigned char *buffer1;
640 unsigned char *buffer2;
641 unsigned char *buffer3;
642 qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
644 sprintf (base, "screenshots/%s", scr_screenshot_name.string);
646 if (strcmp (oldname, scr_screenshot_name.string))
648 sprintf(oldname, "%s", scr_screenshot_name.string);
652 // find a file name to save it to
653 for (;shotnumber < 1000000;shotnumber++)
654 if (!FS_SysFileExists(va("%s/%s%06d.tga", fs_gamedir, base, shotnumber)) && !FS_SysFileExists(va("%s/%s%06d.jpg", fs_gamedir, base, shotnumber)))
656 if (shotnumber >= 1000000)
658 Con_Print("SCR_ScreenShot_f: Couldn't create the image file\n");
662 sprintf(filename, "%s%06d.%s", base, shotnumber, jpeg ? "jpg" : "tga");
664 buffer1 = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 3);
665 buffer2 = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 3);
666 buffer3 = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 3 + 18);
668 if (SCR_ScreenShot (filename, buffer1, buffer2, buffer3, 0, 0, vid.width, vid.height, false, false, false, jpeg, true))
669 Con_Printf("Wrote %s\n", filename);
671 Con_Printf("unable to write %s\n", filename);
680 static void SCR_CaptureVideo_RIFF_Start(void)
682 memset(&cls.capturevideo.riffbuffer, 0, sizeof(sizebuf_t));
683 cls.capturevideo.riffbuffer.maxsize = sizeof(cls.capturevideo.riffbufferdata);
684 cls.capturevideo.riffbuffer.data = cls.capturevideo.riffbufferdata;
687 static void SCR_CaptureVideo_RIFF_Flush(void)
689 if (cls.capturevideo.riffbuffer.cursize > 0)
691 if (!FS_Write(cls.capturevideo.videofile, cls.capturevideo.riffbuffer.data, cls.capturevideo.riffbuffer.cursize))
692 cls.capturevideo.error = true;
693 cls.capturevideo.riffbuffer.cursize = 0;
694 cls.capturevideo.riffbuffer.overflowed = false;
698 static void SCR_CaptureVideo_RIFF_WriteBytes(const unsigned char *data, size_t size)
700 SCR_CaptureVideo_RIFF_Flush();
701 if (!FS_Write(cls.capturevideo.videofile, data, size))
702 cls.capturevideo.error = true;
705 static void SCR_CaptureVideo_RIFF_Write32(int n)
707 if (cls.capturevideo.riffbuffer.cursize + 4 > cls.capturevideo.riffbuffer.maxsize)
708 SCR_CaptureVideo_RIFF_Flush();
709 MSG_WriteLong(&cls.capturevideo.riffbuffer, n);
712 static void SCR_CaptureVideo_RIFF_Write16(int n)
714 if (cls.capturevideo.riffbuffer.cursize + 2 > cls.capturevideo.riffbuffer.maxsize)
715 SCR_CaptureVideo_RIFF_Flush();
716 MSG_WriteShort(&cls.capturevideo.riffbuffer, n);
719 static void SCR_CaptureVideo_RIFF_WriteFourCC(const char *chunkfourcc)
721 if (cls.capturevideo.riffbuffer.cursize + (int)strlen(chunkfourcc) > cls.capturevideo.riffbuffer.maxsize)
722 SCR_CaptureVideo_RIFF_Flush();
723 MSG_WriteUnterminatedString(&cls.capturevideo.riffbuffer, chunkfourcc);
726 static void SCR_CaptureVideo_RIFF_WriteTerminatedString(const char *string)
728 if (cls.capturevideo.riffbuffer.cursize + (int)strlen(string) > cls.capturevideo.riffbuffer.maxsize)
729 SCR_CaptureVideo_RIFF_Flush();
730 MSG_WriteString(&cls.capturevideo.riffbuffer, string);
733 static fs_offset_t SCR_CaptureVideo_RIFF_GetPosition(void)
735 SCR_CaptureVideo_RIFF_Flush();
736 return FS_Tell(cls.capturevideo.videofile);
739 static void SCR_CaptureVideo_RIFF_Push(const char *chunkfourcc, const char *listtypefourcc)
741 SCR_CaptureVideo_RIFF_WriteFourCC(chunkfourcc);
742 SCR_CaptureVideo_RIFF_Write32(0);
743 SCR_CaptureVideo_RIFF_Flush();
744 cls.capturevideo.riffstackstartoffset[cls.capturevideo.riffstacklevel++] = SCR_CaptureVideo_RIFF_GetPosition();
746 SCR_CaptureVideo_RIFF_WriteFourCC(listtypefourcc);
749 static void SCR_CaptureVideo_RIFF_Pop(void)
753 unsigned char sizebytes[4];
754 // write out the chunk size and then return to the current file position
755 cls.capturevideo.riffstacklevel--;
756 offset = SCR_CaptureVideo_RIFF_GetPosition();
757 x = (int)(offset - (cls.capturevideo.riffstackstartoffset[cls.capturevideo.riffstacklevel]));
758 sizebytes[0] = (x) & 0xff;sizebytes[1] = (x >> 8) & 0xff;sizebytes[2] = (x >> 16) & 0xff;sizebytes[3] = (x >> 24) & 0xff;
759 FS_Seek(cls.capturevideo.videofile, -(x + 4), SEEK_END);
760 FS_Write(cls.capturevideo.videofile, sizebytes, 4);
761 FS_Seek(cls.capturevideo.videofile, 0, SEEK_END);
765 FS_Write(cls.capturevideo.videofile, &c, 1);
769 static void SCR_CaptureVideo_RIFF_IndexEntry(const char *chunkfourcc, int chunksize, int flags)
771 if (cls.capturevideo.riffstacklevel != 2)
772 Sys_Error("SCR_Capturevideo_RIFF_IndexEntry: RIFF stack level is %i (should be 2)\n", cls.capturevideo.riffstacklevel);
773 if (cls.capturevideo.riffindexbuffer.cursize + 16 > cls.capturevideo.riffindexbuffer.maxsize)
775 int oldsize = cls.capturevideo.riffindexbuffer.maxsize;
776 unsigned char *olddata;
777 olddata = cls.capturevideo.riffindexbuffer.data;
778 cls.capturevideo.riffindexbuffer.maxsize = max(cls.capturevideo.riffindexbuffer.maxsize * 2, 4096);
779 cls.capturevideo.riffindexbuffer.data = Mem_Alloc(tempmempool, cls.capturevideo.riffindexbuffer.maxsize);
782 memcpy(cls.capturevideo.riffindexbuffer.data, olddata, oldsize);
786 MSG_WriteUnterminatedString(&cls.capturevideo.riffindexbuffer, chunkfourcc);
787 MSG_WriteLong(&cls.capturevideo.riffindexbuffer, flags);
788 MSG_WriteLong(&cls.capturevideo.riffindexbuffer, (int)FS_Tell(cls.capturevideo.videofile) - cls.capturevideo.riffstackstartoffset[1]);
789 MSG_WriteLong(&cls.capturevideo.riffindexbuffer, chunksize);
792 static void SCR_CaptureVideo_RIFF_Finish(void)
794 // close the "movi" list
795 SCR_CaptureVideo_RIFF_Pop();
796 // write the idx1 chunk that we've been building while saving the frames
797 SCR_CaptureVideo_RIFF_Push("idx1", NULL);
798 SCR_CaptureVideo_RIFF_WriteBytes(cls.capturevideo.riffindexbuffer.data, cls.capturevideo.riffindexbuffer.cursize);
799 SCR_CaptureVideo_RIFF_Pop();
800 cls.capturevideo.riffindexbuffer.cursize = 0;
801 // pop the RIFF chunk itself
802 while (cls.capturevideo.riffstacklevel > 0)
803 SCR_CaptureVideo_RIFF_Pop();
804 SCR_CaptureVideo_RIFF_Flush();
807 static void SCR_CaptureVideo_RIFF_OverflowCheck(int framesize)
810 if (cls.capturevideo.riffstacklevel != 2)
811 Sys_Error("SCR_CaptureVideo_RIFF_OverflowCheck: chunk stack leakage!\n");
812 // check where we are in the file
813 SCR_CaptureVideo_RIFF_Flush();
814 cursize = SCR_CaptureVideo_RIFF_GetPosition() - cls.capturevideo.riffstackstartoffset[0];
815 // if this would overflow the windows limit of 1GB per RIFF chunk, we need
816 // to close the current RIFF chunk and open another for future frames
817 if (8 + cursize + framesize > 1<<30)
819 SCR_CaptureVideo_RIFF_Finish();
820 while (cls.capturevideo.riffstacklevel > 0)
821 SCR_CaptureVideo_RIFF_Pop();
822 // begin a new 1GB extended section of the AVI
823 SCR_CaptureVideo_RIFF_Push("RIFF", "AVIX");
824 SCR_CaptureVideo_RIFF_Push("LIST", "movi");
828 void SCR_CaptureVideo_BeginVideo(void)
831 int width = vid.width, height = vid.height, x;
833 unsigned char out[44];
834 if (cls.capturevideo.active)
836 memset(&cls.capturevideo, 0, sizeof(cls.capturevideo));
837 // soundrate is figured out on the first SoundFrame
838 cls.capturevideo.active = true;
839 cls.capturevideo.starttime = Sys_DoubleTime();
840 cls.capturevideo.framerate = bound(1, cl_capturevideo_fps.value, 1000);
841 cls.capturevideo.soundrate = S_GetSoundRate();
842 cls.capturevideo.frame = 0;
843 cls.capturevideo.soundsampleframe = 0;
844 cls.capturevideo.buffer = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * (3+3+3) + 18);
845 gamma = 1.0/scr_screenshot_gammaboost.value;
846 dpsnprintf(cls.capturevideo.basename, sizeof(cls.capturevideo.basename), "video/dpvideo%03i", cl_capturevideo_number.integer);
847 Cvar_SetValueQuick(&cl_capturevideo_number, cl_capturevideo_number.integer + 1);
850 for (i = 0;i < 256;i++)
852 unsigned char j = (unsigned char)bound(0, 255*pow(i/255.0, gamma), 255);
853 cls.capturevideo.rgbgammatable[0][i] = j;
854 cls.capturevideo.rgbgammatable[1][i] = j;
855 cls.capturevideo.rgbgammatable[2][i] = j;
859 R = Y + 1.4075 * (Cr - 128);
860 G = Y + -0.3455 * (Cb - 128) + -0.7169 * (Cr - 128);
861 B = Y + 1.7790 * (Cb - 128);
862 Y = R * .299 + G * .587 + B * .114;
863 Cb = R * -.169 + G * -.332 + B * .500 + 128.;
864 Cr = R * .500 + G * -.419 + B * -.0813 + 128.;
866 for (i = 0;i < 256;i++)
868 g = 255*pow(i/255.0, gamma);
869 // Y weights from RGB
870 cls.capturevideo.rgbtoyuvscaletable[0][0][i] = (short)(g * 0.299);
871 cls.capturevideo.rgbtoyuvscaletable[0][1][i] = (short)(g * 0.587);
872 cls.capturevideo.rgbtoyuvscaletable[0][2][i] = (short)(g * 0.114);
873 // Cb weights from RGB
874 cls.capturevideo.rgbtoyuvscaletable[1][0][i] = (short)(g * -0.169);
875 cls.capturevideo.rgbtoyuvscaletable[1][1][i] = (short)(g * -0.332);
876 cls.capturevideo.rgbtoyuvscaletable[1][2][i] = (short)(g * 0.500);
877 // Cr weights from RGB
878 cls.capturevideo.rgbtoyuvscaletable[2][0][i] = (short)(g * 0.500);
879 cls.capturevideo.rgbtoyuvscaletable[2][1][i] = (short)(g * -0.419);
880 cls.capturevideo.rgbtoyuvscaletable[2][2][i] = (short)(g * -0.0813);
881 // range reduction of YCbCr to valid signal range
882 cls.capturevideo.yuvnormalizetable[0][i] = 16 + i * (236-16) / 256;
883 cls.capturevideo.yuvnormalizetable[1][i] = 16 + i * (240-16) / 256;
884 cls.capturevideo.yuvnormalizetable[2][i] = 16 + i * (240-16) / 256;
887 if (cl_capturevideo_avi_i420.integer)
889 cls.capturevideo.format = CAPTUREVIDEOFORMAT_AVI_I420;
890 cls.capturevideo.videofile = FS_Open (va("%s.avi", cls.capturevideo.basename), "wb", false, true);
891 SCR_CaptureVideo_RIFF_Start();
892 // enclosing RIFF chunk (there can be multiple of these in >1GB files, the later ones are "AVIX" instead of "AVI " and have no header/stream info)
893 SCR_CaptureVideo_RIFF_Push("RIFF", "AVI ");
895 SCR_CaptureVideo_RIFF_Push("LIST", "hdrl");
896 SCR_CaptureVideo_RIFF_Push("avih", NULL);
897 SCR_CaptureVideo_RIFF_Write32((int)(1000000.0 / cls.capturevideo.framerate)); // microseconds per frame
898 SCR_CaptureVideo_RIFF_Write32(0); // max bytes per second
899 SCR_CaptureVideo_RIFF_Write32(0); // padding granularity
900 SCR_CaptureVideo_RIFF_Write32(0x910); // flags (AVIF_HASINDEX | AVIF_ISINTERLEAVED | AVIF_TRUSTCKTYPE)
901 cls.capturevideo.videofile_totalframes_offset1 = SCR_CaptureVideo_RIFF_GetPosition();
902 SCR_CaptureVideo_RIFF_Write32(0); // total frames
903 SCR_CaptureVideo_RIFF_Write32(0); // initial frames
904 if (cls.capturevideo.soundrate)
905 SCR_CaptureVideo_RIFF_Write32(2); // number of streams
907 SCR_CaptureVideo_RIFF_Write32(1); // number of streams
908 SCR_CaptureVideo_RIFF_Write32(0); // suggested buffer size
909 SCR_CaptureVideo_RIFF_Write32(width); // width
910 SCR_CaptureVideo_RIFF_Write32(height); // height
911 SCR_CaptureVideo_RIFF_Write32(0); // reserved[0]
912 SCR_CaptureVideo_RIFF_Write32(0); // reserved[1]
913 SCR_CaptureVideo_RIFF_Write32(0); // reserved[2]
914 SCR_CaptureVideo_RIFF_Write32(0); // reserved[3]
915 SCR_CaptureVideo_RIFF_Pop();
917 SCR_CaptureVideo_RIFF_Push("LIST", "strl");
918 SCR_CaptureVideo_RIFF_Push("strh", "vids");
919 SCR_CaptureVideo_RIFF_WriteFourCC("I420"); // stream fourcc (I420 colorspace, uncompressed)
920 SCR_CaptureVideo_RIFF_Write32(0); // flags
921 SCR_CaptureVideo_RIFF_Write16(0); // priority
922 SCR_CaptureVideo_RIFF_Write16(0); // language
923 SCR_CaptureVideo_RIFF_Write32(0); // initial frames
924 // find an ideal divisor for the framerate
925 for (x = 1;x < 1000;x++)
926 if (cls.capturevideo.framerate * x == floor(cls.capturevideo.framerate * x))
928 SCR_CaptureVideo_RIFF_Write32(x); // samples/second divisor
929 SCR_CaptureVideo_RIFF_Write32((int)(cls.capturevideo.framerate * x)); // samples/second multiplied by divisor
930 SCR_CaptureVideo_RIFF_Write32(0); // start
931 cls.capturevideo.videofile_totalframes_offset2 = SCR_CaptureVideo_RIFF_GetPosition();
932 SCR_CaptureVideo_RIFF_Write32(0); // length
933 SCR_CaptureVideo_RIFF_Write32(width*height+(width/2)*(height/2)*2); // suggested buffer size
934 SCR_CaptureVideo_RIFF_Write32(0); // quality
935 SCR_CaptureVideo_RIFF_Write32(0); // sample size
936 SCR_CaptureVideo_RIFF_Write16(0); // frame left
937 SCR_CaptureVideo_RIFF_Write16(0); // frame top
938 SCR_CaptureVideo_RIFF_Write16(width); // frame right
939 SCR_CaptureVideo_RIFF_Write16(height); // frame bottom
940 SCR_CaptureVideo_RIFF_Pop();
941 // video stream format
942 SCR_CaptureVideo_RIFF_Push("strf", NULL);
943 SCR_CaptureVideo_RIFF_Write32(40); // BITMAPINFO struct size
944 SCR_CaptureVideo_RIFF_Write32(width); // width
945 SCR_CaptureVideo_RIFF_Write32(height); // height
946 SCR_CaptureVideo_RIFF_Write16(3); // planes
947 SCR_CaptureVideo_RIFF_Write16(12); // bitcount
948 SCR_CaptureVideo_RIFF_WriteFourCC("I420"); // compression
949 SCR_CaptureVideo_RIFF_Write32(width*height+(width/2)*(height/2)*2); // size of image
950 SCR_CaptureVideo_RIFF_Write32(0); // x pixels per meter
951 SCR_CaptureVideo_RIFF_Write32(0); // y pixels per meter
952 SCR_CaptureVideo_RIFF_Write32(0); // color used
953 SCR_CaptureVideo_RIFF_Write32(0); // color important
954 SCR_CaptureVideo_RIFF_Pop();
955 SCR_CaptureVideo_RIFF_Pop();
956 if (cls.capturevideo.soundrate)
959 SCR_CaptureVideo_RIFF_Push("LIST", "strl");
960 SCR_CaptureVideo_RIFF_Push("strh", "auds");
961 SCR_CaptureVideo_RIFF_Write32(1); // stream fourcc (PCM audio, uncompressed)
962 SCR_CaptureVideo_RIFF_Write32(0); // flags
963 SCR_CaptureVideo_RIFF_Write16(0); // priority
964 SCR_CaptureVideo_RIFF_Write16(0); // language
965 SCR_CaptureVideo_RIFF_Write32(0); // initial frames
966 SCR_CaptureVideo_RIFF_Write32(1); // samples/second divisor
967 SCR_CaptureVideo_RIFF_Write32((int)(cls.capturevideo.soundrate)); // samples/second multiplied by divisor
968 SCR_CaptureVideo_RIFF_Write32(0); // start
969 cls.capturevideo.videofile_totalsampleframes_offset = SCR_CaptureVideo_RIFF_GetPosition();
970 SCR_CaptureVideo_RIFF_Write32(0); // length
971 SCR_CaptureVideo_RIFF_Write32(cls.capturevideo.soundrate * 2); // suggested buffer size (this is a half second)
972 SCR_CaptureVideo_RIFF_Write32(0); // quality
973 SCR_CaptureVideo_RIFF_Write32(4); // sample size
974 SCR_CaptureVideo_RIFF_Write16(0); // frame left
975 SCR_CaptureVideo_RIFF_Write16(0); // frame top
976 SCR_CaptureVideo_RIFF_Write16(0); // frame right
977 SCR_CaptureVideo_RIFF_Write16(0); // frame bottom
978 SCR_CaptureVideo_RIFF_Pop();
979 // audio stream format
980 SCR_CaptureVideo_RIFF_Push("strf", NULL);
981 SCR_CaptureVideo_RIFF_Write16(1); // format (uncompressed PCM?)
982 SCR_CaptureVideo_RIFF_Write16(2); // channels (stereo)
983 SCR_CaptureVideo_RIFF_Write32(cls.capturevideo.soundrate); // sampleframes per second
984 SCR_CaptureVideo_RIFF_Write32(cls.capturevideo.soundrate * 4); // average bytes per second
985 SCR_CaptureVideo_RIFF_Write16(4); // block align
986 SCR_CaptureVideo_RIFF_Write16(16); // bits per sample
987 SCR_CaptureVideo_RIFF_Write16(0); // size
988 SCR_CaptureVideo_RIFF_Pop();
989 SCR_CaptureVideo_RIFF_Pop();
991 // close the AVI header list
992 SCR_CaptureVideo_RIFF_Pop();
993 // software that produced this AVI video file
994 SCR_CaptureVideo_RIFF_Push("LIST", "INFO");
995 SCR_CaptureVideo_RIFF_Push("ISFT", NULL);
996 SCR_CaptureVideo_RIFF_WriteTerminatedString(engineversion);
997 SCR_CaptureVideo_RIFF_Pop();
998 SCR_CaptureVideo_RIFF_Push("JUNK", NULL);
999 x = 4096 - SCR_CaptureVideo_RIFF_GetPosition();
1002 const char *junkfiller = "[ DarkPlaces junk data ]";
1003 int i = min(x, (int)strlen(junkfiller));
1004 SCR_CaptureVideo_RIFF_WriteBytes((const unsigned char *)junkfiller, i);
1007 SCR_CaptureVideo_RIFF_Pop();
1008 SCR_CaptureVideo_RIFF_Pop();
1009 // begin the actual video section now
1010 SCR_CaptureVideo_RIFF_Push("LIST", "movi");
1011 // we're done with the headers now...
1012 SCR_CaptureVideo_RIFF_Flush();
1013 if (cls.capturevideo.riffstacklevel != 2)
1014 Sys_Error("SCR_CaptureVideo_BeginVideo: broken AVI writing code (stack level is %i (should be 2) at end of headers)\n", cls.capturevideo.riffstacklevel);
1016 else if (cl_capturevideo_rawrgb.integer)
1018 cls.capturevideo.format = CAPTUREVIDEOFORMAT_RAWRGB;
1019 cls.capturevideo.videofile = FS_Open (va("%s.rgb", cls.capturevideo.basename), "wb", false, true);
1021 else if (cl_capturevideo_rawyv12.integer)
1023 cls.capturevideo.format = CAPTUREVIDEOFORMAT_RAWYV12;
1024 cls.capturevideo.videofile = FS_Open (va("%s.yv12", cls.capturevideo.basename), "wb", false, true);
1026 else if (scr_screenshot_jpeg.integer)
1028 cls.capturevideo.format = CAPTUREVIDEOFORMAT_JPEG;
1029 cls.capturevideo.videofile = NULL;
1033 cls.capturevideo.format = CAPTUREVIDEOFORMAT_TARGA;
1034 cls.capturevideo.videofile = NULL;
1037 switch(cls.capturevideo.format)
1039 case CAPTUREVIDEOFORMAT_AVI_I420:
1040 cls.capturevideo.soundfile = NULL;
1043 cls.capturevideo.soundfile = FS_Open (va("%s.wav", cls.capturevideo.basename), "wb", false, true);
1044 if (cls.capturevideo.soundfile)
1046 // wave header will be filled out when video ends
1048 FS_Write (cls.capturevideo.soundfile, out, 44);
1051 Con_Printf("Could not open video/dpvideo.wav for writing, sound capture disabled\n");
1056 void SCR_CaptureVideo_EndVideo(void)
1059 unsigned char out[44];
1060 if (!cls.capturevideo.active)
1062 cls.capturevideo.active = false;
1063 if (cls.capturevideo.videofile)
1065 switch(cls.capturevideo.format)
1067 case CAPTUREVIDEOFORMAT_AVI_I420:
1068 // close any open chunks
1069 SCR_CaptureVideo_RIFF_Finish();
1070 FS_Seek(cls.capturevideo.videofile, cls.capturevideo.videofile_totalframes_offset1, SEEK_SET);
1071 SCR_CaptureVideo_RIFF_Write32(cls.capturevideo.frame);
1072 SCR_CaptureVideo_RIFF_Flush();
1073 FS_Seek(cls.capturevideo.videofile, cls.capturevideo.videofile_totalframes_offset2, SEEK_SET);
1074 SCR_CaptureVideo_RIFF_Write32(cls.capturevideo.frame);
1075 SCR_CaptureVideo_RIFF_Flush();
1076 if (cls.capturevideo.soundrate)
1078 FS_Seek(cls.capturevideo.videofile, cls.capturevideo.videofile_totalsampleframes_offset, SEEK_SET);
1079 SCR_CaptureVideo_RIFF_Write32(cls.capturevideo.soundsampleframe);
1080 SCR_CaptureVideo_RIFF_Flush();
1086 FS_Close(cls.capturevideo.videofile);
1087 cls.capturevideo.videofile = NULL;
1090 // finish the wave file
1091 if (cls.capturevideo.soundfile)
1093 i = (int)FS_Tell (cls.capturevideo.soundfile);
1094 //"RIFF", (int) unknown (chunk size), "WAVE",
1095 //"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
1096 //"data", (int) unknown (chunk size)
1097 memcpy (out, "RIFF****WAVEfmt \x10\x00\x00\x00\x01\x00\x02\x00********\x04\x00\x10\0data****", 44);
1098 // the length of the whole RIFF chunk
1100 out[4] = (n) & 0xFF;
1101 out[5] = (n >> 8) & 0xFF;
1102 out[6] = (n >> 16) & 0xFF;
1103 out[7] = (n >> 24) & 0xFF;
1105 n = cls.capturevideo.soundrate;
1106 out[24] = (n) & 0xFF;
1107 out[25] = (n >> 8) & 0xFF;
1108 out[26] = (n >> 16) & 0xFF;
1109 out[27] = (n >> 24) & 0xFF;
1110 // bytes per second (rate * channels * bytes per channel)
1111 n = cls.capturevideo.soundrate * 2 * 2;
1112 out[28] = (n) & 0xFF;
1113 out[29] = (n >> 8) & 0xFF;
1114 out[30] = (n >> 16) & 0xFF;
1115 out[31] = (n >> 24) & 0xFF;
1116 // the length of the data chunk
1118 out[40] = (n) & 0xFF;
1119 out[41] = (n >> 8) & 0xFF;
1120 out[42] = (n >> 16) & 0xFF;
1121 out[43] = (n >> 24) & 0xFF;
1122 FS_Seek (cls.capturevideo.soundfile, 0, SEEK_SET);
1123 FS_Write (cls.capturevideo.soundfile, out, 44);
1124 FS_Close (cls.capturevideo.soundfile);
1125 cls.capturevideo.soundfile = NULL;
1128 if (cls.capturevideo.buffer)
1130 Mem_Free (cls.capturevideo.buffer);
1131 cls.capturevideo.buffer = NULL;
1134 memset(&cls.capturevideo, 0, sizeof(cls.capturevideo));
1137 // converts from RGB24 to YV12 colorspace (native colorspace used by MPEG encoders/decoders)
1138 void SCR_CaptureVideo_ConvertFrame_RGB_to_YV12_flip(int width, int height, unsigned char *instart, unsigned char *outstart)
1141 int outoffset = (width/2)*(height/2);
1142 unsigned char *b, *out;
1143 // process one line at a time, and CbCr every other line at 2 pixel intervals
1144 for (y = 0;y < height;y++)
1147 for (b = instart + (height-1-y)*width*3, out = outstart + y*width, x = 0;x < width;x++, b += 3, out++)
1148 *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]]];
1151 // 2x2 Cb and Cr planes
1153 // low quality, no averaging
1154 for (b = instart + (height-2-y)*width*3, out = outstart + width*height + (y/2)*(width/2), x = 0;x < width/2;x++, b += 6, out++)
1157 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];
1159 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];
1162 // high quality, averaging
1163 int inpitch = width*3;
1164 for (b = instart + (height-2-y)*width*3, out = outstart + width*height + (y/2)*(width/2), x = 0;x < width/2;x++, b += 6, out++)
1166 int blockr, blockg, blockb;
1167 blockr = (b[0] + b[3] + b[inpitch+0] + b[inpitch+3]) >> 2;
1168 blockg = (b[1] + b[4] + b[inpitch+1] + b[inpitch+4]) >> 2;
1169 blockb = (b[2] + b[5] + b[inpitch+2] + b[inpitch+5]) >> 2;
1171 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];
1173 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];
1180 // converts from RGB24 to I420 colorspace (identical to YV12 except chroma plane order is reversed)
1181 void SCR_CaptureVideo_ConvertFrame_RGB_to_I420_flip(int width, int height, unsigned char *instart, unsigned char *outstart)
1184 int outoffset = (width/2)*(height/2);
1185 unsigned char *b, *out;
1186 // process one line at a time, and CbCr every other line at 2 pixel intervals
1187 for (y = 0;y < height;y++)
1190 for (b = instart + (height-1-y)*width*3, out = outstart + y*width, x = 0;x < width;x++, b += 3, out++)
1191 *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]]];
1194 // 2x2 Cr and Cb planes
1196 // low quality, no averaging
1197 for (b = instart + (height-2-y)*width*3, out = outstart + width*height + (y/2)*(width/2), x = 0;x < width/2;x++, b += 6, out++)
1200 out[0 ] = 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];
1202 out[outoffset] = 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];
1205 // high quality, averaging
1206 int inpitch = width*3;
1207 for (b = instart + (height-2-y)*width*3, out = outstart + width*height + (y/2)*(width/2), x = 0;x < width/2;x++, b += 6, out++)
1209 int blockr, blockg, blockb;
1210 blockr = (b[0] + b[3] + b[inpitch+0] + b[inpitch+3]) >> 2;
1211 blockg = (b[1] + b[4] + b[inpitch+1] + b[inpitch+4]) >> 2;
1212 blockb = (b[2] + b[5] + b[inpitch+2] + b[inpitch+5]) >> 2;
1214 out[0 ] = cls.capturevideo.yuvnormalizetable[1][cls.capturevideo.rgbtoyuvscaletable[1][0][blockr] + cls.capturevideo.rgbtoyuvscaletable[1][1][blockg] + cls.capturevideo.rgbtoyuvscaletable[1][2][blockb] + 128];
1216 out[outoffset] = cls.capturevideo.yuvnormalizetable[2][cls.capturevideo.rgbtoyuvscaletable[2][0][blockr] + cls.capturevideo.rgbtoyuvscaletable[2][1][blockg] + cls.capturevideo.rgbtoyuvscaletable[2][2][blockb] + 128];
1223 qboolean SCR_CaptureVideo_VideoFrame(int newframenum)
1225 int x = 0, y = 0, width = vid.width, height = vid.height;
1226 unsigned char *in, *out;
1229 //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);
1230 // speed is critical here, so do saving as directly as possible
1231 switch (cls.capturevideo.format)
1233 case CAPTUREVIDEOFORMAT_AVI_I420:
1234 // if there's no videofile we have to just give up, and abort saving if there's no video or sound file
1235 if (!cls.capturevideo.videofile)
1236 return cls.capturevideo.soundfile != NULL;
1237 // FIXME: width/height must be multiple of 2, enforce this?
1238 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cls.capturevideo.buffer);CHECKGLERROR
1239 in = cls.capturevideo.buffer;
1240 out = cls.capturevideo.buffer + width*height*3;
1241 SCR_CaptureVideo_ConvertFrame_RGB_to_I420_flip(width, height, in, out);
1242 x = width*height+(width/2)*(height/2)*2;
1243 SCR_CaptureVideo_RIFF_OverflowCheck(8 + x);
1244 for (;cls.capturevideo.frame < newframenum;cls.capturevideo.frame++)
1246 SCR_CaptureVideo_RIFF_IndexEntry("00dc", x, 0x10); // AVIIF_KEYFRAME
1247 SCR_CaptureVideo_RIFF_Push("00dc", NULL);
1248 SCR_CaptureVideo_RIFF_WriteBytes(out, x);
1249 SCR_CaptureVideo_RIFF_Pop();
1252 case CAPTUREVIDEOFORMAT_RAWYV12:
1253 // if there's no videofile we have to just give up, and abort saving if there's no video or sound file
1254 if (!cls.capturevideo.videofile)
1255 return cls.capturevideo.soundfile != NULL;
1256 // FIXME: width/height must be multiple of 2, enforce this?
1257 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cls.capturevideo.buffer);CHECKGLERROR
1258 in = cls.capturevideo.buffer;
1259 out = cls.capturevideo.buffer + width*height*3;
1260 SCR_CaptureVideo_ConvertFrame_RGB_to_YV12_flip(width, height, in, out);
1261 x = width*height+(width/2)*(height/2)*2;
1262 for (;cls.capturevideo.frame < newframenum;cls.capturevideo.frame++)
1263 if (!FS_Write (cls.capturevideo.videofile, out, x))
1266 case CAPTUREVIDEOFORMAT_RAWRGB:
1267 // if there's no videofile we have to just give up, and abort saving if there's no video or sound file
1268 if (!cls.capturevideo.videofile)
1269 return cls.capturevideo.soundfile != NULL;
1270 // FIXME: this should flip the images... ?
1271 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cls.capturevideo.buffer);CHECKGLERROR
1272 for (;cls.capturevideo.frame < newframenum;cls.capturevideo.frame++)
1273 if (!FS_Write (cls.capturevideo.videofile, cls.capturevideo.buffer, width*height*3))
1276 case CAPTUREVIDEOFORMAT_JPEG:
1277 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cls.capturevideo.buffer);CHECKGLERROR
1278 for (;cls.capturevideo.frame < newframenum;cls.capturevideo.frame++)
1280 sprintf(filename, "%s_%06d.jpg", cls.capturevideo.basename, cls.capturevideo.frame);
1281 if (!JPEG_SaveImage_preflipped (filename, width, height, cls.capturevideo.buffer))
1285 case CAPTUREVIDEOFORMAT_TARGA:
1286 //return Image_WriteTGARGB_preflipped (filename, width, height, cls.capturevideo.buffer, cls.capturevideo.buffer + vid.width * vid.height * 3, );
1287 memset (cls.capturevideo.buffer, 0, 18);
1288 cls.capturevideo.buffer[2] = 2; // uncompressed type
1289 cls.capturevideo.buffer[12] = (width >> 0) & 0xFF;
1290 cls.capturevideo.buffer[13] = (width >> 8) & 0xFF;
1291 cls.capturevideo.buffer[14] = (height >> 0) & 0xFF;
1292 cls.capturevideo.buffer[15] = (height >> 8) & 0xFF;
1293 cls.capturevideo.buffer[16] = 24; // pixel size
1294 qglReadPixels (x, y, width, height, GL_BGR, GL_UNSIGNED_BYTE, cls.capturevideo.buffer + 18);CHECKGLERROR
1295 for (;cls.capturevideo.frame < newframenum;cls.capturevideo.frame++)
1297 sprintf(filename, "%s_%06d.tga", cls.capturevideo.basename, cls.capturevideo.frame);
1298 if (!FS_WriteFile (filename, cls.capturevideo.buffer, width*height*3 + 18))
1307 void SCR_CaptureVideo_SoundFrame(unsigned char *bufstereo16le, size_t length, int rate)
1310 cls.capturevideo.soundrate = rate;
1311 cls.capturevideo.soundsampleframe += length;
1312 switch (cls.capturevideo.format)
1314 case CAPTUREVIDEOFORMAT_AVI_I420:
1316 SCR_CaptureVideo_RIFF_OverflowCheck(8 + x);
1317 SCR_CaptureVideo_RIFF_IndexEntry("01wb", x, 0x10); // AVIIF_KEYFRAME
1318 SCR_CaptureVideo_RIFF_Push("01wb", NULL);
1319 SCR_CaptureVideo_RIFF_WriteBytes(bufstereo16le, x);
1320 SCR_CaptureVideo_RIFF_Pop();
1323 if (cls.capturevideo.soundfile)
1324 if (FS_Write(cls.capturevideo.soundfile, bufstereo16le, 4 * length) < (fs_offset_t)(4 * length))
1325 cls.capturevideo.error = true;
1330 void SCR_CaptureVideo(void)
1333 if (cl_capturevideo.integer && r_render.integer)
1335 if (!cls.capturevideo.active)
1336 SCR_CaptureVideo_BeginVideo();
1337 if (cls.capturevideo.framerate != cl_capturevideo_fps.value)
1339 Con_Printf("You can not change the video framerate while recording a video.\n");
1340 Cvar_SetValueQuick(&cl_capturevideo_fps, cls.capturevideo.framerate);
1342 // for AVI saving we have to make sure that sound is saved before video
1343 if (cls.capturevideo.soundrate && !cls.capturevideo.soundsampleframe)
1346 if (cls.capturevideo.soundfile)
1348 // preserve sound sync by duplicating frames when running slow
1349 newframenum = (int)((Sys_DoubleTime() - cls.capturevideo.starttime) * cls.capturevideo.framerate);
1353 newframenum = cls.capturevideo.frame + 1;
1354 // if falling behind more than one second, stop
1355 if (newframenum - cls.capturevideo.frame > (int)ceil(cls.capturevideo.framerate))
1357 Cvar_SetValueQuick(&cl_capturevideo, 0);
1358 Con_Printf("video saving failed on frame %i, your machine is too slow for this capture speed.\n", cls.capturevideo.frame);
1359 SCR_CaptureVideo_EndVideo();
1363 SCR_CaptureVideo_VideoFrame(newframenum);
1364 if (cls.capturevideo.error)
1366 Cvar_SetValueQuick(&cl_capturevideo, 0);
1367 Con_Printf("video saving failed on frame %i, out of disk space? stopping video capture.\n", cls.capturevideo.frame);
1368 SCR_CaptureVideo_EndVideo();
1371 else if (cls.capturevideo.active)
1372 SCR_CaptureVideo_EndVideo();
1379 Grab six views for environment mapping tests
1386 qboolean flipx, flipy, flipdiagonaly;
1390 {{ 0, 0, 0}, "rt", false, false, false},
1391 {{ 0, 270, 0}, "ft", false, false, false},
1392 {{ 0, 180, 0}, "lf", false, false, false},
1393 {{ 0, 90, 0}, "bk", false, false, false},
1394 {{-90, 180, 0}, "up", true, true, false},
1395 {{ 90, 180, 0}, "dn", true, true, false},
1397 {{ 0, 0, 0}, "px", true, true, true},
1398 {{ 0, 90, 0}, "py", false, true, false},
1399 {{ 0, 180, 0}, "nx", false, false, true},
1400 {{ 0, 270, 0}, "ny", true, false, false},
1401 {{-90, 180, 0}, "pz", false, false, true},
1402 {{ 90, 180, 0}, "nz", false, false, true}
1405 static void R_Envmap_f (void)
1408 char filename[MAX_QPATH], basename[MAX_QPATH];
1409 unsigned char *buffer1;
1410 unsigned char *buffer2;
1411 unsigned char *buffer3;
1413 if (Cmd_Argc() != 3)
1415 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");
1419 strlcpy (basename, Cmd_Argv(1), sizeof (basename));
1420 size = atoi(Cmd_Argv(2));
1421 if (size != 128 && size != 256 && size != 512 && size != 1024)
1423 Con_Print("envmap: size must be one of 128, 256, 512, or 1024\n");
1426 if (size > vid.width || size > vid.height)
1428 Con_Print("envmap: your resolution is not big enough to render that size\n");
1432 r_refdef.envmap = true;
1434 R_UpdateVariables();
1439 r_view.width = size;
1440 r_view.height = size;
1443 r_view.frustum_x = tan(90 * M_PI / 360.0);
1444 r_view.frustum_y = tan(90 * M_PI / 360.0);
1446 buffer1 = (unsigned char *)Mem_Alloc(tempmempool, size * size * 3);
1447 buffer2 = (unsigned char *)Mem_Alloc(tempmempool, size * size * 3);
1448 buffer3 = (unsigned char *)Mem_Alloc(tempmempool, size * size * 3 + 18);
1450 for (j = 0;j < 12;j++)
1452 sprintf(filename, "env/%s%s.tga", basename, envmapinfo[j].name);
1453 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);
1458 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);
1465 r_refdef.envmap = false;
1468 //=============================================================================
1470 // LordHavoc: SHOWLMP stuff
1471 #define SHOWLMP_MAXLABELS 256
1472 typedef struct showlmp_s
1482 showlmp_t showlmp[SHOWLMP_MAXLABELS];
1484 void SHOWLMP_decodehide(void)
1488 lmplabel = MSG_ReadString();
1489 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1490 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
1492 showlmp[i].isactive = false;
1497 void SHOWLMP_decodeshow(void)
1500 char lmplabel[256], picname[256];
1502 strlcpy (lmplabel,MSG_ReadString(), sizeof (lmplabel));
1503 strlcpy (picname, MSG_ReadString(), sizeof (picname));
1504 if (gamemode == GAME_NEHAHRA) // LordHavoc: nasty old legacy junk
1511 x = MSG_ReadShort();
1512 y = MSG_ReadShort();
1515 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1516 if (showlmp[i].isactive)
1518 if (strcmp(showlmp[i].label, lmplabel) == 0)
1521 break; // drop out to replace it
1524 else if (k < 0) // find first empty one to replace
1527 return; // none found to replace
1528 // change existing one
1529 showlmp[k].isactive = true;
1530 strlcpy (showlmp[k].label, lmplabel, sizeof (showlmp[k].label));
1531 strlcpy (showlmp[k].pic, picname, sizeof (showlmp[k].pic));
1536 void SHOWLMP_drawall(void)
1539 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1540 if (showlmp[i].isactive)
1541 DrawQ_Pic(showlmp[i].x, showlmp[i].y, Draw_CachePic(showlmp[i].pic, true), 0, 0, 1, 1, 1, 1, 0);
1544 void SHOWLMP_clear(void)
1547 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1548 showlmp[i].isactive = false;
1552 ==============================================================================
1556 ==============================================================================
1559 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)
1561 int indices[3] = {0,1,2};
1564 if (!r_render.integer)
1568 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, buffer1);CHECKGLERROR
1570 if (scr_screenshot_gammaboost.value != 1 && gammacorrect)
1573 double igamma = 1.0 / scr_screenshot_gammaboost.value;
1574 unsigned char ramp[256];
1575 for (i = 0;i < 256;i++)
1576 ramp[i] = (unsigned char) (pow(i * (1.0 / 255.0), igamma) * 255.0);
1577 for (i = 0;i < width*height*3;i++)
1578 buffer1[i] = ramp[buffer1[i]];
1581 Image_CopyMux (buffer2, buffer1, width, height, flipx, flipy, flipdiagonal, 3, 3, indices);
1584 ret = JPEG_SaveImage_preflipped (filename, width, height, buffer2);
1586 ret = Image_WriteTGARGB_preflipped (filename, width, height, buffer2, buffer3);
1591 //=============================================================================
1593 void R_ClearScreen(void)
1597 if (r_refdef.fogenabled)
1599 qglClearColor(r_refdef.fogcolor[0],r_refdef.fogcolor[1],r_refdef.fogcolor[2],0);CHECKGLERROR
1603 qglClearColor(0,0,0,0);CHECKGLERROR
1605 qglClearDepth(1);CHECKGLERROR
1608 // LordHavoc: we use a stencil centered around 128 instead of 0,
1609 // to avoid clamping interfering with strange shadow volume
1611 qglClearStencil(128);CHECKGLERROR
1614 if (r_render.integer)
1615 GL_Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | (gl_stencil ? GL_STENCIL_BUFFER_BIT : 0));
1616 // set dithering mode
1617 if (gl_dither.integer)
1619 qglEnable(GL_DITHER);CHECKGLERROR
1623 qglDisable(GL_DITHER);CHECKGLERROR
1627 qboolean CL_VM_UpdateView (void);
1628 void SCR_DrawConsole (void);
1629 void R_Shadow_EditLights_DrawSelectedLightProperties(void);
1633 void SCR_DrawScreen (void)
1637 if (r_timereport_active)
1638 R_TimeReport("setup");
1640 R_UpdateVariables();
1642 if (cls.signon == SIGNONS)
1646 size = scr_viewsize.value * (1.0 / 100.0);
1647 size = min(size, 1);
1649 if (r_stereo_sidebyside.integer)
1651 r_view.width = (int)(vid.width * size / 2.5);
1652 r_view.height = (int)(vid.height * size / 2.5 * (1 - bound(0, r_letterbox.value, 100) / 100));
1654 r_view.x = (int)((vid.width - r_view.width * 2.5) * 0.5);
1655 r_view.y = (int)((vid.height - r_view.height)/2);
1658 r_view.x += (int)(r_view.width * 1.5);
1662 r_view.width = (int)(vid.width * size);
1663 r_view.height = (int)(vid.height * size * (1 - bound(0, r_letterbox.value, 100) / 100));
1665 r_view.x = (int)((vid.width - r_view.width)/2);
1666 r_view.y = (int)((vid.height - r_view.height)/2);
1670 // LordHavoc: viewzoom (zoom in for sniper rifles, etc)
1671 // LordHavoc: this is designed to produce widescreen fov values
1672 // when the screen is wider than 4/3 width/height aspect, to do
1673 // this it simply assumes the requested fov is the vertical fov
1674 // for a 4x3 display, if the ratio is not 4x3 this makes the fov
1675 // higher/lower according to the ratio
1676 r_view.frustum_y = tan(scr_fov.value * cl.viewzoom * M_PI / 360.0) * (3.0/4.0);
1677 r_view.frustum_x = r_view.frustum_y * (float)r_view.width / (float)r_view.height / vid_pixelheight.value;
1679 r_view.frustum_x *= r_refdef.frustumscale_x;
1680 r_view.frustum_y *= r_refdef.frustumscale_y;
1682 if(!CL_VM_UpdateView())
1687 if (scr_zoomwindow.integer)
1689 float sizex = bound(10, scr_zoomwindow_viewsizex.value, 100) / 100.0;
1690 float sizey = bound(10, scr_zoomwindow_viewsizey.value, 100) / 100.0;
1691 r_view.width = (int)(vid.width * sizex);
1692 r_view.height = (int)(vid.height * sizey);
1694 r_view.x = (int)((vid.width - r_view.width)/2);
1698 r_view.frustum_y = tan(scr_zoomwindow_fov.value * cl.viewzoom * M_PI / 360.0) * (3.0/4.0);
1699 r_view.frustum_x = r_view.frustum_y * vid_pixelheight.value * (float)r_view.width / (float)r_view.height;
1701 r_view.frustum_x *= r_refdef.frustumscale_x;
1702 r_view.frustum_y *= r_refdef.frustumscale_y;
1704 if(!CL_VM_UpdateView())
1709 if (!r_stereo_sidebyside.integer)
1711 r_view.width = vid.width;
1712 r_view.height = vid.height;
1721 //FIXME: force menu if nothing else to look at?
1722 //if (key_dest == key_game && cls.signon != SIGNONS && cls.state == ca_disconnected)
1724 if (cls.signon == SIGNONS)
1729 if (!r_letterbox.value)
1732 SCR_CheckDrawCenterString();
1736 R_Shadow_EditLights_DrawSelectedLightProperties();
1745 if (r_timereport_active)
1748 if (cls.signon == SIGNONS)
1749 R_TimeReport_Frame();
1757 if (r_timereport_active)
1758 R_TimeReport("meshfinish");
1761 void SCR_UpdateLoadingScreen (void)
1766 float texcoord2f[8];
1767 // don't do anything if not initialized yet
1771 qglViewport(0, 0, vid.width, vid.height);CHECKGLERROR
1772 //qglDisable(GL_SCISSOR_TEST);CHECKGLERROR
1773 //qglDepthMask(1);CHECKGLERROR
1774 qglColorMask(1,1,1,1);CHECKGLERROR
1775 //qglClearColor(0,0,0,0);CHECKGLERROR
1776 //qglClear(GL_COLOR_BUFFER_BIT);CHECKGLERROR
1777 //qglCullFace(GL_FRONT);CHECKGLERROR
1778 //qglDisable(GL_CULL_FACE);CHECKGLERROR
1781 GL_SetupView_Mode_Ortho(0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100);
1783 R_Mesh_Matrix(&identitymatrix);
1784 // draw the loading plaque
1785 pic = Draw_CachePic("gfx/loading", true);
1786 x = (vid_conwidth.integer - pic->width)/2;
1787 y = (vid_conheight.integer - pic->height)/2;
1789 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1790 GL_DepthTest(false);
1791 R_Mesh_VertexPointer(vertex3f);
1792 R_Mesh_ColorPointer(NULL);
1793 R_Mesh_ResetTextureState();
1794 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
1795 R_Mesh_TexCoordPointer(0, 2, texcoord2f);
1796 vertex3f[2] = vertex3f[5] = vertex3f[8] = vertex3f[11] = 0;
1797 vertex3f[0] = vertex3f[9] = x;
1798 vertex3f[1] = vertex3f[4] = y;
1799 vertex3f[3] = vertex3f[6] = x + pic->width;
1800 vertex3f[7] = vertex3f[10] = y + pic->height;
1801 texcoord2f[0] = 0;texcoord2f[1] = 0;
1802 texcoord2f[2] = 1;texcoord2f[3] = 0;
1803 texcoord2f[4] = 1;texcoord2f[5] = 1;
1804 texcoord2f[6] = 0;texcoord2f[7] = 1;
1805 R_Mesh_Draw(0, 4, 2, polygonelements);
1811 void CL_UpdateScreen(void)
1813 float conwidth, conheight;
1818 if (!scr_initialized || !con_initialized || vid_hidden)
1819 return; // not initialized yet
1821 // don't allow cheats in multiplayer
1822 if (!cl.islocalgame && cl.worldmodel)
1824 if (r_fullbright.integer != 0)
1825 Cvar_Set ("r_fullbright", "0");
1826 if (r_ambient.value != 0)
1827 Cvar_Set ("r_ambient", "0");
1830 conwidth = bound(320, vid_conwidth.value, 2048);
1831 conheight = bound(200, vid_conheight.value, 1536);
1832 if (vid_conwidth.value != conwidth)
1833 Cvar_SetValue("vid_conwidth", conwidth);
1834 if (vid_conheight.value != conheight)
1835 Cvar_SetValue("vid_conheight", conheight);
1838 if (scr_viewsize.value < 30)
1839 Cvar_Set ("viewsize","30");
1840 if (scr_viewsize.value > 120)
1841 Cvar_Set ("viewsize","120");
1843 // bound field of view
1844 if (scr_fov.value < 1)
1845 Cvar_Set ("fov","1");
1846 if (scr_fov.value > 170)
1847 Cvar_Set ("fov","170");
1849 // validate r_textureunits cvar
1850 if (r_textureunits.integer > gl_textureunits)
1851 Cvar_SetValueQuick(&r_textureunits, gl_textureunits);
1852 if (r_textureunits.integer < 1)
1853 Cvar_SetValueQuick(&r_textureunits, 1);
1855 // validate gl_combine cvar
1856 if (gl_combine.integer && !gl_combine_extension)
1857 Cvar_SetValueQuick(&gl_combine, 0);
1859 // intermission is always full screen
1860 if (cl.intermission)
1864 if (scr_viewsize.value >= 120)
1865 sb_lines = 0; // no status bar at all
1866 else if (scr_viewsize.value >= 110)
1867 sb_lines = 24; // no inventory
1872 r_view.colormask[0] = 1;
1873 r_view.colormask[1] = 1;
1874 r_view.colormask[2] = 1;
1876 if (r_timereport_active)
1877 R_TimeReport("other");
1879 SCR_SetUpToDrawConsole();
1881 if (r_timereport_active)
1882 R_TimeReport("start");
1885 qglViewport(0, 0, vid.width, vid.height);CHECKGLERROR
1886 qglDisable(GL_SCISSOR_TEST);CHECKGLERROR
1887 qglDepthMask(1);CHECKGLERROR
1888 qglColorMask(1,1,1,1);CHECKGLERROR
1889 qglClearColor(0,0,0,0);CHECKGLERROR
1890 qglClear(GL_COLOR_BUFFER_BIT);CHECKGLERROR
1892 if (r_timereport_active)
1893 R_TimeReport("clear");
1895 if (r_stereo_redblue.integer || r_stereo_redgreen.integer || r_stereo_redcyan.integer || r_stereo_sidebyside.integer)
1897 matrix4x4_t originalmatrix = r_view.matrix;
1898 r_view.matrix.m[0][3] = originalmatrix.m[0][3] + r_stereo_separation.value * -0.5f * r_view.matrix.m[0][1];
1899 r_view.matrix.m[1][3] = originalmatrix.m[1][3] + r_stereo_separation.value * -0.5f * r_view.matrix.m[1][1];
1900 r_view.matrix.m[2][3] = originalmatrix.m[2][3] + r_stereo_separation.value * -0.5f * r_view.matrix.m[2][1];
1902 if (r_stereo_sidebyside.integer)
1905 if (r_stereo_redblue.integer || r_stereo_redgreen.integer || r_stereo_redcyan.integer)
1907 r_view.colormask[0] = 1;
1908 r_view.colormask[1] = 0;
1909 r_view.colormask[2] = 0;
1914 r_view.matrix.m[0][3] = originalmatrix.m[0][3] + r_stereo_separation.value * 0.5f * r_view.matrix.m[0][1];
1915 r_view.matrix.m[1][3] = originalmatrix.m[1][3] + r_stereo_separation.value * 0.5f * r_view.matrix.m[1][1];
1916 r_view.matrix.m[2][3] = originalmatrix.m[2][3] + r_stereo_separation.value * 0.5f * r_view.matrix.m[2][1];
1918 if (r_stereo_sidebyside.integer)
1921 if (r_stereo_redblue.integer || r_stereo_redgreen.integer || r_stereo_redcyan.integer)
1923 r_view.colormask[0] = 0;
1924 r_view.colormask[1] = r_stereo_redcyan.integer || r_stereo_redgreen.integer;
1925 r_view.colormask[2] = r_stereo_redcyan.integer || r_stereo_redblue.integer;
1930 r_view.matrix = originalmatrix;
1938 if (r_timereport_active)
1939 R_TimeReport("finish");
1942 void CL_Screen_NewMap(void)