17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-27 14:07:28 +01:00

feat(editor): the terrain of the tracks in the panel of the tracks: Draw the terrain to the track, of the selected tracks or of their whole run, Restore the terrain, each one step of the history, the terrain following the tracks edited or not, and Tracks onto the terrain and Terrain under the tracks for all the tracks of the scenery

The formation of a track as it is already was taken out of the terrain when the track was shaped again, as after a change of its properties alone: the formations the tracks still have are no longer counted as gone. Selftest trackterrain pull|restore <tracks>.
This commit is contained in:
maj00r
2026-10-24 18:53:32 +02:00
parent 11d1342181
commit 7d0caee541
6 changed files with 176 additions and 1 deletions

View File

@@ -333,6 +333,7 @@ class editor_mode : public application_mode, private editor_track::observer, pri
bool shape{true}; // the formation and the slopes of the tracks as modifiers of the terrain
bool grow{true}; // while a track is laid, the terrain is made around the cursor where there's none
bool laying{false}; // the tracks committed are being laid: they leave the terrain as it is
bool follow{true}; // a track with its formation edited takes it along, the formation led anew
bool grown{false}; // the chunks waiting were all asked for by growing: the formations aren't made anew after them
double grow_time{-10.0};
double slope{1.5};
@@ -454,6 +455,11 @@ class editor_mode : public application_mode, private editor_track::observer, pri
bool render_nmt_place(bool const Moving, bool const Button);
// the work under way: what's left of it and the button to stop it; Compact: one line, for the 3d view
void render_nmt_progress(bool const Compact);
// the formations of the tracks led into the terrain, down or up to it, or taken out of it; one step of the history each
void nmt_pull_terrain(std::vector<TTrack *> const &Tracks);
void nmt_restore_terrain(std::vector<TTrack *> const &Tracks);
// the terrain of the selected tracks in the panel of the tracks
void render_track_terrain(std::vector<TTrack *> const &Tracks);
// the chunks waiting for the terrain model and the tiles of the imagery asked for outlined where they will be, so it's seen
// what's on its way
void draw_nmt_pending() const;

View File

@@ -682,6 +682,12 @@ void editor_mode::nmt_follow_tracks()
auto &state{m_nmt_state};
if (state.changed.empty() && state.gone.empty())
return;
if (false == state.follow)
{
state.changed.clear();
state.gone.clear();
return;
}
if (state.stage != nmt_state::automatic::idle || state.fitting || ImGui::GetTime() - state.changed_time < 0.3 || ImGui::IsMouseDown(ImGuiMouseButton_Left) || ImGui::IsMouseDown(ImGuiMouseButton_Right))
return;
// laying on, from the end of the track laid or dragged from a free end, the terrain waits for the laying to end: shaping it
@@ -878,6 +884,10 @@ std::size_t editor_mode::nmt_shape_tracks(std::vector<TTrack *> const *Only)
});
};
auto const &existing{m_streamer.modifiers()};
// the formations the tracks have, the ones as they are already too: those only aren't led again, nor taken out as gone
std::unordered_set<std::string> kept;
for (auto const &modifier : laid)
kept.insert(modifier.name);
std::erase_if(laid, [&](heightmap::modifier const &Modifier) {
auto const found{std::find_if(existing.begin(), existing.end(), [&](heightmap::modifier const &Other) { return Other.name == Modifier.name; })};
return found != existing.end() && same(*found, Modifier);
@@ -892,7 +902,7 @@ std::size_t editor_mode::nmt_shape_tracks(std::vector<TTrack *> const *Only)
continue;
auto const own{"nmt " + track->name()};
for (auto const &modifier : m_streamer.modifiers())
if ((modifier.name == own || modifier.name.starts_with(own + " ")) && std::none_of(laid.begin(), laid.end(), [&](heightmap::modifier const &Laid) { return Laid.name == modifier.name; }))
if ((modifier.name == own || modifier.name.starts_with(own + " ")) && false == kept.contains(modifier.name))
gone.push_back(modifier.name);
}
for (auto const &name : gone)
@@ -1160,6 +1170,104 @@ void editor_mode::render_nmt_progress(bool const Compact)
item_tooltip("Stops loading: the terrain made so far stays");
}
void editor_mode::nmt_pull_terrain(std::vector<TTrack *> const &Tracks)
{
auto &state{m_nmt_state};
std::vector<TTrack *> rails;
for (auto *track : Tracks)
if (track != nullptr && false == track->m_editorremoved && rail(*track))
rails.push_back(track);
if (rails.empty())
{
state.status = STR("No tracks to draw the terrain to");
return;
}
if (false == ensure_terrain())
{
state.status = STR("The heightmap terrain can't be opened");
return;
}
terrain_history_step();
auto const laid{nmt_shape_tracks(&rails)};
terrain_history_step();
state.status = format(STR_P("The terrain drawn to %zu track: %zu formations led into it, Ctrl+Z takes it back", "The terrain drawn to %zu tracks: %zu formations led into it, Ctrl+Z takes it back", rails.size()), rails.size(), laid);
WriteLog("Editor: NMT - " + state.status, logtype::generic);
}
void editor_mode::nmt_restore_terrain(std::vector<TTrack *> const &Tracks)
{
auto &state{m_nmt_state};
if (false == m_streamer.active())
return;
std::vector<std::string> gone;
for (auto const *track : Tracks)
{
if (track == nullptr)
continue;
auto const own{"nmt " + formation_name(*track)};
for (auto const &modifier : m_streamer.modifiers())
if ((modifier.name == own || modifier.name.starts_with(own + " ")) && std::find(gone.begin(), gone.end(), modifier.name) == gone.end())
gone.push_back(modifier.name);
}
terrain_history_step();
for (auto const &name : gone)
m_streamer.remove_modifier(name);
if (false == gone.empty())
forget_ground();
terrain_history_step();
state.status = format(STR_P("%zu formation taken out of the terrain, Ctrl+Z takes it back", "%zu formations taken out of the terrain, Ctrl+Z takes it back", gone.size()), gone.size());
WriteLog("Editor: NMT - " + state.status, logtype::generic);
}
void editor_mode::render_track_terrain(std::vector<TTrack *> const &Tracks)
{
auto &state{m_nmt_state};
// a scenery with no terrain made nor any place on the map has nothing to draw to its tracks
if (false == m_streamer.active() && false == m_nmt.placed())
return;
if (false == ImGui::CollapsingHeader(format("%s###trackterrain", STR_C("Terrain")).c_str()))
return;
auto const busy{nmt_busy()};
auto const spacing{ImGui::GetStyle().ItemSpacing.x};
auto const half{std::floor((ImGui::GetContentRegionAvail().x - spacing) * 0.5f)};
ImGui::BeginDisabled(busy);
if (ImGui::Button(STR_C("Draw the terrain to the track"), ImVec2(half, 0.0f)))
nmt_pull_terrain(Tracks);
item_tooltip("The formation and the slopes of the selected tracks led into the terrain, down or up to it; elsewhere the terrain stays as it is");
ImGui::SameLine();
if (ImGui::Button(STR_C("The whole run"), ImVec2(-FLT_MIN, 0.0f)))
{
m_track_set = Tracks;
track_set_grow();
select_tracks(m_track_set);
nmt_pull_terrain(m_track_set);
}
item_tooltip("The tracks joined to the selected ones added to the selection, by the rules of the spread, and the terrain drawn to all of them");
if (ImGui::Button(STR_C("Restore the terrain"), ImVec2(half, 0.0f)))
nmt_restore_terrain(Tracks);
item_tooltip("The formations of the selected tracks taken out of the terrain: the ground goes back to how it was before them");
ImGui::EndDisabled();
toggle_chips("nmtfollow", {{STR_C("The terrain follows the tracks edited"), &state.follow, "A track with its formation moved or changed takes it along; off: the terrain stays as it is until it's drawn to the tracks again"}});
ImGui::TextDisabled("%s", STR_C("All the tracks of the scenery"));
ImGui::BeginDisabled(busy || false == m_nmt.placed());
if (ImGui::Button(STR_C("Tracks onto the terrain"), ImVec2(half, 0.0f)))
nmt_auto_start();
item_tooltip("The terrain model is loaded along the tracks and the tracks go onto it, wherever the scenery was built: the top of the ballast at the height of the model, the grade line of every line fitted to it within the limits of the line, switches and the tracks beside them on one grade line, the formation and the slopes made along them. Where the model is off the tracks keep their line and it's reported");
ImGui::SameLine();
if (ImGui::Button(STR_C("Terrain under the tracks"), ImVec2(-FLT_MIN, 0.0f)))
nmt_under_start();
item_tooltip("The tracks aren't touched: the terrain model is loaded along them and drawn to them, at each place by as much as the tracks lie above or below it there, fading out away from them, so its relief stays and the tracks don't end up in deep cuttings; then the formation and the slopes along them. For a scenery built level or at its own heights");
ImGui::EndDisabled();
if (busy)
render_nmt_progress(true);
if (false == state.status.empty())
{
ImGui::PushTextWrapPos(0.0f);
ImGui::TextDisabled("%s", state.status.c_str());
ImGui::PopTextWrapPos();
}
}
void editor_mode::draw_nmt_pending() const
{
auto const &state{m_nmt_state};

View File

@@ -157,6 +157,7 @@ editor_mode::selftest_result editor_mode::selftest_dispatch(std::string const &c
// with the timings and counts of the renderer
// fittime <metres>... the grade line of a straight over rolling ground of each length fitted, as at a click, and the time the
// frame waits for it while the track is laid, the line fitted on the thread of the fitter
// trackterrain <pull|restore> <track>... the terrain drawn to the tracks, or their formations taken out of it, as the panel of the tracks
// terrainhash a hash of the heights of the terrain chunks in memory, to compare the ground two builds made
// rebuildall the geometry of every path in the sections made already laid anew, the time it took logged,
// and the 3d sleepers of all the tracks laid anew, timed apart

View File

@@ -471,6 +471,22 @@ editor_mode::selftest_result editor_mode::selftest_terrain(std::string const &co
WriteLog("SELFTEST terrainadd: " + m_terrain_status);
return selftest_result::handled;
}
if (command == "trackterrain")
{
// the terrain drawn to the named tracks, or their formations taken out of it, as the buttons of the panel of the tracks
std::string action;
words >> action;
std::vector<TTrack *> tracks;
for (std::string name; words >> name;)
if (auto *track{simulation::Paths.find(name)})
tracks.push_back(track);
if (action == "pull")
nmt_pull_terrain(tracks);
else
nmt_restore_terrain(tracks);
WriteLog(format("SELFTEST trackterrain %s %zu tracks: %s, %zu modifiers", action.c_str(), tracks.size(), m_nmt_state.status.c_str(), m_streamer.modifiers().size()));
return selftest_result::handled;
}
if (command == "terrainhash")
{
// the heights of the chunks in memory hashed, to compare the ground two builds made

View File

@@ -751,6 +751,7 @@ void editor_mode::render_track_selection()
if (false == sheet)
return;
render_track_select_tools(tracks);
render_track_terrain(tracks);
render_track_style(tracks);
render_track_properties(tracks);
ImGui::Spacing();
@@ -1451,6 +1452,7 @@ void editor_mode::render_path_ui()
}
std::vector<TTrack *> const tracks{track};
render_track_select_tools(tracks);
render_track_terrain(tracks);
render_track_style(tracks);
render_track_properties(tracks);
render_path_events(*track);

View File

@@ -6731,6 +6731,48 @@ msgstr "Wczytywanie NMT"
msgid "Orthophoto loading"
msgstr "Wczytywanie ortofotomapy"
msgid "No tracks to draw the terrain to"
msgstr "Brak torów, do których można dociągnąć teren"
msgid "The terrain drawn to %zu track: %zu formations led into it, Ctrl+Z takes it back"
msgid_plural "The terrain drawn to %zu tracks: %zu formations led into it, Ctrl+Z takes it back"
msgstr[0] "Teren dociągnięty do %zu toru: wprowadzone formacje: %zu, Ctrl+Z cofa"
msgstr[1] "Teren dociągnięty do %zu torów: wprowadzone formacje: %zu, Ctrl+Z cofa"
msgstr[2] "Teren dociągnięty do %zu torów: wprowadzone formacje: %zu, Ctrl+Z cofa"
msgid "%zu formation taken out of the terrain, Ctrl+Z takes it back"
msgid_plural "%zu formations taken out of the terrain, Ctrl+Z takes it back"
msgstr[0] "%zu formacja usunięta z terenu, Ctrl+Z cofa"
msgstr[1] "%zu formacje usunięte z terenu, Ctrl+Z cofa"
msgstr[2] "%zu formacji usuniętych z terenu, Ctrl+Z cofa"
msgid "Draw the terrain to the track"
msgstr "Dociągnij teren do toru"
msgid "The formation and the slopes of the selected tracks led into the terrain, down or up to it; elsewhere the terrain stays as it is"
msgstr "Formacja i skarpy zaznaczonych torów wprowadzone w teren, w dół albo w górę do niego; poza nimi teren zostaje bez zmian"
msgid "The whole run"
msgstr "Cały ciąg"
msgid "The tracks joined to the selected ones added to the selection, by the rules of the spread, and the terrain drawn to all of them"
msgstr "Tory połączone z zaznaczonymi dodane do zaznaczenia według zasad rozszerzania i teren dociągnięty do nich wszystkich"
msgid "Restore the terrain"
msgstr "Przywróć teren"
msgid "The formations of the selected tracks taken out of the terrain: the ground goes back to how it was before them"
msgstr "Formacje zaznaczonych torów usunięte z terenu: grunt wraca do stanu sprzed nich"
msgid "The terrain follows the tracks edited"
msgstr "Teren podąża za edytowanymi torami"
msgid "A track with its formation moved or changed takes it along; off: the terrain stays as it is until it's drawn to the tracks again"
msgstr "Przesunięty lub zmieniony tor z formacją pociąga ją za sobą; wyłączone: teren zostaje bez zmian, dopóki nie dociągniesz go do torów ponownie"
msgid "All the tracks of the scenery"
msgstr "Wszystkie tory scenerii"
msgid "By hand: the model as it is, the tracks stay"
msgstr "Ręcznie: NMT bez zmian, tory zostają"