17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-26 18:14:55 +01:00

feat(editor): the top view draws the tracks whole, without the ballast or the rails alone, in a colour of their own over the imagery and the map: both rails close up, one line further out; a choice by the extent and in the View menu, kept in the settings

This commit is contained in:
maj00r
2026-10-23 13:20:06 +02:00
parent fe3dd758ea
commit a892532fda
9 changed files with 137 additions and 27 deletions

View File

@@ -645,7 +645,7 @@ editor_ui *editor_mode::ui() const
bool editor_mode::init()
{
EditorSettings.load();
Global.EditorOrthoBallast = EditorSettings.top_ballast();
Global.EditorOrthoTracks = EditorSettings.top_tracks();
set_norms_free(EditorSettings.norms() == editorSettings::track_norms::free_laying);
EditorModelSets.load();
Camera.Init({0, 15, 0}, {glm::radians(-30.0), glm::radians(180.0), 0}, nullptr);
@@ -1486,6 +1486,8 @@ bool editor_mode::update()
draw_track_labels();
draw_track_legend();
}
else if (Global.EditorOrtho && Global.EditorOrthoTracks > 1)
draw_track_lines();
if (selected_track())
draw_track_overlay();
if (route_active())
@@ -3627,13 +3629,11 @@ void editor_mode::render_view_menu()
toggle_ortho();
if (ImGui::IsItemHovered())
ImGui::SetTooltip("%s", STR_C("The view straight down without perspective, as on a map; again for the camera as it was"));
if (ImGui::MenuItem(STR_C("Ballast in the top view"), nullptr, &Global.EditorOrthoBallast))
if (ImGui::BeginMenu(STR_C("Tracks in the top view")))
{
EditorSettings.top_ballast(Global.EditorOrthoBallast);
EditorSettings.save();
render_top_tracks_choice(false);
ImGui::EndMenu();
}
if (ImGui::IsItemHovered())
ImGui::SetTooltip("%s", STR_C("Off: the top view draws the rails and sleepers of the tracks without the ballast, so the ground under them shows"));
if (ImGui::BeginMenu(STR_C("Track colours")))
{
char const *const names[]{"Plain", "By the radius", "By the speed", "By the grade"};
@@ -5656,13 +5656,7 @@ void editor_mode::render_gizmo_options()
if (ImGui::IsItemHovered())
ImGui::SetTooltip("%s", STR_C("Wheel zooms. Hold the wheel and drag to pan."));
ImGui::SameLine();
if (ImGui::Checkbox(STR_C("Ballast"), &Global.EditorOrthoBallast))
{
EditorSettings.top_ballast(Global.EditorOrthoBallast);
EditorSettings.save();
}
if (ImGui::IsItemHovered())
ImGui::SetTooltip("%s", STR_C("Off: the top view draws the rails and sleepers of the tracks without the ballast, so the ground under them shows"));
render_top_tracks_choice(true);
}
if (ui()->mode() == nodebank_panel::TRACK && m_track_tab != track_tab::path && m_track_tab != track_tab::turnout && m_track_tab != track_tab::turntable && m_track_tab != track_tab::srk &&
m_track_tab != track_tab::lineside)
@@ -6999,3 +6993,51 @@ void editor_mode::starter_trainset(std::vector<std::string> &Trailing)
return;
}
}
void editor_mode::render_top_tracks_choice(bool const Combo)
{
static char const *const labels[]{"Whole", "Without ballast", "Rails only"};
static char const *const tooltips[]{"The tracks as in the 3D view", "The rails and the sleepers, the ground under them shows", "The rails alone, without the sleepers and the timbers of the switches, as on a plan"};
auto chosen{std::clamp(Global.EditorOrthoTracks, 0, 2)};
auto const pick = [&](int const Choice) {
Global.EditorOrthoTracks = Choice;
EditorSettings.top_tracks(Choice);
EditorSettings.save();
};
if (false == Combo)
{
for (int choice = 0; choice < 3; ++choice)
{
if (ImGui::MenuItem(STR_C(labels[choice]), nullptr, chosen == choice))
pick(choice);
if (ImGui::IsItemHovered())
ImGui::SetTooltip("%s", STR_C(tooltips[choice]));
}
return;
}
ImGui::SetNextItemWidth(130.0f);
if (ImGui::BeginCombo(STR_C("Tracks"), STR_C(labels[chosen])))
{
for (int choice = 0; choice < 3; ++choice)
{
if (ImGui::Selectable(STR_C(labels[choice]), chosen == choice))
pick(choice);
if (ImGui::IsItemHovered())
ImGui::SetTooltip("%s", STR_C(tooltips[choice]));
}
ImGui::EndCombo();
}
if (ImGui::IsItemHovered())
ImGui::SetTooltip("%s", STR_C("How the top view draws the tracks"));
if (chosen < 2)
return;
ImGui::SameLine();
auto colour{ImGui::ColorConvertU32ToFloat4(EditorSettings.top_rails())};
if (ImGui::ColorEdit3("##toprails", &colour.x, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel))
{
EditorSettings.top_rails(ImGui::ColorConvertFloat4ToU32(ImVec4(colour.x, colour.y, colour.z, 1.0f)));
EditorSettings.save();
}
if (ImGui::IsItemHovered())
ImGui::SetTooltip("%s", STR_C("The colour of the rails, to stand out on the imagery and the map"));
}

View File

@@ -765,6 +765,8 @@ class editor_mode : public application_mode, private editor_track::observer, pri
void render_gizmo();
// gizmo settings, drawn in the toolset window
void render_gizmo_options();
// how the top view draws the tracks: as a combo with the colour of the rails, or as items of a menu
void render_top_tracks_choice(bool const Combo);
// the tools of the field of work in the toolbar under the menu
void render_toolbar();
// an edit mode of the node bank as the field of work, from the toolbar or its key (1-5)

View File

@@ -283,8 +283,8 @@ editor_mode::selftest_result editor_mode::selftest_scene(std::string const &comm
if (extent > 0.0f)
Global.EditorOrthoExtent = std::clamp(extent, 5.0f, 50000.0f);
if (false == ballast.empty())
Global.EditorOrthoBallast = ballast != "noballast";
WriteLog(format("SELFTEST ortho extent %.0f ballast %d", Global.EditorOrthoExtent, Global.EditorOrthoBallast ? 1 : 0));
Global.EditorOrthoTracks = ballast == "rails" ? 2 : ballast == "noballast" ? 1 : 0;
WriteLog(format("SELFTEST ortho extent %.0f tracks %d", Global.EditorOrthoExtent, Global.EditorOrthoTracks));
return selftest_result::handled;
}
if (command == "mapstate")

View File

@@ -15,6 +15,7 @@ http://mozilla.org/MPL/2.0/.
#include "application/editormodetrackshared.h"
#include "editor/editorFormat.hpp"
#include "editor/editorGeometry.hpp"
#include "editor/editorSettings.hpp"
#include "editor/editorSwitchStore.hpp"
#include "utilities/Globals.h"
@@ -201,14 +202,18 @@ void editor_mode::update_track_hover()
void editor_mode::draw_track_lines() const
{
auto const metres{ortho_metres_per_pixel()};
auto const colouring{m_track_colouring};
if (metres < kLinesFrom && colouring == track_colouring::plain)
auto const colouring{ui()->mode() == nodebank_panel::TRACK ? m_track_colouring : track_colouring::plain};
// the top view of the rails alone draws them in a colour of their own, as on a plan: both rails where they're apart on the screen
auto const rails{metres > 0.0 && Global.EditorOrthoTracks > 1 && colouring == track_colouring::plain};
if (metres < kLinesFrom && colouring == track_colouring::plain && false == rails)
return;
auto const &io{ImGui::GetIO()};
screen_projection const projection;
glm::dvec2 const centre{Global.pCamera.Pos.x, Global.pCamera.Pos.z};
auto const reach{Global.EditorOrtho ? 0.5 * metres * std::hypot(static_cast<double>(io.DisplaySize.x), static_cast<double>(io.DisplaySize.y)) : kColouredReach};
auto const width{colouring == track_colouring::plain ? 1.5f : 3.0f};
auto const width{rails ? std::max(2.0f, static_cast<float>(0.07 / metres)) : colouring == track_colouring::plain ? 1.5f : 3.0f};
auto const railcolour{EditorSettings.top_rails()};
std::vector<glm::dvec3> points;
auto *drawlist{ImGui::GetBackgroundDrawList(ImGui::GetMainViewport())};
for (auto *track : simulation::Paths.sequence())
{
@@ -220,7 +225,7 @@ void editor_mode::draw_track_lines() const
bezier const curve{path};
if (false == plan_near(curve, centre, reach))
continue;
auto colour{IM_COL32(235, 235, 235, 150)};
auto colour{rails ? railcolour : IM_COL32(235, 235, 235, 150)};
if (colouring == track_colouring::radius)
{
auto const radius{::track_mode::piece_plan_radius(path)};
@@ -229,7 +234,24 @@ void editor_mode::draw_track_lines() const
else if (colouring == track_colouring::speed)
colour = velocity > 0.0 ? band_colour(kSpeedBands, velocity) : kUncoloured;
auto const length{curve.plan_length(4)};
auto const count{std::clamp(static_cast<int>(length / std::max(metres, 0.5) / 8.0), colouring == track_colouring::grade ? std::clamp(static_cast<int>(length / 10.0), 1, 8) : 1, 32)};
auto const count{std::clamp(static_cast<int>(length / std::max(metres, 0.5) / 8.0), colouring == track_colouring::grade ? std::clamp(static_cast<int>(length / 10.0), 1, 8) : 1, rails ? 128 : 32)};
if (rails && track->fTrackWidth / metres > 6.0)
{
points.clear();
for (int k = 0; k <= count; ++k)
points.push_back(curve.point(static_cast<double>(k) / count));
auto const half{(track->fTrackWidth + 0.07) * 0.5};
auto const across = [&](std::size_t const K) {
auto const along{points[std::min(K + 1, points.size() - 1)] - points[K > 0 ? K - 1 : 0]};
auto const normal{glm::dvec3{-along.z, 0.0, along.x}};
auto const size{glm::length(normal)};
return size > 1e-9 ? normal * (half / size) : glm::dvec3{0.0};
};
for (auto const side : {-1.0, 1.0})
for (std::size_t k = 1; k < points.size(); ++k)
projection.line(drawlist, points[k - 1] + across(k - 1) * side, points[k] + across(k) * side, colour, width);
continue;
}
auto previous{curve.point(0.0)};
for (int k = 1; k <= count; ++k)
{

View File

@@ -109,7 +109,11 @@ bool editorSettings::load()
else if (key == "track_tools_all")
m_track_tools_more = (value == "1");
else if (key == "top_ballast")
m_top_ballast = (value == "1");
m_top_tracks = value == "1" ? 0 : 1;
else if (key == "top_tracks")
m_top_tracks = std::clamp(std::atoi(value.c_str()), 0, 2);
else if (key == "top_rails")
m_top_rails = static_cast<std::uint32_t>(std::strtoul(value.c_str(), nullptr, 16)) | 0xff000000u;
else if (key == "track_norms")
m_norms = value == "free" ? track_norms::free_laying : value == "plk" ? track_norms::plk : track_norms::ask;
else if (key.rfind(origin_prefix, 0) == 0)
@@ -158,7 +162,8 @@ bool editorSettings::save()
stream << "first_steps " << (m_first_steps ? 1 : 0) << "\n";
stream << "terrain_offer " << (m_terrain_offer ? 1 : 0) << "\n";
stream << "track_tools_all " << (m_track_tools_more ? 1 : 0) << "\n";
stream << "top_ballast " << (m_top_ballast ? 1 : 0) << "\n";
stream << "top_tracks " << m_top_tracks << "\n";
stream << "top_rails " << std::hex << (m_top_rails & 0xffffffu) << std::dec << "\n";
stream << "track_norms " << (m_norms == track_norms::free_laying ? "free" : m_norms == track_norms::plk ? "plk" : "ask") << "\n";
stream << std::fixed << std::setprecision(3);
for (auto const &origin : m_orthophoto_origins)

View File

@@ -1,5 +1,6 @@
#pragma once
#include <cstdint>
#include <map>
#include <string>
#include <utility>
@@ -62,8 +63,10 @@ public:
bool track_tools_more() const { return m_track_tools_more; }
void track_tools_more(bool Show) { m_track_tools_more = Show; }
// the ballast of the railway drawn in the top view
bool top_ballast() const { return m_top_ballast; }
void top_ballast(bool Show) { m_top_ballast = Show; }
int top_tracks() const { return m_top_tracks; }
void top_tracks(int const Tracks) { m_top_tracks = Tracks; }
std::uint32_t top_rails() const { return m_top_rails; }
void top_rails(std::uint32_t const Colour) { m_top_rails = Colour; }
// how the new tracks are laid: asked as the laying starts, or the choice kept
enum class track_norms
{
@@ -86,7 +89,8 @@ private:
bool m_first_steps{true};
bool m_terrain_offer{true};
bool m_track_tools_more{false};
bool m_top_ballast{true};
int m_top_tracks{0}; // the railway in the top view: whole, without the ballast, the rails alone
std::uint32_t m_top_rails{0xffd246ff}; // colour of the rails alone, as ImGui keeps it: ABGR
track_norms m_norms{track_norms::ask};
};

View File

@@ -11472,3 +11472,30 @@ msgstr ": pozostałe stoją dalej niż 10 m od toru, przy torze z pojazdami albo
msgid " (layer \"%s\")"
msgstr " (warstwa \"%s\")"
msgid "Tracks in the top view"
msgstr "Tory w widoku z góry"
msgid "Whole"
msgstr "Całe"
msgid "Without ballast"
msgstr "Bez podsypki"
msgid "Rails only"
msgstr "Same szyny"
msgid "The tracks as in the 3D view"
msgstr "Tory jak w widoku 3D"
msgid "The rails and the sleepers, the ground under them shows"
msgstr "Szyny i podkłady, spod nich widać teren"
msgid "The rails alone, without the sleepers and the timbers of the switches, as on a plan"
msgstr "Same szyny, bez podkładów i podrozjazdnic, jak na planie"
msgid "How the top view draws the tracks"
msgstr "Jak widok z góry rysuje tory"
msgid "The colour of the rails, to stand out on the imagery and the map"
msgstr "Kolor szyn, żeby odcinały się od ortofotomapy i mapy"

View File

@@ -54,7 +54,14 @@ editor_track_view_range(float const Range, bool const Apply)
bool
editor_ballast_hidden(TTrack const *Track, bool const Colour)
{
return Colour && EditorModeFlag && Global.EditorOrtho && false == Global.EditorOrthoBallast && (Track->iCategoryFlag & 15) == 1;
return Colour && EditorModeFlag && Global.EditorOrtho && Global.EditorOrthoTracks > 0 && (Track->iCategoryFlag & 15) == 1;
}
// ...or the sleepers and the timbers of the switches too, the rails alone as on a plan
bool
editor_sleepers_hidden()
{
return EditorModeFlag && Global.EditorOrtho && Global.EditorOrthoTracks > 1;
}
// the top view draws the scenery as far as the tracks; its models keep their own ranges
@@ -4701,6 +4708,7 @@ void opengl33_renderer::Render(scene::basic_cell::path_sequence::const_iterator
switch( m_renderpass.draw_mode ) {
case rendermode::color:
case rendermode::reflections: {
if( editor_sleepers_hidden() ) { break; }
for( auto first { First }; first != Last; ++first ) {
auto *track = *first;
if( false == track->m_visible ) { continue; }

View File

@@ -174,7 +174,7 @@ struct global_settings {
float FieldOfView{ 45.f }; // vertical field of view for the camera. TODO: move it to the renderer
bool EditorOrtho{ false };
float EditorOrthoExtent{ 150.f };
bool EditorOrthoBallast{ true }; // the ballast of the railway drawn in the top view of the editor
int EditorOrthoTracks{ 0 }; // the railway in the top view of the editor: 0 whole, 1 without the ballast, 2 the rails alone
GLint iMaxTextureSize{ 4096 }; // maksymalny rozmiar tekstury
GLint iMaxCabTextureSize{ 4096 }; // largest allowed texture in vehicle cab
int iMultisampling{ 2 }; // tryb antyaliasingu: 0=brak,1=2px,2=4px,3=8px,4=16px