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;
38 extern int con_vislines;
40 void DrawCrosshair(int num);
41 static void SCR_ScreenShot_f (void);
42 static void R_Envmap_f (void);
45 void R_ClearScreen(void);
48 ===============================================================================
52 ===============================================================================
55 char scr_centerstring[1024];
56 float scr_centertime_start; // for slow victory printing
57 float scr_centertime_off;
66 Called for important messages that should stay in the center of the screen
70 void SCR_CenterPrint(char *str)
72 strlcpy (scr_centerstring, str, sizeof (scr_centerstring));
73 scr_centertime_off = scr_centertime.value;
74 scr_centertime_start = cl.time;
76 // count the number of lines for centering
87 void SCR_DrawCenterString (void)
94 // the finale prints the characters one at a time
96 remaining = scr_printspeed.value * (cl.time - scr_centertime_start);
100 scr_erase_center = 0;
101 start = scr_centerstring;
103 if (scr_center_lines <= 4)
104 y = vid.conheight*0.35;
110 // scan the width of the line
111 for (l=0 ; l<vid.conwidth/8 ; l++)
112 if (start[l] == '\n' || !start[l])
114 x = (vid.conwidth - l*8)/2;
119 DrawQ_String(x, y, start, l, 8, 8, 1, 1, 1, 1, 0);
127 while (*start && *start != '\n')
132 start++; // skip the \n
136 void SCR_CheckDrawCenterString (void)
138 if (scr_center_lines > scr_erase_lines)
139 scr_erase_lines = scr_center_lines;
141 scr_centertime_off -= host_frametime;
143 // don't draw if this is a normal stats-screen intermission,
144 // only if it is not an intermission, or a finale intermission
145 if (cl.intermission == 1)
147 if (scr_centertime_off <= 0 && !cl.intermission)
149 if (key_dest != key_game)
152 SCR_DrawCenterString ();
160 void SCR_DrawTurtle (void)
164 if (cls.state != ca_connected)
167 if (!scr_showturtle.integer)
170 if (host_frametime < 0.1)
180 DrawQ_Pic (0, 0, "gfx/turtle.lmp", 0, 0, 1, 1, 1, 1, 0);
188 void SCR_DrawNet (void)
190 if (cls.state != ca_connected)
192 if (realtime - cl.last_received_message < 0.3)
194 if (cls.demoplayback)
197 DrawQ_Pic (64, 0, "gfx/net.lmp", 0, 0, 1, 1, 1, 1, 0);
205 void SCR_DrawPause (void)
209 if (cls.state != ca_connected)
212 if (!scr_showpause.integer) // turn off for screenshots
218 pic = Draw_CachePic ("gfx/pause.lmp", true);
219 DrawQ_Pic ((vid.conwidth - pic->width)/2, (vid.conheight - pic->height)/2, "gfx/pause.lmp", 0, 0, 1, 1, 1, 1, 0);
226 //=============================================================================
231 SCR_SetUpToDrawConsole
234 void SCR_SetUpToDrawConsole (void)
236 // lines of console to display
241 if (key_dest == key_game && cls.signon != SIGNONS && scr_conforcewhiledisconnected.integer)
242 key_consoleactive |= KEY_CONSOLEACTIVE_FORCED;
244 key_consoleactive &= ~KEY_CONSOLEACTIVE_FORCED;
246 // decide on the height of the console
247 if (key_consoleactive & KEY_CONSOLEACTIVE_USER)
248 conlines = vid.conheight/2; // half screen
250 conlines = 0; // none visible
252 if (scr_conspeed.value)
254 if (scr_con_current > conlines)
256 scr_con_current -= scr_conspeed.value*host_realframetime;
257 if (scr_con_current < conlines)
258 scr_con_current = conlines;
261 else if (scr_con_current < conlines)
263 scr_con_current += scr_conspeed.value*host_realframetime;
264 if (scr_con_current > conlines)
265 scr_con_current = conlines;
269 scr_con_current = conlines;
277 void SCR_DrawConsole (void)
279 if (key_consoleactive & KEY_CONSOLEACTIVE_FORCED)
282 Con_DrawConsole (vid.conheight);
284 else if (scr_con_current)
285 Con_DrawConsole (scr_con_current);
289 if (key_dest == key_game || key_dest == key_message)
290 Con_DrawNotify (); // only draw notify in game
296 SCR_BeginLoadingPlaque
300 void SCR_BeginLoadingPlaque (void)
303 SCR_UpdateLoadingScreen();
306 //=============================================================================
308 char r_speeds_string[1024];
309 int speedstringcount, r_timereport_active;
310 double r_timereport_temp = 0, r_timereport_current = 0, r_timereport_start = 0;
312 void R_TimeReport(char *desc)
318 if (!r_timereport_active)
321 r_timereport_temp = r_timereport_current;
322 r_timereport_current = Sys_DoubleTime();
323 t = (int) ((r_timereport_current - r_timereport_temp) * 1000000.0);
325 sprintf(tempbuf, "%8i %s", t, desc);
326 length = strlen(tempbuf);
328 tempbuf[length++] = ' ';
330 if (speedstringcount + length > (vid.conwidth / 8))
332 strcat(r_speeds_string, "\n");
333 speedstringcount = 0;
335 // skip the space at the beginning if it's the first on the line
336 if (speedstringcount == 0)
338 strcat(r_speeds_string, tempbuf + 1);
339 speedstringcount = length - 1;
343 strcat(r_speeds_string, tempbuf);
344 speedstringcount += length;
348 void R_TimeReport_Start(void)
350 r_timereport_active = r_speeds.integer && cls.signon == SIGNONS && cls.state == ca_connected;
351 r_speeds_string[0] = 0;
352 if (r_timereport_active)
354 speedstringcount = 0;
355 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]);
356 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);
357 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);
358 sprintf(r_speeds_string + strlen(r_speeds_string), "%6i modeltris%6i meshs%6i meshtris\n", c_alias_polys, c_meshs, c_meshelements / 3);
359 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);
360 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);
361 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);
362 sprintf(r_speeds_string + strlen(r_speeds_string), "precomputed: %6i shadowmeshes%6i shadowtris\n", c_rtcached_shadowmeshes, c_rtcached_shadowtris);
379 c_rt_shadowmeshes = 0;
381 c_rt_lightmeshes = 0;
383 c_rtcached_shadowmeshes = 0;
384 c_rtcached_shadowtris = 0;
387 c_bloomcopypixels = 0;
389 c_bloomdrawpixels = 0;
391 r_timereport_start = Sys_DoubleTime();
395 void R_TimeReport_End(void)
397 r_timereport_current = r_timereport_start;
398 R_TimeReport("total");
400 if (r_timereport_active)
404 for (i = 0;r_speeds_string[i];i++)
405 if (r_speeds_string[i] == '\n')
407 y = vid.conheight - sb_lines - lines * 8;
409 DrawQ_Fill(0, y, vid.conwidth, lines * 8, 0, 0, 0, 0.5, 0);
410 while (r_speeds_string[i])
413 while (r_speeds_string[i] && r_speeds_string[i] != '\n')
416 DrawQ_String(0, y, r_speeds_string + j, i - j, 8, 8, 1, 1, 1, 1, 0);
417 if (r_speeds_string[i] == '\n')
431 void SCR_SizeUp_f (void)
433 Cvar_SetValue ("viewsize",scr_viewsize.value+10);
444 void SCR_SizeDown_f (void)
446 Cvar_SetValue ("viewsize",scr_viewsize.value-10);
449 void CL_Screen_Init(void)
451 Cvar_RegisterVariable (&scr_fov);
452 Cvar_RegisterVariable (&scr_viewsize);
453 Cvar_RegisterVariable (&scr_conspeed);
454 Cvar_RegisterVariable (&scr_conalpha);
455 Cvar_RegisterVariable (&scr_conbrightness);
456 Cvar_RegisterVariable (&scr_conforcewhiledisconnected);
457 Cvar_RegisterVariable (&scr_showram);
458 Cvar_RegisterVariable (&scr_showturtle);
459 Cvar_RegisterVariable (&scr_showpause);
460 Cvar_RegisterVariable (&scr_centertime);
461 Cvar_RegisterVariable (&scr_printspeed);
462 Cvar_RegisterVariable (&vid_conwidth);
463 Cvar_RegisterVariable (&vid_conheight);
464 Cvar_RegisterVariable (&vid_pixelaspect);
465 Cvar_RegisterVariable (&scr_screenshot_jpeg);
466 Cvar_RegisterVariable (&scr_screenshot_jpeg_quality);
467 Cvar_RegisterVariable (&scr_screenshot_gamma);
468 Cvar_RegisterVariable (&cl_capturevideo);
469 Cvar_RegisterVariable (&cl_capturevideo_fps);
470 Cvar_RegisterVariable (&cl_capturevideo_rawrgb);
471 Cvar_RegisterVariable (&cl_capturevideo_rawyv12);
472 Cvar_RegisterVariable (&r_textshadow);
473 Cvar_RegisterVariable (&r_letterbox);
475 Cmd_AddCommand ("sizeup",SCR_SizeUp_f);
476 Cmd_AddCommand ("sizedown",SCR_SizeDown_f);
477 Cmd_AddCommand ("screenshot",SCR_ScreenShot_f);
478 Cmd_AddCommand ("envmap", R_Envmap_f);
480 scr_initialized = true;
483 void DrawQ_Clear(void)
485 r_refdef.drawqueuesize = 0;
488 static int picelements[6] = {0, 1, 2, 0, 2, 3};
489 void DrawQ_Pic(float x, float y, char *picname, float width, float height, float red, float green, float blue, float alpha, int flags)
491 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);
494 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)
499 if (alpha < (1.0f / 255.0f))
502 len = strlen(string);
504 for (len = 0;len < maxlen && string[len];len++);
505 for (;len > 0 && string[0] == ' ';string++, x += scalex, len--);
506 for (;len > 0 && string[len - 1] == ' ';len--);
509 if (x >= vid.conwidth || y >= vid.conheight || x < (-scalex * len) || y < (-scaley))
511 size = sizeof(*dq) + ((len + 1 + 3) & ~3);
512 if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
514 red = bound(0, red, 1);
515 green = bound(0, green, 1);
516 blue = bound(0, blue, 1);
517 alpha = bound(0, alpha, 1);
518 dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
520 dq->command = DRAWQUEUE_STRING;
522 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));
527 out = (char *)(dq + 1);
528 memcpy(out, string, len);
530 r_refdef.drawqueuesize += dq->size;
533 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)
535 if (r_textshadow.integer)
536 DrawQ_String_Real(x+scalex*0.25,y+scaley*0.25,string,maxlen,scalex,scaley,0,0,0,alpha*0.8,flags);
538 DrawQ_String_Real(x,y,string,maxlen,scalex,scaley,red,green,blue,alpha,flags);
541 void DrawQ_Fill (float x, float y, float w, float h, float red, float green, float blue, float alpha, int flags)
543 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);
546 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)
550 drawqueuemesh_t mesh;
551 memset(&mesh, 0, sizeof(mesh));
552 if (picname && picname[0])
554 pic = Draw_CachePic(picname, false);
558 height = pic->height;
559 mesh.texture = pic->tex;
561 mesh.num_triangles = 2;
562 mesh.num_vertices = 4;
563 mesh.data_element3i = picelements;
564 mesh.data_vertex3f = floats;
565 mesh.data_texcoord2f = floats + 12;
566 mesh.data_color4f = floats + 20;
567 memset(floats, 0, sizeof(floats));
568 mesh.data_vertex3f[0] = mesh.data_vertex3f[9] = x;
569 mesh.data_vertex3f[1] = mesh.data_vertex3f[4] = y;
570 mesh.data_vertex3f[3] = mesh.data_vertex3f[6] = x + width;
571 mesh.data_vertex3f[7] = mesh.data_vertex3f[10] = y + height;
572 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;
573 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;
574 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;
575 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;
576 DrawQ_Mesh (&mesh, flags);
579 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
586 size += sizeof(drawqueuemesh_t);
587 size += sizeof(int[3]) * mesh->num_triangles;
588 size += sizeof(float[3]) * mesh->num_vertices;
589 size += sizeof(float[2]) * mesh->num_vertices;
590 size += sizeof(float[4]) * mesh->num_vertices;
591 if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
593 dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
595 dq->command = DRAWQUEUE_MESH;
602 p = (void *)(dq + 1);
603 m = p;p = (qbyte*)p + sizeof(drawqueuemesh_t);
604 m->num_triangles = mesh->num_triangles;
605 m->num_vertices = mesh->num_vertices;
606 m->texture = mesh->texture;
607 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]);
608 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]);
609 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]);
610 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]);
611 r_refdef.drawqueuesize += dq->size;
614 void DrawQ_SetClipArea(float x, float y, float width, float height)
617 if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
619 Con_DPrint("DrawQueue full !\n");
622 dq = (void*) (r_refdef.drawqueue + r_refdef.drawqueuesize);
623 dq->size = sizeof(*dq);
624 dq->command = DRAWQUEUE_SETCLIP;
632 r_refdef.drawqueuesize += dq->size;
635 void DrawQ_ResetClipArea(void)
638 if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
640 Con_DPrint("DrawQueue full !\n");
643 dq = (void*) (r_refdef.drawqueue + r_refdef.drawqueuesize);
644 dq->size = sizeof(*dq);
645 dq->command = DRAWQUEUE_RESETCLIP;
653 r_refdef.drawqueuesize += dq->size;
661 void SCR_ScreenShot_f (void)
663 static int shotnumber;
664 static char oldname[MAX_QPATH];
665 char base[MAX_QPATH];
666 char filename[MAX_QPATH];
670 qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
672 sprintf (base, "screenshots/%s", scr_screenshot_name.string);
674 if (strcmp (oldname, scr_screenshot_name.string))
676 sprintf(oldname, "%s", scr_screenshot_name.string);
680 // find a file name to save it to
681 for (;shotnumber < 1000000;shotnumber++)
682 if (!FS_SysFileExists(va("%s/%s%06d.tga", fs_gamedir, base, shotnumber)) && !FS_SysFileExists(va("%s/%s%06d.jpg", fs_gamedir, base, shotnumber)))
684 if (shotnumber >= 1000000)
686 Con_Print("SCR_ScreenShot_f: Couldn't create the image file\n");
690 sprintf(filename, "%s%06d.%s", base, shotnumber, jpeg ? "jpg" : "tga");
692 buffer1 = Mem_Alloc(tempmempool, vid.realwidth * vid.realheight * 3);
693 buffer2 = Mem_Alloc(tempmempool, vid.realwidth * vid.realheight * 3);
694 buffer3 = Mem_Alloc(tempmempool, vid.realwidth * vid.realheight * 3 + 18);
696 if (SCR_ScreenShot (filename, buffer1, buffer2, buffer3, vid.realx, vid.realy, vid.realwidth, vid.realheight, false, false, false, jpeg))
697 Con_Printf("Wrote %s\n", filename);
699 Con_Printf("unable to write %s\n", filename);
708 typedef enum capturevideoformat_e
710 CAPTUREVIDEOFORMAT_TARGA,
711 CAPTUREVIDEOFORMAT_JPEG,
712 CAPTUREVIDEOFORMAT_RAWRGB,
713 CAPTUREVIDEOFORMAT_RAWYV12
715 capturevideoformat_t;
717 qboolean cl_capturevideo_active = false;
718 capturevideoformat_t cl_capturevideo_format;
719 static double cl_capturevideo_starttime = 0;
720 double cl_capturevideo_framerate = 0;
721 static int cl_capturevideo_soundrate = 0;
722 static int cl_capturevideo_frame = 0;
723 static qbyte *cl_capturevideo_buffer = NULL;
724 static qfile_t *cl_capturevideo_videofile = NULL;
725 static qfile_t *cl_capturevideo_soundfile = NULL;
726 static short cl_capturevideo_rgbtoyuvscaletable[3][3][256];
727 static unsigned char cl_capturevideo_yuvnormalizetable[3][256];
728 //static unsigned char cl_capturevideo_rgbgammatable[3][256];
730 void SCR_CaptureVideo_BeginVideo(void)
735 if (cl_capturevideo_active)
737 // soundrate is figured out on the first SoundFrame
738 cl_capturevideo_active = true;
739 cl_capturevideo_starttime = Sys_DoubleTime();
740 cl_capturevideo_framerate = bound(1, cl_capturevideo_fps.value, 1000);
741 cl_capturevideo_soundrate = 0;
742 cl_capturevideo_frame = 0;
743 cl_capturevideo_buffer = Mem_Alloc(tempmempool, vid.realwidth * vid.realheight * (3+3+3) + 18);
744 gamma = 1.0/scr_screenshot_gamma.value;
747 for (i = 0;i < 256;i++)
749 unsigned char j = (unsigned char)bound(0, 255*pow(i/255.0, gamma), 255);
750 cl_capturevideo_rgbgammatable[0][i] = j;
751 cl_capturevideo_rgbgammatable[1][i] = j;
752 cl_capturevideo_rgbgammatable[2][i] = j;
756 R = Y + 1.4075 * (Cr - 128);
757 G = Y + -0.3455 * (Cb - 128) + -0.7169 * (Cr - 128);
758 B = Y + 1.7790 * (Cb - 128);
759 Y = R * .299 + G * .587 + B * .114;
760 Cb = R * -.169 + G * -.332 + B * .500 + 128.;
761 Cr = R * .500 + G * -.419 + B * -.0813 + 128.;
763 for (i = 0;i < 256;i++)
765 g = 255*pow(i/255.0, gamma);
766 // Y weights from RGB
767 cl_capturevideo_rgbtoyuvscaletable[0][0][i] = (short)(g * 0.299);
768 cl_capturevideo_rgbtoyuvscaletable[0][1][i] = (short)(g * 0.587);
769 cl_capturevideo_rgbtoyuvscaletable[0][2][i] = (short)(g * 0.114);
770 // Cb weights from RGB
771 cl_capturevideo_rgbtoyuvscaletable[1][0][i] = (short)(g * -0.169);
772 cl_capturevideo_rgbtoyuvscaletable[1][1][i] = (short)(g * -0.332);
773 cl_capturevideo_rgbtoyuvscaletable[1][2][i] = (short)(g * 0.500);
774 // Cr weights from RGB
775 cl_capturevideo_rgbtoyuvscaletable[2][0][i] = (short)(g * 0.500);
776 cl_capturevideo_rgbtoyuvscaletable[2][1][i] = (short)(g * -0.419);
777 cl_capturevideo_rgbtoyuvscaletable[2][2][i] = (short)(g * -0.0813);
778 // range reduction of YCbCr to valid signal range
779 cl_capturevideo_yuvnormalizetable[0][i] = 16 + i * (236-16) / 256;
780 cl_capturevideo_yuvnormalizetable[1][i] = 16 + i * (240-16) / 256;
781 cl_capturevideo_yuvnormalizetable[2][i] = 16 + i * (240-16) / 256;
784 if (cl_capturevideo_rawrgb.integer)
786 cl_capturevideo_format = CAPTUREVIDEOFORMAT_RAWRGB;
787 cl_capturevideo_videofile = FS_Open ("video/dpvideo.rgb", "wb", false, true);
789 else if (cl_capturevideo_rawyv12.integer)
791 cl_capturevideo_format = CAPTUREVIDEOFORMAT_RAWYV12;
792 cl_capturevideo_videofile = FS_Open ("video/dpvideo.yv12", "wb", false, true);
794 else if (scr_screenshot_jpeg.integer)
796 cl_capturevideo_format = CAPTUREVIDEOFORMAT_JPEG;
797 cl_capturevideo_videofile = NULL;
801 cl_capturevideo_format = CAPTUREVIDEOFORMAT_TARGA;
802 cl_capturevideo_videofile = NULL;
805 cl_capturevideo_soundfile = FS_Open ("video/dpvideo.wav", "wb", false, true);
807 // wave header will be filled out when video ends
809 FS_Write (cl_capturevideo_soundfile, out, 44);
812 void SCR_CaptureVideo_EndVideo(void)
816 if (!cl_capturevideo_active)
818 cl_capturevideo_active = false;
820 if (cl_capturevideo_videofile)
822 FS_Close(cl_capturevideo_videofile);
823 cl_capturevideo_videofile = NULL;
826 // finish the wave file
827 if (cl_capturevideo_soundfile)
829 i = FS_Tell (cl_capturevideo_soundfile);
830 //"RIFF", (int) unknown (chunk size), "WAVE",
831 //"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
832 //"data", (int) unknown (chunk size)
833 memcpy (out, "RIFF****WAVEfmt \x10\x00\x00\x00\x01\x00\x02\x00********\x04\x00\x10\0data****", 44);
834 // the length of the whole RIFF chunk
837 out[5] = (n >> 8) & 0xFF;
838 out[6] = (n >> 16) & 0xFF;
839 out[7] = (n >> 24) & 0xFF;
841 n = cl_capturevideo_soundrate;
842 out[24] = (n) & 0xFF;
843 out[25] = (n >> 8) & 0xFF;
844 out[26] = (n >> 16) & 0xFF;
845 out[27] = (n >> 24) & 0xFF;
846 // bytes per second (rate * channels * bytes per channel)
847 n = cl_capturevideo_soundrate * 2 * 2;
848 out[28] = (n) & 0xFF;
849 out[29] = (n >> 8) & 0xFF;
850 out[30] = (n >> 16) & 0xFF;
851 out[31] = (n >> 24) & 0xFF;
852 // the length of the data chunk
854 out[40] = (n) & 0xFF;
855 out[41] = (n >> 8) & 0xFF;
856 out[42] = (n >> 16) & 0xFF;
857 out[43] = (n >> 24) & 0xFF;
858 FS_Seek (cl_capturevideo_soundfile, 0, SEEK_SET);
859 FS_Write (cl_capturevideo_soundfile, out, 44);
860 FS_Close (cl_capturevideo_soundfile);
861 cl_capturevideo_soundfile = NULL;
864 if (cl_capturevideo_buffer)
866 Mem_Free (cl_capturevideo_buffer);
867 cl_capturevideo_buffer = NULL;
870 cl_capturevideo_starttime = 0;
871 cl_capturevideo_framerate = 0;
872 cl_capturevideo_frame = 0;
875 qboolean SCR_CaptureVideo_VideoFrame(int newframenum)
877 int x = vid.realx, y = vid.realy, width = vid.realwidth, height = vid.realheight;
878 unsigned char *b, *out;
880 int outoffset = (width/2)*(height/2);
881 //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);
882 // speed is critical here, so do saving as directly as possible
883 switch (cl_capturevideo_format)
885 case CAPTUREVIDEOFORMAT_RAWYV12:
886 // FIXME: width/height must be multiple of 2, enforce this?
887 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
889 // process one line at a time, and CbCr every other line at 2 pixel intervals
890 for (y = 0;y < height;y++)
893 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++)
894 *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]]];
897 // 2x2 Cb and Cr planes
899 // low quality, no averaging
900 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++)
903 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];
905 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];
908 // high quality, averaging
909 int inpitch = width*3;
910 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++)
912 int blockr, blockg, blockb;
913 blockr = (b[0] + b[3] + b[inpitch+0] + b[inpitch+3]) >> 2;
914 blockg = (b[1] + b[4] + b[inpitch+1] + b[inpitch+4]) >> 2;
915 blockb = (b[2] + b[5] + b[inpitch+2] + b[inpitch+5]) >> 2;
917 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];
919 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];
924 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
925 if (!FS_Write (cl_capturevideo_videofile, cl_capturevideo_buffer + width*height*3, width*height+(width/2)*(height/2)*2))
928 case CAPTUREVIDEOFORMAT_RAWRGB:
929 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
931 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
932 if (!FS_Write (cl_capturevideo_videofile, cl_capturevideo_buffer, width*height*3))
935 case CAPTUREVIDEOFORMAT_JPEG:
936 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
938 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
940 sprintf(filename, "video/dp%06d.jpg", cl_capturevideo_frame);
941 if (!JPEG_SaveImage_preflipped (filename, width, height, cl_capturevideo_buffer))
945 case CAPTUREVIDEOFORMAT_TARGA:
946 //return Image_WriteTGARGB_preflipped (filename, width, height, cl_capturevideo_buffer, cl_capturevideo_buffer + vid.realwidth * vid.realheight * 3, );
947 memset (cl_capturevideo_buffer, 0, 18);
948 cl_capturevideo_buffer[2] = 2; // uncompressed type
949 cl_capturevideo_buffer[12] = (width >> 0) & 0xFF;
950 cl_capturevideo_buffer[13] = (width >> 8) & 0xFF;
951 cl_capturevideo_buffer[14] = (height >> 0) & 0xFF;
952 cl_capturevideo_buffer[15] = (height >> 8) & 0xFF;
953 cl_capturevideo_buffer[16] = 24; // pixel size
954 qglReadPixels (x, y, width, height, GL_BGR, GL_UNSIGNED_BYTE, cl_capturevideo_buffer + 18);
956 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
958 sprintf(filename, "video/dp%06d.tga", cl_capturevideo_frame);
959 if (!FS_WriteFile (filename, cl_capturevideo_buffer, width*height*3 + 18))
968 void SCR_CaptureVideo_SoundFrame(qbyte *bufstereo16le, size_t length, int rate)
970 cl_capturevideo_soundrate = rate;
971 if (FS_Write (cl_capturevideo_soundfile, bufstereo16le, 4 * length) < 4 * length)
973 Cvar_SetValueQuick(&cl_capturevideo, 0);
974 Con_Printf("video sound saving failed on frame %i, out of disk space? stopping video capture.\n", cl_capturevideo_frame);
975 SCR_CaptureVideo_EndVideo();
979 void SCR_CaptureVideo(void)
982 if (cl_capturevideo.integer && r_render.integer)
984 if (!cl_capturevideo_active)
985 SCR_CaptureVideo_BeginVideo();
986 if (cl_capturevideo_framerate != cl_capturevideo_fps.value)
988 Con_Printf("You can not change the video framerate while recording a video.\n");
989 Cvar_SetValueQuick(&cl_capturevideo_fps, cl_capturevideo_framerate);
991 newframenum = (Sys_DoubleTime() - cl_capturevideo_starttime) * cl_capturevideo_framerate;
992 // if falling behind more than one second, stop
993 if (newframenum - cl_capturevideo_frame > (int)ceil(cl_capturevideo_framerate))
995 Cvar_SetValueQuick(&cl_capturevideo, 0);
996 Con_Printf("video saving failed on frame %i, your machine is too slow for this capture speed.\n", cl_capturevideo_frame);
997 SCR_CaptureVideo_EndVideo();
1001 if (!SCR_CaptureVideo_VideoFrame(newframenum))
1003 Cvar_SetValueQuick(&cl_capturevideo, 0);
1004 Con_Printf("video saving failed on frame %i, out of disk space? stopping video capture.\n", cl_capturevideo_frame);
1005 SCR_CaptureVideo_EndVideo();
1008 else if (cl_capturevideo_active)
1009 SCR_CaptureVideo_EndVideo();
1016 Grab six views for environment mapping tests
1023 qboolean flipx, flipy, flipdiagonaly;
1027 {{ 0, 0, 0}, "rt", true, false, false},
1028 {{ 0, 90, 0}, "ft", true, false, false},
1029 {{ 0, 180, 0}, "lf", true, false, false},
1030 {{ 0, 270, 0}, "bk", true, false, false},
1031 {{-90, 180, 0}, "up", false, true, false},
1032 {{ 90, 180, 0}, "dn", false, true, false},
1034 {{ 0, 0, 0}, "px", true, true, true},
1035 {{ 0, 90, 0}, "py", false, true, false},
1036 {{ 0, 180, 0}, "nx", false, false, true},
1037 {{ 0, 270, 0}, "ny", true, false, false},
1038 {{-90, 180, 0}, "pz", false, false, true},
1039 {{ 90, 180, 0}, "nz", false, false, true}
1042 static void R_Envmap_f (void)
1045 char filename[256], basename[256];
1050 if (Cmd_Argc() != 3)
1052 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");
1056 strlcpy (basename, Cmd_Argv(1), sizeof (basename));
1057 size = atoi(Cmd_Argv(2));
1058 if (size != 128 && size != 256 && size != 512 && size != 1024)
1060 Con_Print("envmap: size must be one of 128, 256, 512, or 1024\n");
1063 if (size > vid.realwidth || size > vid.realheight)
1065 Con_Print("envmap: your resolution is not big enough to render that size\n");
1073 r_refdef.width = size;
1074 r_refdef.height = size;
1076 r_refdef.fov_x = 90;
1077 r_refdef.fov_y = 90;
1079 buffer1 = Mem_Alloc(tempmempool, size * size * 3);
1080 buffer2 = Mem_Alloc(tempmempool, size * size * 3);
1081 buffer3 = Mem_Alloc(tempmempool, size * size * 3 + 18);
1083 for (j = 0;j < 12;j++)
1085 sprintf(filename, "env/%s%s.tga", basename, envmapinfo[j].name);
1086 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);
1091 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);
1101 //=============================================================================
1103 // LordHavoc: SHOWLMP stuff
1104 #define SHOWLMP_MAXLABELS 256
1105 typedef struct showlmp_s
1115 showlmp_t showlmp[SHOWLMP_MAXLABELS];
1117 void SHOWLMP_decodehide(void)
1121 lmplabel = MSG_ReadString();
1122 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1123 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
1125 showlmp[i].isactive = false;
1130 void SHOWLMP_decodeshow(void)
1133 qbyte lmplabel[256], picname[256];
1135 strlcpy (lmplabel,MSG_ReadString(), sizeof (lmplabel));
1136 strlcpy (picname, MSG_ReadString(), sizeof (picname));
1137 if (gamemode == GAME_NEHAHRA) // LordHavoc: nasty old legacy junk
1144 x = MSG_ReadShort();
1145 y = MSG_ReadShort();
1148 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1149 if (showlmp[i].isactive)
1151 if (strcmp(showlmp[i].label, lmplabel) == 0)
1154 break; // drop out to replace it
1157 else if (k < 0) // find first empty one to replace
1160 return; // none found to replace
1161 // change existing one
1162 showlmp[k].isactive = true;
1163 strlcpy (showlmp[k].label, lmplabel, sizeof (showlmp[k].label));
1164 strlcpy (showlmp[k].pic, picname, sizeof (showlmp[k].pic));
1169 void SHOWLMP_drawall(void)
1172 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1173 if (showlmp[i].isactive)
1174 DrawQ_Pic(showlmp[i].x, showlmp[i].y, showlmp[i].pic, 0, 0, 1, 1, 1, 1, 0);
1177 void SHOWLMP_clear(void)
1180 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1181 showlmp[i].isactive = false;
1184 void CL_SetupScreenSize(void)
1186 float conwidth, conheight;
1188 VID_GetWindowSize (&vid.realx, &vid.realy, &vid.realwidth, &vid.realheight);
1190 VID_UpdateGamma(false);
1192 conwidth = bound(320, vid_conwidth.value, 2048);
1193 conheight = bound(200, vid_conheight.value, 1536);
1194 if (vid_conwidth.value != conwidth)
1195 Cvar_SetValue("vid_conwidth", conwidth);
1196 if (vid_conheight.value != conheight)
1197 Cvar_SetValue("vid_conheight", conheight);
1199 vid.conwidth = vid_conwidth.integer;
1200 vid.conheight = vid_conheight.integer;
1202 /* if (vid.realheight > 240)
1204 vid.conheight = (vid.realheight - 240) * scr_2dresolution.value + 240;
1205 vid.conheight = bound(240, vid.conheight, vid.realheight);
1208 vid.conheight = 240;*/
1210 SCR_SetUpToDrawConsole();
1213 extern void R_Shadow_EditLights_DrawSelectedLightProperties(void);
1214 void CL_UpdateScreen(void)
1216 if (!scr_initialized || !con_initialized || vid_hidden)
1217 return; // not initialized yet
1219 // don't allow cheats in multiplayer
1220 if (!cl.islocalgame && cl.worldmodel)
1222 if (r_fullbright.integer != 0)
1223 Cvar_Set ("r_fullbright", "0");
1224 if (r_ambient.value != 0)
1225 Cvar_Set ("r_ambient", "0");
1229 if (scr_viewsize.value < 30)
1230 Cvar_Set ("viewsize","30");
1231 if (scr_viewsize.value > 120)
1232 Cvar_Set ("viewsize","120");
1234 // bound field of view
1235 if (scr_fov.value < 1)
1236 Cvar_Set ("fov","1");
1237 if (scr_fov.value > 170)
1238 Cvar_Set ("fov","170");
1240 // intermission is always full screen
1241 if (cl.intermission)
1245 if (scr_viewsize.value >= 120)
1246 sb_lines = 0; // no status bar at all
1247 else if (scr_viewsize.value >= 110)
1248 sb_lines = 24; // no inventory
1253 r_refdef.colormask[0] = 1;
1254 r_refdef.colormask[1] = 1;
1255 r_refdef.colormask[2] = 1;
1259 if (cls.signon == SIGNONS)
1260 R_TimeReport("other");
1262 CL_SetupScreenSize();
1266 if (cls.signon == SIGNONS)
1267 R_TimeReport("setup");
1269 //FIXME: force menu if nothing else to look at?
1270 //if (key_dest == key_game && cls.signon != SIGNONS && cls.state == ca_disconnected)
1272 if (cls.signon == SIGNONS)
1277 if (!r_letterbox.value)
1280 SCR_CheckDrawCenterString();
1286 if (cls.signon == SIGNONS)
1290 R_TimeReport_Start();
1292 R_Shadow_EditLights_DrawSelectedLightProperties();
1299 void CL_Screen_NewMap(void)