1 //**************************************************************************
3 //** ## ## ## ## ## #### #### ### ###
4 //** ## ## ## ## ## ## ## ## ## ## #### ####
5 //** ## ## ## ## ## ## ## ## ## ## ## ## ## ##
6 //** ## ## ######## ## ## ## ## ## ## ## ### ##
7 //** ### ## ## ### ## ## ## ## ## ##
8 //** # ## ## # #### #### ## ##
10 //** Copyright (C) 1999-2006 Jānis Legzdiņš
11 //** Copyright (C) 2018-2023 Ketmar Dark
13 //** This program is free software: you can redistribute it and/or modify
14 //** it under the terms of the GNU General Public License as published by
15 //** the Free Software Foundation, version 3 of the License ONLY.
17 //** This program is distributed in the hope that it will be useful,
18 //** but WITHOUT ANY WARRANTY; without even the implied warranty of
19 //** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 //** GNU General Public License for more details.
22 //** You should have received a copy of the GNU General Public License
23 //** along with this program. If not, see <http://www.gnu.org/licenses/>.
25 //**************************************************************************
31 #include "widgets/ui.h"
36 #include "psim/p_player.h"
37 #include "server/server.h"
38 #include "client/client.h"
39 #include "filesys/files.h"
40 #include "psim/p_entity.h"
43 extern int screenblocks
;
45 extern VCvarB dbg_world_think_vm_time
;
46 extern VCvarB dbg_world_think_decal_time
;
48 extern double worldThinkTimeVM
;
49 extern double worldThinkTimeDecal
;
51 static double wipeStartedTime
= -1.0;
52 static bool wipeStarted
= false;
56 int RealScreenWidth
= 0;
57 int RealScreenHeight
= 0;
59 int VirtualWidth
= 640;
60 int VirtualHeight
= 480;
62 static int lastFSMode
= -1;
69 // Table of RGB values in current gamma corection level
70 static const vuint8 gammatable
[5][256] = {
71 {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,
72 17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,
73 33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,
74 49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,
75 65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,
76 81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,
77 97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,
78 113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,
79 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
80 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
81 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
82 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
83 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
84 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
85 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
86 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255},
88 {2,4,5,7,8,10,11,12,14,15,16,18,19,20,21,23,24,25,26,27,29,30,31,
89 32,33,34,36,37,38,39,40,41,42,44,45,46,47,48,49,50,51,52,54,55,
90 56,57,58,59,60,61,62,63,64,65,66,67,69,70,71,72,73,74,75,76,77,
91 78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,
92 99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,
93 115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,129,
94 130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,
95 146,147,148,148,149,150,151,152,153,154,155,156,157,158,159,160,
96 161,162,163,163,164,165,166,167,168,169,170,171,172,173,174,175,
97 175,176,177,178,179,180,181,182,183,184,185,186,186,187,188,189,
98 190,191,192,193,194,195,196,196,197,198,199,200,201,202,203,204,
99 205,205,206,207,208,209,210,211,212,213,214,214,215,216,217,218,
100 219,220,221,222,222,223,224,225,226,227,228,229,230,230,231,232,
101 233,234,235,236,237,237,238,239,240,241,242,243,244,245,245,246,
102 247,248,249,250,251,252,252,253,254,255},
104 {4,7,9,11,13,15,17,19,21,22,24,26,27,29,30,32,33,35,36,38,39,40,42,
105 43,45,46,47,48,50,51,52,54,55,56,57,59,60,61,62,63,65,66,67,68,69,
106 70,72,73,74,75,76,77,78,79,80,82,83,84,85,86,87,88,89,90,91,92,93,
107 94,95,96,97,98,100,101,102,103,104,105,106,107,108,109,110,111,112,
108 113,114,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,
109 129,130,131,132,133,133,134,135,136,137,138,139,140,141,142,143,144,
110 144,145,146,147,148,149,150,151,152,153,153,154,155,156,157,158,159,
111 160,160,161,162,163,164,165,166,166,167,168,169,170,171,172,172,173,
112 174,175,176,177,178,178,179,180,181,182,183,183,184,185,186,187,188,
113 188,189,190,191,192,193,193,194,195,196,197,197,198,199,200,201,201,
114 202,203,204,205,206,206,207,208,209,210,210,211,212,213,213,214,215,
115 216,217,217,218,219,220,221,221,222,223,224,224,225,226,227,228,228,
116 229,230,231,231,232,233,234,235,235,236,237,238,238,239,240,241,241,
117 242,243,244,244,245,246,247,247,248,249,250,251,251,252,253,254,254,
120 {8,12,16,19,22,24,27,29,31,34,36,38,40,41,43,45,47,49,50,52,53,55,
121 57,58,60,61,63,64,65,67,68,70,71,72,74,75,76,77,79,80,81,82,84,85,
122 86,87,88,90,91,92,93,94,95,96,98,99,100,101,102,103,104,105,106,107,
123 108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,
124 125,126,127,128,129,130,131,132,133,134,135,135,136,137,138,139,140,
125 141,142,143,143,144,145,146,147,148,149,150,150,151,152,153,154,155,
126 155,156,157,158,159,160,160,161,162,163,164,165,165,166,167,168,169,
127 169,170,171,172,173,173,174,175,176,176,177,178,179,180,180,181,182,
128 183,183,184,185,186,186,187,188,189,189,190,191,192,192,193,194,195,
129 195,196,197,197,198,199,200,200,201,202,202,203,204,205,205,206,207,
130 207,208,209,210,210,211,212,212,213,214,214,215,216,216,217,218,219,
131 219,220,221,221,222,223,223,224,225,225,226,227,227,228,229,229,230,
132 231,231,232,233,233,234,235,235,236,237,237,238,238,239,240,240,241,
133 242,242,243,244,244,245,246,246,247,247,248,249,249,250,251,251,252,
134 253,253,254,254,255},
136 {16,23,28,32,36,39,42,45,48,50,53,55,57,60,62,64,66,68,69,71,73,75,76,
137 78,80,81,83,84,86,87,89,90,92,93,94,96,97,98,100,101,102,103,105,106,
138 107,108,109,110,112,113,114,115,116,117,118,119,120,121,122,123,124,
139 125,126,128,128,129,130,131,132,133,134,135,136,137,138,139,140,141,
140 142,143,143,144,145,146,147,148,149,150,150,151,152,153,154,155,155,
141 156,157,158,159,159,160,161,162,163,163,164,165,166,166,167,168,169,
142 169,170,171,172,172,173,174,175,175,176,177,177,178,179,180,180,181,
143 182,182,183,184,184,185,186,187,187,188,189,189,190,191,191,192,193,
144 193,194,195,195,196,196,197,198,198,199,200,200,201,202,202,203,203,
145 204,205,205,206,207,207,208,208,209,210,210,211,211,212,213,213,214,
146 214,215,216,216,217,217,218,219,219,220,220,221,221,222,223,223,224,
147 224,225,225,226,227,227,228,228,229,229,230,230,231,232,232,233,233,
148 234,234,235,235,236,236,237,237,238,239,239,240,240,241,241,242,242,
149 243,243,244,244,245,245,246,246,247,247,248,248,249,249,250,250,251,
150 251,252,252,253,254,254,255,255}
154 const vuint8
*getGammaTable (int idx
) {
155 if (idx
< 0) idx
= 0; else if (idx
> 4) idx
= 4;
156 return gammatable
[idx
];
159 static bool setresolutionneeded
= false;
160 static int setwidth
= 0;
161 static int setheight
= 0;
162 static float lastScrScale
= 0;
164 static VCvarB
dbg_disable_world_render("dbg_disable_world_render", false, "Disable world rendering?", CVAR_NoShadow
);
166 static VCvarF
menu_darkening("menu_darkening", "0.7", "Screen darkening for active menus.", CVAR_Archive
|CVAR_NoShadow
);
167 static VCvarB
draw_pause("draw_pause", true, "Draw \"paused\" text?", CVAR_Archive
|CVAR_NoShadow
);
169 static VCvarB
crosshair_topmost("crosshair_topmost", false, "Render crosshair on the top of everything?", CVAR_Archive
|CVAR_NoShadow
);
171 VCvarB
r_wipe_enabled("r_wipe_enabled", true, "Is screen wipe effect enabled?", CVAR_Archive
|CVAR_NoShadow
);
172 VCvarI
r_wipe_type("r_wipe_type", "0", "Wipe type?", CVAR_Archive
|CVAR_NoShadow
);
173 VCvarF
r_wipe_duration("r_wipe_duration", "1", "Wipe duration, in seconds?", CVAR_Archive
|CVAR_NoShadow
);
175 static VCvarI
ui_max_scale("ui_max_scale", "0", "Maximal UI scale (0 means unlimited).", CVAR_Archive
|CVAR_NoShadow
);
176 static VCvarI
ui_min_scale("ui_min_scale", "0", "Minimal UI scale (0 means unlimited).", CVAR_Archive
|CVAR_NoShadow
);
178 VCvarF
screen_scale("screen_scale", "1", "Screen scaling factor (you can set it to >1 to render screen in lower resolution).", CVAR_Archive
|CVAR_NoShadow
);
179 static VCvarI
screen_width("screen_width", "0", "Custom screen width", CVAR_Archive
|CVAR_NoShadow
);
180 static VCvarI
screen_height("screen_height", "0", "Custom screen height", CVAR_Archive
|CVAR_NoShadow
);
181 static VCvarI
screen_width_internal("screen_width_internal", "0", "Internal (rendering) screen width", CVAR_Rom
|CVAR_NoShadow
);
182 static VCvarI
screen_height_internal("screen_height_internal", "0", "Internal (rendering) screen height", CVAR_Rom
|CVAR_NoShadow
);
183 VCvarI
screen_fsmode("screen_fsmode", "0", "Video mode: windowed(0), fullscreen scaled(1), fullscreen real(2)", CVAR_Archive
|CVAR_NoShadow
);
184 static VCvarI
brightness("brightness", "0", "Brightness.", CVAR_Archive
|CVAR_NoShadow
);
186 static VCvarI
draw_fps("draw_fps", "0", "Draw FPS counter (1:FPS; 2:MSECS)?", CVAR_Archive
|CVAR_NoShadow
);
187 static VCvarI
draw_fps_posx("draw_fps_posx", "0", "FPS counter position (<0:left; 0:center; >0:right)", CVAR_Archive
|CVAR_NoShadow
);
188 static double fps_start
= 0.0;
189 static double ms
= 0.0;
190 static int fps_frames
= 0;
191 static int show_fps
= 0;
192 static double lastTexGCTime
= 0.0;
194 VCvarB
draw_lag("draw_lag", true, "Draw network lag value?", CVAR_Archive
|CVAR_NoShadow
);
196 static VCvarI
draw_gc_stats("draw_gc_stats", "0", "Draw GC stats (0: none; 1: brief; 2: full)?", CVAR_Archive
|CVAR_NoShadow
);
198 //static VCvarB draw_cycles("draw_cycles", false, "Draw cycle counter?", CVAR_NoShadow); //NOOP
201 //**************************************************************************
205 //**************************************************************************
206 #ifdef VAVOOM_DISABLE_STB_IMAGE_JPEG
207 # ifdef VAVOOM_USE_LIBJPG
208 # define VV_SWR_HAS_JPEG
211 # define VV_SWR_HAS_JPEG
214 #ifdef VV_SWR_HAS_JPEG
215 # define VV_SWR_CVAR_JPG "/jpg"
216 # define VV_SWR_CVAR_FMT "jpg"
218 # define VV_SWR_CVAR_JPG ""
219 # define VV_SWR_CVAR_FMT "png"
221 static VCvarS
screenshot_type("screenshot_type", VV_SWR_CVAR_FMT
, "Screenshot type (png" VV_SWR_CVAR_JPG
"/tga/pcx).", CVAR_Archive
|CVAR_NoShadow
);
223 extern void WriteTGA (VStr FileName
, void *data
, int width
, int height
, int bpp
, bool bot2top
);
224 extern void WritePCX (VStr FileName
, void *data
, int width
, int height
, int bpp
, bool bot2top
);
225 extern void WritePNG (VStr FileName
, const void *Data
, int Width
, int Height
, int Bpp
, bool Bot2top
);
226 #ifdef VV_SWR_HAS_JPEG
227 extern void WriteJPG (VStr FileName
, const void *Data
, int Width
, int Height
, int Bpp
, bool Bot2top
);
240 //==========================================================================
244 //==========================================================================
245 static int getSSTypeByExt (VStr ext
) noexcept
{
246 while (!ext
.isEmpty() && ext
[0] == '.') ext
.chopLeft(1);
248 ext
= screenshot_type
.asStr();
249 while (!ext
.isEmpty() && ext
[0] == '.') ext
.chopLeft(1);
252 #ifdef VV_SWR_HAS_JPEG
258 if (ext
.strEquCI("png")) return SS_PNG
;
259 if (ext
.strEquCI("tga")) return SS_TGA
;
260 if (ext
.strEquCI("pcx")) return SS_PCX
;
261 #ifdef VV_SWR_HAS_JPEG
262 if (ext
.strEquCI("jpg")) return SS_JPG
;
263 if (ext
.strEquCI("jpeg")) return SS_JPG
;
269 //==========================================================================
271 // Screenshot command
273 //==========================================================================
274 COMMAND(Screenshot
) {
283 if (Args
.length() >= 2) {
284 ssname
= Args
[1].FixFileSlashes();
285 if (ssname
.indexOf('/') >= 0) GCon
->Log(NAME_Error
, "screenshot file name cannot contain a path");
286 while (!ssname
.isEmpty() && ssname
.endsWith(".")) ssname
.chopRight(1);
292 if (!ssname
.isEmpty()) sst
= ssname
.ExtractFileExtension();
293 stype
= getSSTypeByExt(sst
);
295 if (stype
== SS_BAD
) { GCon
->Log(NAME_Error
, "Unknown screenshot type"); return; }
299 case SS_PNG
: sext
= "png"; break;
300 case SS_TGA
: sext
= "tga"; break;
301 case SS_PCX
: sext
= "pcx"; break;
302 case SS_JPG
: sext
= "jpg"; break;
303 default: GCon
->Log(NAME_Error
, "WTF?!"); return;
306 if (!ssname
.isEmpty()) {
307 VStr e
= ssname
.ExtractFileExtension();
308 if (e
== ".") ssname
+= sext
;
309 else if (e
.isEmpty()) { ssname
+= "."; ssname
+= sext
; }
312 // find a file name to save it to
313 VStr BaseDir
= FL_GetScreenshotsDir();
314 if (BaseDir
.isEmpty()) {
315 GCon
->Logf(NAME_Error
, "Invalid engine screenshot directory");
319 if (ssname
.isEmpty()) {
320 for (i
= 0; i
<= 9999; ++i
) {
321 snprintf(tmpbuf
, sizeof(tmpbuf
), "shot%04d.%s", i
, *sext
);
322 filename
= BaseDir
+"/"+tmpbuf
;
323 if (!Sys_FileExists(filename
)) break; // file doesn't exist
327 GCon
->Log(NAME_Error
, "Couldn't create a screenshot file");
331 filename
= BaseDir
.appendPath(ssname
);
334 // save screenshot file
335 data
= Drawer
->ReadScreen(&bpp
, &bot2top
);
338 // type is already lowercased
339 if (stype
== SS_PCX
) WritePCX(filename
, data
, ScreenWidth
, ScreenHeight
, bpp
, bot2top
);
340 else if (stype
== SS_TGA
) WriteTGA(filename
, data
, ScreenWidth
, ScreenHeight
, bpp
, bot2top
);
341 else if (stype
== SS_PNG
) WritePNG(filename
, data
, ScreenWidth
, ScreenHeight
, bpp
, bot2top
);
342 #ifdef VV_SWR_HAS_JPEG
343 else if (stype
== SS_JPG
) WriteJPG(filename
, data
, ScreenWidth
, ScreenHeight
, bpp
, bot2top
);
347 GCon
->Log(NAME_Error
, "Bad screenshot type");
348 #ifdef VV_SWR_HAS_JPEG
349 GCon
->Log(NAME_Error
, "Supported formats are pcx, tga, png, jpg");
351 GCon
->Log(NAME_Error
, "Supported formats are pcx, tga, png");
354 if (report
) GCon
->Logf("Saved screenshot to '%s'", *filename
);
357 GCon
->Log(NAME_Error
, "Not enough memory to take a screenshot");
362 //**************************************************************************
364 // Misc drawing stuff
366 //**************************************************************************
368 //==========================================================================
372 //==========================================================================
374 static RunningAverageExp vmAverage
;
375 static RunningAverageExp decalAverage
;
376 static bool stripeRendered
;
378 static void DrawDebugTimesStripe () {
379 if (stripeRendered
) return;
380 stripeRendered
= true;
381 const float stripeAlpha
= 0.666f
;
383 int sXPos
= VirtualWidth
;
384 int sYPos
= T_FontHeight();
385 GRoot
->ToDrawerCoords(sXPos
, sYPos
);
386 Drawer
->ShadeRect(0, 0, sXPos
, sYPos
, stripeAlpha
);
391 static void DrawFPS () {
393 const bool isClient
= (GGameInfo
->NetMode
== NM_Client
&& cl
&& cl
->Net
);
395 if (worldThinkTimeVM
< 0) vmAverage
.setValue(0);
396 else if (worldThinkTimeVM
>= 0) vmAverage
.update(worldThinkTimeVM
);
398 if (worldThinkTimeDecal
< 0) decalAverage
.setValue(0);
399 else if (worldThinkTimeDecal
>= 0) decalAverage
.update(worldThinkTimeDecal
);
402 stripeRendered
= false;
407 if ((dbg_world_think_vm_time && worldThinkTimeVM >= 0) || (dbg_world_think_decal_time && worldThinkTimeDecal >= 0)) {
408 DrawDebugTimesStripe();
410 T_SetAlign(hleft, vtop);
411 int xpos = VirtualWidth/2;
413 //ypos = GRoot->GetHeight()-64;
415 if (dbg_world_think_decal_time && dbg_world_think_vm_time) {
416 T_DrawText(xpos, ypos, va("VM:%d | DECALS:%d", (int)(vmAverage.getValue()*1000+0.5), (int)(decalAverage.getValue()*1000+0.5)), CR_DARKBROWN);
418 if (dbg_world_think_decal_time) T_DrawText(xpos, ypos, va("DECALS:%d", (int)(decalAverage.getValue()*1000+0.5)), CR_DARKBROWN);
419 if (dbg_world_think_vm_time) T_DrawText(xpos, ypos, va("VM:%d", (int)(vmAverage.getValue()*1000+0.5)), CR_DARKBROWN);
426 if (draw_gc_stats
> 0) {
427 const VObject::GCStats
&stats
= VObject::GetGCStats();
428 DrawDebugTimesStripe();
431 T_SetAlign(hcenter
, vtop
);
432 xpos
= VirtualWidth
/2;
434 if (Sys_Time()-stats
.lastCollectTime
> 1) VObject::ResetGCStatsLastCollected();
436 VStr ss
= va("[\034U%3d\034-/\034U%4d\034-/\034U%3d\034-]", stats
.lastCollected
, stats
.alive
, (int)(stats
.lastCollectDuration
*1000+0.5));
437 if ((dbg_world_think_vm_time
&& worldThinkTimeVM
>= 0) || (dbg_world_think_decal_time
&& worldThinkTimeDecal
>= 0)) {
438 if (dbg_world_think_decal_time
&& dbg_world_think_vm_time
) {
439 ss
+= va(" [VM:\034U%d\034- | DC:\034U%d\034-]", (int)(vmAverage
.getValue()*1000+0.5), (int)(decalAverage
.getValue()*1000+0.5));
441 if (dbg_world_think_decal_time
) ss
+= va(" [DC:\034U%d\034-]", (int)(decalAverage
.getValue()*1000+0.5));
442 if (dbg_world_think_vm_time
) ss
+= va(" [VM:\034U%d\034-]", (int)(vmAverage
.getValue()*1000+0.5));
446 T_DrawText(xpos
, ypos
, ss
, CR_DARKBROWN
);
448 T_DrawText(xpos, ypos, va("OBJ:[\034U%3d\034-/\034U%3d\034-] ARRAY:[\034U%5d\034-/\034U%5d\034-/\034U%d\034-]; \034U%2d\034- MSEC GC",
449 stats.lastCollected, stats.alive, stats.firstFree, stats.poolSize, stats.poolAllocated, (int)(stats.lastCollectDuration*1000+0.5)), CR_DARKBROWN);
452 //ypos += T_FontHeight();
454 T_SetAlign(hleft
, vtop
);
455 T_DrawText(7*T_TextWidth("W"), ypos
, va("[T\034U%5d\034-/S\034U%5d\034-|N\034U%4d\034-/F\034U%3d\034-]", dbgEntityTickTotal
, dbgEntityTickSimple
, dbgEntityTickNoTick
, dbgEntityTickTotal
-(dbgEntityTickSimple
+dbgEntityTickNoTick
)), CR_DARKBROWN
);
461 double time
= Sys_Time();
464 if (time
-fps_start
> 1.0) {
465 show_fps
= (int)(fps_frames
/(time
-fps_start
)+0.5);
466 if (draw_fps
== 2) ms
= 1000.0/fps_frames
/(time
-fps_start
);
471 T_SetFont(SmallFont
);
473 if (stripeRendered
) {
475 T_SetAlign(hleft
, vtop
);
477 } else if (draw_fps_posx
< 0) {
478 T_SetAlign(hleft
, vtop
);
480 } else if (draw_fps_posx
== 0) {
481 T_SetAlign(hcenter
, vtop
);
482 xpos
= VirtualWidth
/2;
484 T_SetAlign(hright
, vtop
);
485 xpos
= VirtualWidth
-2;
487 if (stripeRendered
) {
488 T_DrawText(xpos
, ypos
, va("FPS:%02d", show_fps
), CR_DARKBROWN
);
490 T_DrawText(xpos
, ypos
, va("%02d FPS", show_fps
), CR_DARKBROWN
);
493 if (!stripeRendered
&& draw_fps
== 2) {
494 T_SetAlign(hright
, vtop
);
495 T_DrawText(VirtualWidth
-2, ypos
, va("%.2f MSEC", ms
), CR_DARKBROWN
);
502 if (isClient
&& draw_lag
) {
504 T_SetAlign(hright
, vtop
);
505 int xpos
= GRoot
->GetWidth()-4;
506 int lypos
= GRoot
->GetHeight()-64;
508 const int nlag
= clampval(CL_GetNetLag(), 0, 999);
509 T_DrawText(xpos
, lypos
, va("(%d CHANS) LAG:%3d", CL_GetNumberOfChannels(), nlag
), (Host_IsDangerousTimeout() ? CR_RED
: CR_DARKBROWN
));
510 //lypos -= T_FontHeight();
513 const int ChartHeight
= 32;
518 GRoot
->ToDrawerCoords(sXPos
, sYPos
);
519 sXPos
-= NETLAG_CHART_ITEMS
;
521 Drawer
->ShadeRect(sXPos
, sYPos
, sXPos
+NETLAG_CHART_ITEMS
, sYPos
-ChartHeight
, 0.666f
);
522 Drawer
->StartAutomap(true); // as overlay
523 unsigned pos
= (NetLagChartPos
+1)%NETLAG_CHART_ITEMS
;
524 for (int xx
= 0; xx
< NETLAG_CHART_ITEMS
; ++xx
) {
525 //const int hgt = min2(500, NetLagChart[pos])*ChartHeight/500;
526 const int hgt
= NetLagChart
[pos
]*ChartHeight
/1000;
527 Drawer
->DrawLineAM(sXPos
+xx
, sYPos
, 0xff00cf00u
, sXPos
+xx
, sYPos
-hgt
-1, 0xff005f00u
);
528 pos
= (pos
+1)%NETLAG_CHART_ITEMS
;
530 Drawer
->EndAutomap();
536 //**************************************************************************
540 //**************************************************************************
542 //==========================================================================
546 //==========================================================================
547 static void ChangeResolution (int InWidth
, int InHeight
) {
549 int height
= InHeight
;
551 //if (screen_fsmode > 0) win = true;
553 if (Drawer
->RendLev
) Drawer
->RendLev
->UncacheLevel();
554 Drawer
->DeinitResolution();
556 // changing resolution
557 if (!Drawer
->SetResolution(width
, height
, screen_fsmode
)) {
558 GCon
->Logf("Failed to set resolution %dx%d", width
, height
);
559 if (RealScreenWidth
&& lastFSMode
>= 0) {
560 if (!Drawer
->SetResolution(RealScreenWidth
, RealScreenHeight
, lastFSMode
)) {
561 Sys_Error("ChangeResolution: failed to restore resolution");
563 GCon
->Log("Restoring previous resolution");
566 if (!Drawer
->SetResolution(0, 0, 0)) {
567 Sys_Error("ChangeResolution: Failed to set default resolution");
569 GCon
->Log("Setting default resolution");
574 lastFSMode
= screen_fsmode
;
576 //GCon->Logf("%dx%d.", ScreenWidth, ScreenHeight);
578 screen_width
= RealScreenWidth
;
579 screen_height
= RealScreenHeight
;
581 screen_width_internal
= ScreenWidth
;
582 screen_height_internal
= ScreenHeight
;
584 lastScrScale
= max2(1.0f
, screen_scale
.asFloat());
586 VirtualWidth
= ScreenWidth
;
587 VirtualHeight
= ScreenHeight
;
590 //GCon->Logf(NAME_Debug, "000: %dx%d", VirtualWidth, VirtualHeight);
591 while (VirtualWidth
/scale
>= 640 && VirtualHeight
/scale
>= 480) ++scale
;
592 if (scale
> 1) --scale
;
593 if (ui_max_scale
.asInt() > 0 && scale
> ui_max_scale
.asInt()) scale
= ui_max_scale
.asInt();
594 if (ui_min_scale
.asInt() > 0 && scale
< ui_min_scale
.asInt()) scale
= ui_min_scale
.asInt();
595 VirtualWidth
/= scale
;
596 VirtualHeight
/= scale
;
598 fScaleX
= (float)ScreenWidth
/(float)VirtualWidth
;
599 fScaleY
= (float)ScreenHeight
/(float)VirtualHeight
;
601 if (GRoot
) GRoot
->RefreshScale();
603 // don't forget to call `GRoot->RefreshScale()`!
604 //GCon->Logf("***SCALE0: %g, %g; scr:%dx%d; vscr:%dx%d", fScaleX, fScaleY, ScreenWidth, ScreenHeight, VirtualWidth, VirtualHeight);
605 // level precaching will be called by the caller
609 //==========================================================================
611 // CheckResolutionChange
613 //==========================================================================
614 static void CheckResolutionChange () {
615 bool res_changed
= false;
617 if (brightness
!= usegamma
) {
618 usegamma
= brightness
;
621 brightness
= usegamma
;
625 brightness
= usegamma
;
629 if (setresolutionneeded
) {
630 ChangeResolution(setwidth
, setheight
);
631 setresolutionneeded
= false;
633 } else if (!screen_width
|| screen_width
!= RealScreenWidth
|| screen_height
!= RealScreenHeight
|| lastScrScale
!= max2(1.0f
, screen_scale
.asFloat())) {
634 ChangeResolution(screen_width
, screen_height
);
639 Drawer
->InitResolution();
640 //R_OSDMsgReset(OSD_MapLoading);
641 if (Drawer
->RendLev
) Drawer
->RendLev
->PrecacheLevel();
642 if (GRoot
) GRoot
->RefreshScale();
643 // post "resolution changed" event
646 ev
.type
= ev_broadcast
;
647 ev
.data1
= ev_resolution
;
648 VObject::PostEvent(ev
);
649 // recalculate view size and other data
650 //R_SetViewSize(screenblocks);
651 if (Drawer
->RendLev
) R_ForceViewSizeUpdate();
652 //GCon->Logf(NAME_Debug, "RES: real=(%dx%d); scr=(%dx%d)", RealScreenWidth, RealScreenHeight, ScreenWidth, ScreenHeight);
657 //==========================================================================
661 //==========================================================================
662 COMMAND(SetResolution
) {
663 if (Args
.length() == 3) {
664 int w
= VStr::atoi(*Args
[1]);
665 int h
= VStr::atoi(*Args
[2]);
666 if (w
>= 320 && h
>= 200 && w
<= 8192 && h
<= 8192) {
669 setresolutionneeded
= true;
671 GCon
->Logf(NAME_Error
, "SetResolution: invalid resolution (%sx%s)", *Args
[1], *Args
[2]);
674 GCon
->Log("SetResolution <width> <height> -- change resolution");
679 //==========================================================================
681 // COMMAND vid_restart
683 //==========================================================================
684 COMMAND(vid_restart
) {
685 setwidth
= RealScreenWidth
;
686 setheight
= RealScreenHeight
;
687 setresolutionneeded
= true;
691 //**************************************************************************
693 // General (public) stuff
695 //**************************************************************************
697 //==========================================================================
701 //==========================================================================
706 //==========================================================================
708 // SCR_SignalWipeStart
710 //==========================================================================
711 void SCR_SignalWipeStart () {
712 if (!r_wipe_enabled
|| !GGameInfo
|| !GGameInfo
->IsWipeAllowed()) {
717 wipeStartedTime
= -1.0;
722 //==========================================================================
726 //==========================================================================
727 void SCR_Update (bool fullUpdate
) {
728 CheckResolutionChange();
730 if (Drawer
) Drawer
->IncUpdateFrame();
732 // disable tty logs for network games
733 if (GGameInfo
->NetMode
>= NM_DedicatedServer
) C_DisableTTYLogs(); else C_EnableTTYLogs();
735 if (!fullUpdate
) return;
737 if (clWipeTimer
>= 0.0f
&& wipeStartedTime
< 0.0) {
738 //GCon->Logf(NAME_Debug, "PrepareWipe(): clWipeTimer=%g; wipeStartedTime=%g; wipeStarted=%d", clWipeTimer, wipeStartedTime, (int)wipeStarted);
739 Drawer
->PrepareWipe();
740 wipeStartedTime
= Sys_Time();
743 Drawer
->ResetCrosshair();
745 bool updateStarted
= false;
746 bool allowClear
= true;
747 bool allowWipeStart
= true;
748 bool drawOther
= true;
750 //GCon->Logf(NAME_Debug, "cl=%p; signon=%d; MO=%p; ingame=%d (iphase:%d); clWipeTimer=%g; TicTime=%d; srft=%d", cl, cls.signon, (cl ? cl->MO : nullptr), CL_IsInGame(), CL_IntermissionPhase(), clWipeTimer, (GLevel ? GLevel->TicTime : -1), serverStartRenderFramesTic);
752 // if the map forced "map end" on the very first ticks...
753 if (GGameInfo
->NetMode
!= NM_Client
&& cl
&& cls
.signon
&& cl
->MO
&& CL_IntermissionPhase() && clWipeTimer
>= 0.0f
&& GLevel
&& GLevel
->TicTime
< serverStartRenderFramesTic
) {
754 //GCon->Logf(NAME_Debug, "*************************");
756 Drawer
->RenderWipe(-1.0f
);
760 const bool isBadApple
= (cl
&& cl
->MO
&& cl
->MO
->XLevel
->IsBadApple());
762 // do buffered drawing
763 if (cl
&& cls
.signon
&& cl
->MO
&& /*!GClGame->InIntermission()*/CL_IsInGame()) {
764 if (GGameInfo
->NetMode
== NM_Client
&& !cl
->Level
) {
766 allowWipeStart
= false;
768 } else if (!GLevel
|| GLevel
->TicTime
>= serverStartRenderFramesTic
) {
769 //k8: always render level, so automap will be updated in all cases
770 updateStarted
= true;
771 Drawer
->StartUpdate();
772 if (!CL_GotNetOrigin()) {
773 Drawer
->ClearScreen(VDrawer::CLEAR_ALL
);
774 } else if (am_always_update
|| clWipeTimer
>= 0.0f
|| AM_IsOverlay()) {
775 if (dbg_disable_world_render
) {
776 Drawer
->ClearScreen(VDrawer::CLEAR_ALL
);
778 //if (clWipeTimer >= 0.0f) GCon->Logf(NAME_Debug, "R_RenderPlayerView(): clWipeTimer=%g; wipeStartedTime=%g; wipeStarted=%d; Time=%g; TicTime=%d", clWipeTimer, wipeStartedTime, (int)wipeStarted, GLevel->Time, GLevel->TicTime);
779 R_RenderPlayerView();
781 if (!isBadApple
&& cl
&& cl
->MO
&& cl
->MO
== cl
->Camera
&& GGameInfo
->NetMode
!= NM_TitleMap
) {
782 Drawer
->WantCrosshair();
783 if (!crosshair_topmost
) Drawer
->DrawCrosshair();
787 Drawer
->ClearScreen(VDrawer::CLEAR_ALL
);
789 Drawer
->Setup2D(); // restore 2D projection
790 if (GGameInfo
->NetMode
!= NM_TitleMap
&& !isBadApple
) {
794 //if (cl && cl->MO && cl->MO == cl->Camera) AM_DrawAtWidget(GRoot, cl->MO->Origin.x, cl->MO->Origin.y, 2.0f, 0.0f, 1.0f);
797 //GCon->Logf("render: tic=%d; starttic=%d", GLevel->TicTime, serverStartRenderFramesTic);
798 //return; // skip all rendering
799 // k8: nope, we still need to render console
801 allowWipeStart
= false;
803 } else if (GGameInfo
->NetMode
== NM_Client
&& cl
&& cl
->Net
&& !cls
.signon
&& /*!GClGame->InIntermission()*/CL_IsInGame()) {
805 allowWipeStart
= false;
809 if (!updateStarted
) {
810 Drawer
->StartUpdate();
811 if (allowClear
) Drawer
->ClearScreen();
812 Drawer
->Setup2D(); // setup 2D projection
813 if (clWipeTimer
>= 0.0f
&& wipeStartedTime
> 0.0) Drawer
->RenderWipe(-1.0f
);
817 // draw user interface
818 GRoot
->DrawWidgets();
822 // various on-screen statistics
825 // so it will be always visible
826 Drawer
->DrawCrosshair();
828 if (clWipeTimer
>= 0.0f
&& (!GLevel
|| GLevel
->TicTime
>= serverStartRenderFramesTic
)) {
830 const double ctt
= Sys_Time();
831 if (allowWipeStart
) {
832 //GCon->Logf(NAME_Debug, "wiperender: clWipeTimer=%g; wipeStartedTime=%g; wipeStarted=%d", clWipeTimer, wipeStartedTime, (int)wipeStarted);
833 if (!wipeStarted
) { wipeStarted
= true; wipeStartedTime
= ctt
; }
834 clWipeTimer
= (float)(ctt
-wipeStartedTime
);
836 if (clWipeTimer
>= 0.0f
) {
837 if (!Drawer
->RenderWipe(clWipeTimer
)) {
839 if (isBadApple
) R_ResetAnimatedSurfaces();
843 Drawer
->RenderWipe(-1.0f
);
845 } else if (clWipeTimer
>= 0.0f
) {
846 Drawer
->RenderWipe(-1.0f
);
849 if ((wipeStarted
|| (!r_wipe_enabled
&& updateStarted
)) && (!GLevel
|| GLevel
->TicTime
>= serverStartRenderFramesTic
) && clWipeTimer
< 0.0f
) {
850 MN_CheckStartupWarning();
852 } else if (GGameInfo
->NetMode
== NM_Client
&& cl
&& cl
->Net
&& !cls
.signon
&& /*!GClGame->InIntermission()*/CL_IsInGame()) {
853 T_SetFont(SmallFont
);
854 T_SetAlign(hleft
, vtop
);
855 const int y
= 8+cls
.gotmap
*8;
856 // slightly off vcenter
857 switch (cls
.gotmap
) {
858 case 0: T_DrawText(4, y
, "getting network data (map)", CR_TAN
); break;
859 case 1: T_DrawText(4, y
, "getting network data (world)", CR_TAN
); break;
860 case 2: T_DrawText(4, y
, "getting network data (spawning)", CR_TAN
); break;
861 default: T_DrawText(4, y
, "getting network data (something)", CR_TAN
); break;
866 // draw touchscreen controls
870 // page flip or blit buffer
874 if (Drawer
->CurrentTime
-lastTexGCTime
>= 3.2) {
875 lastTexGCTime
= Sys_Time();
876 VTexture::GCStep(lastTexGCTime
);
881 //==========================================================================
885 //==========================================================================
886 static void DrawSomeIcon (VName icname
) {
887 if (icname
== NAME_None
) return;
888 if (!Drawer
|| !Drawer
->IsInited()) return;
889 int pt
= GTextureManager
.AddPatch(icname
, TEXTYPE_Pic
, true);
892 GTextureManager
.GetTextureInfo(pt
, &info
);
893 if (info
.width
< 1 || info
.height
< 1) return;
895 int xpos
= (int)((float)ScreenWidth
*260.0f
/320.0f
);
896 int ypos
= (int)((float)ScreenHeight
*68.0f
/200.0f
);
897 Drawer
->StartUpdate();
898 R_DrawPic(xpos
, ypos
, pt
);
901 const int oldVW
= VirtualWidth
;
902 const int oldVH
= VirtualHeight
;
903 VirtualWidth
= ScreenWidth
;
904 VirtualHeight
= ScreenHeight
;
905 fScaleX
= fScaleY
= 1.0f
;
906 if (GRoot
) GRoot
->RefreshScale();
907 Drawer
->StartUpdate();
908 R_DrawPic(VirtualWidth
-info
.width
, VirtualHeight
-info
.height
, pt
, 0.4f
);
910 VirtualWidth
= oldVW
;
911 VirtualHeight
= oldVH
;
912 fScaleX
= (float)ScreenWidth
/(float)VirtualWidth
;
913 fScaleY
= (float)ScreenHeight
/(float)VirtualHeight
;
914 if (GRoot
) GRoot
->RefreshScale();
919 //==========================================================================
923 //==========================================================================
924 void Draw_TeleportIcon () {
925 DrawSomeIcon(NAME_teleicon
);
929 //==========================================================================
933 //==========================================================================
934 void Draw_SaveIcon () {
935 DrawSomeIcon(NAME_saveicon
);
939 //==========================================================================
943 //==========================================================================
944 void Draw_LoadIcon () {
945 DrawSomeIcon(NAME_loadicon
);
949 //==========================================================================
951 // SCR_SetVirtualScreen
953 //==========================================================================
954 void SCR_SetVirtualScreen (int Width
, int Height
) {
955 VirtualWidth
= Width
;
956 VirtualHeight
= Height
;
957 fScaleX
= (float)ScreenWidth
/(float)VirtualWidth
;
958 fScaleY
= (float)ScreenHeight
/(float)VirtualHeight
;
959 if (GRoot
) GRoot
->RefreshScale();
960 //GCon->Logf("***SCALE1: %g, %g; scr:%dx%d; vscr:%dx%d", fScaleX, fScaleY, ScreenWidth, ScreenHeight, VirtualWidth, VirtualHeight);