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");
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);
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;
746 for (i = 0;i < 256;i++)
748 j = (unsigned char)bound(0, 255*pow(i/255.0, gamma), 255);
749 cl_capturevideo_rgbgammatable[0][i] = j;
750 cl_capturevideo_rgbgammatable[1][i] = j;
751 cl_capturevideo_rgbgammatable[2][i] = j;
754 R = Y + 1.4075 * (Cr - 128);
755 G = Y + -0.3455 * (Cb - 128) + -0.7169 * (Cr - 128);
756 B = Y + 1.7790 * (Cb - 128);
757 Y = R * .299 + G * .587 + B * .114;
758 Cb = R * -.169 + G * -.332 + B * .500 + 128.;
759 Cr = R * .500 + G * -.419 + B * -.0813 + 128.;
761 for (i = 0;i < 256;i++)
763 g = i;//255*pow(i/255.0, gamma);
764 // Y weights from RGB
765 cl_capturevideo_rgbtoyuvscaletable[0][0][i] = (short)(g * 0.299);
766 cl_capturevideo_rgbtoyuvscaletable[0][1][i] = (short)(g * 0.587);
767 cl_capturevideo_rgbtoyuvscaletable[0][2][i] = (short)(g * 0.114);
768 // Cb weights from RGB
769 cl_capturevideo_rgbtoyuvscaletable[1][0][i] = (short)(g * -0.169);
770 cl_capturevideo_rgbtoyuvscaletable[1][1][i] = (short)(g * -0.332);
771 cl_capturevideo_rgbtoyuvscaletable[1][2][i] = (short)(g * 0.500);
772 // Cr weights from RGB
773 cl_capturevideo_rgbtoyuvscaletable[2][0][i] = (short)(g * 0.500);
774 cl_capturevideo_rgbtoyuvscaletable[2][1][i] = (short)(g * -0.419);
775 cl_capturevideo_rgbtoyuvscaletable[2][2][i] = (short)(g * -0.0813);
776 // range reduction of YCbCr to valid signal range
777 cl_capturevideo_yuvnormalizetable[0][i] = 16 + i * (236-16) / 256;
778 cl_capturevideo_yuvnormalizetable[1][i] = 16 + i * (240-16) / 256;
779 cl_capturevideo_yuvnormalizetable[2][i] = 16 + i * (240-16) / 256;
782 if (cl_capturevideo_rawrgb.integer)
784 cl_capturevideo_format = CAPTUREVIDEOFORMAT_RAWRGB;
785 cl_capturevideo_videofile = FS_Open ("video/dpvideo.rgb", "wb", false);
787 else if (cl_capturevideo_rawyv12.integer)
789 cl_capturevideo_format = CAPTUREVIDEOFORMAT_RAWYV12;
790 cl_capturevideo_videofile = FS_Open ("video/dpvideo.yv12", "wb", false);
792 else if (scr_screenshot_jpeg.integer)
794 cl_capturevideo_format = CAPTUREVIDEOFORMAT_JPEG;
795 cl_capturevideo_videofile = NULL;
799 cl_capturevideo_format = CAPTUREVIDEOFORMAT_TARGA;
800 cl_capturevideo_videofile = NULL;
803 cl_capturevideo_soundfile = FS_Open ("video/dpvideo.wav", "wb", false);
805 // wave header will be filled out when video ends
807 FS_Write (cl_capturevideo_soundfile, out, 44);
810 void SCR_CaptureVideo_EndVideo(void)
814 if (!cl_capturevideo_active)
816 cl_capturevideo_active = false;
818 if (cl_capturevideo_videofile)
820 FS_Close(cl_capturevideo_videofile);
821 cl_capturevideo_videofile = NULL;
824 // finish the wave file
825 if (cl_capturevideo_soundfile)
827 i = FS_Tell (cl_capturevideo_soundfile);
828 //"RIFF", (int) unknown (chunk size), "WAVE",
829 //"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
830 //"data", (int) unknown (chunk size)
831 memcpy (out, "RIFF****WAVEfmt \x10\x00\x00\x00\x01\x00\x02\x00********\x04\x00\x10\0data****", 44);
832 // the length of the whole RIFF chunk
835 out[5] = (n >> 8) & 0xFF;
836 out[6] = (n >> 16) & 0xFF;
837 out[7] = (n >> 24) & 0xFF;
839 n = cl_capturevideo_soundrate;
840 out[24] = (n) & 0xFF;
841 out[25] = (n >> 8) & 0xFF;
842 out[26] = (n >> 16) & 0xFF;
843 out[27] = (n >> 24) & 0xFF;
844 // bytes per second (rate * channels * bytes per channel)
845 n = cl_capturevideo_soundrate * 2 * 2;
846 out[28] = (n) & 0xFF;
847 out[29] = (n >> 8) & 0xFF;
848 out[30] = (n >> 16) & 0xFF;
849 out[31] = (n >> 24) & 0xFF;
850 // the length of the data chunk
852 out[40] = (n) & 0xFF;
853 out[41] = (n >> 8) & 0xFF;
854 out[42] = (n >> 16) & 0xFF;
855 out[43] = (n >> 24) & 0xFF;
856 FS_Seek (cl_capturevideo_soundfile, 0, SEEK_SET);
857 FS_Write (cl_capturevideo_soundfile, out, 44);
858 FS_Close (cl_capturevideo_soundfile);
859 cl_capturevideo_soundfile = NULL;
862 if (cl_capturevideo_buffer)
864 Mem_Free (cl_capturevideo_buffer);
865 cl_capturevideo_buffer = NULL;
868 cl_capturevideo_starttime = 0;
869 cl_capturevideo_framerate = 0;
870 cl_capturevideo_frame = 0;
873 qboolean SCR_CaptureVideo_VideoFrame(int newframenum)
875 int x = vid.realx, y = vid.realy, width = vid.realwidth, height = vid.realheight;
876 unsigned char *b, *out;
878 int outoffset = (width/2)*(height/2);
879 //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);
880 // speed is critical here, so do saving as directly as possible
881 switch (cl_capturevideo_format)
883 case CAPTUREVIDEOFORMAT_RAWYV12:
884 // FIXME: width/height must be multiple of 2, enforce this?
885 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
887 // process one line at a time, and CbCr every other line at 2 pixel intervals
888 for (y = 0;y < height;y++)
891 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++)
892 *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]]];
895 // 2x2 Cb and Cr planes
897 // low quality, no averaging
898 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++)
901 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];
903 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];
906 // high quality, averaging
907 int inpitch = width*3;
908 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 int blockr, blockg, blockb;
911 blockr = (b[0] + b[3] + b[inpitch+0] + b[inpitch+3]) >> 2;
912 blockg = (b[1] + b[4] + b[inpitch+1] + b[inpitch+4]) >> 2;
913 blockb = (b[2] + b[5] + b[inpitch+2] + b[inpitch+5]) >> 2;
915 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];
917 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];
922 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
923 if (!FS_Write (cl_capturevideo_videofile, cl_capturevideo_buffer + width*height*3, width*height+(width/2)*(height/2)*2))
926 case CAPTUREVIDEOFORMAT_RAWRGB:
927 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
929 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
930 if (!FS_Write (cl_capturevideo_videofile, cl_capturevideo_buffer, width*height*3))
933 case CAPTUREVIDEOFORMAT_JPEG:
934 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
936 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
938 sprintf(filename, "video/dp%06d.jpg", cl_capturevideo_frame);
939 if (!JPEG_SaveImage_preflipped (filename, width, height, cl_capturevideo_buffer))
943 case CAPTUREVIDEOFORMAT_TARGA:
944 //return Image_WriteTGARGB_preflipped (filename, width, height, cl_capturevideo_buffer, cl_capturevideo_buffer + vid.realwidth * vid.realheight * 3, );
945 memset (cl_capturevideo_buffer, 0, 18);
946 cl_capturevideo_buffer[2] = 2; // uncompressed type
947 cl_capturevideo_buffer[12] = (width >> 0) & 0xFF;
948 cl_capturevideo_buffer[13] = (width >> 8) & 0xFF;
949 cl_capturevideo_buffer[14] = (height >> 0) & 0xFF;
950 cl_capturevideo_buffer[15] = (height >> 8) & 0xFF;
951 cl_capturevideo_buffer[16] = 24; // pixel size
952 qglReadPixels (x, y, width, height, GL_BGR, GL_UNSIGNED_BYTE, cl_capturevideo_buffer + 18);
954 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
956 sprintf(filename, "video/dp%06d.tga", cl_capturevideo_frame);
957 if (!FS_WriteFile (filename, cl_capturevideo_buffer, width*height*3 + 18))
966 void SCR_CaptureVideo_SoundFrame(qbyte *bufstereo16le, size_t length, int rate)
968 cl_capturevideo_soundrate = rate;
969 if (FS_Write (cl_capturevideo_soundfile, bufstereo16le, 4 * length) < 4 * length)
971 Cvar_SetValueQuick(&cl_capturevideo, 0);
972 Con_Printf("video sound saving failed on frame %i, out of disk space? stopping video capture.\n", cl_capturevideo_frame);
973 SCR_CaptureVideo_EndVideo();
977 void SCR_CaptureVideo(void)
980 if (cl_capturevideo.integer && r_render.integer)
982 if (!cl_capturevideo_active)
983 SCR_CaptureVideo_BeginVideo();
984 if (cl_capturevideo_framerate != cl_capturevideo_fps.value)
986 Con_Printf("You can not change the video framerate while recording a video.\n");
987 Cvar_SetValueQuick(&cl_capturevideo_fps, cl_capturevideo_framerate);
989 newframenum = (Sys_DoubleTime() - cl_capturevideo_starttime) * cl_capturevideo_framerate;
990 // if falling behind more than one second, stop
991 if (newframenum - cl_capturevideo_frame > (int)ceil(cl_capturevideo_framerate))
993 Cvar_SetValueQuick(&cl_capturevideo, 0);
994 Con_Printf("video saving failed on frame %i, your machine is too slow for this capture speed.\n", cl_capturevideo_frame);
995 SCR_CaptureVideo_EndVideo();
999 if (!SCR_CaptureVideo_VideoFrame(newframenum))
1001 Cvar_SetValueQuick(&cl_capturevideo, 0);
1002 Con_Printf("video saving failed on frame %i, out of disk space? stopping video capture.\n", cl_capturevideo_frame);
1003 SCR_CaptureVideo_EndVideo();
1006 else if (cl_capturevideo_active)
1007 SCR_CaptureVideo_EndVideo();
1014 Grab six views for environment mapping tests
1021 qboolean flipx, flipy, flipdiagonaly;
1025 {{ 0, 0, 0}, "rt", true, false, false},
1026 {{ 0, 90, 0}, "ft", true, false, false},
1027 {{ 0, 180, 0}, "lf", true, false, false},
1028 {{ 0, 270, 0}, "bk", true, false, false},
1029 {{-90, 180, 0}, "up", false, true, false},
1030 {{ 90, 180, 0}, "dn", false, true, false},
1032 {{ 0, 0, 0}, "px", true, true, true},
1033 {{ 0, 90, 0}, "py", false, true, false},
1034 {{ 0, 180, 0}, "nx", false, false, true},
1035 {{ 0, 270, 0}, "ny", true, false, false},
1036 {{-90, 180, 0}, "pz", false, false, true},
1037 {{ 90, 180, 0}, "nz", false, false, true}
1040 static void R_Envmap_f (void)
1043 char filename[256], basename[256];
1048 if (Cmd_Argc() != 3)
1050 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");
1054 strlcpy (basename, Cmd_Argv(1), sizeof (basename));
1055 size = atoi(Cmd_Argv(2));
1056 if (size != 128 && size != 256 && size != 512 && size != 1024)
1058 Con_Print("envmap: size must be one of 128, 256, 512, or 1024\n");
1061 if (size > vid.realwidth || size > vid.realheight)
1063 Con_Print("envmap: your resolution is not big enough to render that size\n");
1071 r_refdef.width = size;
1072 r_refdef.height = size;
1074 r_refdef.fov_x = 90;
1075 r_refdef.fov_y = 90;
1077 buffer1 = Mem_Alloc(tempmempool, size * size * 3);
1078 buffer2 = Mem_Alloc(tempmempool, size * size * 3);
1079 buffer3 = Mem_Alloc(tempmempool, size * size * 3 + 18);
1081 for (j = 0;j < 12;j++)
1083 sprintf(filename, "env/%s%s.tga", basename, envmapinfo[j].name);
1084 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);
1089 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);
1099 //=============================================================================
1101 // LordHavoc: SHOWLMP stuff
1102 #define SHOWLMP_MAXLABELS 256
1103 typedef struct showlmp_s
1113 showlmp_t showlmp[SHOWLMP_MAXLABELS];
1115 void SHOWLMP_decodehide(void)
1119 lmplabel = MSG_ReadString();
1120 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1121 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
1123 showlmp[i].isactive = false;
1128 void SHOWLMP_decodeshow(void)
1131 qbyte lmplabel[256], picname[256];
1133 strlcpy (lmplabel,MSG_ReadString(), sizeof (lmplabel));
1134 strlcpy (picname, MSG_ReadString(), sizeof (picname));
1135 if (gamemode == GAME_NEHAHRA) // LordHavoc: nasty old legacy junk
1142 x = MSG_ReadShort();
1143 y = MSG_ReadShort();
1146 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1147 if (showlmp[i].isactive)
1149 if (strcmp(showlmp[i].label, lmplabel) == 0)
1152 break; // drop out to replace it
1155 else if (k < 0) // find first empty one to replace
1158 return; // none found to replace
1159 // change existing one
1160 showlmp[k].isactive = true;
1161 strlcpy (showlmp[k].label, lmplabel, sizeof (showlmp[k].label));
1162 strlcpy (showlmp[k].pic, picname, sizeof (showlmp[k].pic));
1167 void SHOWLMP_drawall(void)
1170 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1171 if (showlmp[i].isactive)
1172 DrawQ_Pic(showlmp[i].x, showlmp[i].y, showlmp[i].pic, 0, 0, 1, 1, 1, 1, 0);
1175 void SHOWLMP_clear(void)
1178 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1179 showlmp[i].isactive = false;
1182 void CL_SetupScreenSize(void)
1184 float conwidth, conheight;
1186 VID_GetWindowSize (&vid.realx, &vid.realy, &vid.realwidth, &vid.realheight);
1188 VID_UpdateGamma(false);
1190 conwidth = bound(320, vid_conwidth.value, 2048);
1191 conheight = bound(200, vid_conheight.value, 1536);
1192 if (vid_conwidth.value != conwidth)
1193 Cvar_SetValue("vid_conwidth", conwidth);
1194 if (vid_conheight.value != conheight)
1195 Cvar_SetValue("vid_conheight", conheight);
1197 vid.conwidth = vid_conwidth.integer;
1198 vid.conheight = vid_conheight.integer;
1200 /* if (vid.realheight > 240)
1202 vid.conheight = (vid.realheight - 240) * scr_2dresolution.value + 240;
1203 vid.conheight = bound(240, vid.conheight, vid.realheight);
1206 vid.conheight = 240;*/
1208 SCR_SetUpToDrawConsole();
1211 extern void R_Shadow_EditLights_DrawSelectedLightProperties(void);
1212 void CL_UpdateScreen(void)
1214 if (!scr_initialized || !con_initialized || vid_hidden)
1215 return; // not initialized yet
1217 // don't allow cheats in multiplayer
1218 if (!cl.islocalgame && cl.worldmodel)
1220 if (r_fullbright.integer != 0)
1221 Cvar_Set ("r_fullbright", "0");
1222 if (r_ambient.value != 0)
1223 Cvar_Set ("r_ambient", "0");
1227 if (scr_viewsize.value < 30)
1228 Cvar_Set ("viewsize","30");
1229 if (scr_viewsize.value > 120)
1230 Cvar_Set ("viewsize","120");
1232 // bound field of view
1233 if (scr_fov.value < 1)
1234 Cvar_Set ("fov","1");
1235 if (scr_fov.value > 170)
1236 Cvar_Set ("fov","170");
1238 // intermission is always full screen
1239 if (cl.intermission)
1243 if (scr_viewsize.value >= 120)
1244 sb_lines = 0; // no status bar at all
1245 else if (scr_viewsize.value >= 110)
1246 sb_lines = 24; // no inventory
1251 r_refdef.colormask[0] = 1;
1252 r_refdef.colormask[1] = 1;
1253 r_refdef.colormask[2] = 1;
1257 if (cls.signon == SIGNONS)
1258 R_TimeReport("other");
1260 CL_SetupScreenSize();
1264 if (cls.signon == SIGNONS)
1265 R_TimeReport("setup");
1267 //FIXME: force menu if nothing else to look at?
1268 //if (key_dest == key_game && cls.signon != SIGNONS && cls.state == ca_disconnected)
1270 if (cls.signon == SIGNONS)
1275 if (!r_letterbox.value)
1278 SCR_CheckDrawCenterString();
1284 if (cls.signon == SIGNONS)
1288 R_TimeReport_Start();
1290 R_Shadow_EditLights_DrawSelectedLightProperties();
1297 void CL_Screen_NewMap(void)