diff --git a/application/editormode.cpp b/application/editormode.cpp index 0bec52a7..5f99a52a 100644 --- a/application/editormode.cpp +++ b/application/editormode.cpp @@ -516,8 +516,7 @@ editor_mode::editor_mode() { ui()->set_edit_menu([this]() { render_edit_menu(); }); ui()->set_menu_options([this]() { render_object_menu(); - render_track_menu(); - render_road_menu(); + render_view_menu(); render_map_menu(); render_help_menu(); }); @@ -684,91 +683,16 @@ void editor_mode::snap_to_ground(scene::basic_node *node, bool const Alone, bool if (!simulation::Region->point_inside(origin)) return; - // small tolerance so a node already resting on a surface still snaps cleanly to it - double const epsilon = 0.05; - double bestY = -std::numeric_limits::max(); - bool found = false; - - // record the highest surface that is at or below the node's current position at its (x,z) - auto consider_triangle = [&](glm::dvec3 const &a, glm::dvec3 const &b, glm::dvec3 const &c) { - double y; - if (triangle_height_at(a, b, c, origin.x, origin.z, y) && y <= origin.y + epsilon && y > bestY) - { - bestY = y; - found = true; - } - }; - - auto consider_shapes = [&](std::vector const &shapes) { - for (auto const &shape : shapes) - { - // quick reject: skip shapes whose bounding circle doesn't cover our (x,z) column - auto const &sdata = shape.data(); - double const sdx = origin.x - sdata.area.center.x; - double const sdz = origin.z - sdata.area.center.z; - if (sdx * sdx + sdz * sdz > static_cast(sdata.area.radius) * sdata.area.radius) - continue; - - auto const &verts = sdata.vertices; - for (std::size_t i = 0; i + 2 < verts.size(); i += 3) - consider_triangle(verts[i].position, verts[i + 1].position, verts[i + 2].position); - } - }; - - scene::basic_section &sec = simulation::Region->section(origin); - // section level holds the large opaque geometry, including legacy terrain - consider_shapes(sec.m_shapes); - - // scan a 3x3 neighbourhood of cells for smaller geometry and other model instances below us - for (int dz = -1; dz <= 1; ++dz) - for (int dx = -1; dx <= 1; ++dx) - { - scene::basic_cell &cell = sec.cell(origin, glm::ivec2(dx, dz)); - consider_shapes(cell.m_shapesopaque); - consider_shapes(cell.m_shapestranslucent); - - // other instances are approximated by their bounding sphere, so a node can rest on top of them - for (auto *inst : cell.m_instancesopaque) - { - if (!inst || inst == node) - continue; - glm::dvec3 const ic = inst->location(); - double const r = static_cast(inst->radius()); - double const idx = origin.x - ic.x, idz = origin.z - ic.z; - double const horiz2 = idx * idx + idz * idz; - if (horiz2 < r * r) - { - double const ytop = ic.y + std::sqrt(r * r - horiz2); - if (ytop <= origin.y + epsilon && ytop > bestY) - { - bestY = ytop; - found = true; - } - } - } - } - - // editable terrain patches keep their heightmap on the CPU, so query them directly - for (editor_terrain *terrain : active_terrains()) - { - if (!terrain->contains(origin.x, origin.z)) - continue; - double const y = terrain->height_at(origin.x, origin.z); - if (y <= origin.y + epsilon && y > bestY) - { - bestY = y; - found = true; - } - } - - if (!found) + // the same ground the models are put on when they're inserted: the terrain shapes, the terrain tile models and the terrain + // patches of the editor, under the node, or over it when it went under the ground. the shapes alone and the spheres of the + // models around, which were asked about before, missed the ground of the sceneries made of terrain tile models + glm::dvec3 const target = placement_on_ground(origin); + if (std::abs(target.y - origin.y) < 1e-4) return; push_snapshot(node, EditorSnapshot::Action::Move); if (false == m_history.empty()) m_history.back().joined = Joined; - glm::dvec3 target = origin; - target.y = bestY; if (auto *model = dynamic_cast(node); Alone && model != nullptr && node->group() > 1) { // the model of a group goes down by itself, the rest of the group stays @@ -1491,6 +1415,8 @@ bool editor_mode::update() render_new_scenery_popup(); render_open_scenery_popup(); render_shortcuts_window(); + render_orthophoto_window(); + render_environment_window(); draw_selection_overlay(); render_context_menu(); @@ -2896,17 +2822,107 @@ void editor_mode::render_map_menu() m_orthophoto.enabled(false == enabled); if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", STR_C("Aerial imagery of geoportal.gov.pl under the scenery, laid out by the origin of the scenery")); - // the settings are a part of the terrain tools - if (ImGui::MenuItem(STR_C("Orthophoto settings..."), nullptr, m_terrain_open)) - { - m_track_window_open = false; - m_roadtool.window = false; - terrain_workspace(true); - m_orthophoto_expand = true; - } + ImGui::MenuItem(STR_C("Orthophoto settings..."), nullptr, &m_orthophoto_window); ImGui::EndMenu(); } +void editor_mode::render_orthophoto_window() +{ + if (false == m_orthophoto_window) + return; + ImGui::SetNextWindowSize(ImVec2(430.0f, 0.0f), ImGuiCond_FirstUseEver); + if (ImGui::Begin((std::string(STR_C("Orthophoto")) + "###orthophoto").c_str(), &m_orthophoto_window)) + render_orthophoto_ui(); + ImGui::End(); +} + +void editor_mode::render_view_menu() +{ + if (false == ImGui::BeginMenu(STR_C("View"))) + return; + if (ImGui::MenuItem(STR_C("Top view, orthographic"), "O", Global.EditorOrtho)) + 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")); + ImGui::MenuItem("Show lanes", nullptr, &m_roadtool.lanes); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("%s", "Draws the lanes of the roads with the ways the vehicles take through the junctions"); + if (ImGui::MenuItem(STR_C("Structure gauge"), nullptr, m_gauge.open)) + m_gauge.open = !m_gauge.open; + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("%s", STR_C("Checks which models enter the structure gauge of the tracks and the clearance over the roads (skrajnia budowli)")); + ImGui::Separator(); + ImGui::MenuItem(STR_C("Weather and time"), nullptr, &m_environment_open); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("%s", STR_C("Time of day, day of the year, visibility, clouds and air temperature, as the scenery is seen in the editor")); + ImGui::EndMenu(); +} + +void editor_mode::render_environment_window() +{ + if (false == m_environment_open) + return; + ImGui::SetNextWindowSize(ImVec2(420.0f * Global.ui_scale, 0.0f), ImGuiCond_FirstUseEver); + if (ImGui::Begin((std::string(STR_C("Weather and time")) + "###environment").c_str(), &m_environment_open, ImGuiWindowFlags_NoCollapse)) + { + // what the setdatetime, setweather and settemperature commands do in the driver mode; the editor doesn't take commands + auto const set_date_time = [](int const Yearday, int const Minute) { + simulation::Time.set_time(Yearday, Minute); + auto const weather{Global.Weather}; + simulation::Environment.compute_season(Yearday); + if (weather != Global.Weather) + { + // HACK: force re-calculation of precipitation, as the command does + Global.Overcast = std::clamp(Global.Overcast - 0.0001f, 0.0f, 2.0f); + } + simulation::Environment.update_moon(); + }; + // the names of the period, season and weather end with a colon, being keys of other lookups + auto const name = [](std::string Text) { + if (false == Text.empty() && Text.back() == ':') + Text.pop_back(); + return Text; + }; + auto const &time{simulation::Time.data()}; + auto minute{time.wHour * 60 + time.wMinute}; + auto const clock{format("%02d:%02d (%s)", time.wHour, time.wMinute, name(Global.Period).c_str())}; + if (ImGui::SliderInt((std::string(STR_C("Time of day")) + "###timeofday").c_str(), &minute, 0, 1439, clock.c_str())) + set_date_time(static_cast(std::round(Global.fMoveLight)), std::clamp(minute, 0, 1439)); + auto yearday{Global.fMoveLight}; + auto const day{format("%.0f (%s)", Global.fMoveLight, name(Global.Season).c_str())}; + if (ImGui::SliderFloat((std::string(STR_C("Day of the year")) + "###dayofyear").c_str(), &yearday, 0.0f, 364.0f, day.c_str())) + set_date_time(static_cast(std::round(std::clamp(yearday, 0.0f, 365.0f))), time.wHour * 60 + time.wMinute); + ImGui::Separator(); + auto visibility{std::log(Global.fFogEnd)}; + auto const fog{format("%.0f m", Global.fFogEnd)}; + if (ImGui::SliderFloat((std::string(STR_C("Visibility")) + "###visibility").c_str(), &visibility, std::log(10.0f), std::log(50000.0f), fog.c_str())) + { + Global.fFogEnd = std::clamp(std::exp(visibility), 10.0f, 50000.0f); + simulation::Environment.compute_weather(); + } + auto overcast{Global.Overcast}; + auto const clouds{format("%.2f (%s)", Global.Overcast, name(Global.Weather).c_str())}; + if (ImGui::SliderFloat((std::string(STR_C("Clouds")) + "###overcast").c_str(), &overcast, 0.0f, 2.0f, clouds.c_str())) + { + Global.Overcast = std::clamp(overcast, 0.0f, 2.0f); + simulation::Environment.compute_weather(); + } + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("%s", STR_C("Up to 1 the sky clouds over, past it rain or snow falls, by the air temperature")); + auto temperature{Global.AirTemperature}; + auto const air{format("%.1f deg C", Global.AirTemperature)}; + if (ImGui::SliderFloat((std::string(STR_C("Air temperature")) + "###airtemperature").c_str(), &temperature, -35.0f, 40.0f, air.c_str())) + { + Global.AirTemperature = std::clamp(temperature, -35.0f, 40.0f); + simulation::Environment.compute_weather(); + } + ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled)); + ImGui::TextWrapped("%s", STR_C("Seen in the editor only; the scenery keeps its own time and weather")); + ImGui::PopStyleColor(); + } + ImGui::End(); +} + void editor_mode::render_new_scenery_popup() { if (m_newscenery_asked) @@ -3062,18 +3078,8 @@ void editor_mode::terrain_workspace(bool const Open) void editor_mode::render_terrain_tool_options() { + // the orthophoto has a window of its own, see render_orthophoto_window() render_terrain_ui(); - bool const expand{m_orthophoto_expand}; - m_orthophoto_expand = false; - if (expand) - ImGui::SetNextItemOpen(true); - if (ImGui::CollapsingHeader(STR_C("Orthophoto"))) - { - // opened from the map menu: brought into sight, the terrain tools above them take the most of the window - if (expand) - ImGui::SetScrollHereY(0.0f); - render_orthophoto_ui(); - } } void editor_mode::render_object_menu() @@ -3864,6 +3870,7 @@ void editor_mode::render_terrain_toolbar() m_orthophoto.enabled(false == orthophoto); if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", STR_C("Aerial imagery of geoportal.gov.pl under the scenery, laid out by the origin of the scenery")); + ImGui::MenuItem(STR_C("Orthophoto settings..."), nullptr, &m_orthophoto_window); } void editor_mode::render_toolbar() @@ -3959,6 +3966,12 @@ void editor_mode::render_edit_menu() start_focus(m_node, 0.6); if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", STR_C("The camera flies to the selected model and looks at it")); + if (ImGui::MenuItem(STR_C("Find a track by its name"), "Ctrl+F")) + { + terrain_workspace(false); + show_track_tab(m_track_window_open ? m_track_tab : track_tab::path); + m_track_search.focus = true; + } if (ImGui::MenuItem(STR_C("Bend along the track"), "B", false, ui()->mode() != nodebank_panel::TRACK)) bend_shortcut(); if (ImGui::IsItemHovered()) diff --git a/application/editormode.h b/application/editormode.h index 45847a91..25ee8451 100644 --- a/application/editormode.h +++ b/application/editormode.h @@ -378,7 +378,13 @@ class editor_mode : public application_mode, private editor_track::observer std::vector m_openscenery_list; std::string m_openscenery_choice; void render_open_scenery_popup(); - bool m_orthophoto_expand{false}; // the orthophoto settings of the terrain tools are opened the next time they're drawn + bool m_orthophoto_window{false}; + void render_orthophoto_window(); + // view menu: the top view, the lanes of the roads, the structure gauge, the environment + void render_view_menu(); + // time of day, day of the year, visibility, clouds and air temperature, as the scenery is seen in the editor + void render_environment_window(); + bool m_environment_open{false}; // origin of the scenery in PUWG 1992 the scenery file gives in its //$g line, if it does bool m_georeference_read{false}; bool m_georeference{false}; @@ -894,7 +900,6 @@ class editor_mode : public application_mode, private editor_track::observer // the objects along the track: a tab for each tool int m_lineside_tab{0}; void render_lineside_ui(); - void render_track_menu(); void arm_switch(); bool m_track_window_open{false}; // the inspector is pinned to the right edge of the screen, the profile strip to the bottom one, both resized from the inner edge @@ -1814,7 +1819,6 @@ class editor_mode : public application_mode, private editor_track::observer char banktext[128]{}; char islandtext[128]{"none"}; }; - void render_road_menu(); void render_road_tool_options(); // tools of the roads in the toolbar, and the change of the tool void render_road_toolbar(); diff --git a/application/editormoderoad.cpp b/application/editormoderoad.cpp index 0ff5ef67..8c800d90 100644 --- a/application/editormoderoad.cpp +++ b/application/editormoderoad.cpp @@ -216,24 +216,6 @@ glm::dvec3 path_direction(segment_data const &Path, double const T) } -void editor_mode::render_road_menu() -{ - if (ImGui::BeginMenu("Roads")) - { - if (ImGui::MenuItem("Road editor", "F4", &m_roadtool.window) && m_roadtool.window) - { - m_track_window_open = false; - terrain_workspace(false); - } - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("%s", "The roads as the field of work: their tools in the toolbar, their settings in the tool options"); - ImGui::MenuItem("Show lanes", nullptr, &m_roadtool.lanes); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("%s", "Draws the lanes of the roads with the ways the vehicles take through the junctions"); - ImGui::EndMenu(); - } -} - void editor_mode::update_road_tool() { auto &tool{m_roadtool}; diff --git a/application/editormodetrack.cpp b/application/editormodetrack.cpp index 89d3b3e1..0172fbe9 100644 --- a/application/editormodetrack.cpp +++ b/application/editormodetrack.cpp @@ -580,35 +580,6 @@ std::array const &editor_mode::track_modes() return modes; } -void editor_mode::render_track_menu() -{ - // the tools of the tracks with their keys, as in the toolbar of the field of work - if (false == ImGui::BeginMenu(STR_C("Tracks"))) - return; - for (auto const &mode : track_modes()) - { - if (ImGui::MenuItem(STR_C(mode.label), mode.key, m_track_window_open && m_track_tab == mode.tab)) - { - terrain_workspace(false); - show_track_tab(mode.tab); - } - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("%s", STR_C(mode.tooltip)); - } - ImGui::Separator(); - if (ImGui::MenuItem(STR_C("Find a track by its name"), "Ctrl+F")) - { - terrain_workspace(false); - show_track_tab(m_track_window_open ? m_track_tab : track_tab::path); - m_track_search.focus = true; - } - if (ImGui::MenuItem(STR_C("Structure gauge"), nullptr, m_gauge.open)) - m_gauge.open = !m_gauge.open; - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("%s", STR_C("Checks which models enter the structure gauge of the tracks and the clearance over the roads (skrajnia budowli)")); - ImGui::EndMenu(); -} - void editor_mode::show_track_tab(track_tab const Tab) { m_track_window_open = true; diff --git a/application/editoruilayer.cpp b/application/editoruilayer.cpp index 79c265df..b5d7f462 100644 --- a/application/editoruilayer.cpp +++ b/application/editoruilayer.cpp @@ -96,7 +96,7 @@ void editor_ui::build_default_layout(unsigned int const Dockspace) ImGui::DockBuilderDockWindow("###tooloptions", lefttop); for (auto const *window : {"###scenetree", "###Layers", "###scenehierarchy"}) ImGui::DockBuilderDockWindow(window, leftbottom); - for (auto const *window : {"###inspector", "###editorsettings", "###Include database"}) + for (auto const *window : {"###inspector", "###editorsettings", "###Include database", "###orthophoto", "###environment"}) ImGui::DockBuilderDockWindow(window, right); for (auto const *window : {"###Node bank", "###profilestrip", "###trackanalysis", "###structuregauge", "###editorhistory", "###modelsets"}) ImGui::DockBuilderDockWindow(window, bottom); diff --git a/application/scenarioloaderuilayer.cpp b/application/scenarioloaderuilayer.cpp index de0d4e64..207a1756 100644 --- a/application/scenarioloaderuilayer.cpp +++ b/application/scenarioloaderuilayer.cpp @@ -193,6 +193,8 @@ void scenarioloader_ui::render_() ImVec2 screen_size(Global.window_size.x, Global.window_size.y); ImGui::SetNextWindowPos(ImVec2(-padding, -padding)); ImGui::SetNextWindowSize(ImVec2(Global.window_size.x + padding * 2, Global.window_size.y + padding * 2)); + // it reaches past the edges of the main window, which would make it a window of its own with the panels able to leave the main one + ImGui::SetNextWindowViewport(ImGui::GetMainViewport()->ID); 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); diff --git a/lang/editor_pl.po b/lang/editor_pl.po index bec33efc..e8b593be 100644 --- a/lang/editor_pl.po +++ b/lang/editor_pl.po @@ -4989,3 +4989,38 @@ msgstr "Co można z nim zrobić" msgid "What the handles of the gizmo on the selection do when dragged; Ctrl held snaps the values" msgstr "Co robią uchwyty gizma na zaznaczeniu przy przeciąganiu; z Ctrl wartości są przyciągane" + +# scenery editor: view menu, weather and time + +msgid "View" +msgstr "Widok" + +msgid "Weather and time" +msgstr "Pogoda i czas" + +msgid "Time of day, day of the year, visibility, clouds and air temperature, as the scenery is seen in the editor" +msgstr "Godzina, dzień roku, widoczność, zachmurzenie i temperatura powietrza, z jakimi sceneria jest widoczna w edytorze" + +msgid "Time of day" +msgstr "Godzina" + +msgid "Day of the year" +msgstr "Dzień roku" + +msgid "Visibility" +msgstr "Widoczność" + +msgid "Clouds" +msgstr "Zachmurzenie" + +msgid "Up to 1 the sky clouds over, past it rain or snow falls, by the air temperature" +msgstr "Do 1 niebo się chmurzy, powyżej pada deszcz albo śnieg, zależnie od temperatury powietrza" + +msgid "Air temperature" +msgstr "Temperatura powietrza" + +msgid "Seen in the editor only; the scenery keeps its own time and weather" +msgstr "Widoczne tylko w edytorze; sceneria zachowuje własny czas i pogodę" + +msgid "Top view, orthographic (O)" +msgstr "Widok z góry, ortogonalny (O)"