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.
This commit is contained in:
jakubg1
2025-09-10 01:53:22 +02:00
parent 9fa22d81e5
commit 5e2175302b
10 changed files with 160 additions and 81 deletions

View File

@@ -136,6 +136,7 @@ set(SOURCES
"editoruilayer.cpp"
"editoruipanels.cpp"
"scenarioloadermode.cpp"
"scenarioloaderuilayer.cpp"
"scenenodegroups.cpp"
"simulationenvironment.cpp"
"simulationstateserializer.cpp"

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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 { ; }

View File

@@ -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:

View File

@@ -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<scenarioloader_ui>();
}
// 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();
}

109
scenarioloaderuilayer.cpp Normal file
View File

@@ -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<ImTextureID>(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<ImTextureID>(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<int>(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));
}

View File

@@ -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<std::string> 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();
};

View File

@@ -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<std::string> images;
for (auto &f : std::filesystem::directory_iterator("textures/logo"))
@@ -233,6 +234,8 @@ bool ui_layer::init(GLFWwindow *Window)
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]);
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())

View File

@@ -10,7 +10,6 @@ http://mozilla.org/MPL/2.0/.
#pragma once
#include <string>
#include <utility>
#include "Texture.h"
#include "widgets/popup.h"
@@ -102,9 +101,9 @@ public:
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; }
// 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<ui_panel *> m_panels;
std::vector<std::unique_ptr<ui_panel>> m_ownedpanels;