From 5e2175302be21f3fe5426277695eb708a1e05559 Mon Sep 17 00:00:00 2001 From: jakubg1 <24206305+jakubg1@users.noreply.github.com> Date: Wed, 10 Sep 2025 01:53:22 +0200 Subject: [PATCH] Loading screen overhaul POC The loading screen has been significantly changed: - The background image is now properly picked randomly instead of taking the `textures/logo.bmp` file, prepended by an external program such as Rainsted/Starter. - The progress bar has been moved from a floating window to a thin green line at the bottom of the loading screen. - The log window is now hidden by default. It can be turned on by setting `loadinglog` to `true` in `eu07.ini`. - There is an animated wheel icon on the loading screen, and a trivia text is displayed. New translation key: - `"Did you know"` TODO: - Load trivia from a separate file. --- CMakeLists.txt | 1 + Globals.h | 2 +- launcher/launchermode.cpp | 2 - launcher/launchermode.h | 12 ++-- launcher/launcheruilayer.h | 2 +- scenarioloadermode.cpp | 21 ++----- scenarioloaderuilayer.cpp | 109 +++++++++++++++++++++++++++++++++++++ scenarioloaderuilayer.h | 15 +++-- uilayer.cpp | 54 ++++++------------ uilayer.h | 23 ++++---- 10 files changed, 160 insertions(+), 81 deletions(-) create mode 100644 scenarioloaderuilayer.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e87ed77f..6069c92f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -136,6 +136,7 @@ set(SOURCES "editoruilayer.cpp" "editoruipanels.cpp" "scenarioloadermode.cpp" +"scenarioloaderuilayer.cpp" "scenenodegroups.cpp" "simulationenvironment.cpp" "simulationstateserializer.cpp" diff --git a/Globals.h b/Globals.h index b9e4e964..39c0e653 100644 --- a/Globals.h +++ b/Globals.h @@ -233,7 +233,7 @@ struct global_settings { std::string asVersion; // z opisem motiontelemetry::conf_t motiontelemetry_conf; std::string screenshot_dir; - bool loading_log = true; + bool loading_log = false; bool dds_upper_origin = false; bool captureonstart = true; bool render_cab = true; diff --git a/launcher/launchermode.cpp b/launcher/launchermode.cpp index 9bf5b6a2..3e5df2a9 100644 --- a/launcher/launchermode.cpp +++ b/launcher/launchermode.cpp @@ -23,9 +23,7 @@ bool launcher_mode::update() void launcher_mode::enter() { Application.set_cursor( GLFW_CURSOR_NORMAL ); - simulation::is_ready = false; - Application.set_title(Global.AppName); } diff --git a/launcher/launchermode.h b/launcher/launchermode.h index 35b45361..7cd8d0e9 100644 --- a/launcher/launchermode.h +++ b/launcher/launchermode.h @@ -18,17 +18,13 @@ public: launcher_mode(); // methods // initializes internal data structures of the mode. returns: true on success, false otherwise - bool - init() override; + bool init() override; // mode-specific update of simulation data. returns: false on error, true otherwise - bool - update() override; + bool update() override; // maintenance method, called when the mode is activated - void - enter() override; + void enter() override; // maintenance method, called when the mode is deactivated - void - exit() override; + void exit() override; // input handlers void on_key( int Key, int Scancode, int Action, int Mods ) override; void on_cursor_pos( double const Horizontal, double const Vertical ) override { ; } diff --git a/launcher/launcheruilayer.h b/launcher/launcheruilayer.h index 8b4181e8..691802c1 100644 --- a/launcher/launcheruilayer.h +++ b/launcher/launcheruilayer.h @@ -19,7 +19,7 @@ http://mozilla.org/MPL/2.0/. class launcher_ui : public ui_layer { public: launcher_ui(); - bool on_key(const int Key, const int Action) override; + bool on_key(int Key, int Action) override; void on_window_resize(int w, int h) override; private: diff --git a/scenarioloadermode.cpp b/scenarioloadermode.cpp index 0ca4cd8b..73305a8e 100644 --- a/scenarioloadermode.cpp +++ b/scenarioloadermode.cpp @@ -14,7 +14,6 @@ http://mozilla.org/MPL/2.0/. #include "simulation.h" #include "simulationtime.h" #include "simulationenvironment.h" -#include "Timer.h" #include "application.h" #include "scenarioloaderuilayer.h" #include "renderer.h" @@ -22,20 +21,17 @@ http://mozilla.org/MPL/2.0/. #include "translation.h" scenarioloader_mode::scenarioloader_mode() { - m_userinterface = std::make_shared(); } // initializes internal data structures of the mode. returns: true on success, false otherwise -bool -scenarioloader_mode::init() { +bool scenarioloader_mode::init() { // nothing to do here return true; } // mode-specific update of simulation data. returns: false on error, true otherwise -bool -scenarioloader_mode::update() { +bool scenarioloader_mode::update() { if (!Global.ready_to_load) // waiting for network connection return true; @@ -69,31 +65,24 @@ scenarioloader_mode::update() { return true; } -bool -scenarioloader_mode::is_command_processor() const { +bool scenarioloader_mode::is_command_processor() const { return false; } // maintenance method, called when the mode is activated -void -scenarioloader_mode::enter() { - +void scenarioloader_mode::enter() { // TBD: hide cursor in fullscreen mode? Application.set_cursor( GLFW_CURSOR_NORMAL ); simulation::is_ready = false; - m_userinterface->set_background( "logo" ); Application.set_title( Global.AppName + " (" + Global.SceneryFile + ")" ); - m_userinterface->set_progress(); m_userinterface->set_progress(STR("Loading scenery")); GfxRenderer->Render(); } // maintenance method, called when the mode is deactivated -void -scenarioloader_mode::exit() { - +void scenarioloader_mode::exit() { simulation::Time.init( Global.starting_timestamp ); simulation::Environment.init(); } diff --git a/scenarioloaderuilayer.cpp b/scenarioloaderuilayer.cpp new file mode 100644 index 00000000..7353c15d --- /dev/null +++ b/scenarioloaderuilayer.cpp @@ -0,0 +1,109 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#include "scenarioloaderuilayer.h" + +#include "Globals.h" +#include "translation.h" + +scenarioloader_ui::scenarioloader_ui() +{ + m_suppress_menu = true; + load_random_background(); + generate_gradient_tex(); + load_wheel_frames(); + + // Temorary trivia contents + m_trivia.push_back("W trakcie przeprowadzonej rewizji technicznej wagonów kolejowych"); + m_trivia.push_back("sprawdza się również daty ostatnich napraw okresowych. Wagony, dla"); + m_trivia.push_back("których upływa termin dokonania następnej naprawy okresowej pracownik"); + m_trivia.push_back("zespołu rewizji technicznej prowadzący oględziny winien jest przekierować"); + m_trivia.push_back("do odpowiednich Zakładów Naprawczych Taboru Kolejowego lub wagonowni."); +} + +void scenarioloader_ui::render_() +{ + // For some reason, ImGui windows have some padding. Offset it. + // TODO: Find out a way to exactly adjust the position. + constexpr int padding = 12; + ImGui::SetNextWindowPos(ImVec2(-padding, -padding)); + ImGui::SetNextWindowSize(ImVec2(Global.window_size.x + padding * 2, Global.window_size.y + padding * 2)); + ImGui::Begin("Neo Loading Screen", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoBackground); + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + ImGui::PushFont(font_loading); + ImGui::SetWindowFontScale(1); + const float font_scale_mult = 48 / ImGui::GetFontSize(); + // Gradient at the lower half of the screen + const auto tex = reinterpret_cast(m_gradient_overlay_tex); + draw_list->AddImage(tex, ImVec2(0, Global.window_size.y / 2), ImVec2(Global.window_size.x, Global.window_size.y), ImVec2(0, 0), ImVec2(1, 1)); + // Loading label + ImGui::SetWindowFontScale(font_scale_mult * 0.8); + draw_list->AddText(ImVec2(200, 885), IM_COL32_WHITE, m_progresstext.c_str()); + // Loading wheel icon + const deferred_image* img = &m_loading_wheel_frames[38]; + const auto loading_tex = img->get(); + const auto loading_size = img->size(); + draw_list->AddImage(reinterpret_cast(loading_tex), ImVec2(35, 850), ImVec2(35 + loading_size.x, 850 + loading_size.y), ImVec2(0, 0), ImVec2(1, 1)); + // Trivia header + ImGui::SetWindowFontScale(font_scale_mult * 1); + draw_list->AddText(ImVec2(1280 - ImGui::CalcTextSize(STR_C("Did you know")).x / 2, 770), IM_COL32_WHITE, STR_C("Did you know")); + // Trivia content + ImGui::SetWindowFontScale(font_scale_mult * 0.6); + for (int i = 0; i < m_trivia.size(); i++) + { + std::string line = m_trivia[i]; + draw_list->AddText(ImVec2(1280 - ImGui::CalcTextSize(line.c_str()).x / 2, 825 + i * 25), IM_COL32_WHITE, line.c_str()); + } + // Progress bar at the bottom of the screen + const auto p1 = ImVec2(0, Global.window_size.y - 2); + const auto p2 = ImVec2(Global.window_size.x * m_progress, Global.window_size.y); + draw_list->AddRectFilled(p1, p2, ImColor(40, 210, 60, 255)); + ImGui::PopFont(); + ImGui::End(); +} + +void scenarioloader_ui::generate_gradient_tex() +{ + constexpr int image_width = 1; + constexpr int image_height = 256; + const auto image_data = new char[image_width * image_height * 4]; + for (int x = 0; x < image_width; x++) + for (int y = 0; y < image_height; y++) + { + image_data[(y * image_width + x) * 4] = 0; + image_data[(y * image_width + x) * 4 + 1] = 0; + image_data[(y * image_width + x) * 4 + 2] = 0; + image_data[(y * image_width + x) * 4 + 3] = clamp(static_cast(pow(y / 255.f, 0.7) * 255), 0, 255); + } + + // Create a OpenGL texture identifier + GLuint image_texture; + glGenTextures(1, &image_texture); + glBindTexture(GL_TEXTURE_2D, image_texture); + + // Setup filtering parameters for display + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + // Upload pixels into texture + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image_width, image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data); + + delete[] image_data; + + m_gradient_overlay_width = image_width; + m_gradient_overlay_height = image_height; + m_gradient_overlay_tex = image_texture; +} + +void scenarioloader_ui::load_wheel_frames() +{ + for (int i = 0; i < 60; i++) + m_loading_wheel_frames[i] = deferred_image("ui/loading_wheel/" + std::to_string(i + 1)); +} \ No newline at end of file diff --git a/scenarioloaderuilayer.h b/scenarioloaderuilayer.h index 3890e8ec..224a8f0e 100644 --- a/scenarioloaderuilayer.h +++ b/scenarioloaderuilayer.h @@ -10,11 +10,18 @@ http://mozilla.org/MPL/2.0/. #pragma once #include "uilayer.h" +#include "launcher/deferred_image.h" class scenarioloader_ui : public ui_layer { + int m_gradient_overlay_width; + int m_gradient_overlay_height; + GLuint m_gradient_overlay_tex; + deferred_image m_loading_wheel_frames[60]; + std::vector m_trivia; public: - scenarioloader_ui() : ui_layer() { - load_random_background(); - } - // TODO: implement mode-specific elements + scenarioloader_ui(); + void render_() override; +private: + void generate_gradient_tex(); + void load_wheel_frames(); }; diff --git a/uilayer.cpp b/uilayer.cpp index 5354b57a..2d912c09 100644 --- a/uilayer.cpp +++ b/uilayer.cpp @@ -29,6 +29,7 @@ GLint ui_layer::m_textureunit { GL_TEXTURE0 }; bool ui_layer::m_cursorvisible; ImFont *ui_layer::font_default{nullptr}; ImFont *ui_layer::font_mono{nullptr}; +ImFont *ui_layer::font_loading{nullptr}; ui_panel::ui_panel(std::string Identifier, bool const Isopen) : is_open(Isopen), m_name(std::move(Identifier)) {} @@ -132,7 +133,7 @@ bool ui_layer::mouse_button_callback(int button, int action, int mods) return m_imguiio->WantCaptureMouse; } -void::ui_layer::load_random_background() +void ui_layer::load_random_background() { std::vector images; for (auto &f : std::filesystem::directory_iterator("textures/logo")) @@ -230,9 +231,11 @@ bool ui_layer::init(GLFWwindow *Window) }; if (FileExists("fonts/dejavusans.ttf")) - font_default = m_imguiio->Fonts->AddFontFromFileTTF("fonts/dejavusans.ttf", Global.ui_fontsize, nullptr, &ranges[0]); + font_default = m_imguiio->Fonts->AddFontFromFileTTF("fonts/dejavusans.ttf", Global.ui_fontsize, nullptr, &ranges[0]); if (FileExists("fonts/dejavusansmono.ttf")) - font_mono = m_imguiio->Fonts->AddFontFromFileTTF("fonts/dejavusansmono.ttf", Global.ui_fontsize, nullptr, &ranges[0]); + font_mono = m_imguiio->Fonts->AddFontFromFileTTF("fonts/dejavusansmono.ttf", Global.ui_fontsize, nullptr, &ranges[0]); + if (FileExists("fonts/bahnschrift.ttf")) + font_loading = m_imguiio->Fonts->AddFontFromFileTTF("fonts/bahnschrift.ttf", 48, nullptr, &ranges[0]); if (!font_default && !font_mono) font_default = font_mono = m_imguiio->Fonts->AddFontDefault(); @@ -240,6 +243,8 @@ bool ui_layer::init(GLFWwindow *Window) font_default = font_mono; else if (!font_mono) font_mono = font_default; + if (!font_loading) + font_loading = font_default; imgui_style(); @@ -336,7 +341,6 @@ void ui_layer::update() void ui_layer::render() { render_background(); - render_progress(); render_panels(); render_tooltip(); render_menu(); @@ -397,26 +401,20 @@ void ui_layer::set_cursor(int const Mode) m_cursorvisible = (Mode != GLFW_CURSOR_DISABLED); } -void ui_layer::set_progress(float const Progress, float const Subtaskprogress) +void ui_layer::set_progress(std::string const &Text) { - m_progress = Progress * 0.01f; - m_subtaskprogress = Subtaskprogress * 0.01f; + m_progresstext = Text; +} + +void ui_layer::set_progress(float const progress, float const subtaskprogress) +{ + m_progress = progress * 0.01f; + m_subtaskprogress = subtaskprogress * 0.01f; } void ui_layer::set_background(std::string const &Filename) { - if (false == Filename.empty()) - { - m_background = GfxRenderer->Fetch_Texture(Filename); - } - else - { - m_background = null_handle; - } - if (m_background != null_handle) - { - auto const &texture = GfxRenderer->Texture(m_background); - } + m_background = Filename.empty() ? null_handle : GfxRenderer->Fetch_Texture(Filename); } void ui_layer::clear_panels() @@ -437,22 +435,6 @@ void ui_layer::add_owned_panel(ui_panel *Panel) m_ownedpanels.emplace_back( Panel ); } -void ui_layer::render_progress() -{ - if ((m_progress == 0.0f) && (m_subtaskprogress == 0.0f)) - return; - - ImGui::SetNextWindowPos(ImVec2(50, 50)); - ImGui::SetNextWindowSize(ImVec2(0, 0)); - ImGui::Begin(STR_C("Loading"), nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); - if (!m_progresstext.empty()) - ImGui::ProgressBar(m_progress, ImVec2(300, 0), m_progresstext.c_str()); - else - ImGui::ProgressBar(m_progress, ImVec2(300, 0)); - ImGui::ProgressBar(m_subtaskprogress, ImVec2(300, 0)); - ImGui::End(); -} - void ui_layer::render_panels() { for (auto *panel : m_panels) @@ -517,7 +499,7 @@ void ui_layer::render_menu() { glm::dvec2 mousepos = Global.cursor_pos; - if (!((Global.ControlPicking && mousepos.y < 50.0f) || m_imguiio->WantCaptureMouse) || m_progress != 0.0f || m_suppress_menu) + if (!((Global.ControlPicking && mousepos.y < 50.0f) || m_imguiio->WantCaptureMouse) || m_suppress_menu) return; if (ImGui::BeginMainMenuBar()) diff --git a/uilayer.h b/uilayer.h index 6349c2ac..3cd80c4d 100644 --- a/uilayer.h +++ b/uilayer.h @@ -10,7 +10,6 @@ http://mozilla.org/MPL/2.0/. #pragma once #include -#include #include "Texture.h" #include "widgets/popup.h" @@ -101,10 +100,10 @@ public: void begin_ui_frame(); static void begin_ui_frame_internal(); // - static void set_cursor( int Mode ); - // stores operation progress - void set_progress( float Progress = 0.0f, float Subtaskprogress = 0.0f ); - void set_progress( std::string const &Text ) { m_progresstext = Text; } + static void set_cursor( int Mode ); + // loading + void set_progress(std::string const &Text); + void set_progress(float progress, float subtaskprogress); // sets the ui background texture, if any void set_background( std::string const &Filename = "" ); void set_tooltip( std::string const &Tooltip ) { m_tooltip = Tooltip; } @@ -121,6 +120,7 @@ public: static ImFont *font_default; static ImFont *font_mono; + static ImFont *font_loading; protected: // members @@ -132,15 +132,17 @@ protected: virtual void render_menu_contents(); ui_log_panel m_logpanel { "Log", true }; bool m_suppress_menu = false; // if `true`, the menu at the top of the window will not be present + // progress bar config + float m_progress { 0.0f }; // percentage of filled progres bar, to indicate lengthy operations. + float m_subtaskprogress{ 0.0f }; // percentage of filled progres bar, to indicate lengthy operations. + std::string m_progresstext; // label placed over the progress bar private: // methods // render() subclass details - virtual void render_() {}; + virtual void render_() {} // draws background quad with specified earlier texture void render_background(); - // draws a progress bar in defined earlier state - void render_progress(); void render_tooltip(); void render_panels(); void render_menu(); @@ -150,11 +152,6 @@ protected: // members static GLint m_textureunit; - // progress bar config. TODO: put these together into an object - float m_progress { 0.0f }; // percentage of filled progres bar, to indicate lengthy operations. - float m_subtaskprogress{ 0.0f }; // percentage of filled progres bar, to indicate lengthy operations. - std::string m_progresstext; // label placed over the progress bar - texture_handle m_background { null_handle }; // path to texture used as the background. size depends on mAspect. std::vector m_panels; std::vector> m_ownedpanels;