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)
203 // the finale prints the characters one at a time
205 remaining = scr_printspeed.value * (cl.time - scr_centertime_start);
209 scr_erase_center = 0;
210 start = scr_centerstring;
212 if (scr_center_lines <= 4)
213 y = vid_conheight.integer*0.35;
219 // scan the width of the line
220 for (l=0 ; l<vid_conwidth.integer/8 ; l++)
221 if (start[l] == '\n' || !start[l])
223 x = (vid_conwidth.integer - l*8)/2;
228 DrawQ_String(x, y, start, l, 8, 8, 1, 1, 1, 1, 0);
236 while (*start && *start != '\n')
241 start++; // skip the \n
245 void SCR_CheckDrawCenterString (void)
247 if (scr_center_lines > scr_erase_lines)
248 scr_erase_lines = scr_center_lines;
250 scr_centertime_off -= host_frametime;
252 // don't draw if this is a normal stats-screen intermission,
253 // only if it is not an intermission, or a finale intermission
254 if (cl.intermission == 1)
256 if (scr_centertime_off <= 0 && !cl.intermission)
258 if (key_dest != key_game)
261 SCR_DrawCenterString ();
269 void SCR_DrawTurtle (void)
273 if (cls.state != ca_connected)
276 if (!scr_showturtle.integer)
279 if (host_frametime < 0.1)
289 DrawQ_Pic (0, 0, "gfx/turtle", 0, 0, 1, 1, 1, 1, 0);
297 void SCR_DrawNet (void)
299 if (cls.state != ca_connected)
301 if (realtime - cl.last_received_message < 0.3)
303 if (cls.demoplayback)
306 DrawQ_Pic (64, 0, "gfx/net", 0, 0, 1, 1, 1, 1, 0);
314 void SCR_DrawPause (void)
318 if (cls.state != ca_connected)
321 if (!scr_showpause.integer) // turn off for screenshots
327 pic = Draw_CachePic ("gfx/pause", true);
328 DrawQ_Pic ((vid_conwidth.integer - pic->width)/2, (vid_conheight.integer - pic->height)/2, "gfx/pause", 0, 0, 1, 1, 1, 1, 0);
335 //=============================================================================
340 SCR_SetUpToDrawConsole
343 void SCR_SetUpToDrawConsole (void)
345 // lines of console to display
350 if (key_dest == key_game && cls.signon != SIGNONS && scr_conforcewhiledisconnected.integer)
351 key_consoleactive |= KEY_CONSOLEACTIVE_FORCED;
353 key_consoleactive &= ~KEY_CONSOLEACTIVE_FORCED;
355 // decide on the height of the console
356 if (key_consoleactive & KEY_CONSOLEACTIVE_USER)
357 conlines = vid_conheight.integer/2; // half screen
359 conlines = 0; // none visible
361 if (scr_conspeed.value)
363 if (scr_con_current > conlines)
365 scr_con_current -= scr_conspeed.value*host_realframetime;
366 if (scr_con_current < conlines)
367 scr_con_current = conlines;
370 else if (scr_con_current < conlines)
372 scr_con_current += scr_conspeed.value*host_realframetime;
373 if (scr_con_current > conlines)
374 scr_con_current = conlines;
378 scr_con_current = conlines;
386 void SCR_DrawConsole (void)
388 if (key_consoleactive & KEY_CONSOLEACTIVE_FORCED)
391 Con_DrawConsole (vid_conheight.integer);
393 else if (scr_con_current)
394 Con_DrawConsole (scr_con_current);
398 if (key_dest == key_game || key_dest == key_message)
399 Con_DrawNotify (); // only draw notify in game
405 SCR_BeginLoadingPlaque
409 void SCR_BeginLoadingPlaque (void)
413 SCR_UpdateLoadingScreen();
416 //=============================================================================
418 char r_speeds_string[1024];
419 int speedstringcount, r_timereport_active;
420 double r_timereport_temp = 0, r_timereport_current = 0, r_timereport_start = 0;
422 void R_TimeReport(char *desc)
428 if (!r_timereport_active || r_showtrispass)
431 r_timereport_temp = r_timereport_current;
432 r_timereport_current = Sys_DoubleTime();
433 t = (int) ((r_timereport_current - r_timereport_temp) * 1000000.0);
435 dpsnprintf(tempbuf, sizeof(tempbuf), "%8i %s", t, desc);
436 length = (int)strlen(tempbuf);
438 tempbuf[length++] = ' ';
440 if (speedstringcount + length > (vid_conwidth.integer / 8))
442 strlcat(r_speeds_string, "\n", sizeof(r_speeds_string));
443 speedstringcount = 0;
445 // skip the space at the beginning if it's the first on the line
446 if (speedstringcount == 0)
448 strlcat(r_speeds_string, tempbuf + 1, sizeof(r_speeds_string));
449 speedstringcount = length - 1;
453 strlcat(r_speeds_string, tempbuf, sizeof(r_speeds_string));
454 speedstringcount += length;
458 void R_TimeReport_Start(void)
460 r_timereport_active = r_speeds.integer && cls.signon == SIGNONS && cls.state == ca_connected;
461 r_speeds_string[0] = 0;
462 if (r_timereport_active)
464 speedstringcount = 0;
465 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]);
466 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);
467 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);
468 sprintf(r_speeds_string + strlen(r_speeds_string), "%6i modeltris%6i meshs%6i meshtris\n", c_alias_polys, c_meshs, c_meshelements / 3);
469 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);
470 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);
471 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);
472 sprintf(r_speeds_string + strlen(r_speeds_string), "precomputed: %6i shadowmeshes%6i shadowtris\n", c_rtcached_shadowmeshes, c_rtcached_shadowtris);
489 c_rt_shadowmeshes = 0;
491 c_rt_lightmeshes = 0;
493 c_rtcached_shadowmeshes = 0;
494 c_rtcached_shadowtris = 0;
497 c_bloomcopypixels = 0;
499 c_bloomdrawpixels = 0;
501 r_timereport_start = Sys_DoubleTime();
505 void R_TimeReport_End(void)
507 r_timereport_current = r_timereport_start;
508 R_TimeReport("total");
510 if (r_timereport_active)
514 for (i = 0;r_speeds_string[i];i++)
515 if (r_speeds_string[i] == '\n')
517 y = vid_conheight.integer - sb_lines - lines * 8;
519 DrawQ_Fill(0, y, vid_conwidth.integer, lines * 8, 0, 0, 0, 0.5, 0);
520 while (r_speeds_string[i])
523 while (r_speeds_string[i] && r_speeds_string[i] != '\n')
526 DrawQ_String(0, y, r_speeds_string + j, i - j, 8, 8, 1, 1, 1, 1, 0);
527 if (r_speeds_string[i] == '\n')
541 void SCR_SizeUp_f (void)
543 Cvar_SetValue ("viewsize",scr_viewsize.value+10);
554 void SCR_SizeDown_f (void)
556 Cvar_SetValue ("viewsize",scr_viewsize.value-10);
559 void CL_Screen_Init(void)
561 Cvar_RegisterVariable (&scr_fov);
562 Cvar_RegisterVariable (&scr_viewsize);
563 Cvar_RegisterVariable (&scr_conspeed);
564 Cvar_RegisterVariable (&scr_conalpha);
565 Cvar_RegisterVariable (&scr_conbrightness);
566 Cvar_RegisterVariable (&scr_conforcewhiledisconnected);
567 Cvar_RegisterVariable (&scr_showram);
568 Cvar_RegisterVariable (&scr_showturtle);
569 Cvar_RegisterVariable (&scr_showpause);
570 Cvar_RegisterVariable (&scr_centertime);
571 Cvar_RegisterVariable (&scr_printspeed);
572 Cvar_RegisterVariable (&vid_conwidth);
573 Cvar_RegisterVariable (&vid_conheight);
574 Cvar_RegisterVariable (&vid_pixelaspect);
575 Cvar_RegisterVariable (&scr_screenshot_jpeg);
576 Cvar_RegisterVariable (&scr_screenshot_jpeg_quality);
577 Cvar_RegisterVariable (&scr_screenshot_gamma);
578 Cvar_RegisterVariable (&cl_capturevideo);
579 Cvar_RegisterVariable (&cl_capturevideo_sound);
580 Cvar_RegisterVariable (&cl_capturevideo_fps);
581 Cvar_RegisterVariable (&cl_capturevideo_rawrgb);
582 Cvar_RegisterVariable (&cl_capturevideo_rawyv12);
583 Cvar_RegisterVariable (&r_textshadow);
584 Cvar_RegisterVariable (&r_letterbox);
586 Cmd_AddCommand ("sizeup",SCR_SizeUp_f);
587 Cmd_AddCommand ("sizedown",SCR_SizeDown_f);
588 Cmd_AddCommand ("screenshot",SCR_ScreenShot_f);
589 Cmd_AddCommand ("envmap", R_Envmap_f);
591 scr_initialized = true;
594 void DrawQ_Clear(void)
596 r_refdef.drawqueuesize = 0;
599 static int picelements[6] = {0, 1, 2, 0, 2, 3};
600 void DrawQ_Pic(float x, float y, const char *picname, float width, float height, float red, float green, float blue, float alpha, int flags)
602 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);
605 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)
610 if (alpha < (1.0f / 255.0f))
613 len = (int)strlen(string);
615 for (len = 0;len < maxlen && string[len];len++);
616 for (;len > 0 && string[0] == ' ';string++, x += scalex, len--);
617 for (;len > 0 && string[len - 1] == ' ';len--);
620 if (x >= vid_conwidth.integer || y >= vid_conheight.integer || x < (-scalex * len) || y < (-scaley))
622 size = sizeof(*dq) + ((len + 1 + 3) & ~3);
623 if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
625 red = bound(0, red, 1);
626 green = bound(0, green, 1);
627 blue = bound(0, blue, 1);
628 alpha = bound(0, alpha, 1);
629 dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
631 dq->command = DRAWQUEUE_STRING;
633 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));
638 out = (char *)(dq + 1);
639 memcpy(out, string, len);
641 r_refdef.drawqueuesize += dq->size;
644 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)
646 if (r_textshadow.integer)
647 DrawQ_String_Real(x+scalex*0.25,y+scaley*0.25,string,maxlen,scalex,scaley,0,0,0,alpha*0.8,flags);
649 DrawQ_String_Real(x,y,string,maxlen,scalex,scaley,red,green,blue,alpha,flags);
654 void DrawQ_Fill (float x, float y, float w, float h, float red, float green, float blue, float alpha, int flags)
656 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);
659 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)
663 drawqueuemesh_t mesh;
664 memset(&mesh, 0, sizeof(mesh));
665 if (picname && picname[0])
667 pic = Draw_CachePic(picname, false);
671 height = pic->height;
672 mesh.texture = pic->tex;
674 mesh.num_triangles = 2;
675 mesh.num_vertices = 4;
676 mesh.data_element3i = picelements;
677 mesh.data_vertex3f = floats;
678 mesh.data_texcoord2f = floats + 12;
679 mesh.data_color4f = floats + 20;
680 memset(floats, 0, sizeof(floats));
681 mesh.data_vertex3f[0] = mesh.data_vertex3f[9] = x;
682 mesh.data_vertex3f[1] = mesh.data_vertex3f[4] = y;
683 mesh.data_vertex3f[3] = mesh.data_vertex3f[6] = x + width;
684 mesh.data_vertex3f[7] = mesh.data_vertex3f[10] = y + height;
685 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;
686 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;
687 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;
688 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;
689 DrawQ_Mesh (&mesh, flags);
692 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
699 size += sizeof(drawqueuemesh_t);
700 size += sizeof(int[3]) * mesh->num_triangles;
701 size += sizeof(float[3]) * mesh->num_vertices;
702 size += sizeof(float[2]) * mesh->num_vertices;
703 size += sizeof(float[4]) * mesh->num_vertices;
704 if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
706 dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
708 dq->command = DRAWQUEUE_MESH;
715 p = (void *)(dq + 1);
716 m = p;p = (qbyte*)p + sizeof(drawqueuemesh_t);
717 m->num_triangles = mesh->num_triangles;
718 m->num_vertices = mesh->num_vertices;
719 m->texture = mesh->texture;
720 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]);
721 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]);
722 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]);
723 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]);
724 r_refdef.drawqueuesize += dq->size;
727 void DrawQ_SetClipArea(float x, float y, float width, float height)
730 if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
732 Con_DPrint("DrawQueue full !\n");
735 dq = (void*) (r_refdef.drawqueue + r_refdef.drawqueuesize);
736 dq->size = sizeof(*dq);
737 dq->command = DRAWQUEUE_SETCLIP;
745 r_refdef.drawqueuesize += dq->size;
748 void DrawQ_ResetClipArea(void)
751 if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
753 Con_DPrint("DrawQueue full !\n");
756 dq = (void*) (r_refdef.drawqueue + r_refdef.drawqueuesize);
757 dq->size = sizeof(*dq);
758 dq->command = DRAWQUEUE_RESETCLIP;
766 r_refdef.drawqueuesize += dq->size;
774 void SCR_ScreenShot_f (void)
776 static int shotnumber;
777 static char oldname[MAX_QPATH];
778 char base[MAX_QPATH];
779 char filename[MAX_QPATH];
783 qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
785 sprintf (base, "screenshots/%s", scr_screenshot_name.string);
787 if (strcmp (oldname, scr_screenshot_name.string))
789 sprintf(oldname, "%s", scr_screenshot_name.string);
793 // find a file name to save it to
794 for (;shotnumber < 1000000;shotnumber++)
795 if (!FS_SysFileExists(va("%s/%s%06d.tga", fs_gamedir, base, shotnumber)) && !FS_SysFileExists(va("%s/%s%06d.jpg", fs_gamedir, base, shotnumber)))
797 if (shotnumber >= 1000000)
799 Con_Print("SCR_ScreenShot_f: Couldn't create the image file\n");
803 sprintf(filename, "%s%06d.%s", base, shotnumber, jpeg ? "jpg" : "tga");
805 buffer1 = Mem_Alloc(tempmempool, vid.width * vid.height * 3);
806 buffer2 = Mem_Alloc(tempmempool, vid.width * vid.height * 3);
807 buffer3 = Mem_Alloc(tempmempool, vid.width * vid.height * 3 + 18);
809 if (SCR_ScreenShot (filename, buffer1, buffer2, buffer3, 0, 0, vid.width, vid.height, false, false, false, jpeg, true))
810 Con_Printf("Wrote %s\n", filename);
812 Con_Printf("unable to write %s\n", filename);
821 typedef enum capturevideoformat_e
823 CAPTUREVIDEOFORMAT_TARGA,
824 CAPTUREVIDEOFORMAT_JPEG,
825 CAPTUREVIDEOFORMAT_RAWRGB,
826 CAPTUREVIDEOFORMAT_RAWYV12
828 capturevideoformat_t;
830 qboolean cl_capturevideo_active = false;
831 capturevideoformat_t cl_capturevideo_format;
832 static double cl_capturevideo_starttime = 0;
833 double cl_capturevideo_framerate = 0;
834 static int cl_capturevideo_soundrate = 0;
835 static int cl_capturevideo_frame = 0;
836 static qbyte *cl_capturevideo_buffer = NULL;
837 static qfile_t *cl_capturevideo_videofile = NULL;
838 qfile_t *cl_capturevideo_soundfile = NULL;
839 static short cl_capturevideo_rgbtoyuvscaletable[3][3][256];
840 static unsigned char cl_capturevideo_yuvnormalizetable[3][256];
841 //static unsigned char cl_capturevideo_rgbgammatable[3][256];
843 void SCR_CaptureVideo_BeginVideo(void)
848 if (cl_capturevideo_active)
850 // soundrate is figured out on the first SoundFrame
851 cl_capturevideo_active = true;
852 cl_capturevideo_starttime = Sys_DoubleTime();
853 cl_capturevideo_framerate = bound(1, cl_capturevideo_fps.value, 1000);
854 cl_capturevideo_soundrate = 0;
855 cl_capturevideo_frame = 0;
856 cl_capturevideo_buffer = Mem_Alloc(tempmempool, vid.width * vid.height * (3+3+3) + 18);
857 gamma = 1.0/scr_screenshot_gamma.value;
860 for (i = 0;i < 256;i++)
862 unsigned char j = (unsigned char)bound(0, 255*pow(i/255.0, gamma), 255);
863 cl_capturevideo_rgbgammatable[0][i] = j;
864 cl_capturevideo_rgbgammatable[1][i] = j;
865 cl_capturevideo_rgbgammatable[2][i] = j;
869 R = Y + 1.4075 * (Cr - 128);
870 G = Y + -0.3455 * (Cb - 128) + -0.7169 * (Cr - 128);
871 B = Y + 1.7790 * (Cb - 128);
872 Y = R * .299 + G * .587 + B * .114;
873 Cb = R * -.169 + G * -.332 + B * .500 + 128.;
874 Cr = R * .500 + G * -.419 + B * -.0813 + 128.;
876 for (i = 0;i < 256;i++)
878 g = 255*pow(i/255.0, gamma);
879 // Y weights from RGB
880 cl_capturevideo_rgbtoyuvscaletable[0][0][i] = (short)(g * 0.299);
881 cl_capturevideo_rgbtoyuvscaletable[0][1][i] = (short)(g * 0.587);
882 cl_capturevideo_rgbtoyuvscaletable[0][2][i] = (short)(g * 0.114);
883 // Cb weights from RGB
884 cl_capturevideo_rgbtoyuvscaletable[1][0][i] = (short)(g * -0.169);
885 cl_capturevideo_rgbtoyuvscaletable[1][1][i] = (short)(g * -0.332);
886 cl_capturevideo_rgbtoyuvscaletable[1][2][i] = (short)(g * 0.500);
887 // Cr weights from RGB
888 cl_capturevideo_rgbtoyuvscaletable[2][0][i] = (short)(g * 0.500);
889 cl_capturevideo_rgbtoyuvscaletable[2][1][i] = (short)(g * -0.419);
890 cl_capturevideo_rgbtoyuvscaletable[2][2][i] = (short)(g * -0.0813);
891 // range reduction of YCbCr to valid signal range
892 cl_capturevideo_yuvnormalizetable[0][i] = 16 + i * (236-16) / 256;
893 cl_capturevideo_yuvnormalizetable[1][i] = 16 + i * (240-16) / 256;
894 cl_capturevideo_yuvnormalizetable[2][i] = 16 + i * (240-16) / 256;
897 if (cl_capturevideo_rawrgb.integer)
899 cl_capturevideo_format = CAPTUREVIDEOFORMAT_RAWRGB;
900 cl_capturevideo_videofile = FS_Open ("video/dpvideo.rgb", "wb", false, true);
902 else if (cl_capturevideo_rawyv12.integer)
904 cl_capturevideo_format = CAPTUREVIDEOFORMAT_RAWYV12;
905 cl_capturevideo_videofile = FS_Open ("video/dpvideo.yv12", "wb", false, true);
907 else if (scr_screenshot_jpeg.integer)
909 cl_capturevideo_format = CAPTUREVIDEOFORMAT_JPEG;
910 cl_capturevideo_videofile = NULL;
914 cl_capturevideo_format = CAPTUREVIDEOFORMAT_TARGA;
915 cl_capturevideo_videofile = NULL;
918 if (cl_capturevideo_sound.integer)
920 cl_capturevideo_soundfile = FS_Open ("video/dpvideo.wav", "wb", false, true);
921 // wave header will be filled out when video ends
923 FS_Write (cl_capturevideo_soundfile, out, 44);
926 cl_capturevideo_soundfile = NULL;
929 void SCR_CaptureVideo_EndVideo(void)
933 if (!cl_capturevideo_active)
935 cl_capturevideo_active = false;
937 if (cl_capturevideo_videofile)
939 FS_Close(cl_capturevideo_videofile);
940 cl_capturevideo_videofile = NULL;
943 // finish the wave file
944 if (cl_capturevideo_soundfile)
946 i = (int)FS_Tell (cl_capturevideo_soundfile);
947 //"RIFF", (int) unknown (chunk size), "WAVE",
948 //"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
949 //"data", (int) unknown (chunk size)
950 memcpy (out, "RIFF****WAVEfmt \x10\x00\x00\x00\x01\x00\x02\x00********\x04\x00\x10\0data****", 44);
951 // the length of the whole RIFF chunk
954 out[5] = (n >> 8) & 0xFF;
955 out[6] = (n >> 16) & 0xFF;
956 out[7] = (n >> 24) & 0xFF;
958 n = cl_capturevideo_soundrate;
959 out[24] = (n) & 0xFF;
960 out[25] = (n >> 8) & 0xFF;
961 out[26] = (n >> 16) & 0xFF;
962 out[27] = (n >> 24) & 0xFF;
963 // bytes per second (rate * channels * bytes per channel)
964 n = cl_capturevideo_soundrate * 2 * 2;
965 out[28] = (n) & 0xFF;
966 out[29] = (n >> 8) & 0xFF;
967 out[30] = (n >> 16) & 0xFF;
968 out[31] = (n >> 24) & 0xFF;
969 // the length of the data chunk
971 out[40] = (n) & 0xFF;
972 out[41] = (n >> 8) & 0xFF;
973 out[42] = (n >> 16) & 0xFF;
974 out[43] = (n >> 24) & 0xFF;
975 FS_Seek (cl_capturevideo_soundfile, 0, SEEK_SET);
976 FS_Write (cl_capturevideo_soundfile, out, 44);
977 FS_Close (cl_capturevideo_soundfile);
978 cl_capturevideo_soundfile = NULL;
981 if (cl_capturevideo_buffer)
983 Mem_Free (cl_capturevideo_buffer);
984 cl_capturevideo_buffer = NULL;
987 cl_capturevideo_starttime = 0;
988 cl_capturevideo_framerate = 0;
989 cl_capturevideo_frame = 0;
992 qboolean SCR_CaptureVideo_VideoFrame(int newframenum)
994 int x = 0, y = 0, width = vid.width, height = vid.height;
995 unsigned char *b, *out;
997 int outoffset = (width/2)*(height/2);
998 //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);
999 // speed is critical here, so do saving as directly as possible
1000 switch (cl_capturevideo_format)
1002 case CAPTUREVIDEOFORMAT_RAWYV12:
1003 // FIXME: width/height must be multiple of 2, enforce this?
1004 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
1006 // process one line at a time, and CbCr every other line at 2 pixel intervals
1007 for (y = 0;y < height;y++)
1010 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++)
1011 *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]]];
1014 // 2x2 Cb and Cr planes
1016 // low quality, no averaging
1017 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++)
1020 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];
1022 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];
1025 // high quality, averaging
1026 int inpitch = width*3;
1027 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++)
1029 int blockr, blockg, blockb;
1030 blockr = (b[0] + b[3] + b[inpitch+0] + b[inpitch+3]) >> 2;
1031 blockg = (b[1] + b[4] + b[inpitch+1] + b[inpitch+4]) >> 2;
1032 blockb = (b[2] + b[5] + b[inpitch+2] + b[inpitch+5]) >> 2;
1034 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];
1036 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];
1041 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
1042 if (!FS_Write (cl_capturevideo_videofile, cl_capturevideo_buffer + width*height*3, width*height+(width/2)*(height/2)*2))
1045 case CAPTUREVIDEOFORMAT_RAWRGB:
1046 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
1048 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
1049 if (!FS_Write (cl_capturevideo_videofile, cl_capturevideo_buffer, width*height*3))
1052 case CAPTUREVIDEOFORMAT_JPEG:
1053 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
1055 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
1057 sprintf(filename, "video/dp%06d.jpg", cl_capturevideo_frame);
1058 if (!JPEG_SaveImage_preflipped (filename, width, height, cl_capturevideo_buffer))
1062 case CAPTUREVIDEOFORMAT_TARGA:
1063 //return Image_WriteTGARGB_preflipped (filename, width, height, cl_capturevideo_buffer, cl_capturevideo_buffer + vid.width * vid.height * 3, );
1064 memset (cl_capturevideo_buffer, 0, 18);
1065 cl_capturevideo_buffer[2] = 2; // uncompressed type
1066 cl_capturevideo_buffer[12] = (width >> 0) & 0xFF;
1067 cl_capturevideo_buffer[13] = (width >> 8) & 0xFF;
1068 cl_capturevideo_buffer[14] = (height >> 0) & 0xFF;
1069 cl_capturevideo_buffer[15] = (height >> 8) & 0xFF;
1070 cl_capturevideo_buffer[16] = 24; // pixel size
1071 qglReadPixels (x, y, width, height, GL_BGR, GL_UNSIGNED_BYTE, cl_capturevideo_buffer + 18);
1073 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
1075 sprintf(filename, "video/dp%06d.tga", cl_capturevideo_frame);
1076 if (!FS_WriteFile (filename, cl_capturevideo_buffer, width*height*3 + 18))
1085 void SCR_CaptureVideo_SoundFrame(qbyte *bufstereo16le, size_t length, int rate)
1087 if (!cl_capturevideo_soundfile)
1089 cl_capturevideo_soundrate = rate;
1090 if (FS_Write (cl_capturevideo_soundfile, bufstereo16le, 4 * length) < 4 * length)
1092 Cvar_SetValueQuick(&cl_capturevideo, 0);
1093 Con_Printf("video sound saving failed on frame %i, out of disk space? stopping video capture.\n", cl_capturevideo_frame);
1094 SCR_CaptureVideo_EndVideo();
1098 void SCR_CaptureVideo(void)
1101 if (cl_capturevideo.integer && r_render.integer)
1103 if (!cl_capturevideo_active)
1104 SCR_CaptureVideo_BeginVideo();
1105 if (cl_capturevideo_framerate != cl_capturevideo_fps.value)
1107 Con_Printf("You can not change the video framerate while recording a video.\n");
1108 Cvar_SetValueQuick(&cl_capturevideo_fps, cl_capturevideo_framerate);
1110 if (cl_capturevideo_soundfile)
1112 // preserve sound sync by duplicating frames when running slow
1113 newframenum = (Sys_DoubleTime() - cl_capturevideo_starttime) * cl_capturevideo_framerate;
1116 newframenum = cl_capturevideo_frame + 1;
1117 // if falling behind more than one second, stop
1118 if (newframenum - cl_capturevideo_frame > (int)ceil(cl_capturevideo_framerate))
1120 Cvar_SetValueQuick(&cl_capturevideo, 0);
1121 Con_Printf("video saving failed on frame %i, your machine is too slow for this capture speed.\n", cl_capturevideo_frame);
1122 SCR_CaptureVideo_EndVideo();
1126 if (!SCR_CaptureVideo_VideoFrame(newframenum))
1128 Cvar_SetValueQuick(&cl_capturevideo, 0);
1129 Con_Printf("video saving failed on frame %i, out of disk space? stopping video capture.\n", cl_capturevideo_frame);
1130 SCR_CaptureVideo_EndVideo();
1133 else if (cl_capturevideo_active)
1134 SCR_CaptureVideo_EndVideo();
1141 Grab six views for environment mapping tests
1148 qboolean flipx, flipy, flipdiagonaly;
1152 {{ 0, 0, 0}, "rt", false, false, false},
1153 {{ 0, 270, 0}, "ft", false, false, false},
1154 {{ 0, 180, 0}, "lf", false, false, false},
1155 {{ 0, 90, 0}, "bk", false, false, false},
1156 {{-90, 180, 0}, "up", true, true, false},
1157 {{ 90, 180, 0}, "dn", true, true, false},
1159 {{ 0, 0, 0}, "px", true, true, true},
1160 {{ 0, 90, 0}, "py", false, true, false},
1161 {{ 0, 180, 0}, "nx", false, false, true},
1162 {{ 0, 270, 0}, "ny", true, false, false},
1163 {{-90, 180, 0}, "pz", false, false, true},
1164 {{ 90, 180, 0}, "nz", false, false, true}
1167 static void R_Envmap_f (void)
1170 char filename[256], basename[256];
1175 if (Cmd_Argc() != 3)
1177 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");
1181 strlcpy (basename, Cmd_Argv(1), sizeof (basename));
1182 size = atoi(Cmd_Argv(2));
1183 if (size != 128 && size != 256 && size != 512 && size != 1024)
1185 Con_Print("envmap: size must be one of 128, 256, 512, or 1024\n");
1188 if (size > vid.width || size > vid.height)
1190 Con_Print("envmap: your resolution is not big enough to render that size\n");
1198 r_refdef.width = size;
1199 r_refdef.height = size;
1201 r_refdef.fov_x = 90;
1202 r_refdef.fov_y = 90;
1204 buffer1 = Mem_Alloc(tempmempool, size * size * 3);
1205 buffer2 = Mem_Alloc(tempmempool, size * size * 3);
1206 buffer3 = Mem_Alloc(tempmempool, size * size * 3 + 18);
1208 for (j = 0;j < 12;j++)
1210 sprintf(filename, "env/%s%s.tga", basename, envmapinfo[j].name);
1211 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);
1216 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);
1226 //=============================================================================
1228 // LordHavoc: SHOWLMP stuff
1229 #define SHOWLMP_MAXLABELS 256
1230 typedef struct showlmp_s
1240 showlmp_t showlmp[SHOWLMP_MAXLABELS];
1242 void SHOWLMP_decodehide(void)
1246 lmplabel = MSG_ReadString();
1247 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1248 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
1250 showlmp[i].isactive = false;
1255 void SHOWLMP_decodeshow(void)
1258 qbyte lmplabel[256], picname[256];
1260 strlcpy (lmplabel,MSG_ReadString(), sizeof (lmplabel));
1261 strlcpy (picname, MSG_ReadString(), sizeof (picname));
1262 if (gamemode == GAME_NEHAHRA) // LordHavoc: nasty old legacy junk
1269 x = MSG_ReadShort();
1270 y = MSG_ReadShort();
1273 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1274 if (showlmp[i].isactive)
1276 if (strcmp(showlmp[i].label, lmplabel) == 0)
1279 break; // drop out to replace it
1282 else if (k < 0) // find first empty one to replace
1285 return; // none found to replace
1286 // change existing one
1287 showlmp[k].isactive = true;
1288 strlcpy (showlmp[k].label, lmplabel, sizeof (showlmp[k].label));
1289 strlcpy (showlmp[k].pic, picname, sizeof (showlmp[k].pic));
1294 void SHOWLMP_drawall(void)
1297 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1298 if (showlmp[i].isactive)
1299 DrawQ_Pic(showlmp[i].x, showlmp[i].y, showlmp[i].pic, 0, 0, 1, 1, 1, 1, 0);
1302 void SHOWLMP_clear(void)
1305 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1306 showlmp[i].isactive = false;
1309 void CL_SetupScreenSize(void)
1311 float conwidth, conheight;
1313 VID_UpdateGamma(false);
1315 conwidth = bound(320, vid_conwidth.value, 2048);
1316 conheight = bound(200, vid_conheight.value, 1536);
1317 if (vid_conwidth.value != conwidth)
1318 Cvar_SetValue("vid_conwidth", conwidth);
1319 if (vid_conheight.value != conheight)
1320 Cvar_SetValue("vid_conheight", conheight);
1322 vid_conwidth.integer = vid_conwidth.integer;
1323 vid_conheight.integer = vid_conheight.integer;
1325 SCR_SetUpToDrawConsole();
1328 extern void R_Shadow_EditLights_DrawSelectedLightProperties(void);
1329 void CL_UpdateScreen(void)
1331 if (!scr_initialized || !con_initialized || vid_hidden)
1332 return; // not initialized yet
1334 // don't allow cheats in multiplayer
1335 if (!cl.islocalgame && cl.worldmodel)
1337 if (r_fullbright.integer != 0)
1338 Cvar_Set ("r_fullbright", "0");
1339 if (r_ambient.value != 0)
1340 Cvar_Set ("r_ambient", "0");
1344 if (scr_viewsize.value < 30)
1345 Cvar_Set ("viewsize","30");
1346 if (scr_viewsize.value > 120)
1347 Cvar_Set ("viewsize","120");
1349 // bound field of view
1350 if (scr_fov.value < 1)
1351 Cvar_Set ("fov","1");
1352 if (scr_fov.value > 170)
1353 Cvar_Set ("fov","170");
1355 // intermission is always full screen
1356 if (cl.intermission)
1360 if (scr_viewsize.value >= 120)
1361 sb_lines = 0; // no status bar at all
1362 else if (scr_viewsize.value >= 110)
1363 sb_lines = 24; // no inventory
1368 r_refdef.colormask[0] = 1;
1369 r_refdef.colormask[1] = 1;
1370 r_refdef.colormask[2] = 1;
1374 if (cls.signon == SIGNONS)
1375 R_TimeReport("other");
1377 CL_SetupScreenSize();
1381 if (cls.signon == SIGNONS)
1382 R_TimeReport("setup");
1384 //FIXME: force menu if nothing else to look at?
1385 //if (key_dest == key_game && cls.signon != SIGNONS && cls.state == ca_disconnected)
1387 if (cls.signon == SIGNONS)
1392 if (!r_letterbox.value)
1395 SCR_CheckDrawCenterString();
1401 if (cls.signon == SIGNONS)
1405 R_TimeReport_Start();
1407 R_Shadow_EditLights_DrawSelectedLightProperties();
1414 void CL_Screen_NewMap(void)