MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
MeshNetwork.cpp
Go to the documentation of this file.
1#include "MeshNetwork.hpp"
2
6
8
9// =============================================================================
10// Construction
11// =============================================================================
12
19
20// =============================================================================
21// Slot management
22// =============================================================================
23
25 std::string name,
26 std::shared_ptr<GpuSync::MeshWriterNode> node,
27 std::optional<uint32_t> parent)
28{
29 if (!node) {
30 error<std::invalid_argument>(
32 std::source_location::current(),
33 "MeshNetwork::add_slot: null MeshWriterNode for slot '{}'", name);
34 }
35
36 if (parent.has_value() && parent.value() >= m_slots.size()) {
37 error<std::out_of_range>(
39 std::source_location::current(),
40 "MeshNetwork::add_slot: parent index {} out of range (slot count: {})",
41 parent.value(), m_slots.size());
42 }
43
44 const auto idx = static_cast<uint32_t>(m_slots.size());
45
46 MeshSlot slot;
47 slot.index = idx;
48 slot.name = std::move(name);
49 slot.node = std::move(node);
50 slot.parent_index = parent;
51 slot.dirty = true;
52
53 if (parent.has_value()) {
54 m_slots[parent.value()].child_indices.push_back(idx);
55 }
56
57 m_slots.push_back(std::move(slot));
58 m_sort_dirty = true;
59
61 "MeshNetwork: added slot '{}' at index {} (parent: {})",
62 m_slots[idx].name, idx,
63 parent.has_value() ? std::to_string(parent.value()) : "none");
64
65 return idx;
66}
67
69{
70 return m_slots.at(index);
71}
72
73const MeshSlot& MeshNetwork::get_slot(uint32_t index) const
74{
75 return m_slots.at(index);
76}
77
79{
80 auto it = std::ranges::find_if(m_slots,
81 [name](const MeshSlot& s) { return s.name == name; });
82 return it != m_slots.end() ? &*it : nullptr;
83}
84
85const MeshSlot* MeshNetwork::find_slot(std::string_view name) const
86{
87 auto it = std::ranges::find_if(m_slots,
88 [name](const MeshSlot& s) { return s.name == name; });
89 return it != m_slots.end() ? &*it : nullptr;
90}
91
92std::optional<uint32_t> MeshNetwork::find_slot_index(std::string_view name) const
93{
94 for (uint32_t i = 0; i < static_cast<uint32_t>(m_slots.size()); ++i) {
95 if (m_slots[i].name == name)
96 return i;
97 }
98 return std::nullopt;
99}
100
101// =============================================================================
102// NodeNetwork interface
103// =============================================================================
104
105void MeshNetwork::process_batch(unsigned int num_samples)
106{
107 if (!is_enabled() || m_slots.empty())
108 return;
109
110 if (m_sort_dirty)
111 rebuild_sort();
112
113 const float dt = get_timestep();
114
115 for (unsigned int frame = 0; frame < num_samples; ++frame) {
117
118 if (m_operator) {
119 if (auto* mesh_op = dynamic_cast<MeshOperator*>(m_operator.get()))
120 mesh_op->set_slots(m_slots, m_sorted_indices);
121 m_operator->process(dt);
122 }
123
124 if (m_operator_chain) {
125 for (const auto& op : m_operator_chain->operators()) {
126 if (auto* mesh_op = dynamic_cast<MeshOperator*>(op.get()))
127 mesh_op->set_slots(m_slots, m_sorted_indices);
128 op->process(dt);
129 }
130 }
131 }
132
133 for (const auto& slot : m_slots) {
134 if (slot.node)
135 slot.node->compute_frame();
136 }
137}
138
140{
141 for (auto& slot : m_slots) {
142 slot.local_transform = glm::mat4(1.0F);
143 slot.world_transform = glm::mat4(1.0F);
144 slot.dirty = true;
145 }
146}
147
148std::optional<double> MeshNetwork::get_node_output(size_t index) const
149{
150 if (index >= m_slots.size())
151 return std::nullopt;
152 return static_cast<double>(index);
153}
154
155std::unordered_map<std::string, std::string> MeshNetwork::get_metadata() const
156{
157 auto meta = NodeNetwork::get_metadata();
158 meta["slot_count"] = std::to_string(m_slots.size());
159 meta["operator"] = m_operator ? std::string(m_operator->get_type_name()) : "none";
160 meta["chain_size"] = m_operator_chain
161 ? std::to_string(m_operator_chain->size())
162 : "0";
163 return meta;
164}
165
166// =============================================================================
167// Operator management
168// =============================================================================
169
170void MeshNetwork::set_operator(std::shared_ptr<NetworkOperator> op)
171{
172 if (!op) {
174 "MeshNetwork::set_operator: null operator ignored");
175 return;
176 }
177 m_operator = std::move(op);
178}
179
181{
182 if (m_sort_dirty)
183 rebuild_sort();
184}
185
186// =============================================================================
187// Private helpers
188// =============================================================================
189
191{
192 // Roots: slots with no parent.
193 std::vector<uint32_t> queue;
194 for (uint32_t i = 0; i < static_cast<uint32_t>(m_slots.size()); ++i) {
195 if (!m_slots[i].parent_index.has_value())
196 queue.push_back(i);
197 }
198
199 m_sorted_indices.clear();
200 m_sorted_indices.reserve(m_slots.size());
201
202 while (!queue.empty()) {
203 uint32_t idx = queue.front();
204 queue.erase(queue.begin());
205 m_sorted_indices.push_back(idx);
206
207 for (uint32_t child : m_slots[idx].child_indices)
208 queue.push_back(child);
209 }
210
211 if (m_sorted_indices.size() != m_slots.size()) {
213 "MeshNetwork: slot DAG contains a cycle — sort is incomplete");
214 }
215
216 m_sort_dirty = false;
217
219 "MeshNetwork: rebuilt slot sort order ({} slots)", m_sorted_indices.size());
220}
221
223{
224 for (uint32_t idx : m_sorted_indices) {
225 auto& slot = m_slots[idx];
226 if (slot.parent_index.has_value()) {
227 slot.world_transform
228 = m_slots[slot.parent_index.value()].world_transform
229 * slot.local_transform;
230 } else {
231 slot.world_transform = slot.local_transform;
232 }
233 }
234}
235
236std::optional<Kakshya::MeshData> MeshNetwork::get_mesh_data() const
237{
238 auto mesh_data = Kakshya::MeshData::empty();
239 Kakshya::MeshInsertion ins(mesh_data.vertex_variant, mesh_data.index_variant);
240
241 for (const auto& slot : m_slots) {
242 if (!slot.node) {
243 continue;
244 }
245
246 const auto packed = slot.node->get_mesh_data();
247 if (!packed) {
248 continue;
249 }
250
251 ins.insert_submesh(
252 std::get<std::vector<uint8_t>>(packed->vertex_variant),
253 std::get<std::vector<uint32_t>>(packed->index_variant),
254 slot.name.empty() ? std::to_string(slot.index) : slot.name,
255 {},
256 packed->layout);
257 }
258
259 const auto access = ins.build();
260 if (!access) {
261 return std::nullopt;
262 }
263 mesh_data.layout = access->layout;
264 mesh_data.submeshes = access->submeshes;
265
266 return mesh_data;
267}
268
269} // namespace MayaFlux::Nodes::Network
#define MF_ERROR(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
std::vector< uint32_t > parent
std::string name
Definition VKDevice.cpp:123
uint32_t index
Definition VKDevice.cpp:122
void insert_submesh(std::span< const uint8_t > vertex_bytes, std::span< const uint32_t > index_data, std::string_view name={}, std::string_view material_name={}, const VertexLayout &layout=VertexLayout::for_meshes(60))
Append one submesh batch, accumulating into the shared buffers.
std::optional< MeshAccess > build() const
Produce a MeshAccess over the current variant contents.
Write counterpart to MeshAccess.
MeshSlot & get_slot(uint32_t index)
Return the slot at the given index.
std::vector< uint32_t > m_sorted_indices
Processing order: indices into m_slots, parents before children.
bool m_sort_dirty
Set when add_slot() changes the DAG and a re-sort is needed.
void set_operator(std::shared_ptr< NetworkOperator > op)
Replace the primary operator.
std::optional< Kakshya::MeshData > get_mesh_data() const
Every slot's geometry merged into one MeshData, one submesh per slot.
std::unordered_map< std::string, std::string > get_metadata() const override
Get network metadata for debugging/visualization.
uint32_t add_slot(std::string name, std::shared_ptr< GpuSync::MeshWriterNode > node, std::optional< uint32_t > parent=std::nullopt)
Add a slot to the network.
std::shared_ptr< NetworkOperator > m_operator
void process_batch(unsigned int num_samples) override
Process the network for the given number of samples.
void ensure_sorted()
Ensure the slot processing order is up to date.
std::optional< uint32_t > find_slot_index(std::string_view name) const
Find the index of a slot by name.
MeshSlot * find_slot(std::string_view name)
Find a slot by name.
std::optional< double > get_node_output(size_t index) const override
Get output of specific internal node (for ONE_TO_ONE mapping)
void reset() override
Reset network to initial state.
Abstract base for operators that process MeshNetwork slots.
virtual void set_topology(Topology topology)
Set the network's topology.
bool is_enabled() const
Check if network is enabled.
float get_timestep() const
Seconds one visual frame advances this network.
std::shared_ptr< OperatorChain > m_operator_chain
virtual std::unordered_map< std::string, std::string > get_metadata() const
Get network metadata for debugging/visualization.
void set_output_mode(OutputMode mode)
Set the network's output routing mode.
@ Init
Engine/subsystem initialization.
@ Nodes
DSP Generator and Filter Nodes, graph pipeline, node management.
@ CUSTOM
User-defined arbitrary topology.
@ GRAPHICS_BIND
State available for visualization (read-only)
static MeshData empty()
Construct an empty MeshData with the canonical 60-byte mesh layout.
Definition MeshData.hpp:49
std::string name
Logical name. Used for lookup and logging only.
Definition MeshSlot.hpp:36
bool dirty
Set when local_transform or slot config changes since last upload.
Definition MeshSlot.hpp:54
std::shared_ptr< GpuSync::MeshWriterNode > node
Geometry node for this slot.
Definition MeshSlot.hpp:39
uint32_t index
Position of this slot in MeshNetwork::m_slots. Stable after insertion.
Definition MeshSlot.hpp:33
std::optional< uint32_t > parent_index
Index of the parent slot, or nullopt for a root slot.
Definition MeshSlot.hpp:48
Named, independently transformable mesh unit within a MeshNetwork.
Definition MeshSlot.hpp:31