MayaFlux 0.4.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 Vertex attribute to drive (POSITION, COLOR, NORMAL, TANGENT).
56 * @param field VectorField: glm::vec3 -> glm::vec3.
57 *
58 * Creates a FieldOperator for the slot if one does not yet exist.
59 * The FieldOperator is initialised from the slot's current vertex data.
60 * Subsequent binds on the same slot append fields to the existing operator.
61 */
62 void bind(uint32_t slot_index, FieldTarget target, Kinesis::VectorField field);
63
64 /**
65 * @brief Bind a SpatialField to a slot.
66 * @param slot_index Target slot index.
67 * @param target Vertex attribute to drive (SCALAR or UV).
68 * @param field SpatialField: glm::vec3 -> float.
69 */
70 void bind(uint32_t slot_index, FieldTarget target, Kinesis::SpatialField field);
71
72 /**
73 * @brief Bind a UVField to a slot.
74 * @param slot_index Target slot index.
75 * @param target Must be FieldTarget::UV.
76 * @param field UVField: glm::vec3 -> glm::vec2.
77 */
78 void bind(uint32_t slot_index, FieldTarget target, Kinesis::UVField field);
79
80 /**
81 * @brief Remove all fields bound to a target on a specific slot.
82 * @param slot_index Target slot index.
83 * @param target Target to clear.
84 */
85 void unbind(uint32_t slot_index, FieldTarget target);
86
87 /**
88 * @brief Remove the entire FieldOperator for a slot.
89 * @param slot_index Target slot index.
90 */
91 void unbind_slot(uint32_t slot_index);
92
93 /**
94 * @brief Remove all per-slot FieldOperators.
95 */
96 void unbind_all();
97
98 /**
99 * @brief Set the field application mode for a slot's FieldOperator.
100 * @param slot_index Target slot index. The FieldOperator must already exist.
101 * @param mode ABSOLUTE or ACCUMULATE.
102 */
103 void set_mode(uint32_t slot_index, FieldMode mode);
104
105 // -------------------------------------------------------------------------
106 // MeshOperator interface
107 // -------------------------------------------------------------------------
108
109 /**
110 * @brief Run the slot's FieldOperator (if bound) and write results back to
111 * slot.node via set_mesh_vertices().
112 */
113 void process_slot(MeshSlot& slot, float dt) override;
114
115 [[nodiscard]] std::string_view get_type_name() const override
116 {
117 return "MeshField";
118 }
119
120 // -------------------------------------------------------------------------
121 // GPU execution
122 // -------------------------------------------------------------------------
123
124 /**
125 * @brief Attach a GPU executor and switch to async dispatch mode.
126 *
127 * Constructs a GpuComputeNode from the executor. On process() calls,
128 * compute_frame() is driven on the node instead of the per-slot CPU
129 * FieldOperator loop.
130 *
131 * The on_complete callback expects gpu_result.primary to contain vertex
132 * data for each slot packed sequentially: slot 0 vertices, then slot 1
133 * vertices, etc. Vertex count per slot must match the node's current
134 * get_mesh_vertex_count() at the time the callback fires. The callback
135 * writes back via slot.node->set_mesh_vertices().
136 *
137 * Slot vertex counts are snapshotted at set_gpu_executor() time from the
138 * current m_slots state. Call set_gpu_executor() after slots are fully
139 * populated. If slot geometry changes after attachment, call
140 * set_gpu_executor() again to rebuild the snapshot.
141 *
142 * CPU FieldOperator bindings are preserved but ignored while a GPU
143 * executor is attached. Passing nullptr clears the GPU path.
144 *
145 * @param executor Pre-configured ShaderExecutionContext. nullptr clears.
146 * @param continuous If true the node re-arms after every completed dispatch.
147 */
148 void set_gpu_executor(
149 std::shared_ptr<Yantra::ShaderExecutionContext<>> executor,
150 bool continuous = true);
151
152 /**
153 * @brief Update push constants on the attached GPU executor.
154 *
155 * Arms the next dispatch. No-op if no GPU executor is attached.
156 *
157 * @tparam T Trivially copyable struct matching the shader push constant layout.
158 * @param data Push constant data.
159 */
160 template <typename T>
161 void push_constants(const T& data)
162 {
163 if (m_executor)
164 m_executor->push(data);
165 if (m_compute_node)
166 m_compute_node->set_dirty();
167 }
168
169 /**
170 * @brief Returns true if a GPU executor is currently attached.
171 */
172 [[nodiscard]] bool has_gpu_executor() const { return m_compute_node != nullptr; }
173
174 // Override process() to drive compute_frame() on the GPU path.
175 void process(float dt) override;
176
177private:
178 std::unordered_map<uint32_t, std::shared_ptr<FieldOperator>> m_field_ops;
179
180 /**
181 * @brief Return an existing FieldOperator for the slot, or create and
182 * initialise one from the slot's current vertex data.
183 * @param slot Slot whose FieldOperator to retrieve or create.
184 * @param slot_index Slot index (key into m_field_ops).
185 * @return Pointer to the (possibly new) FieldOperator.
186 */
187 [[nodiscard]] std::shared_ptr<FieldOperator>
188 get_or_create(MeshSlot& slot, uint32_t slot_index);
189
190 std::shared_ptr<Yantra::ShaderExecutionContext<>> m_executor;
191 std::shared_ptr<Nodes::GpuSync::GpuComputeNode> m_compute_node;
192
193 /**
194 * @brief Vertex count per slot index, snapshotted at set_gpu_executor() time.
195 *
196 * Used by the on_complete callback to slice gpu_result.primary into
197 * per-slot spans without re-querying slot nodes.
198 */
199 std::vector<size_t> m_gpu_slot_vertex_counts;
200};
201
202} // 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.
FieldMode
How fields are applied each frame.
FieldTarget
Vertex attribute targets for Tendency field evaluation.
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