MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
PathOperator.cpp
Go to the documentation of this file.
1#include "PathOperator.hpp"
3
5
6//-----------------------------------------------------------------------------
7// Construction
8//-----------------------------------------------------------------------------
9
12 Eigen::Index samples_per_segment)
13 : m_default_mode(mode)
14 , m_default_samples_per_segment(samples_per_segment)
15{
17 "PathOperator created with mode {}, {} samples per segment",
18 static_cast<int>(mode), samples_per_segment);
19}
20
21//-----------------------------------------------------------------------------
22// GraphicsOperator Interface (Simple Initialization)
23//-----------------------------------------------------------------------------
24
25void PathOperator::initialize(const std::vector<LineVertex>& vertices)
26{
27 if (vertices.empty()) {
29 "Cannot initialize PathOperator with zero vertices");
30 return;
31 }
32
33 m_paths.clear();
35
37 "PathOperator initialized with {} control vertices", vertices.size());
38}
39
40//-----------------------------------------------------------------------------
41// Advanced Initialization (Multiple Paths)
42//-----------------------------------------------------------------------------
43
45 const std::vector<std::vector<LineVertex>>& paths,
47{
48 for (const auto& path : paths) {
50 }
51
53 "PathOperator initialized with {} paths",
54 paths.size());
55}
56
58 const std::vector<LineVertex>& control_vertices,
59 Kinesis::InterpolationMode mode, uint32_t default_samples_per_segment, size_t max_control_points, double tension)
60{
61 if (control_vertices.empty()) {
63 "Cannot add path with zero control vertices");
64 return;
65 }
66
67 auto path = std::make_shared<GpuSync::PathGeneratorNode>(
68 mode,
69 default_samples_per_segment,
70 max_control_points,
71 tension);
72
73 path->set_control_points(control_vertices);
74 path->set_path_thickness(m_default_thickness);
75 path->compute_frame();
76
77 uint32_t expected = 0;
78 while (!m_access_token.compare_exchange_weak(expected, 1,
79 std::memory_order_acquire, std::memory_order_relaxed)) {
80 if (m_shutdown.load(std::memory_order_relaxed))
81 return;
82 expected = 0;
83 }
84
85 m_paths.push_back(std::move(path));
86
87 m_access_token.store(0, std::memory_order_release);
88
90 "Added path #{} with {} control vertices, {} generated vertices",
91 m_paths.size(), control_vertices.size(),
92 m_paths.back()->get_generated_vertex_count());
93}
94
95void PathOperator::add_node(std::shared_ptr<GpuSync::PathGeneratorNode> node)
96{
97 if (!node) {
99 "PathOperator::add_node: null node ignored");
100 return;
101 }
102
103 node->compute_frame();
104
105 uint32_t expected = 0;
106 while (!m_access_token.compare_exchange_weak(expected, 1,
107 std::memory_order_acquire, std::memory_order_relaxed)) {
108 if (m_shutdown.load(std::memory_order_relaxed))
109 return;
110 expected = 0;
111 }
112
113 m_paths.push_back(std::move(node));
114
115 m_access_token.store(0, std::memory_order_release);
116
118 "PathOperator: added node #{} ({} vertices)",
119 m_paths.size(), m_paths.back()->get_vertex_count());
120}
121
122//-----------------------------------------------------------------------------
123// Processing
124//-----------------------------------------------------------------------------
125
126void PathOperator::process(float /*dt*/)
127{
128 uint32_t expected = 0;
129 while (!m_access_token.compare_exchange_weak(expected, 1,
130 std::memory_order_acquire, std::memory_order_relaxed)) {
131 if (m_shutdown.load(std::memory_order_relaxed))
132 return;
133 expected = 0;
134 }
135
136 for (auto& path : m_paths) {
137 path->compute_frame();
138 }
139
140 m_access_token.store(0, std::memory_order_release);
141}
142
143//-----------------------------------------------------------------------------
144// GraphicsOperator Interface (Data Extraction)
145//-----------------------------------------------------------------------------
146
147std::vector<LineVertex> PathOperator::extract_vertices() const
148{
149 std::vector<LineVertex> positions;
150
151 for (const auto& path : m_paths) {
152 const auto points = path->get_control_points();
153 positions.insert(positions.end(), points.rbegin(), points.rend());
154 }
155
156 return positions;
157}
158
159std::span<const uint8_t> PathOperator::get_vertex_data_for_collection(uint32_t idx) const
160{
161 if (m_paths.empty() || idx >= m_paths.size()) {
162 return {};
163 }
164 return m_paths[idx]->get_vertex_data();
165}
166
167std::span<const uint8_t> PathOperator::get_vertex_data() const
168{
170 for (const auto& group : m_paths) {
171 auto span = group->get_vertex_data();
174 span.begin(), span.end());
175 }
176 return { m_vertex_data_aggregate.data(), m_vertex_data_aggregate.size() };
177}
178
180{
181 if (m_paths.empty()) {
182 return {};
183 }
184
185 auto layout_opt = m_paths[0]->get_vertex_layout();
186 if (!layout_opt) {
187 return {};
188 }
189
190 auto layout = *layout_opt;
191 layout.vertex_count = static_cast<uint32_t>(get_vertex_count());
192 return layout;
193}
194
195std::optional<Portal::Graphics::PrimitiveTopology> PathOperator::declared_topology() const
196{
197 if (m_paths.empty()) {
198 return std::nullopt;
199 }
200 return m_paths[0]->get_primitive_topology();
201}
202
204{
205 size_t total = 0;
206 for (const auto& path : m_paths) {
207 total += path->get_vertex_count();
208 }
209 return total;
210}
211
213{
214 return std::ranges::any_of(
215 m_paths,
216 [](const auto& group) { return group->needs_gpu_update(); });
217}
218
220{
221 for (auto& path : m_paths) {
222 path->clear_gpu_update_flag();
223 }
224}
225
226std::vector<GraphicsOperator::DirtyVertexRange> PathOperator::dirty_vertex_ranges() const
227{
228 std::vector<DirtyVertexRange> ranges;
229
230 uint32_t offset = 0;
231 uint32_t index = 0;
232 for (const auto& path : m_paths) {
233 const auto count = static_cast<uint32_t>(path->get_vertex_count());
234 if (path->needs_gpu_update()) {
235 ranges.push_back(DirtyVertexRange {
237 .vertex_offset = offset,
238 .vertex_count = count });
239 }
240 offset += count;
241 ++index;
242 }
243
244 return ranges;
245}
246
247std::vector<DrawRun> PathOperator::topology_runs() const
248{
249 std::vector<DrawRun> runs;
250 uint32_t offset = 0;
251
252 for (const auto& path : m_paths) {
253 const auto count = static_cast<uint32_t>(path->get_vertex_count());
254 const auto topo = path->get_primitive_topology();
255
256 if (!runs.empty() && runs.back().topology == topo && is_concatenable_topology(topo)) {
257 runs.back().vertex_count += count;
258 } else {
259 runs.push_back(DrawRun { .topology = topo, .vertex_offset = offset, .vertex_count = count });
260 }
261
262 offset += count;
263 }
264
265 return runs;
266}
267
269{
270 size_t total = 0;
271 for (const auto& path : m_paths) {
272 total += path->get_all_vertex_count();
273 }
274 return total;
275}
276
277std::vector<uint32_t> PathOperator::build_cluster_ids() const
278{
279 std::vector<uint32_t> ids(get_vertex_count(), 0U);
280
281 if (m_paths.size() <= 1) {
282 return ids;
283 }
284
285 size_t offset = 0;
286 uint32_t cluster = 0;
287 for (const auto& path : m_paths) {
288 const size_t count = path->get_vertex_count();
289 for (size_t i = 0; i < count && offset + i < ids.size(); ++i) {
290 ids[offset + i] = cluster;
291 }
292 offset += count;
293 ++cluster;
294 }
295
296 return ids;
297}
298
299//-----------------------------------------------------------------------------
300// Parameter Control
301//-----------------------------------------------------------------------------
302
303void PathOperator::set_parameter(std::string_view param, double value)
304{
305 if (param == "tension") {
306 for (auto& path : m_paths) {
307 path->set_tension(value);
308 }
309 } else if (param == "samples_per_segment") {
310 m_default_samples_per_segment = static_cast<Eigen::Index>(value);
311 for (auto& path : m_paths) {
312 path->set_samples_per_segment(static_cast<Eigen::Index>(value));
313 }
314 } else if (param == "thickness") {
315 m_default_thickness = static_cast<float>(value);
316 for (auto& path : m_paths) {
317 path->set_path_thickness(m_default_thickness);
318 }
319 }
320}
321
322std::optional<double> PathOperator::query_state(std::string_view query) const
323{
324 if (query == "control_point_count") {
325 size_t total = 0;
326 for (const auto& path : m_paths) {
327 total += path->get_control_point_count();
328 }
329 return static_cast<double>(total);
330 }
331 if (query == "vertex_count") {
332 return static_cast<double>(get_vertex_count());
333 }
334 if (query == "path_count") {
335 return static_cast<double>(m_paths.size());
336 }
337 return std::nullopt;
338}
339
340//-----------------------------------------------------------------------------
341// Path Configuration
342//-----------------------------------------------------------------------------
343
345{
347 for (auto& path : m_paths) {
348 path->set_samples_per_segment(samples);
349 }
350}
351
352void PathOperator::set_tension(double tension)
353{
354 for (auto& path : m_paths) {
355 path->set_tension(tension);
356 }
357}
358
360{
361 m_default_thickness = thickness;
362 for (auto& path : m_paths) {
363 path->set_path_thickness(thickness);
364 }
365}
366
368{
369 for (auto& path : m_paths) {
370 path->set_path_color(color);
371 }
372}
373
374void PathOperator::set_vertices(const std::vector<LineVertex>& vertices)
375{
376 size_t total = 0;
377 for (const auto& path : m_paths) {
378 total += path->get_control_point_count();
379 }
380
381 if (m_paths.empty() || vertices.size() != total) {
382 initialize(vertices);
383 return;
384 }
385
386 uint32_t expected = 0;
387 while (!m_access_token.compare_exchange_weak(expected, 1,
388 std::memory_order_acquire, std::memory_order_relaxed)) {
389 if (m_shutdown.load(std::memory_order_relaxed))
390 return;
391 expected = 0;
392 }
393
394 auto first = vertices.begin();
395 for (auto& path : m_paths) {
396 const auto count = static_cast<std::ptrdiff_t>(path->get_control_point_count());
397 path->set_control_points(std::vector<LineVertex>(first, first + count));
398 path->compute_frame();
399 first += count;
400 }
401
402 m_access_token.store(0, std::memory_order_release);
403}
404
405void* PathOperator::get_data_at(size_t /*global_index*/)
406{
407 return nullptr;
408}
409
410} // namespace MayaFlux::Nodes::Network
#define MF_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
uint64_t offset
Definition Composite.cpp:10
glm::vec3 color
Definition Decoder.cpp:97
std::vector< glm::vec2 > * points
uint32_t total
uint32_t mode
std::vector< uint64_t > ids
std::vector< glm::vec3 > positions
glm::vec3 value
uint32_t index
Definition VKDevice.cpp:122
size_t count
void set_parameter(std::string_view param, double value) override
Set operator parameter.
void * get_data_at(size_t global_index) override
Get mutable access to point at global index.
std::vector< DrawRun > topology_runs() const override
Coalesces adjacent paths sharing a concatenable topology into one run.
std::vector< uint8_t > m_vertex_data_aggregate
Kinesis::InterpolationMode m_default_mode
std::vector< DirtyVertexRange > dirty_vertex_ranges() const override
One DirtyVertexRange per path whose PathGeneratorNode needs a GPU update, so add_control_point() on o...
void add_path(const std::vector< LineVertex > &control_vertices, Kinesis::InterpolationMode mode, uint32_t default_samples_per_segment=32, size_t max_control_points=64, double tension=0.5)
Add a new path with given control points and properties.
bool is_vertex_data_dirty() const override
Check if geometry changed this frame.
void initialize_paths(const std::vector< std::vector< LineVertex > > &paths, Kinesis::InterpolationMode mode)
Initialize multiple paths with given control points and properties.
std::vector< std::shared_ptr< GpuSync::PathGeneratorNode > > m_paths
void add_node(std::shared_ptr< GpuSync::PathGeneratorNode > node)
Add an externally constructed PathGeneratorNode subtype to the operator.
void set_tension(double tension)
Set the tension parameter for all paths (if supported by mode).
size_t get_vertex_count() const override
Get number of vertices (may differ from point count for topology/path)
PathOperator(Kinesis::InterpolationMode mode=Kinesis::InterpolationMode::CATMULL_ROM, Eigen::Index samples_per_segment=32)
size_t get_point_count() const override
Get source point count (before topology expansion)
Kakshya::VertexLayout get_vertex_layout() const override
Get vertex layout describing vertex structure.
void set_samples_per_segment(Eigen::Index samples)
Set the number of samples per segment for all paths.
std::optional< Portal::Graphics::PrimitiveTopology > declared_topology() const override
Forwards to the first path's own get_primitive_topology().
void set_global_color(const glm::vec3 &color)
Set the global color tint for all paths.
void set_vertices(const std::vector< LineVertex > &vertices)
Replace every control point, in extract_vertices() order, keeping the paths.
void initialize(const std::vector< LineVertex > &vertices)
Initialize a single path with given control points and properties.
void process(float dt) override
Process for one batch cycle.
std::vector< uint32_t > build_cluster_ids() const override
Overrides GraphicsOperator::build_cluster_ids() for real multi-path scenes.
void mark_vertex_data_clean() override
Clear dirty flag after GPU upload.
std::span< const uint8_t > get_vertex_data_for_collection(uint32_t idx) const override
Get vertex data for specific collection (if multiple)
std::vector< LineVertex > extract_vertices() const
Extract current vertex data as LineVertex array.
void set_global_thickness(float thickness)
Set the global thickness for all paths.
std::span< const uint8_t > get_vertex_data() const override
Get vertex data for GPU upload.
std::optional< double > query_state(std::string_view query) const override
Query operator internal state.
void initialize()
Definition main.cpp:10
@ NodeProcessing
Node graph processing (Nodes::NodeGraphManager)
@ Nodes
DSP Generator and Filter Nodes, graph pipeline, node management.
InterpolationMode
Mathematical interpolation methods.
uint32_t vertex_count
Total number of vertices in this buffer.
Complete description of vertex data layout in a buffer.
A contiguous span of vertex records that changed since the last mark_vertex_data_clean().
One contiguous span of vertices sharing a primitive topology.