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.integer*0.35;
111 // scan the width of the line
112 for (l=0 ; l<vid_conwidth.integer/8 ; l++)
113 if (start[l] == '\n' || !start[l])
115 x = (vid_conwidth.integer - 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", 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", 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", true);
220 DrawQ_Pic ((vid_conwidth.integer - pic->width)/2, (vid_conheight.integer - pic->height)/2, "gfx/pause", 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.integer/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.integer);
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)
305 SCR_UpdateLoadingScreen();
308 //=============================================================================
310 char r_speeds_string[1024];
311 int speedstringcount, r_timereport_active;
312 double r_timereport_temp = 0, r_timereport_current = 0, r_timereport_start = 0;
314 void R_TimeReport(char *desc)
320 if (!r_timereport_active || r_showtrispass)
323 r_timereport_temp = r_timereport_current;
324 r_timereport_current = Sys_DoubleTime();
325 t = (int) ((r_timereport_current - r_timereport_temp) * 1000000.0);
327 sprintf(tempbuf, "%8i %s", t, desc);
328 length = strlen(tempbuf);
330 tempbuf[length++] = ' ';
332 if (speedstringcount + length > (vid_conwidth.integer / 8))
334 strcat(r_speeds_string, "\n");
335 speedstringcount = 0;
337 // skip the space at the beginning if it's the first on the line
338 if (speedstringcount == 0)
340 strcat(r_speeds_string, tempbuf + 1);
341 speedstringcount = length - 1;
345 strcat(r_speeds_string, tempbuf);
346 speedstringcount += length;
350 void R_TimeReport_Start(void)
352 r_timereport_active = r_speeds.integer && cls.signon == SIGNONS && cls.state == ca_connected;
353 r_speeds_string[0] = 0;
354 if (r_timereport_active)
356 speedstringcount = 0;
357 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]);
358 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);
359 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);
360 sprintf(r_speeds_string + strlen(r_speeds_string), "%6i modeltris%6i meshs%6i meshtris\n", c_alias_polys, c_meshs, c_meshelements / 3);
361 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);
362 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);
363 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);
364 sprintf(r_speeds_string + strlen(r_speeds_string), "precomputed: %6i shadowmeshes%6i shadowtris\n", c_rtcached_shadowmeshes, c_rtcached_shadowtris);
381 c_rt_shadowmeshes = 0;
383 c_rt_lightmeshes = 0;
385 c_rtcached_shadowmeshes = 0;
386 c_rtcached_shadowtris = 0;
389 c_bloomcopypixels = 0;
391 c_bloomdrawpixels = 0;
393 r_timereport_start = Sys_DoubleTime();
397 void R_TimeReport_End(void)
399 r_timereport_current = r_timereport_start;
400 R_TimeReport("total");
402 if (r_timereport_active)
406 for (i = 0;r_speeds_string[i];i++)
407 if (r_speeds_string[i] == '\n')
409 y = vid_conheight.integer - sb_lines - lines * 8;
411 DrawQ_Fill(0, y, vid_conwidth.integer, lines * 8, 0, 0, 0, 0.5, 0);
412 while (r_speeds_string[i])
415 while (r_speeds_string[i] && r_speeds_string[i] != '\n')
418 DrawQ_String(0, y, r_speeds_string + j, i - j, 8, 8, 1, 1, 1, 1, 0);
419 if (r_speeds_string[i] == '\n')
433 void SCR_SizeUp_f (void)
435 Cvar_SetValue ("viewsize",scr_viewsize.value+10);
446 void SCR_SizeDown_f (void)
448 Cvar_SetValue ("viewsize",scr_viewsize.value-10);
451 void CL_Screen_Init(void)
453 Cvar_RegisterVariable (&scr_fov);
454 Cvar_RegisterVariable (&scr_viewsize);
455 Cvar_RegisterVariable (&scr_conspeed);
456 Cvar_RegisterVariable (&scr_conalpha);
457 Cvar_RegisterVariable (&scr_conbrightness);
458 Cvar_RegisterVariable (&scr_conforcewhiledisconnected);
459 Cvar_RegisterVariable (&scr_showram);
460 Cvar_RegisterVariable (&scr_showturtle);
461 Cvar_RegisterVariable (&scr_showpause);
462 Cvar_RegisterVariable (&scr_centertime);
463 Cvar_RegisterVariable (&scr_printspeed);
464 Cvar_RegisterVariable (&vid_conwidth);
465 Cvar_RegisterVariable (&vid_conheight);
466 Cvar_RegisterVariable (&vid_pixelaspect);
467 Cvar_RegisterVariable (&scr_screenshot_jpeg);
468 Cvar_RegisterVariable (&scr_screenshot_jpeg_quality);
469 Cvar_RegisterVariable (&scr_screenshot_gamma);
470 Cvar_RegisterVariable (&cl_capturevideo);
471 Cvar_RegisterVariable (&cl_capturevideo_sound);
472 Cvar_RegisterVariable (&cl_capturevideo_fps);
473 Cvar_RegisterVariable (&cl_capturevideo_rawrgb);
474 Cvar_RegisterVariable (&cl_capturevideo_rawyv12);
475 Cvar_RegisterVariable (&r_textshadow);
476 Cvar_RegisterVariable (&r_letterbox);
478 Cmd_AddCommand ("sizeup",SCR_SizeUp_f);
479 Cmd_AddCommand ("sizedown",SCR_SizeDown_f);
480 Cmd_AddCommand ("screenshot",SCR_ScreenShot_f);
481 Cmd_AddCommand ("envmap", R_Envmap_f);
483 scr_initialized = true;
486 void DrawQ_Clear(void)
488 r_refdef.drawqueuesize = 0;
491 static int picelements[6] = {0, 1, 2, 0, 2, 3};
492 void DrawQ_Pic(float x, float y, const char *picname, float width, float height, float red, float green, float blue, float alpha, int flags)
494 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);
497 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)
502 if (alpha < (1.0f / 255.0f))
505 len = strlen(string);
507 for (len = 0;len < maxlen && string[len];len++);
508 for (;len > 0 && string[0] == ' ';string++, x += scalex, len--);
509 for (;len > 0 && string[len - 1] == ' ';len--);
512 if (x >= vid_conwidth.integer || y >= vid_conheight.integer || x < (-scalex * len) || y < (-scaley))
514 size = sizeof(*dq) + ((len + 1 + 3) & ~3);
515 if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
517 red = bound(0, red, 1);
518 green = bound(0, green, 1);
519 blue = bound(0, blue, 1);
520 alpha = bound(0, alpha, 1);
521 dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
523 dq->command = DRAWQUEUE_STRING;
525 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));
530 out = (char *)(dq + 1);
531 memcpy(out, string, len);
533 r_refdef.drawqueuesize += dq->size;
536 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)
538 if (r_textshadow.integer)
539 DrawQ_String_Real(x+scalex*0.25,y+scaley*0.25,string,maxlen,scalex,scaley,0,0,0,alpha*0.8,flags);
541 DrawQ_String_Real(x,y,string,maxlen,scalex,scaley,red,green,blue,alpha,flags);
544 void DrawQ_Fill (float x, float y, float w, float h, float red, float green, float blue, float alpha, int flags)
546 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);
549 void DrawQ_SuperPic(float x, float y, const char *picname, float width, float height, float s1, float t1, float r1, float g1, float b1, float a1, float s2, float t2, float r2, float g2, float b2, float a2, float s3, float t3, float r3, float g3, float b3, float a3, float s4, float t4, float r4, float g4, float b4, float a4, int flags)
553 drawqueuemesh_t mesh;
554 memset(&mesh, 0, sizeof(mesh));
555 if (picname && picname[0])
557 pic = Draw_CachePic(picname, false);
561 height = pic->height;
562 mesh.texture = pic->tex;
564 mesh.num_triangles = 2;
565 mesh.num_vertices = 4;
566 mesh.data_element3i = picelements;
567 mesh.data_vertex3f = floats;
568 mesh.data_texcoord2f = floats + 12;
569 mesh.data_color4f = floats + 20;
570 memset(floats, 0, sizeof(floats));
571 mesh.data_vertex3f[0] = mesh.data_vertex3f[9] = x;
572 mesh.data_vertex3f[1] = mesh.data_vertex3f[4] = y;
573 mesh.data_vertex3f[3] = mesh.data_vertex3f[6] = x + width;
574 mesh.data_vertex3f[7] = mesh.data_vertex3f[10] = y + height;
575 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;
576 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;
577 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;
578 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;
579 DrawQ_Mesh (&mesh, flags);
582 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
589 size += sizeof(drawqueuemesh_t);
590 size += sizeof(int[3]) * mesh->num_triangles;
591 size += sizeof(float[3]) * mesh->num_vertices;
592 size += sizeof(float[2]) * mesh->num_vertices;
593 size += sizeof(float[4]) * mesh->num_vertices;
594 if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
596 dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
598 dq->command = DRAWQUEUE_MESH;
605 p = (void *)(dq + 1);
606 m = p;p = (qbyte*)p + sizeof(drawqueuemesh_t);
607 m->num_triangles = mesh->num_triangles;
608 m->num_vertices = mesh->num_vertices;
609 m->texture = mesh->texture;
610 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]);
611 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]);
612 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]);
613 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]);
614 r_refdef.drawqueuesize += dq->size;
617 void DrawQ_SetClipArea(float x, float y, float width, float height)
620 if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
622 Con_DPrint("DrawQueue full !\n");
625 dq = (void*) (r_refdef.drawqueue + r_refdef.drawqueuesize);
626 dq->size = sizeof(*dq);
627 dq->command = DRAWQUEUE_SETCLIP;
635 r_refdef.drawqueuesize += dq->size;
638 void DrawQ_ResetClipArea(void)
641 if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
643 Con_DPrint("DrawQueue full !\n");
646 dq = (void*) (r_refdef.drawqueue + r_refdef.drawqueuesize);
647 dq->size = sizeof(*dq);
648 dq->command = DRAWQUEUE_RESETCLIP;
656 r_refdef.drawqueuesize += dq->size;
664 void SCR_ScreenShot_f (void)
666 static int shotnumber;
667 static char oldname[MAX_QPATH];
668 char base[MAX_QPATH];
669 char filename[MAX_QPATH];
673 qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
675 sprintf (base, "screenshots/%s", scr_screenshot_name.string);
677 if (strcmp (oldname, scr_screenshot_name.string))
679 sprintf(oldname, "%s", scr_screenshot_name.string);
683 // find a file name to save it to
684 for (;shotnumber < 1000000;shotnumber++)
685 if (!FS_SysFileExists(va("%s/%s%06d.tga", fs_gamedir, base, shotnumber)) && !FS_SysFileExists(va("%s/%s%06d.jpg", fs_gamedir, base, shotnumber)))
687 if (shotnumber >= 1000000)
689 Con_Print("SCR_ScreenShot_f: Couldn't create the image file\n");
693 sprintf(filename, "%s%06d.%s", base, shotnumber, jpeg ? "jpg" : "tga");
695 buffer1 = Mem_Alloc(tempmempool, vid.width * vid.height * 3);
696 buffer2 = Mem_Alloc(tempmempool, vid.width * vid.height * 3);
697 buffer3 = Mem_Alloc(tempmempool, vid.width * vid.height * 3 + 18);
699 if (SCR_ScreenShot (filename, buffer1, buffer2, buffer3, 0, 0, vid.width, vid.height, false, false, false, jpeg, true))
700 Con_Printf("Wrote %s\n", filename);
702 Con_Printf("unable to write %s\n", filename);
711 typedef enum capturevideoformat_e
713 CAPTUREVIDEOFORMAT_TARGA,
714 CAPTUREVIDEOFORMAT_JPEG,
715 CAPTUREVIDEOFORMAT_RAWRGB,
716 CAPTUREVIDEOFORMAT_RAWYV12
718 capturevideoformat_t;
720 qboolean cl_capturevideo_active = false;
721 capturevideoformat_t cl_capturevideo_format;
722 static double cl_capturevideo_starttime = 0;
723 double cl_capturevideo_framerate = 0;
724 static int cl_capturevideo_soundrate = 0;
725 static int cl_capturevideo_frame = 0;
726 static qbyte *cl_capturevideo_buffer = NULL;
727 static qfile_t *cl_capturevideo_videofile = NULL;
728 qfile_t *cl_capturevideo_soundfile = NULL;
729 static short cl_capturevideo_rgbtoyuvscaletable[3][3][256];
730 static unsigned char cl_capturevideo_yuvnormalizetable[3][256];
731 //static unsigned char cl_capturevideo_rgbgammatable[3][256];
733 void SCR_CaptureVideo_BeginVideo(void)
738 if (cl_capturevideo_active)
740 // soundrate is figured out on the first SoundFrame
741 cl_capturevideo_active = true;
742 cl_capturevideo_starttime = Sys_DoubleTime();
743 cl_capturevideo_framerate = bound(1, cl_capturevideo_fps.value, 1000);
744 cl_capturevideo_soundrate = 0;
745 cl_capturevideo_frame = 0;
746 cl_capturevideo_buffer = Mem_Alloc(tempmempool, vid.width * vid.height * (3+3+3) + 18);
747 gamma = 1.0/scr_screenshot_gamma.value;
750 for (i = 0;i < 256;i++)
752 unsigned char j = (unsigned char)bound(0, 255*pow(i/255.0, gamma), 255);
753 cl_capturevideo_rgbgammatable[0][i] = j;
754 cl_capturevideo_rgbgammatable[1][i] = j;
755 cl_capturevideo_rgbgammatable[2][i] = j;
759 R = Y + 1.4075 * (Cr - 128);
760 G = Y + -0.3455 * (Cb - 128) + -0.7169 * (Cr - 128);
761 B = Y + 1.7790 * (Cb - 128);
762 Y = R * .299 + G * .587 + B * .114;
763 Cb = R * -.169 + G * -.332 + B * .500 + 128.;
764 Cr = R * .500 + G * -.419 + B * -.0813 + 128.;
766 for (i = 0;i < 256;i++)
768 g = 255*pow(i/255.0, gamma);
769 // Y weights from RGB
770 cl_capturevideo_rgbtoyuvscaletable[0][0][i] = (short)(g * 0.299);
771 cl_capturevideo_rgbtoyuvscaletable[0][1][i] = (short)(g * 0.587);
772 cl_capturevideo_rgbtoyuvscaletable[0][2][i] = (short)(g * 0.114);
773 // Cb weights from RGB
774 cl_capturevideo_rgbtoyuvscaletable[1][0][i] = (short)(g * -0.169);
775 cl_capturevideo_rgbtoyuvscaletable[1][1][i] = (short)(g * -0.332);
776 cl_capturevideo_rgbtoyuvscaletable[1][2][i] = (short)(g * 0.500);
777 // Cr weights from RGB
778 cl_capturevideo_rgbtoyuvscaletable[2][0][i] = (short)(g * 0.500);
779 cl_capturevideo_rgbtoyuvscaletable[2][1][i] = (short)(g * -0.419);
780 cl_capturevideo_rgbtoyuvscaletable[2][2][i] = (short)(g * -0.0813);
781 // range reduction of YCbCr to valid signal range
782 cl_capturevideo_yuvnormalizetable[0][i] = 16 + i * (236-16) / 256;
783 cl_capturevideo_yuvnormalizetable[1][i] = 16 + i * (240-16) / 256;
784 cl_capturevideo_yuvnormalizetable[2][i] = 16 + i * (240-16) / 256;
787 if (cl_capturevideo_rawrgb.integer)
789 cl_capturevideo_format = CAPTUREVIDEOFORMAT_RAWRGB;
790 cl_capturevideo_videofile = FS_Open ("video/dpvideo.rgb", "wb", false, true);
792 else if (cl_capturevideo_rawyv12.integer)
794 cl_capturevideo_format = CAPTUREVIDEOFORMAT_RAWYV12;
795 cl_capturevideo_videofile = FS_Open ("video/dpvideo.yv12", "wb", false, true);
797 else if (scr_screenshot_jpeg.integer)
799 cl_capturevideo_format = CAPTUREVIDEOFORMAT_JPEG;
800 cl_capturevideo_videofile = NULL;
804 cl_capturevideo_format = CAPTUREVIDEOFORMAT_TARGA;
805 cl_capturevideo_videofile = NULL;
808 if (cl_capturevideo_sound.integer)
810 cl_capturevideo_soundfile = FS_Open ("video/dpvideo.wav", "wb", false, true);
811 // wave header will be filled out when video ends
813 FS_Write (cl_capturevideo_soundfile, out, 44);
816 cl_capturevideo_soundfile = NULL;
819 void SCR_CaptureVideo_EndVideo(void)
823 if (!cl_capturevideo_active)
825 cl_capturevideo_active = false;
827 if (cl_capturevideo_videofile)
829 FS_Close(cl_capturevideo_videofile);
830 cl_capturevideo_videofile = NULL;
833 // finish the wave file
834 if (cl_capturevideo_soundfile)
836 i = FS_Tell (cl_capturevideo_soundfile);
837 //"RIFF", (int) unknown (chunk size), "WAVE",
838 //"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
839 //"data", (int) unknown (chunk size)
840 memcpy (out, "RIFF****WAVEfmt \x10\x00\x00\x00\x01\x00\x02\x00********\x04\x00\x10\0data****", 44);
841 // the length of the whole RIFF chunk
844 out[5] = (n >> 8) & 0xFF;
845 out[6] = (n >> 16) & 0xFF;
846 out[7] = (n >> 24) & 0xFF;
848 n = cl_capturevideo_soundrate;
849 out[24] = (n) & 0xFF;
850 out[25] = (n >> 8) & 0xFF;
851 out[26] = (n >> 16) & 0xFF;
852 out[27] = (n >> 24) & 0xFF;
853 // bytes per second (rate * channels * bytes per channel)
854 n = cl_capturevideo_soundrate * 2 * 2;
855 out[28] = (n) & 0xFF;
856 out[29] = (n >> 8) & 0xFF;
857 out[30] = (n >> 16) & 0xFF;
858 out[31] = (n >> 24) & 0xFF;
859 // the length of the data chunk
861 out[40] = (n) & 0xFF;
862 out[41] = (n >> 8) & 0xFF;
863 out[42] = (n >> 16) & 0xFF;
864 out[43] = (n >> 24) & 0xFF;
865 FS_Seek (cl_capturevideo_soundfile, 0, SEEK_SET);
866 FS_Write (cl_capturevideo_soundfile, out, 44);
867 FS_Close (cl_capturevideo_soundfile);
868 cl_capturevideo_soundfile = NULL;
871 if (cl_capturevideo_buffer)
873 Mem_Free (cl_capturevideo_buffer);
874 cl_capturevideo_buffer = NULL;
877 cl_capturevideo_starttime = 0;
878 cl_capturevideo_framerate = 0;
879 cl_capturevideo_frame = 0;
882 qboolean SCR_CaptureVideo_VideoFrame(int newframenum)
884 int x = 0, y = 0, width = vid.width, height = vid.height;
885 unsigned char *b, *out;
887 int outoffset = (width/2)*(height/2);
888 //return SCR_ScreenShot(filename, cl_capturevideo_buffer, cl_capturevideo_buffer + vid.width * vid.height * 3, cl_capturevideo_buffer + vid.width * vid.height * 6, 0, 0, vid.width, vid.height, false, false, false, jpeg, true);
889 // speed is critical here, so do saving as directly as possible
890 switch (cl_capturevideo_format)
892 case CAPTUREVIDEOFORMAT_RAWYV12:
893 // FIXME: width/height must be multiple of 2, enforce this?
894 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
896 // process one line at a time, and CbCr every other line at 2 pixel intervals
897 for (y = 0;y < height;y++)
900 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++)
901 *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]]];
904 // 2x2 Cb and Cr planes
906 // low quality, no averaging
907 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++)
910 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];
912 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];
915 // high quality, averaging
916 int inpitch = width*3;
917 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++)
919 int blockr, blockg, blockb;
920 blockr = (b[0] + b[3] + b[inpitch+0] + b[inpitch+3]) >> 2;
921 blockg = (b[1] + b[4] + b[inpitch+1] + b[inpitch+4]) >> 2;
922 blockb = (b[2] + b[5] + b[inpitch+2] + b[inpitch+5]) >> 2;
924 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];
926 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];
931 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
932 if (!FS_Write (cl_capturevideo_videofile, cl_capturevideo_buffer + width*height*3, width*height+(width/2)*(height/2)*2))
935 case CAPTUREVIDEOFORMAT_RAWRGB:
936 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
938 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
939 if (!FS_Write (cl_capturevideo_videofile, cl_capturevideo_buffer, width*height*3))
942 case CAPTUREVIDEOFORMAT_JPEG:
943 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
945 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
947 sprintf(filename, "video/dp%06d.jpg", cl_capturevideo_frame);
948 if (!JPEG_SaveImage_preflipped (filename, width, height, cl_capturevideo_buffer))
952 case CAPTUREVIDEOFORMAT_TARGA:
953 //return Image_WriteTGARGB_preflipped (filename, width, height, cl_capturevideo_buffer, cl_capturevideo_buffer + vid.width * vid.height * 3, );
954 memset (cl_capturevideo_buffer, 0, 18);
955 cl_capturevideo_buffer[2] = 2; // uncompressed type
956 cl_capturevideo_buffer[12] = (width >> 0) & 0xFF;
957 cl_capturevideo_buffer[13] = (width >> 8) & 0xFF;
958 cl_capturevideo_buffer[14] = (height >> 0) & 0xFF;
959 cl_capturevideo_buffer[15] = (height >> 8) & 0xFF;
960 cl_capturevideo_buffer[16] = 24; // pixel size
961 qglReadPixels (x, y, width, height, GL_BGR, GL_UNSIGNED_BYTE, cl_capturevideo_buffer + 18);
963 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
965 sprintf(filename, "video/dp%06d.tga", cl_capturevideo_frame);
966 if (!FS_WriteFile (filename, cl_capturevideo_buffer, width*height*3 + 18))
975 void SCR_CaptureVideo_SoundFrame(qbyte *bufstereo16le, size_t length, int rate)
977 if (!cl_capturevideo_soundfile)
979 cl_capturevideo_soundrate = rate;
980 if (FS_Write (cl_capturevideo_soundfile, bufstereo16le, 4 * length) < 4 * length)
982 Cvar_SetValueQuick(&cl_capturevideo, 0);
983 Con_Printf("video sound saving failed on frame %i, out of disk space? stopping video capture.\n", cl_capturevideo_frame);
984 SCR_CaptureVideo_EndVideo();
988 void SCR_CaptureVideo(void)
991 if (cl_capturevideo.integer && r_render.integer)
993 if (!cl_capturevideo_active)
994 SCR_CaptureVideo_BeginVideo();
995 if (cl_capturevideo_framerate != cl_capturevideo_fps.value)
997 Con_Printf("You can not change the video framerate while recording a video.\n");
998 Cvar_SetValueQuick(&cl_capturevideo_fps, cl_capturevideo_framerate);
1000 if (cl_capturevideo_soundfile)
1002 // preserve sound sync by duplicating frames when running slow
1003 newframenum = (Sys_DoubleTime() - cl_capturevideo_starttime) * cl_capturevideo_framerate;
1006 newframenum = cl_capturevideo_frame + 1;
1007 // if falling behind more than one second, stop
1008 if (newframenum - cl_capturevideo_frame > (int)ceil(cl_capturevideo_framerate))
1010 Cvar_SetValueQuick(&cl_capturevideo, 0);
1011 Con_Printf("video saving failed on frame %i, your machine is too slow for this capture speed.\n", cl_capturevideo_frame);
1012 SCR_CaptureVideo_EndVideo();
1016 if (!SCR_CaptureVideo_VideoFrame(newframenum))
1018 Cvar_SetValueQuick(&cl_capturevideo, 0);
1019 Con_Printf("video saving failed on frame %i, out of disk space? stopping video capture.\n", cl_capturevideo_frame);
1020 SCR_CaptureVideo_EndVideo();
1023 else if (cl_capturevideo_active)
1024 SCR_CaptureVideo_EndVideo();
1031 Grab six views for environment mapping tests
1038 qboolean flipx, flipy, flipdiagonaly;
1042 {{ 0, 0, 0}, "rt", false, false, false},
1043 {{ 0, 270, 0}, "ft", false, false, false},
1044 {{ 0, 180, 0}, "lf", false, false, false},
1045 {{ 0, 90, 0}, "bk", false, false, false},
1046 {{-90, 180, 0}, "up", true, true, false},
1047 {{ 90, 180, 0}, "dn", true, true, false},
1049 {{ 0, 0, 0}, "px", true, true, true},
1050 {{ 0, 90, 0}, "py", false, true, false},
1051 {{ 0, 180, 0}, "nx", false, false, true},
1052 {{ 0, 270, 0}, "ny", true, false, false},
1053 {{-90, 180, 0}, "pz", false, false, true},
1054 {{ 90, 180, 0}, "nz", false, false, true}
1057 static void R_Envmap_f (void)
1060 char filename[256], basename[256];
1065 if (Cmd_Argc() != 3)
1067 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");
1071 strlcpy (basename, Cmd_Argv(1), sizeof (basename));
1072 size = atoi(Cmd_Argv(2));
1073 if (size != 128 && size != 256 && size != 512 && size != 1024)
1075 Con_Print("envmap: size must be one of 128, 256, 512, or 1024\n");
1078 if (size > vid.width || size > vid.height)
1080 Con_Print("envmap: your resolution is not big enough to render that size\n");
1088 r_refdef.width = size;
1089 r_refdef.height = size;
1091 r_refdef.fov_x = 90;
1092 r_refdef.fov_y = 90;
1094 buffer1 = Mem_Alloc(tempmempool, size * size * 3);
1095 buffer2 = Mem_Alloc(tempmempool, size * size * 3);
1096 buffer3 = Mem_Alloc(tempmempool, size * size * 3 + 18);
1098 for (j = 0;j < 12;j++)
1100 sprintf(filename, "env/%s%s.tga", basename, envmapinfo[j].name);
1101 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);
1106 SCR_ScreenShot(filename, buffer1, buffer2, buffer3, 0, vid.height - (r_refdef.y + r_refdef.height), size, size, envmapinfo[j].flipx, envmapinfo[j].flipy, envmapinfo[j].flipdiagonaly, false, false);
1116 //=============================================================================
1118 // LordHavoc: SHOWLMP stuff
1119 #define SHOWLMP_MAXLABELS 256
1120 typedef struct showlmp_s
1130 showlmp_t showlmp[SHOWLMP_MAXLABELS];
1132 void SHOWLMP_decodehide(void)
1136 lmplabel = MSG_ReadString();
1137 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1138 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
1140 showlmp[i].isactive = false;
1145 void SHOWLMP_decodeshow(void)
1148 qbyte lmplabel[256], picname[256];
1150 strlcpy (lmplabel,MSG_ReadString(), sizeof (lmplabel));
1151 strlcpy (picname, MSG_ReadString(), sizeof (picname));
1152 if (gamemode == GAME_NEHAHRA) // LordHavoc: nasty old legacy junk
1159 x = MSG_ReadShort();
1160 y = MSG_ReadShort();
1163 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1164 if (showlmp[i].isactive)
1166 if (strcmp(showlmp[i].label, lmplabel) == 0)
1169 break; // drop out to replace it
1172 else if (k < 0) // find first empty one to replace
1175 return; // none found to replace
1176 // change existing one
1177 showlmp[k].isactive = true;
1178 strlcpy (showlmp[k].label, lmplabel, sizeof (showlmp[k].label));
1179 strlcpy (showlmp[k].pic, picname, sizeof (showlmp[k].pic));
1184 void SHOWLMP_drawall(void)
1187 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1188 if (showlmp[i].isactive)
1189 DrawQ_Pic(showlmp[i].x, showlmp[i].y, showlmp[i].pic, 0, 0, 1, 1, 1, 1, 0);
1192 void SHOWLMP_clear(void)
1195 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1196 showlmp[i].isactive = false;
1199 void CL_SetupScreenSize(void)
1201 float conwidth, conheight;
1203 VID_UpdateGamma(false);
1205 conwidth = bound(320, vid_conwidth.value, 2048);
1206 conheight = bound(200, vid_conheight.value, 1536);
1207 if (vid_conwidth.value != conwidth)
1208 Cvar_SetValue("vid_conwidth", conwidth);
1209 if (vid_conheight.value != conheight)
1210 Cvar_SetValue("vid_conheight", conheight);
1212 vid_conwidth.integer = vid_conwidth.integer;
1213 vid_conheight.integer = vid_conheight.integer;
1215 SCR_SetUpToDrawConsole();
1218 extern void R_Shadow_EditLights_DrawSelectedLightProperties(void);
1219 void CL_UpdateScreen(void)
1221 if (!scr_initialized || !con_initialized || vid_hidden)
1222 return; // not initialized yet
1224 // don't allow cheats in multiplayer
1225 if (!cl.islocalgame && cl.worldmodel)
1227 if (r_fullbright.integer != 0)
1228 Cvar_Set ("r_fullbright", "0");
1229 if (r_ambient.value != 0)
1230 Cvar_Set ("r_ambient", "0");
1234 if (scr_viewsize.value < 30)
1235 Cvar_Set ("viewsize","30");
1236 if (scr_viewsize.value > 120)
1237 Cvar_Set ("viewsize","120");
1239 // bound field of view
1240 if (scr_fov.value < 1)
1241 Cvar_Set ("fov","1");
1242 if (scr_fov.value > 170)
1243 Cvar_Set ("fov","170");
1245 // intermission is always full screen
1246 if (cl.intermission)
1250 if (scr_viewsize.value >= 120)
1251 sb_lines = 0; // no status bar at all
1252 else if (scr_viewsize.value >= 110)
1253 sb_lines = 24; // no inventory
1258 r_refdef.colormask[0] = 1;
1259 r_refdef.colormask[1] = 1;
1260 r_refdef.colormask[2] = 1;
1264 if (cls.signon == SIGNONS)
1265 R_TimeReport("other");
1267 CL_SetupScreenSize();
1271 if (cls.signon == SIGNONS)
1272 R_TimeReport("setup");
1274 //FIXME: force menu if nothing else to look at?
1275 //if (key_dest == key_game && cls.signon != SIGNONS && cls.state == ca_disconnected)
1277 if (cls.signon == SIGNONS)
1282 if (!r_letterbox.value)
1285 SCR_CheckDrawCenterString();
1291 if (cls.signon == SIGNONS)
1295 R_TimeReport_Start();
1297 R_Shadow_EditLights_DrawSelectedLightProperties();
1304 void CL_Screen_NewMap(void)