Blender V4.5
geometry_nodes_foreach_geometry_element_zone.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
6
9#include "BKE_curves.hh"
11#include "BKE_grease_pencil.hh"
12#include "BKE_node_runtime.hh"
14
17
19
20#include "BLT_translation.hh"
21
23
24namespace blender::nodes {
25
26using bke::AttrDomain;
27using bke::AttributeAccessor;
28using bke::GeometryComponent;
29using bke::GeometrySet;
30using bke::MutableAttributeAccessor;
31using bke::SocketValueVariant;
32using fn::Field;
33using fn::GField;
34
37
43
53 std::optional<bke::GeometryFieldContext> field_context;
54 std::optional<fn::FieldEvaluator> field_evaluator;
60 std::optional<Array<GeometrySet>> element_geometries;
64
66 {
67 if (this->id.component_type == GeometryComponent::Type::GreasePencil &&
68 ELEM(this->id.domain, AttrDomain::Point, AttrDomain::Curve))
69 {
70 this->field_context.emplace(
71 *geometry.get_grease_pencil(), this->id.domain, *this->id.layer_index);
72 }
73 else {
74 this->field_context.emplace(*geometry.get_component(this->id.component_type),
75 this->id.domain);
76 }
77 }
78
80 {
81 return *this->field_context->attributes();
82 }
83
85 {
86 if (this->id.component_type == GeometryComponent::Type::GreasePencil &&
87 ELEM(this->id.domain, AttrDomain::Point, AttrDomain::Curve))
88 {
89 BLI_assert(this->id.layer_index.has_value());
90 GreasePencil *grease_pencil = geometry.get_grease_pencil_for_write();
91 const bke::greasepencil::Layer &layer = grease_pencil->layer(*this->id.layer_index);
92 bke::greasepencil::Drawing *drawing = grease_pencil->get_eval_drawing(layer);
93 BLI_assert(drawing);
94 return drawing->strokes_for_write().attributes_for_write();
95 }
96 GeometryComponent &component = geometry.get_component_for_write(this->id.component_type);
97 return *component.attributes_for_write();
98 }
99};
100
108
109 public:
113
114 void execute_impl(lf::Params &params, const lf::Context &context) const override;
115
116 void handle_main_items_and_geometry(lf::Params &params, const lf::Context &context) const;
117 void handle_generation_items(lf::Params &params, const lf::Context &context) const;
120 const lf::Context &context,
121 int first_valid_item_i) const;
123 const lf::Context &context,
124 int geometry_item_i,
125 IndexRange generation_items_range) const;
127 const lf::Context &context,
128 int geometry_item_i,
129 IndexRange generation_items_range) const;
130};
131
137 public:
138 const bNode *output_bnode_ = nullptr;
140
143 const lf::Context &context) const override
144 {
145 GeoNodesUserData &user_data = *static_cast<GeoNodesUserData *>(context.user_data);
146 const int index = lf_body_nodes_->index_of_try(const_cast<lf::FunctionNode *>(&node));
147 const LazyFunction &fn = node.function();
148 if (index == -1) {
149 /* The node is not a loop body node, just execute it normally. */
150 fn.execute(params, context);
151 return;
152 }
153
154 /* Setup context for the loop body evaluation. */
156 user_data.compute_context, *output_bnode_, index};
157 GeoNodesUserData body_user_data = user_data;
158 body_user_data.compute_context = &body_compute_context;
160 user_data, body_compute_context.hash());
161
162 GeoNodesLocalUserData body_local_user_data{body_user_data};
163 lf::Context body_context{context.storage, &body_user_data, &body_local_user_data};
164 fn.execute(params, body_context);
165 }
166};
167
173 public:
174 const bNode *output_bnode_ = nullptr;
176
178 const lf::Context &context) const override
179 {
180 GeoNodesUserData &user_data = *static_cast<GeoNodesUserData *>(context.user_data);
181 const GeoNodesCallData &call_data = *user_data.call_data;
182 if (!call_data.side_effect_nodes) {
183 return {};
184 }
185 const ComputeContextHash &context_hash = user_data.compute_context->hash();
186 const Span<int> iterations_with_side_effects =
188 {context_hash, output_bnode_->identifier});
189
191 for (const int i : iterations_with_side_effects) {
192 if (i >= 0 && i < lf_body_nodes_.size()) {
193 lf_nodes.append(lf_body_nodes_[i]);
194 }
195 }
196 return lf_nodes;
197 }
198};
199
206
209 std::optional<ForeachGeometryElementZoneSideEffectProvider> side_effect_provider;
210 std::optional<ForeachGeometryElementNodeExecuteWrapper> body_execute_wrapper;
211 std::optional<lf::GraphExecutor> graph_executor;
212 void *graph_executor_storage = nullptr;
213
215 std::optional<LazyFunctionForLogicalOr> or_function;
216 std::optional<LazyFunctionForReduceForeachGeometryElement> reduce_function;
217
223
230};
231
233 private:
234 const bNodeTree &btree_;
235 const bke::bNodeTreeZone &zone_;
236 const bNode &output_bnode_;
237 const ZoneBuildInfo &zone_info_;
238 const ZoneBodyFunction &body_fn_;
239
240 struct ItemIndices {
241 /* `outer` refers to sockets on the outside of the zone, and `inner` to the sockets on the
242 * inside. The `lf` and `bsocket` indices are similar, but the `lf` indices skip unavailable
243 * and extend sockets. */
244 IndexRange lf_outer;
245 IndexRange lf_inner;
246 IndexRange bsocket_outer;
247 IndexRange bsocket_inner;
248 };
249
251 struct {
252 ItemIndices inputs;
253 ItemIndices main;
254 ItemIndices generation;
255 } indices_;
256
258
259 public:
261 const bke::bNodeTreeZone &zone,
262 ZoneBuildInfo &zone_info,
263 const ZoneBodyFunction &body_fn)
264 : btree_(btree),
265 zone_(zone),
266 output_bnode_(*zone.output_node()),
267 zone_info_(zone_info),
268 body_fn_(body_fn)
269 {
270 debug_name_ = "Foreach Geometry Element";
271
272 initialize_zone_wrapper(zone, zone_info, body_fn, true, inputs_, outputs_);
273 /* All main inputs are always used for now. */
274 for (const int i : zone_info.indices.inputs.main) {
276 }
277
278 const auto &node_storage = *static_cast<const NodeGeometryForeachGeometryElementOutput *>(
279 output_bnode_.storage);
280 const AttrDomain iteration_domain = AttrDomain(node_storage.domain);
281 BLI_assert(zone_.input_node()->output_socket(1).is_available() ==
282 (iteration_domain != AttrDomain::Corner));
283
284 const int input_items_num = node_storage.input_items.items_num;
285 const int main_items_num = node_storage.main_items.items_num;
286 const int generation_items_num = node_storage.generation_items.items_num;
287
288 indices_.inputs.lf_outer = IndexRange::from_begin_size(2, input_items_num);
289 indices_.inputs.lf_inner = IndexRange::from_begin_size(
290 iteration_domain == AttrDomain::Corner ? 1 : 2, input_items_num);
291 indices_.inputs.bsocket_outer = indices_.inputs.lf_outer;
292 indices_.inputs.bsocket_inner = indices_.inputs.lf_inner;
293
294 indices_.main.lf_outer = IndexRange::from_begin_size(1, main_items_num);
295 indices_.main.lf_inner = IndexRange::from_begin_size(0, main_items_num);
296 indices_.main.bsocket_outer = indices_.main.lf_outer;
297 indices_.main.bsocket_inner = indices_.main.lf_inner;
298
299 indices_.generation.lf_outer = IndexRange::from_begin_size(1 + main_items_num,
300 generation_items_num);
301 indices_.generation.lf_inner = IndexRange::from_begin_size(main_items_num,
302 generation_items_num);
303 indices_.generation.bsocket_outer = IndexRange::from_begin_size(2 + main_items_num,
304 generation_items_num);
305 indices_.generation.bsocket_inner = IndexRange::from_begin_size(1 + main_items_num,
306 generation_items_num);
307 }
308
309 void *init_storage(LinearAllocator<> &allocator) const override
310 {
311 return allocator.construct<ForeachGeometryElementEvalStorage>().release();
312 }
313
314 void destruct_storage(void *storage) const override
315 {
316 auto *s = static_cast<ForeachGeometryElementEvalStorage *>(storage);
317 if (s->graph_executor_storage) {
318 s->graph_executor->destruct_storage(s->graph_executor_storage);
319 }
320 std::destroy_at(s);
321 }
322
323 void execute_impl(lf::Params &params, const lf::Context &context) const override
324 {
325 const ScopedNodeTimer node_timer{context, output_bnode_};
326
327 auto &user_data = *static_cast<GeoNodesUserData *>(context.user_data);
328 auto &local_user_data = *static_cast<GeoNodesLocalUserData *>(context.local_user_data);
329
330 const auto &node_storage = *static_cast<const NodeGeometryForeachGeometryElementOutput *>(
331 output_bnode_.storage);
332 auto &eval_storage = *static_cast<ForeachGeometryElementEvalStorage *>(context.storage);
333 geo_eval_log::GeoTreeLogger *tree_logger = local_user_data.try_get_tree_logger(user_data);
334
335 if (!eval_storage.graph_executor) {
336 /* Create the execution graph in the first evaluation. */
337 this->initialize_execution_graph(params, eval_storage, node_storage);
338
339 if (tree_logger) {
340 if (eval_storage.total_iterations_num == 0) {
341 if (!eval_storage.main_geometry.is_empty()) {
342 tree_logger->node_warnings.append(
343 *tree_logger->allocator,
344 {zone_.input_node()->identifier,
345 {NodeWarningType::Info,
346 N_("Input geometry has no elements in the iteration domain.")}});
347 }
348 }
349 }
350 }
351
352 lf::Context eval_graph_context{
353 eval_storage.graph_executor_storage, context.user_data, context.local_user_data};
354
355 eval_storage.graph_executor->execute(params, eval_graph_context);
356 }
357
361 const NodeGeometryForeachGeometryElementOutput &node_storage) const
362 {
363 eval_storage.main_geometry = params.extract_input<GeometrySet>(
364 zone_info_.indices.inputs.main[0]);
365
366 /* Find all the things we need to iterate over in the input geometry. */
367 this->prepare_components(params, eval_storage, node_storage);
368
369 /* Add interface sockets for the zone graph. Those are the same as for the entire zone, even
370 * though some of the inputs are not strictly needed anymore. It's easier to avoid another
371 * level of index remapping though. */
372 lf::Graph &lf_graph = eval_storage.graph;
375 for (const int i : inputs_.index_range()) {
376 const lf::Input &input = inputs_[i];
377 graph_inputs.append(&lf_graph.add_input(*input.type, this->input_name(i)));
378 }
379 for (const int i : outputs_.index_range()) {
380 const lf::Output &output = outputs_[i];
381 graph_outputs.append(&lf_graph.add_output(*output.type, this->output_name(i)));
382 }
383
384 /* Add all the nodes and links to the graph. */
385 this->build_graph_contents(eval_storage, node_storage, graph_inputs, graph_outputs);
386
387 eval_storage.side_effect_provider.emplace();
388 eval_storage.side_effect_provider->output_bnode_ = &output_bnode_;
389 eval_storage.side_effect_provider->lf_body_nodes_ = eval_storage.lf_body_nodes;
390
391 eval_storage.body_execute_wrapper.emplace();
392 eval_storage.body_execute_wrapper->output_bnode_ = &output_bnode_;
393 eval_storage.body_execute_wrapper->lf_body_nodes_ = &eval_storage.lf_body_nodes;
394
395 lf_graph.update_node_indices();
396 eval_storage.graph_executor.emplace(lf_graph,
397 graph_inputs.as_span(),
398 graph_outputs.as_span(),
399 nullptr,
400 &*eval_storage.side_effect_provider,
401 &*eval_storage.body_execute_wrapper);
402 eval_storage.graph_executor_storage = eval_storage.graph_executor->init_storage(
403 eval_storage.allocator);
404
405 /* Log graph for debugging purposes. */
406 const bNodeTree &btree_orig = *DEG_get_original(&btree_);
407 if (btree_orig.runtime->logged_zone_graphs) {
408 std::lock_guard lock{btree_orig.runtime->logged_zone_graphs->mutex};
409 btree_orig.runtime->logged_zone_graphs->graph_by_zone_id.lookup_or_add_cb(
410 output_bnode_.identifier, [&]() { return lf_graph.to_dot(); });
411 }
412 }
413
416 const NodeGeometryForeachGeometryElementOutput &node_storage) const
417 {
418 const AttrDomain iteration_domain = AttrDomain(node_storage.domain);
419
420 /* TODO: Get propagation info from input, but that's not necessary for correctness for now. */
421 bke::AttributeFilter attribute_filter;
422
423 const bNodeSocket &element_geometry_bsocket = zone_.input_node()->output_socket(1);
424 const bool create_element_geometries = element_geometry_bsocket.is_available() &&
425 element_geometry_bsocket.is_directly_linked();
426
427 /* Gather components to process. */
429 for (const GeometryComponent *src_component : eval_storage.main_geometry.get_components()) {
430 const GeometryComponent::Type component_type = src_component->type();
431 if (src_component->type() == GeometryComponent::Type::GreasePencil &&
432 ELEM(iteration_domain, AttrDomain::Point, AttrDomain::Curve))
433 {
434 const GreasePencil &grease_pencil = *eval_storage.main_geometry.get_grease_pencil();
435 for (const int layer_i : grease_pencil.layers().index_range()) {
436 const bke::greasepencil::Drawing *drawing = grease_pencil.get_eval_drawing(
437 grease_pencil.layer(layer_i));
438 if (drawing == nullptr) {
439 continue;
440 }
441 const bke::CurvesGeometry &curves = drawing->strokes();
442 if (curves.is_empty()) {
443 continue;
444 }
445 component_ids.append({component_type, iteration_domain, layer_i});
446 }
447 }
448 else {
449 const int domain_size = src_component->attribute_domain_size(iteration_domain);
450 if (domain_size > 0) {
451 component_ids.append({component_type, iteration_domain});
452 }
453 }
454 }
455
456 const Field<bool> selection_field = params
457 .extract_input<SocketValueVariant>(
458 zone_info_.indices.inputs.main[1])
460
461 /* Evaluate the selection and field inputs for all components. */
462 int body_nodes_offset = 0;
463 eval_storage.components.reinitialize(component_ids.size());
464 for (const int component_i : component_ids.index_range()) {
465 const ForeachElementComponentID id = component_ids[component_i];
466 ForeachElementComponent &component_info = eval_storage.components[component_i];
467 component_info.id = id;
468 component_info.emplace_field_context(eval_storage.main_geometry);
469
470 const int domain_size = component_info.input_attributes().domain_size(id.domain);
471 BLI_assert(domain_size > 0);
472
473 /* Prepare field evaluation for the zone inputs. */
474 component_info.field_evaluator.emplace(*component_info.field_context, domain_size);
475 component_info.field_evaluator->set_selection(selection_field);
476 for (const int item_i : IndexRange(node_storage.input_items.items_num)) {
477 const GField item_field =
478 params
479 .get_input<SocketValueVariant>(
480 zone_info_.indices.inputs.main[indices_.inputs.lf_outer[item_i]])
481 .get<GField>();
482 component_info.field_evaluator->add(item_field);
483 }
484
485 /* Evaluate all fields passed to the zone input. */
486 component_info.field_evaluator->evaluate();
487
488 /* The mask contains all the indices that should be iterated over in the component. */
489 const IndexMask mask = component_info.field_evaluator->get_evaluated_selection_as_mask();
490 component_info.body_nodes_range = IndexRange::from_begin_size(body_nodes_offset,
491 mask.size());
492 body_nodes_offset += mask.size();
493
494 /* Prepare indices that are passed into each iteration. */
495 component_info.index_values.reinitialize(mask.size());
496 mask.foreach_index(
497 [&](const int i, const int pos) { component_info.index_values[pos].set(i); });
498
499 if (create_element_geometries) {
501 eval_storage.main_geometry, id, mask, attribute_filter);
502 }
503
504 /* Prepare remaining inputs that come from the field evaluation. */
505 component_info.item_input_values.reinitialize(node_storage.input_items.items_num);
506 for (const int item_i : IndexRange(node_storage.input_items.items_num)) {
507 const NodeForeachGeometryElementInputItem &item = node_storage.input_items.items[item_i];
508 const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type);
509 component_info.item_input_values[item_i].reinitialize(mask.size());
510 const GVArray &values = component_info.field_evaluator->get_evaluated(item_i);
511 mask.foreach_index(GrainSize(1024), [&](const int i, const int pos) {
512 SocketValueVariant &value_variant = component_info.item_input_values[item_i][pos];
513 void *buffer = value_variant.allocate_single(socket_type);
514 values.get_to_uninitialized(i, buffer);
515 });
516 }
517 }
518
519 eval_storage.total_iterations_num = body_nodes_offset;
520 }
521
522 std::optional<Array<GeometrySet>> try_extract_element_geometries(
523 const GeometrySet &main_geometry,
525 const IndexMask &mask,
526 const bke::AttributeFilter &attribute_filter) const
527 {
528 switch (id.component_type) {
529 case GeometryComponent::Type::Mesh: {
530 const Mesh &main_mesh = *main_geometry.get_mesh();
531 Array<Mesh *> meshes;
532 switch (id.domain) {
533 case AttrDomain::Point: {
534 meshes = geometry::extract_mesh_vertices(main_mesh, mask, attribute_filter);
535 break;
536 }
537 case AttrDomain::Edge: {
538 meshes = geometry::extract_mesh_edges(main_mesh, mask, attribute_filter);
539 break;
540 }
541 case AttrDomain::Face: {
542 meshes = geometry::extract_mesh_faces(main_mesh, mask, attribute_filter);
543 break;
544 }
545 default: {
546 return std::nullopt;
547 }
548 }
549 Array<GeometrySet> element_geometries(meshes.size());
550 for (const int i : meshes.index_range()) {
551 element_geometries[i].replace_mesh(meshes[i]);
552 }
553 return element_geometries;
554 }
555 case GeometryComponent::Type::PointCloud: {
556 if (id.domain != AttrDomain::Point) {
557 return std::nullopt;
558 }
559 const PointCloud &main_pointcloud = *main_geometry.get_pointcloud();
561 main_pointcloud, mask, attribute_filter);
562 Array<GeometrySet> element_geometries(pointclouds.size());
563 for (const int i : pointclouds.index_range()) {
564 element_geometries[i].replace_pointcloud(pointclouds[i]);
565 }
566 return element_geometries;
567 }
568 case GeometryComponent::Type::Curve: {
569 const Curves &main_curves = *main_geometry.get_curves();
570 Array<Curves *> element_curves;
571 switch (id.domain) {
572 case AttrDomain::Point: {
573 element_curves = geometry::extract_curves_points(main_curves, mask, attribute_filter);
574 break;
575 }
576 case AttrDomain::Curve: {
577 element_curves = geometry::extract_curves(main_curves, mask, attribute_filter);
578 break;
579 }
580 default:
581 return std::nullopt;
582 }
583 Array<GeometrySet> element_geometries(element_curves.size());
584 for (const int i : element_curves.index_range()) {
585 element_geometries[i].replace_curves(element_curves[i]);
586 }
587 return element_geometries;
588 }
589 case GeometryComponent::Type::Instance: {
590 if (id.domain != AttrDomain::Instance) {
591 return std::nullopt;
592 }
593 const bke::Instances &main_instances = *main_geometry.get_instances();
595 main_instances, mask, attribute_filter);
596 Array<GeometrySet> element_geometries(element_instances.size());
597 for (const int i : element_instances.index_range()) {
598 element_geometries[i].replace_instances(element_instances[i]);
599 }
600 return element_geometries;
601 }
602 case GeometryComponent::Type::GreasePencil: {
603 const GreasePencil &main_grease_pencil = *main_geometry.get_grease_pencil();
604 Array<GreasePencil *> element_grease_pencils;
605 switch (id.domain) {
606 case AttrDomain::Layer: {
607 element_grease_pencils = geometry::extract_greasepencil_layers(
608 main_grease_pencil, mask, attribute_filter);
609 break;
610 }
611 case AttrDomain::Point: {
612 element_grease_pencils = geometry::extract_greasepencil_layer_points(
613 main_grease_pencil, *id.layer_index, mask, attribute_filter);
614 break;
615 }
616 case AttrDomain::Curve: {
617 element_grease_pencils = geometry::extract_greasepencil_layer_curves(
618 main_grease_pencil, *id.layer_index, mask, attribute_filter);
619 break;
620 }
621 default:
622 return std::nullopt;
623 }
624 Array<GeometrySet> element_geometries(element_grease_pencils.size());
625 for (const int i : element_geometries.index_range()) {
626 element_geometries[i].replace_grease_pencil(element_grease_pencils[i]);
627 }
628 return element_geometries;
629 }
630 default:
631 break;
632 }
633 return std::nullopt;
634 }
635
638 Span<lf::GraphInputSocket *> graph_inputs,
639 Span<lf::GraphOutputSocket *> graph_outputs) const
640 {
641 lf::Graph &lf_graph = eval_storage.graph;
642
643 /* Create body nodes. */
644 VectorSet<lf::FunctionNode *> &lf_body_nodes = eval_storage.lf_body_nodes;
645 for ([[maybe_unused]] const int i : IndexRange(eval_storage.total_iterations_num)) {
646 lf::FunctionNode &lf_node = lf_graph.add_function(*body_fn_.function);
647 lf_body_nodes.add_new(&lf_node);
648 }
649
650 /* Link up output usages to body nodes. */
651 for (const int zone_output_i : body_fn_.indices.inputs.output_usages.index_range()) {
652 /* +1 because of geometry output. */
653 lf::GraphInputSocket &lf_graph_input =
654 *graph_inputs[zone_info_.indices.inputs.output_usages[1 + zone_output_i]];
655 for (const int i : lf_body_nodes.index_range()) {
656 lf::FunctionNode &lf_node = *lf_body_nodes[i];
657 lf_graph.add_link(lf_graph_input,
658 lf_node.input(body_fn_.indices.inputs.output_usages[zone_output_i]));
659 }
660 }
661
662 const bNodeSocket &element_geometry_bsocket = zone_.input_node()->output_socket(1);
663
664 static const GeometrySet empty_geometry;
665 for (const ForeachElementComponent &component_info : eval_storage.components) {
666 for (const int i : component_info.body_nodes_range.index_range()) {
667 const int body_i = component_info.body_nodes_range[i];
668 lf::FunctionNode &lf_body_node = *lf_body_nodes[body_i];
669 /* Set index input for loop body. */
670 lf_body_node.input(body_fn_.indices.inputs.main[0])
671 .set_default_value(&component_info.index_values[i]);
672 /* Set geometry element input for loop body. */
673 if (element_geometry_bsocket.is_available()) {
674 const GeometrySet *element_geometry = component_info.element_geometries.has_value() ?
675 &(*component_info.element_geometries)[i] :
676 &empty_geometry;
677 lf_body_node.input(body_fn_.indices.inputs.main[1]).set_default_value(element_geometry);
678 }
679 /* Set main input values for loop body. */
680 for (const int item_i : IndexRange(node_storage.input_items.items_num)) {
681 lf_body_node.input(body_fn_.indices.inputs.main[indices_.inputs.lf_inner[item_i]])
682 .set_default_value(&component_info.item_input_values[item_i][i]);
683 }
684 /* Link up border-link inputs to the loop body. */
685 for (const int border_link_i : zone_info_.indices.inputs.border_links.index_range()) {
686 lf_graph.add_link(
687 *graph_inputs[zone_info_.indices.inputs.border_links[border_link_i]],
688 lf_body_node.input(body_fn_.indices.inputs.border_links[border_link_i]));
689 }
690 /* Link up reference sets. */
691 for (const auto &item : body_fn_.indices.inputs.reference_sets.items()) {
692 lf_graph.add_link(
693 *graph_inputs[zone_info_.indices.inputs.reference_sets.lookup(item.key)],
694 lf_body_node.input(item.value));
695 }
696 }
697 }
698
699 /* Add the reduce function that has all outputs from the zone bodies as input. */
700 eval_storage.reduce_function.emplace(*this, eval_storage);
701 lf::FunctionNode &lf_reduce = lf_graph.add_function(*eval_storage.reduce_function);
702
703 /* Link up body outputs to reduce function. */
704 const int body_main_outputs_num = node_storage.main_items.items_num +
705 node_storage.generation_items.items_num;
706 BLI_assert(body_main_outputs_num == body_fn_.indices.outputs.main.size());
707 for (const int i : IndexRange(eval_storage.total_iterations_num)) {
708 lf::FunctionNode &lf_body_node = *lf_body_nodes[i];
709 for (const int item_i : IndexRange(node_storage.main_items.items_num)) {
710 lf_graph.add_link(lf_body_node.output(body_fn_.indices.outputs.main[item_i]),
711 lf_reduce.input(i * body_main_outputs_num + item_i));
712 }
713 for (const int item_i : IndexRange(node_storage.generation_items.items_num)) {
714 const int body_output_i = item_i + node_storage.main_items.items_num;
715 lf_graph.add_link(lf_body_node.output(body_fn_.indices.outputs.main[body_output_i]),
716 lf_reduce.input(i * body_main_outputs_num + body_output_i));
717 }
718 }
719
720 /* Link up reduce function outputs to final zone outputs. */
721 lf_graph.add_link(lf_reduce.output(0), *graph_outputs[zone_info_.indices.outputs.main[0]]);
722 for (const int item_i : IndexRange(node_storage.main_items.items_num)) {
723 const int output_i = indices_.main.lf_outer[item_i];
724 lf_graph.add_link(lf_reduce.output(output_i),
725 *graph_outputs[zone_info_.indices.outputs.main[output_i]]);
726 }
727 for (const int item_i : IndexRange(node_storage.generation_items.items_num)) {
728 const int output_i = indices_.generation.lf_outer[item_i];
729 lf_graph.add_link(lf_reduce.output(output_i),
730 *graph_outputs[zone_info_.indices.outputs.main[output_i]]);
731 }
732
733 /* All zone inputs are used for now. */
734 static bool static_true{true};
735 for (const int i : zone_info_.indices.outputs.input_usages) {
736 graph_outputs[i]->set_default_value(&static_true);
737 }
738
739 /* Handle usage outputs for border-links. A border-link is used if it's used by any of the
740 * iterations. */
741 eval_storage.or_function.emplace(eval_storage.total_iterations_num);
742 for (const int border_link_i : zone_.border_links.index_range()) {
743 lf::FunctionNode &lf_or = lf_graph.add_function(*eval_storage.or_function);
744 for (const int i : lf_body_nodes.index_range()) {
745 lf::FunctionNode &lf_body_node = *lf_body_nodes[i];
746 lf_graph.add_link(
747 lf_body_node.output(body_fn_.indices.outputs.border_link_usages[border_link_i]),
748 lf_or.input(i));
749 }
750 lf_graph.add_link(
751 lf_or.output(0),
752 *graph_outputs[zone_info_.indices.outputs.border_link_usages[border_link_i]]);
753 }
754 }
755
756 std::string input_name(const int i) const override
757 {
758 return zone_wrapper_input_name(zone_info_, zone_, inputs_, i);
759 }
760
761 std::string output_name(const int i) const override
762 {
763 return zone_wrapper_output_name(zone_info_, zone_, outputs_, i);
764 }
765};
766
770 : parent_(parent), eval_storage_(eval_storage)
771{
772 debug_name_ = "Reduce";
773
774 const auto &node_storage = *static_cast<NodeGeometryForeachGeometryElementOutput *>(
775 parent.output_bnode_.storage);
776
777 inputs_.reserve(eval_storage.total_iterations_num *
778 (node_storage.main_items.items_num + node_storage.generation_items.items_num));
779
780 for ([[maybe_unused]] const int i : eval_storage.lf_body_nodes.index_range()) {
781 /* Add parameters for main items. */
782 for (const int item_i : IndexRange(node_storage.main_items.items_num)) {
783 const NodeForeachGeometryElementMainItem &item = node_storage.main_items.items[item_i];
784 const bNodeSocket &socket = parent.output_bnode_.input_socket(
785 parent_.indices_.main.bsocket_inner[item_i]);
786 inputs_.append_as(
787 item.name, *socket.typeinfo->geometry_nodes_cpp_type, lf::ValueUsage::Used);
788 }
789 /* Add parameters for generation items. */
790 for (const int item_i : IndexRange(node_storage.generation_items.items_num)) {
791 const NodeForeachGeometryElementGenerationItem &item =
792 node_storage.generation_items.items[item_i];
793 const bNodeSocket &socket = parent.output_bnode_.input_socket(
794 parent_.indices_.generation.bsocket_inner[item_i]);
795 inputs_.append_as(
796 item.name, *socket.typeinfo->geometry_nodes_cpp_type, lf::ValueUsage::Maybe);
797 }
798 }
799
800 /* Add output for main geometry. */
801 outputs_.append_as("Geometry", CPPType::get<GeometrySet>());
802 /* Add outputs for main items. */
803 for (const int item_i : IndexRange(node_storage.main_items.items_num)) {
804 const NodeForeachGeometryElementMainItem &item = node_storage.main_items.items[item_i];
805 const bNodeSocket &socket = parent.output_bnode_.output_socket(
806 parent_.indices_.main.bsocket_outer[item_i]);
807 outputs_.append_as(item.name, *socket.typeinfo->geometry_nodes_cpp_type);
808 }
809 /* Add outputs for generation items. */
810 for (const int item_i : IndexRange(node_storage.generation_items.items_num)) {
811 const NodeForeachGeometryElementGenerationItem &item =
812 node_storage.generation_items.items[item_i];
813 const bNodeSocket &socket = parent.output_bnode_.output_socket(
814 parent_.indices_.generation.bsocket_outer[item_i]);
815 outputs_.append_as(item.name, *socket.typeinfo->geometry_nodes_cpp_type);
816 }
817}
818
820static std::optional<AttrDomain> get_foreach_attribute_propagation_target_domain(
821 const GeometryComponent::Type component_type)
822{
823 switch (component_type) {
824 case GeometryComponent::Type::Mesh:
825 case GeometryComponent::Type::PointCloud:
826 return AttrDomain::Point;
827 case GeometryComponent::Type::Curve:
828 return AttrDomain::Curve;
829 case GeometryComponent::Type::Instance:
830 return AttrDomain::Instance;
831 case GeometryComponent::Type::GreasePencil:
832 return AttrDomain::Layer;
833 default:
834 break;
835 }
836 return std::nullopt;
837}
838
840 const lf::Context &context) const
841{
842 const auto &node_storage = *static_cast<NodeGeometryForeachGeometryElementOutput *>(
843 parent_.output_bnode_.storage);
844
845 this->handle_main_items_and_geometry(params, context);
846 if (node_storage.generation_items.items_num == 0) {
847 return;
848 }
849 this->handle_generation_items(params, context);
850}
851
853 lf::Params &params, const lf::Context &context) const
854{
855 auto &user_data = *static_cast<GeoNodesUserData *>(context.user_data);
856 const auto &node_storage = *static_cast<NodeGeometryForeachGeometryElementOutput *>(
857 parent_.output_bnode_.storage);
858 const int body_main_outputs_num = node_storage.main_items.items_num +
859 node_storage.generation_items.items_num;
860
861 const int main_geometry_output = 0;
862 if (params.output_was_set(main_geometry_output)) {
863 /* Done already. */
864 return;
865 }
866
867 GeometrySet output_geometry = eval_storage_.main_geometry;
868
869 for (const int item_i : IndexRange(node_storage.main_items.items_num)) {
870 const NodeForeachGeometryElementMainItem &item = node_storage.main_items.items[item_i];
871 const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type);
872 const CPPType *base_cpp_type = bke::socket_type_to_geo_nodes_base_cpp_type(socket_type);
873 if (!base_cpp_type) {
874 continue;
875 }
876 const eCustomDataType cd_type = bke::cpp_type_to_custom_data_type(*base_cpp_type);
877
878 /* Compute output attribute name for this item. */
879 const std::string attribute_name = bke::hash_to_anonymous_attribute_name(
880 user_data.call_data->self_object()->id.name,
881 user_data.compute_context->hash(),
882 parent_.output_bnode_.identifier,
883 item.identifier);
884
885 /* Create a new output attribute for the current item on each iteration component. */
886 for (const ForeachElementComponent &component_info : eval_storage_.components) {
887 MutableAttributeAccessor attributes = component_info.attributes_for_write(output_geometry);
888 const int domain_size = attributes.domain_size(component_info.id.domain);
889 const IndexMask mask = component_info.field_evaluator->get_evaluated_selection_as_mask();
890
891 /* Actually create the attribute. */
893 attribute_name, component_info.id.domain, cd_type);
894
895 /* Fill the elements of the attribute that we didn't iterate over because they were not
896 * selected. */
897 IndexMaskMemory memory;
898 const IndexMask inverted_mask = mask.complement(IndexRange(domain_size), memory);
899 base_cpp_type->value_initialize_indices(attribute.span.data(), inverted_mask);
900
901 /* Copy the values from each iteration into the attribute. */
902 mask.foreach_index([&](const int i, const int pos) {
903 const int lf_param_index = pos * body_main_outputs_num + item_i;
904 SocketValueVariant &value_variant = params.get_input<SocketValueVariant>(lf_param_index);
905 value_variant.convert_to_single();
906 const void *value = value_variant.get_single_ptr_raw();
907 base_cpp_type->copy_construct(value, attribute.span[i]);
908 });
909
910 attribute.finish();
911 }
912
913 /* Output the field for the anonymous attribute. */
914 auto attribute_field = std::make_shared<bke::AttributeFieldInput>(
915 attribute_name,
916 *base_cpp_type,
918 parent_.output_bnode_.output_socket(parent_.indices_.main.bsocket_outer[item_i])));
919 SocketValueVariant attribute_value_variant{GField(std::move(attribute_field))};
920 params.set_output(1 + item_i, std::move(attribute_value_variant));
921 }
922
923 /* Output the original geometry with potentially additional attributes. */
924 params.set_output(main_geometry_output, std::move(output_geometry));
925}
926
928 lf::Params &params, const lf::Context &context) const
929{
930 const auto &node_storage = *static_cast<NodeGeometryForeachGeometryElementOutput *>(
931 parent_.output_bnode_.storage);
932
933 const int first_valid_item_i = this->handle_invalid_generation_items(params);
934 if (first_valid_item_i == node_storage.generation_items.items_num) {
935 return;
936 }
937 this->handle_generation_item_groups(params, context, first_valid_item_i);
938}
939
941 lf::Params &params) const
942{
943 const auto &node_storage = *static_cast<NodeGeometryForeachGeometryElementOutput *>(
944 parent_.output_bnode_.storage);
945
946 int item_i = 0;
947 /* Handle invalid generation items that come before a geometry. */
948 for (; item_i < node_storage.generation_items.items_num; item_i++) {
950 node_storage.generation_items.items[item_i];
951 const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type);
952 if (socket_type == SOCK_GEOMETRY) {
953 break;
954 }
955 const int lf_socket_i = parent_.indices_.generation.lf_outer[item_i];
956 if (!params.output_was_set(lf_socket_i)) {
957 const int bsocket_i = parent_.indices_.generation.bsocket_outer[item_i];
959 params, lf_socket_i, parent_.zone_.output_node()->output_socket(bsocket_i));
960 }
961 }
962 return item_i;
963}
964
966 lf::Params &params, const lf::Context &context, const int first_valid_item_i) const
967{
968 const auto &node_storage = *static_cast<NodeGeometryForeachGeometryElementOutput *>(
969 parent_.output_bnode_.storage);
970 int previous_geometry_item_i = first_valid_item_i;
971 /* Iterate over all groups. A group starts with a geometry socket followed by an arbitrary number
972 * of non-geometry sockets. */
973 for (const int item_i :
974 IndexRange::from_begin_end(first_valid_item_i + 1, node_storage.generation_items.items_num))
975 {
977 node_storage.generation_items.items[item_i];
978 const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type);
979 if (socket_type == SOCK_GEOMETRY) {
981 params,
982 context,
983 previous_geometry_item_i,
984 IndexRange::from_begin_end(previous_geometry_item_i + 1, item_i));
985 previous_geometry_item_i = item_i;
986 }
987 }
989 params,
990 context,
991 previous_geometry_item_i,
992 IndexRange::from_begin_end(previous_geometry_item_i + 1,
993 node_storage.generation_items.items_num));
994}
995
998 const lf::Context &context,
999 const int geometry_item_i,
1000 const IndexRange generation_items_range) const
1001{
1002 auto &user_data = *static_cast<GeoNodesUserData *>(context.user_data);
1003 const auto &node_storage = *static_cast<NodeGeometryForeachGeometryElementOutput *>(
1004 parent_.output_bnode_.storage);
1005 const int body_main_outputs_num = node_storage.main_items.items_num +
1006 node_storage.generation_items.items_num;
1007
1008 /* Handle the case when the output is not needed or the inputs have not been computed yet. */
1010 params, context, geometry_item_i, generation_items_range))
1011 {
1012 return;
1013 }
1014
1015 /* TODO: Get propagation info from input, but that's not necessary for correctness for now. */
1016 bke::AttributeFilter attribute_filter;
1017
1018 const int bodies_num = eval_storage_.lf_body_nodes.size();
1019 Array<GeometrySet> geometries(bodies_num + 1);
1020
1021 /* Create attribute names for the outputs. */
1022 Array<std::string> attribute_names(generation_items_range.size());
1023 for (const int i : generation_items_range.index_range()) {
1024 const int item_i = generation_items_range[i];
1026 node_storage.generation_items.items[item_i];
1027 attribute_names[i] = bke::hash_to_anonymous_attribute_name(
1028 user_data.call_data->self_object()->id.name,
1029 user_data.compute_context->hash(),
1030 parent_.output_bnode_.identifier,
1031 item.identifier);
1032 }
1033
1034 for (const ForeachElementComponent &component_info : eval_storage_.components) {
1035 const AttributeAccessor src_attributes = component_info.input_attributes();
1036
1037 /* These are the attributes we need to propagate from the original input geometry. */
1038 struct NameWithType {
1040 eCustomDataType type;
1041 };
1042 Vector<NameWithType> attributes_to_propagate;
1043 src_attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
1044 if (iter.data_type == CD_PROP_STRING) {
1045 return;
1046 }
1047 if (attribute_filter.allow_skip(iter.name)) {
1048 return;
1049 }
1050 attributes_to_propagate.append({iter.name, iter.data_type});
1051 });
1052 Map<StringRef, GVArray> cached_adapted_src_attributes;
1053
1054 const IndexMask mask = component_info.field_evaluator->get_evaluated_selection_as_mask();
1055
1056 /* Add attributes for each field on the geometry created by each iteration. */
1057 mask.foreach_index([&](const int element_i, const int local_body_i) {
1058 const int body_i = component_info.body_nodes_range[local_body_i];
1059 const int geometry_param_i = body_i * body_main_outputs_num +
1060 parent_.indices_.generation.lf_inner[geometry_item_i];
1061 GeometrySet &geometry = geometries[body_i];
1062 geometry = params.extract_input<GeometrySet>(geometry_param_i);
1063
1064 for (const GeometryComponent::Type dst_component_type :
1065 {GeometryComponent::Type::Mesh,
1066 GeometryComponent::Type::PointCloud,
1067 GeometryComponent::Type::Curve,
1068 GeometryComponent::Type::GreasePencil,
1069 GeometryComponent::Type::Instance})
1070 {
1071 if (!geometry.has(dst_component_type)) {
1072 continue;
1073 }
1074 GeometryComponent &dst_component = geometry.get_component_for_write(dst_component_type);
1075 MutableAttributeAccessor dst_attributes = *dst_component.attributes_for_write();
1076
1077 /* Determine the domain that we propagate the input attribute to. Technically, this is only
1078 * a single value for the entire geometry, but we can't optimize for that yet. */
1079 const std::optional<AttrDomain> propagation_domain =
1081 if (!propagation_domain) {
1082 continue;
1083 }
1084
1085 /* Propagate attributes from the input geometry. */
1086 for (const NameWithType &name_with_type : attributes_to_propagate) {
1087 const StringRef name = name_with_type.name;
1088 const eCustomDataType cd_type = name_with_type.type;
1089 if (src_attributes.is_builtin(name) && !dst_attributes.is_builtin(name)) {
1090 continue;
1091 }
1092 if (dst_attributes.contains(name)) {
1093 /* Attributes created in the zone shouldn't be overridden. */
1094 continue;
1095 }
1096 /* Get the source attribute adapted to the iteration domain. */
1097 const GVArray &src_attribute = cached_adapted_src_attributes.lookup_or_add_cb(
1098 name, [&]() {
1099 bke::GAttributeReader attribute = src_attributes.lookup(name);
1100 return src_attributes.adapt_domain(
1101 *attribute, attribute.domain, component_info.id.domain);
1102 });
1103 if (!src_attribute) {
1104 continue;
1105 }
1106 const CPPType &type = src_attribute.type();
1107 BUFFER_FOR_CPP_TYPE_VALUE(type, element_value);
1108 src_attribute.get_to_uninitialized(element_i, element_value);
1109
1110 /* Actually create the attribute. */
1111 bke::GSpanAttributeWriter dst_attribute =
1112 dst_attributes.lookup_or_add_for_write_only_span(name, *propagation_domain, cd_type);
1113 type.fill_assign_n(element_value, dst_attribute.span.data(), dst_attribute.span.size());
1114 dst_attribute.finish();
1115
1116 type.destruct(element_value);
1117 }
1118 }
1119
1120 /* Create an attribute for each field that corresponds to the current geometry. */
1121 for (const int local_item_i : generation_items_range.index_range()) {
1122 const int item_i = generation_items_range[local_item_i];
1124 node_storage.generation_items.items[item_i];
1125 const AttrDomain capture_domain = AttrDomain(item.domain);
1126 const int field_param_i = body_i * body_main_outputs_num +
1127 parent_.indices_.generation.lf_inner[item_i];
1128 GField field = params.get_input<SocketValueVariant>(field_param_i).get<GField>();
1129
1130 if (capture_domain == AttrDomain::Instance) {
1131 if (geometry.has_instances()) {
1133 geometry.get_component_for_write(GeometryComponent::Type::Instance),
1134 attribute_names[local_item_i],
1135 capture_domain,
1136 field);
1137 }
1138 }
1139 else {
1140 geometry.modify_geometry_sets([&](GeometrySet &sub_geometry) {
1141 for (const GeometryComponent::Type component_type :
1142 {GeometryComponent::Type::Mesh,
1143 GeometryComponent::Type::PointCloud,
1144 GeometryComponent::Type::Curve,
1145 GeometryComponent::Type::GreasePencil})
1146 {
1147 if (sub_geometry.has(component_type)) {
1149 sub_geometry.get_component_for_write(component_type),
1150 attribute_names[local_item_i],
1151 capture_domain,
1152 field);
1153 }
1154 }
1155 });
1156 }
1157 }
1158 });
1159 }
1160
1161 /* The last geometry contains the edit data from the main geometry. */
1162 GeometrySet &edit_data_geometry = geometries.last();
1163 edit_data_geometry = eval_storage_.main_geometry;
1164 edit_data_geometry.keep_only({GeometryComponent::Type::Edit});
1165
1166 /* Join the geometries from all iterations into a single one. */
1167 GeometrySet joined_geometry = geometry::join_geometries(geometries, attribute_filter);
1168
1169 /* Output the joined geometry. */
1170 params.set_output(parent_.indices_.generation.lf_outer[geometry_item_i],
1171 std::move(joined_geometry));
1172
1173 /* Output the anonymous attribute fields. */
1174 for (const int local_item_i : generation_items_range.index_range()) {
1175 const int item_i = generation_items_range[local_item_i];
1177 node_storage.generation_items.items[item_i];
1178 const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type);
1179 const CPPType &base_cpp_type = *bke::socket_type_to_geo_nodes_base_cpp_type(socket_type);
1180 const StringRef attribute_name = attribute_names[local_item_i];
1181 auto attribute_field = std::make_shared<bke::AttributeFieldInput>(
1182 attribute_name,
1183 base_cpp_type,
1185 parent_.output_bnode_.output_socket(2 + node_storage.main_items.items_num + item_i)));
1186 SocketValueVariant attribute_value_variant{GField(std::move(attribute_field))};
1187 params.set_output(parent_.indices_.generation.lf_outer[item_i],
1188 std::move(attribute_value_variant));
1189 }
1190}
1191
1194 const lf::Context & /*context*/,
1195 const int geometry_item_i,
1196 const IndexRange generation_items_range) const
1197{
1198 const auto &node_storage = *static_cast<NodeGeometryForeachGeometryElementOutput *>(
1199 parent_.output_bnode_.storage);
1200 const int body_main_outputs_num = node_storage.main_items.items_num +
1201 node_storage.generation_items.items_num;
1202
1203 const int geometry_output_param = parent_.indices_.generation.lf_outer[geometry_item_i];
1204
1205 if (params.output_was_set(geometry_output_param)) {
1206 /* Done already. */
1207 return false;
1208 }
1209 const lf::ValueUsage geometry_output_usage = params.get_output_usage(geometry_output_param);
1210 if (geometry_output_usage == lf::ValueUsage::Unused) {
1211 /* Output dummy values. */
1212 const int start_bsocket_i = parent_.indices_.generation.bsocket_outer[geometry_item_i];
1213 for (const int i : IndexRange(1 + generation_items_range.size())) {
1214 const bNodeSocket &bsocket = parent_.output_bnode_.output_socket(start_bsocket_i + i);
1215 set_default_value_for_output_socket(params, geometry_output_param + i, bsocket);
1216 }
1217 return false;
1218 }
1219 bool any_output_used = false;
1220 for (const int i : IndexRange(1 + generation_items_range.size())) {
1221 const lf::ValueUsage usage = params.get_output_usage(geometry_output_param + i);
1222 if (usage == lf::ValueUsage::Used) {
1223 any_output_used = true;
1224 break;
1225 }
1226 }
1227 if (!any_output_used) {
1228 /* Only execute below if we are sure that the output is actually needed. */
1229 return false;
1230 }
1231 const int bodies_num = eval_storage_.lf_body_nodes.size();
1232
1233 /* Check if all inputs are available, and request them if not. */
1234 bool has_missing_input = false;
1235 for (const int body_i : IndexRange(bodies_num)) {
1236 const int offset = body_i * body_main_outputs_num +
1237 parent_.indices_.generation.lf_inner[geometry_item_i];
1238 for (const int i : IndexRange(1 + generation_items_range.size())) {
1239 const bool is_available = params.try_get_input_data_ptr_or_request(offset + i) != nullptr;
1240 if (!is_available) {
1241 has_missing_input = true;
1242 }
1243 }
1244 }
1245 if (has_missing_input) {
1246 /* Come back when all inputs are available. */
1247 return false;
1248 }
1249 return true;
1250}
1251
1253 const bNodeTree &btree,
1254 const bke::bNodeTreeZone &zone,
1255 ZoneBuildInfo &zone_info,
1256 const ZoneBodyFunction &body_fn)
1257{
1259 btree, zone, zone_info, body_fn);
1260}
1261
1262} // namespace blender::nodes
Low-level operations for curves.
Low-level operations for grease pencil.
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BUFFER_FOR_CPP_TYPE_VALUE(type, variable_name)
#define ELEM(...)
T * DEG_get_original(T *id)
@ CD_PROP_STRING
eNodeSocketDatatype
@ SOCK_GEOMETRY
volatile int lock
__forceinline float extract(const int4 &b)
Definition binning.cpp:27
for(;discarded_id_iter !=nullptr;discarded_id_iter=static_cast< ID * >(discarded_id_iter->next))
Definition blendfile.cc:634
int64_t size() const
Definition BLI_array.hh:245
const T & last(const int64_t n=0) const
Definition BLI_array.hh:285
IndexRange index_range() const
Definition BLI_array.hh:349
static const CPPType & get()
void value_initialize_indices(void *ptr, const IndexMask &mask) const
void fill_assign_n(const void *value, void *dst, int64_t n) const
void copy_construct(const void *src, void *dst) const
void destruct(void *ptr) const
const ComputeContextHash & hash() const
void get_to_uninitialized(int64_t index, void *r_value) const
constexpr int64_t size() const
static constexpr IndexRange from_begin_end(const int64_t begin, const int64_t end)
static constexpr IndexRange from_begin_size(const int64_t begin, const int64_t size)
constexpr IndexRange index_range() const
destruct_ptr< T > construct(Args &&...args)
Value & lookup_or_add_cb(const Key &key, const CreateValueF &create_value)
Definition BLI_map.hh:620
T & construct(Args &&...args)
IndexRange index_range() const
void add_new(const Key &key)
int64_t size() const
void append(const T &value)
IndexRange index_range() const
Span< T > as_span() const
bool is_builtin(const StringRef attribute_id) const
void foreach_attribute(const FunctionRef< void(const AttributeIter &)> fn) const
bool contains(StringRef attribute_id) const
GVArray adapt_domain(const GVArray &varray, const AttrDomain from_domain, const AttrDomain to_domain) const
GAttributeReader lookup(const StringRef attribute_id) const
int domain_size(const AttrDomain domain) const
MutableAttributeAccessor attributes_for_write()
virtual std::optional< MutableAttributeAccessor > attributes_for_write()
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type)
void * allocate_single(eNodeSocketDatatype socket_type)
bke::CurvesGeometry & strokes_for_write()
const bke::CurvesGeometry & strokes() const
FunctionNode & add_function(const LazyFunction &fn)
void add_link(OutputSocket &from, InputSocket &to)
GraphOutputSocket & add_output(const CPPType &type, std::string name="")
GraphInputSocket & add_input(const CPPType &type, std::string name="")
const InputSocket & input(int index) const
const OutputSocket & output(int index) const
void execute_node(const lf::FunctionNode &node, lf::Params &params, const lf::Context &context) const override
Vector< const lf::FunctionNode * > get_nodes_with_side_effects(const lf::Context &context) const override
void execute_impl(lf::Params &params, const lf::Context &context) const override
std::optional< Array< GeometrySet > > try_extract_element_geometries(const GeometrySet &main_geometry, const ForeachElementComponentID &id, const IndexMask &mask, const bke::AttributeFilter &attribute_filter) const
void initialize_execution_graph(lf::Params &params, ForeachGeometryElementEvalStorage &eval_storage, const NodeGeometryForeachGeometryElementOutput &node_storage) const
void prepare_components(lf::Params &params, ForeachGeometryElementEvalStorage &eval_storage, const NodeGeometryForeachGeometryElementOutput &node_storage) const
LazyFunctionForForeachGeometryElementZone(const bNodeTree &btree, const bke::bNodeTreeZone &zone, ZoneBuildInfo &zone_info, const ZoneBodyFunction &body_fn)
void build_graph_contents(ForeachGeometryElementEvalStorage &eval_storage, const NodeGeometryForeachGeometryElementOutput &node_storage, Span< lf::GraphInputSocket * > graph_inputs, Span< lf::GraphOutputSocket * > graph_outputs) const
linear_allocator::ChunkedList< WarningWithNode > node_warnings
uint pos
#define input
#define this
#define output
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
eCustomDataType cpp_type_to_custom_data_type(const CPPType &type)
bool try_capture_field_on_geometry(MutableAttributeAccessor attributes, const fn::FieldContext &field_context, const StringRef attribute_id, AttrDomain domain, const fn::Field< bool > &selection, const fn::GField &field)
const CPPType * socket_type_to_geo_nodes_base_cpp_type(eNodeSocketDatatype type)
Definition node.cc:5413
std::string hash_to_anonymous_attribute_name(Args &&...args)
Array< bke::Instances * > extract_instances(const bke::Instances &instances, const IndexMask &mask, const bke::AttributeFilter &attribute_filter)
Array< GreasePencil * > extract_greasepencil_layer_points(const GreasePencil &grease_pencil, int layer_i, const IndexMask &mask, const bke::AttributeFilter &attribute_filter)
Array< Mesh * > extract_mesh_faces(const Mesh &mesh, const IndexMask &mask, const bke::AttributeFilter &attribute_filter)
Array< Mesh * > extract_mesh_vertices(const Mesh &mesh, const IndexMask &mask, const bke::AttributeFilter &attribute_filter)
Array< Curves * > extract_curves(const Curves &curves, const IndexMask &mask, const bke::AttributeFilter &attribute_filter)
Array< Curves * > extract_curves_points(const Curves &curves, const IndexMask &mask, const bke::AttributeFilter &attribute_filter)
Array< GreasePencil * > extract_greasepencil_layers(const GreasePencil &grease_pencil, const IndexMask &mask, const bke::AttributeFilter &attribute_filter)
Array< GreasePencil * > extract_greasepencil_layer_curves(const GreasePencil &grease_pencil, int layer_i, const IndexMask &mask, const bke::AttributeFilter &attribute_filter)
bke::GeometrySet join_geometries(Span< bke::GeometrySet > geometries, const bke::AttributeFilter &attribute_filter, const std::optional< Span< bke::GeometryComponent::Type > > &component_types_to_join=std::nullopt)
Array< PointCloud * > extract_pointcloud_points(const PointCloud &pointcloud, const IndexMask &mask, const bke::AttributeFilter &attribute_filter)
Array< Mesh * > extract_mesh_edges(const Mesh &mesh, const IndexMask &mask, const bke::AttributeFilter &attribute_filter)
static std::optional< AttrDomain > get_foreach_attribute_propagation_target_domain(const GeometryComponent::Type component_type)
void initialize_zone_wrapper(const bNodeTreeZone &zone, ZoneBuildInfo &zone_info, const ZoneBodyFunction &body_fn, const bool expose_all_reference_sets, Vector< lf::Input > &r_inputs, Vector< lf::Output > &r_outputs)
bool should_log_socket_values_for_context(const GeoNodesUserData &user_data, const ComputeContextHash hash)
std::string make_anonymous_attribute_socket_inspection_string(const bNodeSocket &socket)
void set_default_value_for_output_socket(lf::Params &params, const int lf_index, const bNodeSocket &bsocket)
std::string zone_wrapper_output_name(const ZoneBuildInfo &zone_info, const bNodeTreeZone &zone, const Span< lf::Output > outputs, const int lf_socket_i)
LazyFunction & build_foreach_geometry_element_zone_lazy_function(ResourceScope &scope, const bNodeTree &btree, const bke::bNodeTreeZone &zone, ZoneBuildInfo &zone_info, const ZoneBodyFunction &body_fn)
std::string zone_wrapper_input_name(const ZoneBuildInfo &zone_info, const bNodeTreeZone &zone, const Span< lf::Input > inputs, const int lf_socket_i)
NodeForeachGeometryElementInputItem * items
NodeForeachGeometryElementInputItems input_items
NodeForeachGeometryElementMainItems main_items
NodeForeachGeometryElementGenerationItems generation_items
bNodeTreeRuntimeHandle * runtime
void * storage
bool allow_skip(const StringRef name) const
void keep_only(Span< GeometryComponent::Type > component_types)
GeometryComponent & get_component_for_write(GeometryComponent::Type component_type)
const GreasePencil * get_grease_pencil() const
Vector< const GeometryComponent * > get_components() const
bool has(const GeometryComponent::Type component_type) const
const Curves * get_curves() const
const Instances * get_instances() const
const PointCloud * get_pointcloud() const
const Mesh * get_mesh() const
MutableAttributeAccessor attributes_for_write(GeometrySet &geometry) const
std::optional< LazyFunctionForReduceForeachGeometryElement > reduce_function
std::optional< ForeachGeometryElementZoneSideEffectProvider > side_effect_provider
std::optional< ForeachGeometryElementNodeExecuteWrapper > body_execute_wrapper
const GeoNodesSideEffectNodes * side_effect_nodes
MultiValueMap< std::pair< ComputeContextHash, int32_t >, int > iterations_by_iteration_zone
LazyFunctionForReduceForeachGeometryElement(const LazyFunctionForForeachGeometryElementZone &parent, ForeachGeometryElementEvalStorage &eval_storage)
void handle_main_items_and_geometry(lf::Params &params, const lf::Context &context) const
void handle_generation_items_group(lf::Params &params, const lf::Context &context, int geometry_item_i, IndexRange generation_items_range) const
void handle_generation_item_groups(lf::Params &params, const lf::Context &context, int first_valid_item_i) const
void execute_impl(lf::Params &params, const lf::Context &context) const override
bool handle_generation_items_group_lazyness(lf::Params &params, const lf::Context &context, int geometry_item_i, IndexRange generation_items_range) const
void handle_generation_items(lf::Params &params, const lf::Context &context) const
struct blender::nodes::ZoneFunctionIndices::@165160011314225015162310017251113363101175306052 inputs
i
Definition text_draw.cc:230