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