MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
GraphicsOperator.hpp
Go to the documentation of this file.
1#pragma once
2
4#include "NetworkOperator.hpp"
5
7
8/**
9 * @class GraphicsOperator
10 * @brief Operator that produces GPU-renderable geometry
11 *
12 * Adds graphics-specific interface (vertex data, position extraction)
13 * on top of base NetworkOperator. Uses glm::vec3 for positions since
14 * that's the graphics domain standard.
15 */
16class MAYAFLUX_API GraphicsOperator : public NetworkOperator {
17public:
18 /**
19 * @brief Get vertex data for GPU upload
20 */
21 [[nodiscard]] virtual std::span<const uint8_t> get_vertex_data() const = 0;
22
23 /**
24 * @brief Get vertex data for specific collection (if multiple)
25 * @param idx Collection index
26 */
27 [[nodiscard]] virtual std::span<const uint8_t> get_vertex_data_for_collection(uint32_t idx = 0) const = 0;
28
29 /**
30 * @brief Get vertex layout describing vertex structure
31 */
32 [[nodiscard]] virtual Kakshya::VertexLayout get_vertex_layout() const = 0;
33
34 /**
35 * @brief Get number of vertices (may differ from point count for topology/path)
36 */
37 [[nodiscard]] virtual size_t get_vertex_count() const = 0;
38
39 /**
40 * @brief Check if geometry changed this frame
41 */
42 [[nodiscard]] virtual bool is_vertex_data_dirty() const = 0;
43
44 /**
45 * @brief Clear dirty flag after GPU upload
46 */
47 virtual void mark_vertex_data_clean() = 0;
48
49 /**
50 * @struct DirtyVertexRange
51 * @brief A contiguous span of vertex records that changed since the last
52 * mark_vertex_data_clean().
53 *
54 * group_index selects which vertex-array pack the span's bytes come from,
55 * for a consumer that reads one pack at a time through
56 * get_vertex_data_for_collection(); it is 0 for an operator holding a
57 * single pack. vertex_offset and vertex_count are in global vertex-index
58 * order, the same concatenation order get_vertex_data() and
59 * build_cluster_ids() produce.
60 */
62 uint32_t group_index {};
63 uint32_t vertex_offset {};
64 uint32_t vertex_count {};
65 };
66
67 /**
68 * @brief Vertex sub-ranges that changed since the last
69 * mark_vertex_data_clean().
70 * @return One entry per contiguous dirty region, in global vertex-index
71 * order, or empty when nothing changed.
72 *
73 * The default reports a single range spanning every vertex whenever
74 * is_vertex_data_dirty() is true, so a consumer that uploads per-range
75 * stays identical to one that re-uploads the whole buffer. An operator
76 * holding several independent vertex-array packs (PathOperator's paths,
77 * TopologyOperator's graphs) overrides this to report only the packs
78 * whose own geometry changed this cycle, so an edit to one pack leaves
79 * the uploaded bytes of the others, and any GPU-side accumulation already
80 * written into them, untouched.
81 */
82 [[nodiscard]] virtual std::vector<DirtyVertexRange> dirty_vertex_ranges() const
83 {
84 if (!is_vertex_data_dirty()) {
85 return {};
86 }
87 return { DirtyVertexRange { .vertex_count = static_cast<uint32_t>(get_vertex_count()) } };
88 }
89
90 /**
91 * @brief Whether every vertex mutation on this operator sets a group's
92 * dirty flag, so dirty_vertex_ranges() is the complete record of
93 * what changed since the last mark_vertex_data_clean().
94 *
95 * False by default: a consumer must re-upload the whole buffer. True for
96 * operators (PathOperator, TopologyOperator) whose per-group flags are
97 * authoritative, letting a consumer upload only the reported ranges and
98 * then call mark_vertex_data_clean(). An operator that reports true must
99 * also route every path through which its vertices change (add, edit,
100 * clear, interpolation-parameter changes) to a group dirty flag.
101 */
102 [[nodiscard]] virtual bool supports_incremental_upload() const { return false; }
103
104 /**
105 * @brief Get source point count (before topology expansion)
106 */
107 [[nodiscard]] virtual size_t get_point_count() const = 0;
108
109 /**
110 * @brief Per-vertex collection index, global index order.
111 * @return get_vertex_count() elements, not get_point_count(): the
112 * consumer indexes it in lockstep with the vertex buffer a
113 * GpuFieldOperator dispatch or the spatial hash actually reads
114 * (vertices[i], not points[i]), and for an operator whose
115 * rendered vertex count differs from its source point count
116 * (TopologyOperator/PathOperator after interpolation) those are
117 * two different numbers. Default: every entry 0, meaning "one
118 * population, no cluster distinction" -- correct for every
119 * operator that carries no notion of multiple complete
120 * vertex-array packs, which is every GraphicsOperator except
121 * PhysicsOperator today. get_point_count() and get_vertex_count()
122 * coincide for PhysicsOperator's point-sprite geometry, so this
123 * default is exactly as correct there as a point_count-sized
124 * array would have been.
125 *
126 * The source NetworkGeometryBuffer::ensure_cluster_ids() reads from when
127 * declaring and populating the hash_cluster_id state field a
128 * cluster-aware GPU stage (ClaimProcessor, HashDensityColorProcessor, a
129 * cluster-scoped GpuFieldOperator binding) consumes. An operator that
130 * does carry multiple collections overrides this once, here, and every
131 * such consumer picks it up with no further wiring: none of them
132 * dynamic_cast to a concrete operator type to get it. An override must
133 * size and order its result the same way: by rendered vertex, in the
134 * same concatenation order get_vertex_data() itself produces.
135 */
136 [[nodiscard]] virtual std::vector<uint32_t> build_cluster_ids() const
137 {
138 return std::vector<uint32_t>(get_vertex_count(), 0U);
139 }
140
141 /**
142 * @brief Apply ONE_TO_ONE parameter mapping
143 *
144 * Default implementation handles common graphics properties:
145 * - "color": Per-point color
146 * - "size": Per-point size (for point rendering)
147 */
148 void apply_one_to_one(
149 std::string_view param,
150 const std::shared_ptr<NodeNetwork>& source) override;
151
152 /**
153 * @brief Get human-readable vertex type name (for validation/debugging)
154 */
155 [[nodiscard]] virtual const char* get_vertex_type_name() const = 0;
156
157 /**
158 * @brief Whether this operator contributes a vertex slice to rendering.
159 *
160 * Default true. Set false for transform-only chain operators (e.g. a
161 * FieldOperator deforming upstream vertices) that must not add an
162 * independent render slice.
163 */
164 [[nodiscard]] bool participates_in_rendering() const { return m_participates_in_rendering; }
165 void set_participates_in_rendering(bool value) { m_participates_in_rendering = value; }
166
167 /**
168 * @brief Whether this operator requests upstream vertex state before process().
169 *
170 * Default false. Set true for operators that derive their initial vertex
171 * data from the preceding operator in the chain rather than from an
172 * explicit initialize() call.
173 */
174 [[nodiscard]] bool consumes_upstream() const { return m_consumes_upstream; }
175 void set_consumes_upstream(bool value) { m_consumes_upstream = value; }
176
177 /**
178 * @brief Receive upstream vertex state before process() is called.
179 *
180 * Called by OperatorChain::process() only when consumes_upstream() is true.
181 * Implementations seed their internal vertex buffer from the upstream
182 * operator's current output. Default no-op.
183 *
184 * @param upstream Last GraphicsOperator that ran before this one in the
185 * chain, or the primary operator if this is the first chain
186 * entry. Null if no upstream GraphicsOperator exists.
187 */
188 virtual void seed_from_upstream(const GraphicsOperator* upstream) { }
189
190 /**
191 * @brief Override the dt passed by the caller with a fixed internal value.
192 *
193 * Useful when the owning network passes 0.0F or a sample-count dt that is
194 * meaningless for time-based integration (e.g. PhysicsOperator in a
195 * PointCloudNetwork chain). When true, process() ignores the incoming dt
196 * and substitutes m_internal_dt instead.
197 */
198 void set_force_internal_dt(bool value) { m_force_internal_dt = value; }
199 [[nodiscard]] bool uses_force_internal_dt() const { return m_force_internal_dt; }
200
201protected:
202 /**
203 * @brief Get mutable access to point at global index
204 * @return Pointer to vertex data, or nullptr if index invalid
205 *
206 * Subclasses must implement to provide per-point access
207 */
208 virtual void* get_data_at(size_t global_index) = 0;
209
210 bool m_participates_in_rendering { true };
211 bool m_consumes_upstream {};
212 bool m_force_internal_dt {};
213};
214
215} // namespace MayaFlux::Nodes::Network::Operators
float value
bool participates_in_rendering() const
Whether this operator contributes a vertex slice to rendering.
virtual void * get_data_at(size_t global_index)=0
Get mutable access to point at global index.
virtual size_t get_point_count() const =0
Get source point count (before topology expansion)
virtual std::span< const uint8_t > get_vertex_data_for_collection(uint32_t idx=0) const =0
Get vertex data for specific collection (if multiple)
virtual size_t get_vertex_count() const =0
Get number of vertices (may differ from point count for topology/path)
virtual std::vector< uint32_t > build_cluster_ids() const
Per-vertex collection index, global index order.
virtual void seed_from_upstream(const GraphicsOperator *upstream)
Receive upstream vertex state before process() is called.
virtual Kakshya::VertexLayout get_vertex_layout() const =0
Get vertex layout describing vertex structure.
virtual bool is_vertex_data_dirty() const =0
Check if geometry changed this frame.
virtual bool supports_incremental_upload() const
Whether every vertex mutation on this operator sets a group's dirty flag, so dirty_vertex_ranges() is...
bool consumes_upstream() const
Whether this operator requests upstream vertex state before process().
void set_force_internal_dt(bool value)
Override the dt passed by the caller with a fixed internal value.
virtual std::span< const uint8_t > get_vertex_data() const =0
Get vertex data for GPU upload.
virtual const char * get_vertex_type_name() const =0
Get human-readable vertex type name (for validation/debugging)
virtual std::vector< DirtyVertexRange > dirty_vertex_ranges() const
Vertex sub-ranges that changed since the last mark_vertex_data_clean().
virtual void mark_vertex_data_clean()=0
Clear dirty flag after GPU upload.
Operator that produces GPU-renderable geometry.
Domain-agnostic interpretive lens for network processing.
Complete description of vertex data layout in a buffer.
A contiguous span of vertex records that changed since the last mark_vertex_data_clean().