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

feat(editor): labels for a layman, light at the start; whole sceneries fitted to the terrain model

Labels:
- every button, field, tooltip and message of the editor reread for a beginner: short nouns for the fields with the
  sentence in the tooltip, the less used options under More settings, one glossary (field of work, template, track,
  grade), sides told from directions, degrees and per mille as signs, messages built from one format string
- plural forms through STR_P; the last entry of a po file is read too, and the Polish plural rule is in the header
- lang/editor_pl.po: the code pass entries merged, 391 entries no code uses and 3 duplicates dropped
- roads: the notes and errors naming a road translated; the build options and the layout fold the texture length,
  kerb sizes, traffic share, roundabout part, height over the ground and junction speed limit under More settings, the
  lanes start folded

Grade lines fitted to the terrain model (NMT), a line or the whole scenery at once:
- fit_ground: a robust fit on nodes 5 m apart; the ground parting from the line (a bridge, a viaduct, a wall) is gone
  past rather than followed, the bends keep off the switches as far as their curves reach, a run of bends the same way
  is one point of intersection, a curve with no room is as sharp as the room lets it be
- the ends of the line and the other paths of its switches leading into lines fitted already bound the line by what
  the tracks between can climb; ends meeting in the plan, laid apart in height by hand, are joined before the run
- apply_profile: the paths are split where split_path takes the parameter (a straight was cut in the wrong place, into
  pieces of a few centimetres the game takes for zero length), no piece shorter than the margin; the paths off the
  ends of the route are brought to its height
- Bytom: 1600 lines, grade breaks at the joints 89 -> 20; Tarnowskie Gory: 848 lines, 223 -> 3; no step over 2 cm
This commit is contained in:
maj00r
2026-10-19 15:27:50 +02:00
parent 5a00d88be9
commit ff1710d9ea
78 changed files with 15011 additions and 4386 deletions

View File

@@ -1370,17 +1370,34 @@ void terrain_streamer::water_material(std::string const &Material)
m_waterchanged = true;
}
std::vector<std::pair<glm::dvec2, glm::dvec2>> terrain_streamer::modifier_bounds() const
{
std::vector<std::pair<glm::dvec2, glm::dvec2>> result;
result.reserve(m_manifest.modifiers.size());
for (auto const &modifier : m_manifest.modifiers)
result.push_back(modifier.bounds());
return result;
}
void terrain_streamer::apply_modifiers(editor_terrain &Chunk, glm::dvec2 const &Min, glm::dvec2 const &Max)
{
apply_modifiers(Chunk, Min, Max, modifier_bounds());
}
void terrain_streamer::apply_modifiers(editor_terrain &Chunk, glm::dvec2 const &Min, glm::dvec2 const &Max, std::vector<std::pair<glm::dvec2, glm::dvec2>> const &Bounds)
{
auto const &current{Chunk.data()};
auto const low{current.corner()};
auto const high{low + glm::dvec2{heightmap::chunk_size}};
std::vector<heightmap::modifier_shape> shapes;
shapes.reserve(m_manifest.modifiers.size());
bool reachesany{false};
for (auto const &modifier : m_manifest.modifiers)
for (std::size_t i = 0; i < m_manifest.modifiers.size(); ++i)
{
heightmap::modifier_shape shape{modifier};
// the shape of a modifier far away isn't worth making
auto const &[from, to]{Bounds[i]};
if (to.x < low.x || from.x > high.x || to.y < low.y || from.y > high.y)
continue;
heightmap::modifier_shape shape{m_manifest.modifiers[i]};
if (false == shape.reaches(low, high))
continue;
reachesany = true;
@@ -1418,18 +1435,84 @@ void terrain_streamer::apply_modifiers(editor_terrain &Chunk, glm::dvec2 const &
for (int ix = xs.first; ix <= xs.second; ++ix)
{
auto const index{static_cast<std::size_t>(iz) * data.side() + ix};
auto height{data.base[index]};
auto const ground{static_cast<double>(data.base[index])};
auto const x{low.x + ix * step}, z{low.y + iz * step};
// the same for any order of the modifiers: within the formation of a track its formation, of the nearest one where two
// overlap; elsewhere the slopes, the highest fill and the lowest cut, the nearer of the two where both reach
std::optional<heightmap::modifier_shape::effect> bed, fill, cut;
for (auto const &shape : shapes)
shape.apply(x, z, height);
data.heights[index] = touchups ? height + data.adjust[index] : height;
{
auto const effect{shape.evaluate(x, z, ground)};
if (false == effect.has_value())
continue;
if (effect->outside <= 0.0)
{
if (false == bed.has_value() || effect->distance < bed->distance)
bed = effect;
}
else if (effect->height > ground)
{
if (false == fill.has_value() || effect->height > fill->height)
fill = effect;
}
else if (effect->height < ground && (false == cut.has_value() || effect->height < cut->height))
cut = effect;
}
auto height{ground};
if (bed.has_value())
height = bed->height;
else if (fill.has_value() && cut.has_value())
height = fill->outside <= cut->outside ? fill->height : cut->height;
else if (fill.has_value())
height = fill->height;
else if (cut.has_value())
height = cut->height;
data.heights[index] = static_cast<float>(touchups ? height + data.adjust[index] : height);
}
}
void terrain_streamer::apply_modifiers(glm::dvec2 const &Min, glm::dvec2 const &Max)
{
auto const bounds{modifier_bounds()};
for (auto *chunk : chunks_in(Min, Max, true))
apply_modifiers(*chunk, Min, Max);
apply_modifiers(*chunk, Min, Max, bounds);
}
void terrain_streamer::modifiers(std::vector<heightmap::modifier> Modifiers)
{
if (Modifiers.empty())
return;
remember_manifest();
std::vector<std::pair<glm::dvec2, glm::dvec2>> areas;
for (auto &modifier : Modifiers)
{
auto bounds{modifier.bounds()};
auto const existing{std::find_if(m_manifest.modifiers.begin(), m_manifest.modifiers.end(), [&](heightmap::modifier const &Item) { return Item.name == modifier.name; })};
if (existing != m_manifest.modifiers.end())
{
auto const old{existing->bounds()};
bounds.first = glm::min(bounds.first, old.first);
bounds.second = glm::max(bounds.second, old.second);
*existing = std::move(modifier);
}
else
m_manifest.modifiers.push_back(std::move(modifier));
areas.push_back(bounds);
}
m_manifestchanged = true;
// every chunk reached, once, whole
std::vector<editor_terrain *> chunks;
for (auto const &[from, to] : areas)
for (auto *chunk : chunks_in(from, to, true))
chunks.push_back(chunk);
std::sort(chunks.begin(), chunks.end());
chunks.erase(std::unique(chunks.begin(), chunks.end()), chunks.end());
auto const bounds{modifier_bounds()};
for (auto *chunk : chunks)
{
auto const low{chunk->data().corner()};
apply_modifiers(*chunk, low, low + glm::dvec2{heightmap::chunk_size}, bounds);
}
}
void terrain_streamer::modifier(heightmap::modifier Modifier)
@@ -1451,6 +1534,19 @@ void terrain_streamer::modifier(heightmap::modifier Modifier)
apply_modifiers(bounds.first, bounds.second);
}
std::size_t terrain_streamer::drop_modifiers(std::string const &Prefix)
{
auto &list{m_manifest.modifiers};
auto const matches = [&](heightmap::modifier const &Item) { return Item.name.rfind(Prefix, 0) == 0; };
auto const count{static_cast<std::size_t>(std::count_if(list.begin(), list.end(), matches))};
if (count == 0)
return 0;
remember_manifest();
list.erase(std::remove_if(list.begin(), list.end(), matches), list.end());
m_manifestchanged = true;
return count;
}
bool terrain_streamer::remove_modifier(std::string const &Name)
{
auto const existing{std::find_if(m_manifest.modifiers.begin(), m_manifest.modifiers.end(), [&](heightmap::modifier const &Item) { return Item.name == Name; })};