5 #include "cl_collision.h"
7 cvar_t scr_viewsize = {CVAR_SAVE, "viewsize","100"};
8 cvar_t scr_fov = {CVAR_SAVE, "fov","90"}; // 1 - 170
9 cvar_t scr_conspeed = {CVAR_SAVE, "scr_conspeed","900"}; // LordHavoc: quake used 300
10 cvar_t scr_conalpha = {CVAR_SAVE, "scr_conalpha", "1"};
11 cvar_t scr_conbrightness = {CVAR_SAVE, "scr_conbrightness", "0.2"};
12 cvar_t scr_conforcewhiledisconnected = {CVAR_SAVE, "scr_conforcewhiledisconnected", "1"};
13 cvar_t scr_centertime = {0, "scr_centertime","2"};
14 cvar_t scr_showram = {CVAR_SAVE, "showram","1"};
15 cvar_t scr_showturtle = {CVAR_SAVE, "showturtle","0"};
16 cvar_t scr_showpause = {CVAR_SAVE, "showpause","1"};
17 cvar_t scr_printspeed = {0, "scr_printspeed","8"};
18 cvar_t vid_conwidth = {CVAR_SAVE, "vid_conwidth", "640"};
19 cvar_t vid_conheight = {CVAR_SAVE, "vid_conheight", "480"};
20 cvar_t vid_pixelaspect = {CVAR_SAVE, "vid_pixelaspect", "1"};
21 cvar_t scr_screenshot_jpeg = {CVAR_SAVE, "scr_screenshot_jpeg","0"};
22 cvar_t scr_screenshot_jpeg_quality = {CVAR_SAVE, "scr_screenshot_jpeg_quality","0.9"};
23 cvar_t scr_screenshot_gamma = {CVAR_SAVE, "scr_screenshot_gamma","2.2"};
24 cvar_t scr_screenshot_name = {0, "scr_screenshot_name","dp"};
25 cvar_t cl_capturevideo = {0, "cl_capturevideo", "0"};
26 cvar_t cl_capturevideo_sound = {0, "cl_capturevideo_sound", "0"};
27 cvar_t cl_capturevideo_fps = {0, "cl_capturevideo_fps", "30"};
28 cvar_t cl_capturevideo_rawrgb = {0, "cl_capturevideo_rawrgb", "0"};
29 cvar_t cl_capturevideo_rawyv12 = {0, "cl_capturevideo_rawyv12", "0"};
30 cvar_t r_textshadow = {0, "r_textshadow", "0"};
31 cvar_t r_letterbox = {0, "r_letterbox", "0"};
33 int jpeg_supported = false;
35 qboolean scr_initialized; // ready to draw
37 float scr_con_current;
39 extern int con_vislines;
41 void DrawCrosshair(int num);
42 static void SCR_ScreenShot_f (void);
43 static void R_Envmap_f (void);
46 void R_ClearScreen(void);
49 ===============================================================================
53 ===============================================================================
56 char scr_centerstring[1024];
57 float scr_centertime_start; // for slow victory printing
58 float scr_centertime_off;
67 Called for important messages that should stay in the center of the screen
71 void SCR_CenterPrint(char *str)
73 strlcpy (scr_centerstring, str, sizeof (scr_centerstring));
74 scr_centertime_off = scr_centertime.value;
75 scr_centertime_start = cl.time;
77 // count the number of lines for centering
88 void SCR_DrawCenterString (void)
95 // the finale prints the characters one at a time
97 remaining = scr_printspeed.value * (cl.time - scr_centertime_start);
101 scr_erase_center = 0;
102 start = scr_centerstring;
104 if (scr_center_lines <= 4)
105 y = vid.conheight*0.35;
111 // scan the width of the line
112 for (l=0 ; l<vid.conwidth/8 ; l++)
113 if (start[l] == '\n' || !start[l])
115 x = (vid.conwidth - l*8)/2;
120 DrawQ_String(x, y, start, l, 8, 8, 1, 1, 1, 1, 0);
128 while (*start && *start != '\n')
133 start++; // skip the \n
137 void SCR_CheckDrawCenterString (void)
139 if (scr_center_lines > scr_erase_lines)
140 scr_erase_lines = scr_center_lines;
142 scr_centertime_off -= host_frametime;
144 // don't draw if this is a normal stats-screen intermission,
145 // only if it is not an intermission, or a finale intermission
146 if (cl.intermission == 1)
148 if (scr_centertime_off <= 0 && !cl.intermission)
150 if (key_dest != key_game)
153 SCR_DrawCenterString ();
161 void SCR_DrawTurtle (void)
165 if (cls.state != ca_connected)
168 if (!scr_showturtle.integer)
171 if (host_frametime < 0.1)
181 DrawQ_Pic (0, 0, "gfx/turtle.lmp", 0, 0, 1, 1, 1, 1, 0);
189 void SCR_DrawNet (void)
191 if (cls.state != ca_connected)
193 if (realtime - cl.last_received_message < 0.3)
195 if (cls.demoplayback)
198 DrawQ_Pic (64, 0, "gfx/net.lmp", 0, 0, 1, 1, 1, 1, 0);
206 void SCR_DrawPause (void)
210 if (cls.state != ca_connected)
213 if (!scr_showpause.integer) // turn off for screenshots
219 pic = Draw_CachePic ("gfx/pause.lmp", true);
220 DrawQ_Pic ((vid.conwidth - pic->width)/2, (vid.conheight - pic->height)/2, "gfx/pause.lmp", 0, 0, 1, 1, 1, 1, 0);
227 //=============================================================================
232 SCR_SetUpToDrawConsole
235 void SCR_SetUpToDrawConsole (void)
237 // lines of console to display
242 if (key_dest == key_game && cls.signon != SIGNONS && scr_conforcewhiledisconnected.integer)
243 key_consoleactive |= KEY_CONSOLEACTIVE_FORCED;
245 key_consoleactive &= ~KEY_CONSOLEACTIVE_FORCED;
247 // decide on the height of the console
248 if (key_consoleactive & KEY_CONSOLEACTIVE_USER)
249 conlines = vid.conheight/2; // half screen
251 conlines = 0; // none visible
253 if (scr_conspeed.value)
255 if (scr_con_current > conlines)
257 scr_con_current -= scr_conspeed.value*host_realframetime;
258 if (scr_con_current < conlines)
259 scr_con_current = conlines;
262 else if (scr_con_current < conlines)
264 scr_con_current += scr_conspeed.value*host_realframetime;
265 if (scr_con_current > conlines)
266 scr_con_current = conlines;
270 scr_con_current = conlines;
278 void SCR_DrawConsole (void)
280 if (key_consoleactive & KEY_CONSOLEACTIVE_FORCED)
283 Con_DrawConsole (vid.conheight);
285 else if (scr_con_current)
286 Con_DrawConsole (scr_con_current);
290 if (key_dest == key_game || key_dest == key_message)
291 Con_DrawNotify (); // only draw notify in game
297 SCR_BeginLoadingPlaque
301 void SCR_BeginLoadingPlaque (void)
304 SCR_UpdateLoadingScreen();
307 //=============================================================================
309 char r_speeds_string[1024];
310 int speedstringcount, r_timereport_active;
311 double r_timereport_temp = 0, r_timereport_current = 0, r_timereport_start = 0;
313 void R_TimeReport(char *desc)
319 if (!r_timereport_active)
322 r_timereport_temp = r_timereport_current;
323 r_timereport_current = Sys_DoubleTime();
324 t = (int) ((r_timereport_current - r_timereport_temp) * 1000000.0);
326 sprintf(tempbuf, "%8i %s", t, desc);
327 length = strlen(tempbuf);
329 tempbuf[length++] = ' ';
331 if (speedstringcount + length > (vid.conwidth / 8))
333 strcat(r_speeds_string, "\n");
334 speedstringcount = 0;
336 // skip the space at the beginning if it's the first on the line
337 if (speedstringcount == 0)
339 strcat(r_speeds_string, tempbuf + 1);
340 speedstringcount = length - 1;
344 strcat(r_speeds_string, tempbuf);
345 speedstringcount += length;
349 void R_TimeReport_Start(void)
351 r_timereport_active = r_speeds.integer && cls.signon == SIGNONS && cls.state == ca_connected;
352 r_speeds_string[0] = 0;
353 if (r_timereport_active)
355 speedstringcount = 0;
356 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]);
357 sprintf(r_speeds_string + strlen(r_speeds_string), "world:%6i faces%6i nodes%6i leafs%6i dlitwalls\n", c_faces, c_nodes, c_leafs, c_light_polys);
358 sprintf(r_speeds_string + strlen(r_speeds_string), "%5i models%5i bmodels%5i sprites%6i particles%4i dlights\n", c_models, c_bmodels, c_sprites, c_particles, c_dlights);
359 sprintf(r_speeds_string + strlen(r_speeds_string), "%6i modeltris%6i meshs%6i meshtris\n", c_alias_polys, c_meshs, c_meshelements / 3);
360 sprintf(r_speeds_string + strlen(r_speeds_string), "bloom %s: %i copies (%i pixels) %i draws (%i pixels)\n", c_bloom ? "active" : "inactive", c_bloomcopies, c_bloomcopypixels, c_bloomdraws, c_bloomdrawpixels);
361 sprintf(r_speeds_string + strlen(r_speeds_string), "realtime lighting:%4i lights%4i clears%4i scissored\n", c_rt_lights, c_rt_clears, c_rt_scissored);
362 sprintf(r_speeds_string + strlen(r_speeds_string), "dynamic: %6i shadowmeshes%6i shadowtris%6i lightmeshes%6i lighttris\n", c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris);
363 sprintf(r_speeds_string + strlen(r_speeds_string), "precomputed: %6i shadowmeshes%6i shadowtris\n", c_rtcached_shadowmeshes, c_rtcached_shadowtris);
380 c_rt_shadowmeshes = 0;
382 c_rt_lightmeshes = 0;
384 c_rtcached_shadowmeshes = 0;
385 c_rtcached_shadowtris = 0;
388 c_bloomcopypixels = 0;
390 c_bloomdrawpixels = 0;
392 r_timereport_start = Sys_DoubleTime();
396 void R_TimeReport_End(void)
398 r_timereport_current = r_timereport_start;
399 R_TimeReport("total");
401 if (r_timereport_active)
405 for (i = 0;r_speeds_string[i];i++)
406 if (r_speeds_string[i] == '\n')
408 y = vid.conheight - sb_lines - lines * 8;
410 DrawQ_Fill(0, y, vid.conwidth, lines * 8, 0, 0, 0, 0.5, 0);
411 while (r_speeds_string[i])
414 while (r_speeds_string[i] && r_speeds_string[i] != '\n')
417 DrawQ_String(0, y, r_speeds_string + j, i - j, 8, 8, 1, 1, 1, 1, 0);
418 if (r_speeds_string[i] == '\n')
432 void SCR_SizeUp_f (void)
434 Cvar_SetValue ("viewsize",scr_viewsize.value+10);
445 void SCR_SizeDown_f (void)
447 Cvar_SetValue ("viewsize",scr_viewsize.value-10);
450 void CL_Screen_Init(void)
452 Cvar_RegisterVariable (&scr_fov);
453 Cvar_RegisterVariable (&scr_viewsize);
454 Cvar_RegisterVariable (&scr_conspeed);
455 Cvar_RegisterVariable (&scr_conalpha);
456 Cvar_RegisterVariable (&scr_conbrightness);
457 Cvar_RegisterVariable (&scr_conforcewhiledisconnected);
458 Cvar_RegisterVariable (&scr_showram);
459 Cvar_RegisterVariable (&scr_showturtle);
460 Cvar_RegisterVariable (&scr_showpause);
461 Cvar_RegisterVariable (&scr_centertime);
462 Cvar_RegisterVariable (&scr_printspeed);
463 Cvar_RegisterVariable (&vid_conwidth);
464 Cvar_RegisterVariable (&vid_conheight);
465 Cvar_RegisterVariable (&vid_pixelaspect);
466 Cvar_RegisterVariable (&scr_screenshot_jpeg);
467 Cvar_RegisterVariable (&scr_screenshot_jpeg_quality);
468 Cvar_RegisterVariable (&scr_screenshot_gamma);
469 Cvar_RegisterVariable (&cl_capturevideo);
470 Cvar_RegisterVariable (&cl_capturevideo_sound);
471 Cvar_RegisterVariable (&cl_capturevideo_fps);
472 Cvar_RegisterVariable (&cl_capturevideo_rawrgb);
473 Cvar_RegisterVariable (&cl_capturevideo_rawyv12);
474 Cvar_RegisterVariable (&r_textshadow);
475 Cvar_RegisterVariable (&r_letterbox);
477 Cmd_AddCommand ("sizeup",SCR_SizeUp_f);
478 Cmd_AddCommand ("sizedown",SCR_SizeDown_f);
479 Cmd_AddCommand ("screenshot",SCR_ScreenShot_f);
480 Cmd_AddCommand ("envmap", R_Envmap_f);
482 scr_initialized = true;
485 void DrawQ_Clear(void)
487 r_refdef.drawqueuesize = 0;
490 static int picelements[6] = {0, 1, 2, 0, 2, 3};
491 void DrawQ_Pic(float x, float y, char *picname, float width, float height, float red, float green, float blue, float alpha, int flags)
493 DrawQ_SuperPic(x,y,picname,width,height,0,0,red,green,blue,alpha,1,0,red,green,blue,alpha,0,1,red,green,blue,alpha,1,1,red,green,blue,alpha,flags);
496 void DrawQ_String_Real(float x, float y, const char *string, int maxlen, float scalex, float scaley, float red, float green, float blue, float alpha, int flags)
501 if (alpha < (1.0f / 255.0f))
504 len = strlen(string);
506 for (len = 0;len < maxlen && string[len];len++);
507 for (;len > 0 && string[0] == ' ';string++, x += scalex, len--);
508 for (;len > 0 && string[len - 1] == ' ';len--);
511 if (x >= vid.conwidth || y >= vid.conheight || x < (-scalex * len) || y < (-scaley))
513 size = sizeof(*dq) + ((len + 1 + 3) & ~3);
514 if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
516 red = bound(0, red, 1);
517 green = bound(0, green, 1);
518 blue = bound(0, blue, 1);
519 alpha = bound(0, alpha, 1);
520 dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
522 dq->command = DRAWQUEUE_STRING;
524 dq->color = ((unsigned int) (red * 255.0f) << 24) | ((unsigned int) (green * 255.0f) << 16) | ((unsigned int) (blue * 255.0f) << 8) | ((unsigned int) (alpha * 255.0f));
529 out = (char *)(dq + 1);
530 memcpy(out, string, len);
532 r_refdef.drawqueuesize += dq->size;
535 void DrawQ_String(float x, float y, const char *string, int maxlen, float scalex, float scaley, float red, float green, float blue, float alpha, int flags)
537 if (r_textshadow.integer)
538 DrawQ_String_Real(x+scalex*0.25,y+scaley*0.25,string,maxlen,scalex,scaley,0,0,0,alpha*0.8,flags);
540 DrawQ_String_Real(x,y,string,maxlen,scalex,scaley,red,green,blue,alpha,flags);
543 void DrawQ_Fill (float x, float y, float w, float h, float red, float green, float blue, float alpha, int flags)
545 DrawQ_SuperPic(x,y,NULL,w,h,0,0,red,green,blue,alpha,1,0,red,green,blue,alpha,0,1,red,green,blue,alpha,1,1,red,green,blue,alpha,flags);
548 void DrawQ_SuperPic(float x, float y, char *picname, float width, float height, float s1, float t1, float r1, float g1, float b1, float a1, float s2, float t2, float r2, float g2, float b2, float a2, float s3, float t3, float r3, float g3, float b3, float a3, float s4, float t4, float r4, float g4, float b4, float a4, int flags)
552 drawqueuemesh_t mesh;
553 memset(&mesh, 0, sizeof(mesh));
554 if (picname && picname[0])
556 pic = Draw_CachePic(picname, false);
560 height = pic->height;
561 mesh.texture = pic->tex;
563 mesh.num_triangles = 2;
564 mesh.num_vertices = 4;
565 mesh.data_element3i = picelements;
566 mesh.data_vertex3f = floats;
567 mesh.data_texcoord2f = floats + 12;
568 mesh.data_color4f = floats + 20;
569 memset(floats, 0, sizeof(floats));
570 mesh.data_vertex3f[0] = mesh.data_vertex3f[9] = x;
571 mesh.data_vertex3f[1] = mesh.data_vertex3f[4] = y;
572 mesh.data_vertex3f[3] = mesh.data_vertex3f[6] = x + width;
573 mesh.data_vertex3f[7] = mesh.data_vertex3f[10] = y + height;
574 mesh.data_texcoord2f[0] = s1;mesh.data_texcoord2f[1] = t1;mesh.data_color4f[ 0] = r1;mesh.data_color4f[ 1] = g1;mesh.data_color4f[ 2] = b1;mesh.data_color4f[ 3] = a1;
575 mesh.data_texcoord2f[2] = s2;mesh.data_texcoord2f[3] = t2;mesh.data_color4f[ 4] = r2;mesh.data_color4f[ 5] = g2;mesh.data_color4f[ 6] = b2;mesh.data_color4f[ 7] = a2;
576 mesh.data_texcoord2f[4] = s4;mesh.data_texcoord2f[5] = t4;mesh.data_color4f[ 8] = r4;mesh.data_color4f[ 9] = g4;mesh.data_color4f[10] = b4;mesh.data_color4f[11] = a4;
577 mesh.data_texcoord2f[6] = s3;mesh.data_texcoord2f[7] = t3;mesh.data_color4f[12] = r3;mesh.data_color4f[13] = g3;mesh.data_color4f[14] = b3;mesh.data_color4f[15] = a3;
578 DrawQ_Mesh (&mesh, flags);
581 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
588 size += sizeof(drawqueuemesh_t);
589 size += sizeof(int[3]) * mesh->num_triangles;
590 size += sizeof(float[3]) * mesh->num_vertices;
591 size += sizeof(float[2]) * mesh->num_vertices;
592 size += sizeof(float[4]) * mesh->num_vertices;
593 if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
595 dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
597 dq->command = DRAWQUEUE_MESH;
604 p = (void *)(dq + 1);
605 m = p;p = (qbyte*)p + sizeof(drawqueuemesh_t);
606 m->num_triangles = mesh->num_triangles;
607 m->num_vertices = mesh->num_vertices;
608 m->texture = mesh->texture;
609 m->data_element3i = p;memcpy(m->data_element3i , mesh->data_element3i , m->num_triangles * sizeof(int[3]));p = (qbyte*)p + m->num_triangles * sizeof(int[3]);
610 m->data_vertex3f = p;memcpy(m->data_vertex3f , mesh->data_vertex3f , m->num_vertices * sizeof(float[3]));p = (qbyte*)p + m->num_vertices * sizeof(float[3]);
611 m->data_texcoord2f = p;memcpy(m->data_texcoord2f, mesh->data_texcoord2f, m->num_vertices * sizeof(float[2]));p = (qbyte*)p + m->num_vertices * sizeof(float[2]);
612 m->data_color4f = p;memcpy(m->data_color4f , mesh->data_color4f , m->num_vertices * sizeof(float[4]));p = (qbyte*)p + m->num_vertices * sizeof(float[4]);
613 r_refdef.drawqueuesize += dq->size;
616 void DrawQ_SetClipArea(float x, float y, float width, float height)
619 if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
621 Con_DPrint("DrawQueue full !\n");
624 dq = (void*) (r_refdef.drawqueue + r_refdef.drawqueuesize);
625 dq->size = sizeof(*dq);
626 dq->command = DRAWQUEUE_SETCLIP;
634 r_refdef.drawqueuesize += dq->size;
637 void DrawQ_ResetClipArea(void)
640 if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
642 Con_DPrint("DrawQueue full !\n");
645 dq = (void*) (r_refdef.drawqueue + r_refdef.drawqueuesize);
646 dq->size = sizeof(*dq);
647 dq->command = DRAWQUEUE_RESETCLIP;
655 r_refdef.drawqueuesize += dq->size;
663 void SCR_ScreenShot_f (void)
665 static int shotnumber;
666 static char oldname[MAX_QPATH];
667 char base[MAX_QPATH];
668 char filename[MAX_QPATH];
672 qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
674 sprintf (base, "screenshots/%s", scr_screenshot_name.string);
676 if (strcmp (oldname, scr_screenshot_name.string))
678 sprintf(oldname, "%s", scr_screenshot_name.string);
682 // find a file name to save it to
683 for (;shotnumber < 1000000;shotnumber++)
684 if (!FS_SysFileExists(va("%s/%s%06d.tga", fs_gamedir, base, shotnumber)) && !FS_SysFileExists(va("%s/%s%06d.jpg", fs_gamedir, base, shotnumber)))
686 if (shotnumber >= 1000000)
688 Con_Print("SCR_ScreenShot_f: Couldn't create the image file\n");
692 sprintf(filename, "%s%06d.%s", base, shotnumber, jpeg ? "jpg" : "tga");
694 buffer1 = Mem_Alloc(tempmempool, vid.realwidth * vid.realheight * 3);
695 buffer2 = Mem_Alloc(tempmempool, vid.realwidth * vid.realheight * 3);
696 buffer3 = Mem_Alloc(tempmempool, vid.realwidth * vid.realheight * 3 + 18);
698 if (SCR_ScreenShot (filename, buffer1, buffer2, buffer3, vid.realx, vid.realy, vid.realwidth, vid.realheight, false, false, false, jpeg))
699 Con_Printf("Wrote %s\n", filename);
701 Con_Printf("unable to write %s\n", filename);
710 typedef enum capturevideoformat_e
712 CAPTUREVIDEOFORMAT_TARGA,
713 CAPTUREVIDEOFORMAT_JPEG,
714 CAPTUREVIDEOFORMAT_RAWRGB,
715 CAPTUREVIDEOFORMAT_RAWYV12
717 capturevideoformat_t;
719 qboolean cl_capturevideo_active = false;
720 capturevideoformat_t cl_capturevideo_format;
721 static double cl_capturevideo_starttime = 0;
722 double cl_capturevideo_framerate = 0;
723 static int cl_capturevideo_soundrate = 0;
724 static int cl_capturevideo_frame = 0;
725 static qbyte *cl_capturevideo_buffer = NULL;
726 static qfile_t *cl_capturevideo_videofile = NULL;
727 qfile_t *cl_capturevideo_soundfile = NULL;
728 static short cl_capturevideo_rgbtoyuvscaletable[3][3][256];
729 static unsigned char cl_capturevideo_yuvnormalizetable[3][256];
730 //static unsigned char cl_capturevideo_rgbgammatable[3][256];
732 void SCR_CaptureVideo_BeginVideo(void)
737 if (cl_capturevideo_active)
739 // soundrate is figured out on the first SoundFrame
740 cl_capturevideo_active = true;
741 cl_capturevideo_starttime = Sys_DoubleTime();
742 cl_capturevideo_framerate = bound(1, cl_capturevideo_fps.value, 1000);
743 cl_capturevideo_soundrate = 0;
744 cl_capturevideo_frame = 0;
745 cl_capturevideo_buffer = Mem_Alloc(tempmempool, vid.realwidth * vid.realheight * (3+3+3) + 18);
746 gamma = 1.0/scr_screenshot_gamma.value;
749 for (i = 0;i < 256;i++)
751 unsigned char j = (unsigned char)bound(0, 255*pow(i/255.0, gamma), 255);
752 cl_capturevideo_rgbgammatable[0][i] = j;
753 cl_capturevideo_rgbgammatable[1][i] = j;
754 cl_capturevideo_rgbgammatable[2][i] = j;
758 R = Y + 1.4075 * (Cr - 128);
759 G = Y + -0.3455 * (Cb - 128) + -0.7169 * (Cr - 128);
760 B = Y + 1.7790 * (Cb - 128);
761 Y = R * .299 + G * .587 + B * .114;
762 Cb = R * -.169 + G * -.332 + B * .500 + 128.;
763 Cr = R * .500 + G * -.419 + B * -.0813 + 128.;
765 for (i = 0;i < 256;i++)
767 g = 255*pow(i/255.0, gamma);
768 // Y weights from RGB
769 cl_capturevideo_rgbtoyuvscaletable[0][0][i] = (short)(g * 0.299);
770 cl_capturevideo_rgbtoyuvscaletable[0][1][i] = (short)(g * 0.587);
771 cl_capturevideo_rgbtoyuvscaletable[0][2][i] = (short)(g * 0.114);
772 // Cb weights from RGB
773 cl_capturevideo_rgbtoyuvscaletable[1][0][i] = (short)(g * -0.169);
774 cl_capturevideo_rgbtoyuvscaletable[1][1][i] = (short)(g * -0.332);
775 cl_capturevideo_rgbtoyuvscaletable[1][2][i] = (short)(g * 0.500);
776 // Cr weights from RGB
777 cl_capturevideo_rgbtoyuvscaletable[2][0][i] = (short)(g * 0.500);
778 cl_capturevideo_rgbtoyuvscaletable[2][1][i] = (short)(g * -0.419);
779 cl_capturevideo_rgbtoyuvscaletable[2][2][i] = (short)(g * -0.0813);
780 // range reduction of YCbCr to valid signal range
781 cl_capturevideo_yuvnormalizetable[0][i] = 16 + i * (236-16) / 256;
782 cl_capturevideo_yuvnormalizetable[1][i] = 16 + i * (240-16) / 256;
783 cl_capturevideo_yuvnormalizetable[2][i] = 16 + i * (240-16) / 256;
786 if (cl_capturevideo_rawrgb.integer)
788 cl_capturevideo_format = CAPTUREVIDEOFORMAT_RAWRGB;
789 cl_capturevideo_videofile = FS_Open ("video/dpvideo.rgb", "wb", false, true);
791 else if (cl_capturevideo_rawyv12.integer)
793 cl_capturevideo_format = CAPTUREVIDEOFORMAT_RAWYV12;
794 cl_capturevideo_videofile = FS_Open ("video/dpvideo.yv12", "wb", false, true);
796 else if (scr_screenshot_jpeg.integer)
798 cl_capturevideo_format = CAPTUREVIDEOFORMAT_JPEG;
799 cl_capturevideo_videofile = NULL;
803 cl_capturevideo_format = CAPTUREVIDEOFORMAT_TARGA;
804 cl_capturevideo_videofile = NULL;
807 if (cl_capturevideo_sound.integer)
809 cl_capturevideo_soundfile = FS_Open ("video/dpvideo.wav", "wb", false, true);
810 // wave header will be filled out when video ends
812 FS_Write (cl_capturevideo_soundfile, out, 44);
815 cl_capturevideo_soundfile = NULL;
818 void SCR_CaptureVideo_EndVideo(void)
822 if (!cl_capturevideo_active)
824 cl_capturevideo_active = false;
826 if (cl_capturevideo_videofile)
828 FS_Close(cl_capturevideo_videofile);
829 cl_capturevideo_videofile = NULL;
832 // finish the wave file
833 if (cl_capturevideo_soundfile)
835 i = FS_Tell (cl_capturevideo_soundfile);
836 //"RIFF", (int) unknown (chunk size), "WAVE",
837 //"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
838 //"data", (int) unknown (chunk size)
839 memcpy (out, "RIFF****WAVEfmt \x10\x00\x00\x00\x01\x00\x02\x00********\x04\x00\x10\0data****", 44);
840 // the length of the whole RIFF chunk
843 out[5] = (n >> 8) & 0xFF;
844 out[6] = (n >> 16) & 0xFF;
845 out[7] = (n >> 24) & 0xFF;
847 n = cl_capturevideo_soundrate;
848 out[24] = (n) & 0xFF;
849 out[25] = (n >> 8) & 0xFF;
850 out[26] = (n >> 16) & 0xFF;
851 out[27] = (n >> 24) & 0xFF;
852 // bytes per second (rate * channels * bytes per channel)
853 n = cl_capturevideo_soundrate * 2 * 2;
854 out[28] = (n) & 0xFF;
855 out[29] = (n >> 8) & 0xFF;
856 out[30] = (n >> 16) & 0xFF;
857 out[31] = (n >> 24) & 0xFF;
858 // the length of the data chunk
860 out[40] = (n) & 0xFF;
861 out[41] = (n >> 8) & 0xFF;
862 out[42] = (n >> 16) & 0xFF;
863 out[43] = (n >> 24) & 0xFF;
864 FS_Seek (cl_capturevideo_soundfile, 0, SEEK_SET);
865 FS_Write (cl_capturevideo_soundfile, out, 44);
866 FS_Close (cl_capturevideo_soundfile);
867 cl_capturevideo_soundfile = NULL;
870 if (cl_capturevideo_buffer)
872 Mem_Free (cl_capturevideo_buffer);
873 cl_capturevideo_buffer = NULL;
876 cl_capturevideo_starttime = 0;
877 cl_capturevideo_framerate = 0;
878 cl_capturevideo_frame = 0;
881 qboolean SCR_CaptureVideo_VideoFrame(int newframenum)
883 int x = vid.realx, y = vid.realy, width = vid.realwidth, height = vid.realheight;
884 unsigned char *b, *out;
886 int outoffset = (width/2)*(height/2);
887 //return SCR_ScreenShot(filename, cl_capturevideo_buffer, cl_capturevideo_buffer + vid.realwidth * vid.realheight * 3, cl_capturevideo_buffer + vid.realwidth * vid.realheight * 6, vid.realx, vid.realy, vid.realwidth, vid.realheight, false, false, false, jpeg);
888 // speed is critical here, so do saving as directly as possible
889 switch (cl_capturevideo_format)
891 case CAPTUREVIDEOFORMAT_RAWYV12:
892 // FIXME: width/height must be multiple of 2, enforce this?
893 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
895 // process one line at a time, and CbCr every other line at 2 pixel intervals
896 for (y = 0;y < height;y++)
899 for (b = cl_capturevideo_buffer + (height-1-y)*width*3, out = cl_capturevideo_buffer + width*height*3 + y*width, x = 0;x < width;x++, b += 3, out++)
900 *out = cl_capturevideo_yuvnormalizetable[0][cl_capturevideo_rgbtoyuvscaletable[0][0][b[0]] + cl_capturevideo_rgbtoyuvscaletable[0][1][b[1]] + cl_capturevideo_rgbtoyuvscaletable[0][2][b[2]]];
903 // 2x2 Cb and Cr planes
905 // low quality, no averaging
906 for (b = cl_capturevideo_buffer + (height-2-y)*width*3, out = cl_capturevideo_buffer + width*height*3 + width*height + (y/2)*(width/2), x = 0;x < width/2;x++, b += 6, out++)
909 out[0 ] = cl_capturevideo_yuvnormalizetable[2][cl_capturevideo_rgbtoyuvscaletable[2][0][b[0]] + cl_capturevideo_rgbtoyuvscaletable[2][1][b[1]] + cl_capturevideo_rgbtoyuvscaletable[2][2][b[2]] + 128];
911 out[outoffset] = cl_capturevideo_yuvnormalizetable[1][cl_capturevideo_rgbtoyuvscaletable[1][0][b[0]] + cl_capturevideo_rgbtoyuvscaletable[1][1][b[1]] + cl_capturevideo_rgbtoyuvscaletable[1][2][b[2]] + 128];
914 // high quality, averaging
915 int inpitch = width*3;
916 for (b = cl_capturevideo_buffer + (height-2-y)*width*3, out = cl_capturevideo_buffer + width*height*3 + width*height + (y/2)*(width/2), x = 0;x < width/2;x++, b += 6, out++)
918 int blockr, blockg, blockb;
919 blockr = (b[0] + b[3] + b[inpitch+0] + b[inpitch+3]) >> 2;
920 blockg = (b[1] + b[4] + b[inpitch+1] + b[inpitch+4]) >> 2;
921 blockb = (b[2] + b[5] + b[inpitch+2] + b[inpitch+5]) >> 2;
923 out[0 ] = cl_capturevideo_yuvnormalizetable[2][cl_capturevideo_rgbtoyuvscaletable[2][0][blockr] + cl_capturevideo_rgbtoyuvscaletable[2][1][blockg] + cl_capturevideo_rgbtoyuvscaletable[2][2][blockb] + 128];
925 out[outoffset] = cl_capturevideo_yuvnormalizetable[1][cl_capturevideo_rgbtoyuvscaletable[1][0][blockr] + cl_capturevideo_rgbtoyuvscaletable[1][1][blockg] + cl_capturevideo_rgbtoyuvscaletable[1][2][blockb] + 128];
930 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
931 if (!FS_Write (cl_capturevideo_videofile, cl_capturevideo_buffer + width*height*3, width*height+(width/2)*(height/2)*2))
934 case CAPTUREVIDEOFORMAT_RAWRGB:
935 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
937 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
938 if (!FS_Write (cl_capturevideo_videofile, cl_capturevideo_buffer, width*height*3))
941 case CAPTUREVIDEOFORMAT_JPEG:
942 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
944 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
946 sprintf(filename, "video/dp%06d.jpg", cl_capturevideo_frame);
947 if (!JPEG_SaveImage_preflipped (filename, width, height, cl_capturevideo_buffer))
951 case CAPTUREVIDEOFORMAT_TARGA:
952 //return Image_WriteTGARGB_preflipped (filename, width, height, cl_capturevideo_buffer, cl_capturevideo_buffer + vid.realwidth * vid.realheight * 3, );
953 memset (cl_capturevideo_buffer, 0, 18);
954 cl_capturevideo_buffer[2] = 2; // uncompressed type
955 cl_capturevideo_buffer[12] = (width >> 0) & 0xFF;
956 cl_capturevideo_buffer[13] = (width >> 8) & 0xFF;
957 cl_capturevideo_buffer[14] = (height >> 0) & 0xFF;
958 cl_capturevideo_buffer[15] = (height >> 8) & 0xFF;
959 cl_capturevideo_buffer[16] = 24; // pixel size
960 qglReadPixels (x, y, width, height, GL_BGR, GL_UNSIGNED_BYTE, cl_capturevideo_buffer + 18);
962 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
964 sprintf(filename, "video/dp%06d.tga", cl_capturevideo_frame);
965 if (!FS_WriteFile (filename, cl_capturevideo_buffer, width*height*3 + 18))
974 void SCR_CaptureVideo_SoundFrame(qbyte *bufstereo16le, size_t length, int rate)
976 if (!cl_capturevideo_soundfile)
978 cl_capturevideo_soundrate = rate;
979 if (FS_Write (cl_capturevideo_soundfile, bufstereo16le, 4 * length) < 4 * length)
981 Cvar_SetValueQuick(&cl_capturevideo, 0);
982 Con_Printf("video sound saving failed on frame %i, out of disk space? stopping video capture.\n", cl_capturevideo_frame);
983 SCR_CaptureVideo_EndVideo();
987 void SCR_CaptureVideo(void)
990 if (cl_capturevideo.integer && r_render.integer)
992 if (!cl_capturevideo_active)
993 SCR_CaptureVideo_BeginVideo();
994 if (cl_capturevideo_framerate != cl_capturevideo_fps.value)
996 Con_Printf("You can not change the video framerate while recording a video.\n");
997 Cvar_SetValueQuick(&cl_capturevideo_fps, cl_capturevideo_framerate);
999 if (cl_capturevideo_soundfile)
1001 // preserve sound sync by duplicating frames when running slow
1002 newframenum = (Sys_DoubleTime() - cl_capturevideo_starttime) * cl_capturevideo_framerate;
1005 newframenum = cl_capturevideo_frame + 1;
1006 // if falling behind more than one second, stop
1007 if (newframenum - cl_capturevideo_frame > (int)ceil(cl_capturevideo_framerate))
1009 Cvar_SetValueQuick(&cl_capturevideo, 0);
1010 Con_Printf("video saving failed on frame %i, your machine is too slow for this capture speed.\n", cl_capturevideo_frame);
1011 SCR_CaptureVideo_EndVideo();
1015 if (!SCR_CaptureVideo_VideoFrame(newframenum))
1017 Cvar_SetValueQuick(&cl_capturevideo, 0);
1018 Con_Printf("video saving failed on frame %i, out of disk space? stopping video capture.\n", cl_capturevideo_frame);
1019 SCR_CaptureVideo_EndVideo();
1022 else if (cl_capturevideo_active)
1023 SCR_CaptureVideo_EndVideo();
1030 Grab six views for environment mapping tests
1037 qboolean flipx, flipy, flipdiagonaly;
1041 {{ 0, 0, 0}, "rt", true, false, false},
1042 {{ 0, 90, 0}, "ft", true, false, false},
1043 {{ 0, 180, 0}, "lf", true, false, false},
1044 {{ 0, 270, 0}, "bk", true, false, false},
1045 {{-90, 180, 0}, "up", false, true, false},
1046 {{ 90, 180, 0}, "dn", false, true, false},
1048 {{ 0, 0, 0}, "px", true, true, true},
1049 {{ 0, 90, 0}, "py", false, true, false},
1050 {{ 0, 180, 0}, "nx", false, false, true},
1051 {{ 0, 270, 0}, "ny", true, false, false},
1052 {{-90, 180, 0}, "pz", false, false, true},
1053 {{ 90, 180, 0}, "nz", false, false, true}
1056 static void R_Envmap_f (void)
1059 char filename[256], basename[256];
1064 if (Cmd_Argc() != 3)
1066 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");
1070 strlcpy (basename, Cmd_Argv(1), sizeof (basename));
1071 size = atoi(Cmd_Argv(2));
1072 if (size != 128 && size != 256 && size != 512 && size != 1024)
1074 Con_Print("envmap: size must be one of 128, 256, 512, or 1024\n");
1077 if (size > vid.realwidth || size > vid.realheight)
1079 Con_Print("envmap: your resolution is not big enough to render that size\n");
1087 r_refdef.width = size;
1088 r_refdef.height = size;
1090 r_refdef.fov_x = 90;
1091 r_refdef.fov_y = 90;
1093 buffer1 = Mem_Alloc(tempmempool, size * size * 3);
1094 buffer2 = Mem_Alloc(tempmempool, size * size * 3);
1095 buffer3 = Mem_Alloc(tempmempool, size * size * 3 + 18);
1097 for (j = 0;j < 12;j++)
1099 sprintf(filename, "env/%s%s.tga", basename, envmapinfo[j].name);
1100 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);
1105 SCR_ScreenShot(filename, buffer1, buffer2, buffer3, vid.realx, vid.realy + vid.realheight - (r_refdef.y + r_refdef.height), size, size, envmapinfo[j].flipx, envmapinfo[j].flipy, envmapinfo[j].flipdiagonaly, false);
1115 //=============================================================================
1117 // LordHavoc: SHOWLMP stuff
1118 #define SHOWLMP_MAXLABELS 256
1119 typedef struct showlmp_s
1129 showlmp_t showlmp[SHOWLMP_MAXLABELS];
1131 void SHOWLMP_decodehide(void)
1135 lmplabel = MSG_ReadString();
1136 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1137 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
1139 showlmp[i].isactive = false;
1144 void SHOWLMP_decodeshow(void)
1147 qbyte lmplabel[256], picname[256];
1149 strlcpy (lmplabel,MSG_ReadString(), sizeof (lmplabel));
1150 strlcpy (picname, MSG_ReadString(), sizeof (picname));
1151 if (gamemode == GAME_NEHAHRA) // LordHavoc: nasty old legacy junk
1158 x = MSG_ReadShort();
1159 y = MSG_ReadShort();
1162 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1163 if (showlmp[i].isactive)
1165 if (strcmp(showlmp[i].label, lmplabel) == 0)
1168 break; // drop out to replace it
1171 else if (k < 0) // find first empty one to replace
1174 return; // none found to replace
1175 // change existing one
1176 showlmp[k].isactive = true;
1177 strlcpy (showlmp[k].label, lmplabel, sizeof (showlmp[k].label));
1178 strlcpy (showlmp[k].pic, picname, sizeof (showlmp[k].pic));
1183 void SHOWLMP_drawall(void)
1186 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1187 if (showlmp[i].isactive)
1188 DrawQ_Pic(showlmp[i].x, showlmp[i].y, showlmp[i].pic, 0, 0, 1, 1, 1, 1, 0);
1191 void SHOWLMP_clear(void)
1194 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1195 showlmp[i].isactive = false;
1198 void CL_SetupScreenSize(void)
1200 float conwidth, conheight;
1202 VID_GetWindowSize (&vid.realx, &vid.realy, &vid.realwidth, &vid.realheight);
1204 VID_UpdateGamma(false);
1206 conwidth = bound(320, vid_conwidth.value, 2048);
1207 conheight = bound(200, vid_conheight.value, 1536);
1208 if (vid_conwidth.value != conwidth)
1209 Cvar_SetValue("vid_conwidth", conwidth);
1210 if (vid_conheight.value != conheight)
1211 Cvar_SetValue("vid_conheight", conheight);
1213 vid.conwidth = vid_conwidth.integer;
1214 vid.conheight = vid_conheight.integer;
1216 /* if (vid.realheight > 240)
1218 vid.conheight = (vid.realheight - 240) * scr_2dresolution.value + 240;
1219 vid.conheight = bound(240, vid.conheight, vid.realheight);
1222 vid.conheight = 240;*/
1224 SCR_SetUpToDrawConsole();
1227 extern void R_Shadow_EditLights_DrawSelectedLightProperties(void);
1228 void CL_UpdateScreen(void)
1230 if (!scr_initialized || !con_initialized || vid_hidden)
1231 return; // not initialized yet
1233 // don't allow cheats in multiplayer
1234 if (!cl.islocalgame && cl.worldmodel)
1236 if (r_fullbright.integer != 0)
1237 Cvar_Set ("r_fullbright", "0");
1238 if (r_ambient.value != 0)
1239 Cvar_Set ("r_ambient", "0");
1243 if (scr_viewsize.value < 30)
1244 Cvar_Set ("viewsize","30");
1245 if (scr_viewsize.value > 120)
1246 Cvar_Set ("viewsize","120");
1248 // bound field of view
1249 if (scr_fov.value < 1)
1250 Cvar_Set ("fov","1");
1251 if (scr_fov.value > 170)
1252 Cvar_Set ("fov","170");
1254 // intermission is always full screen
1255 if (cl.intermission)
1259 if (scr_viewsize.value >= 120)
1260 sb_lines = 0; // no status bar at all
1261 else if (scr_viewsize.value >= 110)
1262 sb_lines = 24; // no inventory
1267 r_refdef.colormask[0] = 1;
1268 r_refdef.colormask[1] = 1;
1269 r_refdef.colormask[2] = 1;
1273 if (cls.signon == SIGNONS)
1274 R_TimeReport("other");
1276 CL_SetupScreenSize();
1280 if (cls.signon == SIGNONS)
1281 R_TimeReport("setup");
1283 //FIXME: force menu if nothing else to look at?
1284 //if (key_dest == key_game && cls.signon != SIGNONS && cls.state == ca_disconnected)
1286 if (cls.signon == SIGNONS)
1291 if (!r_letterbox.value)
1294 SCR_CheckDrawCenterString();
1300 if (cls.signon == SIGNONS)
1304 R_TimeReport_Start();
1306 R_Shadow_EditLights_DrawSelectedLightProperties();
1313 void CL_Screen_NewMap(void)