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

Editor terrain: texture browser, undo, conversion from terrain files

- Terrain work mode shows a browser of the textures folder in place of the
  node bank; textures/materials can be dragged onto the palette entries
  (replacing the material), the material field and the water material field
- Ctrl+Z/Ctrl+Y for heightmap terrain changes: a brush stroke or an
  operation is one step of the editor history
- Ground conversion reads only the files of the terrain directives
  (.txtf with origin/rotate, or .btf without text), paints the chunks with
  the materials of the triangles, drops only the shapes of those files
- Fix: ground grids converted out-of-range floats to int, so whole-scene
  queries returned the triangles of a single cell (shifted chunks)
- Fix: terrain layers copied from textures already uploaded without their
  data stayed grey
- Fix: ImGui ID conflict between terrain tool radios and section headers
- Fix: layers of terrain .txtf files named with the scenery/ prefix and
  could not be saved
- Editor selftest commands for the terrain
This commit is contained in:
2026-10-09 23:39:26 +02:00
parent 5716bc1669
commit 8cd0bab27b
21 changed files with 1211 additions and 145 deletions

View File

@@ -674,54 +674,84 @@ void editor_mode::profile_restore_ground()
void editor_mode::render_ground_conversion()
{
auto &conversion{m_ground_conversion};
ImGui::PushID("ground_conversion");
if (false == conversion.open)
{
if (ImGui::Button(STR_C("Convert the scenery ground to heightmap terrain...")))
if (ImGui::Button(STR_C("Convert the terrain files to heightmap terrain...")))
{
conversion.open = true;
conversion.materials = ground_materials();
conversion.chosen.clear();
// the material with the most triangles is the ground, most likely
auto const most{std::max_element(conversion.materials.begin(), conversion.materials.end(), [](auto const &A, auto const &B) { return A.second < B.second; })};
if (most != conversion.materials.end())
conversion.chosen.insert(most->first);
load_ground_files();
}
if (ImGui::IsItemHovered())
ImGui::SetTooltip("%s", STR_C("Replaces the triangles of the chosen materials with heightmap terrain of the same shape,\nwhich can be shaped, sculpted and painted; the scenery files lose these triangles on save"));
ImGui::SetTooltip("%s", STR_C("Replaces the triangles of the terrain files of the scenery (terrain directive, .txtf/.btf) with heightmap terrain\n"
"of the same shape, painted with their materials; the files lose these triangles on save"));
if (false == conversion.status.empty())
ImGui::TextWrapped("%s", conversion.status.c_str());
ImGui::PopID();
return;
}
ImGui::TextWrapped("%s", STR_C("Materials of the scenery triangles: tick the ones which are the ground"));
if (conversion.materials.empty())
ImGui::TextDisabled("%s", STR_C("The scenery has no triangles"));
for (auto const &entry : conversion.materials)
if (conversion.files.empty())
ImGui::TextWrapped("%s", STR_C("The scenery has no terrain files: no terrain directive names a .txtf or .btf file"));
else
ImGui::TextUnformatted(STR_C("Terrain files of the scenery:"));
for (std::size_t index = 0; index < conversion.files.size(); ++index)
{
auto const *material{GfxRenderer->Material(entry.first)};
auto const name{material != nullptr ? material->GetName() : std::string{"?"}};
bool chosen{conversion.chosen.count(entry.first) != 0};
ImGui::PushID(static_cast<int>(entry.first));
if (ImGui::Checkbox(format(STR_C("%s (%zu triangles)"), name.c_str(), entry.second).c_str(), &chosen))
auto &source{conversion.files[index]};
ImGui::PushID(static_cast<int>(index));
ImGui::BeginDisabled(false == source.good);
ImGui::Checkbox(source.file.c_str(), &source.chosen);
ImGui::EndDisabled();
ImGui::SameLine();
if (source.good)
ImGui::TextDisabled(source.text ? STR_C("(text, %zu triangles)") : STR_C("(binary, %zu triangles)"), source.count);
else
ImGui::TextDisabled("(%s)", source.message.c_str());
ImGui::PopID();
}
// the materials of the chosen files
std::vector<std::size_t> counts(conversion.names.size(), 0);
for (auto const &source : conversion.files)
if (source.good && source.chosen)
for (auto index = source.first; index < source.first + source.count; ++index)
++counts[conversion.materials[index]];
if (false == conversion.names.empty())
ImGui::TextUnformatted(STR_C("Materials: tick the ones which are the ground"));
for (std::size_t index = 0; index < conversion.names.size(); ++index)
{
if (counts[index] == 0)
continue;
bool chosen{conversion.chosen.count(index) != 0};
ImGui::PushID(static_cast<int>(index) + 0x10000);
if (ImGui::Checkbox(format(STR_C("%s (%zu triangles)"), conversion.names[index].c_str(), counts[index]).c_str(), &chosen))
{
if (chosen)
conversion.chosen.insert(entry.first);
conversion.chosen.insert(index);
else
conversion.chosen.erase(entry.first);
conversion.chosen.erase(index);
}
ImGui::PopID();
}
ImGui::TextWrapped("%s", STR_C("The chunks are covered with the material which covers the most, added to the palette of the terrain. Until saved, loading the scenery again takes it all back."));
if (conversion.chosen.empty())
ImGui::TextDisabled("%s", STR_C("Tick at least one material"));
else if (ImGui::Button(STR_C("Convert")))
ImGui::TextWrapped("%s", STR_C("The chunks are painted with the materials of the triangles, up to 8 of them per chunk, added to the palette of the terrain. "
"Until saved, loading the scenery again takes it all back."));
auto const ready{std::any_of(conversion.files.begin(), conversion.files.end(), [](auto const &Source) { return Source.good && Source.chosen; }) && false == conversion.chosen.empty()};
ImGui::BeginDisabled(false == ready);
if (ImGui::Button(STR_C("Convert")))
{
conversion.status = convert_ground(conversion.chosen);
conversion.status = convert_ground();
conversion.open = false;
profile_resample();
}
ImGui::EndDisabled();
ImGui::SameLine();
if (ImGui::Button(STR_C("Cancel")))
conversion.open = false;
if (false == conversion.open)
{
// the triangles take memory, they're read again the next time
conversion.triangles = {};
conversion.materials = {};
}
ImGui::PopID();
}
void editor_mode::render_profile_earthworks()