MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
MeshFieldOperator.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "FieldOperator.hpp"
4#include "MeshOperator.hpp"
5
7
9
10/**
11 * @class MeshFieldOperator
12 * @brief Chain operator that applies Tendency field deformation to the
13 * vertex data of individual MeshNetwork slots.
14 *
15 * Holds one FieldOperator per bound slot, keyed by slot index.
16 * bind() creates or replaces the per-slot FieldOperator and initialises
17 * it from the slot's current MeshVertex array so the reference frame
18 * is always the geometry as it existed at bind time.
19 *
20 * process_slot() runs the slot's FieldOperator::process(dt) then writes
21 * the resulting MeshVertex array back via
22 * slot.node->set_mesh_vertices(). That call sets m_vertex_data_dirty on
23 * the MeshWriterNode, which any_slot_dirty() in MeshNetworkProcessor
24 * already checks.
25 *
26 * Slots without a bound FieldOperator are skipped entirely.
27 *
28 * This operator belongs in the OperatorChain, not as the primary operator.
29 * MeshTransformOperator runs first (as primary) to propagate world
30 * transforms; MeshFieldOperator runs after to deform vertex positions in
31 * local space.
32 *
33 * Usage:
34 * @code
35 * auto field_op = std::make_shared<MeshFieldOperator>();
36 * net->get_operator_chain()->emplace<MeshFieldOperator>();
37 *
38 * // Retrieve the typed pointer from the chain or hold it before emplacing.
39 * field_op->bind(torso_idx, FieldTarget::POSITION,
40 * Kinesis::make_radial_pull(glm::vec3(0), 4.0F));
41 * @endcode
42 */
43class MAYAFLUX_API MeshFieldOperator : public MeshOperator {
44public:
45 MeshFieldOperator() = default;
46 ~MeshFieldOperator() override = default;
47
48 // -------------------------------------------------------------------------
49 // Field binding
50 // -------------------------------------------------------------------------
51
52 /**
53 * @brief Bind a VectorField to a slot.
54 * @param slot_index Target slot index.
55 * @param target Mask of vertex attributes to drive. Any combination of
56 * POSITION, COLOR, NORMAL and TANGENT.
57 * @param field VectorField: glm::vec3 -> glm::vec3.
58 *
59 * Creates a FieldOperator for the slot if one does not yet exist.
60 * The FieldOperator is initialised from the slot's current vertex data.
61 * Subsequent binds on the same slot append fields to the existing operator.
62 */
63 void bind(uint32_t slot_index, FieldTarget target, Kinesis::VectorField field);
64
65 /**
66 * @brief Bind a SpatialField to a slot.
67 * @param slot_index Target slot index.
68 * @param target Must be FieldTarget::SCALAR.
69 * @param field SpatialField: glm::vec3 -> float.
70 */
71 void bind(uint32_t slot_index, FieldTarget target, Kinesis::SpatialField field);
72
73 /**
74 * @brief Bind a UVField to a slot.
75 * @param slot_index Target slot index.
76 * @param target Must be FieldTarget::UV.
77 * @param field UVField: glm::vec3 -> glm::vec2.
78 */
79 void bind(uint32_t slot_index, FieldTarget target, Kinesis::UVField field);
80
81 /**
82 * @brief Remove all fields bound to the given targets on a specific slot.
83 * @param slot_index Target slot index.
84 * @param target Mask of targets to clear.
85 */
86 void unbind(uint32_t slot_index, FieldTarget target);
87
88 /**
89 * @brief Remove the entire FieldOperator for a slot.
90 * @param slot_index Target slot index.
91 */
92 void unbind_slot(uint32_t slot_index);
93
94 /**
95 * @brief Remove all per-slot FieldOperators.
96 */
97 void unbind_all();
98
99 /**
100 * @brief Set the field application mode for a slot's FieldOperator.
101 * @param slot_index Target slot index. The FieldOperator must already exist.
102 * @param mode ABSOLUTE or ACCUMULATE.
103 */
104 void set_mode(uint32_t slot_index, FieldMode mode);
105
106 // -------------------------------------------------------------------------
107 // MeshOperator interface
108 // -------------------------------------------------------------------------
109
110 /**
111 * @brief Run the slot's FieldOperator (if bound) and write results back to
112 * slot.node via set_mesh_vertices().
113 */
114 void process_slot(MeshSlot& slot, float dt) override;
115
116 [[nodiscard]] std::string_view get_type_name() const override
117 {
118 return "MeshField";
119 }
120
121 // -------------------------------------------------------------------------
122 // GPU execution
123 // -------------------------------------------------------------------------
124
125 /**
126 * @brief Attach a GPU executor and switch to async dispatch mode.
127 *
128 * Constructs a GpuComputeNode from the executor. On process() calls,
129 * compute_frame() is driven on the node instead of the per-slot CPU
130 * FieldOperator loop.
131 *
132 * The on_complete callback expects gpu_result.primary to contain vertex
133 * data for each slot packed sequentially: slot 0 vertices, then slot 1
134 * vertices, etc. Vertex count per slot must match the node's current
135 * get_mesh_vertex_count() at the time the callback fires. The callback
136 * writes back via slot.node->set_mesh_vertices().
137 *
138 * Slot vertex counts are snapshotted at set_gpu_executor() time from the
139 * current m_slots state. Call set_gpu_executor() after slots are fully
140 * populated. If slot geometry changes after attachment, call
141 * set_gpu_executor() again to rebuild the snapshot.
142 *
143 * CPU FieldOperator bindings are preserved but ignored while a GPU
144 * executor is attached. Passing nullptr clears the GPU path.
145 *
146 * @param executor Pre-configured ShaderExecutionContext. nullptr clears.
147 * @param continuous If true the node re-arms after every completed dispatch.
148 */
149 void set_gpu_executor(
150 std::shared_ptr<Yantra::ShaderExecutionContext<>> executor,
151 bool continuous = true);
152
153 /**
154 * @brief Update push constants on the attached GPU executor.
155 *
156 * Arms the next dispatch. No-op if no GPU executor is attached.
157 *
158 * @tparam T Trivially copyable struct matching the shader push constant layout.
159 * @param data Push constant data.
160 */
161 template <typename T>
162 void push_constants(const T& data)
163 {
164 if (m_executor)
165 m_executor->push(data);
166 if (m_compute_node)
167 m_compute_node->set_dirty();
168 }
169
170 /**
171 * @brief Returns true if a GPU executor is currently attached.
172 */
173 [[nodiscard]] bool has_gpu_executor() const { return m_compute_node != nullptr; }
174
175 // Override process() to drive compute_frame() on the GPU path.
176 void process(float dt) override;
177
178private:
179 std::unordered_map<uint32_t, std::shared_ptr<FieldOperator>> m_field_ops;
180
181 /**
182 * @brief Return an existing FieldOperator for the slot, or create and
183 * initialise one from the slot's current vertex data.
184 * @param slot Slot whose FieldOperator to retrieve or create.
185 * @param slot_index Slot index (key into m_field_ops).
186 * @return Pointer to the (possibly new) FieldOperator.
187 */
188 [[nodiscard]] std::shared_ptr<FieldOperator>
189 get_or_create(MeshSlot& slot, uint32_t slot_index);
190
191 std::shared_ptr<Yantra::ShaderExecutionContext<>> m_executor;
192 std::shared_ptr<Nodes::GpuSync::GpuComputeNode> m_compute_node;
193
194 /**
195 * @brief Vertex count per slot index, snapshotted at set_gpu_executor() time.
196 *
197 * Used by the on_complete callback to slice gpu_result.primary into
198 * per-slot spans without re-querying slot nodes.
199 */
200 std::vector<size_t> m_gpu_slot_vertex_counts;
201};
202
203} // namespace MayaFlux::Nodes::Network
void bind(uint32_t slot_index, FieldTarget target, Kinesis::SpatialField field)
Bind a SpatialField to a slot.
std::unordered_map< uint32_t, std::shared_ptr< FieldOperator > > m_field_ops
std::shared_ptr< Yantra::ShaderExecutionContext<> > m_executor
void push_constants(const T &data)
Update push constants on the attached GPU executor.
std::vector< size_t > m_gpu_slot_vertex_counts
Vertex count per slot index, snapshotted at set_gpu_executor() time.
std::shared_ptr< Nodes::GpuSync::GpuComputeNode > m_compute_node
void bind(uint32_t slot_index, FieldTarget target, Kinesis::UVField field)
Bind a UVField to a slot.
std::string_view get_type_name() const override
Type name for introspection.
bool has_gpu_executor() const
Returns true if a GPU executor is currently attached.
Chain operator that applies Tendency field deformation to the vertex data of individual MeshNetwork s...
Abstract base for operators that process MeshNetwork slots.
Concrete GpuExecutionContext for a single fixed shader with fixed bindings.
FieldTarget
What a Tendency drives when applied to a vertex record.
FieldMode
How a field result combines with the value already present.
Typed, composable, stateless callable from domain D to range R.
Definition Tendency.hpp:22
Named, independently transformable mesh unit within a MeshNetwork.
Definition MeshSlot.hpp:31