diff --git a/res/layouts/pages/settings_display.xml.lua b/res/layouts/pages/settings_display.xml.lua index 6e64a7d6..18d45237 100644 --- a/res/layouts/pages/settings_display.xml.lua +++ b/res/layouts/pages/settings_display.xml.lua @@ -1,3 +1,14 @@ +local tostring_overrides = {} +tostring_overrides["display.framerate"] = function(x) + if x == -1 then + return "V-Sync" + elseif x == 0 then + return "Unlimited" + else + return tostring(x) + end +end + function create_setting(id, name, step, postfix, tooltip, changeonrelease) local info = core.get_setting_info(id) postfix = postfix or "" @@ -18,11 +29,18 @@ function create_setting(id, name, step, postfix, tooltip, changeonrelease) end function update_setting(x, id, name, postfix) + local str + local func = tostring_overrides[id] + if func then + str = func(x) + else + str = core.str_setting(id) + end -- updating label document[id..".L"].text = string.format( "%s: %s%s", gui.str(name, "settings"), - core.str_setting(id), + str, postfix ) end @@ -37,8 +55,8 @@ end function on_open() create_setting("camera.fov", "FOV", 1, "°") + create_setting("display.framerate", "Framerate", 1, "", "", true) create_checkbox("display.fullscreen", "Fullscreen") - create_checkbox("display.vsync", "V-Sync") create_checkbox("camera.shaking", "Camera Shaking") create_checkbox("camera.inertia", "Camera Inertia") end diff --git a/res/texts/ru_RU.txt b/res/texts/ru_RU.txt index 905a1fd3..d8734890 100644 --- a/res/texts/ru_RU.txt +++ b/res/texts/ru_RU.txt @@ -55,6 +55,7 @@ settings.Camera Inertia=Инерция Камеры settings.Fog Curve=Кривая Тумана settings.FOV=Поле Зрения settings.Fullscreen=Полный экран +settings.Framerate=Частота кадров settings.Gamma=Гамма settings.Language=Язык settings.Load Distance=Дистанция Загрузки diff --git a/res/textures/misc/icon.png b/res/textures/misc/icon.png new file mode 100644 index 00000000..ab91077d Binary files /dev/null and b/res/textures/misc/icon.png differ diff --git a/src/engine.cpp b/src/engine.cpp index 2c68370a..c9da3e3d 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -64,6 +64,18 @@ inline void create_channel(Engine* engine, std::string name, NumberSetting& sett }, true)); } +static std::unique_ptr load_icon(const fs::path& resdir) { + try { + auto file = resdir / fs::u8path("textures/misc/icon.png"); + if (fs::exists(file)) { + return imageio::read(file.u8string()); + } + } catch (const std::exception& err) { + logger.error() << "could not load window icon: " << err.what(); + } + return nullptr; +} + Engine::Engine(EngineSettings& settings, SettingsHandler& settingsHandler, EnginePaths* paths) : settings(settings), settingsHandler(settingsHandler), paths(paths), interpreter(std::make_unique()) @@ -71,10 +83,16 @@ Engine::Engine(EngineSettings& settings, SettingsHandler& settingsHandler, Engin paths->prepare(); loadSettings(); + auto resdir = paths->getResources(); + controller = std::make_unique(this); if (Window::initialize(&this->settings.display)){ throw initialize_error("could not initialize window"); } + if (auto icon = load_icon(resdir)) { + icon->flipY(); + Window::setIcon(icon.get()); + } loadControls(); audio::initialize(settings.audio.enabled.get()); create_channel(this, "master", settings.audio.volumeMaster); @@ -99,8 +117,6 @@ Engine::Engine(EngineSettings& settings, SettingsHandler& settingsHandler, Engin addWorldGenerators(); scripting::initialize(this); - - auto resdir = paths->getResources(); basePacks = files::read_list(resdir/fs::path("config/builtins.list")); } @@ -171,7 +187,8 @@ void Engine::mainloop() { if (!Window::isIconified()) { renderFrame(batch); } - Window::swapInterval(Window::isIconified() ? 1 : settings.display.vsync.get()); + Window::setFramerate(!Window::isIconified() ? 20 : + settings.display.framerate.get()); processPostRunnables(); diff --git a/src/files/settings_io.cpp b/src/files/settings_io.cpp index 28c62acf..f09ee1f2 100644 --- a/src/files/settings_io.cpp +++ b/src/files/settings_io.cpp @@ -48,7 +48,7 @@ SettingsHandler::SettingsHandler(EngineSettings& settings) { builder.add("width", &settings.display.width); builder.add("height", &settings.display.height); builder.add("samples", &settings.display.samples); - builder.add("vsync", &settings.display.vsync); + builder.add("framerate", &settings.display.framerate); builder.add("fullscreen", &settings.display.fullscreen); builder.section("camera"); diff --git a/src/settings.hpp b/src/settings.hpp index 8a8ad0cb..ccc3f906 100644 --- a/src/settings.hpp +++ b/src/settings.hpp @@ -28,8 +28,8 @@ struct DisplaySettings { IntegerSetting height {720}; /// @brief Anti-aliasing samples IntegerSetting samples {0}; - /// @brief VSync on - FlagSetting vsync {true}; + /// @brief Framerate limit + IntegerSetting framerate {-1, -1, 120}; }; struct ChunksSettings { diff --git a/src/window/Window.cpp b/src/window/Window.cpp index 9e0b1bad..bb266afe 100644 --- a/src/window/Window.cpp +++ b/src/window/Window.cpp @@ -9,6 +9,8 @@ #include #include +#include +#include static debug::Logger logger("window"); @@ -20,6 +22,8 @@ uint Window::width = 0; uint Window::height = 0; int Window::posX = 0; int Window::posY = 0; +int Window::framerate = -1; +double Window::prevSwap = 0.0; bool Window::fullscreen = false; static util::ObjectsKeeper observers_keeper; @@ -58,8 +62,7 @@ bool Window::isIconified() { return glfwGetWindowAttrib(window, GLFW_ICONIFIED); } -bool Window::isFocused() -{ +bool Window::isFocused() { return glfwGetWindowAttrib(window, GLFW_FOCUSED); } @@ -182,7 +185,8 @@ int Window::initialize(DisplaySettings* settings){ } }, true)); - glfwSwapInterval(settings->vsync.get()); + glfwSwapInterval(1); + setFramerate(settings->framerate.get()); const GLubyte* vendor = glGetString(GL_VENDOR); const GLubyte* renderer = glGetString(GL_RENDERER); logger.info() << "GL Vendor: " << (char*)vendor; @@ -281,8 +285,11 @@ void Window::setShouldClose(bool flag){ glfwSetWindowShouldClose(window, flag); } -void Window::swapInterval(int interval){ - glfwSwapInterval(interval); +void Window::setFramerate(int framerate) { + if ((framerate != -1) != (Window::framerate != -1)) { + glfwSwapInterval(framerate == -1); + } + Window::framerate = framerate; } void Window::toggleFullscreen(){ @@ -315,9 +322,15 @@ bool Window::isFullscreen() { return fullscreen; } -void Window::swapBuffers(){ +void Window::swapBuffers() { glfwSwapBuffers(window); Window::resetScissor(); + double currentTime = time(); + if (framerate > 0 && currentTime - prevSwap < (1.0 / framerate)) { + std::this_thread::sleep_for(std::chrono::milliseconds( + static_cast((1.0/framerate - (currentTime-prevSwap))*1000))); + } + prevSwap = time(); } double Window::time() { @@ -362,3 +375,12 @@ bool Window::tryToMaximize(GLFWwindow* window, GLFWmonitor* monitor) { workArea.y + (workArea.w - Window::height) / 2 + windowFrame.y / 2); return false; } + +void Window::setIcon(const ImageData* image) { + GLFWimage icon { + static_cast(image->getWidth()), + static_cast(image->getHeight()), + image->getData() + }; + glfwSetWindowIcon(window, 1, &icon); +} diff --git a/src/window/Window.hpp b/src/window/Window.hpp index b301f6ad..44e6e7b6 100644 --- a/src/window/Window.hpp +++ b/src/window/Window.hpp @@ -19,6 +19,8 @@ class Window { static std::stack scissorStack; static glm::vec4 scissorArea; static bool fullscreen; + static int framerate; + static double prevSwap; static bool tryToMaximize(GLFWwindow* window, GLFWmonitor* monitor); public: @@ -34,7 +36,7 @@ public: static bool isShouldClose(); static void setShouldClose(bool flag); static void swapBuffers(); - static void swapInterval(int interval); + static void setFramerate(int interval); static void toggleFullscreen(); static bool isFullscreen(); static bool isMaximized(); @@ -53,6 +55,7 @@ public: static const char* getClipboardText(); static void setClipboardText(const char* text); static DisplaySettings* getSettings(); + static void setIcon(const ImageData* image); static glm::vec2 size() { return glm::vec2(width, height);