Blender V5.0
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
15#include "DNA_mesh_types.h"
17
18#include "BLT_translation.hh"
19
20#include <fmt/format.h>
21
22namespace blender::bke {
23
25 : mesh_(mesh), domain_(domain)
26{
27 BLI_assert(mesh.attributes().domain_supported(domain_));
28}
29
31 : curves_(curves), domain_(domain)
32{
33 BLI_assert(curves.attributes().domain_supported(domain));
34}
35
41
43 const IndexMask &mask,
44 ResourceScope &scope) const
45{
46 if (const CurvesFieldInput *curves_field_input = dynamic_cast<const CurvesFieldInput *>(
47 &field_input))
48 {
49 if (const bke::greasepencil::Drawing *drawing = this->grease_pencil().get_eval_drawing(
50 this->grease_pencil().layer(this->layer_index())))
51 {
52 if (drawing->strokes().attributes().domain_supported(this->domain())) {
53 const CurvesFieldContext context{drawing->strokes(), this->domain()};
54 return curves_field_input->get_varray_for_context(context, mask, scope);
55 }
56 }
57 return {};
58 }
59 return field_input.get_varray_for_context(*this, mask, scope);
60}
61
63 const AttrDomain domain)
64 : geometry_(other.geometry_),
65 type_(other.type_),
66 domain_(domain),
67 curves_id_(other.curves_id_),
68 grease_pencil_layer_index_(other.grease_pencil_layer_index_)
69{
70}
71
88
90 const AttrDomain domain)
91 : type_(component.type()), domain_(domain)
92{
93 switch (component.type()) {
94 case GeometryComponent::Type::Mesh: {
95 const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
96 geometry_ = mesh_component.get();
97 break;
98 }
100 const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
101 const Curves *curves = curve_component.get();
102 geometry_ = curves ? &curves->geometry.wrap() : nullptr;
103 curves_id_ = curve_component.get();
104 break;
105 }
107 const PointCloudComponent &pointcloud_component = static_cast<const PointCloudComponent &>(
108 component);
109 geometry_ = pointcloud_component.get();
110 break;
111 }
113 const GreasePencilComponent &grease_pencil_component =
114 static_cast<const GreasePencilComponent &>(component);
115 geometry_ = grease_pencil_component.get();
116 /* Need to use another constructor for other domains. */
117 BLI_assert(domain == AttrDomain::Layer);
118 break;
119 }
121 const InstancesComponent &instances_component = static_cast<const InstancesComponent &>(
122 component);
123 geometry_ = instances_component.get();
124 break;
125 }
129 break;
130 }
131}
132
142 : geometry_(&curves_id.geometry.wrap()),
144 domain_(domain),
145 curves_id_(&curves_id)
146{
147}
149 : geometry_(&points), type_(GeometryComponent::Type::PointCloud), domain_(AttrDomain::Point)
150{
151}
159 const AttrDomain domain,
160 const int layer_index)
161 : geometry_(&grease_pencil),
163 domain_(domain),
164 grease_pencil_layer_index_(layer_index)
165{
166}
173
174std::optional<AttributeAccessor> GeometryFieldContext::attributes() const
175{
176 if (const Mesh *mesh = this->mesh()) {
177 return mesh->attributes();
178 }
179 if (const CurvesGeometry *curves = this->curves()) {
180 return curves->attributes();
181 }
182 if (const PointCloud *pointcloud = this->pointcloud()) {
183 return pointcloud->attributes();
184 }
185 if (const GreasePencil *grease_pencil = this->grease_pencil()) {
186 if (domain_ == AttrDomain::Layer) {
187 return grease_pencil->attributes();
188 }
189 if (const greasepencil::Drawing *drawing = grease_pencil->get_eval_drawing(
190 grease_pencil->layer(grease_pencil_layer_index_)))
191 {
192 return drawing->strokes().attributes();
193 }
194 }
195 if (const Instances *instances = this->instances()) {
196 return instances->attributes();
197 }
198 return {};
199}
200
202{
203 return this->type() == GeometryComponent::Type::Mesh ? static_cast<const Mesh *>(geometry_) :
204 nullptr;
205}
207{
208 return this->type() == GeometryComponent::Type::Curve ?
209 static_cast<const CurvesGeometry *>(geometry_) :
210 nullptr;
211}
213{
214 return this->type() == GeometryComponent::Type::PointCloud ?
215 static_cast<const PointCloud *>(geometry_) :
216 nullptr;
217}
219{
221 static_cast<const GreasePencil *>(geometry_) :
222 nullptr;
223}
225{
228 {
229 return nullptr;
230 }
231 return this->grease_pencil()->get_eval_drawing(
232 this->grease_pencil()->layer(this->grease_pencil_layer_index_));
233}
235{
236 if (const CurvesGeometry *curves = this->curves()) {
237 return curves;
238 }
239 if (const greasepencil::Drawing *drawing = this->grease_pencil_layer_drawing()) {
240 return &drawing->strokes();
241 }
242 return nullptr;
243}
245{
246 return curves_id_;
247}
249{
250 return this->type() == GeometryComponent::Type::Instance ?
251 static_cast<const Instances *>(geometry_) :
252 nullptr;
253}
254
256 const IndexMask &mask,
257 ResourceScope & /*scope*/) const
258{
259 if (const GeometryFieldContext *geometry_context = dynamic_cast<const GeometryFieldContext *>(
260 &context))
261 {
262 return this->get_varray_for_context(*geometry_context, mask);
263 }
264 if (const MeshFieldContext *mesh_context = dynamic_cast<const MeshFieldContext *>(&context)) {
265 return this->get_varray_for_context({mesh_context->mesh(), mesh_context->domain()}, mask);
266 }
267 if (const CurvesFieldContext *curve_context = dynamic_cast<const CurvesFieldContext *>(&context))
268 {
269 if (const Curves *curves_id = curve_context->curves_id()) {
270 return this->get_varray_for_context({*curves_id, curve_context->domain()}, mask);
271 }
272 return this->get_varray_for_context({curve_context->curves(), curve_context->domain()}, mask);
273 }
274 if (const PointCloudFieldContext *point_context = dynamic_cast<const PointCloudFieldContext *>(
275 &context))
276 {
277 return this->get_varray_for_context({point_context->pointcloud()}, mask);
278 }
279 if (const GreasePencilFieldContext *grease_pencil_context =
280 dynamic_cast<const GreasePencilFieldContext *>(&context))
281 {
282 return this->get_varray_for_context({grease_pencil_context->grease_pencil()}, mask);
283 }
284 if (const GreasePencilLayerFieldContext *grease_pencil_context =
285 dynamic_cast<const GreasePencilLayerFieldContext *>(&context))
286 {
287 return this->get_varray_for_context({grease_pencil_context->grease_pencil(),
288 grease_pencil_context->domain(),
289 grease_pencil_context->layer_index()},
290 mask);
291 }
292 if (const InstancesFieldContext *instances_context = dynamic_cast<const InstancesFieldContext *>(
293 &context))
294 {
295 return this->get_varray_for_context({instances_context->instances()}, mask);
296 }
297 return {};
298}
299
300std::optional<AttrDomain> GeometryFieldInput::preferred_domain(
301 const GeometryComponent & /*component*/) const
302{
303 return std::nullopt;
304}
305
307 const IndexMask &mask,
308 ResourceScope & /*scope*/) const
309{
310 if (const GeometryFieldContext *geometry_context = dynamic_cast<const GeometryFieldContext *>(
311 &context))
312 {
313 if (const Mesh *mesh = geometry_context->mesh()) {
314 return this->get_varray_for_context(*mesh, geometry_context->domain(), mask);
315 }
316 }
317 if (const MeshFieldContext *mesh_context = dynamic_cast<const MeshFieldContext *>(&context)) {
318 return this->get_varray_for_context(mesh_context->mesh(), mesh_context->domain(), mask);
319 }
320 return {};
321}
322
323std::optional<AttrDomain> MeshFieldInput::preferred_domain(const Mesh & /*mesh*/) const
324{
325 return std::nullopt;
326}
327
329 const IndexMask &mask,
330 ResourceScope & /*scope*/) const
331{
332 if (const GeometryFieldContext *geometry_context = dynamic_cast<const GeometryFieldContext *>(
333 &context))
334 {
335 if (const CurvesGeometry *curves = geometry_context->curves_or_strokes()) {
336 return this->get_varray_for_context(*curves, geometry_context->domain(), mask);
337 }
338 }
339 if (const CurvesFieldContext *curves_context = dynamic_cast<const CurvesFieldContext *>(
340 &context))
341 {
342 return this->get_varray_for_context(curves_context->curves(), curves_context->domain(), mask);
343 }
344 return {};
345}
346
347std::optional<AttrDomain> CurvesFieldInput::preferred_domain(
348 const CurvesGeometry & /*curves*/) const
349{
350 return std::nullopt;
351}
352
354 const IndexMask &mask,
355 ResourceScope & /*scope*/) const
356{
357 if (const GeometryFieldContext *geometry_context = dynamic_cast<const GeometryFieldContext *>(
358 &context))
359 {
360 if (const PointCloud *pointcloud = geometry_context->pointcloud()) {
361 return this->get_varray_for_context(*pointcloud, mask);
362 }
363 }
364 if (const PointCloudFieldContext *point_context = dynamic_cast<const PointCloudFieldContext *>(
365 &context))
366 {
367 return this->get_varray_for_context(point_context->pointcloud(), mask);
368 }
369 return {};
370}
371
373 const IndexMask &mask,
374 ResourceScope & /*scope*/) const
375{
376 if (const GeometryFieldContext *geometry_context = dynamic_cast<const GeometryFieldContext *>(
377 &context))
378 {
379 if (const Instances *instances = geometry_context->instances()) {
380 return this->get_varray_for_context(*instances, mask);
381 }
382 }
383 if (const InstancesFieldContext *instances_context = dynamic_cast<const InstancesFieldContext *>(
384 &context))
385 {
386 return this->get_varray_for_context(instances_context->instances(), mask);
387 }
388 return {};
389}
390
392 const IndexMask & /*mask*/) const
393{
395 const AttrDomain domain = context.domain();
396 if (const GreasePencil *grease_pencil = context.grease_pencil()) {
397 const AttributeAccessor layer_attributes = grease_pencil->attributes();
398 if (domain == AttrDomain::Layer) {
399 return *layer_attributes.lookup(name_, data_type);
400 }
402 const int layer_index = context.grease_pencil_layer_index();
403 const AttributeAccessor curves_attributes = *context.attributes();
404 if (const GAttributeReader reader = curves_attributes.lookup(name_, domain, data_type)) {
405 return *reader;
406 }
407 /* Lookup attribute on the layer domain if it does not exist on points or curves. */
408 if (const GAttributeReader reader = layer_attributes.lookup(name_)) {
409 const CPPType &cpp_type = reader.varray.type();
411 BLI_SCOPED_DEFER([&]() { cpp_type.destruct(value); });
412 reader.varray.get_to_uninitialized(layer_index, value);
413 const int domain_size = curves_attributes.domain_size(domain);
414 return GVArray::from_single(cpp_type, domain_size, value);
415 }
416 }
417 }
418 else if (context.domain() == bke::AttrDomain::Instance && name_ == "position") {
419 /* Special case for "position" which is no longer an attribute on instances. */
420 return bke::instance_position_varray(*context.instances());
421 }
422 else if (auto attributes = context.attributes()) {
423 return *attributes->lookup(name_, domain, data_type);
424 }
425
426 return {};
427}
428
430 const IndexMask & /*mask*/) const
431{
432 const AttrDomain domain = context.domain();
433 if (context.type() == GeometryComponent::Type::GreasePencil) {
434 const AttributeAccessor layer_attributes = context.grease_pencil()->attributes();
435 if (context.domain() == AttrDomain::Layer) {
436 const bool exists = layer_attributes.contains(name_);
437 const int domain_size = layer_attributes.domain_size(AttrDomain::Layer);
438 return VArray<bool>::from_single(exists, domain_size);
439 }
440 const greasepencil::Drawing *drawing = context.grease_pencil_layer_drawing();
441 const AttributeAccessor curve_attributes = drawing->strokes().attributes();
442 const bool exists = layer_attributes.contains(name_) || curve_attributes.contains(name_);
443 const int domain_size = curve_attributes.domain_size(domain);
444 return VArray<bool>::from_single(exists, domain_size);
445 }
446 const bool exists = context.attributes()->contains(name_);
447 const int domain_size = context.attributes()->domain_size(domain);
448 return VArray<bool>::from_single(exists, domain_size);
449}
450
452{
453 if (socket_inspection_name_) {
454 return *socket_inspection_name_;
455 }
456 return fmt::format(fmt::runtime(TIP_("\"{}\" attribute from geometry")), name_);
457}
458
460{
461 return get_default_hash(name_, type_);
462}
463
465{
466 if (const AttributeFieldInput *other_typed = dynamic_cast<const AttributeFieldInput *>(&other)) {
467 return name_ == other_typed->name_ && type_ == other_typed->type_;
468 }
469 return false;
470}
471
473 const GeometryComponent &component) const
474{
475 const std::optional<AttributeAccessor> attributes = component.attributes();
476 if (!attributes.has_value()) {
477 return std::nullopt;
478 }
479 const std::optional<AttributeMetaData> meta_data = attributes->lookup_meta_data(name_);
480 if (!meta_data.has_value()) {
481 return std::nullopt;
482 }
483 return meta_data->domain;
484}
485
487{
488 switch (domain) {
491 return "id";
492 default:
493 return "";
494 }
495}
496
498 const IndexMask &mask) const
499{
500
501 const StringRef name = get_random_id_attribute_name(context.domain());
502 if (auto attributes = context.attributes()) {
503 if (GVArray attribute = *attributes->lookup<int>(name, context.domain())) {
504 return attribute;
505 }
506 }
507
508 /* Use the index as the fallback if no random ID attribute exists. */
510}
511
513{
514 return TIP_("ID / Index");
515}
516
518{
519 /* All random ID attribute inputs are the same within the same evaluation context. */
520 return 92386459827;
521}
522
524{
525 /* All random ID attribute inputs are the same within the same evaluation context. */
526 return dynamic_cast<const IDAttributeFieldInput *>(&other) != nullptr;
527}
528
530 const bke::GeometryFieldContext &context, const IndexMask &mask) const
531{
532 using namespace bke::greasepencil;
533 const AttrDomain domain = context.domain();
535 return {};
536 }
537
538 const GreasePencil &grease_pencil = *context.grease_pencil();
539 if (!context.grease_pencil()) {
540 return {};
541 }
542
543 auto layer_is_selected = [selection_name = StringRef(layer_name_),
544 &grease_pencil,
545 size = mask.min_array_size()](const int layer_i) {
546 if (layer_i < 0 || layer_i >= grease_pencil.layers().size()) {
547 return false;
548 }
549 const Layer &layer = grease_pencil.layer(layer_i);
550 return layer.name() == selection_name;
551 };
552
554 const int layer_i = context.grease_pencil_layer_index();
555 const bool selected = layer_is_selected(layer_i);
556 return VArray<bool>::from_single(selected, mask.min_array_size());
557 }
558
559 return VArray<bool>::from_func(mask.min_array_size(), layer_is_selected);
560}
561
563{
564 return get_default_hash(layer_name_, type_);
565}
566
568{
569 if (const NamedLayerSelectionFieldInput *other_named_layer =
570 dynamic_cast<const NamedLayerSelectionFieldInput *>(&other))
571 {
572 return layer_name_ == other_named_layer->layer_name_;
573 }
574 return false;
575}
576
578 const bke::GeometryComponent & /*component*/) const
579{
580 return AttrDomain::Layer;
581}
582
583template<typename T>
585 const VArray<int> &indices,
586 const IndexMask &mask,
587 MutableSpan<T> dst)
588{
589 const IndexRange src_range = src.index_range();
590 devirtualize_varray2(src, indices, [&](const auto src, const auto indices) {
591 mask.foreach_index(GrainSize(4096), [&](const int i) {
592 const int index = indices[i];
593 if (src_range.contains(index)) {
594 dst[i] = src[index];
595 }
596 else {
597 dst[i] = {};
598 }
599 });
600 });
601}
602
604 const VArray<int> &indices,
605 const IndexMask &mask,
606 GMutableSpan dst)
607{
609 using T = decltype(dummy);
610 copy_with_checked_indices(src.typed<T>(), indices, mask, dst.typed<T>());
611 });
612}
613
615 fn::GField value_field,
616 AttrDomain value_field_domain)
617 : bke::GeometryFieldInput(value_field.cpp_type(), "Evaluate at Index"),
618 index_field_(std::move(index_field)),
619 value_field_(std::move(value_field)),
620 value_field_domain_(value_field_domain)
621{
622}
623
625 const IndexMask &mask) const
626{
627 const std::optional<AttributeAccessor> attributes = context.attributes();
628 if (!attributes) {
629 return {};
630 }
631
632 const bke::GeometryFieldContext value_context{context, value_field_domain_};
633 fn::FieldEvaluator value_evaluator{value_context, attributes->domain_size(value_field_domain_)};
634 value_evaluator.add(value_field_);
635 value_evaluator.evaluate();
636 const GVArray &values = value_evaluator.get_evaluated(0);
637
638 fn::FieldEvaluator index_evaluator{context, &mask};
639 index_evaluator.add(index_field_);
640 index_evaluator.evaluate();
641 const VArray<int> indices = index_evaluator.get_evaluated<int>(0);
642
643 GArray<> dst_array(values.type(), mask.min_array_size());
644 copy_with_checked_indices(values, indices, mask, dst_array);
645 return GVArray::from_garray(std::move(dst_array));
646}
647
649 : bke::GeometryFieldInput(field.cpp_type(), "Evaluate on Domain"),
650 src_field_(std::move(field)),
651 src_domain_(domain)
652{
653}
654
656 const IndexMask & /*mask*/) const
657{
658 const AttrDomain dst_domain = context.domain();
659 const int dst_domain_size = context.attributes()->domain_size(dst_domain);
660 const CPPType &cpp_type = src_field_.cpp_type();
661
662 if (context.type() == GeometryComponent::Type::GreasePencil &&
663 (src_domain_ == AttrDomain::Layer) != (dst_domain == AttrDomain::Layer))
664 {
665 /* Evaluate field just for the current layer. */
666 if (src_domain_ == AttrDomain::Layer) {
667 const bke::GeometryFieldContext src_domain_context{context, AttrDomain::Layer};
668 const int layer_index = context.grease_pencil_layer_index();
669
670 const IndexMask single_layer_mask = IndexRange(layer_index, 1);
671 fn::FieldEvaluator value_evaluator{src_domain_context, &single_layer_mask};
672 value_evaluator.add(src_field_);
673 value_evaluator.evaluate();
674
675 const GVArray &values = value_evaluator.get_evaluated(0);
676
678 BLI_SCOPED_DEFER([&]() { cpp_type.destruct(value); });
679 values.get_to_uninitialized(layer_index, value);
680 return GVArray::from_single(cpp_type, dst_domain_size, value);
681 }
682 /* We don't adapt from curve to layer domain currently. */
683 return GVArray::from_single_default(cpp_type, dst_domain_size);
684 }
685
686 const bke::AttributeAccessor attributes = *context.attributes();
687
688 const bke::GeometryFieldContext other_domain_context{context, src_domain_};
689 const int64_t src_domain_size = attributes.domain_size(src_domain_);
690 GArray<> values(cpp_type, src_domain_size);
691 fn::FieldEvaluator value_evaluator{other_domain_context, src_domain_size};
692 value_evaluator.add_with_destination(src_field_, values.as_mutable_span());
693 value_evaluator.evaluate();
694 return attributes.adapt_domain(GVArray::from_garray(std::move(values)), src_domain_, dst_domain);
695}
696
698 FunctionRef<void(const FieldInput &)> fn) const
699{
700 src_field_.node().for_each_field_input_recursive(fn);
701}
702
704 const GeometryComponent & /*component*/) const
705{
706 return src_domain_;
707}
708
709} // namespace blender::bke
710
711/* -------------------------------------------------------------------- */
714
715namespace blender::bke {
716
718 const IndexMask &mask) const
719{
720 if (const Mesh *mesh = context.mesh()) {
721 return mesh_normals_varray(
722 *mesh, mask, context.domain(), legacy_corner_normals_, true_normals_);
723 }
724 if (const CurvesGeometry *curves = context.curves_or_strokes()) {
725 return curve_normals_varray(*curves, context.domain());
726 }
727 return {};
728}
729
731{
732 return true_normals_ ? TIP_("True Normal") : TIP_("Normal");
733}
734
736{
737 return get_default_hash(2980541, legacy_corner_normals_, true_normals_);
738}
739
741{
742 if (const NormalFieldInput *other_typed = dynamic_cast<const NormalFieldInput *>(&other)) {
743 return legacy_corner_normals_ == other_typed->legacy_corner_normals_ &&
744 true_normals_ == other_typed->true_normals_;
745 }
746 return false;
747}
748
749static std::optional<StringRefNull> try_get_field_direct_attribute_id(const fn::GField &any_field)
750{
751 if (const auto *field = dynamic_cast<const AttributeFieldInput *>(&any_field.node())) {
752 return field->attribute_name();
753 }
754 return {};
755}
756
757static bool attribute_kind_matches(const AttributeMetaData meta_data,
758 const AttrDomain domain,
759 const bke::AttrType data_type)
760{
761 return meta_data.domain == domain && meta_data.data_type == data_type;
762}
763
769 const StringRef id_to_create,
770 const AttrDomain domain,
771 const fn::GField &field)
772{
773 const std::optional<StringRef> field_id = try_get_field_direct_attribute_id(field);
774 if (!field_id) {
775 return false;
776 }
777 const std::optional<AttributeMetaData> meta_data = attributes.lookup_meta_data(*field_id);
778 if (!meta_data) {
779 return false;
780 }
781 const bke::AttrType data_type = bke::cpp_type_to_attribute_type(field.cpp_type());
782 if (!attribute_kind_matches(*meta_data, domain, data_type)) {
783 /* Avoid costly domain and type interpolation, which would make sharing impossible. */
784 return false;
785 }
786 const GAttributeReader attribute = attributes.lookup(*field_id, domain, data_type);
787 if (!attribute.sharing_info || !attribute.varray.is_span()) {
788 return false;
789 }
791 *attribute.sharing_info);
792 return attributes.add(id_to_create, domain, data_type, init);
793}
794
795static bool attribute_data_matches_varray(const GAttributeReader &attribute, const GVArray &varray)
796{
797 const CommonVArrayInfo varray_info = varray.common_info();
798 if (varray_info.type != CommonVArrayInfo::Type::Span) {
799 return false;
800 }
801 const CommonVArrayInfo attribute_info = attribute.varray.common_info();
802 if (attribute_info.type != CommonVArrayInfo::Type::Span) {
803 return false;
804 }
805 return varray_info.data == attribute_info.data;
806}
807
809 const AttrDomain domain,
810 const int domain_size,
811 const StringRef name,
812 const CPPType &type,
813 const bke::AttrType data_type,
814 void *buffer)
815{
816 /* NOTE: It's unnecessary to fill the values for elements that will be selected and also set
817 * during field evaluation. A future optimization could evaluate the selection separately and use
818 * its inverse here. */
819
820 if (attributes.is_builtin(name)) {
821 if (const GPointer value = attributes.get_builtin_default(name)) {
822 type.fill_construct_n(value.get(), buffer, domain_size);
823 return;
824 }
825 }
826 if (const GAttributeReader old_attribute = attributes.lookup(name, domain, data_type)) {
827 old_attribute.varray.materialize(buffer);
828 return;
829 }
830 type.fill_construct_n(type.default_value(), buffer, domain_size);
831}
832
834 const fn::FieldContext &field_context,
835 const Span<StringRef> attribute_ids,
836 const AttrDomain domain,
837 const fn::Field<bool> &selection,
838 const Span<fn::GField> fields)
839{
840 BLI_assert(attribute_ids.size() == fields.size());
841 const int domain_size = attributes.domain_size(domain);
842 if (domain_size == 0) {
843 bool all_added = true;
844 for (const int i : attribute_ids.index_range()) {
845 const bke::AttrType data_type = bke::cpp_type_to_attribute_type(fields[i].cpp_type());
846 all_added &= attributes.add(attribute_ids[i], domain, data_type, AttributeInitConstruct{});
847 }
848 return all_added;
849 }
850
851 fn::FieldEvaluator evaluator{field_context, domain_size};
852 evaluator.set_selection(selection);
853
854 const bool selection_is_full = !selection.node().depends_on_input() &&
856
857 struct StoreResult {
858 int input_index;
859 int evaluator_index;
860 };
861 Vector<StoreResult> results_to_store;
862
863 struct AddResult {
864 int input_index;
865 int evaluator_index;
866 void *buffer;
867 };
868 Vector<AddResult> results_to_add;
869
870 bool success = true;
871
872 for (const int input_index : attribute_ids.index_range()) {
873 const StringRef id = attribute_ids[input_index];
874 const CPPType &type = fields[input_index].cpp_type();
875 const bke::AttrType data_type = bke::cpp_type_to_attribute_type(type);
876
877 /* Avoid adding or writing to builtin attributes with an incorrect type or domain. */
878 if (const std::optional<AttributeDomainAndType> meta_data =
879 attributes.get_builtin_domain_and_type(id))
880 {
881 if (*meta_data != AttributeDomainAndType{domain, data_type}) {
882 success = false;
883 continue;
884 }
885 }
886
887 const AttributeValidator validator = attributes.lookup_validator(id);
888 const fn::GField field = validator.validate_field_if_necessary(fields[input_index]);
889
890 /* We are writing to an attribute that exists already with the correct domain and type. */
891 if (const GAttributeReader dst = attributes.lookup(id)) {
892 if (dst.domain == domain && dst.varray.type() == field.cpp_type()) {
893 const int evaluator_index = evaluator.add(field);
894 results_to_store.append({input_index, evaluator_index});
895 continue;
896 }
897 }
898
899 if (!validator && selection_is_full) {
900 if (try_add_shared_field_attribute(attributes, id, domain, field)) {
901 continue;
902 }
903 }
904
905 /* Could avoid allocating a new buffer if:
906 * - The field does not depend on that attribute (we can't easily check for that yet). */
907 void *buffer = MEM_mallocN_aligned(type.size * domain_size, type.alignment, __func__);
908 if (!selection_is_full) {
909 initialize_new_data(attributes, domain, domain_size, id, type, data_type, buffer);
910 }
911
912 GMutableSpan dst(type, buffer, domain_size);
913 const int evaluator_index = evaluator.add_with_destination(field, dst);
914 results_to_add.append({input_index, evaluator_index, buffer});
915 }
916
917 evaluator.evaluate();
919
920 for (const StoreResult &result : results_to_store) {
921 const StringRef id = attribute_ids[result.input_index];
922 const GVArray &result_data = evaluator.get_evaluated(result.evaluator_index);
923 const GAttributeReader dst = attributes.lookup(id);
924 if (!attribute_data_matches_varray(dst, result_data)) {
925 GSpanAttributeWriter dst_mut = attributes.lookup_for_write_span(id);
926 array_utils::copy(result_data, mask, dst_mut.span);
927 dst_mut.finish();
928 }
929 }
930
931 for (const AddResult &result : results_to_add) {
932 const StringRef id = attribute_ids[result.input_index];
933 attributes.remove(id);
934 const CPPType &type = fields[result.input_index].cpp_type();
935 const bke::AttrType data_type = bke::cpp_type_to_attribute_type(type);
936 if (!attributes.add(id, domain, data_type, AttributeInitMoveArray(result.buffer))) {
937 /* If the name corresponds to a builtin attribute, removing the attribute might fail if
938 * it's required, adding the attribute might fail if the domain or type is incorrect. */
939 type.destruct_n(result.buffer, domain_size);
940 MEM_freeN(result.buffer);
941 success = false;
942 }
943 }
944
945 return success;
946}
947
949 const Span<StringRef> attribute_ids,
950 const AttrDomain domain,
951 const fn::Field<bool> &selection,
952 const Span<fn::GField> fields)
953{
954 const GeometryComponent::Type component_type = component.type();
955 if (component_type == GeometryComponent::Type::GreasePencil &&
957 {
958 /* Capture the field on every layer individually. */
959 auto &grease_pencil_component = static_cast<GreasePencilComponent &>(component);
960 GreasePencil *grease_pencil = grease_pencil_component.get_for_write();
961 if (grease_pencil == nullptr) {
962 return false;
963 }
964 bool any_success = false;
965 threading::parallel_for(grease_pencil->layers().index_range(), 8, [&](const IndexRange range) {
966 for (const int layer_index : range) {
967 if (greasepencil::Drawing *drawing = grease_pencil->get_eval_drawing(
968 grease_pencil->layer(layer_index)))
969 {
970 const GeometryFieldContext field_context{*grease_pencil, domain, layer_index};
971 const bool success = try_capture_fields_on_geometry(
972 drawing->strokes_for_write().attributes_for_write(),
973 field_context,
974 attribute_ids,
975 domain,
976 selection,
977 fields);
978 if (success & !any_success) {
979 any_success = true;
980 }
981 }
982 }
983 });
984 return any_success;
985 }
986 if (component_type == GeometryComponent::Type::GreasePencil && domain != AttrDomain::Layer) {
987 /* The remaining code only handles the layer domain for grease pencil geometries. */
988 return false;
989 }
990
991 MutableAttributeAccessor attributes = *component.attributes_for_write();
992 const GeometryFieldContext field_context{component, domain};
994 attributes, field_context, attribute_ids, domain, selection, fields);
995}
996
998 const Span<StringRef> attribute_ids,
999 const AttrDomain domain,
1000 const Span<fn::GField> fields)
1001{
1002 const fn::Field<bool> selection = fn::make_constant_field<bool>(true);
1003 return try_capture_fields_on_geometry(component, attribute_ids, domain, selection, fields);
1004}
1005
1006std::optional<AttrDomain> try_detect_field_domain(const GeometryComponent &component,
1007 const fn::GField &field)
1008{
1009 const GeometryComponent::Type component_type = component.type();
1010 if (component_type == GeometryComponent::Type::PointCloud) {
1011 return AttrDomain::Point;
1012 }
1013 if (component_type == GeometryComponent::Type::GreasePencil) {
1014 return AttrDomain::Layer;
1015 }
1016 if (component_type == GeometryComponent::Type::Instance) {
1017 return AttrDomain::Instance;
1018 }
1019 const std::shared_ptr<const fn::FieldInputs> &field_inputs = field.node().field_inputs();
1020 if (!field_inputs) {
1021 return std::nullopt;
1022 }
1023 std::optional<AttrDomain> output_domain;
1024 auto handle_domain = [&](const std::optional<AttrDomain> domain) {
1025 if (!domain.has_value()) {
1026 return false;
1027 }
1028 if (output_domain.has_value()) {
1029 if (*output_domain != *domain) {
1030 return false;
1031 }
1032 return true;
1033 }
1034 output_domain = domain;
1035 return true;
1036 };
1037 if (component_type == GeometryComponent::Type::Mesh) {
1038 const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
1039 const Mesh *mesh = mesh_component.get();
1040 if (mesh == nullptr) {
1041 return std::nullopt;
1042 }
1043 for (const fn::FieldInput &field_input : field_inputs->deduplicated_nodes) {
1044 if (const auto *geometry_field_input = dynamic_cast<const GeometryFieldInput *>(
1045 &field_input))
1046 {
1047 if (!handle_domain(geometry_field_input->preferred_domain(component))) {
1048 return std::nullopt;
1049 }
1050 }
1051 else if (const auto *mesh_field_input = dynamic_cast<const MeshFieldInput *>(&field_input)) {
1052 if (!handle_domain(mesh_field_input->preferred_domain(*mesh))) {
1053 return std::nullopt;
1054 }
1055 }
1056 else {
1057 return std::nullopt;
1058 }
1059 }
1060 }
1061 if (component_type == GeometryComponent::Type::Curve) {
1062 const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
1063 const Curves *curves = curve_component.get();
1064 if (curves == nullptr) {
1065 return std::nullopt;
1066 }
1067 for (const fn::FieldInput &field_input : field_inputs->deduplicated_nodes) {
1068 if (const auto *geometry_field_input = dynamic_cast<const GeometryFieldInput *>(
1069 &field_input))
1070 {
1071 if (!handle_domain(geometry_field_input->preferred_domain(component))) {
1072 return std::nullopt;
1073 }
1074 }
1075 else if (const auto *curves_field_input = dynamic_cast<const CurvesFieldInput *>(
1076 &field_input))
1077 {
1078 if (!handle_domain(curves_field_input->preferred_domain(curves->geometry.wrap()))) {
1079 return std::nullopt;
1080 }
1081 }
1082 else {
1083 return std::nullopt;
1084 }
1085 }
1086 }
1087 return output_domain;
1088}
1089
1090} // namespace blender::bke
1091
Low-level operations for curves.
Low-level operations for grease pencil.
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BUFFER_FOR_CPP_TYPE_VALUE(type, variable_name)
#define BLI_SCOPED_DEFER(function_to_defer)
#define ELEM(...)
#define TIP_(msgid)
void init()
long long int int64_t
unsigned long long int uint64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
void destruct_n(void *ptr, int64_t n) const
void fill_construct_n(const void *value, void *dst, int64_t n) const
const void * default_value() const
GMutableSpan as_mutable_span()
const void * data() const
CommonVArrayInfo common_info() const
void get_to_uninitialized(int64_t index, void *r_value) const
static GVArray from_single_default(const CPPType &type, int64_t size)
static GVArray from_garray(GArray<> array)
static GVArray from_single(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:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
IndexRange index_range() const
static VArray from_single(T value, const int64_t size)
static VArray from_func(const int64_t size, GetFunc get_func)
void append(const T &value)
bool is_builtin(const StringRef attribute_id) const
std::optional< AttributeDomainAndType > get_builtin_domain_and_type(const StringRef name) const
AttributeValidator lookup_validator(const StringRef attribute_id) const
GPointer get_builtin_default(const StringRef attribute_id) 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
std::optional< AttributeMetaData > lookup_meta_data(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
AttributeFieldInput(std::string name, const CPPType &type, std::optional< std::string > socket_inspection_name=std::nullopt)
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
const CurvesGeometry & curves() const
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
AttributeAccessor attributes() const
GVArray get_varray_for_context(const bke::GeometryFieldContext &context, const IndexMask &mask) const final
EvaluateAtIndexInput(fn::Field< int > index_field, fn::GField value_field, AttrDomain value_field_domain)
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
FieldInput(const CPPType &type, std::string debug_name="")
Definition field.cc:677
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 override
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
bool add(const StringRef attribute_id, const AttrDomain domain, const AttrType data_type, const AttributeInit &initializer)
bool remove(const StringRef attribute_id)
GSpanAttributeWriter lookup_for_write_span(StringRef attribute_id)
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
NormalFieldInput(const bool legacy_corner_normals=false, const bool true_normals=false)
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
void set_selection(Field< bool > selection)
Definition FN_field.hh:383
int add(GField field, GVArray *varray_ptr)
Definition field.cc:751
IndexMask get_evaluated_selection_as_mask() const
Definition field.cc:817
int add_with_destination(GField field, GVMutableArray dst)
Definition field.cc:738
const GVArray & get_evaluated(const int field_index) const
Definition FN_field.hh:448
const CPPType * type_
Definition FN_field.hh:270
const CPPType & cpp_type() const
Definition FN_field.hh:632
virtual GVArray get_varray_for_context(const FieldContext &context, const IndexMask &mask, ResourceScope &scope) const =0
bool depends_on_input() const
Definition FN_field.hh:554
const std::shared_ptr< const FieldInputs > & field_inputs() const
Definition FN_field.hh:559
const CPPType & cpp_type() const
Definition FN_field.hh:130
const FieldNode & node() const
Definition FN_field.hh:135
static GVArray get_index_varray(const IndexMask &mask)
Definition field.cc:548
static ushort indices[]
void * MEM_mallocN_aligned(size_t len, size_t alignment, const char *str)
Definition mallocn.cc:138
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
ccl_device_inline float2 mask(const MaskType mask, const float2 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_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)
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 > mesh_normals_varray(const Mesh &mesh, const IndexMask &mask, AttrDomain domain, bool no_corner_normals=false, bool true_normals=false)
VArray< float3 > instance_position_varray(const Instances &instances)
Definition instances.cc:529
static bool try_add_shared_field_attribute(MutableAttributeAccessor attributes, const StringRef id_to_create, const AttrDomain domain, const fn::GField &field)
static void initialize_new_data(MutableAttributeAccessor &attributes, const AttrDomain domain, const int domain_size, const StringRef name, const CPPType &type, const bke::AttrType data_type, void *buffer)
static bool attribute_kind_matches(const AttributeMetaData meta_data, const AttrDomain domain, const bke::AttrType data_type)
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)
AttrType cpp_type_to_attribute_type(const CPPType &type)
GField make_constant_field(const CPPType &type, const void *value)
Definition field.cc:528
void evaluate_constant_field(const GField &field, void *r_value)
Definition field.cc:493
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
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, const Args &...args)
Definition BLI_hash.hh:233
float wrap(float value, float max, float min)
Definition node_math.h:103
static void init(bNodeTree *, bNode *node)
const char * name
fn::GField validate_field_if_necessary(const fn::GField &field) const
const ImplicitSharingInfo * sharing_info
i
Definition text_draw.cc:230