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 qboolean scr_drawloading = false;
43 void DrawCrosshair(int num);
44 static void SCR_ScreenShot_f (void);
45 static void R_Envmap_f (void);
48 void R_ClearScreen(void);
51 ===============================================================================
55 ===============================================================================
58 char scr_centerstring[1024];
59 float scr_centertime_start; // for slow victory printing
60 float scr_centertime_off;
69 Called for important messages that should stay in the center of the screen
73 void SCR_CenterPrint(char *str)
75 strlcpy (scr_centerstring, str, sizeof (scr_centerstring));
76 scr_centertime_off = scr_centertime.value;
77 scr_centertime_start = cl.time;
79 // count the number of lines for centering
90 void SCR_DrawCenterString (void)
97 // the finale prints the characters one at a time
99 remaining = scr_printspeed.value * (cl.time - scr_centertime_start);
103 scr_erase_center = 0;
104 start = scr_centerstring;
106 if (scr_center_lines <= 4)
107 y = vid.conheight*0.35;
113 // scan the width of the line
114 for (l=0 ; l<vid.conwidth/8 ; l++)
115 if (start[l] == '\n' || !start[l])
117 x = (vid.conwidth - l*8)/2;
122 DrawQ_String(x, y, start, l, 8, 8, 1, 1, 1, 1, 0);
130 while (*start && *start != '\n')
135 start++; // skip the \n
139 void SCR_CheckDrawCenterString (void)
141 if (scr_center_lines > scr_erase_lines)
142 scr_erase_lines = scr_center_lines;
144 scr_centertime_off -= host_frametime;
146 // don't draw if this is a normal stats-screen intermission,
147 // only if it is not an intermission, or a finale intermission
148 if (cl.intermission == 1)
150 if (scr_centertime_off <= 0 && !cl.intermission)
152 if (key_dest != key_game)
155 SCR_DrawCenterString ();
163 void SCR_DrawTurtle (void)
167 if (cls.state != ca_connected)
170 if (!scr_showturtle.integer)
173 if (host_frametime < 0.1)
183 DrawQ_Pic (0, 0, "gfx/turtle.lmp", 0, 0, 1, 1, 1, 1, 0);
191 void SCR_DrawNet (void)
193 if (cls.state != ca_connected)
195 if (realtime - cl.last_received_message < 0.3)
197 if (cls.demoplayback)
200 DrawQ_Pic (64, 0, "gfx/net.lmp", 0, 0, 1, 1, 1, 1, 0);
208 void SCR_DrawPause (void)
212 if (cls.state != ca_connected)
215 if (!scr_showpause.integer) // turn off for screenshots
221 pic = Draw_CachePic ("gfx/pause.lmp");
222 DrawQ_Pic ((vid.conwidth - pic->width)/2, (vid.conheight - pic->height)/2, "gfx/pause.lmp", 0, 0, 1, 1, 1, 1, 0);
232 void SCR_DrawLoading (void)
236 pic = Draw_CachePic ("gfx/loading.lmp");
237 DrawQ_Pic ((vid.conwidth - pic->width)/2, (vid.conheight - pic->height)/2, "gfx/loading.lmp", 0, 0, 1, 1, 1, 1, 0);
242 //=============================================================================
247 SCR_SetUpToDrawConsole
250 void SCR_SetUpToDrawConsole (void)
254 if (key_dest == key_game && cls.signon != SIGNONS && scr_conforcewhiledisconnected.integer)
255 key_consoleactive |= KEY_CONSOLEACTIVE_FORCED;
257 key_consoleactive &= ~KEY_CONSOLEACTIVE_FORCED;
259 // decide on the height of the console
260 if (key_consoleactive & KEY_CONSOLEACTIVE_FORCED)
261 scr_conlines = vid.conheight; // full screen
262 else if (key_consoleactive & KEY_CONSOLEACTIVE_USER)
263 scr_conlines = vid.conheight/2; // half screen
265 scr_conlines = 0; // none visible
267 if (scr_conspeed.value)
269 if (scr_conlines < scr_con_current)
271 scr_con_current -= scr_conspeed.value*host_realframetime;
272 if (scr_conlines > scr_con_current)
273 scr_con_current = scr_conlines;
276 else if (scr_conlines > scr_con_current)
278 scr_con_current += scr_conspeed.value*host_realframetime;
279 if (scr_conlines < scr_con_current)
280 scr_con_current = scr_conlines;
284 scr_con_current = scr_conlines;
292 void SCR_DrawConsole (void)
295 Con_DrawConsole (scr_con_current);
299 if (key_dest == key_game || key_dest == key_message)
300 Con_DrawNotify (); // only draw notify in game
306 SCR_BeginLoadingPlaque
310 void SCR_BeginLoadingPlaque (void)
317 scr_drawloading = true;
319 scr_drawloading = true;
323 //=============================================================================
325 char r_speeds_string[1024];
326 int speedstringcount, r_timereport_active;
327 double r_timereport_temp = 0, r_timereport_current = 0, r_timereport_start = 0;
329 void R_TimeReport(char *desc)
335 if (!r_timereport_active)
338 r_timereport_temp = r_timereport_current;
339 r_timereport_current = Sys_DoubleTime();
340 t = (int) ((r_timereport_current - r_timereport_temp) * 1000000.0);
342 sprintf(tempbuf, "%8i %s", t, desc);
343 length = strlen(tempbuf);
345 tempbuf[length++] = ' ';
347 if (speedstringcount + length > (vid.conwidth / 8))
349 strcat(r_speeds_string, "\n");
350 speedstringcount = 0;
352 // skip the space at the beginning if it's the first on the line
353 if (speedstringcount == 0)
355 strcat(r_speeds_string, tempbuf + 1);
356 speedstringcount = length - 1;
360 strcat(r_speeds_string, tempbuf);
361 speedstringcount += length;
365 extern int c_rt_lights, c_rt_clears, c_rt_scissored;
366 extern int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris;
367 extern int c_rtcached_shadowmeshes, c_rtcached_shadowtris;
368 void R_TimeReport_Start(void)
370 r_timereport_active = r_speeds.integer && cls.signon == SIGNONS && cls.state == ca_connected;
371 r_speeds_string[0] = 0;
372 if (r_timereport_active)
374 speedstringcount = 0;
375 sprintf(r_speeds_string,
376 "org:'%+8.2f %+8.2f %+8.2f' dir:'%+2.3f %+2.3f %+2.3f'\n"
377 "world:%6i faces%6i nodes%6i leafs%6i dlitwalls\n"
378 "%5i models%5i bmodels%5i sprites%6i particles%4i dlights\n"
379 "%6i modeltris%6i meshs%6i meshtris\n",
380 r_vieworigin[0], r_vieworigin[1], r_vieworigin[2], r_viewforward[0], r_viewforward[1], r_viewforward[2],
381 c_faces, c_nodes, c_leafs, c_light_polys,
382 c_models, c_bmodels, c_sprites, c_particles, c_dlights,
383 c_alias_polys, c_meshs, c_meshelements / 3);
385 sprintf(r_speeds_string + strlen(r_speeds_string),
386 "realtime lighting:%4i lights%4i clears%4i scissored\n"
387 "dynamic: %6i shadowmeshes%6i shadowtris%6i lightmeshes%6i lighttris\n"
388 "precomputed: %6i shadowmeshes%6i shadowtris\n",
389 c_rt_lights, c_rt_clears, c_rt_scissored,
390 c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris,
391 c_rtcached_shadowmeshes, c_rtcached_shadowtris);
405 r_timereport_start = Sys_DoubleTime();
409 void R_TimeReport_End(void)
411 r_timereport_current = r_timereport_start;
412 R_TimeReport("total");
414 if (r_timereport_active)
418 for (i = 0;r_speeds_string[i];i++)
419 if (r_speeds_string[i] == '\n')
421 y = vid.conheight - sb_lines - lines * 8;
423 DrawQ_Fill(0, y, vid.conwidth, lines * 8, 0, 0, 0, 0.5, 0);
424 while (r_speeds_string[i])
427 while (r_speeds_string[i] && r_speeds_string[i] != '\n')
430 DrawQ_String(0, y, r_speeds_string + j, i - j, 8, 8, 1, 1, 1, 1, 0);
431 if (r_speeds_string[i] == '\n')
445 void SCR_SizeUp_f (void)
447 Cvar_SetValue ("viewsize",scr_viewsize.value+10);
458 void SCR_SizeDown_f (void)
460 Cvar_SetValue ("viewsize",scr_viewsize.value-10);
463 void CL_Screen_Init(void)
465 Cvar_RegisterVariable (&scr_fov);
466 Cvar_RegisterVariable (&scr_viewsize);
467 Cvar_RegisterVariable (&scr_conspeed);
468 Cvar_RegisterVariable (&scr_conalpha);
469 Cvar_RegisterVariable (&scr_conbrightness);
470 Cvar_RegisterVariable (&scr_conforcewhiledisconnected);
471 Cvar_RegisterVariable (&scr_showram);
472 Cvar_RegisterVariable (&scr_showturtle);
473 Cvar_RegisterVariable (&scr_showpause);
474 Cvar_RegisterVariable (&scr_centertime);
475 Cvar_RegisterVariable (&scr_printspeed);
476 Cvar_RegisterVariable (&vid_conwidth);
477 Cvar_RegisterVariable (&vid_conheight);
478 Cvar_RegisterVariable (&vid_pixelaspect);
479 Cvar_RegisterVariable (&scr_screenshot_jpeg);
480 Cvar_RegisterVariable (&scr_screenshot_jpeg_quality);
481 Cvar_RegisterVariable (&scr_screenshot_gamma);
482 Cvar_RegisterVariable (&cl_capturevideo);
483 Cvar_RegisterVariable (&cl_capturevideo_fps);
484 Cvar_RegisterVariable (&cl_capturevideo_rawrgb);
485 Cvar_RegisterVariable (&cl_capturevideo_rawyv12);
486 Cvar_RegisterVariable (&r_textshadow);
487 Cvar_RegisterVariable (&r_letterbox);
489 Cmd_AddCommand ("sizeup",SCR_SizeUp_f);
490 Cmd_AddCommand ("sizedown",SCR_SizeDown_f);
491 Cmd_AddCommand ("screenshot",SCR_ScreenShot_f);
492 Cmd_AddCommand ("envmap", R_Envmap_f);
494 scr_initialized = true;
497 void DrawQ_Clear(void)
499 r_refdef.drawqueuesize = 0;
502 static int picelements[6] = {0, 1, 2, 0, 2, 3};
503 void DrawQ_Pic(float x, float y, char *picname, float width, float height, float red, float green, float blue, float alpha, int flags)
505 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);
508 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)
513 if (alpha < (1.0f / 255.0f))
516 len = strlen(string);
518 for (len = 0;len < maxlen && string[len];len++);
519 for (;len > 0 && string[0] == ' ';string++, x += scalex, len--);
520 for (;len > 0 && string[len - 1] == ' ';len--);
523 if (x >= vid.conwidth || y >= vid.conheight || x < (-scalex * maxlen) || y < (-scaley))
525 size = sizeof(*dq) + ((len + 1 + 3) & ~3);
526 if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
528 red = bound(0, red, 1);
529 green = bound(0, green, 1);
530 blue = bound(0, blue, 1);
531 alpha = bound(0, alpha, 1);
532 dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
534 dq->command = DRAWQUEUE_STRING;
536 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));
541 out = (char *)(dq + 1);
542 memcpy(out, string, len);
544 r_refdef.drawqueuesize += dq->size;
547 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)
549 if (r_textshadow.integer)
550 DrawQ_String_Real(x+scalex*0.25,y+scaley*0.25,string,maxlen,scalex,scaley,0,0,0,alpha*0.8,flags);
552 DrawQ_String_Real(x,y,string,maxlen,scalex,scaley,red,green,blue,alpha,flags);
555 void DrawQ_Fill (float x, float y, float w, float h, float red, float green, float blue, float alpha, int flags)
557 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);
560 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)
564 drawqueuemesh_t mesh;
565 memset(&mesh, 0, sizeof(mesh));
566 if (picname && picname[0])
568 pic = Draw_CachePic(picname);
572 height = pic->height;
573 mesh.texture = pic->tex;
575 mesh.num_triangles = 2;
576 mesh.num_vertices = 4;
577 mesh.data_element3i = picelements;
578 mesh.data_vertex3f = floats;
579 mesh.data_texcoord2f = floats + 12;
580 mesh.data_color4f = floats + 20;
581 memset(floats, 0, sizeof(floats));
582 mesh.data_vertex3f[0] = mesh.data_vertex3f[9] = x;
583 mesh.data_vertex3f[1] = mesh.data_vertex3f[4] = y;
584 mesh.data_vertex3f[3] = mesh.data_vertex3f[6] = x + width;
585 mesh.data_vertex3f[7] = mesh.data_vertex3f[10] = y + height;
586 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;
587 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;
588 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;
589 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;
590 DrawQ_Mesh (&mesh, flags);
593 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
600 size += sizeof(drawqueuemesh_t);
601 size += sizeof(int[3]) * mesh->num_triangles;
602 size += sizeof(float[3]) * mesh->num_vertices;
603 size += sizeof(float[2]) * mesh->num_vertices;
604 size += sizeof(float[4]) * mesh->num_vertices;
605 if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
607 dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
609 dq->command = DRAWQUEUE_MESH;
616 p = (void *)(dq + 1);
617 m = p;p = (qbyte*)p + sizeof(drawqueuemesh_t);
618 m->num_triangles = mesh->num_triangles;
619 m->num_vertices = mesh->num_vertices;
620 m->texture = mesh->texture;
621 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]);
622 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]);
623 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]);
624 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]);
625 r_refdef.drawqueuesize += dq->size;
628 void DrawQ_SetClipArea(float x, float y, float width, float height)
631 if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
633 Con_DPrint("DrawQueue full !\n");
636 dq = (void*) (r_refdef.drawqueue + r_refdef.drawqueuesize);
637 dq->size = sizeof(*dq);
638 dq->command = DRAWQUEUE_SETCLIP;
646 r_refdef.drawqueuesize += dq->size;
649 void DrawQ_ResetClipArea(void)
652 if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
654 Con_DPrint("DrawQueue full !\n");
657 dq = (void*) (r_refdef.drawqueue + r_refdef.drawqueuesize);
658 dq->size = sizeof(*dq);
659 dq->command = DRAWQUEUE_RESETCLIP;
667 r_refdef.drawqueuesize += dq->size;
675 void SCR_ScreenShot_f (void)
677 static int shotnumber;
678 static char oldname[MAX_QPATH];
679 char base[MAX_QPATH];
680 char filename[MAX_QPATH];
684 qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
686 sprintf (base, "screenshots/%s", scr_screenshot_name.string);
688 if (strcmp (oldname, scr_screenshot_name.string))
690 sprintf(oldname, "%s", scr_screenshot_name.string);
694 // find a file name to save it to
695 for (;shotnumber < 1000000;shotnumber++)
696 if (!FS_SysFileExists(va("%s/%s%06d.tga", fs_gamedir, base, shotnumber)) && !FS_SysFileExists(va("%s/%s%06d.jpg", fs_gamedir, base, shotnumber)))
698 if (shotnumber >= 1000000)
700 Con_Print("SCR_ScreenShot_f: Couldn't create the image file\n");
704 sprintf(filename, "%s%06d.%s", base, shotnumber, jpeg ? "jpg" : "tga");
706 buffer1 = Mem_Alloc(tempmempool, vid.realwidth * vid.realheight * 3);
707 buffer2 = Mem_Alloc(tempmempool, vid.realwidth * vid.realheight * 3);
708 buffer3 = Mem_Alloc(tempmempool, vid.realwidth * vid.realheight * 3 + 18);
710 if (SCR_ScreenShot (filename, buffer1, buffer2, buffer3, vid.realx, vid.realy, vid.realwidth, vid.realheight, false, false, false, jpeg))
711 Con_Printf("Wrote %s\n", filename);
713 Con_Printf("unable to write %s\n", filename);
722 typedef enum capturevideoformat_e
724 CAPTUREVIDEOFORMAT_TARGA,
725 CAPTUREVIDEOFORMAT_JPEG,
726 CAPTUREVIDEOFORMAT_RAWRGB,
727 CAPTUREVIDEOFORMAT_RAWYV12
729 capturevideoformat_t;
731 qboolean cl_capturevideo_active = false;
732 capturevideoformat_t cl_capturevideo_format;
733 static double cl_capturevideo_starttime = 0;
734 double cl_capturevideo_framerate = 0;
735 static int cl_capturevideo_soundrate = 0;
736 static int cl_capturevideo_frame = 0;
737 static qbyte *cl_capturevideo_buffer = NULL;
738 static qfile_t *cl_capturevideo_videofile = NULL;
739 static qfile_t *cl_capturevideo_soundfile = NULL;
740 static short cl_capturevideo_rgbtoyuvscaletable[3][3][256];
741 static unsigned char cl_capturevideo_yuvnormalizetable[3][256];
742 static unsigned char cl_capturevideo_rgbgammatable[3][256];
744 void SCR_CaptureVideo_BeginVideo(void)
749 if (cl_capturevideo_active)
751 // soundrate is figured out on the first SoundFrame
752 cl_capturevideo_active = true;
753 cl_capturevideo_starttime = Sys_DoubleTime();
754 cl_capturevideo_framerate = bound(1, cl_capturevideo_fps.value, 1000);
755 cl_capturevideo_soundrate = 0;
756 cl_capturevideo_frame = 0;
757 cl_capturevideo_buffer = Mem_Alloc(tempmempool, vid.realwidth * vid.realheight * (3+3+3) + 18);
758 gamma = 1.0/scr_screenshot_gamma.value;
760 for (i = 0;i < 256;i++)
762 j = (unsigned char)bound(0, 255*pow(i/255.0, gamma), 255);
763 cl_capturevideo_rgbgammatable[0][i] = j;
764 cl_capturevideo_rgbgammatable[1][i] = j;
765 cl_capturevideo_rgbgammatable[2][i] = j;
768 R = Y + 1.4075 * (Cr - 128);
769 G = Y + -0.3455 * (Cb - 128) + -0.7169 * (Cr - 128);
770 B = Y + 1.7790 * (Cb - 128);
771 Y = R * .299 + G * .587 + B * .114;
772 Cb = R * -.169 + G * -.332 + B * .500 + 128.;
773 Cr = R * .500 + G * -.419 + B * -.0813 + 128.;
775 for (i = 0;i < 256;i++)
777 g = i;//255*pow(i/255.0, gamma);
778 // Y weights from RGB
779 cl_capturevideo_rgbtoyuvscaletable[0][0][i] = (short)(g * 0.299);
780 cl_capturevideo_rgbtoyuvscaletable[0][1][i] = (short)(g * 0.587);
781 cl_capturevideo_rgbtoyuvscaletable[0][2][i] = (short)(g * 0.114);
782 // Cb weights from RGB
783 cl_capturevideo_rgbtoyuvscaletable[1][0][i] = (short)(g * -0.169);
784 cl_capturevideo_rgbtoyuvscaletable[1][1][i] = (short)(g * -0.332);
785 cl_capturevideo_rgbtoyuvscaletable[1][2][i] = (short)(g * 0.500);
786 // Cr weights from RGB
787 cl_capturevideo_rgbtoyuvscaletable[2][0][i] = (short)(g * 0.500);
788 cl_capturevideo_rgbtoyuvscaletable[2][1][i] = (short)(g * -0.419);
789 cl_capturevideo_rgbtoyuvscaletable[2][2][i] = (short)(g * -0.0813);
790 // range reduction of YCbCr to valid signal range
791 cl_capturevideo_yuvnormalizetable[0][i] = 16 + i * (236-16) / 256;
792 cl_capturevideo_yuvnormalizetable[1][i] = 16 + i * (240-16) / 256;
793 cl_capturevideo_yuvnormalizetable[2][i] = 16 + i * (240-16) / 256;
796 if (cl_capturevideo_rawrgb.integer)
798 cl_capturevideo_format = CAPTUREVIDEOFORMAT_RAWRGB;
799 cl_capturevideo_videofile = FS_Open ("video/dpvideo.rgb", "wb", false);
801 else if (cl_capturevideo_rawyv12.integer)
803 cl_capturevideo_format = CAPTUREVIDEOFORMAT_RAWYV12;
804 cl_capturevideo_videofile = FS_Open ("video/dpvideo.yv12", "wb", false);
806 else if (scr_screenshot_jpeg.integer)
808 cl_capturevideo_format = CAPTUREVIDEOFORMAT_JPEG;
809 cl_capturevideo_videofile = NULL;
813 cl_capturevideo_format = CAPTUREVIDEOFORMAT_TARGA;
814 cl_capturevideo_videofile = NULL;
817 cl_capturevideo_soundfile = FS_Open ("video/dpvideo.wav", "wb", false);
819 // wave header will be filled out when video ends
821 FS_Write (cl_capturevideo_soundfile, out, 44);
824 void SCR_CaptureVideo_EndVideo(void)
828 if (!cl_capturevideo_active)
830 cl_capturevideo_active = false;
832 if (cl_capturevideo_videofile)
834 FS_Close(cl_capturevideo_videofile);
835 cl_capturevideo_videofile = NULL;
838 // finish the wave file
839 if (cl_capturevideo_soundfile)
841 i = FS_Tell (cl_capturevideo_soundfile);
842 //"RIFF", (int) unknown (chunk size), "WAVE",
843 //"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
844 //"data", (int) unknown (chunk size)
845 memcpy (out, "RIFF****WAVEfmt \x10\x00\x00\x00\x01\x00\x02\x00********\x04\x00\x10\0data****", 44);
846 // the length of the whole RIFF chunk
849 out[5] = (n >> 8) & 0xFF;
850 out[6] = (n >> 16) & 0xFF;
851 out[7] = (n >> 24) & 0xFF;
853 n = cl_capturevideo_soundrate;
854 out[24] = (n) & 0xFF;
855 out[25] = (n >> 8) & 0xFF;
856 out[26] = (n >> 16) & 0xFF;
857 out[27] = (n >> 24) & 0xFF;
858 // bytes per second (rate * channels * bytes per channel)
859 n = cl_capturevideo_soundrate * 2 * 2;
860 out[28] = (n) & 0xFF;
861 out[29] = (n >> 8) & 0xFF;
862 out[30] = (n >> 16) & 0xFF;
863 out[31] = (n >> 24) & 0xFF;
864 // the length of the data chunk
866 out[40] = (n) & 0xFF;
867 out[41] = (n >> 8) & 0xFF;
868 out[42] = (n >> 16) & 0xFF;
869 out[43] = (n >> 24) & 0xFF;
870 FS_Seek (cl_capturevideo_soundfile, 0, SEEK_SET);
871 FS_Write (cl_capturevideo_soundfile, out, 44);
872 FS_Close (cl_capturevideo_soundfile);
873 cl_capturevideo_soundfile = NULL;
876 if (cl_capturevideo_buffer)
878 Mem_Free (cl_capturevideo_buffer);
879 cl_capturevideo_buffer = NULL;
882 cl_capturevideo_starttime = 0;
883 cl_capturevideo_framerate = 0;
884 cl_capturevideo_frame = 0;
887 qboolean SCR_CaptureVideo_VideoFrame(int newframenum)
889 int x = vid.realx, y = vid.realy, width = vid.realwidth, height = vid.realheight;
890 unsigned char *b, *out;
892 int outoffset = (width/2)*(height/2);
893 //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);
894 // speed is critical here, so do saving as directly as possible
895 switch (cl_capturevideo_format)
897 case CAPTUREVIDEOFORMAT_RAWYV12:
898 // FIXME: width/height must be multiple of 2, enforce this?
899 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
901 // process one line at a time, and CbCr every other line at 2 pixel intervals
902 for (y = 0;y < height;y++)
905 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++)
906 *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]]];
909 // 2x2 Cb and Cr planes
911 // low quality, no averaging
912 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++)
915 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];
917 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];
920 // high quality, averaging
921 int inpitch = width*3;
922 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++)
924 int blockr, blockg, blockb;
925 blockr = (b[0] + b[3] + b[inpitch+0] + b[inpitch+3]) >> 2;
926 blockg = (b[1] + b[4] + b[inpitch+1] + b[inpitch+4]) >> 2;
927 blockb = (b[2] + b[5] + b[inpitch+2] + b[inpitch+5]) >> 2;
929 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];
931 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];
936 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
937 if (!FS_Write (cl_capturevideo_videofile, cl_capturevideo_buffer + width*height*3, width*height+(width/2)*(height/2)*2))
940 case CAPTUREVIDEOFORMAT_RAWRGB:
941 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
943 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
944 if (!FS_Write (cl_capturevideo_videofile, cl_capturevideo_buffer, width*height*3))
947 case CAPTUREVIDEOFORMAT_JPEG:
948 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
950 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
952 sprintf(filename, "video/dp%06d.jpg", cl_capturevideo_frame);
953 if (!JPEG_SaveImage_preflipped (filename, width, height, cl_capturevideo_buffer))
957 case CAPTUREVIDEOFORMAT_TARGA:
958 //return Image_WriteTGARGB_preflipped (filename, width, height, cl_capturevideo_buffer, cl_capturevideo_buffer + vid.realwidth * vid.realheight * 3, );
959 memset (cl_capturevideo_buffer, 0, 18);
960 cl_capturevideo_buffer[2] = 2; // uncompressed type
961 cl_capturevideo_buffer[12] = (width >> 0) & 0xFF;
962 cl_capturevideo_buffer[13] = (width >> 8) & 0xFF;
963 cl_capturevideo_buffer[14] = (height >> 0) & 0xFF;
964 cl_capturevideo_buffer[15] = (height >> 8) & 0xFF;
965 cl_capturevideo_buffer[16] = 24; // pixel size
966 qglReadPixels (x, y, width, height, GL_BGR, GL_UNSIGNED_BYTE, cl_capturevideo_buffer + 18);
968 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
970 sprintf(filename, "video/dp%06d.tga", cl_capturevideo_frame);
971 if (!FS_WriteFile (filename, cl_capturevideo_buffer, width*height*3 + 18))
980 void SCR_CaptureVideo_SoundFrame(qbyte *bufstereo16le, size_t length, int rate)
982 cl_capturevideo_soundrate = rate;
983 if (FS_Write (cl_capturevideo_soundfile, bufstereo16le, 4 * length) < 4 * length)
985 Cvar_SetValueQuick(&cl_capturevideo, 0);
986 Con_Printf("video sound saving failed on frame %i, out of disk space? stopping video capture.\n", cl_capturevideo_frame);
987 SCR_CaptureVideo_EndVideo();
991 void SCR_CaptureVideo(void)
994 if (cl_capturevideo.integer && r_render.integer)
996 if (!cl_capturevideo_active)
997 SCR_CaptureVideo_BeginVideo();
998 if (cl_capturevideo_framerate != cl_capturevideo_fps.value)
1000 Con_Printf("You can not change the video framerate while recording a video.\n");
1001 Cvar_SetValueQuick(&cl_capturevideo_fps, cl_capturevideo_framerate);
1003 newframenum = (Sys_DoubleTime() - cl_capturevideo_starttime) * cl_capturevideo_framerate;
1004 // if falling behind more than one second, stop
1005 if (newframenum - cl_capturevideo_frame > (int)ceil(cl_capturevideo_framerate))
1007 Cvar_SetValueQuick(&cl_capturevideo, 0);
1008 Con_Printf("video saving failed on frame %i, your machine is too slow for this capture speed.\n", cl_capturevideo_frame);
1009 SCR_CaptureVideo_EndVideo();
1013 if (!SCR_CaptureVideo_VideoFrame(newframenum))
1015 Cvar_SetValueQuick(&cl_capturevideo, 0);
1016 Con_Printf("video saving failed on frame %i, out of disk space? stopping video capture.\n", cl_capturevideo_frame);
1017 SCR_CaptureVideo_EndVideo();
1020 else if (cl_capturevideo_active)
1021 SCR_CaptureVideo_EndVideo();
1028 Grab six views for environment mapping tests
1035 qboolean flipx, flipy, flipdiagonaly;
1039 {{ 0, 0, 0}, "rt", true, false, false},
1040 {{ 0, 90, 0}, "ft", true, false, false},
1041 {{ 0, 180, 0}, "lf", true, false, false},
1042 {{ 0, 270, 0}, "bk", true, false, false},
1043 {{-90, 180, 0}, "up", false, true, false},
1044 {{ 90, 180, 0}, "dn", false, true, false},
1046 {{ 0, 0, 0}, "px", true, true, true},
1047 {{ 0, 90, 0}, "py", false, true, false},
1048 {{ 0, 180, 0}, "nx", false, false, true},
1049 {{ 0, 270, 0}, "ny", true, false, false},
1050 {{-90, 180, 0}, "pz", false, false, true},
1051 {{ 90, 180, 0}, "nz", false, false, true}
1054 static void R_Envmap_f (void)
1057 char filename[256], basename[256];
1062 if (Cmd_Argc() != 3)
1064 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");
1068 strlcpy (basename, Cmd_Argv(1), sizeof (basename));
1069 size = atoi(Cmd_Argv(2));
1070 if (size != 128 && size != 256 && size != 512 && size != 1024)
1072 Con_Print("envmap: size must be one of 128, 256, 512, or 1024\n");
1075 if (size > vid.realwidth || size > vid.realheight)
1077 Con_Print("envmap: your resolution is not big enough to render that size\n");
1085 r_refdef.width = size;
1086 r_refdef.height = size;
1088 r_refdef.fov_x = 90;
1089 r_refdef.fov_y = 90;
1091 buffer1 = Mem_Alloc(tempmempool, size * size * 3);
1092 buffer2 = Mem_Alloc(tempmempool, size * size * 3);
1093 buffer3 = Mem_Alloc(tempmempool, size * size * 3 + 18);
1095 for (j = 0;j < 12;j++)
1097 sprintf(filename, "env/%s%s.tga", basename, envmapinfo[j].name);
1098 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);
1103 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);
1113 //=============================================================================
1115 // LordHavoc: SHOWLMP stuff
1116 #define SHOWLMP_MAXLABELS 256
1117 typedef struct showlmp_s
1127 showlmp_t showlmp[SHOWLMP_MAXLABELS];
1129 void SHOWLMP_decodehide(void)
1133 lmplabel = MSG_ReadString();
1134 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1135 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
1137 showlmp[i].isactive = false;
1142 void SHOWLMP_decodeshow(void)
1145 qbyte lmplabel[256], picname[256];
1147 strlcpy (lmplabel,MSG_ReadString(), sizeof (lmplabel));
1148 strlcpy (picname, MSG_ReadString(), sizeof (picname));
1149 if (gamemode == GAME_NEHAHRA) // LordHavoc: nasty old legacy junk
1156 x = MSG_ReadShort();
1157 y = MSG_ReadShort();
1160 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1161 if (showlmp[i].isactive)
1163 if (strcmp(showlmp[i].label, lmplabel) == 0)
1166 break; // drop out to replace it
1169 else if (k < 0) // find first empty one to replace
1172 return; // none found to replace
1173 // change existing one
1174 showlmp[k].isactive = true;
1175 strlcpy (showlmp[k].label, lmplabel, sizeof (showlmp[k].label));
1176 strlcpy (showlmp[k].pic, picname, sizeof (showlmp[k].pic));
1181 void SHOWLMP_drawall(void)
1184 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1185 if (showlmp[i].isactive)
1186 DrawQ_Pic(showlmp[i].x, showlmp[i].y, showlmp[i].pic, 0, 0, 1, 1, 1, 1, 0);
1189 void SHOWLMP_clear(void)
1192 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1193 showlmp[i].isactive = false;
1196 void CL_SetupScreenSize(void)
1198 float conwidth, conheight;
1200 VID_GetWindowSize (&vid.realx, &vid.realy, &vid.realwidth, &vid.realheight);
1202 VID_UpdateGamma(false);
1204 conwidth = bound(320, vid_conwidth.value, 2048);
1205 conheight = bound(200, vid_conheight.value, 1536);
1206 if (vid_conwidth.value != conwidth)
1207 Cvar_SetValue("vid_conwidth", conwidth);
1208 if (vid_conheight.value != conheight)
1209 Cvar_SetValue("vid_conheight", conheight);
1211 vid.conwidth = vid_conwidth.integer;
1212 vid.conheight = vid_conheight.integer;
1214 /* if (vid.realheight > 240)
1216 vid.conheight = (vid.realheight - 240) * scr_2dresolution.value + 240;
1217 vid.conheight = bound(240, vid.conheight, vid.realheight);
1220 vid.conheight = 240;*/
1222 SCR_SetUpToDrawConsole();
1225 extern void R_Shadow_EditLights_DrawSelectedLightProperties(void);
1226 void CL_UpdateScreen(void)
1228 if (!scr_initialized || !con_initialized || vid_hidden)
1229 return; // not initialized yet
1233 if (cls.signon == SIGNONS)
1234 R_TimeReport("other");
1236 CL_SetupScreenSize();
1240 if (cls.signon == SIGNONS)
1241 R_TimeReport("setup");
1243 //FIXME: force menu if nothing else to look at?
1244 //if (key_dest == key_game && cls.signon != SIGNONS && cls.state == ca_disconnected)
1246 if (scr_drawloading)
1248 scr_drawloading = false;
1253 if (cls.signon == SIGNONS)
1258 if (!r_letterbox.value)
1261 SCR_CheckDrawCenterString();
1267 if (cls.signon == SIGNONS)
1271 R_TimeReport_Start();
1273 R_Shadow_EditLights_DrawSelectedLightProperties();
1280 void CL_Screen_NewMap(void)