MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
FieldOperator.cpp
Go to the documentation of this file.
1#include "FieldOperator.hpp"
2
5
7
9 : m_mode(mode)
10{
11}
12
13// =========================================================================
14// Initialization
15// =========================================================================
16
17void FieldOperator::store_reference(const void* data, size_t count)
18{
19 m_count = count;
20 size_t total = count * k_stride;
21 m_reference_data.resize(total);
22 m_vertex_data.resize(total);
23 std::memcpy(m_reference_data.data(), data, total);
24 std::memcpy(m_vertex_data.data(), data, total);
25 m_dirty = true;
26}
27
28void FieldOperator::initialize(const std::vector<PointVertex>& vertices)
29{
31 store_reference(vertices.data(), vertices.size());
32
34 "FieldOperator initialized with {} PointVertex", vertices.size());
35}
36
37void FieldOperator::initialize(const std::vector<LineVertex>& vertices)
38{
40 store_reference(vertices.data(), vertices.size());
41
43 "FieldOperator initialized with {} LineVertex", vertices.size());
44}
45
46void FieldOperator::initialize(const std::vector<MeshVertex>& vertices)
47{
49 store_reference(vertices.data(), vertices.size());
50
52 "FieldOperator initialized with {} MeshVertex", vertices.size());
53}
54
56{
57 if (m_count > 0 || !upstream)
58 return;
59
60 const auto data = upstream->get_vertex_data();
61 const auto layout = upstream->get_vertex_layout();
62 std::string type_name = upstream->get_vertex_type_name();
63
64 if (type_name == "PointVertex") {
66 } else if (type_name == "LineVertex") {
68 } else if (type_name == "MeshVertex") {
70 } else {
72 "FieldOperator::seed_from_upstream: unrecognized vertex type '{}', defaulting to LineVertex",
73 type_name);
75 }
76
77 if (data.empty() || layout.stride_bytes == 0)
78 return;
79
80 if (layout.stride_bytes != k_stride) {
82 "FieldOperator::seed_from_upstream: upstream stride {} != k_stride {}, skipping",
83 layout.stride_bytes, k_stride);
84 return;
85 }
86
87 const size_t count = data.size() / k_stride;
88 if (count == 0)
89 return;
90
91 store_reference(data.data(), count);
92
94 "FieldOperator seeded {} vertices from upstream '{}'",
95 count, upstream->get_type_name());
96}
97
98// =========================================================================
99// Processing
100// =========================================================================
101
103{
104 if (m_count == 0)
105 return;
106
107 if (m_mode == FieldMode::ABSOLUTE) {
108 std::memcpy(m_vertex_data.data(), m_reference_data.data(), m_count * k_stride);
109 }
110
111 bool has_position = !m_position_fields.empty();
112 bool has_color = !m_color_fields.empty();
113 bool has_normal = !m_normal_fields.empty();
114 bool has_tangent = !m_tangent_fields.empty();
115 bool has_scalar = !m_scalar_fields.empty();
116 bool has_uv = !m_uv_fields.empty();
117
118 for (size_t i = 0; i < m_count; ++i) {
119 glm::vec3& pos = vec3_at(i, k_position_offset);
120
121 if (has_position) {
122 glm::vec3 displacement(0.0F);
123 for (const auto& field : m_position_fields) {
124 displacement += field(pos);
125 }
126 pos += displacement;
127 }
128
129 if (has_color) {
130 glm::vec3 color(0.0F);
131 for (const auto& field : m_color_fields) {
132 color += field(pos);
133 }
134 vec3_at(i, k_color_offset) = color;
135 }
136
137 if (has_normal) {
138 glm::vec3 normal(0.0F);
139 for (const auto& field : m_normal_fields) {
140 normal += field(pos);
141 }
142 float len = glm::length(normal);
143 vec3_at(i, k_normal_offset) = len > 1e-6F ? normal / len : glm::vec3(0.0F, 0.0F, 1.0F);
144 }
145
146 if (has_tangent) {
147 glm::vec3 tangent(0.0F);
148 for (const auto& field : m_tangent_fields) {
149 tangent += field(pos);
150 }
151 float len = glm::length(tangent);
152 vec3_at(i, k_tangent_offset) = len > 1e-6F ? tangent / len : glm::vec3(1.0F, 0.0F, 0.0F);
153 }
154
155 if (has_scalar) {
156 float value = 0.0F;
157 for (const auto& field : m_scalar_fields) {
158 value += field(pos);
159 }
161 }
162
163 if (has_uv) {
164 const glm::vec3 ref = ref_position_at(i);
165 glm::vec2 uv(0.0F);
166 for (const auto& field : m_uv_fields)
167 uv += field(ref);
168 *reinterpret_cast<glm::vec2*>(
169 m_vertex_data.data() + i * k_stride + k_uv_offset) = uv;
170 }
171 }
172
173 m_dirty = true;
174}
175
176// =========================================================================
177// Field binding
178// =========================================================================
179
181{
182 if (!any_flag(target)) {
184 "bind: empty target mask");
185 return;
186 }
187
188 if (any_flag(target & ~Kinesis::k_vector_targets)) {
190 "Cannot bind VectorField to mask {:#x}: unreached bits {:#x}. "
191 "SCALAR needs a SpatialField, UV needs a UVField, and targets this "
192 "operator does not implement are rejected. Nothing was bound.",
193 static_cast<uint16_t>(target),
194 static_cast<uint16_t>(target & ~Kinesis::k_vector_targets));
195 return;
196 }
197
198 if (has_flag(target, FieldTarget::POSITION))
199 m_position_fields.push_back(field);
200 if (has_flag(target, FieldTarget::COLOR))
201 m_color_fields.push_back(field);
202 if (has_flag(target, FieldTarget::NORMAL))
203 m_normal_fields.push_back(field);
204 if (has_flag(target, FieldTarget::TANGENT))
205 m_tangent_fields.push_back(std::move(field));
206}
207
209{
210 if (target != FieldTarget::SCALAR) {
212 "SpatialField binds only to FieldTarget::SCALAR, got mask {:#x}. "
213 "UV requires a UVField; vector targets require a VectorField.",
214 static_cast<uint16_t>(target));
215 return;
216 }
217 m_scalar_fields.push_back(std::move(field));
218}
219
221{
222 if (target != FieldTarget::UV) {
224 "UVField binds only to FieldTarget::UV, got mask {:#x}",
225 static_cast<uint16_t>(target));
226 return;
227 }
228 m_uv_fields.push_back(std::move(field));
229}
230
232{
233 if (has_flag(target, FieldTarget::POSITION))
234 m_position_fields.clear();
235 if (has_flag(target, FieldTarget::COLOR))
236 m_color_fields.clear();
237 if (has_flag(target, FieldTarget::NORMAL))
238 m_normal_fields.clear();
239 if (has_flag(target, FieldTarget::TANGENT))
240 m_tangent_fields.clear();
241 if (has_flag(target, FieldTarget::SCALAR))
242 m_scalar_fields.clear();
243 if (has_flag(target, FieldTarget::UV))
244 m_uv_fields.clear();
245}
246
248{
249 m_position_fields.clear();
250 m_color_fields.clear();
251 m_normal_fields.clear();
252 m_tangent_fields.clear();
253 m_scalar_fields.clear();
254 m_uv_fields.clear();
255}
256
257// =========================================================================
258// Vertex accessors
259// =========================================================================
260
261glm::vec3& FieldOperator::vec3_at(size_t i, size_t offset)
262{
263 return *reinterpret_cast<glm::vec3*>(m_vertex_data.data() + i * k_stride + offset);
264}
265
266float& FieldOperator::float_at(size_t i, size_t offset)
267{
268 return *reinterpret_cast<float*>(m_vertex_data.data() + i * k_stride + offset);
269}
270
271glm::vec3 FieldOperator::ref_position_at(size_t i) const
272{
273 return *reinterpret_cast<const glm::vec3*>(m_reference_data.data() + i * k_stride + k_position_offset);
274}
275
276// =========================================================================
277// GraphicsOperator interface
278// =========================================================================
279
280std::span<const uint8_t> FieldOperator::get_vertex_data() const
281{
282 return { m_vertex_data.data(), m_vertex_data.size() };
283}
284
285std::span<const uint8_t> FieldOperator::get_vertex_data_for_collection(uint32_t) const
286{
287 return get_vertex_data();
288}
289
291{
293 switch (m_vertex_type) {
296 break;
297 case VertexType::LINE:
299 break;
300 case VertexType::MESH:
302 break;
303 default:
304 return {};
305 }
306 layout.vertex_count = static_cast<uint32_t>(m_count);
307 return layout;
308}
309
311{
312 return m_count;
313}
314
316{
317 return m_dirty;
318}
319
321{
322 m_dirty = false;
323}
324
326{
327 return m_count;
328}
329
331{
332 switch (m_vertex_type) {
334 return "PointVertex";
335 case VertexType::LINE:
336 return "LineVertex";
337 case VertexType::MESH:
338 return "MeshVertex";
339 default:
340 return "Unknown";
341 }
342}
343
344std::vector<PointVertex> FieldOperator::extract_point_vertices() const
345{
346 std::vector<PointVertex> result(m_count);
347 std::memcpy(result.data(), m_vertex_data.data(), m_count * k_stride);
348 return result;
349}
350
351std::vector<LineVertex> FieldOperator::extract_line_vertices() const
352{
353 std::vector<LineVertex> result(m_count);
354 std::memcpy(result.data(), m_vertex_data.data(), m_count * k_stride);
355 return result;
356}
357
358std::vector<MeshVertex> FieldOperator::extract_mesh_vertices() const
359{
360 std::vector<MeshVertex> result(m_count);
361 if (m_count > 0) {
362 std::memcpy(result.data(), m_vertex_data.data(), m_count * k_stride);
363 }
364 return result;
365}
366
367void FieldOperator::set_parameter(std::string_view param, double value)
368{
369 if (param == "mode") {
370 m_mode = value > 0.5 ? FieldMode::ACCUMULATE : FieldMode::ABSOLUTE;
371 }
372}
373
374std::optional<double> FieldOperator::query_state(std::string_view query) const
375{
376 if (query == "point_count") {
377 return static_cast<double>(m_count);
378 }
379 if (query == "field_count") {
380 return static_cast<double>(
381 m_position_fields.size() + m_color_fields.size()
382 + m_normal_fields.size() + m_tangent_fields.size()
383 + m_scalar_fields.size() + m_uv_fields.size());
384 }
385 return std::nullopt;
386}
387
389 std::string_view param,
390 const std::shared_ptr<NodeNetwork>& source)
391{
393}
394
395void* FieldOperator::get_data_at(size_t global_index)
396{
397 if (global_index >= m_count)
398 return nullptr;
399 return m_vertex_data.data() + global_index * k_stride;
400}
401
402} // namespace MayaFlux::Nodes::Network
#define MF_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
size_t count
float value
float offset
size_t get_point_count() const override
Get source point count (before topology expansion)
void store_reference(const void *data, size_t count)
std::vector< Kinesis::VectorField > m_position_fields
size_t get_vertex_count() const override
Get number of vertices (may differ from point count for topology/path)
void seed_from_upstream(const GraphicsOperator *upstream) override
Seed vertex data from the upstream operator's current output.
void set_parameter(std::string_view param, double value) override
Set operator parameter.
bool is_vertex_data_dirty() const override
Check if geometry changed this frame.
void unbind_all()
Remove all bound fields.
std::vector< LineVertex > extract_line_vertices() const
std::vector< Kinesis::VectorField > m_tangent_fields
std::vector< Kinesis::SpatialField > m_scalar_fields
glm::vec3 & vec3_at(size_t i, size_t offset)
std::vector< Kinesis::VectorField > m_color_fields
Kakshya::VertexLayout get_vertex_layout() const override
Get vertex layout describing vertex structure.
std::span< const uint8_t > get_vertex_data_for_collection(uint32_t idx) const override
Get vertex data for specific collection (if multiple)
std::span< const uint8_t > get_vertex_data() const override
Get vertex data for GPU upload.
void * get_data_at(size_t global_index) override
Get mutable access to point at global index.
std::vector< PointVertex > extract_point_vertices() const
void mark_vertex_data_clean() override
Clear dirty flag after GPU upload.
void unbind(FieldTarget target)
Remove all fields bound to a target.
void apply_one_to_one(std::string_view param, const std::shared_ptr< NodeNetwork > &source) override
Apply ONE_TO_ONE parameter mapping (per-point control)
std::vector< Kinesis::VectorField > m_normal_fields
std::optional< double > query_state(std::string_view query) const override
Query operator internal state.
void bind(FieldTarget target, Kinesis::VectorField field)
Bind a VectorField to a vec3 target.
float & float_at(size_t i, size_t offset)
std::vector< Kinesis::UVField > m_uv_fields
const char * get_vertex_type_name() const override
Get human-readable vertex type name (for validation/debugging)
std::vector< MeshVertex > extract_mesh_vertices() const
void process(float dt) override
Process for one batch cycle.
void initialize(const std::vector< PointVertex > &vertices)
Initialize from PointVertex data.
FieldOperator(FieldMode mode=FieldMode::ABSOLUTE)
glm::vec3 ref_position_at(size_t i) const
void apply_one_to_one(std::string_view param, const std::shared_ptr< NodeNetwork > &source) override
Apply ONE_TO_ONE parameter mapping.
virtual Kakshya::VertexLayout get_vertex_layout() const =0
Get vertex layout describing vertex structure.
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)
Operator that produces GPU-renderable geometry.
virtual std::string_view get_type_name() const =0
Type name for introspection.
@ NodeProcessing
Node graph processing (Nodes::NodeGraphManager)
@ Nodes
DSP Generator and Filter Nodes, graph pipeline, node management.
FieldTarget
What a Tendency drives when applied to a vertex record.
FieldMode
How a field result combines with the value already present.
constexpr FieldTarget k_vector_targets
Targets that accept a three-component field.
Kinesis::FieldTarget FieldTarget
uint32_t vertex_count
Total number of vertices in this buffer.
static VertexLayout for_lines(uint32_t stride=60)
Factory: layout for LineVertex (position, color, thickness, uv, normal, tangent)
static VertexLayout for_meshes(uint32_t stride=60)
Factory: layout for MeshVertex (position, color, weight, uv, normal, tangent)
static VertexLayout for_points(uint32_t stride=60)
Factory: layout for PointVertex (position, color, size, uv, normal, tangent)
Complete description of vertex data layout in a buffer.
Typed, composable, stateless callable from domain D to range R.
Definition Tendency.hpp:22