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

feat(editor): edit session (-edit) and scenery layers

This commit is contained in:
2026-10-02 21:29:18 +02:00
parent 69f5f551a1
commit 73f2f68aa0
19 changed files with 560 additions and 3 deletions

194
scene/scenelayers.cpp Normal file
View File

@@ -0,0 +1,194 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#include "stdafx.h"
#include "scene/scenelayers.h"
#include "simulation/simulation.h"
#include "model/AnimModel.h"
#include "world/Track.h"
#include "world/Traction.h"
namespace scene
{
node_layers Layers;
std::size_t basic_layer::item_count() const
{
return std::accumulate(std::begin(items), std::end(items), std::size_t{0});
}
// indicates start of content of specified scenery file. returns: handle to the layer of the file
layer_handle node_layers::open(std::string const &Name)
{
// the same file can be included more than once, all its content goes to a single layer then
auto const lookup{std::find_if(std::begin(m_layers), std::end(m_layers), [&](basic_layer const &Layer) { return Layer.name == Name; })};
auto layerhandle{static_cast<layer_handle>(std::distance(std::begin(m_layers), lookup) + 1)};
if (lookup == std::end(m_layers))
{
if (m_layers.size() < static_cast<std::size_t>(std::numeric_limits<layer_handle>::max()))
{
m_layers.emplace_back();
m_layers.back().name = Name;
m_layers.back().parent = handle();
}
else
{
// out of handles, content of the file is attributed to the parent layer
layerhandle = handle();
}
}
// NOTE: the stack receives an entry even if the layer wasn't created, to keep it in sync with close() calls
m_stack.emplace_back(layerhandle);
return layerhandle;
}
// indicates end of content of the current scenery file. returns: handle to the parent layer, or null_handle if the stack is empty
layer_handle node_layers::close()
{
if (false == m_stack.empty())
{
m_stack.pop_back();
}
return handle();
}
void node_layers::clear()
{
m_layers.clear();
m_stack.clear();
m_active = null_handle;
}
int node_layers::depth(layer_handle Layer) const
{
auto depth{0};
// NOTE: parent is always created before its children, which rules out cycles
while (valid(Layer) && layer(Layer).parent != null_handle && layer(Layer).parent < Layer)
{
Layer = layer(Layer).parent;
++depth;
}
return depth;
}
void node_layers::count(layer_handle const Layer, layer_item const Item, int const Change)
{
if (false == valid(Layer))
{
return;
}
auto &itemcount{layer(Layer).items[static_cast<std::size_t>(Item)]};
if (Change >= 0)
{
itemcount += static_cast<std::size_t>(Change);
}
else
{
itemcount -= std::min(itemcount, static_cast<std::size_t>(-Change));
}
}
void node_layers::active(layer_handle const Layer)
{
if (false == valid(Layer))
{
return;
}
// new items land in the active layer, they'd be out of reach right away if it was hidden or locked
visible(Layer, true);
locked(Layer, false);
m_active = Layer;
}
// shows or hides nodes of specified layer. visibility defined in the scenery is preserved, hidden layer only overrides it
void node_layers::visible(layer_handle const Layer, bool const Visible)
{
if (false == valid(Layer) || layer(Layer).visible == Visible)
{
return;
}
layer(Layer).visible = Visible;
auto const update_node = [Layer, Visible](basic_node *Node) {
if (Node == nullptr || Node->layer() != Layer)
{
return;
}
if (Visible)
{
if (Node->m_layerhidden)
{
Node->visible(true);
Node->m_layerhidden = false;
}
}
else if (Node->visible())
{
// nodes which are invisible on their own aren't marked, so they stay that way when the layer is shown again
Node->visible(false);
Node->m_layerhidden = true;
}
};
// only these node types are drawn by the renderers basing on their visibility flag
for (auto *instance : simulation::Instances.sequence())
{
update_node(instance);
}
for (auto *path : simulation::Paths.sequence())
{
update_node(path);
}
for (auto *traction : simulation::Traction.sequence())
{
update_node(traction);
}
}
void node_layers::locked(layer_handle const Layer, bool const Locked)
{
if (valid(Layer))
{
layer(Layer).locked = Locked;
}
}
// true if nodes of specified layer can be selected and modified in the editor
bool node_layers::editable(layer_handle const Layer) const
{
// nodes without a layer (regular load, or created by the simulation) aren't restricted
return false == valid(Layer) || (layer(Layer).visible && false == layer(Layer).locked);
}
std::vector<layer_handle> node_layers::hidden() const
{
std::vector<layer_handle> layers;
for (std::size_t idx = 0; idx < m_layers.size(); ++idx)
{
if (false == m_layers[idx].visible)
{
layers.emplace_back(static_cast<layer_handle>(idx + 1));
}
}
return layers;
}
void node_layers::show_all()
{
for (auto const hiddenlayer : hidden())
{
visible(hiddenlayer, true);
}
}
} // namespace scene
//---------------------------------------------------------------------------

117
scene/scenelayers.h Normal file
View File

@@ -0,0 +1,117 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "scene/scenenode.h"
namespace scene
{
// kinds of scenery items a layer keeps count of
enum class layer_item
{
model,
track,
traction,
powersource,
memcell,
launcher,
event,
vehicle,
sound,
shape,
lines,
count_
};
// scenery layer: content of a single scenery file (.scn, .scm, .ctr...) other than an .inc node template
struct basic_layer
{
std::string name; // file name, relative to the scenery directory
layer_handle parent{null_handle}; // layer of the file this one was included from
bool visible{true};
bool locked{false}; // items of locked layer can't be selected in the editor
bool binary{false}; // the file wasn't parsed, its content came from binary terrain file
std::array<std::size_t, static_cast<std::size_t>(layer_item::count_)> items{}; // amount of items of each kind
std::size_t item_count() const;
};
// holds scenery layers, established during load of scenery opened for editing (-edit)
// NOTE: regular load doesn't create any layers, all nodes are left with null_handle
class node_layers
{
public:
// methods
// indicates start of content of specified scenery file. returns: handle to the layer of the file
layer_handle open(std::string const &Name);
// indicates end of content of the current scenery file. returns: handle to the parent layer, or null_handle if the stack is empty
layer_handle close();
// returns layer of the scenery file being loaded, or null_handle if there's none
layer_handle handle() const
{
return m_stack.empty() ? null_handle : m_stack.back();
}
// removes all layers; layer handles stored in the nodes aren't touched
void clear();
bool empty() const
{
return m_layers.empty();
}
std::size_t size() const
{
return m_layers.size();
}
bool valid(layer_handle const Layer) const
{
return Layer != null_handle && Layer <= m_layers.size();
}
// grants direct access to specified layer. NOTE: handle has to be valid
basic_layer &layer(layer_handle const Layer)
{
return m_layers[Layer - 1];
}
basic_layer const &layer(layer_handle const Layer) const
{
return m_layers[Layer - 1];
}
// number of layers between specified layer and the scenario file
int depth(layer_handle Layer) const;
// adjusts item count of specified layer
void count(layer_handle const Layer, layer_item const Item, int const Change = 1);
// layer receiving the items created in the editor, or null_handle if there's none
layer_handle active() const
{
return m_active;
}
// NOTE: active layer is made visible and unlocked
void active(layer_handle const Layer);
// shows or hides nodes of specified layer. visibility defined in the scenery is preserved, hidden layer only overrides it
void visible(layer_handle const Layer, bool const Visible);
void locked(layer_handle const Layer, bool const Locked);
// true if nodes of specified layer can be selected and modified in the editor
bool editable(layer_handle const Layer) const;
// handles of layers which are currently hidden
std::vector<layer_handle> hidden() const;
// restores visibility of all layers
void show_all();
private:
// members
std::vector<basic_layer> m_layers; // layer handle is index in the vector + 1
std::vector<layer_handle> m_stack; // helper, layers of scenery files being loaded
layer_handle m_active{null_handle};
};
extern node_layers Layers;
} // namespace scene
//---------------------------------------------------------------------------

View File

@@ -717,7 +717,8 @@ memory_node::deserialize( cParser &Input, node_data const &Nodedata ) {
basic_node::basic_node( scene::node_data const &Nodedata ) :
m_name( Nodedata.name )
m_name( Nodedata.name ),
m_layer( Nodedata.layer )
{
uuid = UID::random();
node_type = Nodedata.type;

View File

@@ -70,6 +70,7 @@ struct node_data {
double range_max { std::numeric_limits<double>::max() };
std::string name;
std::string type;
layer_handle layer { null_handle }; // scenery layer the node is defined in, if any
};
// holds unique piece of geometry, covered with single material
@@ -359,6 +360,10 @@ public:
group( scene::group_handle Group );
scene::group_handle
group() const;
void
layer( scene::layer_handle Layer );
scene::layer_handle
layer() const;
void
mark_dirty() { m_dirty = true; }
bool
@@ -375,6 +380,8 @@ public:
bool m_visible { true }; // visibility flag
std::string m_name;
bool m_dirty { false };
bool m_layerhidden { false }; // the node was visible until its scenery layer was hidden
scene::layer_handle m_layer { null_handle }; // scenery layer this node belongs to, if any
UID uuid;
private:
@@ -443,6 +450,18 @@ basic_node::group() const {
return m_group;
}
inline
void
basic_node::layer( scene::layer_handle Layer ) {
m_layer = Layer;
}
inline
scene::layer_handle
basic_node::layer() const {
return m_layer;
}
} // scene
//---------------------------------------------------------------------------