Blender V5.0
node_geo_simulation.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BLI_string.h"
6#include "BLI_string_utf8.h"
7
11#include "BKE_context.hh"
12#include "BKE_curves.hh"
13#include "BKE_instances.hh"
14#include "BKE_modifier.hh"
16#include "BKE_screen.hh"
17
19
21
22#include "NOD_common.hh"
23#include "NOD_geo_bake.hh"
24#include "NOD_geo_simulation.hh"
26#include "NOD_socket.hh"
31
32#include "DNA_mesh_types.h"
34
35#include "ED_node.hh"
36
37#include "RNA_access.hh"
38#include "RNA_prototypes.hh"
39
40#include "BLT_translation.hh"
41
42#include "GEO_mix_geometries.hh"
43
44#include "WM_api.hh"
45
46#include "BLO_read_write.hh"
47
48#include "node_geometry_util.hh"
49
51
53 const Span<NodeSimulationItem> node_simulation_items)
54{
56 const int items_num = node_simulation_items.size();
57 config.domains.resize(items_num);
58 config.names.resize(items_num);
59 config.types.resize(items_num);
60 config.geometries_by_attribute.resize(items_num);
61
62 int last_geometry_index = -1;
63 for (const int item_i : node_simulation_items.index_range()) {
64 const NodeSimulationItem &item = node_simulation_items[item_i];
65 config.types[item_i] = eNodeSocketDatatype(item.socket_type);
66 config.names[item_i] = item.name;
67 config.domains[item_i] = AttrDomain(item.attribute_domain);
68 if (item.socket_type == SOCK_GEOMETRY) {
69 last_geometry_index = item_i;
70 }
71 else if (last_geometry_index != -1) {
72 config.geometries_by_attribute[item_i].append(last_geometry_index);
73 }
74 }
75 return config;
76}
77
78static std::shared_ptr<AttributeFieldInput> make_attribute_field(
79 const Object &self_object,
80 const ComputeContext &compute_context,
81 const bNode &node,
82 const NodeSimulationItem &item,
83 const CPPType &type)
84{
85 std::string attribute_name = bke::hash_to_anonymous_attribute_name(
86 self_object.id.name, compute_context.hash(), node.identifier, item.identifier);
87 std::string socket_inspection_name = make_anonymous_attribute_socket_inspection_string(
88 node.label_or_name(), item.name);
89 return std::make_shared<AttributeFieldInput>(
90 std::move(attribute_name), type, std::move(socket_inspection_name));
91}
92
94 const Span<NodeSimulationItem> node_simulation_items,
95 bke::bake::BakeState zone_state,
96 const Object &self_object,
97 const ComputeContext &compute_context,
98 const bNode &node,
99 bke::bake::BakeDataBlockMap *data_block_map)
100{
101 const bke::bake::BakeSocketConfig config = make_bake_socket_config(node_simulation_items);
103 for (const NodeSimulationItem &item : node_simulation_items) {
104 std::unique_ptr<bke::bake::BakeItem> *bake_item = zone_state.items_by_id.lookup_ptr(
105 item.identifier);
106 bake_items.append(bake_item ? bake_item->get() : nullptr);
107 }
108
110 bake_items, config, data_block_map, [&](const int i, const CPPType &type) {
112 self_object, compute_context, node, node_simulation_items[i], type);
113 });
114}
115
117 const Span<NodeSimulationItem> node_simulation_items,
118 const bke::bake::BakeStateRef &zone_state,
119 const Object &self_object,
120 const ComputeContext &compute_context,
121 const bNode &node,
122 bke::bake::BakeDataBlockMap *data_block_map)
123{
124 const bke::bake::BakeSocketConfig config = make_bake_socket_config(node_simulation_items);
126 for (const NodeSimulationItem &item : node_simulation_items) {
127 const bke::bake::BakeItem *const *bake_item = zone_state.items_by_id.lookup_ptr(
128 item.identifier);
129 bake_items.append(bake_item ? *bake_item : nullptr);
130 }
131
133 bake_items, config, data_block_map, [&](const int i, const CPPType &type) {
135 self_object, compute_context, node, node_simulation_items[i], type);
136 });
137}
138
140 const Span<NodeSimulationItem> node_simulation_items,
142 bke::bake::BakeDataBlockMap *data_block_map)
143{
144 const bke::bake::BakeSocketConfig config = make_bake_socket_config(node_simulation_items);
145
147 bke::bake::move_socket_values_to_bake_items(input_values, config, data_block_map);
148
149 bke::bake::BakeState bake_state;
150 for (const int i : node_simulation_items.index_range()) {
151 const NodeSimulationItem &item = node_simulation_items[i];
152 std::unique_ptr<bke::bake::BakeItem> &bake_item = bake_items[i];
153 if (bake_item) {
154 bake_state.items_by_id.add_new(item.identifier, std::move(bake_item));
155 }
156 }
157 return bake_state;
158}
159
161 uiLayout *layout,
162 bNodeTree &ntree,
163 bNode &output_node)
164{
165 if (uiLayout *panel = layout->panel(
166 C, "simulation_state_items", false, IFACE_("Simulation State")))
167 {
169 C, panel, ntree, output_node);
170 auto &storage = *static_cast<NodeGeometrySimulationOutput *>(output_node.storage);
172 ntree, output_node, [&](PointerRNA *item_ptr) {
173 NodeSimulationItem &active_item = storage.items[storage.active_index];
174 panel->use_property_split_set(true);
175 panel->use_property_decorate_set(false);
176 panel->prop(item_ptr, "socket_type", UI_ITEM_NONE, std::nullopt, ICON_NONE);
178 panel->prop(item_ptr, "attribute_domain", UI_ITEM_NONE, std::nullopt, ICON_NONE);
179 }
180 });
181 }
182}
183
185static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *current_node_ptr)
186{
187 bNodeTree &ntree = *reinterpret_cast<bNodeTree *>(current_node_ptr->owner_id);
188 bNode *current_node = static_cast<bNode *>(current_node_ptr->data);
189
190 const bke::bNodeTreeZones *zones = ntree.zones();
191 if (!zones) {
192 return;
193 }
194 const bke::bNodeTreeZone *zone = zones->get_zone_by_node(current_node->identifier);
195 if (!zone) {
196 return;
197 }
198 if (!zone->output_node_id) {
199 return;
200 }
201 bNode &output_node = const_cast<bNode &>(*zone->output_node());
202
203 BakeDrawContext ctx;
204 if (!get_bake_draw_context(C, output_node, ctx)) {
205 return;
206 }
208
209 draw_simulation_state(C, layout, ntree, output_node);
210
211 layout->use_property_split_set(true);
212 layout->use_property_decorate_set(false);
213
214 layout->enabled_set(ID_IS_EDITABLE(ctx.object));
215
216 {
217 uiLayout *col = &layout->column(false);
218 draw_bake_button_row(ctx, col, true);
219 if (const std::optional<std::string> bake_state_str = get_bake_state_string(ctx)) {
220 uiLayout *row = &col->row(true);
221 row->label(*bake_state_str, ICON_NONE);
222 }
223 }
224 draw_common_bake_settings(C, ctx, layout);
225 draw_data_blocks(C, layout, ctx.bake_rna);
226}
227
228namespace sim_input_node {
229
231
233 const bNode &node_;
234 int32_t output_node_id_;
235 Span<NodeSimulationItem> simulation_items_;
236
237 public:
239 const bNode &node,
240 GeometryNodesLazyFunctionGraphInfo &own_lf_graph_info)
241 : node_(node)
242 {
243 debug_name_ = "Simulation Input";
244 output_node_id_ = node_storage(node).output_node_id;
245 const bNode &output_node = *node_tree.node_by_id(output_node_id_);
246 const NodeGeometrySimulationOutput &storage = *static_cast<NodeGeometrySimulationOutput *>(
247 output_node.storage);
248 simulation_items_ = {storage.items, storage.items_num};
249
250 MutableSpan<int> lf_index_by_bsocket = own_lf_graph_info.mapping.lf_index_by_bsocket;
251 lf_index_by_bsocket[node.output_socket(0).index_in_tree()] = outputs_.append_and_get_index_as(
252 "Delta Time", CPPType::get<SocketValueVariant>());
253
254 for (const int i : simulation_items_.index_range()) {
255 const NodeSimulationItem &item = simulation_items_[i];
256 const bNodeSocket &input_bsocket = node.input_socket(i);
257 const bNodeSocket &output_bsocket = node.output_socket(i + 1);
258
259 lf_index_by_bsocket[input_bsocket.index_in_tree()] = inputs_.append_and_get_index_as(
261 lf_index_by_bsocket[output_bsocket.index_in_tree()] = outputs_.append_and_get_index_as(
263 }
264 }
265
266 void execute_impl(lf::Params &params, const lf::Context &context) const final
267 {
268 const GeoNodesUserData &user_data = *static_cast<const GeoNodesUserData *>(context.user_data);
269 if (!user_data.call_data->simulation_params) {
271 return;
272 }
273 if (!user_data.call_data->self_object()) {
274 /* Self object is currently required for creating anonymous attribute names. */
276 return;
277 }
278 std::optional<FoundNestedNodeID> found_id = find_nested_node_id(user_data, output_node_id_);
279 if (!found_id) {
281 return;
282 }
283 if (found_id->is_in_loop || found_id->is_in_closure) {
285 return;
286 }
287 SimulationZoneBehavior *zone_behavior = user_data.call_data->simulation_params->get(
288 found_id->id);
289 if (!zone_behavior) {
291 return;
292 }
293 sim_input::Behavior &input_behavior = zone_behavior->input;
294 float delta_time = 0.0f;
295 if (auto *info = std::get_if<sim_input::OutputCopy>(&input_behavior)) {
296 delta_time = info->delta_time;
298 params, user_data, zone_behavior->data_block_map, info->state);
299 }
300 else if (auto *info = std::get_if<sim_input::OutputMove>(&input_behavior)) {
301 delta_time = info->delta_time;
303 params, user_data, zone_behavior->data_block_map, std::move(info->state));
304 }
305 else if (std::get_if<sim_input::PassThrough>(&input_behavior)) {
306 delta_time = 0.0f;
307 this->pass_through(params, user_data, zone_behavior->data_block_map);
308 }
309 else {
311 }
312 if (!params.output_was_set(0)) {
313 params.set_output(0, SocketValueVariant(delta_time));
314 }
315 }
316
321
323 const GeoNodesUserData &user_data,
324 bke::bake::BakeDataBlockMap *data_block_map,
325 const bke::bake::BakeStateRef &zone_state) const
326 {
328 simulation_items_,
329 zone_state,
330 *user_data.call_data->self_object(),
331 *user_data.compute_context,
332 node_,
333 data_block_map);
334 for (const int i : simulation_items_.index_range()) {
335 params.set_output(i + 1, std::move(output_values[i]));
336 }
337 }
338
340 const GeoNodesUserData &user_data,
341 bke::bake::BakeDataBlockMap *data_block_map,
342 bke::bake::BakeState zone_state) const
343 {
345 simulation_items_,
346 std::move(zone_state),
347 *user_data.call_data->self_object(),
348 *user_data.compute_context,
349 node_,
350 data_block_map);
351 for (const int i : simulation_items_.index_range()) {
352 params.set_output(i + 1, std::move(output_values[i]));
353 }
354 }
355
357 const GeoNodesUserData &user_data,
358 bke::bake::BakeDataBlockMap *data_block_map) const
359 {
360 Array<SocketValueVariant *> input_value_pointers(inputs_.size());
361 for (const int i : inputs_.index_range()) {
362 input_value_pointers[i] = params.try_get_input_data_ptr_or_request<SocketValueVariant>(i);
363 }
364 if (input_value_pointers.as_span().contains(nullptr)) {
365 /* Wait for inputs to be computed. */
366 return;
367 }
368 Array<SocketValueVariant> input_values(inputs_.size());
369 for (const int i : inputs_.index_range()) {
370 input_values[i] = std::move(*input_value_pointers[i]);
371 }
372
373 /* Instead of outputting the initial values directly, convert them to a simulation state and
374 * then back. This ensures that some geometry processing happens on the data consistently (e.g.
375 * removing anonymous attributes). */
377 simulation_items_, input_values, data_block_map);
378 this->output_simulation_state_move(params, user_data, data_block_map, std::move(bake_state));
379 }
380};
381
383{
384 b.use_custom_socket_order();
385 b.allow_any_socket_order();
386 b.add_output<decl::Float>("Delta Time");
387
388 const bNode *node = b.node_or_null();
389 const bNodeTree *node_tree = b.tree_or_null();
390 if (ELEM(nullptr, node, node_tree)) {
391 return;
392 }
393
394 const bNode *output_node = node_tree->node_by_id(node_storage(*node).output_node_id);
395 if (!output_node) {
396 return;
397 }
398 const auto &output_storage = *static_cast<const NodeGeometrySimulationOutput *>(
399 output_node->storage);
400
401 for (const int i : IndexRange(output_storage.items_num)) {
402 const NodeSimulationItem &item = output_storage.items[i];
403 const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type);
404 if (socket_type == SOCK_GEOMETRY && i > 0) {
405 b.add_separator();
406 }
407 const StringRef name = item.name;
408 const std::string identifier = SimulationItemsAccessor::socket_identifier_for_item(item);
409 auto &input_decl = b.add_input(socket_type, name, identifier)
410 .socket_name_ptr(
411 &node_tree->id, SimulationItemsAccessor::item_srna, &item, "name");
412 auto &output_decl = b.add_output(socket_type, name, identifier).align_with_previous();
413 if (socket_type_supports_fields(socket_type)) {
414 /* If it's below a geometry input it may be a field evaluated on that geometry. */
415 input_decl.supports_field().structure_type(StructureType::Dynamic);
416 output_decl.dependent_field({input_decl.index()});
417 }
418 }
419 b.add_input<decl::Extend>("", "__extend__").structure_type(StructureType::Dynamic);
420 b.add_output<decl::Extend>("", "__extend__")
421 .structure_type(StructureType::Dynamic)
422 .align_with_previous();
423}
424
425static void node_init(bNodeTree * /*tree*/, bNode *node)
426{
428 /* Needs to be initialized for the node to work. */
429 data->output_node_id = 0;
430 node->storage = data;
431}
432
433static void node_label(const bNodeTree * /*ntree*/,
434 const bNode * /*node*/,
435 char *label,
436 const int label_maxncpy)
437{
438 BLI_strncpy_utf8(label, CTX_IFACE_(BLT_I18NCONTEXT_ID_NODETREE, "Simulation"), label_maxncpy);
439}
440
442{
443 bNode *output_node = params.ntree.node_by_id(node_storage(params.node).output_node_id);
444 if (!output_node) {
445 return true;
446 }
448 params.ntree, params.node, *output_node, params.link);
449}
450
451static void node_register()
452{
453 static blender::bke::bNodeType ntype;
454 geo_node_type_base(&ntype, "GeometryNodeSimulationInput", GEO_NODE_SIMULATION_INPUT);
455 ntype.ui_name = "Simulation Input";
456 ntype.ui_description = "Input data for the simulation zone";
457 ntype.enum_name_legacy = "SIMULATION_INPUT";
459 ntype.initfunc = node_init;
460 ntype.declare = node_declare;
461 ntype.labelfunc = node_label;
463 ntype.gather_link_search_ops = nullptr;
464 ntype.no_muting = true;
467 "NodeGeometrySimulationInput",
471}
473
474} // namespace sim_input_node
475
477
479
481 const bNode &node_;
482 Span<NodeSimulationItem> simulation_items_;
483 int skip_input_index_;
489 int skip_inputs_offset_;
494 int solve_inputs_offset_;
495
496 public:
498 GeometryNodesLazyFunctionGraphInfo &own_lf_graph_info)
499 : node_(node)
500 {
501 debug_name_ = "Simulation Output";
502 const NodeGeometrySimulationOutput &storage = node_storage(node);
503 simulation_items_ = {storage.items, storage.items_num};
504
505 MutableSpan<int> lf_index_by_bsocket = own_lf_graph_info.mapping.lf_index_by_bsocket;
506
507 const bNodeSocket &skip_bsocket = node.input_socket(0);
508 skip_input_index_ = inputs_.append_and_get_index_as(
510 lf_index_by_bsocket[skip_bsocket.index_in_tree()] = skip_input_index_;
511
512 skip_inputs_offset_ = inputs_.size();
513
514 /* Add the skip inputs that are linked to the simulation input node. */
515 for (const int i : simulation_items_.index_range()) {
516 const NodeSimulationItem &item = simulation_items_[i];
518 }
519
520 solve_inputs_offset_ = inputs_.size();
521
522 /* Add the solve inputs that correspond to the simulation state inputs in the UI. */
523 for (const int i : simulation_items_.index_range()) {
524 const NodeSimulationItem &item = simulation_items_[i];
525 const bNodeSocket &input_bsocket = node.input_socket(i + 1);
526 const bNodeSocket &output_bsocket = node.output_socket(i);
527
528 lf_index_by_bsocket[input_bsocket.index_in_tree()] = inputs_.append_and_get_index_as(
530 lf_index_by_bsocket[output_bsocket.index_in_tree()] = outputs_.append_and_get_index_as(
532 }
533 }
534
535 void execute_impl(lf::Params &params, const lf::Context &context) const final
536 {
537 GeoNodesUserData &user_data = *static_cast<GeoNodesUserData *>(context.user_data);
538 GeoNodesLocalUserData &local_user_data = *static_cast<GeoNodesLocalUserData *>(
539 context.local_user_data);
540 if (!user_data.call_data->self_object()) {
541 /* The self object is currently required for generating anonymous attribute names. */
543 return;
544 }
545 if (!user_data.call_data->simulation_params) {
546 if (geo_eval_log::GeoTreeLogger *tree_logger = local_user_data.try_get_tree_logger(
547 user_data))
548 {
549 tree_logger->node_warnings.append(
550 *tree_logger->allocator,
551 {node_.identifier,
552 {NodeWarningType::Error, TIP_("Simulation zone is not supported")}});
553 }
555 return;
556 }
557 std::optional<FoundNestedNodeID> found_id = find_nested_node_id(user_data, node_.identifier);
558 if (!found_id) {
560 return;
561 }
562 if (found_id->is_in_loop || found_id->is_in_closure) {
563 if (geo_eval_log::GeoTreeLogger *tree_logger = local_user_data.try_get_tree_logger(
564 user_data))
565 {
566 const StringRefNull message = TIP_("Simulation must not be in a loop or closure");
567 tree_logger->node_warnings.append(*tree_logger->allocator,
568 {node_.identifier, {NodeWarningType::Error, message}});
569 }
571 return;
572 }
573 SimulationZoneBehavior *zone_behavior = user_data.call_data->simulation_params->get(
574 found_id->id);
575 if (!zone_behavior) {
576 this->set_default_outputs(params);
577 return;
578 }
579 sim_output::Behavior &output_behavior = zone_behavior->output;
580 if (auto *info = std::get_if<sim_output::ReadSingle>(&output_behavior)) {
581 this->output_cached_state(params, user_data, zone_behavior->data_block_map, info->state);
582 }
583 else if (auto *info = std::get_if<sim_output::ReadInterpolated>(&output_behavior)) {
584 this->output_mixed_cached_state(params,
585 zone_behavior->data_block_map,
586 *user_data.call_data->self_object(),
587 *user_data.compute_context,
588 info->prev_state,
589 info->next_state,
590 info->mix_factor);
591 }
592 else if (std::get_if<sim_output::PassThrough>(&output_behavior)) {
593 this->pass_through(params, user_data, zone_behavior->data_block_map);
594 }
595 else if (auto *info = std::get_if<sim_output::StoreNewState>(&output_behavior)) {
596 this->store_new_state(params, user_data, zone_behavior->data_block_map, *info);
597 }
598 else {
600 }
601 }
602
607
609 GeoNodesUserData &user_data,
610 bke::bake::BakeDataBlockMap *data_block_map,
611 const bke::bake::BakeStateRef &state) const
612 {
614 simulation_items_,
615 state,
616 *user_data.call_data->self_object(),
617 *user_data.compute_context,
618 node_,
619 data_block_map);
620 for (const int i : simulation_items_.index_range()) {
621 params.set_output(i, std::move(output_values[i]));
622 }
623 }
624
626 bke::bake::BakeDataBlockMap *data_block_map,
627 const Object &self_object,
628 const ComputeContext &compute_context,
629 const bke::bake::BakeStateRef &prev_state,
631 const float mix_factor) const
632 {
634 simulation_items_, prev_state, self_object, compute_context, node_, data_block_map);
635
637 simulation_items_, next_state, self_object, compute_context, node_, data_block_map);
638 for (const int i : simulation_items_.index_range()) {
639 mix_baked_data_item(eNodeSocketDatatype(simulation_items_[i].socket_type),
640 output_values[i],
641 next_values[i],
642 mix_factor);
643 }
644 for (const int i : simulation_items_.index_range()) {
645 params.set_output(i, std::move(output_values[i]));
646 }
647 }
648
650 GeoNodesUserData &user_data,
651 bke::bake::BakeDataBlockMap *data_block_map) const
652 {
653 std::optional<bke::bake::BakeState> bake_state = this->get_bake_state_from_inputs(
654 params, data_block_map, true);
655 if (!bake_state) {
656 /* Wait for inputs to be computed. */
657 return;
658 }
660 simulation_items_,
661 std::move(*bake_state),
662 *user_data.call_data->self_object(),
663 *user_data.compute_context,
664 node_,
665 data_block_map);
666 for (const int i : simulation_items_.index_range()) {
667 params.set_output(i, std::move(output_values[i]));
668 }
669 }
670
672 GeoNodesUserData &user_data,
673 bke::bake::BakeDataBlockMap *data_block_map,
674 const sim_output::StoreNewState &info) const
675 {
676 const SocketValueVariant *skip_variant =
677 params.try_get_input_data_ptr_or_request<SocketValueVariant>(skip_input_index_);
678 if (skip_variant == nullptr) {
679 /* Wait for skip input to be computed. */
680 return;
681 }
682 const bool skip = skip_variant->get<bool>();
683
684 /* Instead of outputting the values directly, convert them to a bake state and then back.
685 * This ensures that some geometry processing happens on the data consistently (e.g. removing
686 * anonymous attributes). */
687 std::optional<bke::bake::BakeState> bake_state = this->get_bake_state_from_inputs(
688 params, data_block_map, skip);
689 if (!bake_state) {
690 /* Wait for inputs to be computed. */
691 return;
692 }
693 this->output_cached_state(params, user_data, data_block_map, *bake_state);
694 info.store_fn(std::move(*bake_state));
695 }
696
697 std::optional<bke::bake::BakeState> get_bake_state_from_inputs(
698 lf::Params &params, bke::bake::BakeDataBlockMap *data_block_map, const bool skip) const
699 {
700 /* Choose which set of input parameters to use. The others are ignored. */
701 const int params_offset = skip ? skip_inputs_offset_ : solve_inputs_offset_;
702 Array<SocketValueVariant *> input_value_pointers(simulation_items_.size());
703 for (const int i : simulation_items_.index_range()) {
704 input_value_pointers[i] = params.try_get_input_data_ptr_or_request<SocketValueVariant>(
705 i + params_offset);
706 }
707 if (input_value_pointers.as_span().contains(nullptr)) {
708 /* Wait for inputs to be computed. */
709 return std::nullopt;
710 }
711
712 Array<SocketValueVariant> input_values(simulation_items_.size());
713 for (const int i : simulation_items_.index_range()) {
714 input_values[i] = std::move(*input_value_pointers[i]);
715 }
716
717 return move_values_to_simulation_state(simulation_items_, input_values, data_block_map);
718 }
719};
720
722{
723 b.use_custom_socket_order();
724 b.allow_any_socket_order();
725 b.add_input<decl::Bool>("Skip").hide_value().description(
726 "Forward the output of the simulation input node directly to the output node and ignore "
727 "the nodes in the simulation zone");
728
729 const bNodeTree *tree = b.tree_or_null();
730 const bNode *node = b.node_or_null();
731 if (node == nullptr) {
732 return;
733 }
734
735 const NodeGeometrySimulationOutput &storage = node_storage(*node);
736
737 for (const int i : IndexRange(storage.items_num)) {
738 const NodeSimulationItem &item = storage.items[i];
739 const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type);
740 if (socket_type == SOCK_GEOMETRY && i > 0) {
741 b.add_separator();
742 }
743 const StringRef name = item.name;
744 const std::string identifier = SimulationItemsAccessor::socket_identifier_for_item(item);
745 auto &input_decl = b.add_input(socket_type, name, identifier)
746 .socket_name_ptr(
747 &tree->id, SimulationItemsAccessor::item_srna, &item, "name");
748 auto &output_decl = b.add_output(socket_type, name, identifier).align_with_previous();
749 if (socket_type_supports_fields(socket_type)) {
750 /* If it's below a geometry input it may be a field evaluated on that geometry. */
751 input_decl.supports_field().structure_type(StructureType::Dynamic);
752 output_decl.dependent_field({input_decl.index()});
753 }
754 }
755 b.add_input<decl::Extend>("", "__extend__").structure_type(StructureType::Dynamic);
756 b.add_output<decl::Extend>("", "__extend__")
757 .structure_type(StructureType::Dynamic)
758 .align_with_previous();
759}
760
761static void node_init(bNodeTree * /*tree*/, bNode *node)
762{
764
765 data->next_identifier = 0;
766
767 data->items = MEM_calloc_arrayN<NodeSimulationItem>(1, __func__);
768 data->items[0].name = BLI_strdup(DATA_("Geometry"));
769 data->items[0].socket_type = SOCK_GEOMETRY;
770 data->items[0].identifier = data->next_identifier++;
771 data->items_num = 1;
772
773 node->storage = data;
774}
775
781
782static void node_copy_storage(bNodeTree * /*dst_tree*/, bNode *dst_node, const bNode *src_node)
783{
784 const NodeGeometrySimulationOutput &src_storage = node_storage(*src_node);
785 auto *dst_storage = MEM_dupallocN<NodeGeometrySimulationOutput>(__func__, src_storage);
786 dst_node->storage = dst_storage;
787
789}
790
795
801
803{
804 BakeDrawContext ctx;
805 if (!get_bake_draw_context(&params.C, params.node, ctx)) {
806 return;
807 }
810 row.text = TIP_("Cannot bake in zone");
811 row.icon = ICON_ERROR;
812 params.rows.append(std::move(row));
813 }
814 if (ctx.is_baked) {
816 row.text = get_baked_string(ctx);
817 params.rows.append(std::move(row));
818 }
819}
820
822{
823 const bNodeSocket &other_socket = params.other_socket();
825 params.node_tree().type))
826 {
827 return;
828 }
829 params.add_item_full_name(IFACE_("Simulation"), [](LinkSearchOpParams &params) {
830 bNode &input_node = params.add_node("GeometryNodeSimulationInput");
831 bNode &output_node = params.add_node("GeometryNodeSimulationOutput");
832 output_node.location[0] = 300;
833
834 auto &input_storage = *static_cast<NodeGeometrySimulationInput *>(input_node.storage);
835 input_storage.output_node_id = output_node.identifier;
836
839 params.node_tree,
840 output_node,
841 eNodeSocketDatatype(params.socket.type),
842 params.socket.name);
843 update_node_declaration_and_sockets(params.node_tree, input_node);
844 update_node_declaration_and_sockets(params.node_tree, output_node);
845 if (params.socket.in_out == SOCK_IN) {
846 params.connect_available_socket(output_node, params.socket.name);
847 }
848 else {
849 params.connect_available_socket(input_node, params.socket.name);
850 }
851 params.node_tree.ensure_topology_cache();
852 bke::node_add_link(params.node_tree,
853 input_node,
854 input_node.output_socket(1),
855 output_node,
856 output_node.input_socket(1));
857 });
858}
859
860static void node_blend_write(const bNodeTree & /*tree*/, const bNode &node, BlendWriter &writer)
861{
863}
864
865static void node_blend_read(bNodeTree & /*tree*/, bNode &node, BlendDataReader &reader)
866{
868}
869
870static void node_register()
871{
872 static blender::bke::bNodeType ntype;
873
874 geo_node_type_base(&ntype, "GeometryNodeSimulationOutput", GEO_NODE_SIMULATION_OUTPUT);
875 ntype.ui_name = "Simulation Output";
876 ntype.ui_description = "Output data from the simulation zone";
877 ntype.enum_name_legacy = "SIMULATION_OUTPUT";
879 ntype.initfunc = node_init;
880 ntype.declare = node_declare;
885 ntype.no_muting = true;
891 ntype, "NodeGeometrySimulationOutput", node_free_storage, node_copy_storage);
893}
894NOD_REGISTER_NODE(node_register)
895
896} // namespace sim_output_node
897
898} // namespace blender::nodes::node_geo_simulation_cc
899
900namespace blender::nodes {
901
902std::unique_ptr<LazyFunction> get_simulation_input_lazy_function(
903 const bNodeTree &node_tree,
904 const bNode &node,
905 GeometryNodesLazyFunctionGraphInfo &own_lf_graph_info)
906{
908 return std::make_unique<
910 node_tree, node, own_lf_graph_info);
911}
912
913std::unique_ptr<LazyFunction> get_simulation_output_lazy_function(
914 const bNode &node, GeometryNodesLazyFunctionGraphInfo &own_lf_graph_info)
915{
917 return std::make_unique<
919 node, own_lf_graph_info);
920}
921
923 SocketValueVariant &prev,
925 const float factor)
926{
927 switch (socket_type) {
928 case SOCK_GEOMETRY: {
929 GeometrySet &prev_geo = *prev.get_single_ptr().get<GeometrySet>();
930 const GeometrySet &next_geo = *next.get_single_ptr().get<GeometrySet>();
931 prev_geo = geometry::mix_geometries(std::move(prev_geo), next_geo, factor);
932 break;
933 }
934 case SOCK_FLOAT:
935 case SOCK_VECTOR:
936 case SOCK_INT:
937 case SOCK_BOOLEAN:
938 case SOCK_ROTATION:
939 case SOCK_RGBA:
940 case SOCK_MATRIX: {
941 const CPPType &type = *bke::socket_type_to_geo_nodes_base_cpp_type(socket_type);
942 if (!prev.is_single() || !next.is_single()) {
943 /* Fields are evaluated on geometries and are mixed there. */
944 break;
945 }
946
947 prev.convert_to_single();
948
949 SocketValueVariant next_copy = next;
950 next_copy.convert_to_single();
951
952 void *prev_value = prev.get_single_ptr().get();
953 const void *next_value = next_copy.get_single_ptr().get();
954
955 bke::attribute_math::convert_to_static_type(type, [&](auto dummy) {
956 using T = decltype(dummy);
957 *static_cast<T *>(prev_value) = bke::attribute_math::mix2(
958 factor, *static_cast<T *>(prev_value), *static_cast<const T *>(next_value));
959 });
960 break;
961 }
962 default:
963 break;
964 }
965}
966
967StructRNA *SimulationItemsAccessor::item_srna = &RNA_SimulationStateItem;
968
970{
971 BLO_write_string(writer, item.name);
972}
973
978
979} // namespace blender::nodes
980
981blender::Span<NodeSimulationItem> NodeGeometrySimulationOutput::items_span() const
982{
983 return blender::Span<NodeSimulationItem>(items, items_num);
984}
985
986blender::MutableSpan<NodeSimulationItem> NodeGeometrySimulationOutput::items_span()
987{
988 return blender::MutableSpan<NodeSimulationItem>(items, items_num);
989}
Low-level operations for curves.
#define NODE_CLASS_INTERFACE
Definition BKE_node.hh:459
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1240
#define GEO_NODE_SIMULATION_OUTPUT
#define GEO_NODE_SIMULATION_INPUT
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define final(a, b, c)
Definition BLI_hash.h:19
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.cc:41
char * BLI_strncpy_utf8(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
#define ELEM(...)
void BLO_read_string(BlendDataReader *reader, char **ptr_p)
Definition readfile.cc:5828
void BLO_write_string(BlendWriter *writer, const char *data_ptr)
#define BLT_I18NCONTEXT_ID_NODETREE
#define TIP_(msgid)
#define CTX_IFACE_(context, msgid)
#define IFACE_(msgid)
#define DATA_(msgid)
#define ID_IS_EDITABLE(_id)
Definition DNA_ID.h:705
@ SOCK_IN
eNodeSocketDatatype
@ SOCK_INT
@ SOCK_VECTOR
@ SOCK_BOOLEAN
@ SOCK_MATRIX
@ SOCK_FLOAT
@ SOCK_GEOMETRY
@ SOCK_ROTATION
@ SOCK_RGBA
#define NOD_REGISTER_NODE(REGISTER_FUNC)
#define C
Definition RandGen.cpp:29
#define UI_ITEM_NONE
BMesh const char void * data
void resize(const int64_t new_size)
Span< T > as_span() const
Definition BLI_array.hh:243
static const CPPType & get()
const ComputeContextHash & hash() const
const void * get() const
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
void append(const T &value)
void resize(const int64_t new_size)
std::optional< int > output_node_id
const bNode * output_node() const
const bNodeTreeZone * get_zone_by_node(const int32_t node_id) const
virtual SimulationZoneBehavior * get(const int zone_id) const =0
void output_simulation_state_move(lf::Params &params, const GeoNodesUserData &user_data, bke::bake::BakeDataBlockMap *data_block_map, bke::bake::BakeState zone_state) const
void pass_through(lf::Params &params, const GeoNodesUserData &user_data, bke::bake::BakeDataBlockMap *data_block_map) const
void execute_impl(lf::Params &params, const lf::Context &context) const final
LazyFunctionForSimulationInputNode(const bNodeTree &node_tree, const bNode &node, GeometryNodesLazyFunctionGraphInfo &own_lf_graph_info)
void output_simulation_state_copy(lf::Params &params, const GeoNodesUserData &user_data, bke::bake::BakeDataBlockMap *data_block_map, const bke::bake::BakeStateRef &zone_state) const
void output_mixed_cached_state(lf::Params &params, bke::bake::BakeDataBlockMap *data_block_map, const Object &self_object, const ComputeContext &compute_context, const bke::bake::BakeStateRef &prev_state, const bke::bake::BakeStateRef &next_state, const float mix_factor) const
void pass_through(lf::Params &params, GeoNodesUserData &user_data, bke::bake::BakeDataBlockMap *data_block_map) const
void execute_impl(lf::Params &params, const lf::Context &context) const final
void store_new_state(lf::Params &params, GeoNodesUserData &user_data, bke::bake::BakeDataBlockMap *data_block_map, const sim_output::StoreNewState &info) const
LazyFunctionForSimulationOutputNode(const bNode &node, GeometryNodesLazyFunctionGraphInfo &own_lf_graph_info)
void output_cached_state(lf::Params &params, GeoNodesUserData &user_data, bke::bake::BakeDataBlockMap *data_block_map, const bke::bake::BakeStateRef &state) const
std::optional< bke::bake::BakeState > get_bake_state_from_inputs(lf::Params &params, bke::bake::BakeDataBlockMap *data_block_map, const bool skip) const
KDTree_3d * tree
uint col
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static ulong * next
static ulong state[N]
static void next_state()
#define T
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
T mix2(float factor, const T &a, const T &b)
Array< std::unique_ptr< BakeItem > > move_socket_values_to_bake_items(MutableSpan< SocketValueVariant > socket_values, const BakeSocketConfig &config, BakeDataBlockMap *data_block_map)
Vector< SocketValueVariant > copy_bake_items_to_socket_values(Span< const BakeItem * > bake_items, const BakeSocketConfig &config, BakeDataBlockMap *data_block_map, FunctionRef< std::shared_ptr< AttributeFieldInput >(int, const CPPType &)> make_attribute_field)
Vector< SocketValueVariant > move_bake_items_to_socket_values(Span< BakeItem * > bake_items, const BakeSocketConfig &config, BakeDataBlockMap *data_block_map, FunctionRef< std::shared_ptr< AttributeFieldInput >(int socket_index, const CPPType &)> make_attribute_field)
void node_register_type(bNodeType &ntype)
Definition node.cc:2416
const CPPType * socket_type_to_geo_nodes_base_cpp_type(eNodeSocketDatatype type)
Definition node.cc:5202
bNodeLink & node_add_link(bNodeTree &ntree, bNode &fromnode, bNodeSocket &fromsock, bNode &tonode, bNodeSocket &tosock)
Definition node.cc:3810
std::string hash_to_anonymous_attribute_name(Args &&...args)
void node_type_storage(bNodeType &ntype, std::optional< StringRefNull > storagename, void(*freefunc)(bNode *node), void(*copyfunc)(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node))
Definition node.cc:5414
bke::GeometrySet mix_geometries(bke::GeometrySet a, const bke::GeometrySet &b, float factor)
static void node_label(const bNodeTree *, const bNode *, char *label, const int label_maxncpy)
static bool node_insert_link(bke::NodeInsertLinkParams &params)
static void node_blend_write(const bNodeTree &, const bNode &node, BlendWriter &writer)
static void node_copy_storage(bNodeTree *, bNode *dst_node, const bNode *src_node)
static void node_extra_info(NodeExtraInfoParams &params)
static bool node_insert_link(bke::NodeInsertLinkParams &params)
static void node_gather_link_searches(GatherLinkSearchOpParams &params)
static void node_blend_read(bNodeTree &, bNode &node, BlendDataReader &reader)
static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *current_node_ptr)
static std::shared_ptr< AttributeFieldInput > make_attribute_field(const Object &self_object, const ComputeContext &compute_context, const bNode &node, const NodeSimulationItem &item, const CPPType &type)
static void draw_simulation_state(const bContext *C, uiLayout *layout, bNodeTree &ntree, bNode &output_node)
static Vector< SocketValueVariant > copy_simulation_state_to_values(const Span< NodeSimulationItem > node_simulation_items, const bke::bake::BakeStateRef &zone_state, const Object &self_object, const ComputeContext &compute_context, const bNode &node, bke::bake::BakeDataBlockMap *data_block_map)
static bke::bake::BakeState move_values_to_simulation_state(const Span< NodeSimulationItem > node_simulation_items, MutableSpan< SocketValueVariant > input_values, bke::bake::BakeDataBlockMap *data_block_map)
static bke::bake::BakeSocketConfig make_bake_socket_config(const Span< NodeSimulationItem > node_simulation_items)
static Vector< SocketValueVariant > move_simulation_state_to_values(const Span< NodeSimulationItem > node_simulation_items, bke::bake::BakeState zone_state, const Object &self_object, const ComputeContext &compute_context, const bNode &node, bke::bake::BakeDataBlockMap *data_block_map)
std::variant< PassThrough, OutputCopy, OutputMove > Behavior
std::variant< PassThrough, StoreNewState, ReadSingle, ReadInterpolated, ReadError > Behavior
static void draw_items_list_with_operators(const bContext *C, uiLayout *layout, const bNodeTree &tree, const bNode &node)
static void draw_active_item_props(const bNodeTree &tree, const bNode &node, const FunctionRef< void(PointerRNA *item_ptr)> draw_item)
void blend_write(BlendWriter *writer, const bNode &node)
void blend_read_data(BlendDataReader *reader, bNode &node)
void copy_array(const bNode &src_node, bNode &dst_node)
Accessor::ItemT * add_item_with_socket_type_and_name(bNodeTree &ntree, bNode &node, const eNodeSocketDatatype socket_type, const char *name, std::optional< int > dimensions=std::nullopt)
bool try_add_item_via_any_extend_socket(bNodeTree &ntree, bNode &extend_node, bNode &storage_node, bNodeLink &link, const std::optional< StringRef > socket_identifier=std::nullopt, typename Accessor::ItemT **r_new_item=nullptr)
void mix_baked_data_item(eNodeSocketDatatype socket_type, SocketValueVariant &prev, const SocketValueVariant &next, const float factor)
bool get_bake_draw_context(const bContext *C, const bNode &node, BakeDrawContext &r_ctx)
std::unique_ptr< LazyFunction > get_simulation_output_lazy_function(const bNode &node, GeometryNodesLazyFunctionGraphInfo &own_lf_graph_info)
void draw_data_blocks(const bContext *C, uiLayout *layout, PointerRNA &bake_rna)
std::unique_ptr< LazyFunction > get_simulation_input_lazy_function(const bNodeTree &node_tree, const bNode &node, GeometryNodesLazyFunctionGraphInfo &own_lf_graph_info)
void update_node_declaration_and_sockets(bNodeTree &ntree, bNode &node)
void draw_common_bake_settings(bContext *C, BakeDrawContext &ctx, uiLayout *layout)
void draw_bake_button_row(const BakeDrawContext &ctx, uiLayout *layout, bool is_in_sidebar=false)
std::string make_anonymous_attribute_socket_inspection_string(const bNodeSocket &socket)
std::optional< FoundNestedNodeID > find_nested_node_id(const GeoNodesUserData &user_data, const int node_id)
bool socket_type_supports_fields(const eNodeSocketDatatype socket_type)
void set_default_remaining_node_outputs(lf::Params &params, const bNode &node)
std::optional< std::string > get_bake_state_string(const BakeDrawContext &ctx)
std::string get_baked_string(const BakeDrawContext &ctx)
void geo_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
void node_free_standard_storage(bNode *node)
Definition node_util.cc:42
void node_copy_standard_storage(bNodeTree *, bNode *dest_node, const bNode *src_node)
Definition node_util.cc:54
const char * name
char name[258]
Definition DNA_ID.h:432
ID * owner_id
Definition RNA_types.hh:51
void * data
Definition RNA_types.hh:53
float location[2]
int16_t type_legacy
void * storage
int32_t identifier
Defines a node type.
Definition BKE_node.hh:238
NodeBlendWriteFunction blend_write_storage_content
Definition BKE_node.hh:390
std::string ui_description
Definition BKE_node.hh:244
NodeBlendDataReadFunction blend_data_read_storage_content
Definition BKE_node.hh:391
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:289
void(* labelfunc)(const bNodeTree *ntree, const bNode *node, char *label, int label_maxncpy)
Definition BKE_node.hh:270
void(* draw_buttons_ex)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:261
NodeExtraInfoFunction get_extra_info
Definition BKE_node.hh:381
const char * enum_name_legacy
Definition BKE_node.hh:247
bool(* insert_link)(NodeInsertLinkParams &params)
Definition BKE_node.hh:333
NodeGatherSocketLinkOperationsFunction gather_link_search_ops
Definition BKE_node.hh:378
NodeDeclareFunction declare
Definition BKE_node.hh:362
void(* register_operators)()
Definition BKE_node.hh:417
Vector< Vector< int, 1 > > geometries_by_attribute
Map< int, const BakeItem * > items_by_id
Map< int, std::unique_ptr< BakeItem > > items_by_id
geo_eval_log::GeoTreeLogger * try_get_tree_logger(const GeoNodesUserData &user_data) const
static void blend_read_data_item(BlendDataReader *reader, ItemT &item)
static bool supports_socket_type(const eNodeSocketDatatype socket_type, const int)
static std::string socket_identifier_for_item(const NodeSimulationItem &item)
static void blend_write_item(BlendWriter *writer, const ItemT &item)
std::function< void(bke::bake::BakeState state)> store_fn
void use_property_decorate_set(bool is_sep)
PanelLayout panel(const bContext *C, blender::StringRef idname, bool default_closed)
void label(blender::StringRef name, int icon)
uiLayout & column(bool align)
void active_set(bool active)
void enabled_set(bool enabled)
void use_property_split_set(bool value)
i
Definition text_draw.cc:230