PageRenderTime 83ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/cl_screen.c

https://gitlab.com/xonotic/darkplaces
C | 2961 lines | 2371 code | 311 blank | 279 comment | 460 complexity | 0930f1f7592fab0dba1a6d189ba22508 MD5 | raw file
Possible License(s): GPL-2.0
  1. #include "quakedef.h"
  2. #include "cl_video.h"
  3. #include "image.h"
  4. #include "jpeg.h"
  5. #include "image_png.h"
  6. #include "cl_collision.h"
  7. #include "libcurl.h"
  8. #include "csprogs.h"
  9. #ifdef CONFIG_VIDEO_CAPTURE
  10. #include "cap_avi.h"
  11. #include "cap_ogg.h"
  12. #endif
  13. // we have to include snd_main.h here only to get access to snd_renderbuffer->format.speed when writing the AVI headers
  14. #include "snd_main.h"
  15. cvar_t scr_viewsize = {CVAR_SAVE, "viewsize","100", "how large the view should be, 110 disables inventory bar, 120 disables status bar"};
  16. cvar_t scr_fov = {CVAR_SAVE, "fov","90", "field of vision, 1-170 degrees, default 90, some players use 110-130"};
  17. cvar_t scr_conalpha = {CVAR_SAVE, "scr_conalpha", "1", "opacity of console background gfx/conback"};
  18. cvar_t scr_conalphafactor = {CVAR_SAVE, "scr_conalphafactor", "1", "opacity of console background gfx/conback relative to scr_conalpha; when 0, gfx/conback is not drawn"};
  19. cvar_t scr_conalpha2factor = {CVAR_SAVE, "scr_conalpha2factor", "0", "opacity of console background gfx/conback2 relative to scr_conalpha; when 0, gfx/conback2 is not drawn"};
  20. cvar_t scr_conalpha3factor = {CVAR_SAVE, "scr_conalpha3factor", "0", "opacity of console background gfx/conback3 relative to scr_conalpha; when 0, gfx/conback3 is not drawn"};
  21. cvar_t scr_conbrightness = {CVAR_SAVE, "scr_conbrightness", "1", "brightness of console background (0 = black, 1 = image)"};
  22. cvar_t scr_conforcewhiledisconnected = {0, "scr_conforcewhiledisconnected", "1", "forces fullscreen console while disconnected"};
  23. cvar_t scr_conscroll_x = {CVAR_SAVE, "scr_conscroll_x", "0", "scroll speed of gfx/conback in x direction"};
  24. cvar_t scr_conscroll_y = {CVAR_SAVE, "scr_conscroll_y", "0", "scroll speed of gfx/conback in y direction"};
  25. cvar_t scr_conscroll2_x = {CVAR_SAVE, "scr_conscroll2_x", "0", "scroll speed of gfx/conback2 in x direction"};
  26. cvar_t scr_conscroll2_y = {CVAR_SAVE, "scr_conscroll2_y", "0", "scroll speed of gfx/conback2 in y direction"};
  27. cvar_t scr_conscroll3_x = {CVAR_SAVE, "scr_conscroll3_x", "0", "scroll speed of gfx/conback3 in x direction"};
  28. cvar_t scr_conscroll3_y = {CVAR_SAVE, "scr_conscroll3_y", "0", "scroll speed of gfx/conback3 in y direction"};
  29. #ifdef CONFIG_MENU
  30. cvar_t scr_menuforcewhiledisconnected = {0, "scr_menuforcewhiledisconnected", "0", "forces menu while disconnected"};
  31. #endif
  32. cvar_t scr_centertime = {0, "scr_centertime","2", "how long centerprint messages show"};
  33. cvar_t scr_showram = {CVAR_SAVE, "showram","1", "show ram icon if low on surface cache memory (not used)"};
  34. cvar_t scr_showturtle = {CVAR_SAVE, "showturtle","0", "show turtle icon when framerate is too low"};
  35. cvar_t scr_showpause = {CVAR_SAVE, "showpause","1", "show pause icon when game is paused"};
  36. cvar_t scr_showbrand = {0, "showbrand","0", "shows gfx/brand.tga in a corner of the screen (different values select different positions, including centered)"};
  37. cvar_t scr_printspeed = {0, "scr_printspeed","0", "speed of intermission printing (episode end texts), a value of 0 disables the slow printing"};
  38. cvar_t scr_loadingscreen_background = {0, "scr_loadingscreen_background","0", "show the last visible background during loading screen (costs one screenful of video memory)"};
  39. cvar_t scr_loadingscreen_scale = {0, "scr_loadingscreen_scale","1", "scale factor of the background"};
  40. cvar_t scr_loadingscreen_scale_base = {0, "scr_loadingscreen_scale_base","0", "0 = console pixels, 1 = video pixels"};
  41. cvar_t scr_loadingscreen_scale_limit = {0, "scr_loadingscreen_scale_limit","0", "0 = no limit, 1 = until first edge hits screen edge, 2 = until last edge hits screen edge, 3 = until width hits screen width, 4 = until height hits screen height"};
  42. cvar_t scr_loadingscreen_picture = {CVAR_SAVE, "scr_loadingscreen_picture", "gfx/loading", "picture shown during loading"};
  43. cvar_t scr_loadingscreen_count = {0, "scr_loadingscreen_count","1", "number of loading screen files to use randomly (named loading.tga, loading2.tga, loading3.tga, ...)"};
  44. cvar_t scr_loadingscreen_firstforstartup = {0, "scr_loadingscreen_firstforstartup","0", "remove loading.tga from random scr_loadingscreen_count selection and only display it on client startup, 0 = normal, 1 = firstforstartup"};
  45. cvar_t scr_loadingscreen_barcolor = {0, "scr_loadingscreen_barcolor", "0 0 1", "rgb color of loadingscreen progress bar"};
  46. cvar_t scr_loadingscreen_barheight = {0, "scr_loadingscreen_barheight", "8", "the height of the loadingscreen progress bar"};
  47. cvar_t scr_loadingscreen_maxfps = {0, "scr_loadingscreen_maxfps", "10", "restrict maximal FPS for loading screen so it will not update very often (this will make lesser loading times on a maps loading large number of models)"};
  48. cvar_t scr_infobar_height = {0, "scr_infobar_height", "8", "the height of the infobar items"};
  49. cvar_t vid_conwidth = {CVAR_SAVE, "vid_conwidth", "640", "virtual width of 2D graphics system"};
  50. cvar_t vid_conheight = {CVAR_SAVE, "vid_conheight", "480", "virtual height of 2D graphics system"};
  51. cvar_t vid_pixelheight = {CVAR_SAVE, "vid_pixelheight", "1", "adjusts vertical field of vision to account for non-square pixels (1280x1024 on a CRT monitor for example)"};
  52. cvar_t scr_screenshot_jpeg = {CVAR_SAVE, "scr_screenshot_jpeg","1", "save jpeg instead of targa"};
  53. cvar_t scr_screenshot_jpeg_quality = {CVAR_SAVE, "scr_screenshot_jpeg_quality","0.9", "image quality of saved jpeg"};
  54. cvar_t scr_screenshot_png = {CVAR_SAVE, "scr_screenshot_png","0", "save png instead of targa"};
  55. cvar_t scr_screenshot_gammaboost = {CVAR_SAVE, "scr_screenshot_gammaboost","1", "gamma correction on saved screenshots and videos, 1.0 saves unmodified images"};
  56. cvar_t scr_screenshot_alpha = {0, "scr_screenshot_alpha","0", "try to write an alpha channel to screenshots (debugging feature)"};
  57. cvar_t scr_screenshot_timestamp = {CVAR_SAVE, "scr_screenshot_timestamp", "1", "use a timestamp based number of the type YYYYMMDDHHMMSSsss instead of sequential numbering"};
  58. // scr_screenshot_name is defined in fs.c
  59. #ifdef CONFIG_VIDEO_CAPTURE
  60. cvar_t cl_capturevideo = {0, "cl_capturevideo", "0", "enables saving of video to a .avi file using uncompressed I420 colorspace and PCM audio, note that scr_screenshot_gammaboost affects the brightness of the output)"};
  61. cvar_t cl_capturevideo_demo_stop = {CVAR_SAVE, "cl_capturevideo_demo_stop", "1", "automatically stops video recording when demo ends"};
  62. cvar_t cl_capturevideo_printfps = {CVAR_SAVE, "cl_capturevideo_printfps", "1", "prints the frames per second captured in capturevideo (is only written to the log file, not to the console, as that would be visible on the video)"};
  63. cvar_t cl_capturevideo_width = {CVAR_SAVE, "cl_capturevideo_width", "0", "scales all frames to this resolution before saving the video"};
  64. cvar_t cl_capturevideo_height = {CVAR_SAVE, "cl_capturevideo_height", "0", "scales all frames to this resolution before saving the video"};
  65. cvar_t cl_capturevideo_realtime = {0, "cl_capturevideo_realtime", "0", "causes video saving to operate in realtime (mostly useful while playing, not while capturing demos), this can produce a much lower quality video due to poor sound/video sync and will abort saving if your machine stalls for over a minute"};
  66. cvar_t cl_capturevideo_fps = {CVAR_SAVE, "cl_capturevideo_fps", "30", "how many frames per second to save (29.97 for NTSC, 30 for typical PC video, 15 can be useful)"};
  67. cvar_t cl_capturevideo_nameformat = {CVAR_SAVE, "cl_capturevideo_nameformat", "dpvideo", "prefix for saved videos (the date is encoded using strftime escapes)"};
  68. cvar_t cl_capturevideo_number = {CVAR_SAVE, "cl_capturevideo_number", "1", "number to append to video filename, incremented each time a capture begins"};
  69. cvar_t cl_capturevideo_ogg = {CVAR_SAVE, "cl_capturevideo_ogg", "1", "save captured video data as Ogg/Vorbis/Theora streams"};
  70. cvar_t cl_capturevideo_framestep = {CVAR_SAVE, "cl_capturevideo_framestep", "1", "when set to n >= 1, render n frames to capture one (useful for motion blur like effects)"};
  71. #endif
  72. cvar_t r_letterbox = {0, "r_letterbox", "0", "reduces vertical height of view to simulate a letterboxed movie effect (can be used by mods for cutscenes)"};
  73. cvar_t r_stereo_separation = {0, "r_stereo_separation", "4", "separation distance of eyes in the world (negative values are only useful for cross-eyed viewing)"};
  74. cvar_t r_stereo_sidebyside = {0, "r_stereo_sidebyside", "0", "side by side views for those who can't afford glasses but can afford eye strain (note: use a negative r_stereo_separation if you want cross-eyed viewing)"};
  75. cvar_t r_stereo_horizontal = {0, "r_stereo_horizontal", "0", "aspect skewed side by side view for special decoder/display hardware"};
  76. cvar_t r_stereo_vertical = {0, "r_stereo_vertical", "0", "aspect skewed top and bottom view for special decoder/display hardware"};
  77. cvar_t r_stereo_redblue = {0, "r_stereo_redblue", "0", "red/blue anaglyph stereo glasses (note: most of these glasses are actually red/cyan, try that one too)"};
  78. cvar_t r_stereo_redcyan = {0, "r_stereo_redcyan", "0", "red/cyan anaglyph stereo glasses, the kind given away at drive-in movies like Creature From The Black Lagoon In 3D"};
  79. cvar_t r_stereo_redgreen = {0, "r_stereo_redgreen", "0", "red/green anaglyph stereo glasses (for those who don't mind yellow)"};
  80. cvar_t r_stereo_angle = {0, "r_stereo_angle", "0", "separation angle of eyes (makes the views look different directions, as an example, 90 gives a 90 degree separation where the views are 45 degrees left and 45 degrees right)"};
  81. cvar_t scr_stipple = {0, "scr_stipple", "0", "interlacing-like stippling of the display"};
  82. cvar_t scr_refresh = {0, "scr_refresh", "1", "allows you to completely shut off rendering for benchmarking purposes"};
  83. cvar_t scr_screenshot_name_in_mapdir = {CVAR_SAVE, "scr_screenshot_name_in_mapdir", "0", "if set to 1, screenshots are placed in a subdirectory named like the map they are from"};
  84. cvar_t shownetgraph = {CVAR_SAVE, "shownetgraph", "0", "shows a graph of packet sizes and other information, 0 = off, 1 = show client netgraph, 2 = show client and server netgraphs (when hosting a server)"};
  85. cvar_t cl_demo_mousegrab = {0, "cl_demo_mousegrab", "0", "Allows reading the mouse input while playing demos. Useful for camera mods developed in csqc. (0: never, 1: always)"};
  86. cvar_t timedemo_screenshotframelist = {0, "timedemo_screenshotframelist", "", "when performing a timedemo, take screenshots of each frame in this space-separated list - example: 1 201 401"};
  87. cvar_t vid_touchscreen_outlinealpha = {0, "vid_touchscreen_outlinealpha", "0", "opacity of touchscreen area outlines"};
  88. cvar_t vid_touchscreen_overlayalpha = {0, "vid_touchscreen_overlayalpha", "0.25", "opacity of touchscreen area icons"};
  89. cvar_t r_speeds_graph = {CVAR_SAVE, "r_speeds_graph", "0", "display a graph of renderer statistics "};
  90. cvar_t r_speeds_graph_filter[8] =
  91. {
  92. {CVAR_SAVE, "r_speeds_graph_filter_r", "timedelta", "Red - display the specified renderer statistic"},
  93. {CVAR_SAVE, "r_speeds_graph_filter_g", "batch_batches", "Green - display the specified renderer statistic"},
  94. {CVAR_SAVE, "r_speeds_graph_filter_b", "batch_triangles", "Blue - display the specified renderer statistic"},
  95. {CVAR_SAVE, "r_speeds_graph_filter_y", "fast_triangles", "Yellow - display the specified renderer statistic"},
  96. {CVAR_SAVE, "r_speeds_graph_filter_c", "copytriangles_triangles", "Cyan - display the specified renderer statistic"},
  97. {CVAR_SAVE, "r_speeds_graph_filter_m", "dynamic_triangles", "Magenta - display the specified renderer statistic"},
  98. {CVAR_SAVE, "r_speeds_graph_filter_w", "animcache_shade_vertices", "White - display the specified renderer statistic"},
  99. {CVAR_SAVE, "r_speeds_graph_filter_o", "animcache_shape_vertices", "Orange - display the specified renderer statistic"},
  100. };
  101. cvar_t r_speeds_graph_length = {CVAR_SAVE, "r_speeds_graph_length", "1024", "number of frames in statistics graph, can be from 4 to 8192"};
  102. cvar_t r_speeds_graph_seconds = {CVAR_SAVE, "r_speeds_graph_seconds", "2", "number of seconds in graph, can be from 0.1 to 120"};
  103. cvar_t r_speeds_graph_x = {CVAR_SAVE, "r_speeds_graph_x", "0", "position of graph"};
  104. cvar_t r_speeds_graph_y = {CVAR_SAVE, "r_speeds_graph_y", "0", "position of graph"};
  105. cvar_t r_speeds_graph_width = {CVAR_SAVE, "r_speeds_graph_width", "256", "size of graph"};
  106. cvar_t r_speeds_graph_height = {CVAR_SAVE, "r_speeds_graph_height", "128", "size of graph"};
  107. cvar_t r_speeds_graph_maxtimedelta = {CVAR_SAVE, "r_speeds_graph_maxtimedelta", "16667", "maximum timedelta to display in the graph (this value will be the top line)"};
  108. cvar_t r_speeds_graph_maxdefault = {CVAR_SAVE, "r_speeds_graph_maxdefault", "100", "if the minimum and maximum observed values are closer than this, use this value as the graph range (keeps small numbers from being big graphs)"};
  109. extern cvar_t sbar_info_pos;
  110. extern cvar_t r_fog_clear;
  111. int jpeg_supported = false;
  112. qboolean scr_initialized; // ready to draw
  113. float scr_con_current;
  114. int scr_con_margin_bottom;
  115. extern int con_vislines;
  116. static void SCR_ScreenShot_f (void);
  117. static void R_Envmap_f (void);
  118. // backend
  119. void R_ClearScreen(qboolean fogcolor);
  120. /*
  121. ===============================================================================
  122. CENTER PRINTING
  123. ===============================================================================
  124. */
  125. char scr_centerstring[MAX_INPUTLINE];
  126. float scr_centertime_start; // for slow victory printing
  127. float scr_centertime_off;
  128. int scr_center_lines;
  129. int scr_erase_lines;
  130. int scr_erase_center;
  131. char scr_infobarstring[MAX_INPUTLINE];
  132. float scr_infobartime_off;
  133. /*
  134. ==============
  135. SCR_CenterPrint
  136. Called for important messages that should stay in the center of the screen
  137. for a few moments
  138. ==============
  139. */
  140. void SCR_CenterPrint(const char *str)
  141. {
  142. strlcpy (scr_centerstring, str, sizeof (scr_centerstring));
  143. scr_centertime_off = scr_centertime.value;
  144. scr_centertime_start = cl.time;
  145. // count the number of lines for centering
  146. scr_center_lines = 1;
  147. while (*str)
  148. {
  149. if (*str == '\n')
  150. scr_center_lines++;
  151. str++;
  152. }
  153. }
  154. static void SCR_DrawCenterString (void)
  155. {
  156. char *start;
  157. int x, y;
  158. int remaining;
  159. int color;
  160. if(cl.intermission == 2) // in finale,
  161. if(sb_showscores) // make TAB hide the finale message (sb_showscores overrides finale in sbar.c)
  162. return;
  163. if(scr_centertime.value <= 0 && !cl.intermission)
  164. return;
  165. // the finale prints the characters one at a time, except if printspeed is an absurdly high value
  166. if (cl.intermission && scr_printspeed.value > 0 && scr_printspeed.value < 1000000)
  167. remaining = (int)(scr_printspeed.value * (cl.time - scr_centertime_start));
  168. else
  169. remaining = 9999;
  170. scr_erase_center = 0;
  171. start = scr_centerstring;
  172. if (remaining < 1)
  173. return;
  174. if (scr_center_lines <= 4)
  175. y = (int)(vid_conheight.integer*0.35);
  176. else
  177. y = 48;
  178. color = -1;
  179. do
  180. {
  181. // scan the number of characters on the line, not counting color codes
  182. char *newline = strchr(start, '\n');
  183. int l = newline ? (newline - start) : (int)strlen(start);
  184. float width = DrawQ_TextWidth(start, l, 8, 8, false, FONT_CENTERPRINT);
  185. x = (int) (vid_conwidth.integer - width)/2;
  186. if (l > 0)
  187. {
  188. if (remaining < l)
  189. l = remaining;
  190. DrawQ_String(x, y, start, l, 8, 8, 1, 1, 1, 1, 0, &color, false, FONT_CENTERPRINT);
  191. remaining -= l;
  192. if (remaining <= 0)
  193. return;
  194. }
  195. y += 8;
  196. if (!newline)
  197. break;
  198. start = newline + 1; // skip the \n
  199. } while (1);
  200. }
  201. static void SCR_CheckDrawCenterString (void)
  202. {
  203. if (scr_center_lines > scr_erase_lines)
  204. scr_erase_lines = scr_center_lines;
  205. if (cl.time > cl.oldtime)
  206. scr_centertime_off -= cl.time - cl.oldtime;
  207. // don't draw if this is a normal stats-screen intermission,
  208. // only if it is not an intermission, or a finale intermission
  209. if (cl.intermission == 1)
  210. return;
  211. if (scr_centertime_off <= 0 && !cl.intermission)
  212. return;
  213. if (key_dest != key_game)
  214. return;
  215. SCR_DrawCenterString ();
  216. }
  217. static void SCR_DrawNetGraph_DrawGraph (int graphx, int graphy, int graphwidth, int graphheight, float graphscale, int graphlimit, const char *label, float textsize, int packetcounter, netgraphitem_t *netgraph)
  218. {
  219. netgraphitem_t *graph;
  220. int j, x, y, numlines;
  221. int totalbytes = 0;
  222. char bytesstring[128];
  223. float g[NETGRAPH_PACKETS][7];
  224. float *a;
  225. float *b;
  226. r_vertexgeneric_t vertex[(NETGRAPH_PACKETS+2)*6*2];
  227. r_vertexgeneric_t *v;
  228. DrawQ_Fill(graphx, graphy, graphwidth, graphheight + textsize * 2, 0, 0, 0, 0.5, 0);
  229. // draw the bar graph itself
  230. memset(g, 0, sizeof(g));
  231. for (j = 0;j < NETGRAPH_PACKETS;j++)
  232. {
  233. graph = netgraph + j;
  234. g[j][0] = 1.0f - 0.25f * (realtime - graph->time);
  235. g[j][1] = 1.0f;
  236. g[j][2] = 1.0f;
  237. g[j][3] = 1.0f;
  238. g[j][4] = 1.0f;
  239. g[j][5] = 1.0f;
  240. g[j][6] = 1.0f;
  241. if (graph->unreliablebytes == NETGRAPH_LOSTPACKET)
  242. g[j][1] = 0.00f;
  243. else if (graph->unreliablebytes == NETGRAPH_CHOKEDPACKET)
  244. g[j][2] = 0.90f;
  245. else
  246. {
  247. if(netgraph[j].time >= netgraph[(j+NETGRAPH_PACKETS-1)%NETGRAPH_PACKETS].time)
  248. if(graph->unreliablebytes + graph->reliablebytes + graph->ackbytes >= graphlimit * (netgraph[j].time - netgraph[(j+NETGRAPH_PACKETS-1)%NETGRAPH_PACKETS].time))
  249. g[j][2] = 0.98f;
  250. g[j][3] = 1.0f - graph->unreliablebytes * graphscale;
  251. g[j][4] = g[j][3] - graph->reliablebytes * graphscale;
  252. g[j][5] = g[j][4] - graph->ackbytes * graphscale;
  253. // count bytes in the last second
  254. if (realtime - graph->time < 1.0f)
  255. totalbytes += graph->unreliablebytes + graph->reliablebytes + graph->ackbytes;
  256. }
  257. if(graph->cleartime >= 0)
  258. g[j][6] = 0.5f + 0.5f * (2.0 / M_PI) * atan((M_PI / 2.0) * (graph->cleartime - graph->time));
  259. g[j][1] = bound(0.0f, g[j][1], 1.0f);
  260. g[j][2] = bound(0.0f, g[j][2], 1.0f);
  261. g[j][3] = bound(0.0f, g[j][3], 1.0f);
  262. g[j][4] = bound(0.0f, g[j][4], 1.0f);
  263. g[j][5] = bound(0.0f, g[j][5], 1.0f);
  264. g[j][6] = bound(0.0f, g[j][6], 1.0f);
  265. }
  266. // render the lines for the graph
  267. numlines = 0;
  268. v = vertex;
  269. for (j = 0;j < NETGRAPH_PACKETS;j++)
  270. {
  271. a = g[j];
  272. b = g[(j+1)%NETGRAPH_PACKETS];
  273. if (a[0] < 0.0f || b[0] > 1.0f || b[0] < a[0])
  274. continue;
  275. VectorSet(v->vertex3f, graphx + graphwidth * a[0], graphy + graphheight * a[2], 0.0f);Vector4Set(v->color4f, 1.0f, 1.0f, 0.0f, 1.0f);Vector2Set(v->texcoord2f, 0.0f, 0.0f);v++;
  276. VectorSet(v->vertex3f, graphx + graphwidth * b[0], graphy + graphheight * b[2], 0.0f);Vector4Set(v->color4f, 1.0f, 1.0f, 0.0f, 1.0f);Vector2Set(v->texcoord2f, 0.0f, 0.0f);v++;
  277. VectorSet(v->vertex3f, graphx + graphwidth * a[0], graphy + graphheight * a[1], 0.0f);Vector4Set(v->color4f, 1.0f, 0.0f, 0.0f, 1.0f);Vector2Set(v->texcoord2f, 0.0f, 0.0f);v++;
  278. VectorSet(v->vertex3f, graphx + graphwidth * b[0], graphy + graphheight * b[1], 0.0f);Vector4Set(v->color4f, 1.0f, 0.0f, 0.0f, 1.0f);Vector2Set(v->texcoord2f, 0.0f, 0.0f);v++;
  279. VectorSet(v->vertex3f, graphx + graphwidth * a[0], graphy + graphheight * a[5], 0.0f);Vector4Set(v->color4f, 0.0f, 1.0f, 0.0f, 1.0f);Vector2Set(v->texcoord2f, 0.0f, 0.0f);v++;
  280. VectorSet(v->vertex3f, graphx + graphwidth * b[0], graphy + graphheight * b[5], 0.0f);Vector4Set(v->color4f, 0.0f, 1.0f, 0.0f, 1.0f);Vector2Set(v->texcoord2f, 0.0f, 0.0f);v++;
  281. VectorSet(v->vertex3f, graphx + graphwidth * a[0], graphy + graphheight * a[4], 0.0f);Vector4Set(v->color4f, 1.0f, 1.0f, 1.0f, 1.0f);Vector2Set(v->texcoord2f, 0.0f, 0.0f);v++;
  282. VectorSet(v->vertex3f, graphx + graphwidth * b[0], graphy + graphheight * b[4], 0.0f);Vector4Set(v->color4f, 1.0f, 1.0f, 1.0f, 1.0f);Vector2Set(v->texcoord2f, 0.0f, 0.0f);v++;
  283. VectorSet(v->vertex3f, graphx + graphwidth * a[0], graphy + graphheight * a[3], 0.0f);Vector4Set(v->color4f, 1.0f, 0.5f, 0.0f, 1.0f);Vector2Set(v->texcoord2f, 0.0f, 0.0f);v++;
  284. VectorSet(v->vertex3f, graphx + graphwidth * b[0], graphy + graphheight * b[3], 0.0f);Vector4Set(v->color4f, 1.0f, 0.5f, 0.0f, 1.0f);Vector2Set(v->texcoord2f, 0.0f, 0.0f);v++;
  285. VectorSet(v->vertex3f, graphx + graphwidth * a[0], graphy + graphheight * a[6], 0.0f);Vector4Set(v->color4f, 0.0f, 0.0f, 1.0f, 1.0f);Vector2Set(v->texcoord2f, 0.0f, 0.0f);v++;
  286. VectorSet(v->vertex3f, graphx + graphwidth * b[0], graphy + graphheight * b[6], 0.0f);Vector4Set(v->color4f, 0.0f, 0.0f, 1.0f, 1.0f);Vector2Set(v->texcoord2f, 0.0f, 0.0f);v++;
  287. numlines += 6;
  288. }
  289. if (numlines > 0)
  290. {
  291. R_Mesh_PrepareVertices_Generic(numlines*2, vertex, NULL, 0);
  292. DrawQ_Lines(0.0f, numlines, 0, false);
  293. }
  294. x = graphx;
  295. y = graphy + graphheight;
  296. dpsnprintf(bytesstring, sizeof(bytesstring), "%i", totalbytes);
  297. DrawQ_String(x, y, label , 0, textsize, textsize, 1.0f, 1.0f, 1.0f, 1.0f, 0, NULL, false, FONT_DEFAULT);y += textsize;
  298. DrawQ_String(x, y, bytesstring, 0, textsize, textsize, 1.0f, 1.0f, 1.0f, 1.0f, 0, NULL, false, FONT_DEFAULT);y += textsize;
  299. }
  300. /*
  301. ==============
  302. SCR_DrawNetGraph
  303. ==============
  304. */
  305. static void SCR_DrawNetGraph (void)
  306. {
  307. int i, separator1, separator2, graphwidth, graphheight, netgraph_x, netgraph_y, textsize, index, netgraphsperrow, graphlimit;
  308. float graphscale;
  309. netconn_t *c;
  310. char vabuf[1024];
  311. if (cls.state != ca_connected)
  312. return;
  313. if (!cls.netcon)
  314. return;
  315. if (!shownetgraph.integer)
  316. return;
  317. separator1 = 2;
  318. separator2 = 4;
  319. textsize = 8;
  320. graphwidth = 120;
  321. graphheight = 70;
  322. graphscale = 1.0f / 1500.0f;
  323. graphlimit = cl_rate.integer;
  324. netgraphsperrow = (vid_conwidth.integer + separator2) / (graphwidth * 2 + separator1 + separator2);
  325. netgraphsperrow = max(netgraphsperrow, 1);
  326. index = 0;
  327. netgraph_x = (vid_conwidth.integer + separator2) - (1 + (index % netgraphsperrow)) * (graphwidth * 2 + separator1 + separator2);
  328. netgraph_y = (vid_conheight.integer - 48 - sbar_info_pos.integer + separator2) - (1 + (index / netgraphsperrow)) * (graphheight + textsize + separator2);
  329. c = cls.netcon;
  330. SCR_DrawNetGraph_DrawGraph(netgraph_x , netgraph_y, graphwidth, graphheight, graphscale, graphlimit, "incoming", textsize, c->incoming_packetcounter, c->incoming_netgraph);
  331. SCR_DrawNetGraph_DrawGraph(netgraph_x + graphwidth + separator1, netgraph_y, graphwidth, graphheight, graphscale, graphlimit, "outgoing", textsize, c->outgoing_packetcounter, c->outgoing_netgraph);
  332. index++;
  333. if (sv.active && shownetgraph.integer >= 2)
  334. {
  335. for (i = 0;i < svs.maxclients;i++)
  336. {
  337. c = svs.clients[i].netconnection;
  338. if (!c)
  339. continue;
  340. netgraph_x = (vid_conwidth.integer + separator2) - (1 + (index % netgraphsperrow)) * (graphwidth * 2 + separator1 + separator2);
  341. netgraph_y = (vid_conheight.integer - 48 + separator2) - (1 + (index / netgraphsperrow)) * (graphheight + textsize + separator2);
  342. SCR_DrawNetGraph_DrawGraph(netgraph_x , netgraph_y, graphwidth, graphheight, graphscale, graphlimit, va(vabuf, sizeof(vabuf), "%s", svs.clients[i].name), textsize, c->outgoing_packetcounter, c->outgoing_netgraph);
  343. SCR_DrawNetGraph_DrawGraph(netgraph_x + graphwidth + separator1, netgraph_y, graphwidth, graphheight, graphscale, graphlimit, "" , textsize, c->incoming_packetcounter, c->incoming_netgraph);
  344. index++;
  345. }
  346. }
  347. }
  348. /*
  349. ==============
  350. SCR_DrawTurtle
  351. ==============
  352. */
  353. static void SCR_DrawTurtle (void)
  354. {
  355. static int count;
  356. if (cls.state != ca_connected)
  357. return;
  358. if (!scr_showturtle.integer)
  359. return;
  360. if (cl.realframetime < 0.1)
  361. {
  362. count = 0;
  363. return;
  364. }
  365. count++;
  366. if (count < 3)
  367. return;
  368. DrawQ_Pic (0, 0, Draw_CachePic ("gfx/turtle"), 0, 0, 1, 1, 1, 1, 0);
  369. }
  370. /*
  371. ==============
  372. SCR_DrawNet
  373. ==============
  374. */
  375. static void SCR_DrawNet (void)
  376. {
  377. if (cls.state != ca_connected)
  378. return;
  379. if (realtime - cl.last_received_message < 0.3)
  380. return;
  381. if (cls.demoplayback)
  382. return;
  383. DrawQ_Pic (64, 0, Draw_CachePic ("gfx/net"), 0, 0, 1, 1, 1, 1, 0);
  384. }
  385. /*
  386. ==============
  387. DrawPause
  388. ==============
  389. */
  390. static void SCR_DrawPause (void)
  391. {
  392. cachepic_t *pic;
  393. if (cls.state != ca_connected)
  394. return;
  395. if (!scr_showpause.integer) // turn off for screenshots
  396. return;
  397. if (!cl.paused)
  398. return;
  399. pic = Draw_CachePic ("gfx/pause");
  400. DrawQ_Pic ((vid_conwidth.integer - pic->width)/2, (vid_conheight.integer - pic->height)/2, pic, 0, 0, 1, 1, 1, 1, 0);
  401. }
  402. /*
  403. ==============
  404. SCR_DrawBrand
  405. ==============
  406. */
  407. static void SCR_DrawBrand (void)
  408. {
  409. cachepic_t *pic;
  410. float x, y;
  411. if (!scr_showbrand.value)
  412. return;
  413. pic = Draw_CachePic ("gfx/brand");
  414. switch ((int)scr_showbrand.value)
  415. {
  416. case 1: // bottom left
  417. x = 0;
  418. y = vid_conheight.integer - pic->height;
  419. break;
  420. case 2: // bottom centre
  421. x = (vid_conwidth.integer - pic->width) / 2;
  422. y = vid_conheight.integer - pic->height;
  423. break;
  424. case 3: // bottom right
  425. x = vid_conwidth.integer - pic->width;
  426. y = vid_conheight.integer - pic->height;
  427. break;
  428. case 4: // centre right
  429. x = vid_conwidth.integer - pic->width;
  430. y = (vid_conheight.integer - pic->height) / 2;
  431. break;
  432. case 5: // top right
  433. x = vid_conwidth.integer - pic->width;
  434. y = 0;
  435. break;
  436. case 6: // top centre
  437. x = (vid_conwidth.integer - pic->width) / 2;
  438. y = 0;
  439. break;
  440. case 7: // top left
  441. x = 0;
  442. y = 0;
  443. break;
  444. case 8: // centre left
  445. x = 0;
  446. y = (vid_conheight.integer - pic->height) / 2;
  447. break;
  448. default:
  449. return;
  450. }
  451. DrawQ_Pic (x, y, pic, 0, 0, 1, 1, 1, 1, 0);
  452. }
  453. /*
  454. ==============
  455. SCR_DrawQWDownload
  456. ==============
  457. */
  458. static int SCR_DrawQWDownload(int offset)
  459. {
  460. // sync with SCR_InfobarHeight
  461. int len;
  462. float x, y;
  463. float size = scr_infobar_height.value;
  464. char temp[256];
  465. if (!cls.qw_downloadname[0])
  466. {
  467. cls.qw_downloadspeedrate = 0;
  468. cls.qw_downloadspeedtime = realtime;
  469. cls.qw_downloadspeedcount = 0;
  470. return 0;
  471. }
  472. if (realtime >= cls.qw_downloadspeedtime + 1)
  473. {
  474. cls.qw_downloadspeedrate = cls.qw_downloadspeedcount;
  475. cls.qw_downloadspeedtime = realtime;
  476. cls.qw_downloadspeedcount = 0;
  477. }
  478. if (cls.protocol == PROTOCOL_QUAKEWORLD)
  479. dpsnprintf(temp, sizeof(temp), "Downloading %s %3i%% (%i) at %i bytes/s", cls.qw_downloadname, cls.qw_downloadpercent, cls.qw_downloadmemorycursize, cls.qw_downloadspeedrate);
  480. else
  481. dpsnprintf(temp, sizeof(temp), "Downloading %s %3i%% (%i/%i) at %i bytes/s", cls.qw_downloadname, cls.qw_downloadpercent, cls.qw_downloadmemorycursize, cls.qw_downloadmemorymaxsize, cls.qw_downloadspeedrate);
  482. len = (int)strlen(temp);
  483. x = (vid_conwidth.integer - DrawQ_TextWidth(temp, len, size, size, true, FONT_INFOBAR)) / 2;
  484. y = vid_conheight.integer - size - offset;
  485. DrawQ_Fill(0, y, vid_conwidth.integer, size, 0, 0, 0, cls.signon == SIGNONS ? 0.5 : 1, 0);
  486. DrawQ_String(x, y, temp, len, size, size, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
  487. return size;
  488. }
  489. /*
  490. ==============
  491. SCR_DrawInfobarString
  492. ==============
  493. */
  494. static int SCR_DrawInfobarString(int offset)
  495. {
  496. int len;
  497. float x, y;
  498. float size = scr_infobar_height.value;
  499. len = (int)strlen(scr_infobarstring);
  500. x = (vid_conwidth.integer - DrawQ_TextWidth(scr_infobarstring, len, size, size, false, FONT_INFOBAR)) / 2;
  501. y = vid_conheight.integer - size - offset;
  502. DrawQ_Fill(0, y, vid_conwidth.integer, size, 0, 0, 0, cls.signon == SIGNONS ? 0.5 : 1, 0);
  503. DrawQ_String(x, y, scr_infobarstring, len, size, size, 1, 1, 1, 1, 0, NULL, false, FONT_INFOBAR);
  504. return size;
  505. }
  506. /*
  507. ==============
  508. SCR_DrawCurlDownload
  509. ==============
  510. */
  511. static int SCR_DrawCurlDownload(int offset)
  512. {
  513. // sync with SCR_InfobarHeight
  514. int len;
  515. int nDownloads;
  516. int i;
  517. float x, y;
  518. float size = scr_infobar_height.value;
  519. Curl_downloadinfo_t *downinfo;
  520. char temp[256];
  521. char addinfobuf[128];
  522. const char *addinfo;
  523. downinfo = Curl_GetDownloadInfo(&nDownloads, &addinfo, addinfobuf, sizeof(addinfobuf));
  524. if(!downinfo)
  525. return 0;
  526. y = vid_conheight.integer - size * nDownloads - offset;
  527. if(addinfo)
  528. {
  529. len = (int)strlen(addinfo);
  530. x = (vid_conwidth.integer - DrawQ_TextWidth(addinfo, len, size, size, true, FONT_INFOBAR)) / 2;
  531. DrawQ_Fill(0, y - size, vid_conwidth.integer, size, 1, 1, 1, cls.signon == SIGNONS ? 0.8 : 1, 0);
  532. DrawQ_String(x, y - size, addinfo, len, size, size, 0, 0, 0, 1, 0, NULL, true, FONT_INFOBAR);
  533. }
  534. for(i = 0; i != nDownloads; ++i)
  535. {
  536. if(downinfo[i].queued)
  537. dpsnprintf(temp, sizeof(temp), "Still in queue: %s", downinfo[i].filename);
  538. else if(downinfo[i].progress <= 0)
  539. dpsnprintf(temp, sizeof(temp), "Downloading %s ... ???.?%% @ %.1f KiB/s", downinfo[i].filename, downinfo[i].speed / 1024.0);
  540. else
  541. dpsnprintf(temp, sizeof(temp), "Downloading %s ... %5.1f%% @ %.1f KiB/s", downinfo[i].filename, 100.0 * downinfo[i].progress, downinfo[i].speed / 1024.0);
  542. len = (int)strlen(temp);
  543. x = (vid_conwidth.integer - DrawQ_TextWidth(temp, len, size, size, true, FONT_INFOBAR)) / 2;
  544. DrawQ_Fill(0, y + i * size, vid_conwidth.integer, size, 0, 0, 0, cls.signon == SIGNONS ? 0.5 : 1, 0);
  545. DrawQ_String(x, y + i * size, temp, len, size, size, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
  546. }
  547. Z_Free(downinfo);
  548. return size * (nDownloads + (addinfo ? 1 : 0));
  549. }
  550. /*
  551. ==============
  552. SCR_DrawInfobar
  553. ==============
  554. */
  555. static void SCR_DrawInfobar(void)
  556. {
  557. int offset = 0;
  558. offset += SCR_DrawQWDownload(offset);
  559. offset += SCR_DrawCurlDownload(offset);
  560. if(scr_infobartime_off > 0)
  561. offset += SCR_DrawInfobarString(offset);
  562. if(offset != scr_con_margin_bottom)
  563. Con_DPrintf("broken console margin calculation: %d != %d\n", offset, scr_con_margin_bottom);
  564. }
  565. static int SCR_InfobarHeight(void)
  566. {
  567. int offset = 0;
  568. Curl_downloadinfo_t *downinfo;
  569. const char *addinfo;
  570. int nDownloads;
  571. char addinfobuf[128];
  572. if (cl.time > cl.oldtime)
  573. scr_infobartime_off -= cl.time - cl.oldtime;
  574. if(scr_infobartime_off > 0)
  575. offset += 1;
  576. if(cls.qw_downloadname[0])
  577. offset += 1;
  578. downinfo = Curl_GetDownloadInfo(&nDownloads, &addinfo, addinfobuf, sizeof(addinfobuf));
  579. if(downinfo)
  580. {
  581. offset += (nDownloads + (addinfo ? 1 : 0));
  582. Z_Free(downinfo);
  583. }
  584. offset *= scr_infobar_height.value;
  585. return offset;
  586. }
  587. /*
  588. ==============
  589. SCR_InfoBar_f
  590. ==============
  591. */
  592. static void SCR_InfoBar_f(void)
  593. {
  594. if(Cmd_Argc() == 3)
  595. {
  596. scr_infobartime_off = atof(Cmd_Argv(1));
  597. strlcpy(scr_infobarstring, Cmd_Argv(2), sizeof(scr_infobarstring));
  598. }
  599. else
  600. {
  601. Con_Printf("usage:\ninfobar expiretime \"string\"\n");
  602. }
  603. }
  604. //=============================================================================
  605. /*
  606. ==================
  607. SCR_SetUpToDrawConsole
  608. ==================
  609. */
  610. static void SCR_SetUpToDrawConsole (void)
  611. {
  612. // lines of console to display
  613. float conlines;
  614. #ifdef CONFIG_MENU
  615. static int framecounter = 0;
  616. #endif
  617. Con_CheckResize ();
  618. #ifdef CONFIG_MENU
  619. if (scr_menuforcewhiledisconnected.integer && key_dest == key_game && cls.state == ca_disconnected)
  620. {
  621. if (framecounter >= 2)
  622. MR_ToggleMenu(1);
  623. else
  624. framecounter++;
  625. }
  626. else
  627. framecounter = 0;
  628. #endif
  629. if (scr_conforcewhiledisconnected.integer && key_dest == key_game && cls.signon != SIGNONS)
  630. key_consoleactive |= KEY_CONSOLEACTIVE_FORCED;
  631. else
  632. key_consoleactive &= ~KEY_CONSOLEACTIVE_FORCED;
  633. // decide on the height of the console
  634. if (key_consoleactive & KEY_CONSOLEACTIVE_USER)
  635. conlines = vid_conheight.integer/2; // half screen
  636. else
  637. conlines = 0; // none visible
  638. scr_con_current = conlines;
  639. }
  640. /*
  641. ==================
  642. SCR_DrawConsole
  643. ==================
  644. */
  645. void SCR_DrawConsole (void)
  646. {
  647. scr_con_margin_bottom = SCR_InfobarHeight();
  648. if (key_consoleactive & KEY_CONSOLEACTIVE_FORCED)
  649. {
  650. // full screen
  651. Con_DrawConsole (vid_conheight.integer - scr_con_margin_bottom);
  652. }
  653. else if (scr_con_current)
  654. Con_DrawConsole (min((int)scr_con_current, vid_conheight.integer - scr_con_margin_bottom));
  655. else
  656. con_vislines = 0;
  657. }
  658. /*
  659. ===============
  660. SCR_BeginLoadingPlaque
  661. ================
  662. */
  663. void SCR_BeginLoadingPlaque (qboolean startup)
  664. {
  665. // save console log up to this point to log_file if it was set by configs
  666. Log_Start();
  667. Host_StartVideo();
  668. SCR_UpdateLoadingScreen(false, startup);
  669. }
  670. //=============================================================================
  671. const char *r_stat_name[r_stat_count] =
  672. {
  673. "timedelta",
  674. "quality",
  675. "renders",
  676. "entities",
  677. "entities_surfaces",
  678. "entities_triangles",
  679. "world_leafs",
  680. "world_portals",
  681. "world_surfaces",
  682. "world_triangles",
  683. "lightmapupdates",
  684. "lightmapupdatepixels",
  685. "particles",
  686. "drawndecals",
  687. "totaldecals",
  688. "draws",
  689. "draws_vertices",
  690. "draws_elements",
  691. "lights",
  692. "lights_clears",
  693. "lights_scissored",
  694. "lights_lighttriangles",
  695. "lights_shadowtriangles",
  696. "lights_dynamicshadowtriangles",
  697. "bouncegrid_lights",
  698. "bouncegrid_particles",
  699. "bouncegrid_traces",
  700. "bouncegrid_hits",
  701. "bouncegrid_splats",
  702. "bouncegrid_bounces",
  703. "photoncache_animated",
  704. "photoncache_cached",
  705. "photoncache_traced",
  706. "bloom",
  707. "bloom_copypixels",
  708. "bloom_drawpixels",
  709. "indexbufferuploadcount",
  710. "indexbufferuploadsize",
  711. "vertexbufferuploadcount",
  712. "vertexbufferuploadsize",
  713. "framedatacurrent",
  714. "framedatasize",
  715. "bufferdatacurrent_vertex", // R_BUFFERDATA_ types are added to this index
  716. "bufferdatacurrent_index16",
  717. "bufferdatacurrent_index32",
  718. "bufferdatacurrent_uniform",
  719. "bufferdatasize_vertex", // R_BUFFERDATA_ types are added to this index
  720. "bufferdatasize_index16",
  721. "bufferdatasize_index32",
  722. "bufferdatasize_uniform",
  723. "animcache_vertexmesh_count",
  724. "animcache_vertexmesh_vertices",
  725. "animcache_vertexmesh_maxvertices",
  726. "animcache_skeletal_count",
  727. "animcache_skeletal_bones",
  728. "animcache_skeletal_maxbones",
  729. "animcache_shade_count",
  730. "animcache_shade_vertices",
  731. "animcache_shade_maxvertices",
  732. "animcache_shape_count",
  733. "animcache_shape_vertices",
  734. "animcache_shape_maxvertices",
  735. "batch_batches",
  736. "batch_withgaps",
  737. "batch_surfaces",
  738. "batch_vertices",
  739. "batch_triangles",
  740. "fast_batches",
  741. "fast_surfaces",
  742. "fast_vertices",
  743. "fast_triangles",
  744. "copytriangles_batches",
  745. "copytriangles_surfaces",
  746. "copytriangles_vertices",
  747. "copytriangles_triangles",
  748. "dynamic_batches",
  749. "dynamic_surfaces",
  750. "dynamic_vertices",
  751. "dynamic_triangles",
  752. "dynamicskeletal_batches",
  753. "dynamicskeletal_surfaces",
  754. "dynamicskeletal_vertices",
  755. "dynamicskeletal_triangles",
  756. "dynamic_batches_because_cvar",
  757. "dynamic_surfaces_because_cvar",
  758. "dynamic_vertices_because_cvar",
  759. "dynamic_triangles_because_cvar",
  760. "dynamic_batches_because_lightmapvertex",
  761. "dynamic_surfaces_because_lightmapvertex",
  762. "dynamic_vertices_because_lightmapvertex",
  763. "dynamic_triangles_because_lightmapvertex",
  764. "dynamic_batches_because_deformvertexes_autosprite",
  765. "dynamic_surfaces_because_deformvertexes_autosprite",
  766. "dynamic_vertices_because_deformvertexes_autosprite",
  767. "dynamic_triangles_because_deformvertexes_autosprite",
  768. "dynamic_batches_because_deformvertexes_autosprite2",
  769. "dynamic_surfaces_because_deformvertexes_autosprite2",
  770. "dynamic_vertices_because_deformvertexes_autosprite2",
  771. "dynamic_triangles_because_deformvertexes_autosprite2",
  772. "dynamic_batches_because_deformvertexes_normal",
  773. "dynamic_surfaces_because_deformvertexes_normal",
  774. "dynamic_vertices_because_deformvertexes_normal",
  775. "dynamic_triangles_because_deformvertexes_normal",
  776. "dynamic_batches_because_deformvertexes_wave",
  777. "dynamic_surfaces_because_deformvertexes_wave",
  778. "dynamic_vertices_because_deformvertexes_wave",
  779. "dynamic_triangles_because_deformvertexes_wave",
  780. "dynamic_batches_because_deformvertexes_bulge",
  781. "dynamic_surfaces_because_deformvertexes_bulge",
  782. "dynamic_vertices_because_deformvertexes_bulge",
  783. "dynamic_triangles_because_deformvertexes_bulge",
  784. "dynamic_batches_because_deformvertexes_move",
  785. "dynamic_surfaces_because_deformvertexes_move",
  786. "dynamic_vertices_because_deformvertexes_move",
  787. "dynamic_triangles_because_deformvertexes_move",
  788. "dynamic_batches_because_tcgen_lightmap",
  789. "dynamic_surfaces_because_tcgen_lightmap",
  790. "dynamic_vertices_because_tcgen_lightmap",
  791. "dynamic_triangles_because_tcgen_lightmap",
  792. "dynamic_batches_because_tcgen_vector",
  793. "dynamic_surfaces_because_tcgen_vector",
  794. "dynamic_vertices_because_tcgen_vector",
  795. "dynamic_triangles_because_tcgen_vector",
  796. "dynamic_batches_because_tcgen_environment",
  797. "dynamic_surfaces_because_tcgen_environment",
  798. "dynamic_vertices_because_tcgen_environment",
  799. "dynamic_triangles_because_tcgen_environment",
  800. "dynamic_batches_because_tcmod_turbulent",
  801. "dynamic_surfaces_because_tcmod_turbulent",
  802. "dynamic_vertices_because_tcmod_turbulent",
  803. "dynamic_triangles_because_tcmod_turbulent",
  804. "dynamic_batches_because_interleavedarrays",
  805. "dynamic_surfaces_because_interleavedarrays",
  806. "dynamic_vertices_because_interleavedarrays",
  807. "dynamic_triangles_because_interleavedarrays",
  808. "dynamic_batches_because_nogaps",
  809. "dynamic_surfaces_because_nogaps",
  810. "dynamic_vertices_because_nogaps",
  811. "dynamic_triangles_because_nogaps",
  812. "dynamic_batches_because_derived",
  813. "dynamic_surfaces_because_derived",
  814. "dynamic_vertices_because_derived",
  815. "dynamic_triangles_because_derived",
  816. "entitycache_count",
  817. "entitycache_surfaces",
  818. "entitycache_vertices",
  819. "entitycache_triangles",
  820. "entityanimate_count",
  821. "entityanimate_surfaces",
  822. "entityanimate_vertices",
  823. "entityanimate_triangles",
  824. "entityskeletal_count",
  825. "entityskeletal_surfaces",
  826. "entityskeletal_vertices",
  827. "entityskeletal_triangles",
  828. "entitystatic_count",
  829. "entitystatic_surfaces",
  830. "entitystatic_vertices",
  831. "entitystatic_triangles",
  832. "entitycustom_count",
  833. "entitycustom_surfaces",
  834. "entitycustom_vertices",
  835. "entitycustom_triangles",
  836. };
  837. char r_speeds_timestring[4096];
  838. int speedstringcount, r_timereport_active;
  839. double r_timereport_temp = 0, r_timereport_current = 0, r_timereport_start = 0;
  840. int r_speeds_longestitem = 0;
  841. void R_TimeReport(const char *desc)
  842. {
  843. char tempbuf[256];
  844. int length;
  845. int t;
  846. if (r_speeds.integer < 2 || !r_timereport_active)
  847. return;
  848. CHECKGLERROR
  849. if (r_speeds.integer == 2)
  850. GL_Finish();
  851. CHECKGLERROR
  852. r_timereport_temp = r_timereport_current;
  853. r_timereport_current = Sys_DirtyTime();
  854. t = (int) ((r_timereport_current - r_timereport_temp) * 1000000.0 + 0.5);
  855. length = dpsnprintf(tempbuf, sizeof(tempbuf), "%8i %s", t, desc);
  856. if (length < 0)
  857. length = (int)sizeof(tempbuf) - 1;
  858. if (r_speeds_longestitem < length)
  859. r_speeds_longestitem = length;
  860. for (;length < r_speeds_longestitem;length++)
  861. tempbuf[length] = ' ';
  862. tempbuf[length] = 0;
  863. if (speedstringcount + length > (vid_conwidth.integer / 8))
  864. {
  865. strlcat(r_speeds_timestring, "\n", sizeof(r_speeds_timestring));
  866. speedstringcount = 0;
  867. }
  868. strlcat(r_speeds_timestring, tempbuf, sizeof(r_speeds_timestring));
  869. speedstringcount += length;
  870. }
  871. static void R_TimeReport_BeginFrame(void)
  872. {
  873. speedstringcount = 0;
  874. r_speeds_timestring[0] = 0;
  875. r_timereport_active = false;
  876. memset(&r_refdef.stats, 0, sizeof(r_refdef.stats));
  877. if (r_speeds.integer >= 2)
  878. {
  879. r_timereport_active = true;
  880. r_timereport_start = r_timereport_current = Sys_DirtyTime();
  881. }
  882. }
  883. static int R_CountLeafTriangles(const dp_model_t *model, const mleaf_t *leaf)
  884. {
  885. int i, triangles = 0;
  886. for (i = 0;i < leaf->numleafsurfaces;i++)
  887. triangles += model->data_surfaces[leaf->firstleafsurface[i]].num_triangles;
  888. return triangles;
  889. }
  890. #define R_SPEEDS_GRAPH_COLORS 8
  891. #define R_SPEEDS_GRAPH_TEXTLENGTH 64
  892. static float r_speeds_graph_colors[R_SPEEDS_GRAPH_COLORS][4] = {{1, 0, 0, 1}, {0, 1, 0, 1}, {0, 0, 1, 1}, {1, 1, 0, 1}, {0, 1, 1, 1}, {1, 0, 1, 1}, {1, 1, 1, 1}, {1, 0.5f, 0, 1}};
  893. extern cvar_t r_viewscale;
  894. extern float viewscalefpsadjusted;
  895. static void R_TimeReport_EndFrame(void)
  896. {
  897. int j, lines;
  898. cl_locnode_t *loc;
  899. char string[1024+4096];
  900. mleaf_t *viewleaf;
  901. static double oldtime = 0;
  902. r_refdef.stats[r_stat_timedelta] = (int)((realtime - oldtime) * 1000000.0);
  903. oldtime = realtime;
  904. r_refdef.stats[r_stat_quality] = (int)(100 * r_refdef.view.quality);
  905. string[0] = 0;
  906. if (r_speeds.integer)
  907. {
  908. // put the location name in the r_speeds display as it greatly helps
  909. // when creating loc files
  910. loc = CL_Locs_FindNearest(cl.movement_origin);
  911. viewleaf = (r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.PointInLeaf) ? r_refdef.scene.worldmodel->brush.PointInLeaf(r_refdef.scene.worldmodel, r_refdef.view.origin) : NULL;
  912. dpsnprintf(string, sizeof(string),
  913. "%6ius time delta %s%s %.3f cl.time%2.4f brightness\n"
  914. "%3i renders org:'%+8.2f %+8.2f %+8.2f' dir:'%+2.3f %+2.3f %+2.3f'\n"
  915. "%5i viewleaf%5i cluster%3i area%4i brushes%4i surfaces(%7i triangles)\n"
  916. "%7i surfaces%7i triangles %5i entities (%7i surfaces%7i triangles)\n"
  917. "%5i leafs%5i portals%6i/%6i particles%6i/%6i decals %3i%% quality\n"
  918. "%7i lightmap updates (%7i pixels)%8i/%8i framedata\n"
  919. "%4i lights%4i clears%4i scissored%7i light%7i shadow%7i dynamic\n"
  920. "bouncegrid:%4i lights%6i particles%6i traces%6i hits%6i splats%6i bounces\n"
  921. "photon cache efficiency:%6i cached%6i traced%6ianimated\n"
  922. "%6i draws%8i vertices%8i triangles bloompixels%8i copied%8i drawn\n"
  923. "updated%5i indexbuffers%8i bytes%5i vertexbuffers%8i bytes\n"
  924. "animcache%5ib gpuskeletal%7i vertices (%7i with normals)\n"
  925. "fastbatch%5i count%5i surfaces%7i vertices %7i triangles\n"
  926. "copytris%5i count%5i surfaces%7i vertices %7i triangles\n"
  927. "dynamic%5i count%5i surfaces%7i vertices%7i triangles\n"
  928. "%s"
  929. , r_refdef.stats[r_stat_timedelta], loc ? "Location: " : "", loc ? loc->name : "", cl.time, r_refdef.view.colorscale
  930. , r_refdef.stats[r_stat_renders], r_refdef.view.origin[0], r_refdef.view.origin[1], r_refdef.view.origin[2], r_refdef.view.forward[0], r_refdef.view.forward[1], r_refdef.view.forward[2]
  931. , viewleaf ? (int)(viewleaf - r_refdef.scene.worldmodel->brush.data_leafs) : -1, viewleaf ? viewleaf->clusterindex : -1, viewleaf ? viewleaf->areaindex : -1, viewleaf ? viewleaf->numleafbrushes : 0, viewleaf ? viewleaf->numleafsurfaces : 0, viewleaf ? R_CountLeafTriangles(r_refdef.scene.worldmodel, viewleaf) : 0
  932. , r_refdef.stats[r_stat_world_surfaces], r_refdef.stats[r_stat_world_triangles], r_refdef.stats[r_stat_entities], r_refdef.stats[r_stat_entities_surfaces], r_refdef.stats[r_stat_entities_triangles]
  933. , r_refdef.stats[r_stat_world_leafs], r_refdef.stats[r_stat_world_portals], r_refdef.stats[r_stat_particles], cl.num_particles, r_refdef.stats[r_stat_drawndecals], r_refdef.stats[r_stat_totaldecals], r_refdef.stats[r_stat_quality]
  934. , r_refdef.stats[r_stat_lightmapupdates], r_refdef.stats[r_stat_lightmapupdatepixels], r_refdef.stats[r_stat_framedatacurrent], r_refdef.stats[r_stat_framedatasize]
  935. , r_refdef.stats[r_stat_lights], r_refdef.stats[r_stat_lights_clears], r_refdef.stats[r_stat_lights_scissored], r_refdef.stats[r_stat_lights_lighttriangles], r_refdef.stats[r_stat_lights_shadowtriangles], r_refdef.stats[r_stat_lights_dynamicshadowtriangles]
  936. , r_refdef.stats[r_stat_bouncegrid_lights], r_refdef.stats[r_stat_bouncegrid_particles], r_refdef.stats[r_stat_bouncegrid_traces], r_refdef.stats[r_stat_bouncegrid_hits], r_refdef.stats[r_stat_bouncegrid_splats], r_refdef.stats[r_stat_bouncegrid_bounces]
  937. , r_refdef.stats[r_stat_photoncache_cached], r_refdef.stats[r_stat_photoncache_traced], r_refdef.stats[r_stat_photoncache_animated]
  938. , r_refdef.stats[r_stat_draws], r_refdef.stats[r_stat_draws_vertices], r_refdef.stats[r_stat_draws_elements] / 3, r_refdef.stats[r_stat_bloom_copypixels], r_refdef.stats[r_stat_bloom_drawpixels]
  939. , r_refdef.stats[r_stat_indexbufferuploadcount], r_refdef.stats[r_stat_indexbufferuploadsize], r_refdef.stats[r_stat_vertexbufferuploadcount], r_refdef.stats[r_stat_vertexbufferuploadsize]
  940. , r_refdef.stats[r_stat_animcache_skeletal_bones], r_refdef.stats[r_stat_animcache_shape_vertices], r_refdef.stats[r_stat_animcache_shade_vertices]
  941. , r_refdef.stats[r_stat_batch_fast_batches], r_refdef.stats[r_stat_batch_fast_surfaces], r_refdef.stats[r_stat_batch_fast_vertices], r_refdef.stats[r_stat_batch_fast_triangles]
  942. , r_refdef.stats[r_stat_batch_copytriangles_batches], r_refdef.stats[r_stat_batch_copytriangles_surfaces], r_refdef.stats[r_stat_batch_copytriangles_vertices], r_refdef.stats[r_stat_batch_copytriangles_triangles]
  943. , r_refdef.stats[r_stat_batch_dynamic_batches], r_refdef.stats[r_stat_batch_dynamic_surfaces], r_refdef.stats[r_stat_batch_dynamic_vertices], r_refdef.stats[r_stat_batch_dynamic_triangles]
  944. , r_speeds_timestring);
  945. }
  946. speedstringcount = 0;
  947. r_speeds_timestring[0] = 0;
  948. r_timereport_active = false;
  949. if (r_speeds.integer >= 2)
  950. {
  951. r_timereport_active = true;
  952. r_timereport_start = r_timereport_current = Sys_DirtyTime();
  953. }
  954. if (string[0])
  955. {
  956. int i, y;
  957. if (string[strlen(string)-1] == '\n')
  958. string[strlen(string)-1] = 0;
  959. lines = 1;
  960. for (i = 0;string[i];i++)
  961. if (string[i] == '\n')
  962. lines++;
  963. y = vid_conheight.integer - sb_lines - lines * 8;
  964. i = j = 0;
  965. r_draw2d_force = true;
  966. DrawQ_Fill(0, y, vid_conwidth.integer, lines * 8, 0, 0, 0, 0.5, 0);
  967. while (string[i])
  968. {
  969. j = i;
  970. while (string[i] && string[i] != '\n')
  971. i++;
  972. if (i - j > 0)
  973. DrawQ_String(0, y, string + j, i - j, 8, 8, 1, 1, 1, 1, 0, NULL, true, FONT_DEFAULT);
  974. if (string[i] == '\n')
  975. i++;
  976. y += 8;
  977. }
  978. r_draw2d_force = false;
  979. }
  980. if (r_speeds_graph_length.integer != bound(4, r_speeds_graph_length.integer, 8192))
  981. Cvar_SetValueQuick(&r_speeds_graph_length, bound(4, r_speeds_graph_length.integer, 8192));
  982. if (fabs(r_speeds_graph_seconds.value - bound(0.1f, r_speeds_graph_seconds.value, 120.0f)) > 0.01f)
  983. Cvar_SetValueQuick(&r_speeds_graph_seconds, bound(0.1f, r_speeds_graph_seconds.value, 120.0f));
  984. if (r_speeds_graph.integer)
  985. {
  986. // if we currently have no graph data, reset the graph data entirely
  987. int i;
  988. if (!cls.r_speeds_graph_data)
  989. for (i = 0;i < r_stat_count;i++)
  990. cls.r_speeds_graph_datamin[i] = cls.r_speeds_graph_datamax[i] = 0;
  991. if (cls.r_speeds_graph_length != r_speeds_graph_length.integer)
  992. {
  993. int stat, index, d, graph_length, *graph_data;
  994. cls.r_speeds_graph_length = r_speeds_graph_length.integer;
  995. cls.r_speeds_graph_current = 0;
  996. if (cls.r_speeds_graph_data)
  997. Mem_Free(cls.r_speeds_graph_data);
  998. cls.r_speeds_graph_data = (int *)Mem_Alloc(cls.permanentmempool, cls.r_speeds_graph_length * sizeof(r_refdef.stats));
  999. // initialize the graph to have the current values throughout history
  1000. graph_data = cls.r_speeds_graph_data;
  1001. graph_length = cls.r_speeds_graph_length;
  1002. index = 0;
  1003. for (stat = 0;stat < r_stat_count;stat++)
  1004. {
  1005. d = r_refdef.stats[stat];
  1006. if (stat == r_stat_timedelta)
  1007. d = 0;
  1008. for (i = 0;i < graph_length;i++)
  1009. graph_data[index++] = d;
  1010. }
  1011. }
  1012. }
  1013. else
  1014. {
  1015. if (cls.r_speeds_graph_length)
  1016. {
  1017. cls.r_speeds_graph_length = 0;
  1018. Mem_Free(cls.r_speeds_graph_data);
  1019. cls.r_speeds_graph_data = NULL;
  1020. cls.r_speeds_graph_current = 0;
  1021. }
  1022. }
  1023. if (cls.r_speeds_graph_length)
  1024. {
  1025. char legend[128];
  1026. r_vertexgeneric_t *v;
  1027. int i, numlines;
  1028. const int *data;
  1029. float x, y, width, height, scalex, scaley;
  1030. int range_default = max(r_speeds_graph_maxdefault.integer, 1);
  1031. int color, stat, stats, index, range_min, range_max;
  1032. int graph_current, graph_length, *graph_data;
  1033. int statindex[R_SPEEDS_GRAPH_COLORS];
  1034. int sum;
  1035. // add current stats to the graph_data
  1036. cls.r_speeds_graph_current++;
  1037. if (cls.r_speeds_graph_current >= cls.r_speeds_graph_length)
  1038. cls.r_speeds_graph_current = 0;
  1039. // poke each new stat into the current offset of its graph
  1040. graph_data = cls.r_speeds_graph_data;
  1041. graph_current = cls.r_speeds_graph_current;
  1042. graph_length = cls.r_speeds_graph_length;
  1043. for (stat = 0;stat < r_stat_count;stat++)
  1044. graph_data[stat * graph_length + graph_current] = r_refdef.stats[stat];
  1045. // update the graph ranges
  1046. for (stat = 0;stat < r_stat_count;stat++)
  1047. {
  1048. if (cls.r_speeds_graph_datamin[stat] > r_refdef.stats[stat])
  1049. cls.r_speeds_graph_datamin[stat] = r_refdef.stats[stat];
  1050. if (cls.r_speeds_graph_datamax[stat] < r_refdef.stats[stat])
  1051. cls.r_speeds_graph_datamax[stat] = r_refdef.stats[stat];
  1052. }
  1053. // force 2D drawing to occur even if r_render is 0
  1054. r_draw2d_force = true;
  1055. // position the graph
  1056. width = r_speeds_graph_width.value;
  1057. height = r_speeds_graph_height.value;
  1058. x = bound(0, r_speeds_graph_x.value, vid_conwidth.value - width);
  1059. y = bound(0, r_speeds_graph_y.value, vid_conheight.value - height);
  1060. // fill background with a pattern of gray and black at one second intervals
  1061. scalex = (float)width / (float)r_speeds_graph_seconds.value;
  1062. for (i = 0;i < r_speeds_graph_seconds.integer + 1;i++)
  1063. {
  1064. float x1 = x + width - (i + 1) * scalex;
  1065. float x2 = x + width - i * scalex;
  1066. if (x1 < x)
  1067. x1 = x;
  1068. if (i & 1)
  1069. DrawQ_Fill(x1, y, x2 - x1, height, 0.0f, 0.0f, 0.0f, 0.5f, 0);
  1070. else
  1071. DrawQ_Fill(x1, y, x2 - x1, height, 0.2f, 0.2f, 0.2f, 0.5f, 0);
  1072. }
  1073. // count how many stats match our pattern
  1074. stats = 0;
  1075. color = 0;
  1076. for (color = 0;color < R_SPEEDS_GRAPH_COLORS;color++)
  1077. {
  1078. // look at all stat names and find ones matching the filter
  1079. statindex[color] = -1;
  1080. if (!r_speeds_graph_filter[color].string)
  1081. continue;
  1082. for (stat = 0;stat < r_stat_count;stat++)
  1083. if (!strcmp(r_stat_name[stat], r_speeds_graph_filter[color].string))
  1084. break;
  1085. if (stat >= r_stat_count)
  1086. continue;
  1087. // record that this color is this stat for the line drawing loop
  1088. statindex[color] = stat;
  1089. // draw the legend text in the background of the graph
  1090. dpsnprintf(legend, sizeof(legend), "%10i :%s", graph_data[stat * graph_length + graph_current], r_stat_name[stat]);
  1091. DrawQ_String(x, y + stats * 8, legend, 0, 8, 8, r_speeds_graph_colors[color][0], r_speeds_graph_colors[color][1], r_speeds_graph_colors[color][2], r_speeds_graph_colors[color][3] * 1.00f, 0, NULL, true, FONT_DEFAULT);
  1092. // count how many stats we need to graph in vertex buffer
  1093. stats++;
  1094. }
  1095. if (stats)
  1096. {
  1097. // legend text is drawn after the graphs
  1098. // render the graph lines, we'll go back and render the legend text later
  1099. scalex = (float)width / (1000000.0 * r_speeds_graph_seconds.value);
  1100. // get space in a vertex buffer to draw this
  1101. numlines = stats * (graph_length - 1);
  1102. v = R_Mesh_PrepareVertices_Generic_Lock(numlines * 2);
  1103. stats = 0;
  1104. for (color = 0;color < R_SPEEDS_GRAPH_COLORS;color++)
  1105. {
  1106. // look at all stat names and find ones matching the filter
  1107. stat = statindex[color];
  1108. if (stat < 0)
  1109. continue;
  1110. // prefer to graph stats with 0 base, but if they are
  1111. // negative we have no choice
  1112. range_min = cls.r_speeds_graph_datamin[stat];
  1113. range_max = max(cls.r_speeds_graph_datamax[stat], range_min + range_default);
  1114. // some stats we specifically override the graph scale on
  1115. if (stat == r_stat_timedelta)
  1116. range_max = r_speeds_graph_maxtimedelta.integer;
  1117. scaley = height / (range_max - range_min);
  1118. // generate lines (2 vertices each)
  1119. // to deal with incomplete data we walk right to left
  1120. data = graph_data + stat * graph_length;
  1121. index = graph_current;
  1122. sum = 0;
  1123. for (i = 0;i < graph_length - 1;)
  1124. {
  1125. v->vertex3f[0] = x + width - sum * scalex;
  1126. if (v->vertex3f[0] < x)
  1127. v->vertex3f[0] = x;
  1128. v->vertex3f[1] = y + height - (data[index] - range_min) * scaley;
  1129. v->vertex3f[2] = 0;
  1130. v->color4f[0] = r_speeds_graph_colors[color][0];
  1131. v->color4f[1] = r_speeds_graph_colors[color][1];
  1132. v->color4f[2] = r_speeds_graph_colors[color][2];
  1133. v->color4f[3] = r_speeds_graph_colors[color][3];
  1134. v->texcoord2f[0] = 0;
  1135. v->texcoord2f[1] = 0;
  1136. v++;
  1137. sum += graph_data[r_stat_timedelta * graph_length + index];
  1138. index--;
  1139. if (index < 0)
  1140. index = graph_length - 1;
  1141. i++;
  1142. v->vertex3f[0] = x + width - sum * scalex;
  1143. if (v->vertex3f[0] < x)
  1144. v->vertex3f[0] = x;
  1145. v->vertex3f[1] = y + height - (data[index] - range_min) * scaley;
  1146. v->vertex3f[2] = 0;
  1147. v->color4f[0] = r_speeds_graph_colors[color][0];
  1148. v->color4f[1] = r_speeds_graph_colors[color][1];
  1149. v->color4f[2] = r_speeds_graph_colors[color][2];
  1150. v->color4f[3] = r_speeds_graph_colors[color][3];
  1151. v->texcoord2f[0] = 0;
  1152. v->texcoord2f[1] = 0;
  1153. v++;
  1154. }
  1155. }
  1156. R_Mesh_PrepareVertices_Generic_Unlock();
  1157. DrawQ_Lines(0.0f, numlines, 0, false);
  1158. }
  1159. // return to not drawing anything if r_render is 0
  1160. r_draw2d_force = false;
  1161. }
  1162. memset(&r_refdef.stats, 0, sizeof(r_refdef.stats));
  1163. }
  1164. /*
  1165. =================
  1166. SCR_SizeUp_f
  1167. Keybinding command
  1168. =================
  1169. */
  1170. static void SCR_SizeUp_f (void)
  1171. {
  1172. Cvar_SetValue ("viewsize",scr_viewsize.value+10);
  1173. }
  1174. /*
  1175. =================
  1176. SCR_SizeDown_f
  1177. Keybinding command
  1178. =================
  1179. */
  1180. static void SCR_SizeDown_f (void)
  1181. {
  1182. Cvar_SetValue ("viewsize",scr_viewsize.value-10);
  1183. }
  1184. #ifdef CONFIG_VIDEO_CAPTURE
  1185. void SCR_CaptureVideo_EndVideo(void);
  1186. #endif
  1187. void CL_Screen_Shutdown(void)
  1188. {
  1189. #ifdef CONFIG_VIDEO_CAPTURE
  1190. SCR_CaptureVideo_EndVideo();
  1191. #endif
  1192. }
  1193. void CL_Screen_Init(void)
  1194. {
  1195. int i;
  1196. Cvar_RegisterVariable (&scr_fov);
  1197. Cvar_RegisterVariable (&scr_viewsize);
  1198. Cvar_RegisterVariable (&scr_conalpha);
  1199. Cvar_RegisterVariable (&scr_conalphafactor);
  1200. Cvar_RegisterVariable (&scr_conalpha2factor);
  1201. Cvar_RegisterVariable (&scr_conalpha3factor);
  1202. Cvar_RegisterVariable (&scr_conscroll_x);
  1203. Cvar_RegisterVariable (&scr_conscroll_y);
  1204. Cvar_RegisterVariable (&scr_conscroll2_x);
  1205. Cvar_RegisterVariable (&scr_conscroll2_y);
  1206. Cvar_RegisterVariable (&scr_conscroll3_x);
  1207. Cvar_RegisterVariable (&scr_conscroll3_y);
  1208. Cvar_RegisterVariable (&scr_conbrightness);
  1209. Cvar_RegisterVariable (&scr_conforcewhiledisconnected);
  1210. #ifdef CONFIG_MENU
  1211. Cvar_RegisterVariable (&scr_menuforcewhiledisconnected);
  1212. #endif
  1213. Cvar_RegisterVariable (&scr_loadingscreen_background);
  1214. Cvar_RegisterVariable (&scr_loadingscreen_scale);
  1215. Cvar_RegisterVariable (&scr_loadingscreen_scale_base);
  1216. Cvar_RegisterVariable (&scr_loadingscreen_scale_limit);
  1217. Cvar_RegisterVariable (&scr_loadingscreen_picture);
  1218. Cvar_RegisterVariable (&scr_loadingscreen_count);
  1219. Cvar_RegisterVariable (&scr_loadingscreen_firstforstartup);
  1220. Cvar_RegisterVariable (&scr_loadingscreen_barcolor);
  1221. Cvar_RegisterVariable (&scr_loadingscreen_barheight);
  1222. Cvar_RegisterVariable (&scr_loadingscreen_maxfps);
  1223. Cvar_RegisterVariable (&scr_infobar_height);
  1224. Cvar_RegisterVariable (&scr_showram);
  1225. Cvar_RegisterVariable (&scr_showturtle);
  1226. Cvar_RegisterVariable (&scr_showpause);
  1227. Cvar_RegisterVariable (&scr_showbrand);
  1228. Cvar_RegisterVariable (&scr_centertime);
  1229. Cvar_RegisterVariable (&scr_printspeed);
  1230. Cvar_RegisterVariable (&vid_conwidth);
  1231. Cvar_RegisterVariable (&vid_conheight);
  1232. Cvar_RegisterVariable (&vid_pixelheight);
  1233. Cvar_RegisterVariable (&scr_screenshot_jpeg);
  1234. Cvar_RegisterVariable (&scr_screenshot_jpeg_quality);
  1235. Cvar_RegisterVariable (&scr_screenshot_png);
  1236. Cvar_RegisterVariable (&scr_screenshot_gammaboost);
  1237. Cvar_RegisterVariable (&scr_screenshot_name_in_mapdir);
  1238. Cvar_RegisterVariable (&scr_screenshot_alpha);
  1239. Cvar_RegisterVariable (&scr_screenshot_timestamp);
  1240. #ifdef CONFIG_VIDEO_CAPTURE
  1241. Cvar_RegisterVariable (&cl_capturevideo);
  1242. Cvar_RegisterVariable (&cl_capturevideo_demo_stop);
  1243. Cvar_RegisterVariable (&cl_capturevideo_printfps);
  1244. Cvar_RegisterVariable (&cl_capturevideo_width);
  1245. Cvar_RegisterVariable (&cl_capturevideo_height);
  1246. Cvar_RegisterVariable (&cl_capturevideo_realtime);
  1247. Cvar_RegisterVariable (&cl_capturevideo_fps);
  1248. Cvar_RegisterVariable (&cl_capturevideo_nameformat);
  1249. Cvar_RegisterVariable (&cl_capturevideo_number);
  1250. Cvar_RegisterVariable (&cl_capturevideo_ogg);
  1251. Cvar_RegisterVariable (&cl_capturevideo_framestep);
  1252. #endif
  1253. Cvar_RegisterVariable (&r_letterbox);
  1254. Cvar_RegisterVariable(&r_stereo_separation);
  1255. Cvar_RegisterVariable(&r_stereo_sidebyside);
  1256. Cvar_RegisterVariable(&r_stereo_horizontal);
  1257. Cvar_RegisterVariable(&r_stereo_vertical);
  1258. Cvar_RegisterVariable(&r_stereo_redblue);
  1259. Cvar_RegisterVariable(&r_stereo_redcyan);
  1260. Cvar_RegisterVariable(&r_stereo_redgreen);
  1261. Cvar_RegisterVariable(&r_stereo_angle);
  1262. Cvar_RegisterVariable(&scr_stipple);
  1263. Cvar_RegisterVariable(&scr_refresh);
  1264. Cvar_RegisterVariable(&shownetgraph);
  1265. Cvar_RegisterVariable(&cl_demo_mousegrab);
  1266. Cvar_RegisterVariable(&timedemo_screenshotframelist);
  1267. Cvar_RegisterVariable(&vid_touchscreen_outlinealpha);
  1268. Cvar_RegisterVariable(&vid_touchscreen_overlayalpha);
  1269. Cvar_RegisterVariable(&r_speeds_graph);
  1270. for (i = 0;i < (int)(sizeof(r_speeds_graph_filter)/sizeof(r_speeds_graph_filter[0]));i++)
  1271. Cvar_RegisterVariable(&r_speeds_graph_filter[i]);
  1272. Cvar_RegisterVariable(&r_speeds_graph_length);
  1273. Cvar_RegisterVariable(&r_speeds_graph_seconds);
  1274. Cvar_RegisterVariable(&r_speeds_graph_x);
  1275. Cvar_RegisterVariable(&r_speeds_graph_y);
  1276. Cvar_RegisterVariable(&r_speeds_graph_width);
  1277. Cvar_RegisterVariable(&r_speeds_graph_height);
  1278. Cvar_RegisterVariable(&r_speeds_graph_maxtimedelta);
  1279. Cvar_RegisterVariable(&r_speeds_graph_maxdefault);
  1280. // if we want no console, turn it off here too
  1281. if (COM_CheckParm ("-noconsole"))
  1282. Cvar_SetQuick(&scr_conforcewhiledisconnected, "0");
  1283. Cmd_AddCommand ("sizeup",SCR_SizeUp_f, "increase view size (increases viewsize cvar)");
  1284. Cmd_AddCommand ("sizedown",SCR_SizeDown_f, "decrease view size (decreases viewsize cvar)");
  1285. Cmd_AddCommand ("screenshot",SCR_ScreenShot_f, "takes a screenshot of the next rendered frame");
  1286. Cmd_AddCommand ("envmap", R_Envmap_f, "render a cubemap (skybox) of the current scene");
  1287. Cmd_AddCommand ("infobar", SCR_InfoBar_f, "display a text in the infobar (usage: infobar expiretime string)");
  1288. #ifdef CONFIG_VIDEO_CAPTURE
  1289. SCR_CaptureVideo_Ogg_Init();
  1290. #endif
  1291. scr_initialized = true;
  1292. }
  1293. /*
  1294. ==================
  1295. SCR_ScreenShot_f
  1296. ==================
  1297. */
  1298. void SCR_ScreenShot_f (void)
  1299. {
  1300. static int shotnumber;
  1301. static char old_prefix_name[MAX_QPATH];
  1302. char prefix_name[MAX_QPATH];
  1303. char filename[MAX_QPATH];
  1304. unsigned char *buffer1;
  1305. unsigned char *buffer2;
  1306. qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
  1307. qboolean png = (scr_screenshot_png.integer != 0) && !jpeg;
  1308. char vabuf[1024];
  1309. if (Cmd_Argc() == 2)
  1310. {
  1311. const char *ext;
  1312. strlcpy(filename, Cmd_Argv(1), sizeof(filename));
  1313. ext = FS_FileExtension(filename);
  1314. if (!strcasecmp(ext, "jpg"))
  1315. {
  1316. jpeg = true;
  1317. png = false;
  1318. }
  1319. else if (!strcasecmp(ext, "tga"))
  1320. {
  1321. jpeg = false;
  1322. png = false;
  1323. }
  1324. else if (!strcasecmp(ext, "png"))
  1325. {
  1326. jpeg = false;
  1327. png = true;
  1328. }
  1329. else
  1330. {
  1331. Con_Printf("screenshot: supplied filename must end in .jpg or .tga or .png\n");
  1332. return;
  1333. }
  1334. }
  1335. else if (scr_screenshot_timestamp.integer)
  1336. {
  1337. int shotnumber100;
  1338. // TODO maybe make capturevideo and screenshot use similar name patterns?
  1339. if (scr_screenshot_name_in_mapdir.integer && cl.worldbasename[0])
  1340. dpsnprintf(prefix_name, sizeof(prefix_name), "%s/%s%s", cl.worldbasename, scr_screenshot_name.string, Sys_TimeString("%Y%m%d%H%M%S"));
  1341. else
  1342. dpsnprintf(prefix_name, sizeof(prefix_name), "%s%s", scr_screenshot_name.string, Sys_TimeString("%Y%m%d%H%M%S"));
  1343. // find a file name to save it to
  1344. for (shotnumber100 = 0;shotnumber100 < 100;shotnumber100++)
  1345. if (!FS_SysFileExists(va(vabuf, sizeof(vabuf), "%s/screenshots/%s-%02d.tga", fs_gamedir, prefix_name, shotnumber100))
  1346. && !FS_SysFileExists(va(vabuf, sizeof(vabuf), "%s/screenshots/%s-%02d.jpg", fs_gamedir, prefix_name, shotnumber100))
  1347. && !FS_SysFileExists(va(vabuf, sizeof(vabuf), "%s/screenshots/%s-%02d.png", fs_gamedir, prefix_name, shotnumber100)))
  1348. break;
  1349. if (shotnumber100 >= 100)
  1350. {
  1351. Con_Print("Couldn't create the image file - already 100 shots taken this second!\n");
  1352. return;
  1353. }
  1354. dpsnprintf(filename, sizeof(filename), "screenshots/%s-%02d.%s", prefix_name, shotnumber100, jpeg ? "jpg" : png ? "png" : "tga");
  1355. }
  1356. else
  1357. {
  1358. // TODO maybe make capturevideo and screenshot use similar name patterns?
  1359. if (scr_screenshot_name_in_mapdir.integer && cl.worldbasename[0])
  1360. dpsnprintf(prefix_name, sizeof(prefix_name), "%s/%s", cl.worldbasename, Sys_TimeString(scr_screenshot_name.string));
  1361. else
  1362. dpsnprintf(prefix_name, sizeof(prefix_name), "%s", Sys_TimeString(scr_screenshot_name.string));
  1363. // if prefix changed, gamedir or map changed, reset the shotnumber so
  1364. // we scan again
  1365. // FIXME: should probably do this whenever FS_Rescan or something like that occurs?
  1366. if (strcmp(old_prefix_name, prefix_name))
  1367. {
  1368. dpsnprintf(old_prefix_name, sizeof(old_prefix_name), "%s", prefix_name );
  1369. shotnumber = 0;
  1370. }
  1371. // find a file name to save it to
  1372. for (;shotnumber < 1000000;shotnumber++)
  1373. if (!FS_SysFileExists(va(vabuf, sizeof(vabuf), "%s/screenshots/%s%06d.tga", fs_gamedir, prefix_name, shotnumber))
  1374. && !FS_SysFileExists(va(vabuf, sizeof(vabuf), "%s/screenshots/%s%06d.jpg", fs_gamedir, prefix_name, shotnumber))
  1375. && !FS_SysFileExists(va(vabuf, sizeof(vabuf), "%s/screenshots/%s%06d.png", fs_gamedir, prefix_name, shotnumber)))
  1376. break;
  1377. if (shotnumber >= 1000000)
  1378. {
  1379. Con_Print("Couldn't create the image file - you already have 1000000 screenshots!\n");
  1380. return;
  1381. }
  1382. dpsnprintf(filename, sizeof(filename), "screenshots/%s%06d.%s", prefix_name, shotnumber, jpeg ? "jpg" : png ? "png" : "tga");
  1383. shotnumber++;
  1384. }
  1385. buffer1 = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 4);
  1386. buffer2 = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * (scr_screenshot_alpha.integer ? 4 : 3));
  1387. if (SCR_ScreenShot (filename, buffer1, buffer2, 0, 0, vid.width, vid.height, false, false, false, jpeg, png, true, scr_screenshot_alpha.integer != 0))
  1388. Con_Printf("Wrote %s\n", filename);
  1389. else
  1390. {
  1391. Con_Printf("Unable to write %s\n", filename);
  1392. if(jpeg || png)
  1393. {
  1394. if(SCR_ScreenShot (filename, buffer1, buffer2, 0, 0, vid.width, vid.height, false, false, false, false, false, true, scr_screenshot_alpha.integer != 0))
  1395. {
  1396. strlcpy(filename + strlen(filename) - 3, "tga", 4);
  1397. Con_Printf("Wrote %s\n", filename);
  1398. }
  1399. }
  1400. }
  1401. Mem_Free (buffer1);
  1402. Mem_Free (buffer2);
  1403. }
  1404. #ifdef CONFIG_VIDEO_CAPTURE
  1405. static void SCR_CaptureVideo_BeginVideo(void)
  1406. {
  1407. double r, g, b;
  1408. unsigned int i;
  1409. int width = cl_capturevideo_width.integer, height = cl_capturevideo_height.integer;
  1410. if (cls.capturevideo.active)
  1411. return;
  1412. memset(&cls.capturevideo, 0, sizeof(cls.capturevideo));
  1413. // soundrate is figured out on the first SoundFrame
  1414. if(width == 0 && height != 0)
  1415. width = (int) (height * (double)vid.width / ((double)vid.height * vid_pixelheight.value)); // keep aspect
  1416. if(width != 0 && height == 0)
  1417. height = (int) (width * ((double)vid.height * vid_pixelheight.value) / (double)vid.width); // keep aspect
  1418. if(width < 2 || width > vid.width) // can't scale up
  1419. width = vid.width;
  1420. if(height < 2 || height > vid.height) // can't scale up
  1421. height = vid.height;
  1422. // ensure it's all even; if not, scale down a little
  1423. if(width % 1)
  1424. --width;
  1425. if(height % 1)
  1426. --height;
  1427. cls.capturevideo.width = width;
  1428. cls.capturevideo.height = height;
  1429. cls.capturevideo.active = true;
  1430. cls.capturevideo.framerate = bound(1, cl_capturevideo_fps.value, 1001) * bound(1, cl_capturevideo_framestep.integer, 64);
  1431. cls.capturevideo.framestep = cl_capturevideo_framestep.integer;
  1432. cls.capturevideo.soundrate = S_GetSoundRate();
  1433. cls.capturevideo.soundchannels = S_GetSoundChannels();
  1434. cls.capturevideo.startrealtime = realtime;
  1435. cls.capturevideo.frame = cls.capturevideo.lastfpsframe = 0;
  1436. cls.capturevideo.starttime = cls.capturevideo.lastfpstime = realtime;
  1437. cls.capturevideo.soundsampleframe = 0;
  1438. cls.capturevideo.realtime = cl_capturevideo_realtime.integer != 0;
  1439. cls.capturevideo.screenbuffer = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 4);
  1440. cls.capturevideo.outbuffer = (unsigned char *)Mem_Alloc(tempmempool, width * height * (4+4) + 18);
  1441. dpsnprintf(cls.capturevideo.basename, sizeof(cls.capturevideo.basename), "video/%s%03i", Sys_TimeString(cl_capturevideo_nameformat.string), cl_capturevideo_number.integer);
  1442. Cvar_SetValueQuick(&cl_capturevideo_number, cl_capturevideo_number.integer + 1);
  1443. /*
  1444. for (i = 0;i < 256;i++)
  1445. {
  1446. unsigned char j = (unsigned char)bound(0, 255*pow(i/255.0, gamma), 255);
  1447. cls.capturevideo.rgbgammatable[0][i] = j;
  1448. cls.capturevideo.rgbgammatable[1][i] = j;
  1449. cls.capturevideo.rgbgammatable[2][i] = j;
  1450. }
  1451. */
  1452. /*
  1453. R = Y + 1.4075 * (Cr - 128);
  1454. G = Y + -0.3455 * (Cb - 128) + -0.7169 * (Cr - 128);
  1455. B = Y + 1.7790 * (Cb - 128);
  1456. Y = R * .299 + G * .587 + B * .114;
  1457. Cb = R * -.169 + G * -.332 + B * .500 + 128.;
  1458. Cr = R * .500 + G * -.419 + B * -.0813 + 128.;
  1459. */
  1460. // identity gamma table
  1461. BuildGammaTable16(1.0f, 1.0f, 1.0f, 0.0f, 1.0f, cls.capturevideo.vidramp, 256);
  1462. BuildGammaTable16(1.0f, 1.0f, 1.0f, 0.0f, 1.0f, cls.capturevideo.vidramp + 256, 256);
  1463. BuildGammaTable16(1.0f, 1.0f, 1.0f, 0.0f, 1.0f, cls.capturevideo.vidramp + 256*2, 256);
  1464. if(scr_screenshot_gammaboost.value != 1)
  1465. {
  1466. double igamma = 1 / scr_screenshot_gammaboost.value;
  1467. for (i = 0;i < 256 * 3;i++)
  1468. cls.capturevideo.vidramp[i] = (unsigned short) (0.5 + pow(cls.capturevideo.vidramp[i] * (1.0 / 65535.0), igamma) * 65535.0);
  1469. }
  1470. for (i = 0;i < 256;i++)
  1471. {
  1472. r = 255*cls.capturevideo.vidramp[i]/65535.0;
  1473. g = 255*cls.capturevideo.vidramp[i+256]/65535.0;
  1474. b = 255*cls.capturevideo.vidramp[i+512]/65535.0;
  1475. // NOTE: we have to round DOWN here, or integer overflows happen. Sorry for slightly wrong looking colors sometimes...
  1476. // Y weights from RGB
  1477. cls.capturevideo.rgbtoyuvscaletable[0][0][i] = (short)(r * 0.299);
  1478. cls.capturevideo.rgbtoyuvscaletable[0][1][i] = (short)(g * 0.587);
  1479. cls.capturevideo.rgbtoyuvscaletable[0][2][i] = (short)(b * 0.114);
  1480. // Cb weights from RGB
  1481. cls.capturevideo.rgbtoyuvscaletable[1][0][i] = (short)(r * -0.169);
  1482. cls.capturevideo.rgbtoyuvscaletable[1][1][i] = (short)(g * -0.332);
  1483. cls.capturevideo.rgbtoyuvscaletable[1][2][i] = (short)(b * 0.500);
  1484. // Cr weights from RGB
  1485. cls.capturevideo.rgbtoyuvscaletable[2][0][i] = (short)(r * 0.500);
  1486. cls.capturevideo.rgbtoyuvscaletable[2][1][i] = (short)(g * -0.419);
  1487. cls.capturevideo.rgbtoyuvscaletable[2][2][i] = (short)(b * -0.0813);
  1488. // range reduction of YCbCr to valid signal range
  1489. cls.capturevideo.yuvnormalizetable[0][i] = 16 + i * (236-16) / 256;
  1490. cls.capturevideo.yuvnormalizetable[1][i] = 16 + i * (240-16) / 256;
  1491. cls.capturevideo.yuvnormalizetable[2][i] = 16 + i * (240-16) / 256;
  1492. }
  1493. if (cl_capturevideo_ogg.integer)
  1494. {
  1495. if(SCR_CaptureVideo_Ogg_Available())
  1496. {
  1497. SCR_CaptureVideo_Ogg_BeginVideo();
  1498. return;
  1499. }
  1500. else
  1501. Con_Print("cl_capturevideo_ogg: libraries not available. Capturing in AVI instead.\n");
  1502. }
  1503. SCR_CaptureVideo_Avi_BeginVideo();
  1504. }
  1505. void SCR_CaptureVideo_EndVideo(void)
  1506. {
  1507. if (!cls.capturevideo.active)
  1508. return;
  1509. cls.capturevideo.active = false;
  1510. Con_Printf("Finishing capture of %s.%s (%d frames, %d audio frames)\n", cls.capturevideo.basename, cls.capturevideo.formatextension, cls.capturevideo.frame, cls.capturevideo.soundsampleframe);
  1511. if (cls.capturevideo.videofile)
  1512. {
  1513. cls.capturevideo.endvideo();
  1514. }
  1515. if (cls.capturevideo.screenbuffer)
  1516. {
  1517. Mem_Free (cls.capturevideo.screenbuffer);
  1518. cls.capturevideo.screenbuffer = NULL;
  1519. }
  1520. if (cls.capturevideo.outbuffer)
  1521. {
  1522. Mem_Free (cls.capturevideo.outbuffer);
  1523. cls.capturevideo.outbuffer = NULL;
  1524. }
  1525. memset(&cls.capturevideo, 0, sizeof(cls.capturevideo));
  1526. }
  1527. static void SCR_ScaleDownBGRA(unsigned char *in, int inw, int inh, unsigned char *out, int outw, int outh)
  1528. {
  1529. // TODO optimize this function
  1530. int x, y;
  1531. float area;
  1532. // memcpy is faster than me
  1533. if(inw == outw && inh == outh)
  1534. {
  1535. memcpy(out, in, 4 * inw * inh);
  1536. return;
  1537. }
  1538. // otherwise: a box filter
  1539. area = (float)outw * (float)outh / (float)inw / (float)inh;
  1540. for(y = 0; y < outh; ++y)
  1541. {
  1542. float iny0 = y / (float)outh * inh; int iny0_i = (int) floor(iny0);
  1543. float iny1 = (y+1) / (float)outh * inh; int iny1_i = (int) ceil(iny1);
  1544. for(x = 0; x < outw; ++x)
  1545. {
  1546. float inx0 = x / (float)outw * inw; int inx0_i = (int) floor(inx0);
  1547. float inx1 = (x+1) / (float)outw * inw; int inx1_i = (int) ceil(inx1);
  1548. float r = 0, g = 0, b = 0, alpha = 0;
  1549. int xx, yy;
  1550. for(yy = iny0_i; yy < iny1_i; ++yy)
  1551. {
  1552. float ya = min(yy+1, iny1) - max(iny0, yy);
  1553. for(xx = inx0_i; xx < inx1_i; ++xx)
  1554. {
  1555. float a = ya * (min(xx+1, inx1) - max(inx0, xx));
  1556. r += a * in[4*(xx + inw * yy)+0];
  1557. g += a * in[4*(xx + inw * yy)+1];
  1558. b += a * in[4*(xx + inw * yy)+2];
  1559. alpha += a * in[4*(xx + inw * yy)+3];
  1560. }
  1561. }
  1562. out[4*(x + outw * y)+0] = (unsigned char) (r * area);
  1563. out[4*(x + outw * y)+1] = (unsigned char) (g * area);
  1564. out[4*(x + outw * y)+2] = (unsigned char) (b * area);
  1565. out[4*(x + outw * y)+3] = (unsigned char) (alpha * area);
  1566. }
  1567. }
  1568. }
  1569. static void SCR_CaptureVideo_VideoFrame(int newframestepframenum)
  1570. {
  1571. int x = 0, y = 0;
  1572. int width = cls.capturevideo.width, height = cls.capturevideo.height;
  1573. if(newframestepframenum == cls.capturevideo.framestepframe)
  1574. return;
  1575. CHECKGLERROR
  1576. // speed is critical here, so do saving as directly as possible
  1577. GL_ReadPixelsBGRA(x, y, vid.width, vid.height, cls.capturevideo.screenbuffer);
  1578. SCR_ScaleDownBGRA (cls.capturevideo.screenbuffer, vid.width, vid.height, cls.capturevideo.outbuffer, width, height);
  1579. cls.capturevideo.videoframes(newframestepframenum - cls.capturevideo.framestepframe);
  1580. cls.capturevideo.framestepframe = newframestepframenum;
  1581. if(cl_capturevideo_printfps.integer)
  1582. {
  1583. char buf[80];
  1584. double t = realtime;
  1585. if(t > cls.capturevideo.lastfpstime + 1)
  1586. {
  1587. double fps1 = (cls.capturevideo.frame - cls.capturevideo.lastfpsframe) / (t - cls.capturevideo.lastfpstime + 0.0000001);
  1588. double fps = (cls.capturevideo.frame ) / (t - cls.capturevideo.starttime + 0.0000001);
  1589. dpsnprintf(buf, sizeof(buf), "capturevideo: (%.1fs) last second %.3ffps, total %.3ffps\n", cls.capturevideo.frame / cls.capturevideo.framerate, fps1, fps);
  1590. Sys_PrintToTerminal(buf);
  1591. cls.capturevideo.lastfpstime = t;
  1592. cls.capturevideo.lastfpsframe = cls.capturevideo.frame;
  1593. }
  1594. }
  1595. }
  1596. void SCR_CaptureVideo_SoundFrame(const portable_sampleframe_t *paintbuffer, size_t length)
  1597. {
  1598. cls.capturevideo.soundsampleframe += (int)length;
  1599. cls.capturevideo.soundframe(paintbuffer, length);
  1600. }
  1601. static void SCR_CaptureVideo(void)
  1602. {
  1603. int newframenum;
  1604. if (cl_capturevideo.integer)
  1605. {
  1606. if (!cls.capturevideo.active)
  1607. SCR_CaptureVideo_BeginVideo();
  1608. if (cls.capturevideo.framerate != cl_capturevideo_fps.value * cl_capturevideo_framestep.integer)
  1609. {
  1610. Con_Printf("You can not change the video framerate while recording a video.\n");
  1611. Cvar_SetValueQuick(&cl_capturevideo_fps, cls.capturevideo.framerate / (double) cl_capturevideo_framestep.integer);
  1612. }
  1613. // for AVI saving we have to make sure that sound is saved before video
  1614. if (cls.capturevideo.soundrate && !cls.capturevideo.soundsampleframe)
  1615. return;
  1616. if (cls.capturevideo.realtime)
  1617. {
  1618. // preserve sound sync by duplicating frames when running slow
  1619. newframenum = (int)((realtime - cls.capturevideo.startrealtime) * cls.capturevideo.framerate);
  1620. }
  1621. else
  1622. newframenum = cls.capturevideo.frame + 1;
  1623. // if falling behind more than one second, stop
  1624. if (newframenum - cls.capturevideo.frame > 60 * (int)ceil(cls.capturevideo.framerate))
  1625. {
  1626. Cvar_SetValueQuick(&cl_capturevideo, 0);
  1627. Con_Printf("video saving failed on frame %i, your machine is too slow for this capture speed.\n", cls.capturevideo.frame);
  1628. SCR_CaptureVideo_EndVideo();
  1629. return;
  1630. }
  1631. // write frames
  1632. SCR_CaptureVideo_VideoFrame(newframenum / cls.capturevideo.framestep);
  1633. cls.capturevideo.frame = newframenum;
  1634. if (cls.capturevideo.error)
  1635. {
  1636. Cvar_SetValueQuick(&cl_capturevideo, 0);
  1637. Con_Printf("video saving failed on frame %i, out of disk space? stopping video capture.\n", cls.capturevideo.frame);
  1638. SCR_CaptureVideo_EndVideo();
  1639. }
  1640. }
  1641. else if (cls.capturevideo.active)
  1642. SCR_CaptureVideo_EndVideo();
  1643. }
  1644. #endif
  1645. /*
  1646. ===============
  1647. R_Envmap_f
  1648. Grab six views for environment mapping tests
  1649. ===============
  1650. */
  1651. struct envmapinfo_s
  1652. {
  1653. float angles[3];
  1654. const char *name;
  1655. qboolean flipx, flipy, flipdiagonaly;
  1656. }
  1657. envmapinfo[12] =
  1658. {
  1659. {{ 0, 0, 0}, "rt", false, false, false},
  1660. {{ 0, 270, 0}, "ft", false, false, false},
  1661. {{ 0, 180, 0}, "lf", false, false, false},
  1662. {{ 0, 90, 0}, "bk", false, false, false},
  1663. {{-90, 180, 0}, "up", true, true, false},
  1664. {{ 90, 180, 0}, "dn", true, true, false},
  1665. {{ 0, 0, 0}, "px", true, true, true},
  1666. {{ 0, 90, 0}, "py", false, true, false},
  1667. {{ 0, 180, 0}, "nx", false, false, true},
  1668. {{ 0, 270, 0}, "ny", true, false, false},
  1669. {{-90, 180, 0}, "pz", false, false, true},
  1670. {{ 90, 180, 0}, "nz", false, false, true}
  1671. };
  1672. static void R_Envmap_f (void)
  1673. {
  1674. int j, size;
  1675. char filename[MAX_QPATH], basename[MAX_QPATH];
  1676. unsigned char *buffer1;
  1677. unsigned char *buffer2;
  1678. if (Cmd_Argc() != 3)
  1679. {
  1680. 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");
  1681. return;
  1682. }
  1683. strlcpy (basename, Cmd_Argv(1), sizeof (basename));
  1684. size = atoi(Cmd_Argv(2));
  1685. if (size != 128 && size != 256 && size != 512 && size != 1024)
  1686. {
  1687. Con_Print("envmap: size must be one of 128, 256, 512, or 1024\n");
  1688. return;
  1689. }
  1690. if (size > vid.width || size > vid.height)
  1691. {
  1692. Con_Print("envmap: your resolution is not big enough to render that size\n");
  1693. return;
  1694. }
  1695. r_refdef.envmap = true;
  1696. R_UpdateVariables();
  1697. r_refdef.view.x = 0;
  1698. r_refdef.view.y = 0;
  1699. r_refdef.view.z = 0;
  1700. r_refdef.view.width = size;
  1701. r_refdef.view.height = size;
  1702. r_refdef.view.depth = 1;
  1703. r_refdef.view.useperspective = true;
  1704. r_refdef.view.isoverlay = false;
  1705. r_refdef.view.frustum_x = 1; // tan(45 * M_PI / 180.0);
  1706. r_refdef.view.frustum_y = 1; // tan(45 * M_PI / 180.0);
  1707. r_refdef.view.ortho_x = 90; // abused as angle by VM_CL_R_SetView
  1708. r_refdef.view.ortho_y = 90; // abused as angle by VM_CL_R_SetView
  1709. buffer1 = (unsigned char *)Mem_Alloc(tempmempool, size * size * 4);
  1710. buffer2 = (unsigned char *)Mem_Alloc(tempmempool, size * size * 3);
  1711. CL_UpdateEntityShading();
  1712. for (j = 0;j < 12;j++)
  1713. {
  1714. dpsnprintf(filename, sizeof(filename), "env/%s%s.tga", basename, envmapinfo[j].name);
  1715. Matrix4x4_CreateFromQuakeEntity(&r_refdef.view.matrix, r_refdef.view.origin[0], r_refdef.view.origin[1], r_refdef.view.origin[2], envmapinfo[j].angles[0], envmapinfo[j].angles[1], envmapinfo[j].angles[2], 1);
  1716. r_refdef.view.quality = 1;
  1717. r_refdef.view.clear = true;
  1718. R_Mesh_Start();
  1719. R_RenderView();
  1720. R_Mesh_Finish();
  1721. SCR_ScreenShot(filename, buffer1, buffer2, 0, vid.height - (r_refdef.view.y + r_refdef.view.height), size, size, envmapinfo[j].flipx, envmapinfo[j].flipy, envmapinfo[j].flipdiagonaly, false, false, false, false);
  1722. }
  1723. Mem_Free (buffer1);
  1724. Mem_Free (buffer2);
  1725. r_refdef.envmap = false;
  1726. }
  1727. //=============================================================================
  1728. void SHOWLMP_decodehide(void)
  1729. {
  1730. int i;
  1731. char *lmplabel;
  1732. lmplabel = MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
  1733. for (i = 0;i < cl.num_showlmps;i++)
  1734. if (cl.showlmps[i].isactive && strcmp(cl.showlmps[i].label, lmplabel) == 0)
  1735. {
  1736. cl.showlmps[i].isactive = false;
  1737. return;
  1738. }
  1739. }
  1740. void SHOWLMP_decodeshow(void)
  1741. {
  1742. int k;
  1743. char lmplabel[256], picname[256];
  1744. float x, y;
  1745. strlcpy (lmplabel,MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof (lmplabel));
  1746. strlcpy (picname, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof (picname));
  1747. if (gamemode == GAME_NEHAHRA) // LordHavoc: nasty old legacy junk
  1748. {
  1749. x = MSG_ReadByte(&cl_message);
  1750. y = MSG_ReadByte(&cl_message);
  1751. }
  1752. else
  1753. {
  1754. x = MSG_ReadShort(&cl_message);
  1755. y = MSG_ReadShort(&cl_message);
  1756. }
  1757. if (!cl.showlmps || cl.num_showlmps >= cl.max_showlmps)
  1758. {
  1759. showlmp_t *oldshowlmps = cl.showlmps;
  1760. cl.max_showlmps += 16;
  1761. cl.showlmps = (showlmp_t *) Mem_Alloc(cls.levelmempool, cl.max_showlmps * sizeof(showlmp_t));
  1762. if (oldshowlmps)
  1763. {
  1764. if (cl.num_showlmps)
  1765. memcpy(cl.showlmps, oldshowlmps, cl.num_showlmps * sizeof(showlmp_t));
  1766. Mem_Free(oldshowlmps);
  1767. }
  1768. }
  1769. for (k = 0;k < cl.max_showlmps;k++)
  1770. if (cl.showlmps[k].isactive && !strcmp(cl.showlmps[k].label, lmplabel))
  1771. break;
  1772. if (k == cl.max_showlmps)
  1773. for (k = 0;k < cl.max_showlmps;k++)
  1774. if (!cl.showlmps[k].isactive)
  1775. break;
  1776. cl.showlmps[k].isactive = true;
  1777. strlcpy (cl.showlmps[k].label, lmplabel, sizeof (cl.showlmps[k].label));
  1778. strlcpy (cl.showlmps[k].pic, picname, sizeof (cl.showlmps[k].pic));
  1779. cl.showlmps[k].x = x;
  1780. cl.showlmps[k].y = y;
  1781. cl.num_showlmps = max(cl.num_showlmps, k + 1);
  1782. }
  1783. void SHOWLMP_drawall(void)
  1784. {
  1785. int i;
  1786. for (i = 0;i < cl.num_showlmps;i++)
  1787. if (cl.showlmps[i].isactive)
  1788. DrawQ_Pic(cl.showlmps[i].x, cl.showlmps[i].y, Draw_CachePic_Flags (cl.showlmps[i].pic, CACHEPICFLAG_NOTPERSISTENT), 0, 0, 1, 1, 1, 1, 0);
  1789. }
  1790. /*
  1791. ==============================================================================
  1792. SCREEN SHOTS
  1793. ==============================================================================
  1794. */
  1795. // buffer1: 4*w*h
  1796. // buffer2: 3*w*h (or 4*w*h if screenshotting alpha too)
  1797. qboolean SCR_ScreenShot(char *filename, unsigned char *buffer1, unsigned char *buffer2, int x, int y, int width, int height, qboolean flipx, qboolean flipy, qboolean flipdiagonal, qboolean jpeg, qboolean png, qboolean gammacorrect, qboolean keep_alpha)
  1798. {
  1799. int indices[4] = {0,1,2,3}; // BGRA
  1800. qboolean ret;
  1801. GL_ReadPixelsBGRA(x, y, width, height, buffer1);
  1802. if(gammacorrect && (scr_screenshot_gammaboost.value != 1))
  1803. {
  1804. int i;
  1805. double igamma = 1.0 / scr_screenshot_gammaboost.value;
  1806. unsigned short vidramp[256 * 3];
  1807. // identity gamma table
  1808. BuildGammaTable16(1.0f, 1.0f, 1.0f, 0.0f, 1.0f, vidramp, 256);
  1809. BuildGammaTable16(1.0f, 1.0f, 1.0f, 0.0f, 1.0f, vidramp + 256, 256);
  1810. BuildGammaTable16(1.0f, 1.0f, 1.0f, 0.0f, 1.0f, vidramp + 256*2, 256);
  1811. if(scr_screenshot_gammaboost.value != 1)
  1812. {
  1813. for (i = 0;i < 256 * 3;i++)
  1814. vidramp[i] = (unsigned short) (0.5 + pow(vidramp[i] * (1.0 / 65535.0), igamma) * 65535.0);
  1815. }
  1816. for (i = 0;i < width*height*4;i += 4)
  1817. {
  1818. buffer1[i] = (unsigned char) (vidramp[buffer1[i] + 512] * 255.0 / 65535.0 + 0.5); // B
  1819. buffer1[i+1] = (unsigned char) (vidramp[buffer1[i+1] + 256] * 255.0 / 65535.0 + 0.5); // G
  1820. buffer1[i+2] = (unsigned char) (vidramp[buffer1[i+2]] * 255.0 / 65535.0 + 0.5); // R
  1821. // A
  1822. }
  1823. }
  1824. if(keep_alpha && !jpeg)
  1825. {
  1826. if(!png)
  1827. flipy = !flipy; // TGA: not preflipped
  1828. Image_CopyMux (buffer2, buffer1, width, height, flipx, flipy, flipdiagonal, 4, 4, indices);
  1829. if (png)
  1830. ret = PNG_SaveImage_preflipped (filename, width, height, true, buffer2);
  1831. else
  1832. ret = Image_WriteTGABGRA(filename, width, height, buffer2);
  1833. }
  1834. else
  1835. {
  1836. if(jpeg)
  1837. {
  1838. indices[0] = 2;
  1839. indices[2] = 0; // RGB
  1840. }
  1841. Image_CopyMux (buffer2, buffer1, width, height, flipx, flipy, flipdiagonal, 3, 4, indices);
  1842. if (jpeg)
  1843. ret = JPEG_SaveImage_preflipped (filename, width, height, buffer2);
  1844. else if (png)
  1845. ret = PNG_SaveImage_preflipped (filename, width, height, false, buffer2);
  1846. else
  1847. ret = Image_WriteTGABGR_preflipped (filename, width, height, buffer2);
  1848. }
  1849. return ret;
  1850. }
  1851. //=============================================================================
  1852. int scr_numtouchscreenareas;
  1853. scr_touchscreenarea_t scr_touchscreenareas[128];
  1854. static void SCR_DrawTouchscreenOverlay(void)
  1855. {
  1856. int i;
  1857. scr_touchscreenarea_t *a;
  1858. cachepic_t *pic;
  1859. for (i = 0, a = scr_touchscreenareas;i < scr_numtouchscreenareas;i++, a++)
  1860. {
  1861. if (vid_touchscreen_outlinealpha.value > 0 && a->rect[0] >= 0 && a->rect[1] >= 0 && a->rect[2] >= 4 && a->rect[3] >= 4)
  1862. {
  1863. DrawQ_Fill(a->rect[0] + 2, a->rect[1] , a->rect[2] - 4, 1 , 1, 1, 1, vid_touchscreen_outlinealpha.value * (0.5f + 0.5f * a->active), 0);
  1864. DrawQ_Fill(a->rect[0] + 1, a->rect[1] + 1, a->rect[2] - 2, 1 , 1, 1, 1, vid_touchscreen_outlinealpha.value * (0.5f + 0.5f * a->active), 0);
  1865. DrawQ_Fill(a->rect[0] , a->rect[1] + 2, 2 , a->rect[3] - 2, 1, 1, 1, vid_touchscreen_outlinealpha.value * (0.5f + 0.5f * a->active), 0);
  1866. DrawQ_Fill(a->rect[0] + a->rect[2] - 2, a->rect[1] + 2, 2 , a->rect[3] - 2, 1, 1, 1, vid_touchscreen_outlinealpha.value * (0.5f + 0.5f * a->active), 0);
  1867. DrawQ_Fill(a->rect[0] + 1, a->rect[1] + a->rect[3] - 2, a->rect[2] - 2, 1 , 1, 1, 1, vid_touchscreen_outlinealpha.value * (0.5f + 0.5f * a->active), 0);
  1868. DrawQ_Fill(a->rect[0] + 2, a->rect[1] + a->rect[3] - 1, a->rect[2] - 4, 1 , 1, 1, 1, vid_touchscreen_outlinealpha.value * (0.5f + 0.5f * a->active), 0);
  1869. }
  1870. pic = a->pic ? Draw_CachePic(a->pic) : NULL;
  1871. if (pic && pic->tex != r_texture_notexture)
  1872. DrawQ_Pic(a->rect[0], a->rect[1], Draw_CachePic(a->pic), a->rect[2], a->rect[3], 1, 1, 1, vid_touchscreen_overlayalpha.value * (0.5f + 0.5f * a->active), 0);
  1873. if (a->text && a->text[0])
  1874. {
  1875. int textwidth = DrawQ_TextWidth(a->text, 0, a->textheight, a->textheight, false, FONT_CHAT);
  1876. DrawQ_String(a->rect[0] + (a->rect[2] - textwidth) * 0.5f, a->rect[1] + (a->rect[3] - a->textheight) * 0.5f, a->text, 0, a->textheight, a->textheight, 1.0f, 1.0f, 1.0f, vid_touchscreen_overlayalpha.value, 0, NULL, false, FONT_CHAT);
  1877. }
  1878. }
  1879. }
  1880. void R_ClearScreen(qboolean fogcolor)
  1881. {
  1882. float clearcolor[4];
  1883. if (scr_screenshot_alpha.integer)
  1884. // clear to transparency (so png screenshots can contain alpha channel, useful for building model pictures)
  1885. Vector4Set(clearcolor, 0.0f, 0.0f, 0.0f, 0.0f);
  1886. else
  1887. // clear to opaque black (if we're being composited it might otherwise render as transparent)
  1888. Vector4Set(clearcolor, 0.0f, 0.0f, 0.0f, 1.0f);
  1889. if (fogcolor && r_fog_clear.integer)
  1890. {
  1891. R_UpdateFog();
  1892. VectorCopy(r_refdef.fogcolor, clearcolor);
  1893. }
  1894. // clear depth is 1.0
  1895. // LordHavoc: we use a stencil centered around 128 instead of 0,
  1896. // to avoid clamping interfering with strange shadow volume
  1897. // drawing orders
  1898. // clear the screen
  1899. GL_Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | (vid.stencil ? GL_STENCIL_BUFFER_BIT : 0), clearcolor, 1.0f, 128);
  1900. }
  1901. int r_stereo_side;
  1902. static void SCR_DrawScreen (void)
  1903. {
  1904. Draw_Frame();
  1905. R_Mesh_Start();
  1906. R_UpdateVariables();
  1907. // Quake uses clockwise winding, so these are swapped
  1908. r_refdef.view.cullface_front = GL_BACK;
  1909. r_refdef.view.cullface_back = GL_FRONT;
  1910. if (cls.signon == SIGNONS)
  1911. {
  1912. float size;
  1913. size = scr_viewsize.value * (1.0 / 100.0);
  1914. size = min(size, 1);
  1915. if (r_stereo_sidebyside.integer)
  1916. {
  1917. r_refdef.view.width = (int)(vid.width * size / 2.5);
  1918. r_refdef.view.height = (int)(vid.height * size / 2.5 * (1 - bound(0, r_letterbox.value, 100) / 100));
  1919. r_refdef.view.depth = 1;
  1920. r_refdef.view.x = (int)((vid.width - r_refdef.view.width * 2.5) * 0.5);
  1921. r_refdef.view.y = (int)((vid.height - r_refdef.view.height)/2);
  1922. r_refdef.view.z = 0;
  1923. if (r_stereo_side)
  1924. r_refdef.view.x += (int)(r_refdef.view.width * 1.5);
  1925. }
  1926. else if (r_stereo_horizontal.integer)
  1927. {
  1928. r_refdef.view.width = (int)(vid.width * size / 2);
  1929. r_refdef.view.height = (int)(vid.height * size * (1 - bound(0, r_letterbox.value, 100) / 100));
  1930. r_refdef.view.depth = 1;
  1931. r_refdef.view.x = (int)((vid.width - r_refdef.view.width * 2.0)/2);
  1932. r_refdef.view.y = (int)((vid.height - r_refdef.view.height)/2);
  1933. r_refdef.view.z = 0;
  1934. if (r_stereo_side)
  1935. r_refdef.view.x += (int)(r_refdef.view.width);
  1936. }
  1937. else if (r_stereo_vertical.integer)
  1938. {
  1939. r_refdef.view.width = (int)(vid.width * size);
  1940. r_refdef.view.height = (int)(vid.height * size * (1 - bound(0, r_letterbox.value, 100) / 100) / 2);
  1941. r_refdef.view.depth = 1;
  1942. r_refdef.view.x = (int)((vid.width - r_refdef.view.width)/2);
  1943. r_refdef.view.y = (int)((vid.height - r_refdef.view.height * 2.0)/2);
  1944. r_refdef.view.z = 0;
  1945. if (r_stereo_side)
  1946. r_refdef.view.y += (int)(r_refdef.view.height);
  1947. }
  1948. else
  1949. {
  1950. r_refdef.view.width = (int)(vid.width * size);
  1951. r_refdef.view.height = (int)(vid.height * size * (1 - bound(0, r_letterbox.value, 100) / 100));
  1952. r_refdef.view.depth = 1;
  1953. r_refdef.view.x = (int)((vid.width - r_refdef.view.width)/2);
  1954. r_refdef.view.y = (int)((vid.height - r_refdef.view.height)/2);
  1955. r_refdef.view.z = 0;
  1956. }
  1957. // LordHavoc: viewzoom (zoom in for sniper rifles, etc)
  1958. // LordHavoc: this is designed to produce widescreen fov values
  1959. // when the screen is wider than 4/3 width/height aspect, to do
  1960. // this it simply assumes the requested fov is the vertical fov
  1961. // for a 4x3 display, if the ratio is not 4x3 this makes the fov
  1962. // higher/lower according to the ratio
  1963. r_refdef.view.useperspective = true;
  1964. r_refdef.view.frustum_y = tan(scr_fov.value * M_PI / 360.0) * (3.0/4.0) * cl.viewzoom;
  1965. r_refdef.view.frustum_x = r_refdef.view.frustum_y * (float)r_refdef.view.width / (float)r_refdef.view.height / vid_pixelheight.value;
  1966. r_refdef.view.frustum_x *= r_refdef.frustumscale_x;
  1967. r_refdef.view.frustum_y *= r_refdef.frustumscale_y;
  1968. r_refdef.view.ortho_x = atan(r_refdef.view.frustum_x) * (360.0 / M_PI); // abused as angle by VM_CL_R_SetView
  1969. r_refdef.view.ortho_y = atan(r_refdef.view.frustum_y) * (360.0 / M_PI); // abused as angle by VM_CL_R_SetView
  1970. // if CSQC is loaded, it is required to provide the CSQC_UpdateView function,
  1971. // and won't render a view if it does not call that.
  1972. if (cl.csqc_loaded)
  1973. CL_VM_UpdateView(r_stereo_side ? 0.0 : max(0.0, cl.time - cl.oldtime));
  1974. else
  1975. {
  1976. CL_UpdateEntityShading();
  1977. R_RenderView();
  1978. }
  1979. }
  1980. if (!r_stereo_sidebyside.integer && !r_stereo_horizontal.integer && !r_stereo_vertical.integer)
  1981. {
  1982. r_refdef.view.width = vid.width;
  1983. r_refdef.view.height = vid.height;
  1984. r_refdef.view.depth = 1;
  1985. r_refdef.view.x = 0;
  1986. r_refdef.view.y = 0;
  1987. r_refdef.view.z = 0;
  1988. r_refdef.view.useperspective = false;
  1989. }
  1990. if (cls.timedemo && cls.td_frames > 0 && timedemo_screenshotframelist.string && timedemo_screenshotframelist.string[0])
  1991. {
  1992. const char *t;
  1993. int framenum;
  1994. t = timedemo_screenshotframelist.string;
  1995. while (*t)
  1996. {
  1997. while (*t == ' ')
  1998. t++;
  1999. if (!*t)
  2000. break;
  2001. framenum = atof(t);
  2002. if (framenum == cls.td_frames)
  2003. break;
  2004. while (*t && *t != ' ')
  2005. t++;
  2006. }
  2007. if (*t)
  2008. {
  2009. // we need to take a screenshot of this frame...
  2010. char filename[MAX_QPATH];
  2011. unsigned char *buffer1;
  2012. unsigned char *buffer2;
  2013. dpsnprintf(filename, sizeof(filename), "timedemoscreenshots/%s%06d.tga", cls.demoname, cls.td_frames);
  2014. buffer1 = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 4);
  2015. buffer2 = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 3);
  2016. SCR_ScreenShot(filename, buffer1, buffer2, 0, 0, vid.width, vid.height, false, false, false, false, false, true, false);
  2017. Mem_Free(buffer1);
  2018. Mem_Free(buffer2);
  2019. }
  2020. }
  2021. // draw 2D stuff
  2022. if(!scr_con_current && !(key_consoleactive & KEY_CONSOLEACTIVE_FORCED))
  2023. if ((key_dest == key_game || key_dest == key_message) && !r_letterbox.value)
  2024. Con_DrawNotify (); // only draw notify in game
  2025. if (cls.signon == SIGNONS)
  2026. {
  2027. SCR_DrawNet ();
  2028. SCR_DrawTurtle ();
  2029. SCR_DrawPause ();
  2030. if (!r_letterbox.value)
  2031. Sbar_Draw();
  2032. SHOWLMP_drawall();
  2033. SCR_CheckDrawCenterString();
  2034. }
  2035. SCR_DrawNetGraph ();
  2036. #ifdef CONFIG_MENU
  2037. MR_Draw();
  2038. #endif
  2039. CL_DrawVideo();
  2040. R_Shadow_EditLights_DrawSelectedLightProperties();
  2041. SCR_DrawConsole();
  2042. SCR_DrawBrand();
  2043. SCR_DrawInfobar();
  2044. SCR_DrawTouchscreenOverlay();
  2045. if (r_timereport_active)
  2046. R_TimeReport("2d");
  2047. R_TimeReport_EndFrame();
  2048. R_TimeReport_BeginFrame();
  2049. Sbar_ShowFPS();
  2050. DrawQ_Finish();
  2051. R_Mesh_Finish();
  2052. }
  2053. typedef struct loadingscreenstack_s
  2054. {
  2055. struct loadingscreenstack_s *prev;
  2056. char msg[MAX_QPATH];
  2057. float absolute_loading_amount_min; // this corresponds to relative completion 0 of this item
  2058. float absolute_loading_amount_len; // this corresponds to relative completion 1 of this item
  2059. float relative_completion; // 0 .. 1
  2060. }
  2061. loadingscreenstack_t;
  2062. static loadingscreenstack_t *loadingscreenstack = NULL;
  2063. static qboolean loadingscreendone = false;
  2064. static qboolean loadingscreencleared = false;
  2065. static float loadingscreenheight = 0;
  2066. rtexture_t *loadingscreentexture = NULL;
  2067. static float loadingscreentexture_vertex3f[12];
  2068. static float loadingscreentexture_texcoord2f[8];
  2069. static int loadingscreenpic_number = 0;
  2070. static void SCR_ClearLoadingScreenTexture(void)
  2071. {
  2072. if(loadingscreentexture)
  2073. R_FreeTexture(loadingscreentexture);
  2074. loadingscreentexture = NULL;
  2075. }
  2076. extern rtexturepool_t *r_main_texturepool;
  2077. static void SCR_SetLoadingScreenTexture(void)
  2078. {
  2079. int w, h;
  2080. float loadingscreentexture_w;
  2081. float loadingscreentexture_h;
  2082. SCR_ClearLoadingScreenTexture();
  2083. if (vid.support.arb_texture_non_power_of_two)
  2084. {
  2085. w = vid.width; h = vid.height;
  2086. loadingscreentexture_w = loadingscreentexture_h = 1;
  2087. }
  2088. else
  2089. {
  2090. w = CeilPowerOf2(vid.width); h = CeilPowerOf2(vid.height);
  2091. loadingscreentexture_w = vid.width / (float) w;
  2092. loadingscreentexture_h = vid.height / (float) h;
  2093. }
  2094. loadingscreentexture = R_LoadTexture2D(r_main_texturepool, "loadingscreentexture", w, h, NULL, TEXTYPE_COLORBUFFER, TEXF_RENDERTARGET | TEXF_FORCENEAREST | TEXF_CLAMP, -1, NULL);
  2095. R_Mesh_CopyToTexture(loadingscreentexture, 0, 0, 0, 0, vid.width, vid.height);
  2096. loadingscreentexture_vertex3f[2] = loadingscreentexture_vertex3f[5] = loadingscreentexture_vertex3f[8] = loadingscreentexture_vertex3f[11] = 0;
  2097. loadingscreentexture_vertex3f[0] = loadingscreentexture_vertex3f[9] = 0;
  2098. loadingscreentexture_vertex3f[1] = loadingscreentexture_vertex3f[4] = 0;
  2099. loadingscreentexture_vertex3f[3] = loadingscreentexture_vertex3f[6] = vid_conwidth.integer;
  2100. loadingscreentexture_vertex3f[7] = loadingscreentexture_vertex3f[10] = vid_conheight.integer;
  2101. loadingscreentexture_texcoord2f[0] = 0;loadingscreentexture_texcoord2f[1] = loadingscreentexture_h;
  2102. loadingscreentexture_texcoord2f[2] = loadingscreentexture_w;loadingscreentexture_texcoord2f[3] = loadingscreentexture_h;
  2103. loadingscreentexture_texcoord2f[4] = loadingscreentexture_w;loadingscreentexture_texcoord2f[5] = 0;
  2104. loadingscreentexture_texcoord2f[6] = 0;loadingscreentexture_texcoord2f[7] = 0;
  2105. }
  2106. void SCR_UpdateLoadingScreenIfShown(void)
  2107. {
  2108. if(loadingscreendone)
  2109. SCR_UpdateLoadingScreen(loadingscreencleared, false);
  2110. }
  2111. void SCR_PushLoadingScreen (qboolean redraw, const char *msg, float len_in_parent)
  2112. {
  2113. loadingscreenstack_t *s = (loadingscreenstack_t *) Z_Malloc(sizeof(loadingscreenstack_t));
  2114. s->prev = loadingscreenstack;
  2115. loadingscreenstack = s;
  2116. strlcpy(s->msg, msg, sizeof(s->msg));
  2117. s->relative_completion = 0;
  2118. if(s->prev)
  2119. {
  2120. s->absolute_loading_amount_min = s->prev->absolute_loading_amount_min + s->prev->absolute_loading_amount_len * s->prev->relative_completion;
  2121. s->absolute_loading_amount_len = s->prev->absolute_loading_amount_len * len_in_parent;
  2122. if(s->absolute_loading_amount_len > s->prev->absolute_loading_amount_min + s->prev->absolute_loading_amount_len - s->absolute_loading_amount_min)
  2123. s->absolute_loading_amount_len = s->prev->absolute_loading_amount_min + s->prev->absolute_loading_amount_len - s->absolute_loading_amount_min;
  2124. }
  2125. else
  2126. {
  2127. s->absolute_loading_amount_min = 0;
  2128. s->absolute_loading_amount_len = 1;
  2129. }
  2130. if(redraw)
  2131. SCR_UpdateLoadingScreenIfShown();
  2132. }
  2133. void SCR_PopLoadingScreen (qboolean redraw)
  2134. {
  2135. loadingscreenstack_t *s = loadingscreenstack;
  2136. if(!s)
  2137. {
  2138. Con_DPrintf("Popping a loading screen item from an empty stack!\n");
  2139. return;
  2140. }
  2141. loadingscreenstack = s->prev;
  2142. if(s->prev)
  2143. s->prev->relative_completion = (s->absolute_loading_amount_min + s->absolute_loading_amount_len - s->prev->absolute_loading_amount_min) / s->prev->absolute_loading_amount_len;
  2144. Z_Free(s);
  2145. if(redraw)
  2146. SCR_UpdateLoadingScreenIfShown();
  2147. }
  2148. void SCR_ClearLoadingScreen (qboolean redraw)
  2149. {
  2150. while(loadingscreenstack)
  2151. SCR_PopLoadingScreen(redraw && !loadingscreenstack->prev);
  2152. }
  2153. static float SCR_DrawLoadingStack_r(loadingscreenstack_t *s, float y, float size)
  2154. {
  2155. float x;
  2156. size_t len;
  2157. float total;
  2158. total = 0;
  2159. #if 0
  2160. if(s)
  2161. {
  2162. total += SCR_DrawLoadingStack_r(s->prev, y, 8);
  2163. y -= total;
  2164. if(!s->prev || strcmp(s->msg, s->prev->msg))
  2165. {
  2166. len = strlen(s->msg);
  2167. x = (vid_conwidth.integer - DrawQ_TextWidth(s->msg, len, size, size, true, FONT_INFOBAR)) / 2;
  2168. y -= size;
  2169. DrawQ_Fill(0, y, vid_conwidth.integer, size, 0, 0, 0, 1, 0);
  2170. DrawQ_String(x, y, s->msg, len, size, size, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
  2171. total += size;
  2172. }
  2173. }
  2174. #else
  2175. if(s)
  2176. {
  2177. len = strlen(s->msg);
  2178. x = (vid_conwidth.integer - DrawQ_TextWidth(s->msg, len, size, size, true, FONT_INFOBAR)) / 2;
  2179. y -= size;
  2180. DrawQ_Fill(0, y, vid_conwidth.integer, size, 0, 0, 0, 1, 0);
  2181. DrawQ_String(x, y, s->msg, len, size, size, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
  2182. total += size;
  2183. }
  2184. #endif
  2185. return total;
  2186. }
  2187. static void SCR_DrawLoadingStack(void)
  2188. {
  2189. float verts[12];
  2190. float colors[16];
  2191. loadingscreenheight = SCR_DrawLoadingStack_r(loadingscreenstack, vid_conheight.integer, scr_loadingscreen_barheight.value);
  2192. if(loadingscreenstack)
  2193. {
  2194. // height = 32; // sorry, using the actual one is ugly
  2195. GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
  2196. GL_DepthRange(0, 1);
  2197. GL_PolygonOffset(0, 0);
  2198. GL_DepthTest(false);
  2199. // R_Mesh_ResetTextureState();
  2200. verts[2] = verts[5] = verts[8] = verts[11] = 0;
  2201. verts[0] = verts[9] = 0;
  2202. verts[1] = verts[4] = vid_conheight.integer - scr_loadingscreen_barheight.value;
  2203. verts[3] = verts[6] = vid_conwidth.integer * loadingscreenstack->absolute_loading_amount_min;
  2204. verts[7] = verts[10] = vid_conheight.integer;
  2205. #if _MSC_VER >= 1400
  2206. #define sscanf sscanf_s
  2207. #endif
  2208. // ^^^^^^^^^^ blue component
  2209. // ^^^^^^ bottom row
  2210. // ^^^^^^^^^^^^ alpha is always on
  2211. colors[0] = 0; colors[1] = 0; colors[2] = 0; colors[3] = 1;
  2212. colors[4] = 0; colors[5] = 0; colors[6] = 0; colors[7] = 1;
  2213. sscanf(scr_loadingscreen_barcolor.string, "%f %f %f", &colors[8], &colors[9], &colors[10]); colors[11] = 1;
  2214. sscanf(scr_loadingscreen_barcolor.string, "%f %f %f", &colors[12], &colors[13], &colors[14]); colors[15] = 1;
  2215. R_Mesh_PrepareVertices_Generic_Arrays(4, verts, colors, NULL);
  2216. R_SetupShader_Generic_NoTexture(true, true);
  2217. R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
  2218. // make sure everything is cleared, including the progress indicator
  2219. if(loadingscreenheight < 8)
  2220. loadingscreenheight = 8;
  2221. }
  2222. }
  2223. static cachepic_t *loadingscreenpic;
  2224. static float loadingscreenpic_vertex3f[12];
  2225. static float loadingscreenpic_texcoord2f[8];
  2226. static void SCR_DrawLoadingScreen_SharedSetup (qboolean clear)
  2227. {
  2228. r_viewport_t viewport;
  2229. float x, y, w, h, sw, sh, f;
  2230. char vabuf[1024];
  2231. // release mouse grab while loading
  2232. if (!vid.fullscreen)
  2233. VID_SetMouse(false, false, false);
  2234. // CHECKGLERROR
  2235. r_refdef.draw2dstage = true;
  2236. R_Viewport_InitOrtho(&viewport, &identitymatrix, 0, 0, vid.width, vid.height, 0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100, NULL);
  2237. R_Mesh_SetRenderTargets(0, NULL, NULL, NULL, NULL, NULL);
  2238. R_SetViewport(&viewport);
  2239. GL_ColorMask(1,1,1,1);
  2240. // when starting up a new video mode, make sure the screen is cleared to black
  2241. if (clear || loadingscreentexture)
  2242. GL_Clear(GL_COLOR_BUFFER_BIT, NULL, 1.0f, 0);
  2243. R_Textures_Frame();
  2244. R_Mesh_Start();
  2245. R_EntityMatrix(&identitymatrix);
  2246. // draw the loading plaque
  2247. loadingscreenpic = Draw_CachePic_Flags (loadingscreenpic_number ? va(vabuf, sizeof(vabuf), "%s%d", scr_loadingscreen_picture.string, loadingscreenpic_number+1) : scr_loadingscreen_picture.string, loadingscreenpic_number ? CACHEPICFLAG_NOTPERSISTENT : 0);
  2248. w = loadingscreenpic->width;
  2249. h = loadingscreenpic->height;
  2250. // apply scale
  2251. w *= scr_loadingscreen_scale.value;
  2252. h *= scr_loadingscreen_scale.value;
  2253. // apply scale base
  2254. if(scr_loadingscreen_scale_base.integer)
  2255. {
  2256. w *= vid_conwidth.integer / (float) vid.width;
  2257. h *= vid_conheight.integer / (float) vid.height;
  2258. }
  2259. // apply scale limit
  2260. sw = w / vid_conwidth.integer;
  2261. sh = h / vid_conheight.integer;
  2262. f = 1;
  2263. switch(scr_loadingscreen_scale_limit.integer)
  2264. {
  2265. case 1:
  2266. f = max(sw, sh);
  2267. break;
  2268. case 2:
  2269. f = min(sw, sh);
  2270. break;
  2271. case 3:
  2272. f = sw;
  2273. break;
  2274. case 4:
  2275. f = sh;
  2276. break;
  2277. }
  2278. if(f > 1)
  2279. {
  2280. w /= f;
  2281. h /= f;
  2282. }
  2283. x = (vid_conwidth.integer - w)/2;
  2284. y = (vid_conheight.integer - h)/2;
  2285. loadingscreenpic_vertex3f[2] = loadingscreenpic_vertex3f[5] = loadingscreenpic_vertex3f[8] = loadingscreenpic_vertex3f[11] = 0;
  2286. loadingscreenpic_vertex3f[0] = loadingscreenpic_vertex3f[9] = x;
  2287. loadingscreenpic_vertex3f[1] = loadingscreenpic_vertex3f[4] = y;
  2288. loadingscreenpic_vertex3f[3] = loadingscreenpic_vertex3f[6] = x + w;
  2289. loadingscreenpic_vertex3f[7] = loadingscreenpic_vertex3f[10] = y + h;
  2290. loadingscreenpic_texcoord2f[0] = 0;loadingscreenpic_texcoord2f[1] = 0;
  2291. loadingscreenpic_texcoord2f[2] = 1;loadingscreenpic_texcoord2f[3] = 0;
  2292. loadingscreenpic_texcoord2f[4] = 1;loadingscreenpic_texcoord2f[5] = 1;
  2293. loadingscreenpic_texcoord2f[6] = 0;loadingscreenpic_texcoord2f[7] = 1;
  2294. }
  2295. static void SCR_DrawLoadingScreen (qboolean clear)
  2296. {
  2297. // we only need to draw the image if it isn't already there
  2298. GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  2299. GL_DepthRange(0, 1);
  2300. GL_PolygonOffset(0, 0);
  2301. GL_DepthTest(false);
  2302. // R_Mesh_ResetTextureState();
  2303. GL_Color(1,1,1,1);
  2304. if(loadingscreentexture)
  2305. {
  2306. R_Mesh_PrepareVertices_Generic_Arrays(4, loadingscreentexture_vertex3f, NULL, loadingscreentexture_texcoord2f);
  2307. R_SetupShader_Generic(loadingscreentexture, NULL, GL_MODULATE, 1, true, true, true);
  2308. R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
  2309. }
  2310. R_Mesh_PrepareVertices_Generic_Arrays(4, loadingscreenpic_vertex3f, NULL, loadingscreenpic_texcoord2f);
  2311. R_SetupShader_Generic(Draw_GetPicTexture(loadingscreenpic), NULL, GL_MODULATE, 1, true, true, false);
  2312. R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
  2313. SCR_DrawLoadingStack();
  2314. }
  2315. static void SCR_DrawLoadingScreen_SharedFinish (qboolean clear)
  2316. {
  2317. R_Mesh_Finish();
  2318. // refresh
  2319. VID_Finish();
  2320. }
  2321. static double loadingscreen_lastupdate;
  2322. void SCR_UpdateLoadingScreen (qboolean clear, qboolean startup)
  2323. {
  2324. keydest_t old_key_dest;
  2325. int old_key_consoleactive;
  2326. // don't do anything if not initialized yet
  2327. if (vid_hidden || cls.state == ca_dedicated)
  2328. return;
  2329. // limit update rate
  2330. if (scr_loadingscreen_maxfps.value)
  2331. {
  2332. double t = Sys_DirtyTime();
  2333. if ((t - loadingscreen_lastupdate) < 1.0f/scr_loadingscreen_maxfps.value)
  2334. return;
  2335. loadingscreen_lastupdate = t;
  2336. }
  2337. // set up the r_texture_gammaramps texture which we need for rendering the loadingscreenpic
  2338. VID_UpdateGamma();
  2339. R_UpdateVariables();
  2340. if(!scr_loadingscreen_background.integer)
  2341. clear = true;
  2342. if(loadingscreendone)
  2343. clear |= loadingscreencleared;
  2344. if(!loadingscreendone)
  2345. {
  2346. if(startup && scr_loadingscreen_firstforstartup.integer)
  2347. loadingscreenpic_number = 0;
  2348. else if(scr_loadingscreen_firstforstartup.integer)
  2349. if(scr_loadingscreen_count.integer > 1)
  2350. loadingscreenpic_number = rand() % (scr_loadingscreen_count.integer - 1) + 1;
  2351. else
  2352. loadingscreenpic_number = 0;
  2353. else
  2354. loadingscreenpic_number = rand() % (scr_loadingscreen_count.integer > 1 ? scr_loadingscreen_count.integer : 1);
  2355. }
  2356. if(clear)
  2357. SCR_ClearLoadingScreenTexture();
  2358. else if(!loadingscreendone)
  2359. SCR_SetLoadingScreenTexture();
  2360. if(!loadingscreendone)
  2361. {
  2362. loadingscreendone = true;
  2363. loadingscreenheight = 0;
  2364. }
  2365. loadingscreencleared = clear;
  2366. #ifdef USE_GLES2
  2367. SCR_DrawLoadingScreen_SharedSetup(clear);
  2368. SCR_DrawLoadingScreen(clear);
  2369. #else
  2370. if (qglDrawBuffer)
  2371. qglDrawBuffer(GL_BACK);
  2372. SCR_DrawLoadingScreen_SharedSetup(clear);
  2373. if (vid.stereobuffer && qglDrawBuffer)
  2374. {
  2375. qglDrawBuffer(GL_BACK_LEFT);
  2376. SCR_DrawLoadingScreen(clear);
  2377. qglDrawBuffer(GL_BACK_RIGHT);
  2378. SCR_DrawLoadingScreen(clear);
  2379. }
  2380. else
  2381. {
  2382. if (qglDrawBuffer)
  2383. qglDrawBuffer(GL_BACK);
  2384. SCR_DrawLoadingScreen(clear);
  2385. }
  2386. #endif
  2387. SCR_DrawLoadingScreen_SharedFinish(clear);
  2388. // this goes into the event loop, and should prevent unresponsive cursor on vista
  2389. old_key_dest = key_dest;
  2390. old_key_consoleactive = key_consoleactive;
  2391. key_dest = key_void;
  2392. key_consoleactive = false;
  2393. Key_EventQueue_Block(); Sys_SendKeyEvents();
  2394. key_dest = old_key_dest;
  2395. key_consoleactive = old_key_consoleactive;
  2396. }
  2397. qboolean R_Stereo_ColorMasking(void)
  2398. {
  2399. return r_stereo_redblue.integer || r_stereo_redgreen.integer || r_stereo_redcyan.integer;
  2400. }
  2401. qboolean R_Stereo_Active(void)
  2402. {
  2403. return (vid.stereobuffer || r_stereo_sidebyside.integer || r_stereo_horizontal.integer || r_stereo_vertical.integer || R_Stereo_ColorMasking());
  2404. }
  2405. extern cvar_t cl_minfps;
  2406. extern cvar_t cl_minfps_fade;
  2407. extern cvar_t cl_minfps_qualitymax;
  2408. extern cvar_t cl_minfps_qualitymin;
  2409. extern cvar_t cl_minfps_qualitymultiply;
  2410. extern cvar_t cl_minfps_qualityhysteresis;
  2411. extern cvar_t cl_minfps_qualitystepmax;
  2412. extern cvar_t cl_minfps_force;
  2413. static double cl_updatescreen_quality = 1;
  2414. void CL_UpdateScreen(void)
  2415. {
  2416. vec3_t vieworigin;
  2417. static double drawscreenstart = 0.0;
  2418. double drawscreendelta;
  2419. float conwidth, conheight;
  2420. r_viewport_t viewport;
  2421. if(drawscreenstart)
  2422. {
  2423. drawscreendelta = Sys_DirtyTime() - drawscreenstart;
  2424. if (cl_minfps.value > 0 && (cl_minfps_force.integer || !(cls.timedemo || (cls.capturevideo.active && !cls.capturevideo.realtime))) && drawscreendelta >= 0 && drawscreendelta < 60)
  2425. {
  2426. // quality adjustment according to render time
  2427. double actualframetime;
  2428. double targetframetime;
  2429. double adjust;
  2430. double f;
  2431. double h;
  2432. // fade lastdrawscreentime
  2433. r_refdef.lastdrawscreentime += (drawscreendelta - r_refdef.lastdrawscreentime) * cl_minfps_fade.value;
  2434. // find actual and target frame times
  2435. actualframetime = r_refdef.lastdrawscreentime;
  2436. targetframetime = (1.0 / cl_minfps.value);
  2437. // we scale hysteresis by quality
  2438. h = cl_updatescreen_quality * cl_minfps_qualityhysteresis.value;
  2439. // calculate adjustment assuming linearity
  2440. f = cl_updatescreen_quality / actualframetime * cl_minfps_qualitymultiply.value;
  2441. adjust = (targetframetime - actualframetime) * f;
  2442. // one sided hysteresis
  2443. if(adjust > 0)
  2444. adjust = max(0, adjust - h);
  2445. // adjust > 0 if:
  2446. // (targetframetime - actualframetime) * f > h
  2447. // ((1.0 / cl_minfps.value) - actualframetime) * (cl_updatescreen_quality / actualframetime * cl_minfps_qualitymultiply.value) > (cl_updatescreen_quality * cl_minfps_qualityhysteresis.value)
  2448. // ((1.0 / cl_minfps.value) - actualframetime) * (cl_minfps_qualitymultiply.value / actualframetime) > cl_minfps_qualityhysteresis.value
  2449. // (1.0 / cl_minfps.value) * (cl_minfps_qualitymultiply.value / actualframetime) - cl_minfps_qualitymultiply.value > cl_minfps_qualityhysteresis.value
  2450. // (1.0 / cl_minfps.value) * (cl_minfps_qualitymultiply.value / actualframetime) > cl_minfps_qualityhysteresis.value + cl_minfps_qualitymultiply.value
  2451. // (1.0 / cl_minfps.value) / actualframetime > (cl_minfps_qualityhysteresis.value + cl_minfps_qualitymultiply.value) / cl_minfps_qualitymultiply.value
  2452. // (1.0 / cl_minfps.value) / actualframetime > 1.0 + cl_minfps_qualityhysteresis.value / cl_minfps_qualitymultiply.value
  2453. // cl_minfps.value * actualframetime < 1.0 / (1.0 + cl_minfps_qualityhysteresis.value / cl_minfps_qualitymultiply.value)
  2454. // actualframetime < 1.0 / cl_minfps.value / (1.0 + cl_minfps_qualityhysteresis.value / cl_minfps_qualitymultiply.value)
  2455. // actualfps > cl_minfps.value * (1.0 + cl_minfps_qualityhysteresis.value / cl_minfps_qualitymultiply.value)
  2456. // adjust < 0 if:
  2457. // (targetframetime - actualframetime) * f < 0
  2458. // ((1.0 / cl_minfps.value) - actualframetime) * (cl_updatescreen_quality / actualframetime * cl_minfps_qualitymultiply.value) < 0
  2459. // ((1.0 / cl_minfps.value) - actualframetime) < 0
  2460. // -actualframetime) < -(1.0 / cl_minfps.value)
  2461. // actualfps < cl_minfps.value
  2462. /*
  2463. Con_Printf("adjust UP if fps > %f, adjust DOWN if fps < %f\n",
  2464. cl_minfps.value * (1.0 + cl_minfps_qualityhysteresis.value / cl_minfps_qualitymultiply.value),
  2465. cl_minfps.value);
  2466. */
  2467. // don't adjust too much at once
  2468. adjust = bound(-cl_minfps_qualitystepmax.value, adjust, cl_minfps_qualitystepmax.value);
  2469. // adjust!
  2470. cl_updatescreen_quality += adjust;
  2471. cl_updatescreen_quality = bound(max(0.01, cl_minfps_qualitymin.value), cl_updatescreen_quality, cl_minfps_qualitymax.value);
  2472. }
  2473. else
  2474. {
  2475. cl_updatescreen_quality = 1;
  2476. r_refdef.lastdrawscreentime = 0;
  2477. }
  2478. }
  2479. drawscreenstart = Sys_DirtyTime();
  2480. Sbar_ShowFPS_Update();
  2481. if (!scr_initialized || !con_initialized || !scr_refresh.integer)
  2482. return; // not initialized yet
  2483. loadingscreendone = false;
  2484. if(IS_NEXUIZ_DERIVED(gamemode))
  2485. {
  2486. // play a bit with the palette (experimental)
  2487. palette_rgb_pantscolormap[15][0] = (unsigned char) (128 + 127 * sin(cl.time / exp(1.0f) + 0.0f*M_PI/3.0f));
  2488. palette_rgb_pantscolormap[15][1] = (unsigned char) (128 + 127 * sin(cl.time / exp(1.0f) + 2.0f*M_PI/3.0f));
  2489. palette_rgb_pantscolormap[15][2] = (unsigned char) (128 + 127 * sin(cl.time / exp(1.0f) + 4.0f*M_PI/3.0f));
  2490. palette_rgb_shirtcolormap[15][0] = (unsigned char) (128 + 127 * sin(cl.time / M_PI + 5.0f*M_PI/3.0f));
  2491. palette_rgb_shirtcolormap[15][1] = (unsigned char) (128 + 127 * sin(cl.time / M_PI + 3.0f*M_PI/3.0f));
  2492. palette_rgb_shirtcolormap[15][2] = (unsigned char) (128 + 127 * sin(cl.time / M_PI + 1.0f*M_PI/3.0f));
  2493. memcpy(palette_rgb_pantsscoreboard[15], palette_rgb_pantscolormap[15], sizeof(*palette_rgb_pantscolormap));
  2494. memcpy(palette_rgb_shirtscoreboard[15], palette_rgb_shirtcolormap[15], sizeof(*palette_rgb_shirtcolormap));
  2495. }
  2496. if (vid_hidden)
  2497. {
  2498. VID_Finish();
  2499. return;
  2500. }
  2501. conwidth = bound(160, vid_conwidth.value, 32768);
  2502. conheight = bound(90, vid_conheight.value, 24576);
  2503. if (vid_conwidth.value != conwidth)
  2504. Cvar_SetValue("vid_conwidth", conwidth);
  2505. if (vid_conheight.value != conheight)
  2506. Cvar_SetValue("vid_conheight", conheight);
  2507. // bound viewsize
  2508. if (scr_viewsize.value < 30)
  2509. Cvar_Set ("viewsize","30");
  2510. if (scr_viewsize.value > 120)
  2511. Cvar_Set ("viewsize","120");
  2512. // bound field of view
  2513. if (scr_fov.value < 1)
  2514. Cvar_Set ("fov","1");
  2515. if (scr_fov.value > 170)
  2516. Cvar_Set ("fov","170");
  2517. // intermission is always full screen
  2518. if (cl.intermission)
  2519. sb_lines = 0;
  2520. else
  2521. {
  2522. if (scr_viewsize.value >= 120)
  2523. sb_lines = 0; // no status bar at all
  2524. else if (scr_viewsize.value >= 110)
  2525. sb_lines = 24; // no inventory
  2526. else
  2527. sb_lines = 24+16+8;
  2528. }
  2529. R_FrameData_NewFrame();
  2530. R_BufferData_NewFrame();
  2531. Matrix4x4_OriginFromMatrix(&r_refdef.view.matrix, vieworigin);
  2532. R_HDR_UpdateIrisAdaptation(vieworigin);
  2533. r_refdef.view.colormask[0] = 1;
  2534. r_refdef.view.colormask[1] = 1;
  2535. r_refdef.view.colormask[2] = 1;
  2536. SCR_SetUpToDrawConsole();
  2537. #ifndef USE_GLES2
  2538. if (qglDrawBuffer)
  2539. {
  2540. CHECKGLERROR
  2541. qglDrawBuffer(GL_BACK);CHECKGLERROR
  2542. // set dithering mode
  2543. if (gl_dither.integer)
  2544. {
  2545. qglEnable(GL_DITHER);CHECKGLERROR
  2546. }
  2547. else
  2548. {
  2549. qglDisable(GL_DITHER);CHECKGLERROR
  2550. }
  2551. }
  2552. #endif
  2553. R_Viewport_InitOrtho(&viewport, &identitymatrix, 0, 0, vid.width, vid.height, 0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100, NULL);
  2554. R_Mesh_SetRenderTargets(0, NULL, NULL, NULL, NULL, NULL);
  2555. R_SetViewport(&viewport);
  2556. GL_ScissorTest(false);
  2557. GL_ColorMask(1,1,1,1);
  2558. GL_DepthMask(true);
  2559. R_ClearScreen(false);
  2560. r_refdef.view.clear = false;
  2561. r_refdef.view.isoverlay = false;
  2562. // calculate r_refdef.view.quality
  2563. r_refdef.view.quality = cl_updatescreen_quality;
  2564. #ifndef USE_GLES2
  2565. if (qglPolygonStipple)
  2566. {
  2567. if(scr_stipple.integer)
  2568. {
  2569. GLubyte stipple[128];
  2570. int i, s, width, parts;
  2571. static int frame = 0;
  2572. ++frame;
  2573. s = scr_stipple.integer;
  2574. parts = (s & 007);
  2575. width = (s & 070) >> 3;
  2576. qglEnable(GL_POLYGON_STIPPLE);CHECKGLERROR // 0x0B42
  2577. for(i = 0; i < 128; ++i)
  2578. {
  2579. int line = i/4;
  2580. stipple[i] = (((line >> width) + frame) & ((1 << parts) - 1)) ? 0x00 : 0xFF;
  2581. }
  2582. qglPolygonStipple(stipple);CHECKGLERROR
  2583. }
  2584. else
  2585. {
  2586. qglDisable(GL_POLYGON_STIPPLE);CHECKGLERROR
  2587. }
  2588. }
  2589. #endif
  2590. #ifndef USE_GLES2
  2591. if (R_Stereo_Active())
  2592. {
  2593. r_stereo_side = 0;
  2594. if (r_stereo_redblue.integer || r_stereo_redgreen.integer || r_stereo_redcyan.integer)
  2595. {
  2596. r_refdef.view.colormask[0] = 1;
  2597. r_refdef.view.colormask[1] = 0;
  2598. r_refdef.view.colormask[2] = 0;
  2599. }
  2600. if (vid.stereobuffer)
  2601. qglDrawBuffer(GL_BACK_RIGHT);
  2602. SCR_DrawScreen();
  2603. r_stereo_side = 1;
  2604. r_refdef.view.clear = true;
  2605. if (r_stereo_redblue.integer || r_stereo_redgreen.integer || r_stereo_redcyan.integer)
  2606. {
  2607. r_refdef.view.colormask[0] = 0;
  2608. r_refdef.view.colormask[1] = r_stereo_redcyan.integer || r_stereo_redgreen.integer;
  2609. r_refdef.view.colormask[2] = r_stereo_redcyan.integer || r_stereo_redblue.integer;
  2610. }
  2611. if (vid.stereobuffer)
  2612. qglDrawBuffer(GL_BACK_LEFT);
  2613. SCR_DrawScreen();
  2614. r_stereo_side = 0;
  2615. }
  2616. else
  2617. #endif
  2618. {
  2619. r_stereo_side = 0;
  2620. SCR_DrawScreen();
  2621. }
  2622. #ifdef CONFIG_VIDEO_CAPTURE
  2623. SCR_CaptureVideo();
  2624. #endif
  2625. if (qglFlush)
  2626. qglFlush(); // FIXME: should we really be using qglFlush here?
  2627. if (!vid_activewindow)
  2628. VID_SetMouse(false, false, false);
  2629. else if (key_consoleactive)
  2630. VID_SetMouse(vid.fullscreen, false, false);
  2631. else if (key_dest == key_menu_grabbed)
  2632. VID_SetMouse(true, vid_mouse.integer && !in_client_mouse && !vid_touchscreen.integer, !vid_touchscreen.integer);
  2633. else if (key_dest == key_menu)
  2634. VID_SetMouse(vid.fullscreen, vid_mouse.integer && !in_client_mouse && !vid_touchscreen.integer, !vid_touchscreen.integer);
  2635. else
  2636. VID_SetMouse(vid.fullscreen, vid_mouse.integer && !cl.csqc_wantsmousemove && cl_prydoncursor.integer <= 0 && (!cls.demoplayback || cl_demo_mousegrab.integer) && !vid_touchscreen.integer, !vid_touchscreen.integer);
  2637. VID_Finish();
  2638. }
  2639. void CL_Screen_NewMap(void)
  2640. {
  2641. }