Line data Source code
1 : /**
2 : * @file renderwindow.cpp
3 : * @brief screen rendering functionality
4 : *
5 : * screen rendering functions, such as background, progress bar
6 : * also handles stuff such as main menu rendering and other non-intensive rendering
7 : * as well as global rendering settings such as gamma
8 : */
9 : #include "SDL_ttf.h"
10 :
11 : #include "../libprimis-headers/cube.h"
12 : #include "../../shared/geomexts.h"
13 : #include "../../shared/glemu.h"
14 : #include "../../shared/glexts.h"
15 :
16 : #include "hud.h"
17 : #include "octarender.h"
18 : #include "rendergl.h"
19 : #include "renderlights.h"
20 : #include "rendermodel.h"
21 : #include "renderparticles.h"
22 : #include "rendertext.h"
23 : #include "renderttf.h"
24 : #include "renderva.h"
25 : #include "renderwindow.h"
26 : #include "shader.h"
27 : #include "shaderparam.h"
28 : #include "stain.h"
29 : #include "texture.h"
30 :
31 : #include "interface/console.h"
32 : #include "interface/control.h"
33 : #include "interface/input.h"
34 : #include "interface/menus.h"
35 :
36 : #include "world/octaedit.h"
37 :
38 0 : VARFN(screenw, scr_w, SCR_MINW, -1, SCR_MAXW, initwarning("screen resolution"));
39 0 : VARFN(screenh, scr_h, SCR_MINH, -1, SCR_MAXH, initwarning("screen resolution"));
40 :
41 : static VAR(menufps, 0, 60, 1000); //maximum framerate while in main menu
42 : static VARP(maxfps, 0, 240, 1000); //maximum framerate while world is being rendered
43 :
44 : VAR(desktopw, 1, 0, 0); //used in iengine.h
45 : VAR(desktoph, 1, 0, 0); //used in iengine.h
46 :
47 : int screenw = 0,
48 : screenh = 0;
49 :
50 : SDL_Window *screen = nullptr;
51 : static SDL_GLContext glcontext = nullptr;
52 :
53 : bool inbetweenframes = false, //used in iengine.h
54 : renderedframe = true; //used in iengine.h, set to true by main() call in game and false by setbackgroundinfo(), so true during most circumstances
55 :
56 : /**
57 : * @brief helper function for main menu rendering routines
58 : *
59 : * @return w and h if both are above 1024x768
60 : * @return w and h multiplied by the factor by which the smallest dimension is smaller than 1024x768
61 : */
62 0 : static void getbackgroundres(int &w, int &h)
63 : {
64 0 : float wk = 1,
65 0 : hk = 1;
66 0 : if(w < 1024)
67 : {
68 0 : wk = 1024.0f/w; //calculate w subsize factor (if greater than 1)
69 : }
70 0 : if(h < 768)
71 : {
72 0 : hk = 768.0f/h; //calculate h subsize factor (if greater than 1)
73 : }
74 0 : wk = hk = std::max(wk, hk); //pick the largest factor and multiply both by this
75 0 : w = static_cast<int>(std::ceil(w*wk));
76 0 : h = static_cast<int>(std::ceil(h*hk));
77 0 : }
78 :
79 : static std::string backgroundcaption = "",
80 : backgroundmapname = "",
81 : backgroundmapinfo = "";
82 : static const Texture *backgroundmapshot = nullptr;
83 :
84 0 : static void bgquad(float x, float y, float w, float h, float tx = 0, float ty = 0, float tw = 1, float th = 1)
85 : {
86 0 : gle::begin(GL_TRIANGLE_STRIP);
87 0 : gle::attribf(x, y); gle::attribf(tx, ty);
88 0 : gle::attribf(x+w, y); gle::attribf(tx + tw, ty);
89 0 : gle::attribf(x, y+h); gle::attribf(tx, ty + th);
90 0 : gle::attribf(x+w, y+h); gle::attribf(tx + tw, ty + th);
91 0 : gle::end();
92 0 : }
93 :
94 : // void renderbackgroundview(int, int, const char*, Texture*, const char*, const char*)
95 : // Background picture / text handler
96 :
97 : /*
98 : Notes:
99 : * Unsure what 'w' and 'h' refers to, maybe screen resolution?
100 : */
101 :
102 0 : static void renderbackgroundview(int win_w, int win_h, const char *caption, const Texture *mapshot, const char *mapname, const char *mapinfo)
103 : {
104 : static int lastupdate = -1,
105 : lastw = -1,
106 : lasth = -1;
107 : static float backgroundu = 0,
108 : backgroundv = 0;
109 0 : const bool needsRefresh =
110 0 : (renderedframe && !mainmenu && lastupdate != lastmillis)
111 0 : || lastw != win_w
112 0 : || lasth != win_h;
113 0 : if(needsRefresh)
114 : {
115 0 : lastupdate = lastmillis;
116 0 : lastw = win_w;
117 0 : lasth = win_h;
118 :
119 0 : backgroundu = randomfloat(1);
120 0 : backgroundv = randomfloat(1);
121 : }
122 0 : else if(lastupdate != lastmillis)
123 : {
124 0 : lastupdate = lastmillis;
125 : }
126 0 : hudmatrix.ortho(0, win_w, win_h, 0, -1, 1);
127 0 : resethudmatrix();
128 0 : resethudshader();
129 :
130 0 : gle::defvertex(2);
131 0 : gle::deftexcoord0();
132 0 : settexture("media/interface/background.png", 0); //main menu background
133 0 : const float bu = win_w*0.67f/256.0f,
134 0 : bv = win_h*0.67f/256.0f;
135 0 : bgquad(0, 0, win_w, win_h, backgroundu, backgroundv, bu, bv);
136 :
137 0 : glEnable(GL_BLEND);
138 0 : glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
139 0 : settexture("media/interface/shadow.png", 3); //peripheral shadow effect
140 0 : bgquad(0, 0, win_w, win_h);
141 0 : glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
142 : // Set position and size of logo
143 0 : const float logo_h = (1.f/3.f)*std::min(win_w, win_h),
144 0 : logo_w = logo_h*(2.f/1.f), // Aspect ratio of logo, defined here
145 0 : logo_x = 0.5f*(win_w - logo_w),
146 0 : logo_y = 0.5f*(win_h*0.5f - logo_h);
147 :
148 0 : settexture( (maxtexsize >= 1024 || maxtexsize == 0) && (hudw() > 1280 || hudh() > 800)
149 : ? "<premul>media/interface/logo_1024.png" //1024x wide logo
150 : : "<premul>media/interface/logo.png", //512x wide logo for small screens
151 : 3);
152 0 : bgquad(logo_x, logo_y, logo_w, logo_h);
153 :
154 0 : glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
155 :
156 0 : if(caption)
157 : {
158 0 : int tw = text_width(caption);
159 0 : const float tsz = 0.04f*std::min(win_w, win_h)/FONTH,
160 0 : tx = 0.5f*(win_w - tw*tsz),
161 0 : ty = win_h - 0.075f*1.5f*std::min(win_w, win_h) - FONTH*tsz;
162 0 : pushhudtranslate(tx, ty, tsz);
163 : //draw_text(caption, 0, 0);
164 0 : ttr.renderttf(caption, {0xFF, 0xFF, 0xFF, 0}, 0, 0);
165 0 : pophudmatrix();
166 : }
167 0 : if(mapshot || mapname)
168 : {
169 0 : float infowidth = 14*FONTH,
170 0 : sz = 0.35f*std::min(win_w, win_h),
171 0 : msz = (0.85f*std::min(win_w, win_h) - sz)/(infowidth + FONTH),
172 0 : x = 0.5f*win_w,
173 0 : y = logo_y+logo_h - sz/15,
174 0 : mx = 0,
175 0 : my = 0,
176 0 : mw = 0,
177 0 : mh = 0;
178 : // Prepare text area for map info
179 0 : if(mapinfo)
180 : {
181 0 : text_boundsf(mapinfo, mw, mh, infowidth);
182 0 : x -= 0.5f * mw * msz;
183 0 : if (mapshot && mapshot!=notexture)
184 : {
185 0 : x -= 0.5f*FONTH * msz;
186 0 : mx = sz + FONTH * msz;
187 : }
188 : }
189 : // Map shot was provided and isn't empty
190 0 : if(mapshot && mapshot!=notexture)
191 : {
192 0 : x -= 0.5f * sz;
193 0 : resethudshader();
194 0 : glBindTexture(GL_TEXTURE_2D, mapshot->id);
195 0 : bgquad(x, y, sz, sz);
196 : }
197 : // Map name was provided
198 0 : if(mapname)
199 : {
200 0 : float tw = text_widthf(mapname),
201 0 : tsz = sz/(8*FONTH),
202 0 : tx = std::max(0.5f * (mw*msz - tw * tsz), 0.0f);
203 0 : pushhudtranslate(x + mx + tx, y, tsz);
204 : //draw_text(mapname, 0, 0);
205 0 : ttr.fontsize(42);
206 0 : ttr.renderttf(mapname, {0xFF, 0xFF, 0xFF, 0}, 0, 0);
207 0 : pophudmatrix();
208 0 : my = 1.5f*FONTH*tsz;
209 : }
210 : // Map info was provided
211 0 : if(mapinfo)
212 : {
213 0 : pushhudtranslate(x + mx, y + my, msz);
214 : //draw_text(mapinfo, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, -1, infowidth);
215 0 : ttr.fontsize(42);
216 0 : ttr.renderttf(mapinfo, {0xFF, 0xFF, 0xFF, 0}, 0, 0);
217 0 : pophudmatrix();
218 : }
219 : }
220 0 : glDisable(GL_BLEND);
221 0 : }
222 :
223 0 : void swapbuffers(bool)
224 : {
225 0 : gle::disable();
226 0 : SDL_GL_SwapWindow(screen);
227 0 : }
228 :
229 0 : static void setbackgroundinfo(const char *caption = nullptr, const Texture *mapshot = nullptr, const char *mapname = nullptr, const char *mapinfo = nullptr)
230 : {
231 0 : renderedframe = false;
232 0 : backgroundcaption = std::string(caption ? caption : "");
233 0 : backgroundmapshot = mapshot;
234 0 : backgroundmapname = std::string(mapname ? mapname : "");
235 0 : std::string minfo = std::string(mapinfo ? mapinfo : "");
236 0 : if(minfo != backgroundmapinfo)
237 : {
238 0 : backgroundmapinfo = "";
239 0 : if(!minfo.empty())
240 : {
241 0 : backgroundmapinfo = std::string(mapinfo);
242 : }
243 : else
244 : {
245 0 : backgroundmapinfo = "";
246 : }
247 : }
248 0 : }
249 :
250 0 : void renderbackground(const char *caption, const Texture *mapshot, const char *mapname, const char *mapinfo, bool force)
251 : {
252 0 : if(!inbetweenframes && !force)
253 : {
254 0 : return;
255 : }
256 0 : int w = hudw(),
257 0 : h = hudh();
258 0 : if(forceaspect)
259 : {
260 0 : w = std::ceil(h*forceaspect);
261 : }
262 0 : getbackgroundres(w, h);
263 0 : gettextres(w, h);
264 0 : if(force)
265 : {
266 0 : renderbackgroundview(w, h, caption, mapshot, mapname, mapinfo);
267 0 : return;
268 : }
269 : //renders renderbackgroundview three times, with an identical call each time
270 0 : for(int i = 0; i < 3; ++i)
271 : {
272 0 : renderbackgroundview(w, h, caption, mapshot, mapname, mapinfo);
273 0 : swapbuffers(false);
274 : }
275 0 : setbackgroundinfo(caption, mapshot, mapname, mapinfo);
276 : }
277 :
278 0 : static void restorebackground(int w, int h, bool force = false)
279 : {
280 0 : if(renderedframe)
281 : {
282 0 : if(!force)
283 : {
284 0 : return;
285 : }
286 0 : setbackgroundinfo();
287 : }
288 0 : renderbackgroundview(w, h, backgroundcaption.c_str(), backgroundmapshot, backgroundmapname.c_str(), backgroundmapinfo.c_str());
289 : }
290 :
291 : float loadprogress = 0;
292 :
293 0 : static void renderprogressview(int w, int h, float bar, const char *text) // also used during loading
294 : {
295 0 : hudmatrix.ortho(0, w, h, 0, -1, 1);
296 0 : resethudmatrix();
297 0 : resethudshader();
298 :
299 0 : gle::defvertex(2);
300 0 : gle::deftexcoord0();
301 :
302 0 : const float fh = 0.060f*std::min(w, h),
303 0 : fw = fh * 15,
304 0 : fx = renderedframe ? w - fw - fh/4 : 0.5f * (w - fw),
305 0 : fy = renderedframe ? fh/4 : h - fh * 1.5f;
306 0 : settexture("media/interface/loading_frame.png", 3);
307 0 : bgquad(fx, fy, fw, fh);
308 :
309 0 : glEnable(GL_BLEND);
310 0 : glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
311 :
312 0 : const float bw = fw * (512 - 2*8)/512.0f,
313 0 : bh = fh * 20/32.0f,
314 0 : bx = fx + fw * 8/512.0f,
315 0 : by = fy + fh * 6/32.0f,
316 0 : su1 = 0/32.0f,
317 0 : su2 = 8/32.0f,
318 0 : sw = fw * 8/512.0f,
319 0 : eu1 = 24/32.0f,
320 0 : eu2 = 32/32.0f,
321 0 : ew = fw * 8/512.0f,
322 0 : mw = bw - sw - ew,
323 0 : ex = bx+sw + std::max(mw*bar, fw * 8/512.0f);
324 0 : if(bar > 0)
325 : {
326 0 : settexture("media/interface/loading_bar.png", 3);
327 0 : bgquad(bx, by, sw, bh, su1, 0, su2-su1, 1);
328 0 : bgquad(bx+sw, by, ex-(bx+sw), bh, su2, 0, eu1-su2, 1);
329 0 : bgquad(ex, by, ew, bh, eu1, 0, eu2-eu1, 1);
330 : }
331 0 : if(text)
332 : {
333 0 : const int tw = text_width(text);
334 0 : float tsz = bh * 0.6f/FONTH;
335 0 : if(tw * tsz > mw)
336 : {
337 0 : tsz = mw/tw;
338 : }
339 0 : pushhudtranslate(bx+sw, by + (bh - FONTH*tsz)/2, tsz);
340 : //draw_text(text, 0, 0);
341 0 : ttr.fontsize(50);
342 0 : ttr.renderttf(text, {0xFF, 0xFF, 0xFF, 0}, 0, 4);
343 0 : pophudmatrix();
344 : }
345 0 : glDisable(GL_BLEND);
346 0 : }
347 :
348 : static VAR(progressbackground, 0, 0, 1); //force rendering progress bar background texture
349 : static int curvsync = -1;
350 :
351 6 : void renderprogress(float bar, const char *text, bool background) // also used during loading
352 : {
353 6 : if(!inbetweenframes || drawtex)
354 : {
355 6 : return;
356 : }
357 0 : int fps = menufps ? (maxfps ? std::min(maxfps, menufps) : menufps) : maxfps;
358 0 : if(fps)
359 : {
360 : static int lastprogress = 0;
361 0 : int ticks = SDL_GetTicks(),
362 0 : diff = ticks - lastprogress;
363 0 : if(bar > 0 && diff >= 0 && diff < (1000 + fps-1)/fps)
364 : {
365 0 : return;
366 : }
367 0 : lastprogress = ticks;
368 : }
369 0 : int w = hudw(),
370 0 : h = hudh();
371 0 : if(forceaspect)
372 : {
373 0 : w = static_cast<int>(std::ceil(h*forceaspect));
374 : }
375 0 : getbackgroundres(w, h);
376 0 : gettextres(w, h);
377 :
378 0 : bool forcebackground = progressbackground || (mesa_swap_bug && (curvsync || totalmillis==1));
379 0 : if(background || forcebackground)
380 : {
381 0 : restorebackground(w, h, forcebackground);
382 : }
383 0 : renderprogressview(w, h, bar, text);
384 0 : swapbuffers(false);
385 : }
386 :
387 : static bool initwindowpos = false;
388 :
389 0 : static void setfullscreen(bool enable)
390 : {
391 0 : if(!screen)
392 : {
393 0 : return;
394 : }
395 : //initwarning(enable ? "fullscreen" : "windowed");
396 0 : SDL_SetWindowFullscreen(screen, enable ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
397 0 : if(!enable)
398 : {
399 0 : SDL_SetWindowSize(screen, scr_w, scr_h);
400 0 : if(initwindowpos)
401 : {
402 0 : int winx = SDL_WINDOWPOS_CENTERED,
403 0 : winy = SDL_WINDOWPOS_CENTERED;
404 0 : SDL_SetWindowPosition(screen, winx, winy);
405 0 : initwindowpos = false;
406 : }
407 : }
408 : }
409 :
410 : //used in iengine only
411 0 : VARF(fullscreen, 0, 1, 1, setfullscreen(fullscreen!=0));
412 :
413 : /**
414 : * @brief Sets the screen resolution
415 : *
416 : * Sets the window size to w * h pixels, or reduces fullscreen resolution to
417 : * w * h pixels.
418 : *
419 : * @param w width of new screen res
420 : * @param h height of new screen res
421 : */
422 1 : void screenres(int w, int h)
423 : {
424 : //need to cast enum to int for std's clamp implementation
425 1 : scr_w = std::clamp(w, static_cast<int>(SCR_MINW), static_cast<int>(SCR_MAXW));
426 1 : scr_h = std::clamp(h, static_cast<int>(SCR_MINH), static_cast<int>(SCR_MAXH));
427 1 : if(screen)
428 : {
429 0 : scr_w = std::min(scr_w, desktopw);
430 0 : scr_h = std::min(scr_h, desktoph);
431 0 : if(SDL_GetWindowFlags(screen) & SDL_WINDOW_FULLSCREEN)
432 : {
433 0 : gl_resize();
434 : }
435 : else
436 : {
437 0 : SDL_SetWindowSize(screen, scr_w, scr_h);
438 : }
439 : }
440 : else
441 : {
442 1 : initwarning("screen resolution");
443 : }
444 1 : }
445 :
446 :
447 0 : static void setgamma(int val)
448 : {
449 0 : if(screen && SDL_SetWindowBrightness(screen, val/100.0f) < 0)
450 : {
451 0 : conoutf(Console_Error, "Could not set gamma: %s", SDL_GetError());
452 : }
453 0 : }
454 :
455 : static int curgamma = 100;
456 0 : static VARFNP(gamma, reqgamma, 30, 100, 300,
457 : {
458 : if(initing || reqgamma == curgamma)
459 : {
460 : return;
461 : }
462 : curgamma = reqgamma;
463 : setgamma(curgamma);
464 : });
465 :
466 : /**
467 : * @brief Sets gamma to the previous set value.
468 : *
469 : * Useful for reverting bad-ooking gamma trial settings.
470 : *
471 : * used in iengine.h
472 : */
473 0 : void restoregamma()
474 : {
475 0 : if(initing || reqgamma == 100)
476 : {
477 0 : return;
478 : }
479 0 : curgamma = reqgamma;
480 0 : setgamma(curgamma);
481 : }
482 :
483 0 : void cleargamma()
484 : {
485 0 : if(curgamma != 100 && screen)
486 : {
487 0 : SDL_SetWindowBrightness(screen, 1.0f);
488 : }
489 0 : }
490 :
491 : void restorevsync(); //prototype to fix chicken-egg initialization problem caused by VARFP
492 :
493 0 : static VARFP(vsync, 0, 0, 1, restorevsync()); //vertical sync of framebuffer to refresh rate
494 0 : static VARFP(vsynctear, 0, 0, 1, { if(vsync) restorevsync(); }); //toggles sdl2's adaptive sync function
495 :
496 0 : void restorevsync()
497 : {
498 0 : if(initing || !glcontext)
499 : {
500 0 : return;
501 : }
502 0 : if(!SDL_GL_SetSwapInterval(vsync ? (vsynctear ? -1 : 1) : 0))
503 : {
504 0 : curvsync = vsync;
505 : }
506 : }
507 :
508 : //used in iengine.h
509 0 : void setupscreen()
510 : {
511 : //clear prior gl context/screen if present
512 0 : if(glcontext)
513 : {
514 0 : SDL_GL_DeleteContext(glcontext);
515 0 : glcontext = nullptr;
516 : }
517 0 : if(screen)
518 : {
519 0 : SDL_DestroyWindow(screen);
520 0 : screen = nullptr;
521 : }
522 0 : curvsync = -1;
523 :
524 : SDL_Rect desktop;
525 0 : if(SDL_GetDisplayBounds(0, &desktop) < 0)
526 : {
527 0 : fatal("failed querying desktop bounds: %s", SDL_GetError());
528 : }
529 0 : desktopw = desktop.w;
530 0 : desktoph = desktop.h;
531 :
532 0 : if(scr_h < 0)
533 : {
534 0 : scr_h = SCR_DEFAULTH;
535 : }
536 0 : if(scr_w < 0)
537 : {
538 0 : scr_w = (scr_h*desktopw)/desktoph;
539 : }
540 0 : scr_w = std::min(scr_w, desktopw);
541 0 : scr_h = std::min(scr_h, desktoph);
542 :
543 0 : int winx = SDL_WINDOWPOS_UNDEFINED,
544 0 : winy = SDL_WINDOWPOS_UNDEFINED,
545 0 : winw = scr_w,
546 0 : winh = scr_h,
547 0 : flags = SDL_WINDOW_RESIZABLE;
548 0 : if(fullscreen)
549 : {
550 0 : winw = desktopw;
551 0 : winh = desktoph;
552 0 : flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
553 0 : initwindowpos = true;
554 : }
555 :
556 0 : SDL_GL_ResetAttributes();
557 0 : SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
558 0 : SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0);
559 0 : SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0);
560 :
561 0 : uint32_t windowflags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_INPUT_FOCUS | SDL_WINDOW_MOUSE_FOCUS | flags;
562 : //create new screen title x y w h flags
563 0 : screen = SDL_CreateWindow("Imprimis", winx, winy, winw, winh, windowflags);
564 0 : ttr.initttf();
565 0 : ttr.openfont("media/interface/font/default.ttf", 24);
566 :
567 0 : if(!screen)
568 : {
569 0 : fatal("failed to create OpenGL window: %s", SDL_GetError());
570 : }
571 0 : SDL_Surface *icon = loadsurface("media/interface/icon.png"); //path to taskbar icon
572 0 : if(icon)
573 : {
574 0 : SDL_SetWindowIcon(screen, icon);
575 0 : SDL_FreeSurface(icon); //don't need it any more
576 : }
577 :
578 0 : SDL_SetWindowMinimumSize(screen, SCR_MINW, SCR_MINH);
579 0 : SDL_SetWindowMaximumSize(screen, SCR_MAXW, SCR_MAXH);
580 : //set opengl version to 4.0
581 0 : SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
582 0 : SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
583 : //set core profile
584 0 : SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
585 0 : glcontext = SDL_GL_CreateContext(screen);
586 :
587 : //
588 0 : GLenum err = glewInit();
589 0 : if (GLEW_OK != err)
590 : {
591 : /* Problem: glewInit failed, something is seriously wrong. */
592 0 : logoutf("Error: %s", glewGetErrorString(err));
593 : }
594 0 : logoutf("init: GLEW %s", glewGetString(GLEW_VERSION));
595 : //check if OpenGL context is sane
596 0 : if(!glcontext)
597 : {
598 0 : fatal("failed to create OpenGL context: %s", SDL_GetError());
599 : }
600 0 : SDL_GetWindowSize(screen, &screenw, &screenh);
601 0 : }
602 :
603 : //full reset of renderer
604 2 : void resetgl()
605 : {
606 2 : if(!glslversion)
607 : {
608 2 : conoutf(Console_Error, "Cannot reset GL without GL initialized, operation not performed");
609 2 : return;
610 : }
611 0 : clearchanges(Change_Graphics|Change_Shaders);
612 :
613 0 : renderbackground("resetting OpenGL");
614 :
615 0 : rootworld.cleanupva();
616 0 : cleanupparticles();
617 0 : cleanupstains();
618 0 : cleanupmodels();
619 0 : cleanupprefabs();
620 0 : cleanuptextures();
621 0 : cleanuplights();
622 0 : cleanupshaders();
623 0 : cleanupgl();
624 :
625 0 : setupscreen();
626 :
627 0 : inputgrab(grabinput);
628 :
629 0 : gl_init();
630 :
631 0 : inbetweenframes = false;
632 : //texture reloading
633 0 : if(!notexture->reload() ||
634 0 : !reloadtexture("<premul>media/interface/logo.png") ||
635 0 : !reloadtexture("<premul>media/interface/logo_1024.png") ||
636 0 : !reloadtexture("media/interface/background.png") ||
637 0 : !reloadtexture("media/interface/shadow.png") ||
638 0 : !reloadtexture("media/interface/mapshot_frame.png") ||
639 0 : !reloadtexture("media/interface/loading_frame.png") ||
640 0 : !reloadtexture("media/interface/loading_bar.png"))
641 : {
642 0 : fatal("failed to reload core texture");
643 : }
644 0 : reloadfonts();
645 0 : inbetweenframes = true;
646 0 : renderbackground("initializing...");
647 0 : restoregamma();
648 0 : restorevsync();
649 0 : initgbuffer();
650 0 : reloadshaders();
651 0 : reloadtextures();
652 0 : rootworld.allchanged(true);
653 : }
654 :
655 : /**
656 : * @brief Delays the frame to the prescribed rate.
657 : *
658 : * Uses SDL_Delay to delay a frame, given the time the last frame was
659 : * rendered and the current time.
660 : *
661 : * @param millis the time (in ms) since program started
662 : * @param curmillis the last registered frame time
663 : */
664 0 : void limitfps(int &millis, int curmillis)
665 : {
666 0 : int limit = (mainmenu || minimized) && menufps ? (maxfps ? std::min(maxfps, menufps) : menufps) : maxfps;
667 0 : if(!limit)
668 : {
669 0 : return;
670 : }
671 : static int fpserror = 0;
672 0 : int delay = 1000/limit - (millis-curmillis);
673 0 : if(delay < 0)
674 : {
675 0 : fpserror = 0;
676 : }
677 : else
678 : {
679 0 : fpserror += 1000%limit;
680 0 : if(fpserror >= limit)
681 : {
682 0 : ++delay;
683 0 : fpserror -= limit;
684 : }
685 0 : if(delay > 0)
686 : {
687 0 : SDL_Delay(delay);
688 0 : millis += delay;
689 : }
690 : }
691 : }
692 :
693 : #ifdef WIN32
694 : // Force Optimus setups to use the NVIDIA GPU
695 : // or also for AMD dual graphics
696 : extern "C"
697 : {
698 : #ifdef __GNUC__
699 : __attribute__((dllexport))
700 : #else
701 : __declspec(dllexport)
702 : #endif
703 : DWORD NvOptimusEnablement = 1;
704 :
705 : #ifdef __GNUC__
706 : __attribute__((dllexport))
707 : #else
708 : __declspec(dllexport)
709 : #endif
710 : DWORD AmdPowerXpressRequestHighPerformance = 1;
711 : }
712 : #endif
713 :
714 : static constexpr int maxfpshistory = 60;
715 :
716 : static int fpspos = 0;
717 : static std::array<int, maxfpshistory> fpshistory;
718 :
719 : //used only in iengine
720 0 : void resetfpshistory()
721 : {
722 0 : fpshistory.fill(1);
723 0 : fpspos = 0;
724 0 : }
725 :
726 : //used only in iengine
727 0 : void updatefpshistory(int millis)
728 : {
729 0 : fpshistory[fpspos++] = std::max(1, std::min(1000, millis));
730 0 : if(fpspos>=maxfpshistory)
731 : {
732 0 : fpspos = 0;
733 : }
734 0 : }
735 :
736 1 : void getfps(int &fps, int &bestdiff, int &worstdiff)
737 : {
738 1 : int total = fpshistory.at(maxfpshistory-1),
739 1 : best = total,
740 1 : worst = total;
741 61 : for(const int &millis : fpshistory)
742 : {
743 60 : total += millis;
744 60 : if(millis < best)
745 : {
746 0 : best = millis;
747 : }
748 60 : if(millis > worst)
749 : {
750 0 : worst = millis;
751 : }
752 : }
753 1 : if(total) //guard against div by 0
754 : {
755 0 : fps = (1000*maxfpshistory)/total;
756 0 : bestdiff = 1000/best-fps;
757 0 : worstdiff = fps-1000/worst;
758 : }
759 : else
760 : {
761 1 : fps = 0;
762 1 : bestdiff = 0;
763 1 : worstdiff = 0;
764 : }
765 1 : }
766 :
767 1 : static void getfpscmd(const int *raw)
768 : {
769 1 : if(*raw)
770 : {
771 0 : floatret(1000.0f/fpshistory[(fpspos+maxfpshistory-1)%maxfpshistory]);
772 : }
773 : else
774 : {
775 : int fps, bestdiff, worstdiff;
776 1 : getfps(fps, bestdiff, worstdiff);
777 1 : intret(fps);
778 : }
779 1 : }
780 :
781 1 : void initrenderwindowcmds()
782 : {
783 1 : addcommand("getfps", reinterpret_cast<identfun>(getfpscmd), "i", Id_Command);
784 1 : addcommand("resetgl", reinterpret_cast<identfun>(resetgl), "", Id_Command);
785 1 : addcommand("screenres", reinterpret_cast<identfun>(screenres), "ii", Id_Command);
786 1 : }
|