mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-17 02:49:19 +02:00
feat(terrain): grade line fitted to the ground under the route within the limits; editor terrain shaped to the grade line with a formation and rounded slopes, with one step back; scenery ground triangles of chosen materials converted to editor terrain and taken out of the files on save, with a dated backup of each file; crack-free simplified terrain mesh, terrain streamed as far as the scenery is drawn, the nearest chunks first; smoothing brush
This commit is contained in:
@@ -129,6 +129,8 @@ void node_layers::clear()
|
||||
m_context = layer_context();
|
||||
m_sources.clear();
|
||||
m_erased.clear();
|
||||
m_shapes.clear();
|
||||
m_shapeserased = false;
|
||||
m_templates.clear();
|
||||
m_instances.clear();
|
||||
m_instance = 0;
|
||||
@@ -475,6 +477,38 @@ void node_layers::forget(basic_node const *Node)
|
||||
m_sources.erase(lookup);
|
||||
}
|
||||
|
||||
void node_layers::shape(material_handle const Material, source_span const &Span)
|
||||
{
|
||||
auto const current{handle()};
|
||||
if (false == valid(current) || false == Span.valid() || m_instance != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_shapes.push_back({current, Span, Material, false});
|
||||
}
|
||||
|
||||
std::pair<std::size_t, std::size_t> node_layers::erase_shapes(std::set<material_handle> const &Materials)
|
||||
{
|
||||
std::pair<std::size_t, std::size_t> result{0, 0};
|
||||
for (auto &shape : m_shapes)
|
||||
{
|
||||
if (shape.erased || Materials.count(shape.material) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (false == writable(shape.layer))
|
||||
{
|
||||
++result.second;
|
||||
continue;
|
||||
}
|
||||
shape.erased = true;
|
||||
m_erased.emplace_back(shape.layer, shape.span);
|
||||
m_shapeserased = true;
|
||||
++result.first;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool node_layers::is_descendant(layer_handle Layer, layer_handle const Ancestor) const
|
||||
{
|
||||
// NOTE: content of a merged layer, includes of other files among it, belongs to the layer it was merged into
|
||||
|
||||
@@ -135,10 +135,20 @@ struct node_source
|
||||
glm::vec3 scale{1.f};
|
||||
};
|
||||
|
||||
// definition of a shape (triangles) in a scenery layer file
|
||||
struct shape_source
|
||||
{
|
||||
layer_handle layer{null_handle};
|
||||
source_span span;
|
||||
material_handle material{null_handle};
|
||||
bool erased{false}; // the definition goes from the file on save
|
||||
};
|
||||
|
||||
struct save_result
|
||||
{
|
||||
bool success{false};
|
||||
std::vector<std::string> files; // names of saved files
|
||||
std::vector<std::string> backups; // copies of the files made before they lost their ground
|
||||
std::string message; // summary, or cause of the failure
|
||||
};
|
||||
|
||||
@@ -249,6 +259,15 @@ class node_layers
|
||||
void track(basic_node const *Node, source_span const &Span, glm::vec3 const &Angles = glm::vec3{0.f}, glm::vec3 const &Scale = glm::vec3{1.f});
|
||||
// indicates specified node was removed from the scene
|
||||
void forget(basic_node const *Node);
|
||||
// stores location of a shape defined directly in the layer file being loaded
|
||||
void shape(material_handle const Material, source_span const &Span);
|
||||
std::vector<shape_source> const &shapes() const
|
||||
{
|
||||
return m_shapes;
|
||||
}
|
||||
// drops the definitions of the shapes of specified materials from the writable layer files on save.
|
||||
// returns: amount of the shapes to drop, and of the ones which stay as their files can't be rewritten
|
||||
std::pair<std::size_t, std::size_t> erase_shapes(std::set<material_handle> const &Materials);
|
||||
// indicates the scenario initialization (FirstInit) is being performed. Span: location of the directive.
|
||||
// Infile: the directive comes straight from the layer file being loaded
|
||||
void initialization(source_span const &Span, bool Infile);
|
||||
@@ -354,6 +373,8 @@ class node_layers
|
||||
layer_context m_context; // placement in effect at the current point of scenery load
|
||||
std::unordered_map<basic_node const *, node_source> m_sources; // origins of nodes which can be rewritten on save
|
||||
std::vector<std::pair<layer_handle, source_span>> m_erased; // definitions of nodes deleted since the load or the last save
|
||||
std::vector<shape_source> m_shapes;
|
||||
bool m_shapeserased{false}; // the binary terrain file holds shapes which the save drops
|
||||
std::set<std::string> m_templates; // *.inc templates used by the scenery
|
||||
std::vector<include_instance> m_instances; // includes of the templates; instance handle is index in the vector + 1
|
||||
instance_handle m_instance{0}; // helper, include whose template is being loaded
|
||||
|
||||
@@ -1451,6 +1451,34 @@ save_result node_layers::save(std::vector<std::string> const &Rootstatements)
|
||||
}
|
||||
written.emplace_back(output);
|
||||
}
|
||||
// a file which loses its ground to the terrain of the editor keeps a copy of itself each time, whatever was saved before.
|
||||
// without the copy nothing is replaced
|
||||
{
|
||||
auto const now{std::time(nullptr)};
|
||||
char stamp[32];
|
||||
std::strftime(stamp, sizeof(stamp), "%Y%m%d-%H%M%S", std::localtime(&now));
|
||||
for (auto const output : written)
|
||||
{
|
||||
auto const filepath{path(output)};
|
||||
bool const losesground{std::any_of(std::begin(m_shapes), std::end(m_shapes), [&](shape_source const &Shape) { return Shape.erased && Shape.layer == output; })};
|
||||
if (false == losesground || false == std::filesystem::exists(filepath, error))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
auto const backup{filepath + ".bak-" + stamp + "-przed-terenem-edytora"};
|
||||
std::error_code copyerror;
|
||||
if (false == std::filesystem::copy_file(filepath, backup, std::filesystem::copy_options::overwrite_existing, copyerror))
|
||||
{
|
||||
for (auto const leftover : written)
|
||||
{
|
||||
std::filesystem::remove(path(leftover) + ".tmp", error);
|
||||
}
|
||||
return fail("can't make a backup of file \"" + layer(output).name + "\": " + copyerror.message());
|
||||
}
|
||||
WriteLog("Scenery save: the triangles of the ground go from \"" + layer(output).name + "\", its previous content is kept in \"" + backup + "\"");
|
||||
result.backups.emplace_back(backup);
|
||||
}
|
||||
}
|
||||
std::vector<layer_handle> replaced;
|
||||
for (auto const output : written)
|
||||
{
|
||||
@@ -1586,6 +1614,8 @@ save_result node_layers::save(std::vector<std::string> const &Rootstatements)
|
||||
included.dead = included.dead || included.removed;
|
||||
}
|
||||
m_erased.clear();
|
||||
// the shapes of the rewritten files are somewhere else in them now
|
||||
m_shapes.erase(std::remove_if(std::begin(m_shapes), std::end(m_shapes), [&](shape_source const &Shape) { return std::find(std::begin(composed), std::end(composed), Shape.layer) != std::end(composed); }), std::end(m_shapes));
|
||||
for (auto *path : savedpaths)
|
||||
{
|
||||
path->m_dirty = false;
|
||||
@@ -1622,6 +1652,8 @@ save_result node_layers::save(std::vector<std::string> const &Rootstatements)
|
||||
{
|
||||
m_sources.erase(point);
|
||||
}
|
||||
removedterrain |= m_shapeserased;
|
||||
m_shapeserased = false;
|
||||
if (removedterrain)
|
||||
{
|
||||
// binary terrain file still holds geometry of the dropped layer, have it rebuilt on the next load
|
||||
@@ -1649,6 +1681,10 @@ save_result node_layers::save(std::vector<std::string> const &Rootstatements)
|
||||
{
|
||||
result.message += file + (&file != &result.files.back() ? ", " : "");
|
||||
}
|
||||
for (auto const &backup : result.backups)
|
||||
{
|
||||
result.message += ". Backup before the ground was taken out: " + std::filesystem::path(backup).filename().string();
|
||||
}
|
||||
}
|
||||
if (false == unwritten.empty())
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user