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_sound = {0, "cl_capturevideo_sound", "0"};
27 cvar_t cl_capturevideo_fps = {0, "cl_capturevideo_fps", "30"};
28 cvar_t cl_capturevideo_rawrgb = {0, "cl_capturevideo_rawrgb", "0"};
29 cvar_t cl_capturevideo_rawyv12 = {0, "cl_capturevideo_rawyv12", "0"};
30 cvar_t r_textshadow = {0, "r_textshadow", "0"};
31 cvar_t r_letterbox = {0, "r_letterbox", "0"};
33 int jpeg_supported = false;
35 qboolean scr_initialized; // ready to draw
37 float scr_con_current;
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 static vec4_t string_colors[] =
52 // LordHavoc: why on earth is cyan before magenta in Quake3?
53 // LordHavoc: note: Doom3 uses white for [0] and [7]
54 {0.0, 0.0, 0.0, 1.0}, // black
55 {1.0, 0.0, 0.0, 1.0}, // red
56 {0.0, 1.0, 0.0, 1.0}, // green
57 {1.0, 1.0, 0.0, 1.0}, // yellow
58 {0.0, 0.0, 1.0, 1.0}, // blue
59 {0.0, 1.0, 1.0, 1.0}, // cyan
60 {1.0, 0.0, 1.0, 1.0}, // magenta
61 {1.0, 1.0, 1.0, 1.0}, // white
62 // [515]'s BX_COLOREDTEXT extension
63 {1.0, 1.0, 1.0, 0.5}, // half transparent
64 {0.5, 0.5, 0.5, 1.0} // half brightness
65 // Black's color table
66 //{1.0, 1.0, 1.0, 1.0},
67 //{1.0, 0.0, 0.0, 1.0},
68 //{0.0, 1.0, 0.0, 1.0},
69 //{0.0, 0.0, 1.0, 1.0},
70 //{1.0, 1.0, 0.0, 1.0},
71 //{0.0, 1.0, 1.0, 1.0},
72 //{1.0, 0.0, 1.0, 1.0},
73 //{0.1, 0.1, 0.1, 1.0}
76 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
78 // color is read and changed in the end
79 void DrawQ_ColoredString( float x, float y, const char *text, int maxlen, float scalex, float scaley, float basered, float basegreen, float baseblue, float basealpha, int flags, int *outcolor )
84 const char *start, *current;
86 if( !outcolor || *outcolor == -1 ) {
87 colorindex = STRING_COLOR_DEFAULT;
89 colorindex = *outcolor;
91 color = string_colors[colorindex];
94 len = (int)strlen( text );
96 len = min( maxlen, (int) strlen( text ) );
98 start = current = text;
100 // check for color control char
101 if( *current == STRING_COLOR_TAG ) {
108 // display the tag char?
109 if( *current == STRING_COLOR_TAG ) {
110 // only display one of the two
115 } else if( '0' <= *current && *current <= '9' ) {
118 colorindex = colorindex * 10 + (*current - '0');
119 // only read as long as it makes a valid index
120 if( colorindex >= (int)STRING_COLORS_COUNT ) {
121 // undo the last operation
127 } while( len > 0 && '0' <= *current && *current <= '9' );
129 color = string_colors[colorindex];
130 // we jump over the color tag
134 // go on and read normal text in until the next control char
135 while( len > 0 && *current != STRING_COLOR_TAG ) {
140 if( start != current ) {
142 DrawQ_String( x, y, start, current - start, scalex, scaley, basered * color[0], basegreen * color[1], baseblue * color[2], basealpha * color[3], flags );
143 // update x to be at the new start position
144 x += (current - start) * scalex;
145 // set start accordingly
150 // return the last colorindex
152 *outcolor = colorindex;
157 ===============================================================================
161 ===============================================================================
164 char scr_centerstring[1024];
165 float scr_centertime_start; // for slow victory printing
166 float scr_centertime_off;
167 int scr_center_lines;
169 int scr_erase_center;
175 Called for important messages that should stay in the center of the screen
179 void SCR_CenterPrint(char *str)
181 strlcpy (scr_centerstring, str, sizeof (scr_centerstring));
182 scr_centertime_off = scr_centertime.value;
183 scr_centertime_start = cl.time;
185 // count the number of lines for centering
186 scr_center_lines = 1;
196 void SCR_DrawCenterString (void)
204 // the finale prints the characters one at a time
206 remaining = scr_printspeed.value * (cl.time - scr_centertime_start);
210 scr_erase_center = 0;
211 start = scr_centerstring;
213 if (scr_center_lines <= 4)
214 y = vid_conheight.integer*0.35;
221 // scan the width of the line
222 for (l=0 ; l<vid_conwidth.integer/8 ; l++)
223 if (start[l] == '\n' || !start[l])
225 x = (vid_conwidth.integer - l*8)/2;
230 DrawQ_ColoredString(x, y, start, l, 8, 8, 1, 1, 1, 1, 0, &color);
238 while (*start && *start != '\n')
243 start++; // skip the \n
247 void SCR_CheckDrawCenterString (void)
249 if (scr_center_lines > scr_erase_lines)
250 scr_erase_lines = scr_center_lines;
252 scr_centertime_off -= host_frametime;
254 // don't draw if this is a normal stats-screen intermission,
255 // only if it is not an intermission, or a finale intermission
256 if (cl.intermission == 1)
258 if (scr_centertime_off <= 0 && !cl.intermission)
260 if (key_dest != key_game)
263 SCR_DrawCenterString ();
271 void SCR_DrawTurtle (void)
275 if (cls.state != ca_connected)
278 if (!scr_showturtle.integer)
281 if (host_frametime < 0.1)
291 DrawQ_Pic (0, 0, "gfx/turtle", 0, 0, 1, 1, 1, 1, 0);
299 void SCR_DrawNet (void)
301 if (cls.state != ca_connected)
303 if (realtime - cl.last_received_message < 0.3)
305 if (cls.demoplayback)
308 DrawQ_Pic (64, 0, "gfx/net", 0, 0, 1, 1, 1, 1, 0);
316 void SCR_DrawPause (void)
320 if (cls.state != ca_connected)
323 if (!scr_showpause.integer) // turn off for screenshots
329 pic = Draw_CachePic ("gfx/pause", true);
330 DrawQ_Pic ((vid_conwidth.integer - pic->width)/2, (vid_conheight.integer - pic->height)/2, "gfx/pause", 0, 0, 1, 1, 1, 1, 0);
337 //=============================================================================
342 SCR_SetUpToDrawConsole
345 void SCR_SetUpToDrawConsole (void)
347 // lines of console to display
352 if (key_dest == key_game && cls.signon != SIGNONS && scr_conforcewhiledisconnected.integer)
353 key_consoleactive |= KEY_CONSOLEACTIVE_FORCED;
355 key_consoleactive &= ~KEY_CONSOLEACTIVE_FORCED;
357 // decide on the height of the console
358 if (key_consoleactive & KEY_CONSOLEACTIVE_USER)
359 conlines = vid_conheight.integer/2; // half screen
361 conlines = 0; // none visible
363 if (scr_conspeed.value)
365 if (scr_con_current > conlines)
367 scr_con_current -= scr_conspeed.value*host_realframetime;
368 if (scr_con_current < conlines)
369 scr_con_current = conlines;
372 else if (scr_con_current < conlines)
374 scr_con_current += scr_conspeed.value*host_realframetime;
375 if (scr_con_current > conlines)
376 scr_con_current = conlines;
380 scr_con_current = conlines;
388 void SCR_DrawConsole (void)
390 if (key_consoleactive & KEY_CONSOLEACTIVE_FORCED)
393 Con_DrawConsole (vid_conheight.integer);
395 else if (scr_con_current)
396 Con_DrawConsole (scr_con_current);
400 if (key_dest == key_game || key_dest == key_message)
401 Con_DrawNotify (); // only draw notify in game
407 SCR_BeginLoadingPlaque
411 void SCR_BeginLoadingPlaque (void)
415 SCR_UpdateLoadingScreen();
418 //=============================================================================
420 char r_speeds_string[1024];
421 int speedstringcount, r_timereport_active;
422 double r_timereport_temp = 0, r_timereport_current = 0, r_timereport_start = 0;
424 void R_TimeReport(char *desc)
430 if (!r_timereport_active || r_showtrispass)
433 r_timereport_temp = r_timereport_current;
434 r_timereport_current = Sys_DoubleTime();
435 t = (int) ((r_timereport_current - r_timereport_temp) * 1000000.0);
437 dpsnprintf(tempbuf, sizeof(tempbuf), "%8i %s", t, desc);
438 length = (int)strlen(tempbuf);
440 tempbuf[length++] = ' ';
442 if (speedstringcount + length > (vid_conwidth.integer / 8))
444 strlcat(r_speeds_string, "\n", sizeof(r_speeds_string));
445 speedstringcount = 0;
447 // skip the space at the beginning if it's the first on the line
448 if (speedstringcount == 0)
450 strlcat(r_speeds_string, tempbuf + 1, sizeof(r_speeds_string));
451 speedstringcount = length - 1;
455 strlcat(r_speeds_string, tempbuf, sizeof(r_speeds_string));
456 speedstringcount += length;
460 void R_TimeReport_Start(void)
462 r_timereport_active = r_speeds.integer && cls.signon == SIGNONS && cls.state == ca_connected;
463 r_speeds_string[0] = 0;
464 if (r_timereport_active)
466 speedstringcount = 0;
467 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]);
468 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);
469 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);
470 sprintf(r_speeds_string + strlen(r_speeds_string), "%6i modeltris%6i meshs%6i meshtris\n", c_alias_polys, c_meshs, c_meshelements / 3);
471 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);
472 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);
473 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);
474 sprintf(r_speeds_string + strlen(r_speeds_string), "precomputed: %6i shadowmeshes%6i shadowtris\n", c_rtcached_shadowmeshes, c_rtcached_shadowtris);
491 c_rt_shadowmeshes = 0;
493 c_rt_lightmeshes = 0;
495 c_rtcached_shadowmeshes = 0;
496 c_rtcached_shadowtris = 0;
499 c_bloomcopypixels = 0;
501 c_bloomdrawpixels = 0;
503 r_timereport_start = Sys_DoubleTime();
507 void R_TimeReport_End(void)
509 r_timereport_current = r_timereport_start;
510 R_TimeReport("total");
512 if (r_timereport_active)
516 for (i = 0;r_speeds_string[i];i++)
517 if (r_speeds_string[i] == '\n')
519 y = vid_conheight.integer - sb_lines - lines * 8;
521 DrawQ_Fill(0, y, vid_conwidth.integer, lines * 8, 0, 0, 0, 0.5, 0);
522 while (r_speeds_string[i])
525 while (r_speeds_string[i] && r_speeds_string[i] != '\n')
528 DrawQ_String(0, y, r_speeds_string + j, i - j, 8, 8, 1, 1, 1, 1, 0);
529 if (r_speeds_string[i] == '\n')
543 void SCR_SizeUp_f (void)
545 Cvar_SetValue ("viewsize",scr_viewsize.value+10);
556 void SCR_SizeDown_f (void)
558 Cvar_SetValue ("viewsize",scr_viewsize.value-10);
561 void CL_Screen_Init(void)
563 Cvar_RegisterVariable (&scr_fov);
564 Cvar_RegisterVariable (&scr_viewsize);
565 Cvar_RegisterVariable (&scr_conspeed);
566 Cvar_RegisterVariable (&scr_conalpha);
567 Cvar_RegisterVariable (&scr_conbrightness);
568 Cvar_RegisterVariable (&scr_conforcewhiledisconnected);
569 Cvar_RegisterVariable (&scr_showram);
570 Cvar_RegisterVariable (&scr_showturtle);
571 Cvar_RegisterVariable (&scr_showpause);
572 Cvar_RegisterVariable (&scr_centertime);
573 Cvar_RegisterVariable (&scr_printspeed);
574 Cvar_RegisterVariable (&vid_conwidth);
575 Cvar_RegisterVariable (&vid_conheight);
576 Cvar_RegisterVariable (&vid_pixelaspect);
577 Cvar_RegisterVariable (&scr_screenshot_jpeg);
578 Cvar_RegisterVariable (&scr_screenshot_jpeg_quality);
579 Cvar_RegisterVariable (&scr_screenshot_gamma);
580 Cvar_RegisterVariable (&cl_capturevideo);
581 Cvar_RegisterVariable (&cl_capturevideo_sound);
582 Cvar_RegisterVariable (&cl_capturevideo_fps);
583 Cvar_RegisterVariable (&cl_capturevideo_rawrgb);
584 Cvar_RegisterVariable (&cl_capturevideo_rawyv12);
585 Cvar_RegisterVariable (&r_textshadow);
586 Cvar_RegisterVariable (&r_letterbox);
588 Cmd_AddCommand ("sizeup",SCR_SizeUp_f);
589 Cmd_AddCommand ("sizedown",SCR_SizeDown_f);
590 Cmd_AddCommand ("screenshot",SCR_ScreenShot_f);
591 Cmd_AddCommand ("envmap", R_Envmap_f);
593 scr_initialized = true;
596 void DrawQ_Clear(void)
598 r_refdef.drawqueuesize = 0;
601 static int picelements[6] = {0, 1, 2, 0, 2, 3};
602 void DrawQ_Pic(float x, float y, const char *picname, float width, float height, float red, float green, float blue, float alpha, int flags)
604 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);
607 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)
612 if (alpha < (1.0f / 255.0f))
615 len = (int)strlen(string);
617 for (len = 0;len < maxlen && string[len];len++);
618 for (;len > 0 && string[0] == ' ';string++, x += scalex, len--);
619 for (;len > 0 && string[len - 1] == ' ';len--);
622 if (x >= vid_conwidth.integer || y >= vid_conheight.integer || x < (-scalex * len) || y < (-scaley))
624 size = sizeof(*dq) + ((len + 1 + 3) & ~3);
625 if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
627 red = bound(0, red, 1);
628 green = bound(0, green, 1);
629 blue = bound(0, blue, 1);
630 alpha = bound(0, alpha, 1);
631 dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
633 dq->command = DRAWQUEUE_STRING;
635 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));
640 out = (char *)(dq + 1);
641 memcpy(out, string, len);
643 r_refdef.drawqueuesize += dq->size;
646 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)
648 if (r_textshadow.integer)
649 DrawQ_String_Real(x+scalex*0.25,y+scaley*0.25,string,maxlen,scalex,scaley,0,0,0,alpha*0.8,flags);
651 DrawQ_String_Real(x,y,string,maxlen,scalex,scaley,red,green,blue,alpha,flags);
656 void DrawQ_Fill (float x, float y, float w, float h, float red, float green, float blue, float alpha, int flags)
658 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);
661 void DrawQ_SuperPic(float x, float y, const 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)
665 drawqueuemesh_t mesh;
666 memset(&mesh, 0, sizeof(mesh));
667 if (picname && picname[0])
669 pic = Draw_CachePic(picname, false);
673 height = pic->height;
674 mesh.texture = pic->tex;
676 mesh.num_triangles = 2;
677 mesh.num_vertices = 4;
678 mesh.data_element3i = picelements;
679 mesh.data_vertex3f = floats;
680 mesh.data_texcoord2f = floats + 12;
681 mesh.data_color4f = floats + 20;
682 memset(floats, 0, sizeof(floats));
683 mesh.data_vertex3f[0] = mesh.data_vertex3f[9] = x;
684 mesh.data_vertex3f[1] = mesh.data_vertex3f[4] = y;
685 mesh.data_vertex3f[3] = mesh.data_vertex3f[6] = x + width;
686 mesh.data_vertex3f[7] = mesh.data_vertex3f[10] = y + height;
687 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;
688 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;
689 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;
690 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;
691 DrawQ_Mesh (&mesh, flags);
694 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
701 size += sizeof(drawqueuemesh_t);
702 size += sizeof(int[3]) * mesh->num_triangles;
703 size += sizeof(float[3]) * mesh->num_vertices;
704 size += sizeof(float[2]) * mesh->num_vertices;
705 size += sizeof(float[4]) * mesh->num_vertices;
706 if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
708 dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
710 dq->command = DRAWQUEUE_MESH;
717 p = (void *)(dq + 1);
718 m = p;p = (qbyte*)p + sizeof(drawqueuemesh_t);
719 m->num_triangles = mesh->num_triangles;
720 m->num_vertices = mesh->num_vertices;
721 m->texture = mesh->texture;
722 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]);
723 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]);
724 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]);
725 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]);
726 r_refdef.drawqueuesize += dq->size;
729 void DrawQ_SetClipArea(float x, float y, float width, float height)
732 if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
734 Con_DPrint("DrawQueue full !\n");
737 dq = (void*) (r_refdef.drawqueue + r_refdef.drawqueuesize);
738 dq->size = sizeof(*dq);
739 dq->command = DRAWQUEUE_SETCLIP;
747 r_refdef.drawqueuesize += dq->size;
750 void DrawQ_ResetClipArea(void)
753 if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
755 Con_DPrint("DrawQueue full !\n");
758 dq = (void*) (r_refdef.drawqueue + r_refdef.drawqueuesize);
759 dq->size = sizeof(*dq);
760 dq->command = DRAWQUEUE_RESETCLIP;
768 r_refdef.drawqueuesize += dq->size;
776 void SCR_ScreenShot_f (void)
778 static int shotnumber;
779 static char oldname[MAX_QPATH];
780 char base[MAX_QPATH];
781 char filename[MAX_QPATH];
785 qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
787 sprintf (base, "screenshots/%s", scr_screenshot_name.string);
789 if (strcmp (oldname, scr_screenshot_name.string))
791 sprintf(oldname, "%s", scr_screenshot_name.string);
795 // find a file name to save it to
796 for (;shotnumber < 1000000;shotnumber++)
797 if (!FS_SysFileExists(va("%s/%s%06d.tga", fs_gamedir, base, shotnumber)) && !FS_SysFileExists(va("%s/%s%06d.jpg", fs_gamedir, base, shotnumber)))
799 if (shotnumber >= 1000000)
801 Con_Print("SCR_ScreenShot_f: Couldn't create the image file\n");
805 sprintf(filename, "%s%06d.%s", base, shotnumber, jpeg ? "jpg" : "tga");
807 buffer1 = Mem_Alloc(tempmempool, vid.width * vid.height * 3);
808 buffer2 = Mem_Alloc(tempmempool, vid.width * vid.height * 3);
809 buffer3 = Mem_Alloc(tempmempool, vid.width * vid.height * 3 + 18);
811 if (SCR_ScreenShot (filename, buffer1, buffer2, buffer3, 0, 0, vid.width, vid.height, false, false, false, jpeg, true))
812 Con_Printf("Wrote %s\n", filename);
814 Con_Printf("unable to write %s\n", filename);
823 typedef enum capturevideoformat_e
825 CAPTUREVIDEOFORMAT_TARGA,
826 CAPTUREVIDEOFORMAT_JPEG,
827 CAPTUREVIDEOFORMAT_RAWRGB,
828 CAPTUREVIDEOFORMAT_RAWYV12
830 capturevideoformat_t;
832 qboolean cl_capturevideo_active = false;
833 capturevideoformat_t cl_capturevideo_format;
834 static double cl_capturevideo_starttime = 0;
835 double cl_capturevideo_framerate = 0;
836 static int cl_capturevideo_soundrate = 0;
837 static int cl_capturevideo_frame = 0;
838 static qbyte *cl_capturevideo_buffer = NULL;
839 static qfile_t *cl_capturevideo_videofile = NULL;
840 qfile_t *cl_capturevideo_soundfile = NULL;
841 static short cl_capturevideo_rgbtoyuvscaletable[3][3][256];
842 static unsigned char cl_capturevideo_yuvnormalizetable[3][256];
843 //static unsigned char cl_capturevideo_rgbgammatable[3][256];
845 void SCR_CaptureVideo_BeginVideo(void)
850 if (cl_capturevideo_active)
852 // soundrate is figured out on the first SoundFrame
853 cl_capturevideo_active = true;
854 cl_capturevideo_starttime = Sys_DoubleTime();
855 cl_capturevideo_framerate = bound(1, cl_capturevideo_fps.value, 1000);
856 cl_capturevideo_soundrate = 0;
857 cl_capturevideo_frame = 0;
858 cl_capturevideo_buffer = Mem_Alloc(tempmempool, vid.width * vid.height * (3+3+3) + 18);
859 gamma = 1.0/scr_screenshot_gamma.value;
862 for (i = 0;i < 256;i++)
864 unsigned char j = (unsigned char)bound(0, 255*pow(i/255.0, gamma), 255);
865 cl_capturevideo_rgbgammatable[0][i] = j;
866 cl_capturevideo_rgbgammatable[1][i] = j;
867 cl_capturevideo_rgbgammatable[2][i] = j;
871 R = Y + 1.4075 * (Cr - 128);
872 G = Y + -0.3455 * (Cb - 128) + -0.7169 * (Cr - 128);
873 B = Y + 1.7790 * (Cb - 128);
874 Y = R * .299 + G * .587 + B * .114;
875 Cb = R * -.169 + G * -.332 + B * .500 + 128.;
876 Cr = R * .500 + G * -.419 + B * -.0813 + 128.;
878 for (i = 0;i < 256;i++)
880 g = 255*pow(i/255.0, gamma);
881 // Y weights from RGB
882 cl_capturevideo_rgbtoyuvscaletable[0][0][i] = (short)(g * 0.299);
883 cl_capturevideo_rgbtoyuvscaletable[0][1][i] = (short)(g * 0.587);
884 cl_capturevideo_rgbtoyuvscaletable[0][2][i] = (short)(g * 0.114);
885 // Cb weights from RGB
886 cl_capturevideo_rgbtoyuvscaletable[1][0][i] = (short)(g * -0.169);
887 cl_capturevideo_rgbtoyuvscaletable[1][1][i] = (short)(g * -0.332);
888 cl_capturevideo_rgbtoyuvscaletable[1][2][i] = (short)(g * 0.500);
889 // Cr weights from RGB
890 cl_capturevideo_rgbtoyuvscaletable[2][0][i] = (short)(g * 0.500);
891 cl_capturevideo_rgbtoyuvscaletable[2][1][i] = (short)(g * -0.419);
892 cl_capturevideo_rgbtoyuvscaletable[2][2][i] = (short)(g * -0.0813);
893 // range reduction of YCbCr to valid signal range
894 cl_capturevideo_yuvnormalizetable[0][i] = 16 + i * (236-16) / 256;
895 cl_capturevideo_yuvnormalizetable[1][i] = 16 + i * (240-16) / 256;
896 cl_capturevideo_yuvnormalizetable[2][i] = 16 + i * (240-16) / 256;
899 if (cl_capturevideo_rawrgb.integer)
901 cl_capturevideo_format = CAPTUREVIDEOFORMAT_RAWRGB;
902 cl_capturevideo_videofile = FS_Open ("video/dpvideo.rgb", "wb", false, true);
904 else if (cl_capturevideo_rawyv12.integer)
906 cl_capturevideo_format = CAPTUREVIDEOFORMAT_RAWYV12;
907 cl_capturevideo_videofile = FS_Open ("video/dpvideo.yv12", "wb", false, true);
909 else if (scr_screenshot_jpeg.integer)
911 cl_capturevideo_format = CAPTUREVIDEOFORMAT_JPEG;
912 cl_capturevideo_videofile = NULL;
916 cl_capturevideo_format = CAPTUREVIDEOFORMAT_TARGA;
917 cl_capturevideo_videofile = NULL;
920 if (cl_capturevideo_sound.integer)
922 cl_capturevideo_soundfile = FS_Open ("video/dpvideo.wav", "wb", false, true);
923 // wave header will be filled out when video ends
925 FS_Write (cl_capturevideo_soundfile, out, 44);
928 cl_capturevideo_soundfile = NULL;
931 void SCR_CaptureVideo_EndVideo(void)
935 if (!cl_capturevideo_active)
937 cl_capturevideo_active = false;
939 if (cl_capturevideo_videofile)
941 FS_Close(cl_capturevideo_videofile);
942 cl_capturevideo_videofile = NULL;
945 // finish the wave file
946 if (cl_capturevideo_soundfile)
948 i = (int)FS_Tell (cl_capturevideo_soundfile);
949 //"RIFF", (int) unknown (chunk size), "WAVE",
950 //"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
951 //"data", (int) unknown (chunk size)
952 memcpy (out, "RIFF****WAVEfmt \x10\x00\x00\x00\x01\x00\x02\x00********\x04\x00\x10\0data****", 44);
953 // the length of the whole RIFF chunk
956 out[5] = (n >> 8) & 0xFF;
957 out[6] = (n >> 16) & 0xFF;
958 out[7] = (n >> 24) & 0xFF;
960 n = cl_capturevideo_soundrate;
961 out[24] = (n) & 0xFF;
962 out[25] = (n >> 8) & 0xFF;
963 out[26] = (n >> 16) & 0xFF;
964 out[27] = (n >> 24) & 0xFF;
965 // bytes per second (rate * channels * bytes per channel)
966 n = cl_capturevideo_soundrate * 2 * 2;
967 out[28] = (n) & 0xFF;
968 out[29] = (n >> 8) & 0xFF;
969 out[30] = (n >> 16) & 0xFF;
970 out[31] = (n >> 24) & 0xFF;
971 // the length of the data chunk
973 out[40] = (n) & 0xFF;
974 out[41] = (n >> 8) & 0xFF;
975 out[42] = (n >> 16) & 0xFF;
976 out[43] = (n >> 24) & 0xFF;
977 FS_Seek (cl_capturevideo_soundfile, 0, SEEK_SET);
978 FS_Write (cl_capturevideo_soundfile, out, 44);
979 FS_Close (cl_capturevideo_soundfile);
980 cl_capturevideo_soundfile = NULL;
983 if (cl_capturevideo_buffer)
985 Mem_Free (cl_capturevideo_buffer);
986 cl_capturevideo_buffer = NULL;
989 cl_capturevideo_starttime = 0;
990 cl_capturevideo_framerate = 0;
991 cl_capturevideo_frame = 0;
994 qboolean SCR_CaptureVideo_VideoFrame(int newframenum)
996 int x = 0, y = 0, width = vid.width, height = vid.height;
997 unsigned char *b, *out;
999 int outoffset = (width/2)*(height/2);
1000 //return SCR_ScreenShot(filename, cl_capturevideo_buffer, cl_capturevideo_buffer + vid.width * vid.height * 3, cl_capturevideo_buffer + vid.width * vid.height * 6, 0, 0, vid.width, vid.height, false, false, false, jpeg, true);
1001 // speed is critical here, so do saving as directly as possible
1002 switch (cl_capturevideo_format)
1004 case CAPTUREVIDEOFORMAT_RAWYV12:
1005 // FIXME: width/height must be multiple of 2, enforce this?
1006 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
1008 // process one line at a time, and CbCr every other line at 2 pixel intervals
1009 for (y = 0;y < height;y++)
1012 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++)
1013 *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]]];
1016 // 2x2 Cb and Cr planes
1018 // low quality, no averaging
1019 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++)
1022 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];
1024 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];
1027 // high quality, averaging
1028 int inpitch = width*3;
1029 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++)
1031 int blockr, blockg, blockb;
1032 blockr = (b[0] + b[3] + b[inpitch+0] + b[inpitch+3]) >> 2;
1033 blockg = (b[1] + b[4] + b[inpitch+1] + b[inpitch+4]) >> 2;
1034 blockb = (b[2] + b[5] + b[inpitch+2] + b[inpitch+5]) >> 2;
1036 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];
1038 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];
1043 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
1044 if (!FS_Write (cl_capturevideo_videofile, cl_capturevideo_buffer + width*height*3, width*height+(width/2)*(height/2)*2))
1047 case CAPTUREVIDEOFORMAT_RAWRGB:
1048 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
1050 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
1051 if (!FS_Write (cl_capturevideo_videofile, cl_capturevideo_buffer, width*height*3))
1054 case CAPTUREVIDEOFORMAT_JPEG:
1055 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
1057 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
1059 sprintf(filename, "video/dp%06d.jpg", cl_capturevideo_frame);
1060 if (!JPEG_SaveImage_preflipped (filename, width, height, cl_capturevideo_buffer))
1064 case CAPTUREVIDEOFORMAT_TARGA:
1065 //return Image_WriteTGARGB_preflipped (filename, width, height, cl_capturevideo_buffer, cl_capturevideo_buffer + vid.width * vid.height * 3, );
1066 memset (cl_capturevideo_buffer, 0, 18);
1067 cl_capturevideo_buffer[2] = 2; // uncompressed type
1068 cl_capturevideo_buffer[12] = (width >> 0) & 0xFF;
1069 cl_capturevideo_buffer[13] = (width >> 8) & 0xFF;
1070 cl_capturevideo_buffer[14] = (height >> 0) & 0xFF;
1071 cl_capturevideo_buffer[15] = (height >> 8) & 0xFF;
1072 cl_capturevideo_buffer[16] = 24; // pixel size
1073 qglReadPixels (x, y, width, height, GL_BGR, GL_UNSIGNED_BYTE, cl_capturevideo_buffer + 18);
1075 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
1077 sprintf(filename, "video/dp%06d.tga", cl_capturevideo_frame);
1078 if (!FS_WriteFile (filename, cl_capturevideo_buffer, width*height*3 + 18))
1087 void SCR_CaptureVideo_SoundFrame(qbyte *bufstereo16le, size_t length, int rate)
1089 if (!cl_capturevideo_soundfile)
1091 cl_capturevideo_soundrate = rate;
1092 if (FS_Write (cl_capturevideo_soundfile, bufstereo16le, 4 * length) < (fs_offset_t)(4 * length))
1094 Cvar_SetValueQuick(&cl_capturevideo, 0);
1095 Con_Printf("video sound saving failed on frame %i, out of disk space? stopping video capture.\n", cl_capturevideo_frame);
1096 SCR_CaptureVideo_EndVideo();
1100 void SCR_CaptureVideo(void)
1103 if (cl_capturevideo.integer && r_render.integer)
1105 if (!cl_capturevideo_active)
1106 SCR_CaptureVideo_BeginVideo();
1107 if (cl_capturevideo_framerate != cl_capturevideo_fps.value)
1109 Con_Printf("You can not change the video framerate while recording a video.\n");
1110 Cvar_SetValueQuick(&cl_capturevideo_fps, cl_capturevideo_framerate);
1112 if (cl_capturevideo_soundfile)
1114 // preserve sound sync by duplicating frames when running slow
1115 newframenum = (Sys_DoubleTime() - cl_capturevideo_starttime) * cl_capturevideo_framerate;
1118 newframenum = cl_capturevideo_frame + 1;
1119 // if falling behind more than one second, stop
1120 if (newframenum - cl_capturevideo_frame > (int)ceil(cl_capturevideo_framerate))
1122 Cvar_SetValueQuick(&cl_capturevideo, 0);
1123 Con_Printf("video saving failed on frame %i, your machine is too slow for this capture speed.\n", cl_capturevideo_frame);
1124 SCR_CaptureVideo_EndVideo();
1128 if (!SCR_CaptureVideo_VideoFrame(newframenum))
1130 Cvar_SetValueQuick(&cl_capturevideo, 0);
1131 Con_Printf("video saving failed on frame %i, out of disk space? stopping video capture.\n", cl_capturevideo_frame);
1132 SCR_CaptureVideo_EndVideo();
1135 else if (cl_capturevideo_active)
1136 SCR_CaptureVideo_EndVideo();
1143 Grab six views for environment mapping tests
1150 qboolean flipx, flipy, flipdiagonaly;
1154 {{ 0, 0, 0}, "rt", false, false, false},
1155 {{ 0, 270, 0}, "ft", false, false, false},
1156 {{ 0, 180, 0}, "lf", false, false, false},
1157 {{ 0, 90, 0}, "bk", false, false, false},
1158 {{-90, 180, 0}, "up", true, true, false},
1159 {{ 90, 180, 0}, "dn", true, true, false},
1161 {{ 0, 0, 0}, "px", true, true, true},
1162 {{ 0, 90, 0}, "py", false, true, false},
1163 {{ 0, 180, 0}, "nx", false, false, true},
1164 {{ 0, 270, 0}, "ny", true, false, false},
1165 {{-90, 180, 0}, "pz", false, false, true},
1166 {{ 90, 180, 0}, "nz", false, false, true}
1169 static void R_Envmap_f (void)
1172 char filename[256], basename[256];
1177 if (Cmd_Argc() != 3)
1179 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");
1183 strlcpy (basename, Cmd_Argv(1), sizeof (basename));
1184 size = atoi(Cmd_Argv(2));
1185 if (size != 128 && size != 256 && size != 512 && size != 1024)
1187 Con_Print("envmap: size must be one of 128, 256, 512, or 1024\n");
1190 if (size > vid.width || size > vid.height)
1192 Con_Print("envmap: your resolution is not big enough to render that size\n");
1200 r_refdef.width = size;
1201 r_refdef.height = size;
1203 r_refdef.fov_x = 90;
1204 r_refdef.fov_y = 90;
1206 buffer1 = Mem_Alloc(tempmempool, size * size * 3);
1207 buffer2 = Mem_Alloc(tempmempool, size * size * 3);
1208 buffer3 = Mem_Alloc(tempmempool, size * size * 3 + 18);
1210 for (j = 0;j < 12;j++)
1212 sprintf(filename, "env/%s%s.tga", basename, envmapinfo[j].name);
1213 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);
1218 SCR_ScreenShot(filename, buffer1, buffer2, buffer3, 0, vid.height - (r_refdef.y + r_refdef.height), size, size, envmapinfo[j].flipx, envmapinfo[j].flipy, envmapinfo[j].flipdiagonaly, false, false);
1228 //=============================================================================
1230 // LordHavoc: SHOWLMP stuff
1231 #define SHOWLMP_MAXLABELS 256
1232 typedef struct showlmp_s
1242 showlmp_t showlmp[SHOWLMP_MAXLABELS];
1244 void SHOWLMP_decodehide(void)
1248 lmplabel = MSG_ReadString();
1249 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1250 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
1252 showlmp[i].isactive = false;
1257 void SHOWLMP_decodeshow(void)
1260 qbyte lmplabel[256], picname[256];
1262 strlcpy (lmplabel,MSG_ReadString(), sizeof (lmplabel));
1263 strlcpy (picname, MSG_ReadString(), sizeof (picname));
1264 if (gamemode == GAME_NEHAHRA) // LordHavoc: nasty old legacy junk
1271 x = MSG_ReadShort();
1272 y = MSG_ReadShort();
1275 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1276 if (showlmp[i].isactive)
1278 if (strcmp(showlmp[i].label, lmplabel) == 0)
1281 break; // drop out to replace it
1284 else if (k < 0) // find first empty one to replace
1287 return; // none found to replace
1288 // change existing one
1289 showlmp[k].isactive = true;
1290 strlcpy (showlmp[k].label, lmplabel, sizeof (showlmp[k].label));
1291 strlcpy (showlmp[k].pic, picname, sizeof (showlmp[k].pic));
1296 void SHOWLMP_drawall(void)
1299 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1300 if (showlmp[i].isactive)
1301 DrawQ_Pic(showlmp[i].x, showlmp[i].y, showlmp[i].pic, 0, 0, 1, 1, 1, 1, 0);
1304 void SHOWLMP_clear(void)
1307 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1308 showlmp[i].isactive = false;
1311 void CL_SetupScreenSize(void)
1313 float conwidth, conheight;
1315 VID_UpdateGamma(false);
1317 conwidth = bound(320, vid_conwidth.value, 2048);
1318 conheight = bound(200, vid_conheight.value, 1536);
1319 if (vid_conwidth.value != conwidth)
1320 Cvar_SetValue("vid_conwidth", conwidth);
1321 if (vid_conheight.value != conheight)
1322 Cvar_SetValue("vid_conheight", conheight);
1324 vid_conwidth.integer = vid_conwidth.integer;
1325 vid_conheight.integer = vid_conheight.integer;
1327 SCR_SetUpToDrawConsole();
1330 extern void R_Shadow_EditLights_DrawSelectedLightProperties(void);
1331 void CL_UpdateScreen(void)
1333 if (!scr_initialized || !con_initialized || vid_hidden)
1334 return; // not initialized yet
1336 // don't allow cheats in multiplayer
1337 if (!cl.islocalgame && cl.worldmodel)
1339 if (r_fullbright.integer != 0)
1340 Cvar_Set ("r_fullbright", "0");
1341 if (r_ambient.value != 0)
1342 Cvar_Set ("r_ambient", "0");
1346 if (scr_viewsize.value < 30)
1347 Cvar_Set ("viewsize","30");
1348 if (scr_viewsize.value > 120)
1349 Cvar_Set ("viewsize","120");
1351 // bound field of view
1352 if (scr_fov.value < 1)
1353 Cvar_Set ("fov","1");
1354 if (scr_fov.value > 170)
1355 Cvar_Set ("fov","170");
1357 // intermission is always full screen
1358 if (cl.intermission)
1362 if (scr_viewsize.value >= 120)
1363 sb_lines = 0; // no status bar at all
1364 else if (scr_viewsize.value >= 110)
1365 sb_lines = 24; // no inventory
1370 r_refdef.colormask[0] = 1;
1371 r_refdef.colormask[1] = 1;
1372 r_refdef.colormask[2] = 1;
1376 if (cls.signon == SIGNONS)
1377 R_TimeReport("other");
1379 CL_SetupScreenSize();
1383 if (cls.signon == SIGNONS)
1384 R_TimeReport("setup");
1386 //FIXME: force menu if nothing else to look at?
1387 //if (key_dest == key_game && cls.signon != SIGNONS && cls.state == ca_disconnected)
1389 if (cls.signon == SIGNONS)
1394 if (!r_letterbox.value)
1397 SCR_CheckDrawCenterString();
1403 if (cls.signon == SIGNONS)
1407 R_TimeReport_Start();
1409 R_Shadow_EditLights_DrawSelectedLightProperties();
1416 void CL_Screen_NewMap(void)