Blender V4.3
geometry_fields.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BLI_array_utils.hh"
6
7#include "BKE_attribute.hh"
8#include "BKE_curves.hh"
10#include "BKE_geometry_set.hh"
11#include "BKE_grease_pencil.hh"
12#include "BKE_instances.hh"
13#include "BKE_mesh.hh"
14#include "BKE_pointcloud.hh"
16
17#include "DNA_mesh_types.h"
19
20#include "BLT_translation.hh"
21
22#include <fmt/format.h>
23
24namespace blender::bke {
25
27 : mesh_(mesh), domain_(domain)
28{
29 BLI_assert(mesh.attributes().domain_supported(domain_));
30}
31
33 : curves_(curves), domain_(domain)
34{
35 BLI_assert(curves.attributes().domain_supported(domain));
36}
37
39 : CurvesFieldContext(curves_id.geometry.wrap(), domain)
40{
41 curves_id_ = &curves_id;
42}
43
45 const IndexMask &mask,
46 ResourceScope &scope) const
47{
48 if (const CurvesFieldInput *curves_field_input = dynamic_cast<const CurvesFieldInput *>(
49 &field_input))
50 {
51 if (const bke::greasepencil::Drawing *drawing = this->grease_pencil().get_eval_drawing(
52 this->grease_pencil().layer(this->layer_index())))
53 {
54 if (drawing->strokes().attributes().domain_supported(this->domain())) {
55 const CurvesFieldContext context{drawing->strokes(), this->domain()};
56 return curves_field_input->get_varray_for_context(context, mask, scope);
57 }
58 }
59 return {};
60 }
61 return field_input.get_varray_for_context(*this, mask, scope);
62}
63
65 const AttrDomain domain)
66 : geometry_(other.geometry_),
67 type_(other.type_),
68 domain_(domain),
69 curves_id_(other.curves_id_),
70 grease_pencil_layer_index_(other.grease_pencil_layer_index_)
71{
72}
73
75 const GeometryComponent::Type type,
76 const AttrDomain domain,
77 const int grease_pencil_layer_index)
78 : geometry_(geometry),
79 type_(type),
80 domain_(domain),
81 grease_pencil_layer_index_(grease_pencil_layer_index)
82{
83 BLI_assert(ELEM(type,
89}
90
92 const AttrDomain domain)
93 : type_(component.type()), domain_(domain)
94{
95 switch (component.type()) {
97 const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
98 geometry_ = mesh_component.get();
99 break;
100 }
102 const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
103 const Curves *curves = curve_component.get();
104 geometry_ = curves ? &curves->geometry.wrap() : nullptr;
105 curves_id_ = curve_component.get();
106 break;
107 }
109 const PointCloudComponent &pointcloud_component = static_cast<const PointCloudComponent &>(
110 component);
111 geometry_ = pointcloud_component.get();
112 break;
113 }
115 const GreasePencilComponent &grease_pencil_component =
116 static_cast<const GreasePencilComponent &>(component);
117 geometry_ = grease_pencil_component.get();
118 /* Need to use another constructor for other domains. */
119 BLI_assert(domain == AttrDomain::Layer);
120 break;
121 }
123 const InstancesComponent &instances_component = static_cast<const InstancesComponent &>(
124 component);
125 geometry_ = instances_component.get();
126 break;
127 }
131 break;
132 }
133}
134
136 : geometry_(&mesh), type_(GeometryComponent::Type::Mesh), domain_(domain)
137{
138}
140 : geometry_(&curves), type_(GeometryComponent::Type::Curve), domain_(domain)
141{
142}
144 : geometry_(&curves_id.geometry.wrap()),
145 type_(GeometryComponent::Type::Curve),
146 domain_(domain),
147 curves_id_(&curves_id)
148{
149}
151 : geometry_(&points), type_(GeometryComponent::Type::PointCloud), domain_(AttrDomain::Point)
152{
153}
155 : geometry_(&grease_pencil),
156 type_(GeometryComponent::Type::GreasePencil),
157 domain_(AttrDomain::Layer)
158{
159}
161 const AttrDomain domain,
162 const int layer_index)
163 : geometry_(&grease_pencil),
164 type_(GeometryComponent::Type::GreasePencil),
165 domain_(domain),
166 grease_pencil_layer_index_(layer_index)
167{
168}
170 : geometry_(&instances),
171 type_(GeometryComponent::Type::Instance),
172 domain_(AttrDomain::Instance)
173{
174}
175
176std::optional<AttributeAccessor> GeometryFieldContext::attributes() const
177{
178 if (const Mesh *mesh = this->mesh()) {
179 return mesh->attributes();
180 }
181 if (const CurvesGeometry *curves = this->curves()) {
182 return curves->attributes();
183 }
184 if (const PointCloud *pointcloud = this->pointcloud()) {
185 return pointcloud->attributes();
186 }
187 if (const GreasePencil *grease_pencil = this->grease_pencil()) {
188 if (domain_ == AttrDomain::Layer) {
189 return grease_pencil->attributes();
190 }
191 if (const greasepencil::Drawing *drawing = grease_pencil->get_eval_drawing(
192 grease_pencil->layer(grease_pencil_layer_index_)))
193 {
194 return drawing->strokes().attributes();
195 }
196 }
197 if (const Instances *instances = this->instances()) {
198 return instances->attributes();
199 }
200 return {};
201}
202
204{
205 return this->type() == GeometryComponent::Type::Mesh ? static_cast<const Mesh *>(geometry_) :
206 nullptr;
207}
209{
210 return this->type() == GeometryComponent::Type::Curve ?
211 static_cast<const CurvesGeometry *>(geometry_) :
212 nullptr;
213}
215{
216 return this->type() == GeometryComponent::Type::PointCloud ?
217 static_cast<const PointCloud *>(geometry_) :
218 nullptr;
219}
221{
223 static_cast<const GreasePencil *>(geometry_) :
224 nullptr;
225}
227{
230 {
231 return nullptr;
232 }
233 return this->grease_pencil()->get_eval_drawing(
234 this->grease_pencil()->layer(this->grease_pencil_layer_index_));
235}
237{
238 if (const CurvesGeometry *curves = this->curves()) {
239 return curves;
240 }
241 if (const greasepencil::Drawing *drawing = this->grease_pencil_layer_drawing()) {
242 return &drawing->strokes();
243 }
244 return nullptr;
245}
247{
248 return curves_id_;
249}
251{
252 return this->type() == GeometryComponent::Type::Instance ?
253 static_cast<const Instances *>(geometry_) :
254 nullptr;
255}
256
258 const IndexMask &mask,
259 ResourceScope & /*scope*/) const
260{
261 if (const GeometryFieldContext *geometry_context = dynamic_cast<const GeometryFieldContext *>(
262 &context))
263 {
264 return this->get_varray_for_context(*geometry_context, mask);
265 }
266 if (const MeshFieldContext *mesh_context = dynamic_cast<const MeshFieldContext *>(&context)) {
267 return this->get_varray_for_context({mesh_context->mesh(), mesh_context->domain()}, mask);
268 }
269 if (const CurvesFieldContext *curve_context = dynamic_cast<const CurvesFieldContext *>(&context))
270 {
271 if (const Curves *curves_id = curve_context->curves_id()) {
272 return this->get_varray_for_context({*curves_id, curve_context->domain()}, mask);
273 }
274 return this->get_varray_for_context({curve_context->curves(), curve_context->domain()}, mask);
275 }
276 if (const PointCloudFieldContext *point_context = dynamic_cast<const PointCloudFieldContext *>(
277 &context))
278 {
279 return this->get_varray_for_context({point_context->pointcloud()}, mask);
280 }
281 if (const GreasePencilFieldContext *grease_pencil_context =
282 dynamic_cast<const GreasePencilFieldContext *>(&context))
283 {
284 return this->get_varray_for_context({grease_pencil_context->grease_pencil()}, mask);
285 }
286 if (const GreasePencilLayerFieldContext *grease_pencil_context =
287 dynamic_cast<const GreasePencilLayerFieldContext *>(&context))
288 {
289 return this->get_varray_for_context({grease_pencil_context->grease_pencil(),
290 grease_pencil_context->domain(),
291 grease_pencil_context->layer_index()},
292 mask);
293 }
294 if (const InstancesFieldContext *instances_context = dynamic_cast<const InstancesFieldContext *>(
295 &context))
296 {
297 return this->get_varray_for_context({instances_context->instances()}, mask);
298 }
299 return {};
300}
301
302std::optional<AttrDomain> GeometryFieldInput::preferred_domain(
303 const GeometryComponent & /*component*/) const
304{
305 return std::nullopt;
306}
307
309 const IndexMask &mask,
310 ResourceScope & /*scope*/) const
311{
312 if (const GeometryFieldContext *geometry_context = dynamic_cast<const GeometryFieldContext *>(
313 &context))
314 {
315 if (const Mesh *mesh = geometry_context->mesh()) {
316 return this->get_varray_for_context(*mesh, geometry_context->domain(), mask);
317 }
318 }
319 if (const MeshFieldContext *mesh_context = dynamic_cast<const MeshFieldContext *>(&context)) {
320 return this->get_varray_for_context(mesh_context->mesh(), mesh_context->domain(), mask);
321 }
322 return {};
323}
324
325std::optional<AttrDomain> MeshFieldInput::preferred_domain(const Mesh & /*mesh*/) const
326{
327 return std::nullopt;
328}
329
331 const IndexMask &mask,
332 ResourceScope & /*scope*/) const
333{
334 if (const GeometryFieldContext *geometry_context = dynamic_cast<const GeometryFieldContext *>(
335 &context))
336 {
337 if (const CurvesGeometry *curves = geometry_context->curves_or_strokes()) {
338 return this->get_varray_for_context(*curves, geometry_context->domain(), mask);
339 }
340 }
341 if (const CurvesFieldContext *curves_context = dynamic_cast<const CurvesFieldContext *>(
342 &context))
343 {
344 return this->get_varray_for_context(curves_context->curves(), curves_context->domain(), mask);
345 }
346 return {};
347}
348
349std::optional<AttrDomain> CurvesFieldInput::preferred_domain(
350 const CurvesGeometry & /*curves*/) const
351{
352 return std::nullopt;
353}
354
356 const IndexMask &mask,
357 ResourceScope & /*scope*/) const
358{
359 if (const GeometryFieldContext *geometry_context = dynamic_cast<const GeometryFieldContext *>(
360 &context))
361 {
362 if (const PointCloud *pointcloud = geometry_context->pointcloud()) {
363 return this->get_varray_for_context(*pointcloud, mask);
364 }
365 }
366 if (const PointCloudFieldContext *point_context = dynamic_cast<const PointCloudFieldContext *>(
367 &context))
368 {
369 return this->get_varray_for_context(point_context->pointcloud(), mask);
370 }
371 return {};
372}
373
375 const IndexMask &mask,
376 ResourceScope & /*scope*/) const
377{
378 if (const GeometryFieldContext *geometry_context = dynamic_cast<const GeometryFieldContext *>(
379 &context))
380 {
381 if (const Instances *instances = geometry_context->instances()) {
382 return this->get_varray_for_context(*instances, mask);
383 }
384 }
385 if (const InstancesFieldContext *instances_context = dynamic_cast<const InstancesFieldContext *>(
386 &context))
387 {
388 return this->get_varray_for_context(instances_context->instances(), mask);
389 }
390 return {};
391}
392
394 const IndexMask & /*mask*/) const
395{
397 const AttrDomain domain = context.domain();
398 if (const GreasePencil *grease_pencil = context.grease_pencil()) {
399 const AttributeAccessor layer_attributes = grease_pencil->attributes();
400 if (domain == AttrDomain::Layer) {
401 return *layer_attributes.lookup(name_, data_type);
402 }
404 const int layer_index = context.grease_pencil_layer_index();
405 const AttributeAccessor curves_attributes = *context.attributes();
406 if (const GAttributeReader reader = curves_attributes.lookup(name_, domain, data_type)) {
407 return *reader;
408 }
409 /* Lookup attribute on the layer domain if it does not exist on points or curves. */
410 if (const GAttributeReader reader = layer_attributes.lookup(name_)) {
411 const CPPType &cpp_type = reader.varray.type();
413 BLI_SCOPED_DEFER([&]() { cpp_type.destruct(value); });
414 reader.varray.get_to_uninitialized(layer_index, value);
415 const int domain_size = curves_attributes.domain_size(domain);
416 return GVArray::ForSingle(cpp_type, domain_size, value);
417 }
418 }
419 }
420 else if (context.domain() == bke::AttrDomain::Instance && name_ == "position") {
421 /* Special case for "position" which is no longer an attribute on instances. */
422 return bke::instance_position_varray(*context.instances());
423 }
424 else if (auto attributes = context.attributes()) {
425 return *attributes->lookup(name_, domain, data_type);
426 }
427
428 return {};
429}
430
432 const IndexMask & /*mask*/) const
433{
434 const AttrDomain domain = context.domain();
435 if (context.type() == GeometryComponent::Type::GreasePencil) {
436 const AttributeAccessor layer_attributes = context.grease_pencil()->attributes();
437 if (context.domain() == AttrDomain::Layer) {
438 const bool exists = layer_attributes.contains(name_);
439 const int domain_size = layer_attributes.domain_size(AttrDomain::Layer);
440 return VArray<bool>::ForSingle(exists, domain_size);
441 }
442 const greasepencil::Drawing *drawing = context.grease_pencil_layer_drawing();
443 const AttributeAccessor curve_attributes = drawing->strokes().attributes();
444 const bool exists = layer_attributes.contains(name_) || curve_attributes.contains(name_);
445 const int domain_size = curve_attributes.domain_size(domain);
446 return VArray<bool>::ForSingle(exists, domain_size);
447 }
448 const bool exists = context.attributes()->contains(name_);
449 const int domain_size = context.attributes()->domain_size(domain);
450 return VArray<bool>::ForSingle(exists, domain_size);
451}
452
454{
455 if (socket_inspection_name_) {
456 return *socket_inspection_name_;
457 }
458 return fmt::format(TIP_("\"{}\" attribute from geometry"), name_);
459}
460
462{
463 return get_default_hash(name_, type_);
464}
465
467{
468 if (const AttributeFieldInput *other_typed = dynamic_cast<const AttributeFieldInput *>(&other)) {
469 return name_ == other_typed->name_ && type_ == other_typed->type_;
470 }
471 return false;
472}
473
475 const GeometryComponent &component) const
476{
477 const std::optional<AttributeAccessor> attributes = component.attributes();
478 if (!attributes.has_value()) {
479 return std::nullopt;
480 }
481 const std::optional<AttributeMetaData> meta_data = attributes->lookup_meta_data(name_);
482 if (!meta_data.has_value()) {
483 return std::nullopt;
484 }
485 return meta_data->domain;
486}
487
489{
490 switch (domain) {
493 return "id";
494 default:
495 return "";
496 }
497}
498
500 const IndexMask &mask) const
501{
502
503 const StringRef name = get_random_id_attribute_name(context.domain());
504 if (auto attributes = context.attributes()) {
505 if (GVArray attribute = *attributes->lookup(name, context.domain(), CD_PROP_INT32)) {
506 return attribute;
507 }
508 }
509
510 /* Use the index as the fallback if no random ID attribute exists. */
512}
513
515{
516 return TIP_("ID / Index");
517}
518
520{
521 /* All random ID attribute inputs are the same within the same evaluation context. */
522 return 92386459827;
523}
524
526{
527 /* All random ID attribute inputs are the same within the same evaluation context. */
528 return dynamic_cast<const IDAttributeFieldInput *>(&other) != nullptr;
529}
530
532 const bke::GeometryFieldContext &context, const IndexMask &mask) const
533{
534 using namespace bke::greasepencil;
535 const AttrDomain domain = context.domain();
537 return {};
538 }
539
540 const GreasePencil &grease_pencil = *context.grease_pencil();
541 if (!context.grease_pencil()) {
542 return {};
543 }
544
545 auto layer_is_selected = [selection_name = StringRef(layer_name_),
546 &grease_pencil,
547 size = mask.min_array_size()](const int layer_i) {
548 if (layer_i < 0 || layer_i >= grease_pencil.layers().size()) {
549 return false;
550 }
551 const Layer &layer = grease_pencil.layer(layer_i);
552 return layer.name() == selection_name;
553 };
554
556 const int layer_i = context.grease_pencil_layer_index();
557 const bool selected = layer_is_selected(layer_i);
558 return VArray<bool>::ForSingle(selected, mask.min_array_size());
559 }
560
561 return VArray<bool>::ForFunc(mask.min_array_size(), layer_is_selected);
562}
563
565{
566 return get_default_hash(layer_name_, type_);
567}
568
570{
571 if (const NamedLayerSelectionFieldInput *other_named_layer =
572 dynamic_cast<const NamedLayerSelectionFieldInput *>(&other))
573 {
574 return layer_name_ == other_named_layer->layer_name_;
575 }
576 return false;
577}
578
580 const bke::GeometryComponent & /*component*/) const
581{
582 return AttrDomain::Layer;
583}
584
585template<typename T>
587 const VArray<int> &indices,
588 const IndexMask &mask,
589 MutableSpan<T> dst)
590{
591 const IndexRange src_range = src.index_range();
592 devirtualize_varray2(src, indices, [&](const auto src, const auto indices) {
593 mask.foreach_index(GrainSize(4096), [&](const int i) {
594 const int index = indices[i];
595 if (src_range.contains(index)) {
596 dst[i] = src[index];
597 }
598 else {
599 dst[i] = {};
600 }
601 });
602 });
603}
604
606 const VArray<int> &indices,
607 const IndexMask &mask,
608 GMutableSpan dst)
609{
611 using T = decltype(dummy);
612 copy_with_checked_indices(src.typed<T>(), indices, mask, dst.typed<T>());
613 });
614}
615
616EvaluateAtIndexInput::EvaluateAtIndexInput(fn::Field<int> index_field,
617 fn::GField value_field,
618 AttrDomain value_field_domain)
619 : bke::GeometryFieldInput(value_field.cpp_type(), "Evaluate at Index"),
620 index_field_(std::move(index_field)),
621 value_field_(std::move(value_field)),
622 value_field_domain_(value_field_domain)
623{
624}
625
627 const IndexMask &mask) const
628{
629 const std::optional<AttributeAccessor> attributes = context.attributes();
630 if (!attributes) {
631 return {};
632 }
633
634 const bke::GeometryFieldContext value_context{context, value_field_domain_};
635 fn::FieldEvaluator value_evaluator{value_context, attributes->domain_size(value_field_domain_)};
636 value_evaluator.add(value_field_);
637 value_evaluator.evaluate();
638 const GVArray &values = value_evaluator.get_evaluated(0);
639
640 fn::FieldEvaluator index_evaluator{context, &mask};
641 index_evaluator.add(index_field_);
642 index_evaluator.evaluate();
643 const VArray<int> indices = index_evaluator.get_evaluated<int>(0);
644
645 GArray<> dst_array(values.type(), mask.min_array_size());
646 copy_with_checked_indices(values, indices, mask, dst_array);
647 return GVArray::ForGArray(std::move(dst_array));
648}
649
651 : bke::GeometryFieldInput(field.cpp_type(), "Evaluate on Domain"),
652 src_field_(std::move(field)),
653 src_domain_(domain)
654{
655}
656
658 const IndexMask & /*mask*/) const
659{
660 const AttrDomain dst_domain = context.domain();
661 const int dst_domain_size = context.attributes()->domain_size(dst_domain);
662 const CPPType &cpp_type = src_field_.cpp_type();
663
664 if (context.type() == GeometryComponent::Type::GreasePencil &&
665 (src_domain_ == AttrDomain::Layer) != (dst_domain == AttrDomain::Layer))
666 {
667 /* Evaluate field just for the current layer. */
668 if (src_domain_ == AttrDomain::Layer) {
669 const bke::GeometryFieldContext src_domain_context{context, AttrDomain::Layer};
670 const int layer_index = context.grease_pencil_layer_index();
671
672 const IndexMask single_layer_mask = IndexRange(layer_index, 1);
673 fn::FieldEvaluator value_evaluator{src_domain_context, &single_layer_mask};
674 value_evaluator.add(src_field_);
675 value_evaluator.evaluate();
676
677 const GVArray &values = value_evaluator.get_evaluated(0);
678
680 BLI_SCOPED_DEFER([&]() { cpp_type.destruct(value); });
681 values.get_to_uninitialized(layer_index, value);
682 return GVArray::ForSingle(cpp_type, dst_domain_size, value);
683 }
684 /* We don't adapt from curve to layer domain currently. */
685 return GVArray::ForSingleDefault(cpp_type, dst_domain_size);
686 }
687
688 const bke::AttributeAccessor attributes = *context.attributes();
689
690 const bke::GeometryFieldContext other_domain_context{context, src_domain_};
691 const int64_t src_domain_size = attributes.domain_size(src_domain_);
692 GArray<> values(cpp_type, src_domain_size);
693 fn::FieldEvaluator value_evaluator{other_domain_context, src_domain_size};
694 value_evaluator.add_with_destination(src_field_, values.as_mutable_span());
695 value_evaluator.evaluate();
696 return attributes.adapt_domain(GVArray::ForGArray(std::move(values)), src_domain_, dst_domain);
697}
698
700 FunctionRef<void(const FieldInput &)> fn) const
701{
702 src_field_.node().for_each_field_input_recursive(fn);
703}
704
706 const GeometryComponent & /*component*/) const
707{
708 return src_domain_;
709}
710
711} // namespace blender::bke
712
713/* -------------------------------------------------------------------- */
717namespace blender::bke {
718
720 const IndexMask &mask) const
721{
722 if (const Mesh *mesh = context.mesh()) {
723 return mesh_normals_varray(*mesh, mask, context.domain());
724 }
725 if (const CurvesGeometry *curves = context.curves_or_strokes()) {
726 return curve_normals_varray(*curves, context.domain());
727 }
728 return {};
729}
730
732{
733 return TIP_("Normal");
734}
735
737{
738 return 213980475983;
739}
740
742{
743 return dynamic_cast<const NormalFieldInput *>(&other) != nullptr;
744}
745
746static std::optional<StringRefNull> try_get_field_direct_attribute_id(const fn::GField &any_field)
747{
748 if (const auto *field = dynamic_cast<const AttributeFieldInput *>(&any_field.node())) {
749 return field->attribute_name();
750 }
751 return {};
752}
753
754static bool attribute_kind_matches(const AttributeMetaData meta_data,
755 const AttrDomain domain,
756 const eCustomDataType data_type)
757{
758 return meta_data.domain == domain && meta_data.data_type == data_type;
759}
760
766 const StringRef id_to_create,
767 const AttrDomain domain,
768 const fn::GField &field)
769{
770 const std::optional<StringRef> field_id = try_get_field_direct_attribute_id(field);
771 if (!field_id) {
772 return false;
773 }
774 const std::optional<AttributeMetaData> meta_data = attributes.lookup_meta_data(*field_id);
775 if (!meta_data) {
776 return false;
777 }
778 const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(field.cpp_type());
779 if (!attribute_kind_matches(*meta_data, domain, data_type)) {
780 /* Avoid costly domain and type interpolation, which would make sharing impossible. */
781 return false;
782 }
783 const GAttributeReader attribute = attributes.lookup(*field_id, domain, data_type);
784 if (!attribute.sharing_info || !attribute.varray.is_span()) {
785 return false;
786 }
787 const AttributeInitShared init(attribute.varray.get_internal_span().data(),
788 *attribute.sharing_info);
789 return attributes.add(id_to_create, domain, data_type, init);
790}
791
792static bool attribute_data_matches_varray(const GAttributeReader &attribute, const GVArray &varray)
793{
794 const CommonVArrayInfo varray_info = varray.common_info();
795 if (varray_info.type != CommonVArrayInfo::Type::Span) {
796 return false;
797 }
798 const CommonVArrayInfo attribute_info = attribute.varray.common_info();
799 if (attribute_info.type != CommonVArrayInfo::Type::Span) {
800 return false;
801 }
802 return varray_info.data == attribute_info.data;
803}
804
806 const fn::FieldContext &field_context,
807 const Span<StringRef> attribute_ids,
808 const AttrDomain domain,
809 const fn::Field<bool> &selection,
810 const Span<fn::GField> fields)
811{
812 BLI_assert(attribute_ids.size() == fields.size());
813 const int domain_size = attributes.domain_size(domain);
814 if (domain_size == 0) {
815 bool all_added = true;
816 for (const int i : attribute_ids.index_range()) {
817 const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(fields[i].cpp_type());
818 all_added &= attributes.add(attribute_ids[i], domain, data_type, AttributeInitConstruct{});
819 }
820 return all_added;
821 }
822
823 fn::FieldEvaluator evaluator{field_context, domain_size};
824 evaluator.set_selection(selection);
825
826 const bool selection_is_full = !selection.node().depends_on_input() &&
828
829 struct StoreResult {
830 int input_index;
831 int evaluator_index;
832 };
833 Vector<StoreResult> results_to_store;
834
835 struct AddResult {
836 int input_index;
837 int evaluator_index;
838 void *buffer;
839 };
840 Vector<AddResult> results_to_add;
841
842 for (const int input_index : attribute_ids.index_range()) {
843 const StringRef id = attribute_ids[input_index];
844 const AttributeValidator validator = attributes.lookup_validator(id);
845 const fn::GField field = validator.validate_field_if_necessary(fields[input_index]);
846 const CPPType &type = field.cpp_type();
847
848 /* We are writing to an attribute that exists already with the correct domain and type. */
849 if (const GAttributeReader dst = attributes.lookup(id)) {
850 if (dst.domain == domain && dst.varray.type() == field.cpp_type()) {
851 const int evaluator_index = evaluator.add(field);
852 results_to_store.append({input_index, evaluator_index});
853 continue;
854 }
855 }
856
857 if (!validator && selection_is_full) {
858 if (try_add_shared_field_attribute(attributes, id, domain, field)) {
859 continue;
860 }
861 }
862
863 /* Could avoid allocating a new buffer if:
864 * - The field does not depend on that attribute (we can't easily check for that yet). */
865 void *buffer = MEM_mallocN_aligned(type.size() * domain_size, type.alignment(), __func__);
866 if (!selection_is_full) {
867 type.value_initialize_n(buffer, domain_size);
868 }
869
870 GMutableSpan dst(type, buffer, domain_size);
871 const int evaluator_index = evaluator.add_with_destination(field, dst);
872 results_to_add.append({input_index, evaluator_index, buffer});
873 }
874
875 evaluator.evaluate();
876 const IndexMask &mask = evaluator.get_evaluated_selection_as_mask();
877
878 for (const StoreResult &result : results_to_store) {
879 const StringRef id = attribute_ids[result.input_index];
880 const GVArray &result_data = evaluator.get_evaluated(result.evaluator_index);
881 const GAttributeReader dst = attributes.lookup(id);
882 if (!attribute_data_matches_varray(dst, result_data)) {
883 GSpanAttributeWriter dst_mut = attributes.lookup_for_write_span(id);
884 array_utils::copy(result_data, mask, dst_mut.span);
885 dst_mut.finish();
886 }
887 }
888
889 bool success = true;
890 for (const AddResult &result : results_to_add) {
891 const StringRef id = attribute_ids[result.input_index];
892 attributes.remove(id);
893 const CPPType &type = fields[result.input_index].cpp_type();
895 if (!attributes.add(id, domain, data_type, AttributeInitMoveArray(result.buffer))) {
896 /* If the name corresponds to a builtin attribute, removing the attribute might fail if
897 * it's required, adding the attribute might fail if the domain or type is incorrect. */
898 type.destruct_n(result.buffer, domain_size);
899 MEM_freeN(result.buffer);
900 success = false;
901 }
902 }
903
904 return success;
905}
906
908 const Span<StringRef> attribute_ids,
909 const AttrDomain domain,
910 const fn::Field<bool> &selection,
911 const Span<fn::GField> fields)
912{
913 const GeometryComponent::Type component_type = component.type();
914 if (component_type == GeometryComponent::Type::GreasePencil &&
916 {
917 /* Capture the field on every layer individually. */
918 auto &grease_pencil_component = static_cast<GreasePencilComponent &>(component);
919 GreasePencil *grease_pencil = grease_pencil_component.get_for_write();
920 if (grease_pencil == nullptr) {
921 return false;
922 }
923 bool any_success = false;
924 threading::parallel_for(grease_pencil->layers().index_range(), 8, [&](const IndexRange range) {
925 for (const int layer_index : range) {
926 if (greasepencil::Drawing *drawing = grease_pencil->get_eval_drawing(
927 grease_pencil->layer(layer_index)))
928 {
929 const GeometryFieldContext field_context{*grease_pencil, domain, layer_index};
930 const bool success = try_capture_fields_on_geometry(
931 drawing->strokes_for_write().attributes_for_write(),
932 field_context,
933 attribute_ids,
934 domain,
935 selection,
936 fields);
937 if (success & !any_success) {
938 any_success = true;
939 }
940 }
941 }
942 });
943 return any_success;
944 }
945 if (component_type == GeometryComponent::Type::GreasePencil && domain != AttrDomain::Layer) {
946 /* The remaining code only handles the layer domain for grease pencil geometries. */
947 return false;
948 }
949
950 MutableAttributeAccessor attributes = *component.attributes_for_write();
951 const GeometryFieldContext field_context{component, domain};
953 attributes, field_context, attribute_ids, domain, selection, fields);
954}
955
957 const Span<StringRef> attribute_ids,
958 const AttrDomain domain,
959 const Span<fn::GField> fields)
960{
961 const fn::Field<bool> selection = fn::make_constant_field<bool>(true);
962 return try_capture_fields_on_geometry(component, attribute_ids, domain, selection, fields);
963}
964
965std::optional<AttrDomain> try_detect_field_domain(const GeometryComponent &component,
966 const fn::GField &field)
967{
968 const GeometryComponent::Type component_type = component.type();
969 if (component_type == GeometryComponent::Type::PointCloud) {
970 return AttrDomain::Point;
971 }
972 if (component_type == GeometryComponent::Type::GreasePencil) {
973 return AttrDomain::Layer;
974 }
975 if (component_type == GeometryComponent::Type::Instance) {
976 return AttrDomain::Instance;
977 }
978 const std::shared_ptr<const fn::FieldInputs> &field_inputs = field.node().field_inputs();
979 if (!field_inputs) {
980 return std::nullopt;
981 }
982 std::optional<AttrDomain> output_domain;
983 auto handle_domain = [&](const std::optional<AttrDomain> domain) {
984 if (!domain.has_value()) {
985 return false;
986 }
987 if (output_domain.has_value()) {
988 if (*output_domain != *domain) {
989 return false;
990 }
991 return true;
992 }
993 output_domain = domain;
994 return true;
995 };
996 if (component_type == GeometryComponent::Type::Mesh) {
997 const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
998 const Mesh *mesh = mesh_component.get();
999 if (mesh == nullptr) {
1000 return std::nullopt;
1001 }
1002 for (const fn::FieldInput &field_input : field_inputs->deduplicated_nodes) {
1003 if (const auto *geometry_field_input = dynamic_cast<const GeometryFieldInput *>(
1004 &field_input))
1005 {
1006 if (!handle_domain(geometry_field_input->preferred_domain(component))) {
1007 return std::nullopt;
1008 }
1009 }
1010 else if (const auto *mesh_field_input = dynamic_cast<const MeshFieldInput *>(&field_input)) {
1011 if (!handle_domain(mesh_field_input->preferred_domain(*mesh))) {
1012 return std::nullopt;
1013 }
1014 }
1015 else {
1016 return std::nullopt;
1017 }
1018 }
1019 }
1020 if (component_type == GeometryComponent::Type::Curve) {
1021 const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
1022 const Curves *curves = curve_component.get();
1023 if (curves == nullptr) {
1024 return std::nullopt;
1025 }
1026 for (const fn::FieldInput &field_input : field_inputs->deduplicated_nodes) {
1027 if (const auto *geometry_field_input = dynamic_cast<const GeometryFieldInput *>(
1028 &field_input))
1029 {
1030 if (!handle_domain(geometry_field_input->preferred_domain(component))) {
1031 return std::nullopt;
1032 }
1033 }
1034 else if (const auto *curves_field_input = dynamic_cast<const CurvesFieldInput *>(
1035 &field_input))
1036 {
1037 if (!handle_domain(curves_field_input->preferred_domain(curves->geometry.wrap()))) {
1038 return std::nullopt;
1039 }
1040 }
1041 else {
1042 return std::nullopt;
1043 }
1044 }
1045 }
1046 return output_domain;
1047}
1048
1049} // namespace blender::bke
1050
Low-level operations for curves.
Low-level operations for grease pencil.
General operations for point clouds.
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BUFFER_FOR_CPP_TYPE_VALUE(type, variable_name)
#define BLI_SCOPED_DEFER(function_to_defer)
#define ELEM(...)
#define TIP_(msgid)
@ CD_PROP_INT32
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a or normal between and object coordinate space Combine Create a color from its and value channels Color Retrieve a color attribute
void init()
AttributeSet attributes
void destruct(void *ptr) const
CommonVArrayInfo common_info() const
static GVArray ForGArray(GArray<> array)
static GVArray ForSingleDefault(const CPPType &type, int64_t size)
static GVArray ForSingle(const CPPType &type, int64_t size, const void *value)
constexpr bool contains(int64_t value) const
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
IndexRange index_range() const
static VArray ForSingle(T value, const int64_t size)
static VArray ForFunc(const int64_t size, GetFunc get_func)
void append(const T &value)
GAttributeReader lookup(const StringRef attribute_id) const
int domain_size(const AttrDomain domain) const
bool contains(const StringRef attribute_id) const
GVArray get_varray_for_context(const bke::GeometryFieldContext &context, const IndexMask &mask) const final
bool is_equal_to(const fn::FieldNode &other) const override
uint64_t hash() const override
GVArray get_varray_for_context(const GeometryFieldContext &context, const IndexMask &mask) const override
std::string socket_inspection_name() const override
std::optional< AttrDomain > preferred_domain(const GeometryComponent &component) const override
CurvesFieldContext(const CurvesGeometry &curves, AttrDomain domain)
GVArray get_varray_for_context(const fn::FieldContext &context, const IndexMask &mask, ResourceScope &scope) const override
virtual std::optional< AttrDomain > preferred_domain(const CurvesGeometry &curves) const
GVArray get_varray_for_context(const bke::GeometryFieldContext &context, const IndexMask &mask) const final
std::optional< AttrDomain > preferred_domain(const GeometryComponent &) const override
void for_each_field_input_recursive(FunctionRef< void(const FieldInput &)> fn) const override
EvaluateOnDomainInput(fn::GField field, AttrDomain domain)
GVArray get_varray_for_context(const bke::GeometryFieldContext &context, const IndexMask &) const final
virtual std::optional< AttributeAccessor > attributes() const
const CurvesGeometry * curves() const
std::optional< AttributeAccessor > attributes() const
const Instances * instances() const
const PointCloud * pointcloud() const
GeometryFieldContext(const GeometryFieldContext &other, AttrDomain domain)
const greasepencil::Drawing * grease_pencil_layer_drawing() const
const GreasePencil * grease_pencil() const
const CurvesGeometry * curves_or_strokes() const
GeometryComponent::Type type() const
GVArray get_varray_for_context(const fn::FieldContext &context, const IndexMask &mask, ResourceScope &scope) const override
virtual std::optional< AttrDomain > preferred_domain(const GeometryComponent &component) const
GVArray get_varray_for_input(const fn::FieldInput &field_input, const IndexMask &mask, ResourceScope &scope) const
GVArray get_varray_for_context(const GeometryFieldContext &context, const IndexMask &mask) const override
bool is_equal_to(const fn::FieldNode &other) const override
std::string socket_inspection_name() const override
GVArray get_varray_for_context(const fn::FieldContext &context, const IndexMask &mask, ResourceScope &scope) const override
MeshFieldContext(const Mesh &mesh, AttrDomain domain)
virtual std::optional< AttrDomain > preferred_domain(const Mesh &mesh) const
GVArray get_varray_for_context(const fn::FieldContext &context, const IndexMask &mask, ResourceScope &scope) const override
std::optional< AttrDomain > preferred_domain(const GeometryComponent &component) const override
GVArray get_varray_for_context(const bke::GeometryFieldContext &context, const IndexMask &mask) const final
bool is_equal_to(const fn::FieldNode &other) const override
bool is_equal_to(const fn::FieldNode &other) const override
GVArray get_varray_for_context(const GeometryFieldContext &context, const IndexMask &mask) const override
uint64_t hash() const override
std::string socket_inspection_name() const override
GVArray get_varray_for_context(const fn::FieldContext &context, const IndexMask &mask, ResourceScope &scope) const override
const bke::CurvesGeometry & strokes() const
int add(GField field, GVArray *varray_ptr)
Definition field.cc:756
int add_with_destination(GField field, GVMutableArray dst)
Definition field.cc:743
const CPPType * type_
Definition FN_field.hh:272
const CPPType & cpp_type() const
Definition FN_field.hh:634
virtual GVArray get_varray_for_context(const FieldContext &context, const IndexMask &mask, ResourceScope &scope) const =0
virtual void for_each_field_input_recursive(FunctionRef< void(const FieldInput &)> fn) const
Definition field.cc:587
const CPPType & cpp_type() const
Definition FN_field.hh:132
const FieldNode & node() const
Definition FN_field.hh:137
static GVArray get_index_varray(const IndexMask &mask)
Definition field.cc:553
void * MEM_mallocN_aligned(size_t len, size_t alignment, const char *str)
Definition mallocn.cc:110
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
ccl_device_inline float4 mask(const int4 mask, const float4 a)
void copy(const GVArray &src, GMutableSpan dst, int64_t grain_size=4096)
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
static bool attribute_kind_matches(const AttributeMetaData meta_data, const AttrDomain domain, const eCustomDataType data_type)
static bool attribute_data_matches_varray(const GAttributeReader &attribute, const GVArray &varray)
std::optional< AttrDomain > try_detect_field_domain(const GeometryComponent &component, const fn::GField &field)
void copy_with_checked_indices(const GVArray &src, const VArray< int > &indices, const IndexMask &mask, GMutableSpan dst)
eCustomDataType cpp_type_to_custom_data_type(const CPPType &type)
VArray< float3 > curve_normals_varray(const CurvesGeometry &curves, AttrDomain domain)
static std::optional< StringRefNull > try_get_field_direct_attribute_id(const fn::GField &any_field)
VArray< float3 > instance_position_varray(const Instances &instances)
Definition instances.cc:525
static bool try_add_shared_field_attribute(MutableAttributeAccessor attributes, const StringRef id_to_create, const AttrDomain domain, const fn::GField &field)
VArray< float3 > mesh_normals_varray(const Mesh &mesh, const IndexMask &mask, AttrDomain domain)
static StringRef get_random_id_attribute_name(const AttrDomain domain)
bool try_capture_fields_on_geometry(MutableAttributeAccessor attributes, const fn::FieldContext &field_context, Span< StringRef > attribute_ids, AttrDomain domain, const fn::Field< bool > &selection, Span< fn::GField > fields)
GField make_constant_field(const CPPType &type, const void *value)
Definition field.cc:533
void evaluate_constant_field(const GField &field, void *r_value)
Definition field.cc:498
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:95
void devirtualize_varray2(const VArray< T1 > &varray1, const VArray< T2 > &varray2, const Func &func, bool enable=true)
uint64_t get_default_hash(const T &v)
Definition BLI_hash.hh:219
float wrap(float value, float max, float min)
Definition node_math.h:71
__int64 int64_t
Definition stdint.h:89
unsigned __int64 uint64_t
Definition stdint.h:90
fn::GField validate_field_if_necessary(const fn::GField &field) const