MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Evolve.cpp
Go to the documentation of this file.
1#include "Evolve.hpp"
2
5
10
12
13namespace MayaFlux {
14
15namespace {
16
17 /**
18 * @brief Whether the volume already holds a field of this name.
19 * @param volume Volume to query.
20 * @param name Field name.
21 * @return True if declared.
22 */
23 bool has_field(const std::shared_ptr<Buffers::VolumeGridBuffer>& volume, const std::string& name)
24 {
25 const auto names = volume->get_field_names();
26 return std::ranges::find(names, name) != names.end();
27 }
28
29 /**
30 * @brief Whether a ref was issued by a volume other than the one being wired.
31 * @param owner Owner recorded on the ref.
32 * @param volume Volume being wired.
33 * @return True if the ref names a different, live volume.
34 */
35 bool owned_elsewhere(
36 const std::weak_ptr<Buffers::VolumeGridBuffer>& owner,
37 const std::shared_ptr<Buffers::VolumeGridBuffer>& volume)
38 {
39 const auto held = owner.lock();
40 return held && held != volume;
41 }
42
43 /**
44 * @brief Resolve a scalar ref against a volume, declaring the field if absent.
45 * @param volume Volume being wired.
46 * @param ref Ref supplied by the caller.
47 * @param fallback Name given to an unnamed ref. Empty leaves it unnamed.
48 * @param single_slot True to declare a single-slot field, false for a
49 * double-buffered one.
50 * @return A ref issued by the volume, or the input unchanged when it cannot
51 * be resolved.
52 */
53 Buffers::ScalarRef resolve_scalar(
54 const std::shared_ptr<Buffers::VolumeGridBuffer>& volume,
55 Buffers::ScalarRef ref,
56 std::string_view fallback,
57 bool single_slot)
58 {
59 if (ref.name.empty()) {
60 if (fallback.empty()) {
61 return ref;
62 }
63 ref.name = std::string(fallback);
64 }
65
66 if (owned_elsewhere(ref.owner, volume)) {
67 return ref;
68 }
69
70 if (!has_field(volume, ref.name)) {
71 return single_slot ? volume->declare_scratch(ref.name) : volume->declare_scalar(ref.name);
72 }
73
74 ref.owner = volume;
75 return ref;
76 }
77
78 /**
79 * @brief Resolve a vector ref against a volume, declaring the field if absent.
80 * @param volume Volume being wired.
81 * @param ref Ref supplied by the caller.
82 * @param fallback Name given to an unnamed ref. Empty leaves it unnamed.
83 * @return A ref issued by the volume, or the input unchanged when it cannot
84 * be resolved.
85 */
86 Buffers::VectorRef resolve_vector(
87 const std::shared_ptr<Buffers::VolumeGridBuffer>& volume,
88 Buffers::VectorRef ref,
89 std::string_view fallback)
90 {
91 if (ref.name.empty()) {
92 if (fallback.empty()) {
93 return ref;
94 }
95 ref.name = std::string(fallback);
96 }
97
98 if (owned_elsewhere(ref.owner, volume)) {
99 return ref;
100 }
101
102 if (!has_field(volume, ref.name)) {
103 return volume->declare_vector(ref.name);
104 }
105
106 ref.owner = volume;
107 return ref;
108 }
109
110 /**
111 * @brief Whether a render config names a window to draw into.
112 * @param render Render config.
113 * @return True when target_window is set. Logs an error otherwise.
114 */
115 bool has_target(const Evolve::RenderConfig& render)
116 {
117 if (render.target_window) {
118 return true;
119 }
120
122 "evolve: RenderConfig::target_window is required, there is no default window");
123 return false;
124 }
125
126} // namespace
127
128/**
129 * @struct Evolve::Impl
130 * @brief Wiring sequences behind Evolve. Holds no state.
131 */
133 /**
134 * @brief Fill unresolved refs of a flow config from the volume.
135 * @param volume Volume being wired.
136 * @param config Flow config, edited in place.
137 *
138 * Velocity, pressure and divergence are always required, so an unnamed one
139 * is declared as "velocity", "pressure" and "divergence". Scratch is
140 * required only when viscosity is above zero. Carried and buoyancy refs
141 * carry the caller's own names and are declared only if the volume does
142 * not hold them.
143 */
145 const std::shared_ptr<Buffers::VolumeGridBuffer>& volume,
147 {
148 config.velocity = resolve_vector(volume, config.velocity, "velocity");
149 config.pressure = resolve_scalar(volume, config.pressure, "pressure", false);
150 config.divergence = resolve_scalar(volume, config.divergence, "divergence", true);
151
152 if (config.viscosity > 0.0F) {
153 config.scratch = resolve_vector(volume, config.scratch, "scratch");
154 }
155
156 for (auto& carried : config.carried) {
157 carried.field = resolve_scalar(volume, carried.field, {}, false);
158 }
159
160 if (config.buoyancy) {
161 config.buoyancy->temperature = resolve_scalar(volume, config.buoyancy->temperature, {}, false);
162 config.buoyancy->density = resolve_scalar(volume, config.buoyancy->density, {}, false);
163 }
164 }
165
166 [[nodiscard]] std::shared_ptr<Buffers::RelaxationGridBuffer> build_grid(
168 const RenderConfig& render,
169 const std::vector<std::byte>& seed,
170 size_t seed_stride) const
171 {
172 if (!has_target(render)) {
173 return nullptr;
174 }
175
176 auto grid = std::make_shared<Buffers::RelaxationGridBuffer>(config);
177
178 if (!seed.empty()) {
179 if (seed_stride != grid->get_cell_stride_bytes()) {
181 "evolve: seed cell is {} bytes but the grid's cell format is {} bytes",
182 seed_stride, grid->get_cell_stride_bytes());
183 return nullptr;
184 }
185 grid->seed_state(seed.data(), seed.size());
186 }
187
189 grid->setup_rendering(render);
190 return grid;
191 }
192
193 [[nodiscard]] bool build_flow(
194 const std::shared_ptr<Buffers::VolumeGridBuffer>& volume,
196 const RenderConfig& render) const
197 {
198 if (!volume) {
200 "evolve: null VolumeGridBuffer");
201 return false;
202 }
203
204 if (!has_target(render)) {
205 return false;
206 }
207
209 resolve(volume, config);
210
212 (void)volume->setup_flow(config);
213 volume->setup_rendering(render);
214 return true;
215 }
216
217 [[nodiscard]] bool build_field(
218 const std::shared_ptr<Nodes::Network::NodeNetwork>& network,
220 const FieldBinder& bind,
221 const RenderConfig& render) const
222 {
223 if (!network) {
225 "evolve: null NodeNetwork");
226 return false;
227 }
228
229 if (!has_target(render)) {
230 return false;
231 }
232
233 if (!network->has_operator()) {
234 auto particles = std::dynamic_pointer_cast<Nodes::Network::ParticleNetwork>(network);
235 if (particles && particles->default_operator_enabled()) {
236 particles->create_operator<Nodes::Network::PhysicsOperator>();
237 }
238 }
239
240 const auto* primary = dynamic_cast<const Nodes::Network::GraphicsOperator*>(network->get_operator());
241 if (!primary) {
243 "evolve: network has no primary GraphicsOperator to read a vertex layout from");
244 return false;
245 }
246
247 auto chain = network->get_operator_chain();
248 auto field = chain->find<Nodes::Network::GpuFieldOperator>();
249 if (!field) {
250 field = chain->emplace<Nodes::Network::GpuFieldOperator>(primary->get_vertex_layout(), spatial);
251 } else {
253 "evolve: network already carries a GpuFieldOperator, its SpatialFieldConfig is kept");
254 }
255
256 if (bind) {
257 bind(field);
258 }
259
261
262 auto buffer = std::make_shared<Buffers::NetworkGeometryBuffer>(network);
264 buffer->setup_rendering(render);
265 return true;
266 }
267};
268
270 : m_impl(std::make_unique<Impl>())
271{
272}
273
274Evolve::~Evolve() = default;
275
276Evolve::Evolve(Evolve&&) noexcept = default;
277
278Evolve& Evolve::operator=(Evolve&&) noexcept = default;
279
280std::shared_ptr<Buffers::RelaxationGridBuffer> Evolve::build_grid(
281 const Buffers::RelaxationGridBuffer::GridConfig& config,
282 const RenderConfig& render,
283 const std::vector<std::byte>& seed,
284 size_t seed_stride)
285{
286 return m_impl->build_grid(config, render, seed, seed_stride);
287}
288
290 const std::shared_ptr<Buffers::VolumeGridBuffer>& volume,
292 const RenderConfig& render)
293{
294 return m_impl->build_flow(volume, flow, render);
295}
296
298 const std::shared_ptr<Nodes::Network::NodeNetwork>& network,
300 const FieldBinder& bind,
301 const RenderConfig& render)
302{
303 return m_impl->build_field(network, spatial, bind, render);
304}
305
306} // namespace MayaFlux
#define MF_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
Core::GlobalNetworkConfig network
Definition Config.cpp:43
std::shared_ptr< NetworkGeometryBuffer > buffer
BufferProcessingChain * chain
std::string name
Definition VKDevice.cpp:123
bool build_field(const std::shared_ptr< Nodes::Network::NodeNetwork > &network, const Nodes::Network::SpatialFieldConfig &spatial, const FieldBinder &bind, const RenderConfig &render)
Create the field operator, bind, register the network and its buffer, and render.
Definition Evolve.cpp:297
bool build_flow(const std::shared_ptr< Buffers::VolumeGridBuffer > &volume, const Buffers::VolumeGridBuffer::FlowConfig &flow, const RenderConfig &render)
Resolve refs, register, build the flow stages and render a volume.
Definition Evolve.cpp:289
std::function< void(const std::shared_ptr< Nodes::Network::GpuFieldOperator > &)> FieldBinder
Callable that binds fields on the GpuFieldOperator before it is wired.
Definition Evolve.hpp:105
std::unique_ptr< Impl > m_impl
Definition Evolve.hpp:249
Buffers::VKBuffer::RenderConfig RenderConfig
Render target and pipeline overrides, shared by every family.
Definition Evolve.hpp:95
std::shared_ptr< Buffers::RelaxationGridBuffer > build_grid(const Buffers::RelaxationGridBuffer::GridConfig &config, const RenderConfig &render, const std::vector< std::byte > &seed, size_t seed_stride)
Construct, optionally seed, register and render a relaxation grid.
Definition Evolve.cpp:280
Sets a system going from a config: state that persists, a rule that advances it every cycle,...
Definition Evolve.hpp:92
Chain operator that declares Tendency field deformation as a compute shader rather than evaluating it...
Operator that produces GPU-renderable geometry.
N-body physics simulation with point rendering.
@ Init
Engine/subsystem initialization.
@ API
MayaFlux/API Wrapper and convenience functions.
@ VISUAL_RATE
Nodes that process at the visual frame rate.
void register_node_network(const std::shared_ptr< Nodes::Network::NodeNetwork > &network, const Nodes::ProcessingToken &token)
Registers a node network with the default engine's node graph manager.
Definition Graph.cpp:208
void register_graphics_buffer(const std::shared_ptr< Buffers::VKBuffer > &buffer, Buffers::ProcessingToken token)
Registers a VKBuffer with the default engine's buffer manager.
Definition Graph.cpp:250
Main namespace for the Maya Flux audio engine.
Definition Runtime.cpp:12
Arrangement of a relaxation grid: extent, the update rule, the emit stage that draws it,...
VectorRef scratch
Required only when viscosity is above zero.
float viscosity
Zero omits the diffusion stage entirely.
ScalarRef divergence
Required. Single-slot is correct.
Parameters for the incompressible flow stage arrangement.
void resolve(const std::shared_ptr< Buffers::VolumeGridBuffer > &volume, Buffers::VolumeGridBuffer::FlowConfig &config) const
Fill unresolved refs of a flow config from the volume.
Definition Evolve.cpp:144
std::shared_ptr< Buffers::RelaxationGridBuffer > build_grid(const Buffers::RelaxationGridBuffer::GridConfig &config, const RenderConfig &render, const std::vector< std::byte > &seed, size_t seed_stride) const
Definition Evolve.cpp:166
bool build_field(const std::shared_ptr< Nodes::Network::NodeNetwork > &network, const Nodes::Network::SpatialFieldConfig &spatial, const FieldBinder &bind, const RenderConfig &render) const
Definition Evolve.cpp:217
bool build_flow(const std::shared_ptr< Buffers::VolumeGridBuffer > &volume, const Buffers::VolumeGridBuffer::FlowConfig &flow, const RenderConfig &render) const
Definition Evolve.cpp:193
Wiring sequences behind Evolve.
Definition Evolve.cpp:132
Declarative configuration for the GPU-side spatial-relational work a GpuFieldOperator can drive beyon...
Unified rendering configuration for graphics buffers.