diff --git a/application/editormode.cpp b/application/editormode.cpp index 8d16bbee..9572dfed 100644 --- a/application/editormode.cpp +++ b/application/editormode.cpp @@ -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 &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")); +} diff --git a/application/editormode.h b/application/editormode.h index 130df383..b85704eb 100644 --- a/application/editormode.h +++ b/application/editormode.h @@ -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) diff --git a/application/editormodeselftestscene.cpp b/application/editormodeselftestscene.cpp index 46c8e2e0..1102c175 100644 --- a/application/editormodeselftestscene.cpp +++ b/application/editormodeselftestscene.cpp @@ -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") diff --git a/application/editormodetrackux.cpp b/application/editormodetrackux.cpp index f45e585b..fc127955 100644 --- a/application/editormodetrackux.cpp +++ b/application/editormodetrackux.cpp @@ -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(io.DisplaySize.x), static_cast(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(0.07 / metres)) : colouring == track_colouring::plain ? 1.5f : 3.0f}; + auto const railcolour{EditorSettings.top_rails()}; + std::vector 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(length / std::max(metres, 0.5) / 8.0), colouring == track_colouring::grade ? std::clamp(static_cast(length / 10.0), 1, 8) : 1, 32)}; + auto const count{std::clamp(static_cast(length / std::max(metres, 0.5) / 8.0), colouring == track_colouring::grade ? std::clamp(static_cast(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(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) { diff --git a/editor/editorSettings.cpp b/editor/editorSettings.cpp index b2590272..8c22d39c 100644 --- a/editor/editorSettings.cpp +++ b/editor/editorSettings.cpp @@ -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::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) diff --git a/editor/editorSettings.hpp b/editor/editorSettings.hpp index 3adfe782..de7628b8 100644 --- a/editor/editorSettings.hpp +++ b/editor/editorSettings.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -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}; }; diff --git a/lang/editor_pl.po b/lang/editor_pl.po index 87a4e573..d57b4c01 100644 --- a/lang/editor_pl.po +++ b/lang/editor_pl.po @@ -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" diff --git a/rendering/opengl33renderer.cpp b/rendering/opengl33renderer.cpp index 4d699b52..5d89a8bb 100644 --- a/rendering/opengl33renderer.cpp +++ b/rendering/opengl33renderer.cpp @@ -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; } diff --git a/utilities/Globals.h b/utilities/Globals.h index c63197aa..36be95b6 100644 --- a/utilities/Globals.h +++ b/utilities/Globals.h @@ -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