5 #include "cl_collision.h"
7 cvar_t scr_viewsize = {CVAR_SAVE, "viewsize","100"};
8 cvar_t scr_fov = {CVAR_SAVE, "fov","90"}; // 1 - 170
9 cvar_t scr_conspeed = {CVAR_SAVE, "scr_conspeed","900"}; // LordHavoc: quake used 300
10 cvar_t scr_conalpha = {CVAR_SAVE, "scr_conalpha", "1"};
11 cvar_t scr_conbrightness = {CVAR_SAVE, "scr_conbrightness", "0.2"};
12 cvar_t scr_conforcewhiledisconnected = {CVAR_SAVE, "scr_conforcewhiledisconnected", "1"};
13 cvar_t scr_centertime = {0, "scr_centertime","2"};
14 cvar_t scr_showram = {CVAR_SAVE, "showram","1"};
15 cvar_t scr_showturtle = {CVAR_SAVE, "showturtle","0"};
16 cvar_t scr_showpause = {CVAR_SAVE, "showpause","1"};
17 cvar_t scr_printspeed = {0, "scr_printspeed","8"};
18 cvar_t vid_conwidth = {CVAR_SAVE, "vid_conwidth", "640"};
19 cvar_t vid_conheight = {CVAR_SAVE, "vid_conheight", "480"};
20 cvar_t vid_pixelaspect = {CVAR_SAVE, "vid_pixelaspect", "1"};
21 cvar_t scr_screenshot_jpeg = {CVAR_SAVE, "scr_screenshot_jpeg","0"};
22 cvar_t scr_screenshot_jpeg_quality = {CVAR_SAVE, "scr_screenshot_jpeg_quality","0.9"};
23 cvar_t scr_screenshot_gamma = {CVAR_SAVE, "scr_screenshot_gamma","2.2"};
24 cvar_t scr_screenshot_name = {0, "scr_screenshot_name","dp"};
25 cvar_t cl_capturevideo = {0, "cl_capturevideo", "0"};
26 cvar_t cl_capturevideo_fps = {0, "cl_capturevideo_fps", "30"};
27 cvar_t cl_capturevideo_rawrgb = {0, "cl_capturevideo_rawrgb", "0"};
28 cvar_t cl_capturevideo_rawyv12 = {0, "cl_capturevideo_rawyv12", "0"};
29 cvar_t r_textshadow = {0, "r_textshadow", "0"};
30 cvar_t r_letterbox = {0, "r_letterbox", "0"};
32 int jpeg_supported = false;
34 qboolean scr_initialized; // ready to draw
36 float scr_con_current;
37 float scr_conlines; // lines of console to display
39 extern int con_vislines;
41 void DrawCrosshair(int num);
42 static void SCR_ScreenShot_f (void);
43 static void R_Envmap_f (void);
46 void R_ClearScreen(void);
49 ===============================================================================
53 ===============================================================================
56 char scr_centerstring[1024];
57 float scr_centertime_start; // for slow victory printing
58 float scr_centertime_off;
67 Called for important messages that should stay in the center of the screen
71 void SCR_CenterPrint(char *str)
73 strlcpy (scr_centerstring, str, sizeof (scr_centerstring));
74 scr_centertime_off = scr_centertime.value;
75 scr_centertime_start = cl.time;
77 // count the number of lines for centering
88 void SCR_DrawCenterString (void)
95 // the finale prints the characters one at a time
97 remaining = scr_printspeed.value * (cl.time - scr_centertime_start);
101 scr_erase_center = 0;
102 start = scr_centerstring;
104 if (scr_center_lines <= 4)
105 y = vid.conheight*0.35;
111 // scan the width of the line
112 for (l=0 ; l<vid.conwidth/8 ; l++)
113 if (start[l] == '\n' || !start[l])
115 x = (vid.conwidth - l*8)/2;
120 DrawQ_String(x, y, start, l, 8, 8, 1, 1, 1, 1, 0);
128 while (*start && *start != '\n')
133 start++; // skip the \n
137 void SCR_CheckDrawCenterString (void)
139 if (scr_center_lines > scr_erase_lines)
140 scr_erase_lines = scr_center_lines;
142 scr_centertime_off -= host_frametime;
144 // don't draw if this is a normal stats-screen intermission,
145 // only if it is not an intermission, or a finale intermission
146 if (cl.intermission == 1)
148 if (scr_centertime_off <= 0 && !cl.intermission)
150 if (key_dest != key_game)
153 SCR_DrawCenterString ();
161 void SCR_DrawTurtle (void)
165 if (cls.state != ca_connected)
168 if (!scr_showturtle.integer)
171 if (host_frametime < 0.1)
181 DrawQ_Pic (0, 0, "gfx/turtle.lmp", 0, 0, 1, 1, 1, 1, 0);
189 void SCR_DrawNet (void)
191 if (cls.state != ca_connected)
193 if (realtime - cl.last_received_message < 0.3)
195 if (cls.demoplayback)
198 DrawQ_Pic (64, 0, "gfx/net.lmp", 0, 0, 1, 1, 1, 1, 0);
206 void SCR_DrawPause (void)
210 if (cls.state != ca_connected)
213 if (!scr_showpause.integer) // turn off for screenshots
219 pic = Draw_CachePic ("gfx/pause.lmp");
220 DrawQ_Pic ((vid.conwidth - pic->width)/2, (vid.conheight - pic->height)/2, "gfx/pause.lmp", 0, 0, 1, 1, 1, 1, 0);
227 //=============================================================================
232 SCR_SetUpToDrawConsole
235 void SCR_SetUpToDrawConsole (void)
239 if (key_dest == key_game && cls.signon != SIGNONS && scr_conforcewhiledisconnected.integer)
240 key_consoleactive |= KEY_CONSOLEACTIVE_FORCED;
242 key_consoleactive &= ~KEY_CONSOLEACTIVE_FORCED;
244 // decide on the height of the console
245 if (key_consoleactive & KEY_CONSOLEACTIVE_FORCED)
246 scr_conlines = vid.conheight; // full screen
247 else if (key_consoleactive & KEY_CONSOLEACTIVE_USER)
248 scr_conlines = vid.conheight/2; // half screen
250 scr_conlines = 0; // none visible
252 if (scr_conspeed.value)
254 if (scr_conlines < scr_con_current)
256 scr_con_current -= scr_conspeed.value*host_realframetime;
257 if (scr_conlines > scr_con_current)
258 scr_con_current = scr_conlines;
261 else if (scr_conlines > scr_con_current)
263 scr_con_current += scr_conspeed.value*host_realframetime;
264 if (scr_conlines < scr_con_current)
265 scr_con_current = scr_conlines;
269 scr_con_current = scr_conlines;
277 void SCR_DrawConsole (void)
280 Con_DrawConsole (scr_con_current);
284 if (key_dest == key_game || key_dest == key_message)
285 Con_DrawNotify (); // only draw notify in game
291 SCR_BeginLoadingPlaque
295 void SCR_BeginLoadingPlaque (void)
298 SCR_UpdateLoadingScreen();
301 //=============================================================================
303 char r_speeds_string[1024];
304 int speedstringcount, r_timereport_active;
305 double r_timereport_temp = 0, r_timereport_current = 0, r_timereport_start = 0;
307 void R_TimeReport(char *desc)
313 if (!r_timereport_active)
316 r_timereport_temp = r_timereport_current;
317 r_timereport_current = Sys_DoubleTime();
318 t = (int) ((r_timereport_current - r_timereport_temp) * 1000000.0);
320 sprintf(tempbuf, "%8i %s", t, desc);
321 length = strlen(tempbuf);
323 tempbuf[length++] = ' ';
325 if (speedstringcount + length > (vid.conwidth / 8))
327 strcat(r_speeds_string, "\n");
328 speedstringcount = 0;
330 // skip the space at the beginning if it's the first on the line
331 if (speedstringcount == 0)
333 strcat(r_speeds_string, tempbuf + 1);
334 speedstringcount = length - 1;
338 strcat(r_speeds_string, tempbuf);
339 speedstringcount += length;
343 extern int c_rt_lights, c_rt_clears, c_rt_scissored;
344 extern int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris;
345 extern int c_rtcached_shadowmeshes, c_rtcached_shadowtris;
346 void R_TimeReport_Start(void)
348 r_timereport_active = r_speeds.integer && cls.signon == SIGNONS && cls.state == ca_connected;
349 r_speeds_string[0] = 0;
350 if (r_timereport_active)
352 speedstringcount = 0;
353 sprintf(r_speeds_string,
354 "org:'%+8.2f %+8.2f %+8.2f' dir:'%+2.3f %+2.3f %+2.3f'\n"
355 "world:%6i faces%6i nodes%6i leafs%6i dlitwalls\n"
356 "%5i models%5i bmodels%5i sprites%6i particles%4i dlights\n"
357 "%6i modeltris%6i meshs%6i meshtris\n",
358 r_vieworigin[0], r_vieworigin[1], r_vieworigin[2], r_viewforward[0], r_viewforward[1], r_viewforward[2],
359 c_faces, c_nodes, c_leafs, c_light_polys,
360 c_models, c_bmodels, c_sprites, c_particles, c_dlights,
361 c_alias_polys, c_meshs, c_meshelements / 3);
363 sprintf(r_speeds_string + strlen(r_speeds_string),
364 "realtime lighting:%4i lights%4i clears%4i scissored\n"
365 "dynamic: %6i shadowmeshes%6i shadowtris%6i lightmeshes%6i lighttris\n"
366 "precomputed: %6i shadowmeshes%6i shadowtris\n",
367 c_rt_lights, c_rt_clears, c_rt_scissored,
368 c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris,
369 c_rtcached_shadowmeshes, c_rtcached_shadowtris);
383 r_timereport_start = Sys_DoubleTime();
387 void R_TimeReport_End(void)
389 r_timereport_current = r_timereport_start;
390 R_TimeReport("total");
392 if (r_timereport_active)
396 for (i = 0;r_speeds_string[i];i++)
397 if (r_speeds_string[i] == '\n')
399 y = vid.conheight - sb_lines - lines * 8;
401 DrawQ_Fill(0, y, vid.conwidth, lines * 8, 0, 0, 0, 0.5, 0);
402 while (r_speeds_string[i])
405 while (r_speeds_string[i] && r_speeds_string[i] != '\n')
408 DrawQ_String(0, y, r_speeds_string + j, i - j, 8, 8, 1, 1, 1, 1, 0);
409 if (r_speeds_string[i] == '\n')
423 void SCR_SizeUp_f (void)
425 Cvar_SetValue ("viewsize",scr_viewsize.value+10);
436 void SCR_SizeDown_f (void)
438 Cvar_SetValue ("viewsize",scr_viewsize.value-10);
441 void CL_Screen_Init(void)
443 Cvar_RegisterVariable (&scr_fov);
444 Cvar_RegisterVariable (&scr_viewsize);
445 Cvar_RegisterVariable (&scr_conspeed);
446 Cvar_RegisterVariable (&scr_conalpha);
447 Cvar_RegisterVariable (&scr_conbrightness);
448 Cvar_RegisterVariable (&scr_conforcewhiledisconnected);
449 Cvar_RegisterVariable (&scr_showram);
450 Cvar_RegisterVariable (&scr_showturtle);
451 Cvar_RegisterVariable (&scr_showpause);
452 Cvar_RegisterVariable (&scr_centertime);
453 Cvar_RegisterVariable (&scr_printspeed);
454 Cvar_RegisterVariable (&vid_conwidth);
455 Cvar_RegisterVariable (&vid_conheight);
456 Cvar_RegisterVariable (&vid_pixelaspect);
457 Cvar_RegisterVariable (&scr_screenshot_jpeg);
458 Cvar_RegisterVariable (&scr_screenshot_jpeg_quality);
459 Cvar_RegisterVariable (&scr_screenshot_gamma);
460 Cvar_RegisterVariable (&cl_capturevideo);
461 Cvar_RegisterVariable (&cl_capturevideo_fps);
462 Cvar_RegisterVariable (&cl_capturevideo_rawrgb);
463 Cvar_RegisterVariable (&cl_capturevideo_rawyv12);
464 Cvar_RegisterVariable (&r_textshadow);
465 Cvar_RegisterVariable (&r_letterbox);
467 Cmd_AddCommand ("sizeup",SCR_SizeUp_f);
468 Cmd_AddCommand ("sizedown",SCR_SizeDown_f);
469 Cmd_AddCommand ("screenshot",SCR_ScreenShot_f);
470 Cmd_AddCommand ("envmap", R_Envmap_f);
472 scr_initialized = true;
475 void DrawQ_Clear(void)
477 r_refdef.drawqueuesize = 0;
480 static int picelements[6] = {0, 1, 2, 0, 2, 3};
481 void DrawQ_Pic(float x, float y, char *picname, float width, float height, float red, float green, float blue, float alpha, int flags)
483 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);
486 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)
491 if (alpha < (1.0f / 255.0f))
494 len = strlen(string);
496 for (len = 0;len < maxlen && string[len];len++);
497 for (;len > 0 && string[0] == ' ';string++, x += scalex, len--);
498 for (;len > 0 && string[len - 1] == ' ';len--);
501 if (x >= vid.conwidth || y >= vid.conheight || x < (-scalex * len) || y < (-scaley))
503 size = sizeof(*dq) + ((len + 1 + 3) & ~3);
504 if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
506 red = bound(0, red, 1);
507 green = bound(0, green, 1);
508 blue = bound(0, blue, 1);
509 alpha = bound(0, alpha, 1);
510 dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
512 dq->command = DRAWQUEUE_STRING;
514 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));
519 out = (char *)(dq + 1);
520 memcpy(out, string, len);
522 r_refdef.drawqueuesize += dq->size;
525 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)
527 if (r_textshadow.integer)
528 DrawQ_String_Real(x+scalex*0.25,y+scaley*0.25,string,maxlen,scalex,scaley,0,0,0,alpha*0.8,flags);
530 DrawQ_String_Real(x,y,string,maxlen,scalex,scaley,red,green,blue,alpha,flags);
533 void DrawQ_Fill (float x, float y, float w, float h, float red, float green, float blue, float alpha, int flags)
535 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);
538 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)
542 drawqueuemesh_t mesh;
543 memset(&mesh, 0, sizeof(mesh));
544 if (picname && picname[0])
546 pic = Draw_CachePic(picname);
550 height = pic->height;
551 mesh.texture = pic->tex;
553 mesh.num_triangles = 2;
554 mesh.num_vertices = 4;
555 mesh.data_element3i = picelements;
556 mesh.data_vertex3f = floats;
557 mesh.data_texcoord2f = floats + 12;
558 mesh.data_color4f = floats + 20;
559 memset(floats, 0, sizeof(floats));
560 mesh.data_vertex3f[0] = mesh.data_vertex3f[9] = x;
561 mesh.data_vertex3f[1] = mesh.data_vertex3f[4] = y;
562 mesh.data_vertex3f[3] = mesh.data_vertex3f[6] = x + width;
563 mesh.data_vertex3f[7] = mesh.data_vertex3f[10] = y + height;
564 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;
565 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;
566 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;
567 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;
568 DrawQ_Mesh (&mesh, flags);
571 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
578 size += sizeof(drawqueuemesh_t);
579 size += sizeof(int[3]) * mesh->num_triangles;
580 size += sizeof(float[3]) * mesh->num_vertices;
581 size += sizeof(float[2]) * mesh->num_vertices;
582 size += sizeof(float[4]) * mesh->num_vertices;
583 if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
585 dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
587 dq->command = DRAWQUEUE_MESH;
594 p = (void *)(dq + 1);
595 m = p;p = (qbyte*)p + sizeof(drawqueuemesh_t);
596 m->num_triangles = mesh->num_triangles;
597 m->num_vertices = mesh->num_vertices;
598 m->texture = mesh->texture;
599 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]);
600 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]);
601 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]);
602 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]);
603 r_refdef.drawqueuesize += dq->size;
606 void DrawQ_SetClipArea(float x, float y, float width, float height)
609 if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
611 Con_DPrint("DrawQueue full !\n");
614 dq = (void*) (r_refdef.drawqueue + r_refdef.drawqueuesize);
615 dq->size = sizeof(*dq);
616 dq->command = DRAWQUEUE_SETCLIP;
624 r_refdef.drawqueuesize += dq->size;
627 void DrawQ_ResetClipArea(void)
630 if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
632 Con_DPrint("DrawQueue full !\n");
635 dq = (void*) (r_refdef.drawqueue + r_refdef.drawqueuesize);
636 dq->size = sizeof(*dq);
637 dq->command = DRAWQUEUE_RESETCLIP;
645 r_refdef.drawqueuesize += dq->size;
653 void SCR_ScreenShot_f (void)
655 static int shotnumber;
656 static char oldname[MAX_QPATH];
657 char base[MAX_QPATH];
658 char filename[MAX_QPATH];
662 qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
664 sprintf (base, "screenshots/%s", scr_screenshot_name.string);
666 if (strcmp (oldname, scr_screenshot_name.string))
668 sprintf(oldname, "%s", scr_screenshot_name.string);
672 // find a file name to save it to
673 for (;shotnumber < 1000000;shotnumber++)
674 if (!FS_SysFileExists(va("%s/%s%06d.tga", fs_gamedir, base, shotnumber)) && !FS_SysFileExists(va("%s/%s%06d.jpg", fs_gamedir, base, shotnumber)))
676 if (shotnumber >= 1000000)
678 Con_Print("SCR_ScreenShot_f: Couldn't create the image file\n");
682 sprintf(filename, "%s%06d.%s", base, shotnumber, jpeg ? "jpg" : "tga");
684 buffer1 = Mem_Alloc(tempmempool, vid.realwidth * vid.realheight * 3);
685 buffer2 = Mem_Alloc(tempmempool, vid.realwidth * vid.realheight * 3);
686 buffer3 = Mem_Alloc(tempmempool, vid.realwidth * vid.realheight * 3 + 18);
688 if (SCR_ScreenShot (filename, buffer1, buffer2, buffer3, vid.realx, vid.realy, vid.realwidth, vid.realheight, false, false, false, jpeg))
689 Con_Printf("Wrote %s\n", filename);
691 Con_Printf("unable to write %s\n", filename);
700 typedef enum capturevideoformat_e
702 CAPTUREVIDEOFORMAT_TARGA,
703 CAPTUREVIDEOFORMAT_JPEG,
704 CAPTUREVIDEOFORMAT_RAWRGB,
705 CAPTUREVIDEOFORMAT_RAWYV12
707 capturevideoformat_t;
709 qboolean cl_capturevideo_active = false;
710 capturevideoformat_t cl_capturevideo_format;
711 static double cl_capturevideo_starttime = 0;
712 double cl_capturevideo_framerate = 0;
713 static int cl_capturevideo_soundrate = 0;
714 static int cl_capturevideo_frame = 0;
715 static qbyte *cl_capturevideo_buffer = NULL;
716 static qfile_t *cl_capturevideo_videofile = NULL;
717 static qfile_t *cl_capturevideo_soundfile = NULL;
718 static short cl_capturevideo_rgbtoyuvscaletable[3][3][256];
719 static unsigned char cl_capturevideo_yuvnormalizetable[3][256];
720 static unsigned char cl_capturevideo_rgbgammatable[3][256];
722 void SCR_CaptureVideo_BeginVideo(void)
727 if (cl_capturevideo_active)
729 // soundrate is figured out on the first SoundFrame
730 cl_capturevideo_active = true;
731 cl_capturevideo_starttime = Sys_DoubleTime();
732 cl_capturevideo_framerate = bound(1, cl_capturevideo_fps.value, 1000);
733 cl_capturevideo_soundrate = 0;
734 cl_capturevideo_frame = 0;
735 cl_capturevideo_buffer = Mem_Alloc(tempmempool, vid.realwidth * vid.realheight * (3+3+3) + 18);
736 gamma = 1.0/scr_screenshot_gamma.value;
738 for (i = 0;i < 256;i++)
740 j = (unsigned char)bound(0, 255*pow(i/255.0, gamma), 255);
741 cl_capturevideo_rgbgammatable[0][i] = j;
742 cl_capturevideo_rgbgammatable[1][i] = j;
743 cl_capturevideo_rgbgammatable[2][i] = j;
746 R = Y + 1.4075 * (Cr - 128);
747 G = Y + -0.3455 * (Cb - 128) + -0.7169 * (Cr - 128);
748 B = Y + 1.7790 * (Cb - 128);
749 Y = R * .299 + G * .587 + B * .114;
750 Cb = R * -.169 + G * -.332 + B * .500 + 128.;
751 Cr = R * .500 + G * -.419 + B * -.0813 + 128.;
753 for (i = 0;i < 256;i++)
755 g = i;//255*pow(i/255.0, gamma);
756 // Y weights from RGB
757 cl_capturevideo_rgbtoyuvscaletable[0][0][i] = (short)(g * 0.299);
758 cl_capturevideo_rgbtoyuvscaletable[0][1][i] = (short)(g * 0.587);
759 cl_capturevideo_rgbtoyuvscaletable[0][2][i] = (short)(g * 0.114);
760 // Cb weights from RGB
761 cl_capturevideo_rgbtoyuvscaletable[1][0][i] = (short)(g * -0.169);
762 cl_capturevideo_rgbtoyuvscaletable[1][1][i] = (short)(g * -0.332);
763 cl_capturevideo_rgbtoyuvscaletable[1][2][i] = (short)(g * 0.500);
764 // Cr weights from RGB
765 cl_capturevideo_rgbtoyuvscaletable[2][0][i] = (short)(g * 0.500);
766 cl_capturevideo_rgbtoyuvscaletable[2][1][i] = (short)(g * -0.419);
767 cl_capturevideo_rgbtoyuvscaletable[2][2][i] = (short)(g * -0.0813);
768 // range reduction of YCbCr to valid signal range
769 cl_capturevideo_yuvnormalizetable[0][i] = 16 + i * (236-16) / 256;
770 cl_capturevideo_yuvnormalizetable[1][i] = 16 + i * (240-16) / 256;
771 cl_capturevideo_yuvnormalizetable[2][i] = 16 + i * (240-16) / 256;
774 if (cl_capturevideo_rawrgb.integer)
776 cl_capturevideo_format = CAPTUREVIDEOFORMAT_RAWRGB;
777 cl_capturevideo_videofile = FS_Open ("video/dpvideo.rgb", "wb", false);
779 else if (cl_capturevideo_rawyv12.integer)
781 cl_capturevideo_format = CAPTUREVIDEOFORMAT_RAWYV12;
782 cl_capturevideo_videofile = FS_Open ("video/dpvideo.yv12", "wb", false);
784 else if (scr_screenshot_jpeg.integer)
786 cl_capturevideo_format = CAPTUREVIDEOFORMAT_JPEG;
787 cl_capturevideo_videofile = NULL;
791 cl_capturevideo_format = CAPTUREVIDEOFORMAT_TARGA;
792 cl_capturevideo_videofile = NULL;
795 cl_capturevideo_soundfile = FS_Open ("video/dpvideo.wav", "wb", false);
797 // wave header will be filled out when video ends
799 FS_Write (cl_capturevideo_soundfile, out, 44);
802 void SCR_CaptureVideo_EndVideo(void)
806 if (!cl_capturevideo_active)
808 cl_capturevideo_active = false;
810 if (cl_capturevideo_videofile)
812 FS_Close(cl_capturevideo_videofile);
813 cl_capturevideo_videofile = NULL;
816 // finish the wave file
817 if (cl_capturevideo_soundfile)
819 i = FS_Tell (cl_capturevideo_soundfile);
820 //"RIFF", (int) unknown (chunk size), "WAVE",
821 //"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
822 //"data", (int) unknown (chunk size)
823 memcpy (out, "RIFF****WAVEfmt \x10\x00\x00\x00\x01\x00\x02\x00********\x04\x00\x10\0data****", 44);
824 // the length of the whole RIFF chunk
827 out[5] = (n >> 8) & 0xFF;
828 out[6] = (n >> 16) & 0xFF;
829 out[7] = (n >> 24) & 0xFF;
831 n = cl_capturevideo_soundrate;
832 out[24] = (n) & 0xFF;
833 out[25] = (n >> 8) & 0xFF;
834 out[26] = (n >> 16) & 0xFF;
835 out[27] = (n >> 24) & 0xFF;
836 // bytes per second (rate * channels * bytes per channel)
837 n = cl_capturevideo_soundrate * 2 * 2;
838 out[28] = (n) & 0xFF;
839 out[29] = (n >> 8) & 0xFF;
840 out[30] = (n >> 16) & 0xFF;
841 out[31] = (n >> 24) & 0xFF;
842 // the length of the data chunk
844 out[40] = (n) & 0xFF;
845 out[41] = (n >> 8) & 0xFF;
846 out[42] = (n >> 16) & 0xFF;
847 out[43] = (n >> 24) & 0xFF;
848 FS_Seek (cl_capturevideo_soundfile, 0, SEEK_SET);
849 FS_Write (cl_capturevideo_soundfile, out, 44);
850 FS_Close (cl_capturevideo_soundfile);
851 cl_capturevideo_soundfile = NULL;
854 if (cl_capturevideo_buffer)
856 Mem_Free (cl_capturevideo_buffer);
857 cl_capturevideo_buffer = NULL;
860 cl_capturevideo_starttime = 0;
861 cl_capturevideo_framerate = 0;
862 cl_capturevideo_frame = 0;
865 qboolean SCR_CaptureVideo_VideoFrame(int newframenum)
867 int x = vid.realx, y = vid.realy, width = vid.realwidth, height = vid.realheight;
868 unsigned char *b, *out;
870 int outoffset = (width/2)*(height/2);
871 //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);
872 // speed is critical here, so do saving as directly as possible
873 switch (cl_capturevideo_format)
875 case CAPTUREVIDEOFORMAT_RAWYV12:
876 // FIXME: width/height must be multiple of 2, enforce this?
877 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
879 // process one line at a time, and CbCr every other line at 2 pixel intervals
880 for (y = 0;y < height;y++)
883 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++)
884 *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]]];
887 // 2x2 Cb and Cr planes
889 // low quality, no averaging
890 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++)
893 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];
895 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];
898 // high quality, averaging
899 int inpitch = width*3;
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++)
902 int blockr, blockg, blockb;
903 blockr = (b[0] + b[3] + b[inpitch+0] + b[inpitch+3]) >> 2;
904 blockg = (b[1] + b[4] + b[inpitch+1] + b[inpitch+4]) >> 2;
905 blockb = (b[2] + b[5] + b[inpitch+2] + b[inpitch+5]) >> 2;
907 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];
909 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];
914 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
915 if (!FS_Write (cl_capturevideo_videofile, cl_capturevideo_buffer + width*height*3, width*height+(width/2)*(height/2)*2))
918 case CAPTUREVIDEOFORMAT_RAWRGB:
919 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
921 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
922 if (!FS_Write (cl_capturevideo_videofile, cl_capturevideo_buffer, width*height*3))
925 case CAPTUREVIDEOFORMAT_JPEG:
926 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
928 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
930 sprintf(filename, "video/dp%06d.jpg", cl_capturevideo_frame);
931 if (!JPEG_SaveImage_preflipped (filename, width, height, cl_capturevideo_buffer))
935 case CAPTUREVIDEOFORMAT_TARGA:
936 //return Image_WriteTGARGB_preflipped (filename, width, height, cl_capturevideo_buffer, cl_capturevideo_buffer + vid.realwidth * vid.realheight * 3, );
937 memset (cl_capturevideo_buffer, 0, 18);
938 cl_capturevideo_buffer[2] = 2; // uncompressed type
939 cl_capturevideo_buffer[12] = (width >> 0) & 0xFF;
940 cl_capturevideo_buffer[13] = (width >> 8) & 0xFF;
941 cl_capturevideo_buffer[14] = (height >> 0) & 0xFF;
942 cl_capturevideo_buffer[15] = (height >> 8) & 0xFF;
943 cl_capturevideo_buffer[16] = 24; // pixel size
944 qglReadPixels (x, y, width, height, GL_BGR, GL_UNSIGNED_BYTE, cl_capturevideo_buffer + 18);
946 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
948 sprintf(filename, "video/dp%06d.tga", cl_capturevideo_frame);
949 if (!FS_WriteFile (filename, cl_capturevideo_buffer, width*height*3 + 18))
958 void SCR_CaptureVideo_SoundFrame(qbyte *bufstereo16le, size_t length, int rate)
960 cl_capturevideo_soundrate = rate;
961 if (FS_Write (cl_capturevideo_soundfile, bufstereo16le, 4 * length) < 4 * length)
963 Cvar_SetValueQuick(&cl_capturevideo, 0);
964 Con_Printf("video sound saving failed on frame %i, out of disk space? stopping video capture.\n", cl_capturevideo_frame);
965 SCR_CaptureVideo_EndVideo();
969 void SCR_CaptureVideo(void)
972 if (cl_capturevideo.integer && r_render.integer)
974 if (!cl_capturevideo_active)
975 SCR_CaptureVideo_BeginVideo();
976 if (cl_capturevideo_framerate != cl_capturevideo_fps.value)
978 Con_Printf("You can not change the video framerate while recording a video.\n");
979 Cvar_SetValueQuick(&cl_capturevideo_fps, cl_capturevideo_framerate);
981 newframenum = (Sys_DoubleTime() - cl_capturevideo_starttime) * cl_capturevideo_framerate;
982 // if falling behind more than one second, stop
983 if (newframenum - cl_capturevideo_frame > (int)ceil(cl_capturevideo_framerate))
985 Cvar_SetValueQuick(&cl_capturevideo, 0);
986 Con_Printf("video saving failed on frame %i, your machine is too slow for this capture speed.\n", cl_capturevideo_frame);
987 SCR_CaptureVideo_EndVideo();
991 if (!SCR_CaptureVideo_VideoFrame(newframenum))
993 Cvar_SetValueQuick(&cl_capturevideo, 0);
994 Con_Printf("video saving failed on frame %i, out of disk space? stopping video capture.\n", cl_capturevideo_frame);
995 SCR_CaptureVideo_EndVideo();
998 else if (cl_capturevideo_active)
999 SCR_CaptureVideo_EndVideo();
1006 Grab six views for environment mapping tests
1013 qboolean flipx, flipy, flipdiagonaly;
1017 {{ 0, 0, 0}, "rt", true, false, false},
1018 {{ 0, 90, 0}, "ft", true, false, false},
1019 {{ 0, 180, 0}, "lf", true, false, false},
1020 {{ 0, 270, 0}, "bk", true, false, false},
1021 {{-90, 180, 0}, "up", false, true, false},
1022 {{ 90, 180, 0}, "dn", false, true, false},
1024 {{ 0, 0, 0}, "px", true, true, true},
1025 {{ 0, 90, 0}, "py", false, true, false},
1026 {{ 0, 180, 0}, "nx", false, false, true},
1027 {{ 0, 270, 0}, "ny", true, false, false},
1028 {{-90, 180, 0}, "pz", false, false, true},
1029 {{ 90, 180, 0}, "nz", false, false, true}
1032 static void R_Envmap_f (void)
1035 char filename[256], basename[256];
1040 if (Cmd_Argc() != 3)
1042 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");
1046 strlcpy (basename, Cmd_Argv(1), sizeof (basename));
1047 size = atoi(Cmd_Argv(2));
1048 if (size != 128 && size != 256 && size != 512 && size != 1024)
1050 Con_Print("envmap: size must be one of 128, 256, 512, or 1024\n");
1053 if (size > vid.realwidth || size > vid.realheight)
1055 Con_Print("envmap: your resolution is not big enough to render that size\n");
1063 r_refdef.width = size;
1064 r_refdef.height = size;
1066 r_refdef.fov_x = 90;
1067 r_refdef.fov_y = 90;
1069 buffer1 = Mem_Alloc(tempmempool, size * size * 3);
1070 buffer2 = Mem_Alloc(tempmempool, size * size * 3);
1071 buffer3 = Mem_Alloc(tempmempool, size * size * 3 + 18);
1073 for (j = 0;j < 12;j++)
1075 sprintf(filename, "env/%s%s.tga", basename, envmapinfo[j].name);
1076 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);
1081 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);
1091 //=============================================================================
1093 // LordHavoc: SHOWLMP stuff
1094 #define SHOWLMP_MAXLABELS 256
1095 typedef struct showlmp_s
1105 showlmp_t showlmp[SHOWLMP_MAXLABELS];
1107 void SHOWLMP_decodehide(void)
1111 lmplabel = MSG_ReadString();
1112 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1113 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
1115 showlmp[i].isactive = false;
1120 void SHOWLMP_decodeshow(void)
1123 qbyte lmplabel[256], picname[256];
1125 strlcpy (lmplabel,MSG_ReadString(), sizeof (lmplabel));
1126 strlcpy (picname, MSG_ReadString(), sizeof (picname));
1127 if (gamemode == GAME_NEHAHRA) // LordHavoc: nasty old legacy junk
1134 x = MSG_ReadShort();
1135 y = MSG_ReadShort();
1138 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1139 if (showlmp[i].isactive)
1141 if (strcmp(showlmp[i].label, lmplabel) == 0)
1144 break; // drop out to replace it
1147 else if (k < 0) // find first empty one to replace
1150 return; // none found to replace
1151 // change existing one
1152 showlmp[k].isactive = true;
1153 strlcpy (showlmp[k].label, lmplabel, sizeof (showlmp[k].label));
1154 strlcpy (showlmp[k].pic, picname, sizeof (showlmp[k].pic));
1159 void SHOWLMP_drawall(void)
1162 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1163 if (showlmp[i].isactive)
1164 DrawQ_Pic(showlmp[i].x, showlmp[i].y, showlmp[i].pic, 0, 0, 1, 1, 1, 1, 0);
1167 void SHOWLMP_clear(void)
1170 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1171 showlmp[i].isactive = false;
1174 void CL_SetupScreenSize(void)
1176 float conwidth, conheight;
1178 VID_GetWindowSize (&vid.realx, &vid.realy, &vid.realwidth, &vid.realheight);
1180 VID_UpdateGamma(false);
1182 conwidth = bound(320, vid_conwidth.value, 2048);
1183 conheight = bound(200, vid_conheight.value, 1536);
1184 if (vid_conwidth.value != conwidth)
1185 Cvar_SetValue("vid_conwidth", conwidth);
1186 if (vid_conheight.value != conheight)
1187 Cvar_SetValue("vid_conheight", conheight);
1189 vid.conwidth = vid_conwidth.integer;
1190 vid.conheight = vid_conheight.integer;
1192 /* if (vid.realheight > 240)
1194 vid.conheight = (vid.realheight - 240) * scr_2dresolution.value + 240;
1195 vid.conheight = bound(240, vid.conheight, vid.realheight);
1198 vid.conheight = 240;*/
1200 SCR_SetUpToDrawConsole();
1203 extern void R_Shadow_EditLights_DrawSelectedLightProperties(void);
1204 void CL_UpdateScreen(void)
1206 if (!scr_initialized || !con_initialized || vid_hidden)
1207 return; // not initialized yet
1211 if (cls.signon == SIGNONS)
1212 R_TimeReport("other");
1214 CL_SetupScreenSize();
1218 if (cls.signon == SIGNONS)
1219 R_TimeReport("setup");
1221 //FIXME: force menu if nothing else to look at?
1222 //if (key_dest == key_game && cls.signon != SIGNONS && cls.state == ca_disconnected)
1224 if (cls.signon == SIGNONS)
1229 if (!r_letterbox.value)
1232 SCR_CheckDrawCenterString();
1238 if (cls.signon == SIGNONS)
1242 R_TimeReport_Start();
1244 R_Shadow_EditLights_DrawSelectedLightProperties();
1251 void CL_Screen_NewMap(void)