MayaFlux 0.2.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
GeometryWriterNode.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "GpuSync.hpp"
5
7
8/**
9 * @class GeometryContext
10 * @brief Context for GeometryWriterNode - provides vertex buffer access
11 */
12class MAYAFLUX_API GeometryContext : public NodeContext, public GpuStructuredData {
13public:
14 GeometryContext(double value,
15 std::span<const uint8_t> vertex_data,
16 size_t vertex_stride,
17 uint32_t vertex_count)
18 : NodeContext(value, typeid(GeometryContext).name())
19 , GpuStructuredData(vertex_data, vertex_stride, vertex_count)
20 , vertex_count(vertex_count)
21 , vertex_stride(vertex_stride)
22 {
23 }
24
25 uint32_t vertex_count;
27
28protected:
29 friend class GeometryWriterNode;
30};
31
32/**
33 * @class GeometryWriterNode
34 * @brief Base class for nodes that generate 3D geometry data
35 *
36 * Analogous to TextureNode but for vertex geometry instead of pixel data.
37 * Each frame (at VISUAL_RATE), compute_frame() is called to generate new vertex data.
38 * Vertex data is stored as a flat array in configurable interleaved or non-interleaved format.
39 *
40 * Subclasses implement compute_frame() to populate m_vertex_buffer with vertex data.
41 * The buffer follows a user-defined stride (bytes per vertex) allowing flexible vertex formats:
42 * - Positions only (3 floats = 12 bytes per vertex)
43 * - Positions + Colors (3 + 3 floats = 24 bytes per vertex)
44 * - Positions + Normals + Texcoords (3 + 3 + 2 floats = 32 bytes per vertex)
45 * - Any custom interleaved format
46 *
47 * Usage:
48 * ```cpp
49 * class MyParticleSystem : public GeometryWriterNode {
50 * void compute_frame() override {
51 * std::vector<glm::vec3> positions;
52 * for (int i = 0; i < 1000; i++) {
53 * positions.push_back(generate_particle(i));
54 * }
55 *
56 * set_vertex_stride(sizeof(glm::vec3));
57 * resize_vertex_buffer(positions.size());
58 * memcpy(m_vertex_buffer.data(), positions.data(),
59 * positions.size() * sizeof(glm::vec3));
60 * m_vertex_count = positions.size();
61 * }
62 * };
63 * ```
64 */
65class MAYAFLUX_API GeometryWriterNode : public GpuSync {
66protected:
67 /// @brief Vertex data buffer (flat array of bytes)
68 std::vector<uint8_t> m_vertex_buffer;
69
70 /// @brief Number of vertices in buffer
71 uint32_t m_vertex_count {};
72
73 /// @brief Bytes per vertex (stride for vertex buffer binding)
74 size_t m_vertex_stride {};
75
76 /// @brief Cached vertex layout for descriptor binding
77 std::optional<Kakshya::VertexLayout> m_vertex_layout;
78
79 /// @brief Flag indicating if layout needs update
80 bool m_needs_layout_update {};
81
82 /**
83 * @brief Flag: vertex data or layout changed since last GPU upload
84 *
85 * Set to true whenever compute_frame() modifies vertex buffer or layout.
86 * Checked by GeometryBindingsProcessor before staging GPU transfer.
87 * Cleared by processor after successful upload.
88 */
89 bool m_vertex_data_dirty { true };
90
91public:
92 /**
93 * @brief Constructor
94 * @param initial_capacity Initial number of vertices to reserve space for
95 */
96 GeometryWriterNode(uint32_t initial_capacity = 1024);
97
98 ~GeometryWriterNode() override = default;
99
100 /**
101 * @brief Process batch for geometry generation
102 * @param num_samples Number of frames to process
103 * @return Empty vector (geometry nodes don't produce audio output)
104 *
105 * Calls compute_frame() once per batch.
106 * Subclasses can override for custom batch behavior.
107 */
108 std::vector<double> process_batch(unsigned int num_samples) override;
109
110 /**
111 * @brief Get raw vertex buffer data
112 * @return Span of float data (interpreted as bytes, stride-aligned)
113 */
114 [[nodiscard]] std::span<const uint8_t> get_vertex_data() const { return m_vertex_buffer; }
115
116 /**
117 * @brief Get vertex buffer size in bytes
118 * @return Total size of vertex data buffer
119 */
120 [[nodiscard]] size_t get_vertex_buffer_size_bytes() const;
121
122 /**
123 * @brief Get number of vertices
124 * @return Current vertex count
125 */
126 [[nodiscard]] uint32_t get_vertex_count() const { return m_vertex_count; }
127
128 /**
129 * @brief Get stride (bytes per vertex)
130 * @return Stride in bytes
131 */
132 [[nodiscard]] size_t get_vertex_stride() const { return m_vertex_stride; }
133
134 /**
135 * @brief Set vertex stride (bytes per vertex)
136 * @param stride Stride in bytes
137 *
138 * Must be called before filling buffer. Common values:
139 * - 12 (vec3 positions only)
140 * - 24 (vec3 positions + vec3 colors)
141 * - 32 (vec3 positions + vec3 normals + vec2 texcoords)
142 */
143 void set_vertex_stride(size_t stride);
144
145 /**
146 * @brief Resize vertex buffer to hold specified number of vertices
147 * @param vertex_count Number of vertices
148 * @param preserve_data If true, keep existing data; if false, clear buffer
149 *
150 * Allocates/reallocates buffer to hold vertex_count vertices at current stride.
151 * If stride is 0, logs error and does nothing.
152 */
153 void resize_vertex_buffer(uint32_t vertex_count, bool preserve_data = false);
154
155 /**
156 * @brief Copy raw vertex data into buffer
157 * @param data Pointer to vertex data
158 * @param size_bytes Number of bytes to copy
159 *
160 * Direct memory copy into m_vertex_buffer.
161 * User is responsible for ensuring data format matches stride.
162 */
163 void set_vertex_data(const void* data, size_t size_bytes);
164
165 /**
166 * @brief Set a single vertex by index
167 * @param vertex_index Index of vertex to set
168 * @param data Pointer to vertex data
169 * @param size_bytes Size of vertex data (should match stride)
170 *
171 * Copies size_bytes starting at (vertex_index * stride).
172 */
173 void set_vertex(uint32_t vertex_index, const void* data, size_t size_bytes);
174
175 /**
176 * @brief Get a single vertex by index
177 * @param vertex_index Index of vertex
178 * @return Span covering this vertex's data
179 */
180 [[nodiscard]] std::span<const uint8_t> get_vertex(uint32_t vertex_index) const;
181
182 /**
183 * @brief Set multiple vertices from typed array
184 * @tparam T Vertex type
185 * @param vertices Span of vertex data
186 *
187 * Sets vertex stride to sizeof(T), resizes buffer, and copies data.
188 */
189 template <typename T>
190 void set_vertices(std::span<const T> vertices)
191 {
192 set_vertex_stride(sizeof(T));
193 resize_vertex_buffer(vertices.size());
194 std::memcpy(m_vertex_buffer.data(), vertices.data(),
195 vertices.size() * sizeof(T));
196 m_vertex_count = static_cast<uint32_t>(vertices.size());
197 m_needs_layout_update = true;
198 m_vertex_data_dirty = true;
199 }
200
201 /**
202 * @brief Set a single vertex by index from typed data
203 * @tparam T Vertex type
204 * @param index Index of vertex to set
205 * @param vertex Vertex data
206 */
207 template <typename T>
208 void set_vertex_typed(uint32_t index, const T& vertex)
209 {
210 set_vertex(index, &vertex, sizeof(T));
211 }
212
213 /**
214 * @brief Get a single vertex by index as typed data
215 * @tparam T Vertex type
216 * @param index Index of vertex
217 * @return Vertex data of type T
218 */
219 template <typename T>
220 T get_vertex_typed(uint32_t index) const
221 {
222 auto data = get_vertex(index);
223 if (data.size_bytes() < sizeof(T)) {
224 return T {};
225 }
226 T result;
227 std::memcpy(&result, data.data(), sizeof(T));
228 return result;
229 }
230
231 /**
232 * @brief Clear vertex buffer and reset count
233 */
234 void clear();
235
236 /**
237 * @brief Clear vertex buffer and resize to specified count
238 * @param vertex_count Number of vertices
239 */
240 void clear_and_resize(uint32_t vertex_count);
241
242 /**
243 * @brief Set cached vertex layout
244 * @param layout VertexLayout describing attribute structure
245 *
246 * Called internally or by GeometryBindingsProcessor to describe
247 * the semantic meaning of vertex data (positions, colors, normals, etc.)
248 */
249 void set_vertex_layout(const Kakshya::VertexLayout& layout) { m_vertex_layout = layout; }
250
251 /**
252 * @brief Get cached vertex layout
253 * @return Optional containing layout if set
254 */
255 [[nodiscard]] std::optional<Kakshya::VertexLayout> get_vertex_layout() const { return m_vertex_layout; }
256
257 /**
258 * @brief Check if layout needs update
259 * @return True if stride or vertex count changed
260 */
261 [[nodiscard]] bool needs_layout_update() const { return m_needs_layout_update; }
262
263 /**
264 * @brief Clear layout update flag
265 */
266 void clear_layout_update_flag() { m_needs_layout_update = false; }
267
268 /**
269 * @brief Save current geometry state
270 *
271 * Saves vertex buffer, count, stride, and layout.
272 * Subclasses can override to save additional state.
273 */
274 void save_state() override;
275
276 /**
277 * @brief Restore saved geometry state
278 *
279 * Restores vertex buffer, count, stride, and layout.
280 * Subclasses can override to restore additional state.
281 */
282 void restore_state() override;
283
284 /**
285 * @brief Check if vertex data or layout changed since last GPU sync
286 * @return True if m_vertex_buffer or m_vertex_layout has been modified
287 *
288 * For geometry, this combines data mutation (vertex_buffer changed) and
289 * structural change (stride or layout changed).
290 */
291 [[nodiscard]] bool needs_gpu_update() const override
292 {
293 return m_vertex_data_dirty || m_needs_layout_update;
294 }
295
296 /**
297 * @brief Clear the dirty flag after GPU upload completes
298 *
299 * Called by GeometryBindingsProcessor after it stages the vertex data
300 * into a GPU transfer buffer and submits the command.
301 */
302 void clear_gpu_update_flag() override
303 {
304 m_vertex_data_dirty = false;
305 m_needs_layout_update = false;
306 }
307
308 /**
309 * @brief Retrieves the last created context object
310 * @return Reference to the last GeometryContext object
311 *
312 * This method provides access to the most recent GeometryContext object
313 * created by the geometry writer node. This context contains information
314 * about the node's state at the time of the last output generation.
315 */
316 NodeContext& get_last_context() override;
317
318 /**
319 * @brief Updates the context object with the current node state
320 * @param value The current sample value
321 */
322 void update_context(double value) override;
323
324protected:
325 GeometryContext m_context { 0.0, {}, 0, 0 };
326
327private:
329 std::vector<uint8_t> vertex_buffer;
330 uint32_t vertex_count;
332 std::optional<Kakshya::VertexLayout> vertex_layout;
333 };
334
335 std::optional<GeometryState> m_saved_state;
336};
337
338} // namespace MayaFlux::Nodes
GPU-uploadable structured data (arrays of POD structs)
GeometryContext(double value, std::span< const uint8_t > vertex_data, size_t vertex_stride, uint32_t vertex_count)
Context for GeometryWriterNode - provides vertex buffer access.
uint32_t get_vertex_count() const
Get number of vertices.
std::vector< uint8_t > m_vertex_buffer
Vertex data buffer (flat array of bytes)
void clear_gpu_update_flag() override
Clear the dirty flag after GPU upload completes.
bool needs_layout_update() const
Check if layout needs update.
std::optional< Kakshya::VertexLayout > get_vertex_layout() const
Get cached vertex layout.
void set_vertex_layout(const Kakshya::VertexLayout &layout)
Set cached vertex layout.
void set_vertices(std::span< const T > vertices)
Set multiple vertices from typed array.
std::optional< Kakshya::VertexLayout > m_vertex_layout
Cached vertex layout for descriptor binding.
void clear_layout_update_flag()
Clear layout update flag.
size_t get_vertex_stride() const
Get stride (bytes per vertex)
bool needs_gpu_update() const override
Check if vertex data or layout changed since last GPU sync.
void set_vertex_typed(uint32_t index, const T &vertex)
Set a single vertex by index from typed data.
std::span< const uint8_t > get_vertex_data() const
Get raw vertex buffer data.
T get_vertex_typed(uint32_t index) const
Get a single vertex by index as typed data.
Base class for nodes that generate 3D geometry data.
Base context class for node callbacks.
Definition Node.hpp:30
Complete description of vertex data layout in a buffer.