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_fps = {0, "cl_capturevideo_fps", "30"};
27 cvar_t cl_capturevideo_rawrgb = {0, "cl_capturevideo_rawrgb", "0"};
28 cvar_t cl_capturevideo_rawyv12 = {0, "cl_capturevideo_rawyv12", "0"};
29 cvar_t r_textshadow = {0, "r_textshadow", "0"};
30 cvar_t r_letterbox = {0, "r_letterbox", "0"};
32 int jpeg_supported = false;
34 qboolean scr_initialized; // ready to draw
36 float scr_con_current;
37 float scr_conlines; // lines of console to display
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");
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)
239 if (key_dest == key_game && cls.signon != SIGNONS && scr_conforcewhiledisconnected.integer)
240 key_consoleactive |= KEY_CONSOLEACTIVE_FORCED;
242 key_consoleactive &= ~KEY_CONSOLEACTIVE_FORCED;
244 // decide on the height of the console
245 if (key_consoleactive & KEY_CONSOLEACTIVE_FORCED)
246 scr_conlines = vid.conheight; // full screen
247 else if (key_consoleactive & KEY_CONSOLEACTIVE_USER)
248 scr_conlines = vid.conheight/2; // half screen
250 scr_conlines = 0; // none visible
252 if (scr_conspeed.value)
254 if (scr_conlines < scr_con_current)
256 scr_con_current -= scr_conspeed.value*host_realframetime;
257 if (scr_conlines > scr_con_current)
258 scr_con_current = scr_conlines;
261 else if (scr_conlines > scr_con_current)
263 scr_con_current += scr_conspeed.value*host_realframetime;
264 if (scr_conlines < scr_con_current)
265 scr_con_current = scr_conlines;
269 scr_con_current = scr_conlines;
277 void SCR_DrawConsole (void)
280 Con_DrawConsole (scr_con_current);
284 if (key_dest == key_game || key_dest == key_message)
285 Con_DrawNotify (); // only draw notify in game
291 SCR_BeginLoadingPlaque
295 void SCR_BeginLoadingPlaque (void)
298 SCR_UpdateLoadingScreen();
301 //=============================================================================
303 char r_speeds_string[1024];
304 int speedstringcount, r_timereport_active;
305 double r_timereport_temp = 0, r_timereport_current = 0, r_timereport_start = 0;
307 void R_TimeReport(char *desc)
313 if (!r_timereport_active)
316 r_timereport_temp = r_timereport_current;
317 r_timereport_current = Sys_DoubleTime();
318 t = (int) ((r_timereport_current - r_timereport_temp) * 1000000.0);
320 sprintf(tempbuf, "%8i %s", t, desc);
321 length = strlen(tempbuf);
323 tempbuf[length++] = ' ';
325 if (speedstringcount + length > (vid.conwidth / 8))
327 strcat(r_speeds_string, "\n");
328 speedstringcount = 0;
330 // skip the space at the beginning if it's the first on the line
331 if (speedstringcount == 0)
333 strcat(r_speeds_string, tempbuf + 1);
334 speedstringcount = length - 1;
338 strcat(r_speeds_string, tempbuf);
339 speedstringcount += length;
343 void R_TimeReport_Start(void)
345 r_timereport_active = r_speeds.integer && cls.signon == SIGNONS && cls.state == ca_connected;
346 r_speeds_string[0] = 0;
347 if (r_timereport_active)
349 speedstringcount = 0;
350 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]);
351 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);
352 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);
353 sprintf(r_speeds_string + strlen(r_speeds_string), "%6i modeltris%6i meshs%6i meshtris\n", c_alias_polys, c_meshs, c_meshelements / 3);
354 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);
355 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);
356 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);
357 sprintf(r_speeds_string + strlen(r_speeds_string), "precomputed: %6i shadowmeshes%6i shadowtris\n", c_rtcached_shadowmeshes, c_rtcached_shadowtris);
374 c_rt_shadowmeshes = 0;
376 c_rt_lightmeshes = 0;
378 c_rtcached_shadowmeshes = 0;
379 c_rtcached_shadowtris = 0;
382 c_bloomcopypixels = 0;
384 c_bloomdrawpixels = 0;
386 r_timereport_start = Sys_DoubleTime();
390 void R_TimeReport_End(void)
392 r_timereport_current = r_timereport_start;
393 R_TimeReport("total");
395 if (r_timereport_active)
399 for (i = 0;r_speeds_string[i];i++)
400 if (r_speeds_string[i] == '\n')
402 y = vid.conheight - sb_lines - lines * 8;
404 DrawQ_Fill(0, y, vid.conwidth, lines * 8, 0, 0, 0, 0.5, 0);
405 while (r_speeds_string[i])
408 while (r_speeds_string[i] && r_speeds_string[i] != '\n')
411 DrawQ_String(0, y, r_speeds_string + j, i - j, 8, 8, 1, 1, 1, 1, 0);
412 if (r_speeds_string[i] == '\n')
426 void SCR_SizeUp_f (void)
428 Cvar_SetValue ("viewsize",scr_viewsize.value+10);
439 void SCR_SizeDown_f (void)
441 Cvar_SetValue ("viewsize",scr_viewsize.value-10);
444 void CL_Screen_Init(void)
446 Cvar_RegisterVariable (&scr_fov);
447 Cvar_RegisterVariable (&scr_viewsize);
448 Cvar_RegisterVariable (&scr_conspeed);
449 Cvar_RegisterVariable (&scr_conalpha);
450 Cvar_RegisterVariable (&scr_conbrightness);
451 Cvar_RegisterVariable (&scr_conforcewhiledisconnected);
452 Cvar_RegisterVariable (&scr_showram);
453 Cvar_RegisterVariable (&scr_showturtle);
454 Cvar_RegisterVariable (&scr_showpause);
455 Cvar_RegisterVariable (&scr_centertime);
456 Cvar_RegisterVariable (&scr_printspeed);
457 Cvar_RegisterVariable (&vid_conwidth);
458 Cvar_RegisterVariable (&vid_conheight);
459 Cvar_RegisterVariable (&vid_pixelaspect);
460 Cvar_RegisterVariable (&scr_screenshot_jpeg);
461 Cvar_RegisterVariable (&scr_screenshot_jpeg_quality);
462 Cvar_RegisterVariable (&scr_screenshot_gamma);
463 Cvar_RegisterVariable (&cl_capturevideo);
464 Cvar_RegisterVariable (&cl_capturevideo_fps);
465 Cvar_RegisterVariable (&cl_capturevideo_rawrgb);
466 Cvar_RegisterVariable (&cl_capturevideo_rawyv12);
467 Cvar_RegisterVariable (&r_textshadow);
468 Cvar_RegisterVariable (&r_letterbox);
470 Cmd_AddCommand ("sizeup",SCR_SizeUp_f);
471 Cmd_AddCommand ("sizedown",SCR_SizeDown_f);
472 Cmd_AddCommand ("screenshot",SCR_ScreenShot_f);
473 Cmd_AddCommand ("envmap", R_Envmap_f);
475 scr_initialized = true;
478 void DrawQ_Clear(void)
480 r_refdef.drawqueuesize = 0;
483 static int picelements[6] = {0, 1, 2, 0, 2, 3};
484 void DrawQ_Pic(float x, float y, char *picname, float width, float height, float red, float green, float blue, float alpha, int flags)
486 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);
489 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)
494 if (alpha < (1.0f / 255.0f))
497 len = strlen(string);
499 for (len = 0;len < maxlen && string[len];len++);
500 for (;len > 0 && string[0] == ' ';string++, x += scalex, len--);
501 for (;len > 0 && string[len - 1] == ' ';len--);
504 if (x >= vid.conwidth || y >= vid.conheight || x < (-scalex * len) || y < (-scaley))
506 size = sizeof(*dq) + ((len + 1 + 3) & ~3);
507 if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
509 red = bound(0, red, 1);
510 green = bound(0, green, 1);
511 blue = bound(0, blue, 1);
512 alpha = bound(0, alpha, 1);
513 dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
515 dq->command = DRAWQUEUE_STRING;
517 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));
522 out = (char *)(dq + 1);
523 memcpy(out, string, len);
525 r_refdef.drawqueuesize += dq->size;
528 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)
530 if (r_textshadow.integer)
531 DrawQ_String_Real(x+scalex*0.25,y+scaley*0.25,string,maxlen,scalex,scaley,0,0,0,alpha*0.8,flags);
533 DrawQ_String_Real(x,y,string,maxlen,scalex,scaley,red,green,blue,alpha,flags);
536 void DrawQ_Fill (float x, float y, float w, float h, float red, float green, float blue, float alpha, int flags)
538 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);
541 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)
545 drawqueuemesh_t mesh;
546 memset(&mesh, 0, sizeof(mesh));
547 if (picname && picname[0])
549 pic = Draw_CachePic(picname);
553 height = pic->height;
554 mesh.texture = pic->tex;
556 mesh.num_triangles = 2;
557 mesh.num_vertices = 4;
558 mesh.data_element3i = picelements;
559 mesh.data_vertex3f = floats;
560 mesh.data_texcoord2f = floats + 12;
561 mesh.data_color4f = floats + 20;
562 memset(floats, 0, sizeof(floats));
563 mesh.data_vertex3f[0] = mesh.data_vertex3f[9] = x;
564 mesh.data_vertex3f[1] = mesh.data_vertex3f[4] = y;
565 mesh.data_vertex3f[3] = mesh.data_vertex3f[6] = x + width;
566 mesh.data_vertex3f[7] = mesh.data_vertex3f[10] = y + height;
567 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;
568 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;
569 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;
570 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;
571 DrawQ_Mesh (&mesh, flags);
574 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
581 size += sizeof(drawqueuemesh_t);
582 size += sizeof(int[3]) * mesh->num_triangles;
583 size += sizeof(float[3]) * mesh->num_vertices;
584 size += sizeof(float[2]) * mesh->num_vertices;
585 size += sizeof(float[4]) * mesh->num_vertices;
586 if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
588 dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
590 dq->command = DRAWQUEUE_MESH;
597 p = (void *)(dq + 1);
598 m = p;p = (qbyte*)p + sizeof(drawqueuemesh_t);
599 m->num_triangles = mesh->num_triangles;
600 m->num_vertices = mesh->num_vertices;
601 m->texture = mesh->texture;
602 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]);
603 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]);
604 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]);
605 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]);
606 r_refdef.drawqueuesize += dq->size;
609 void DrawQ_SetClipArea(float x, float y, float width, float height)
612 if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
614 Con_DPrint("DrawQueue full !\n");
617 dq = (void*) (r_refdef.drawqueue + r_refdef.drawqueuesize);
618 dq->size = sizeof(*dq);
619 dq->command = DRAWQUEUE_SETCLIP;
627 r_refdef.drawqueuesize += dq->size;
630 void DrawQ_ResetClipArea(void)
633 if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
635 Con_DPrint("DrawQueue full !\n");
638 dq = (void*) (r_refdef.drawqueue + r_refdef.drawqueuesize);
639 dq->size = sizeof(*dq);
640 dq->command = DRAWQUEUE_RESETCLIP;
648 r_refdef.drawqueuesize += dq->size;
656 void SCR_ScreenShot_f (void)
658 static int shotnumber;
659 static char oldname[MAX_QPATH];
660 char base[MAX_QPATH];
661 char filename[MAX_QPATH];
665 qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
667 sprintf (base, "screenshots/%s", scr_screenshot_name.string);
669 if (strcmp (oldname, scr_screenshot_name.string))
671 sprintf(oldname, "%s", scr_screenshot_name.string);
675 // find a file name to save it to
676 for (;shotnumber < 1000000;shotnumber++)
677 if (!FS_SysFileExists(va("%s/%s%06d.tga", fs_gamedir, base, shotnumber)) && !FS_SysFileExists(va("%s/%s%06d.jpg", fs_gamedir, base, shotnumber)))
679 if (shotnumber >= 1000000)
681 Con_Print("SCR_ScreenShot_f: Couldn't create the image file\n");
685 sprintf(filename, "%s%06d.%s", base, shotnumber, jpeg ? "jpg" : "tga");
687 buffer1 = Mem_Alloc(tempmempool, vid.realwidth * vid.realheight * 3);
688 buffer2 = Mem_Alloc(tempmempool, vid.realwidth * vid.realheight * 3);
689 buffer3 = Mem_Alloc(tempmempool, vid.realwidth * vid.realheight * 3 + 18);
691 if (SCR_ScreenShot (filename, buffer1, buffer2, buffer3, vid.realx, vid.realy, vid.realwidth, vid.realheight, false, false, false, jpeg))
692 Con_Printf("Wrote %s\n", filename);
694 Con_Printf("unable to write %s\n", filename);
703 typedef enum capturevideoformat_e
705 CAPTUREVIDEOFORMAT_TARGA,
706 CAPTUREVIDEOFORMAT_JPEG,
707 CAPTUREVIDEOFORMAT_RAWRGB,
708 CAPTUREVIDEOFORMAT_RAWYV12
710 capturevideoformat_t;
712 qboolean cl_capturevideo_active = false;
713 capturevideoformat_t cl_capturevideo_format;
714 static double cl_capturevideo_starttime = 0;
715 double cl_capturevideo_framerate = 0;
716 static int cl_capturevideo_soundrate = 0;
717 static int cl_capturevideo_frame = 0;
718 static qbyte *cl_capturevideo_buffer = NULL;
719 static qfile_t *cl_capturevideo_videofile = NULL;
720 static qfile_t *cl_capturevideo_soundfile = NULL;
721 static short cl_capturevideo_rgbtoyuvscaletable[3][3][256];
722 static unsigned char cl_capturevideo_yuvnormalizetable[3][256];
723 static unsigned char cl_capturevideo_rgbgammatable[3][256];
725 void SCR_CaptureVideo_BeginVideo(void)
730 if (cl_capturevideo_active)
732 // soundrate is figured out on the first SoundFrame
733 cl_capturevideo_active = true;
734 cl_capturevideo_starttime = Sys_DoubleTime();
735 cl_capturevideo_framerate = bound(1, cl_capturevideo_fps.value, 1000);
736 cl_capturevideo_soundrate = 0;
737 cl_capturevideo_frame = 0;
738 cl_capturevideo_buffer = Mem_Alloc(tempmempool, vid.realwidth * vid.realheight * (3+3+3) + 18);
739 gamma = 1.0/scr_screenshot_gamma.value;
741 for (i = 0;i < 256;i++)
743 j = (unsigned char)bound(0, 255*pow(i/255.0, gamma), 255);
744 cl_capturevideo_rgbgammatable[0][i] = j;
745 cl_capturevideo_rgbgammatable[1][i] = j;
746 cl_capturevideo_rgbgammatable[2][i] = j;
749 R = Y + 1.4075 * (Cr - 128);
750 G = Y + -0.3455 * (Cb - 128) + -0.7169 * (Cr - 128);
751 B = Y + 1.7790 * (Cb - 128);
752 Y = R * .299 + G * .587 + B * .114;
753 Cb = R * -.169 + G * -.332 + B * .500 + 128.;
754 Cr = R * .500 + G * -.419 + B * -.0813 + 128.;
756 for (i = 0;i < 256;i++)
758 g = i;//255*pow(i/255.0, gamma);
759 // Y weights from RGB
760 cl_capturevideo_rgbtoyuvscaletable[0][0][i] = (short)(g * 0.299);
761 cl_capturevideo_rgbtoyuvscaletable[0][1][i] = (short)(g * 0.587);
762 cl_capturevideo_rgbtoyuvscaletable[0][2][i] = (short)(g * 0.114);
763 // Cb weights from RGB
764 cl_capturevideo_rgbtoyuvscaletable[1][0][i] = (short)(g * -0.169);
765 cl_capturevideo_rgbtoyuvscaletable[1][1][i] = (short)(g * -0.332);
766 cl_capturevideo_rgbtoyuvscaletable[1][2][i] = (short)(g * 0.500);
767 // Cr weights from RGB
768 cl_capturevideo_rgbtoyuvscaletable[2][0][i] = (short)(g * 0.500);
769 cl_capturevideo_rgbtoyuvscaletable[2][1][i] = (short)(g * -0.419);
770 cl_capturevideo_rgbtoyuvscaletable[2][2][i] = (short)(g * -0.0813);
771 // range reduction of YCbCr to valid signal range
772 cl_capturevideo_yuvnormalizetable[0][i] = 16 + i * (236-16) / 256;
773 cl_capturevideo_yuvnormalizetable[1][i] = 16 + i * (240-16) / 256;
774 cl_capturevideo_yuvnormalizetable[2][i] = 16 + i * (240-16) / 256;
777 if (cl_capturevideo_rawrgb.integer)
779 cl_capturevideo_format = CAPTUREVIDEOFORMAT_RAWRGB;
780 cl_capturevideo_videofile = FS_Open ("video/dpvideo.rgb", "wb", false);
782 else if (cl_capturevideo_rawyv12.integer)
784 cl_capturevideo_format = CAPTUREVIDEOFORMAT_RAWYV12;
785 cl_capturevideo_videofile = FS_Open ("video/dpvideo.yv12", "wb", false);
787 else if (scr_screenshot_jpeg.integer)
789 cl_capturevideo_format = CAPTUREVIDEOFORMAT_JPEG;
790 cl_capturevideo_videofile = NULL;
794 cl_capturevideo_format = CAPTUREVIDEOFORMAT_TARGA;
795 cl_capturevideo_videofile = NULL;
798 cl_capturevideo_soundfile = FS_Open ("video/dpvideo.wav", "wb", false);
800 // wave header will be filled out when video ends
802 FS_Write (cl_capturevideo_soundfile, out, 44);
805 void SCR_CaptureVideo_EndVideo(void)
809 if (!cl_capturevideo_active)
811 cl_capturevideo_active = false;
813 if (cl_capturevideo_videofile)
815 FS_Close(cl_capturevideo_videofile);
816 cl_capturevideo_videofile = NULL;
819 // finish the wave file
820 if (cl_capturevideo_soundfile)
822 i = FS_Tell (cl_capturevideo_soundfile);
823 //"RIFF", (int) unknown (chunk size), "WAVE",
824 //"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
825 //"data", (int) unknown (chunk size)
826 memcpy (out, "RIFF****WAVEfmt \x10\x00\x00\x00\x01\x00\x02\x00********\x04\x00\x10\0data****", 44);
827 // the length of the whole RIFF chunk
830 out[5] = (n >> 8) & 0xFF;
831 out[6] = (n >> 16) & 0xFF;
832 out[7] = (n >> 24) & 0xFF;
834 n = cl_capturevideo_soundrate;
835 out[24] = (n) & 0xFF;
836 out[25] = (n >> 8) & 0xFF;
837 out[26] = (n >> 16) & 0xFF;
838 out[27] = (n >> 24) & 0xFF;
839 // bytes per second (rate * channels * bytes per channel)
840 n = cl_capturevideo_soundrate * 2 * 2;
841 out[28] = (n) & 0xFF;
842 out[29] = (n >> 8) & 0xFF;
843 out[30] = (n >> 16) & 0xFF;
844 out[31] = (n >> 24) & 0xFF;
845 // the length of the data chunk
847 out[40] = (n) & 0xFF;
848 out[41] = (n >> 8) & 0xFF;
849 out[42] = (n >> 16) & 0xFF;
850 out[43] = (n >> 24) & 0xFF;
851 FS_Seek (cl_capturevideo_soundfile, 0, SEEK_SET);
852 FS_Write (cl_capturevideo_soundfile, out, 44);
853 FS_Close (cl_capturevideo_soundfile);
854 cl_capturevideo_soundfile = NULL;
857 if (cl_capturevideo_buffer)
859 Mem_Free (cl_capturevideo_buffer);
860 cl_capturevideo_buffer = NULL;
863 cl_capturevideo_starttime = 0;
864 cl_capturevideo_framerate = 0;
865 cl_capturevideo_frame = 0;
868 qboolean SCR_CaptureVideo_VideoFrame(int newframenum)
870 int x = vid.realx, y = vid.realy, width = vid.realwidth, height = vid.realheight;
871 unsigned char *b, *out;
873 int outoffset = (width/2)*(height/2);
874 //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);
875 // speed is critical here, so do saving as directly as possible
876 switch (cl_capturevideo_format)
878 case CAPTUREVIDEOFORMAT_RAWYV12:
879 // FIXME: width/height must be multiple of 2, enforce this?
880 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
882 // process one line at a time, and CbCr every other line at 2 pixel intervals
883 for (y = 0;y < height;y++)
886 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++)
887 *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]]];
890 // 2x2 Cb and Cr planes
892 // low quality, no averaging
893 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++)
896 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];
898 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];
901 // high quality, averaging
902 int inpitch = width*3;
903 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++)
905 int blockr, blockg, blockb;
906 blockr = (b[0] + b[3] + b[inpitch+0] + b[inpitch+3]) >> 2;
907 blockg = (b[1] + b[4] + b[inpitch+1] + b[inpitch+4]) >> 2;
908 blockb = (b[2] + b[5] + b[inpitch+2] + b[inpitch+5]) >> 2;
910 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];
912 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];
917 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
918 if (!FS_Write (cl_capturevideo_videofile, cl_capturevideo_buffer + width*height*3, width*height+(width/2)*(height/2)*2))
921 case CAPTUREVIDEOFORMAT_RAWRGB:
922 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
924 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
925 if (!FS_Write (cl_capturevideo_videofile, cl_capturevideo_buffer, width*height*3))
928 case CAPTUREVIDEOFORMAT_JPEG:
929 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
931 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
933 sprintf(filename, "video/dp%06d.jpg", cl_capturevideo_frame);
934 if (!JPEG_SaveImage_preflipped (filename, width, height, cl_capturevideo_buffer))
938 case CAPTUREVIDEOFORMAT_TARGA:
939 //return Image_WriteTGARGB_preflipped (filename, width, height, cl_capturevideo_buffer, cl_capturevideo_buffer + vid.realwidth * vid.realheight * 3, );
940 memset (cl_capturevideo_buffer, 0, 18);
941 cl_capturevideo_buffer[2] = 2; // uncompressed type
942 cl_capturevideo_buffer[12] = (width >> 0) & 0xFF;
943 cl_capturevideo_buffer[13] = (width >> 8) & 0xFF;
944 cl_capturevideo_buffer[14] = (height >> 0) & 0xFF;
945 cl_capturevideo_buffer[15] = (height >> 8) & 0xFF;
946 cl_capturevideo_buffer[16] = 24; // pixel size
947 qglReadPixels (x, y, width, height, GL_BGR, GL_UNSIGNED_BYTE, cl_capturevideo_buffer + 18);
949 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
951 sprintf(filename, "video/dp%06d.tga", cl_capturevideo_frame);
952 if (!FS_WriteFile (filename, cl_capturevideo_buffer, width*height*3 + 18))
961 void SCR_CaptureVideo_SoundFrame(qbyte *bufstereo16le, size_t length, int rate)
963 cl_capturevideo_soundrate = rate;
964 if (FS_Write (cl_capturevideo_soundfile, bufstereo16le, 4 * length) < 4 * length)
966 Cvar_SetValueQuick(&cl_capturevideo, 0);
967 Con_Printf("video sound saving failed on frame %i, out of disk space? stopping video capture.\n", cl_capturevideo_frame);
968 SCR_CaptureVideo_EndVideo();
972 void SCR_CaptureVideo(void)
975 if (cl_capturevideo.integer && r_render.integer)
977 if (!cl_capturevideo_active)
978 SCR_CaptureVideo_BeginVideo();
979 if (cl_capturevideo_framerate != cl_capturevideo_fps.value)
981 Con_Printf("You can not change the video framerate while recording a video.\n");
982 Cvar_SetValueQuick(&cl_capturevideo_fps, cl_capturevideo_framerate);
984 newframenum = (Sys_DoubleTime() - cl_capturevideo_starttime) * cl_capturevideo_framerate;
985 // if falling behind more than one second, stop
986 if (newframenum - cl_capturevideo_frame > (int)ceil(cl_capturevideo_framerate))
988 Cvar_SetValueQuick(&cl_capturevideo, 0);
989 Con_Printf("video saving failed on frame %i, your machine is too slow for this capture speed.\n", cl_capturevideo_frame);
990 SCR_CaptureVideo_EndVideo();
994 if (!SCR_CaptureVideo_VideoFrame(newframenum))
996 Cvar_SetValueQuick(&cl_capturevideo, 0);
997 Con_Printf("video saving failed on frame %i, out of disk space? stopping video capture.\n", cl_capturevideo_frame);
998 SCR_CaptureVideo_EndVideo();
1001 else if (cl_capturevideo_active)
1002 SCR_CaptureVideo_EndVideo();
1009 Grab six views for environment mapping tests
1016 qboolean flipx, flipy, flipdiagonaly;
1020 {{ 0, 0, 0}, "rt", true, false, false},
1021 {{ 0, 90, 0}, "ft", true, false, false},
1022 {{ 0, 180, 0}, "lf", true, false, false},
1023 {{ 0, 270, 0}, "bk", true, false, false},
1024 {{-90, 180, 0}, "up", false, true, false},
1025 {{ 90, 180, 0}, "dn", false, true, false},
1027 {{ 0, 0, 0}, "px", true, true, true},
1028 {{ 0, 90, 0}, "py", false, true, false},
1029 {{ 0, 180, 0}, "nx", false, false, true},
1030 {{ 0, 270, 0}, "ny", true, false, false},
1031 {{-90, 180, 0}, "pz", false, false, true},
1032 {{ 90, 180, 0}, "nz", false, false, true}
1035 static void R_Envmap_f (void)
1038 char filename[256], basename[256];
1043 if (Cmd_Argc() != 3)
1045 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");
1049 strlcpy (basename, Cmd_Argv(1), sizeof (basename));
1050 size = atoi(Cmd_Argv(2));
1051 if (size != 128 && size != 256 && size != 512 && size != 1024)
1053 Con_Print("envmap: size must be one of 128, 256, 512, or 1024\n");
1056 if (size > vid.realwidth || size > vid.realheight)
1058 Con_Print("envmap: your resolution is not big enough to render that size\n");
1066 r_refdef.width = size;
1067 r_refdef.height = size;
1069 r_refdef.fov_x = 90;
1070 r_refdef.fov_y = 90;
1072 buffer1 = Mem_Alloc(tempmempool, size * size * 3);
1073 buffer2 = Mem_Alloc(tempmempool, size * size * 3);
1074 buffer3 = Mem_Alloc(tempmempool, size * size * 3 + 18);
1076 for (j = 0;j < 12;j++)
1078 sprintf(filename, "env/%s%s.tga", basename, envmapinfo[j].name);
1079 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);
1084 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);
1094 //=============================================================================
1096 // LordHavoc: SHOWLMP stuff
1097 #define SHOWLMP_MAXLABELS 256
1098 typedef struct showlmp_s
1108 showlmp_t showlmp[SHOWLMP_MAXLABELS];
1110 void SHOWLMP_decodehide(void)
1114 lmplabel = MSG_ReadString();
1115 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1116 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
1118 showlmp[i].isactive = false;
1123 void SHOWLMP_decodeshow(void)
1126 qbyte lmplabel[256], picname[256];
1128 strlcpy (lmplabel,MSG_ReadString(), sizeof (lmplabel));
1129 strlcpy (picname, MSG_ReadString(), sizeof (picname));
1130 if (gamemode == GAME_NEHAHRA) // LordHavoc: nasty old legacy junk
1137 x = MSG_ReadShort();
1138 y = MSG_ReadShort();
1141 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1142 if (showlmp[i].isactive)
1144 if (strcmp(showlmp[i].label, lmplabel) == 0)
1147 break; // drop out to replace it
1150 else if (k < 0) // find first empty one to replace
1153 return; // none found to replace
1154 // change existing one
1155 showlmp[k].isactive = true;
1156 strlcpy (showlmp[k].label, lmplabel, sizeof (showlmp[k].label));
1157 strlcpy (showlmp[k].pic, picname, sizeof (showlmp[k].pic));
1162 void SHOWLMP_drawall(void)
1165 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1166 if (showlmp[i].isactive)
1167 DrawQ_Pic(showlmp[i].x, showlmp[i].y, showlmp[i].pic, 0, 0, 1, 1, 1, 1, 0);
1170 void SHOWLMP_clear(void)
1173 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1174 showlmp[i].isactive = false;
1177 void CL_SetupScreenSize(void)
1179 float conwidth, conheight;
1181 VID_GetWindowSize (&vid.realx, &vid.realy, &vid.realwidth, &vid.realheight);
1183 VID_UpdateGamma(false);
1185 conwidth = bound(320, vid_conwidth.value, 2048);
1186 conheight = bound(200, vid_conheight.value, 1536);
1187 if (vid_conwidth.value != conwidth)
1188 Cvar_SetValue("vid_conwidth", conwidth);
1189 if (vid_conheight.value != conheight)
1190 Cvar_SetValue("vid_conheight", conheight);
1192 vid.conwidth = vid_conwidth.integer;
1193 vid.conheight = vid_conheight.integer;
1195 /* if (vid.realheight > 240)
1197 vid.conheight = (vid.realheight - 240) * scr_2dresolution.value + 240;
1198 vid.conheight = bound(240, vid.conheight, vid.realheight);
1201 vid.conheight = 240;*/
1203 SCR_SetUpToDrawConsole();
1206 extern void R_Shadow_EditLights_DrawSelectedLightProperties(void);
1207 void CL_UpdateScreen(void)
1209 if (!scr_initialized || !con_initialized || vid_hidden)
1210 return; // not initialized yet
1212 // don't allow cheats in multiplayer
1213 if (!cl.islocalgame && cl.worldmodel)
1215 if (r_fullbright.integer != 0)
1216 Cvar_Set ("r_fullbright", "0");
1217 if (r_ambient.value != 0)
1218 Cvar_Set ("r_ambient", "0");
1222 if (scr_viewsize.value < 30)
1223 Cvar_Set ("viewsize","30");
1224 if (scr_viewsize.value > 120)
1225 Cvar_Set ("viewsize","120");
1227 // bound field of view
1228 if (scr_fov.value < 1)
1229 Cvar_Set ("fov","1");
1230 if (scr_fov.value > 170)
1231 Cvar_Set ("fov","170");
1233 // intermission is always full screen
1234 if (cl.intermission)
1238 if (scr_viewsize.value >= 120)
1239 sb_lines = 0; // no status bar at all
1240 else if (scr_viewsize.value >= 110)
1241 sb_lines = 24; // no inventory
1246 r_refdef.colormask[0] = 1;
1247 r_refdef.colormask[1] = 1;
1248 r_refdef.colormask[2] = 1;
1252 if (cls.signon == SIGNONS)
1253 R_TimeReport("other");
1255 CL_SetupScreenSize();
1259 if (cls.signon == SIGNONS)
1260 R_TimeReport("setup");
1262 //FIXME: force menu if nothing else to look at?
1263 //if (key_dest == key_game && cls.signon != SIGNONS && cls.state == ca_disconnected)
1265 if (cls.signon == SIGNONS)
1270 if (!r_letterbox.value)
1273 SCR_CheckDrawCenterString();
1279 if (cls.signon == SIGNONS)
1283 R_TimeReport_Start();
1285 R_Shadow_EditLights_DrawSelectedLightProperties();
1292 void CL_Screen_NewMap(void)