diff --git a/source/blender/blenkernel/BKE_anonymous_attribute.h b/source/blender/blenkernel/BKE_anonymous_attribute.h deleted file mode 100644 index ab26f2c6224..00000000000 --- a/source/blender/blenkernel/BKE_anonymous_attribute.h +++ /dev/null @@ -1,29 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#pragma once - -/** \file - * \ingroup bke - * - * An #AnonymousAttributeID is used to identify attributes that are not explicitly named. - */ - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct AnonymousAttributeID AnonymousAttributeID; - -AnonymousAttributeID *BKE_anonymous_attribute_id_new_weak(const char *debug_name); -AnonymousAttributeID *BKE_anonymous_attribute_id_new_strong(const char *debug_name); -bool BKE_anonymous_attribute_id_has_strong_references(const AnonymousAttributeID *anonymous_id); -void BKE_anonymous_attribute_id_increment_weak(const AnonymousAttributeID *anonymous_id); -void BKE_anonymous_attribute_id_increment_strong(const AnonymousAttributeID *anonymous_id); -void BKE_anonymous_attribute_id_decrement_weak(const AnonymousAttributeID *anonymous_id); -void BKE_anonymous_attribute_id_decrement_strong(const AnonymousAttributeID *anonymous_id); -const char *BKE_anonymous_attribute_id_debug_name(const AnonymousAttributeID *anonymous_id); -const char *BKE_anonymous_attribute_id_internal_name(const AnonymousAttributeID *anonymous_id); - -#ifdef __cplusplus -} -#endif diff --git a/source/blender/blenkernel/BKE_anonymous_attribute.hh b/source/blender/blenkernel/BKE_anonymous_attribute.hh deleted file mode 100644 index d9161bda572..00000000000 --- a/source/blender/blenkernel/BKE_anonymous_attribute.hh +++ /dev/null @@ -1,155 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#pragma once - -#include -#include - -#include "BLI_hash.hh" -#include "BLI_string_ref.hh" - -#include "BKE_anonymous_attribute.h" - -namespace blender::bke { - -/** - * Wrapper for #AnonymousAttributeID with RAII semantics. - * This class should typically not be used directly. Instead use #StrongAnonymousAttributeID or - * #WeakAnonymousAttributeID. - */ -template class OwnedAnonymousAttributeID { - private: - const AnonymousAttributeID *data_ = nullptr; - - template friend class OwnedAnonymousAttributeID; - - public: - OwnedAnonymousAttributeID() = default; - - /** Create a new anonymous attribute id. */ - explicit OwnedAnonymousAttributeID(StringRefNull debug_name) - { - if constexpr (IsStrongReference) { - data_ = BKE_anonymous_attribute_id_new_strong(debug_name.c_str()); - } - else { - data_ = BKE_anonymous_attribute_id_new_weak(debug_name.c_str()); - } - } - - /** - * This transfers ownership, so no incref is necessary. - * The caller has to make sure that it owned the anonymous id. - */ - explicit OwnedAnonymousAttributeID(const AnonymousAttributeID *anonymous_id) - : data_(anonymous_id) - { - } - - template - OwnedAnonymousAttributeID(const OwnedAnonymousAttributeID &other) - { - data_ = other.data_; - this->incref(); - } - - template - OwnedAnonymousAttributeID(OwnedAnonymousAttributeID &&other) - { - data_ = other.data_; - this->incref(); - other.decref(); - other.data_ = nullptr; - } - - ~OwnedAnonymousAttributeID() - { - this->decref(); - } - - template - OwnedAnonymousAttributeID &operator=(const OwnedAnonymousAttributeID &other) - { - if (this == &other) { - return *this; - } - this->~OwnedAnonymousAttributeID(); - new (this) OwnedAnonymousAttributeID(other); - return *this; - } - - template - OwnedAnonymousAttributeID &operator=(OwnedAnonymousAttributeID &&other) - { - if (this == &other) { - return *this; - } - this->~OwnedAnonymousAttributeID(); - new (this) OwnedAnonymousAttributeID(std::move(other)); - return *this; - } - - operator bool() const - { - return data_ != nullptr; - } - - StringRefNull debug_name() const - { - BLI_assert(data_ != nullptr); - return BKE_anonymous_attribute_id_debug_name(data_); - } - - bool has_strong_references() const - { - BLI_assert(data_ != nullptr); - return BKE_anonymous_attribute_id_has_strong_references(data_); - } - - /** Extract the ownership of the currently wrapped anonymous id. */ - const AnonymousAttributeID *extract() - { - const AnonymousAttributeID *extracted_data = data_; - /* Don't decref because the caller becomes the new owner. */ - data_ = nullptr; - return extracted_data; - } - - /** Get the wrapped anonymous id, without taking ownership. */ - const AnonymousAttributeID *get() const - { - return data_; - } - - private: - void incref() - { - if (data_ == nullptr) { - return; - } - if constexpr (IsStrongReference) { - BKE_anonymous_attribute_id_increment_strong(data_); - } - else { - BKE_anonymous_attribute_id_increment_weak(data_); - } - } - - void decref() - { - if (data_ == nullptr) { - return; - } - if constexpr (IsStrongReference) { - BKE_anonymous_attribute_id_decrement_strong(data_); - } - else { - BKE_anonymous_attribute_id_decrement_weak(data_); - } - } -}; - -using StrongAnonymousAttributeID = OwnedAnonymousAttributeID; -using WeakAnonymousAttributeID = OwnedAnonymousAttributeID; - -} // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_anonymous_attribute_id.hh b/source/blender/blenkernel/BKE_anonymous_attribute_id.hh new file mode 100644 index 00000000000..f9b6e2ca543 --- /dev/null +++ b/source/blender/blenkernel/BKE_anonymous_attribute_id.hh @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include + +#include "BLI_set.hh" +#include "BLI_string_ref.hh" +#include "BLI_user_counter.hh" + +namespace blender::bke { + +/** + * An #AnonymousAttributeID contains information about a specific anonymous attribute. + * Like normal attributes, anonymous attributes are also identified by their name, so one should + * not have to compare #AnonymousAttributeID pointers. + * + * Anonymous attributes don't need additional information besides their name, with a few + * exceptions: + * - The name of anonymous attributes is generated automatically, so it is generally not human + * readable (just random characters). #AnonymousAttributeID can provide more context as where a + * specific anonymous attribute was created which can simplify debugging. + * - [Not yet supported.] When anonymous attributes are contained in on-disk caches, we have to map + * those back to anonymous attributes at run-time. The issue is that (for various reasons) we + * might change how anonymous attribute names are generated in the future, which would lead to a + * mis-match between stored and new attribute names. To work around it, we should cache + * additional information for anonymous attributes on disk (like which node created it). This + * information can then be used to map stored attributes to their run-time counterpart. + * + * Once created, #AnonymousAttributeID is immutable. Also it is intrinsicly reference counted so + * that it can have shared ownership. `std::shared_ptr` can't be used for that purpose here, + * because that is not available in C code. If possible, the #AutoAnonymousAttributeID wrapper + * should be used to avoid manual reference counting in C++ code. + */ +class AnonymousAttributeID { + private: + mutable std::atomic users_ = 1; + + protected: + std::string name_; + + public: + virtual ~AnonymousAttributeID() = default; + + StringRefNull name() const + { + return name_; + } + + void user_add() const + { + users_.fetch_add(1); + } + + void user_remove() const + { + const int new_users = users_.fetch_sub(1) - 1; + if (new_users == 0) { + MEM_delete(this); + } + } +}; + +/** Wrapper for #AnonymousAttributeID that avoids manual reference counting. */ +using AutoAnonymousAttributeID = UserCounter; + +/** + * A set of anonymous attribute names that is passed around in geometry nodes. + */ +class AnonymousAttributeSet { + public: + /** + * This uses `std::shared_ptr` because attributes sets are passed around by value during geometry + * nodes evaluation, and this makes it very small if there is no name. Also it makes copying very + * cheap. + */ + std::shared_ptr> names; +}; + +/** + * Can be passed to algorithms which propagate attributes. It can tell the algorithm which + * anonymous attributes should be propagated and can be skipped. + */ +class AnonymousAttributePropagationInfo { + public: + /** + * This uses `std::shared_ptr` because it's usually initialized from an #AnonymousAttributeSet + * and then the set doesn't have to be copied. + */ + std::shared_ptr> names; + + /** + * Propagate all anonymous attributes even if the set above is empty. + */ + bool propagate_all = true; + + /** + * Return true when the anonymous attribute should be propagated and false otherwise. + */ + bool propagate(const AnonymousAttributeID &anonymous_id) const; +}; + +} // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_attribute.hh b/source/blender/blenkernel/BKE_attribute.hh index a4f9d73c31e..031a4bb86ea 100644 --- a/source/blender/blenkernel/BKE_attribute.hh +++ b/source/blender/blenkernel/BKE_attribute.hh @@ -11,7 +11,7 @@ #include "BLI_math_vec_types.hh" #include "BLI_set.hh" -#include "BKE_anonymous_attribute.hh" +#include "BKE_anonymous_attribute_id.hh" #include "BKE_attribute.h" struct Mesh; @@ -24,7 +24,7 @@ class GField; namespace blender::bke { /** - * Identifies an attribute that is either named or anonymous. + * Identifies an attribute with optional anonymous attribute information. * It does not own the identifier, so it is just a reference. */ class AttributeIDRef { @@ -38,15 +38,14 @@ class AttributeIDRef { AttributeIDRef(StringRefNull name); AttributeIDRef(const char *name); AttributeIDRef(const std::string &name); + AttributeIDRef(const AnonymousAttributeID &anonymous_id); AttributeIDRef(const AnonymousAttributeID *anonymous_id); operator bool() const; uint64_t hash() const; - bool is_named() const; bool is_anonymous() const; StringRef name() const; const AnonymousAttributeID &anonymous_id() const; - bool should_be_kept() const; friend bool operator==(const AttributeIDRef &a, const AttributeIDRef &b); friend std::ostream &operator<<(std::ostream &stream, const AttributeIDRef &attribute_id); @@ -749,6 +748,7 @@ Vector retrieve_attributes_for_transfer( const bke::AttributeAccessor src_attributes, bke::MutableAttributeAccessor dst_attributes, eAttrDomainMask domain_mask, + const AnonymousAttributePropagationInfo &propagation_info, const Set &skip = {}); /** @@ -762,6 +762,7 @@ void copy_attribute_domain(AttributeAccessor src_attributes, MutableAttributeAccessor dst_attributes, IndexMask selection, eAttrDomain domain, + const AnonymousAttributePropagationInfo &propagation_info, const Set &skip = {}); bool allow_procedural_attribute_access(StringRef attribute_name); @@ -852,29 +853,31 @@ inline AttributeIDRef::AttributeIDRef(const std::string &name) : name_(name) } /* The anonymous id is only borrowed, the caller has to keep a reference to it. */ -inline AttributeIDRef::AttributeIDRef(const AnonymousAttributeID *anonymous_id) - : anonymous_id_(anonymous_id) +inline AttributeIDRef::AttributeIDRef(const AnonymousAttributeID &anonymous_id) + : AttributeIDRef(anonymous_id.name()) { + anonymous_id_ = &anonymous_id; +} + +inline AttributeIDRef::AttributeIDRef(const AnonymousAttributeID *anonymous_id) + : AttributeIDRef(anonymous_id ? anonymous_id->name() : "") +{ + anonymous_id_ = anonymous_id; } inline bool operator==(const AttributeIDRef &a, const AttributeIDRef &b) { - return a.anonymous_id_ == b.anonymous_id_ && a.name_ == b.name_; + return a.name_ == b.name_; } inline AttributeIDRef::operator bool() const { - return this->is_named() || this->is_anonymous(); + return !name_.is_empty(); } inline uint64_t AttributeIDRef::hash() const { - return get_default_hash_2(name_, anonymous_id_); -} - -inline bool AttributeIDRef::is_named() const -{ - return !name_.is_empty(); + return get_default_hash(name_); } inline bool AttributeIDRef::is_anonymous() const @@ -884,7 +887,6 @@ inline bool AttributeIDRef::is_anonymous() const inline StringRef AttributeIDRef::name() const { - BLI_assert(this->is_named()); return name_; } @@ -894,14 +896,4 @@ inline const AnonymousAttributeID &AttributeIDRef::anonymous_id() const return *anonymous_id_; } -/** - * \return True if the attribute should not be removed automatically as an optimization during - * processing or copying. Anonymous attributes can be removed when they no longer have any - * references. - */ -inline bool AttributeIDRef::should_be_kept() const -{ - return this->is_named() || BKE_anonymous_attribute_id_has_strong_references(anonymous_id_); -} - } // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_curve_to_mesh.hh b/source/blender/blenkernel/BKE_curve_to_mesh.hh index 6e657542e0f..0f67da2d8a5 100644 --- a/source/blender/blenkernel/BKE_curve_to_mesh.hh +++ b/source/blender/blenkernel/BKE_curve_to_mesh.hh @@ -11,6 +11,8 @@ struct Mesh; namespace blender::bke { +class AnonymousAttributePropagationInfo; + /** * Extrude all splines in the profile curve along the path of every spline in the curve input. * Transfer curve attributes to the mesh. @@ -23,11 +25,13 @@ namespace blender::bke { */ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main, const CurvesGeometry &profile, - bool fill_caps); + bool fill_caps, + const AnonymousAttributePropagationInfo &propagation_info); /** * Create a loose-edge mesh based on the evaluated path of the curve's splines. * Transfer curve attributes to the mesh. */ -Mesh *curve_to_wire_mesh(const CurvesGeometry &curve); +Mesh *curve_to_wire_mesh(const CurvesGeometry &curve, + const AnonymousAttributePropagationInfo &propagation_info); } // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh index 31776676940..9f849584710 100644 --- a/source/blender/blenkernel/BKE_curves.hh +++ b/source/blender/blenkernel/BKE_curves.hh @@ -404,8 +404,10 @@ class CurvesGeometry : public ::CurvesGeometry { void calculate_bezier_auto_handles(); - void remove_points(IndexMask points_to_delete); - void remove_curves(IndexMask curves_to_delete); + void remove_points(IndexMask points_to_delete, + const AnonymousAttributePropagationInfo &propagation_info = {}); + void remove_curves(IndexMask curves_to_delete, + const AnonymousAttributePropagationInfo &propagation_info = {}); /** * Change the direction of selected curves (switch the start and end) without changing their diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index 1cdd3c02d8d..582dd1b5a29 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -23,7 +23,6 @@ extern "C" { #endif -struct AnonymousAttributeID; struct BMesh; struct BlendDataReader; struct BlendWriter; @@ -227,7 +226,7 @@ void *CustomData_add_layer_anonymous(struct CustomData *data, eCDAllocType alloctype, void *layer, int totelem, - const struct AnonymousAttributeID *anonymous_id); + const AnonymousAttributeIDHandle *anonymous_id); /** * Frees the active or first data layer with the give type. @@ -275,8 +274,6 @@ void *CustomData_duplicate_referenced_layer_named(struct CustomData *data, int type, const char *name, int totelem); -void *CustomData_duplicate_referenced_layer_anonymous( - CustomData *data, int type, const struct AnonymousAttributeID *anonymous_id, int totelem); bool CustomData_is_referenced_layer(struct CustomData *data, int type); /** diff --git a/source/blender/blenkernel/BKE_geometry_fields.hh b/source/blender/blenkernel/BKE_geometry_fields.hh index 7b493ea5ca9..967bb912cc6 100644 --- a/source/blender/blenkernel/BKE_geometry_fields.hh +++ b/source/blender/blenkernel/BKE_geometry_fields.hh @@ -261,18 +261,14 @@ class NormalFieldInput : public GeometryFieldInput { class AnonymousAttributeFieldInput : public GeometryFieldInput { private: - /** - * A strong reference is required to make sure that the referenced attribute is not removed - * automatically. - */ - StrongAnonymousAttributeID anonymous_id_; + AutoAnonymousAttributeID anonymous_id_; std::string producer_name_; public: - AnonymousAttributeFieldInput(StrongAnonymousAttributeID anonymous_id, + AnonymousAttributeFieldInput(AutoAnonymousAttributeID anonymous_id, const CPPType &type, std::string producer_name) - : GeometryFieldInput(type, anonymous_id.debug_name()), + : GeometryFieldInput(type, anonymous_id->name()), anonymous_id_(std::move(anonymous_id)), producer_name_(producer_name) { @@ -280,7 +276,7 @@ class AnonymousAttributeFieldInput : public GeometryFieldInput { } template - static fn::Field Create(StrongAnonymousAttributeID anonymous_id, std::string producer_name) + static fn::Field Create(AutoAnonymousAttributeID anonymous_id, std::string producer_name) { const CPPType &type = CPPType::get(); auto field_input = std::make_shared( @@ -288,6 +284,11 @@ class AnonymousAttributeFieldInput : public GeometryFieldInput { return fn::Field{field_input}; } + const AutoAnonymousAttributeID &anonymous_id() const + { + return anonymous_id_; + } + GVArray get_varray_for_context(const GeometryFieldContext &context, IndexMask mask) const override; diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index b488806c8e7..e6f55a8500f 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -19,7 +19,7 @@ #include "BLI_user_counter.hh" #include "BLI_vector_set.hh" -#include "BKE_anonymous_attribute.hh" +#include "BKE_anonymous_attribute_id.hh" #include "BKE_attribute.hh" #include "BKE_geometry_set.h" @@ -213,6 +213,7 @@ struct GeometrySet { blender::Span component_types, GeometryComponentType dst_component_type, bool include_instances, + const blender::bke::AnonymousAttributePropagationInfo &propagation_info, blender::Map &r_attributes) const; blender::Vector gather_component_types(bool include_instances, diff --git a/source/blender/blenkernel/BKE_instances.hh b/source/blender/blenkernel/BKE_instances.hh index f17ebba0dfa..3d44e3a4686 100644 --- a/source/blender/blenkernel/BKE_instances.hh +++ b/source/blender/blenkernel/BKE_instances.hh @@ -155,7 +155,8 @@ class Instances { * Remove the indices that are not contained in the mask input, and remove unused instance * references afterwards. */ - void remove(const blender::IndexMask mask); + void remove(const blender::IndexMask mask, + const blender::bke::AnonymousAttributePropagationInfo &propagation_info); /** * Get an id for every instance. These can be used for e.g. motion blur. */ diff --git a/source/blender/blenkernel/BKE_node_runtime.hh b/source/blender/blenkernel/BKE_node_runtime.hh index 6941bcb023a..ca2fa5d0b01 100644 --- a/source/blender/blenkernel/BKE_node_runtime.hh +++ b/source/blender/blenkernel/BKE_node_runtime.hh @@ -7,6 +7,7 @@ #include "BLI_cache_mutex.hh" #include "BLI_multi_value_map.hh" +#include "BLI_resource_scope.hh" #include "BLI_utility_mixins.hh" #include "BLI_vector.hh" #include "BLI_vector_set.hh" @@ -24,6 +25,10 @@ namespace blender::nodes { struct FieldInferencingInterface; class NodeDeclaration; struct GeometryNodesLazyFunctionGraphInfo; +namespace anonymous_attribute_lifetime { +struct RelationsInNode; +} +namespace aal = anonymous_attribute_lifetime; } // namespace blender::nodes namespace blender { @@ -106,6 +111,8 @@ class bNodeTreeRuntime : NonCopyable, NonMovable { /** Information about how inputs and outputs of the node group interact with fields. */ std::unique_ptr field_inferencing_interface; + /** Information about usage of anonymous attributes within the group. */ + std::unique_ptr anonymous_attribute_relations; /** * For geometry nodes, a lazy function graph with some additional info is cached. This is used to @@ -330,7 +337,11 @@ inline bool topology_cache_is_available(const bNodeSocket &socket) namespace node_field_inferencing { bool update_field_inferencing(const bNodeTree &tree); } - +namespace anonymous_attribute_inferencing { +Array get_relations_by_node(const bNodeTree &tree, + ResourceScope &scope); +bool update_anonymous_attribute_relations(bNodeTree &tree); +} // namespace anonymous_attribute_inferencing } // namespace blender::bke /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index bfd9eaaa23e..101baf1e983 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -64,7 +64,7 @@ set(SRC intern/anim_path.c intern/anim_sys.c intern/anim_visualization.c - intern/anonymous_attribute.cc + intern/anonymous_attribute_id.cc intern/appdir.c intern/armature.c intern/armature_deform.c @@ -229,6 +229,7 @@ set(SRC intern/nla.c intern/node.cc intern/node_runtime.cc + intern/node_tree_anonymous_attributes.cc intern/node_tree_field_inferencing.cc intern/node_tree_update.cc intern/object.cc @@ -315,8 +316,7 @@ set(SRC BKE_anim_path.h BKE_anim_visualization.h BKE_animsys.h - BKE_anonymous_attribute.h - BKE_anonymous_attribute.hh + BKE_anonymous_attribute_id.hh BKE_appdir.h BKE_armature.h BKE_armature.hh diff --git a/source/blender/blenkernel/intern/anonymous_attribute.cc b/source/blender/blenkernel/intern/anonymous_attribute.cc deleted file mode 100644 index 636e0af0edf..00000000000 --- a/source/blender/blenkernel/intern/anonymous_attribute.cc +++ /dev/null @@ -1,105 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BKE_anonymous_attribute.hh" - -using namespace blender::bke; - -/** - * A struct that identifies an attribute. It's lifetime is managed by an atomic reference count. - * - * Additionally, this struct can be strongly or weakly owned. The difference is that strong - * ownership means that attributes with this id will be kept around. Weak ownership just makes sure - * that the struct itself stays alive, but corresponding attributes might still be removed - * automatically. - */ -struct AnonymousAttributeID { - /** - * Total number of references to this attribute id. Once this reaches zero, the struct can be - * freed. This includes strong and weak references. - */ - mutable std::atomic refcount_tot = 0; - - /** - * Number of strong references to this attribute id. When this is zero, the corresponding - * attributes can be removed from geometries automatically. - */ - mutable std::atomic refcount_strong = 0; - - /** - * Only used to identify this struct in a debugging session. - */ - std::string debug_name; - - /** - * Unique name of the this attribute id during the current session. - */ - std::string internal_name; -}; - -/** Every time this function is called, it outputs a different name. */ -static std::string get_new_internal_name() -{ - static std::atomic index = 0; - const int next_index = index.fetch_add(1); - return ".a_" + std::to_string(next_index); -} - -AnonymousAttributeID *BKE_anonymous_attribute_id_new_weak(const char *debug_name) -{ - AnonymousAttributeID *anonymous_id = new AnonymousAttributeID(); - anonymous_id->debug_name = debug_name; - anonymous_id->internal_name = get_new_internal_name(); - anonymous_id->refcount_tot.store(1); - return anonymous_id; -} - -AnonymousAttributeID *BKE_anonymous_attribute_id_new_strong(const char *debug_name) -{ - AnonymousAttributeID *anonymous_id = new AnonymousAttributeID(); - anonymous_id->debug_name = debug_name; - anonymous_id->internal_name = get_new_internal_name(); - anonymous_id->refcount_tot.store(1); - anonymous_id->refcount_strong.store(1); - return anonymous_id; -} - -bool BKE_anonymous_attribute_id_has_strong_references(const AnonymousAttributeID *anonymous_id) -{ - return anonymous_id->refcount_strong.load() >= 1; -} - -void BKE_anonymous_attribute_id_increment_weak(const AnonymousAttributeID *anonymous_id) -{ - anonymous_id->refcount_tot.fetch_add(1); -} - -void BKE_anonymous_attribute_id_increment_strong(const AnonymousAttributeID *anonymous_id) -{ - anonymous_id->refcount_tot.fetch_add(1); - anonymous_id->refcount_strong.fetch_add(1); -} - -void BKE_anonymous_attribute_id_decrement_weak(const AnonymousAttributeID *anonymous_id) -{ - const int new_refcount = anonymous_id->refcount_tot.fetch_sub(1) - 1; - if (new_refcount == 0) { - BLI_assert(anonymous_id->refcount_strong == 0); - delete anonymous_id; - } -} - -void BKE_anonymous_attribute_id_decrement_strong(const AnonymousAttributeID *anonymous_id) -{ - anonymous_id->refcount_strong.fetch_sub(1); - BKE_anonymous_attribute_id_decrement_weak(anonymous_id); -} - -const char *BKE_anonymous_attribute_id_debug_name(const AnonymousAttributeID *anonymous_id) -{ - return anonymous_id->debug_name.c_str(); -} - -const char *BKE_anonymous_attribute_id_internal_name(const AnonymousAttributeID *anonymous_id) -{ - return anonymous_id->internal_name.c_str(); -} diff --git a/source/blender/blenkernel/intern/anonymous_attribute_id.cc b/source/blender/blenkernel/intern/anonymous_attribute_id.cc new file mode 100644 index 00000000000..e15ea6b643c --- /dev/null +++ b/source/blender/blenkernel/intern/anonymous_attribute_id.cc @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_anonymous_attribute_id.hh" + +namespace blender::bke { + +bool AnonymousAttributePropagationInfo::propagate(const AnonymousAttributeID &anonymous_id) const +{ + if (this->propagate_all) { + return true; + } + if (!this->names) { + return false; + } + return this->names->contains_as(anonymous_id.name()); +} + +} // namespace blender::bke diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index e5c43a3f90e..a5dbc4645fb 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -40,13 +40,9 @@ namespace blender::bke { std::ostream &operator<<(std::ostream &stream, const AttributeIDRef &attribute_id) { - if (attribute_id.is_named()) { + if (attribute_id) { stream << attribute_id.name(); } - else if (attribute_id.is_anonymous()) { - const AnonymousAttributeID &anonymous_id = attribute_id.anonymous_id(); - stream << "<" << BKE_anonymous_attribute_id_debug_name(&anonymous_id) << ">"; - } else { stream << ""; } @@ -153,7 +149,7 @@ eAttrDomain attribute_domain_highest_priority(Span domains) static AttributeIDRef attribute_id_from_custom_data_layer(const CustomDataLayer &layer) { if (layer.anonymous_id != nullptr) { - return layer.anonymous_id; + return *layer.anonymous_id; } return layer.name; } @@ -207,7 +203,7 @@ static void *add_generic_custom_data_layer(CustomData &custom_data, const int domain_num, const AttributeIDRef &attribute_id) { - if (attribute_id.is_named()) { + if (!attribute_id.is_anonymous()) { char attribute_name_c[MAX_NAME]; attribute_id.name().copy(attribute_name_c); return CustomData_add_layer_named( @@ -261,9 +257,6 @@ static bool custom_data_layer_matches_attribute_id(const CustomDataLayer &layer, if (!attribute_id) { return false; } - if (attribute_id.is_anonymous()) { - return layer.anonymous_id == &attribute_id.anonymous_id(); - } return layer.name == attribute_id.name(); } @@ -451,14 +444,8 @@ GAttributeWriter CustomDataAttributeProvider::try_get_for_write( if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) { continue; } - if (attribute_id.is_named()) { - CustomData_duplicate_referenced_layer_named( - custom_data, layer.type, layer.name, element_num); - } - else { - CustomData_duplicate_referenced_layer_anonymous( - custom_data, layer.type, &attribute_id.anonymous_id(), element_num); - } + CustomData_duplicate_referenced_layer_named(custom_data, layer.type, layer.name, element_num); + const CPPType *type = custom_data_type_to_cpp_type((eCustomDataType)layer.type); if (type == nullptr) { continue; @@ -854,7 +841,7 @@ void MutableAttributeAccessor::remove_anonymous() } while (!anonymous_ids.is_empty()) { - this->remove(anonymous_ids.pop_last()); + this->remove(*anonymous_ids.pop_last()); } } @@ -883,12 +870,7 @@ GAttributeWriter MutableAttributeAccessor::lookup_for_write(const AttributeIDRef #ifdef DEBUG if (attribute) { auto checker = std::make_shared(); - if (attribute_id.is_named()) { - checker->name = attribute_id.name(); - } - else { - checker->name = BKE_anonymous_attribute_id_debug_name(&attribute_id.anonymous_id()); - } + checker->name = attribute_id.name(); checker->real_finish_fn = attribute.tag_modified_fn; attribute.tag_modified_fn = [checker]() { if (checker->real_finish_fn) { @@ -968,6 +950,7 @@ Vector retrieve_attributes_for_transfer( const bke::AttributeAccessor src_attributes, bke::MutableAttributeAccessor dst_attributes, const eAttrDomainMask domain_mask, + const AnonymousAttributePropagationInfo &propagation_info, const Set &skip) { Vector attributes; @@ -976,10 +959,10 @@ Vector retrieve_attributes_for_transfer( if (!(ATTR_DOMAIN_AS_MASK(meta_data.domain) & domain_mask)) { return true; } - if (id.is_named() && skip.contains(id.name())) { + if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) { return true; } - if (!id.should_be_kept()) { + if (skip.contains(id.name())) { return true; } @@ -999,6 +982,7 @@ void copy_attribute_domain(const AttributeAccessor src_attributes, MutableAttributeAccessor dst_attributes, const IndexMask selection, const eAttrDomain domain, + const AnonymousAttributePropagationInfo &propagation_info, const Set &skip) { src_attributes.for_all( @@ -1006,10 +990,10 @@ void copy_attribute_domain(const AttributeAccessor src_attributes, if (meta_data.domain != domain) { return true; } - if (id.is_named() && skip.contains(id.name())) { + if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) { return true; } - if (!id.should_be_kept()) { + if (skip.contains(id.name())) { return true; } diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh index 5ab7c6aadf3..ccf9ae83182 100644 --- a/source/blender/blenkernel/intern/attribute_access_intern.hh +++ b/source/blender/blenkernel/intern/attribute_access_intern.hh @@ -336,7 +336,7 @@ namespace attribute_accessor_functions { template inline bool is_builtin(const void * /*owner*/, const AttributeIDRef &attribute_id) { - if (!attribute_id.is_named()) { + if (attribute_id.is_anonymous()) { return false; } const StringRef name = attribute_id.name(); @@ -346,7 +346,7 @@ inline bool is_builtin(const void * /*owner*/, const AttributeIDRef &attribute_i template inline GAttributeReader lookup(const void *owner, const AttributeIDRef &attribute_id) { - if (attribute_id.is_named()) { + if (!attribute_id.is_anonymous()) { const StringRef name = attribute_id.name(); if (const BuiltinAttributeProvider *provider = providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) { @@ -396,7 +396,7 @@ template inline AttributeValidator lookup_validator(const void * /*owner*/, const blender::bke::AttributeIDRef &attribute_id) { - if (!attribute_id.is_named()) { + if (attribute_id.is_anonymous()) { return {}; } const BuiltinAttributeProvider *provider = @@ -443,7 +443,7 @@ inline std::optional lookup_meta_data(const void *owner, template inline GAttributeWriter lookup_for_write(void *owner, const AttributeIDRef &attribute_id) { - if (attribute_id.is_named()) { + if (!attribute_id.is_anonymous()) { const StringRef name = attribute_id.name(); if (const BuiltinAttributeProvider *provider = providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) { @@ -462,7 +462,7 @@ inline GAttributeWriter lookup_for_write(void *owner, const AttributeIDRef &attr template inline bool remove(void *owner, const AttributeIDRef &attribute_id) { - if (attribute_id.is_named()) { + if (!attribute_id.is_anonymous()) { const StringRef name = attribute_id.name(); if (const BuiltinAttributeProvider *provider = providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) { @@ -487,7 +487,7 @@ inline bool add(void *owner, if (contains(owner, attribute_id)) { return false; } - if (attribute_id.is_named()) { + if (!attribute_id.is_anonymous()) { const StringRef name = attribute_id.name(); if (const BuiltinAttributeProvider *provider = providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) { diff --git a/source/blender/blenkernel/intern/cpp_types.cc b/source/blender/blenkernel/intern/cpp_types.cc index e6b33dd5b1a..d1f2c5efa55 100644 --- a/source/blender/blenkernel/intern/cpp_types.cc +++ b/source/blender/blenkernel/intern/cpp_types.cc @@ -28,6 +28,8 @@ BLI_CPP_TYPE_MAKE(Material *, CPPTypeFlags::BasicType) BLI_CPP_TYPE_MAKE(MStringProperty, CPPTypeFlags::None); +BLI_CPP_TYPE_MAKE(blender::bke::AnonymousAttributeSet, CPPTypeFlags::None); + void BKE_cpp_types_init() { blender::register_cpp_types(); @@ -45,4 +47,6 @@ void BKE_cpp_types_init() BLI_CPP_TYPE_REGISTER(Material *); BLI_CPP_TYPE_REGISTER(MStringProperty); + + BLI_CPP_TYPE_REGISTER(blender::bke::AnonymousAttributeSet); } diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc index e5cafd405df..14c41811505 100644 --- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc +++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc @@ -331,18 +331,19 @@ static eAttrDomain get_attribute_domain_for_mesh(const AttributeAccessor &mesh_a static bool should_add_attribute_to_mesh(const AttributeAccessor &curve_attributes, const AttributeAccessor &mesh_attributes, const AttributeIDRef &id, - const AttributeMetaData &meta_data) + const AttributeMetaData &meta_data, + const AnonymousAttributePropagationInfo &propagation_info) { /* The position attribute has special non-generic evaluation. */ - if (id.is_named() && id.name() == "position") { + if (id.name() == "position") { return false; } /* Don't propagate built-in curves attributes that are not built-in on meshes. */ if (curve_attributes.is_builtin(id) && !mesh_attributes.is_builtin(id)) { return false; } - if (!id.should_be_kept()) { + if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) { return false; } if (meta_data.data_type == CD_PROP_STRING) { @@ -629,7 +630,8 @@ static void copy_curve_domain_attribute_to_mesh(const ResultOffsets &mesh_offset Mesh *curve_to_mesh_sweep(const CurvesGeometry &main, const CurvesGeometry &profile, - const bool fill_caps) + const bool fill_caps, + const AnonymousAttributePropagationInfo &propagation_info) { const CurvesInfo curves_info = get_curves_info(main, profile); @@ -716,7 +718,8 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main, MutableAttributeAccessor mesh_attributes = mesh->attributes_for_write(); main_attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { - if (!should_add_attribute_to_mesh(main_attributes, mesh_attributes, id, meta_data)) { + if (!should_add_attribute_to_mesh( + main_attributes, mesh_attributes, id, meta_data, propagation_info)) { return true; } main_attributes_set.add_new(id); @@ -753,7 +756,8 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main, if (main_attributes.contains(id)) { return true; } - if (!should_add_attribute_to_mesh(profile_attributes, mesh_attributes, id, meta_data)) { + if (!should_add_attribute_to_mesh( + profile_attributes, mesh_attributes, id, meta_data, propagation_info)) { return true; } const eAttrDomain src_domain = meta_data.domain; @@ -797,10 +801,11 @@ static CurvesGeometry get_curve_single_vert() return curves; } -Mesh *curve_to_wire_mesh(const CurvesGeometry &curve) +Mesh *curve_to_wire_mesh(const CurvesGeometry &curve, + const AnonymousAttributePropagationInfo &propagation_info) { static const CurvesGeometry vert_curve = get_curve_single_vert(); - return curve_to_mesh_sweep(curve, vert_curve, false); + return curve_to_mesh_sweep(curve, vert_curve, false, propagation_info); } } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index 401dd113f2c..835b6728e99 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -1047,8 +1047,10 @@ static Array build_point_to_curve_map(const CurvesGeometry &curves) return point_to_curve_map; } -static CurvesGeometry copy_with_removed_points(const CurvesGeometry &curves, - const IndexMask points_to_delete) +static CurvesGeometry copy_with_removed_points( + const CurvesGeometry &curves, + const IndexMask points_to_delete, + const AnonymousAttributePropagationInfo &propagation_info) { /* Use a map from points to curves to facilitate using an #IndexMask input. */ const Array point_to_curve_map = build_point_to_curve_map(curves); @@ -1093,9 +1095,15 @@ static CurvesGeometry copy_with_removed_points(const CurvesGeometry &curves, CurvesGeometry new_curves{new_point_count, new_curve_count}; Vector point_attributes = bke::retrieve_attributes_for_transfer( - curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_POINT); + curves.attributes(), + new_curves.attributes_for_write(), + ATTR_DOMAIN_MASK_POINT, + propagation_info); Vector curve_attributes = bke::retrieve_attributes_for_transfer( - curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_CURVE); + curves.attributes(), + new_curves.attributes_for_write(), + ATTR_DOMAIN_MASK_CURVE, + propagation_info); threading::parallel_invoke( 256 < new_point_count * new_curve_count, @@ -1144,7 +1152,8 @@ static CurvesGeometry copy_with_removed_points(const CurvesGeometry &curves, return new_curves; } -void CurvesGeometry::remove_points(const IndexMask points_to_delete) +void CurvesGeometry::remove_points(const IndexMask points_to_delete, + const AnonymousAttributePropagationInfo &propagation_info) { if (points_to_delete.is_empty()) { return; @@ -1152,11 +1161,13 @@ void CurvesGeometry::remove_points(const IndexMask points_to_delete) if (points_to_delete.size() == this->points_num()) { *this = {}; } - *this = copy_with_removed_points(*this, points_to_delete); + *this = copy_with_removed_points(*this, points_to_delete, propagation_info); } -static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves, - const IndexMask curves_to_delete) +static CurvesGeometry copy_with_removed_curves( + const CurvesGeometry &curves, + const IndexMask curves_to_delete, + const AnonymousAttributePropagationInfo &propagation_info) { const Span old_offsets = curves.offsets(); const Vector old_curve_ranges = curves_to_delete.extract_ranges_invert( @@ -1178,9 +1189,15 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves, CurvesGeometry new_curves{new_tot_points, new_tot_curves}; Vector point_attributes = bke::retrieve_attributes_for_transfer( - curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_POINT); + curves.attributes(), + new_curves.attributes_for_write(), + ATTR_DOMAIN_MASK_POINT, + propagation_info); Vector curve_attributes = bke::retrieve_attributes_for_transfer( - curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_CURVE); + curves.attributes(), + new_curves.attributes_for_write(), + ATTR_DOMAIN_MASK_CURVE, + propagation_info); threading::parallel_invoke( 256 < new_tot_points * new_tot_curves, @@ -1251,7 +1268,8 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves, return new_curves; } -void CurvesGeometry::remove_curves(const IndexMask curves_to_delete) +void CurvesGeometry::remove_curves(const IndexMask curves_to_delete, + const AnonymousAttributePropagationInfo &propagation_info) { if (curves_to_delete.is_empty()) { return; @@ -1260,7 +1278,7 @@ void CurvesGeometry::remove_curves(const IndexMask curves_to_delete) *this = {}; return; } - *this = copy_with_removed_curves(*this, curves_to_delete); + *this = copy_with_removed_curves(*this, curves_to_delete, propagation_info); } template @@ -1315,7 +1333,7 @@ void CurvesGeometry::reverse_curves(const IndexMask curves_to_reverse) if (meta_data.data_type == CD_PROP_STRING) { return true; } - if (id.is_named() && bezier_handle_names.contains(id.name())) { + if (bezier_handle_names.contains(id.name())) { return true; } diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index cb22f4a650c..60d38895a89 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -39,7 +39,7 @@ #include "BLT_translation.h" -#include "BKE_anonymous_attribute.h" +#include "BKE_anonymous_attribute_id.hh" #include "BKE_customdata.h" #include "BKE_customdata_file.h" #include "BKE_deform.h" @@ -2356,7 +2356,7 @@ bool CustomData_merge(const CustomData *source, layer->anonymous_id = nullptr; } else { - BKE_anonymous_attribute_id_increment_weak(layer->anonymous_id); + layer->anonymous_id->user_add(); } } if (alloctype == CD_ASSIGN) { @@ -2460,7 +2460,7 @@ static void customData_free_layer__internal(CustomDataLayer *layer, const int to const LayerTypeInfo *typeInfo; if (layer->anonymous_id != nullptr) { - BKE_anonymous_attribute_id_decrement_weak(layer->anonymous_id); + layer->anonymous_id->user_remove(); layer->anonymous_id = nullptr; } if (!(layer->flag & CD_FLAG_NOFREE) && layer->data) { @@ -2957,9 +2957,9 @@ void *CustomData_add_layer_anonymous(CustomData *data, const eCDAllocType alloctype, void *layerdata, const int totelem, - const AnonymousAttributeID *anonymous_id) + const AnonymousAttributeIDHandle *anonymous_id) { - const char *name = BKE_anonymous_attribute_id_internal_name(anonymous_id); + const char *name = anonymous_id->name().c_str(); CustomDataLayer *layer = customData_add_layer__internal( data, type, alloctype, layerdata, totelem, name); CustomData_update_typemap(data); @@ -2968,7 +2968,7 @@ void *CustomData_add_layer_anonymous(CustomData *data, return nullptr; } - BKE_anonymous_attribute_id_increment_weak(anonymous_id); + anonymous_id->user_add(); layer->anonymous_id = anonymous_id; return layer->data; } @@ -3147,20 +3147,6 @@ void *CustomData_duplicate_referenced_layer_named(CustomData *data, return customData_duplicate_referenced_layer_index(data, layer_index, totelem); } -void *CustomData_duplicate_referenced_layer_anonymous(CustomData *data, - const int /*type*/, - const AnonymousAttributeID *anonymous_id, - const int totelem) -{ - for (int i = 0; i < data->totlayer; i++) { - if (data->layers[i].anonymous_id == anonymous_id) { - return customData_duplicate_referenced_layer_index(data, i, totelem); - } - } - BLI_assert_unreachable(); - return nullptr; -} - void CustomData_duplicate_referenced_layers(CustomData *data, const int totelem) { for (int i = 0; i < data->totlayer; i++) { diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index b9702466d17..855d422251d 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -1055,7 +1055,7 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { GAttributeReader try_get_for_read(const void *owner, const AttributeIDRef &attribute_id) const final { - if (!attribute_id.is_named()) { + if (attribute_id.is_anonymous()) { return {}; } const Mesh *mesh = static_cast(owner); @@ -1079,7 +1079,7 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { GAttributeWriter try_get_for_write(void *owner, const AttributeIDRef &attribute_id) const final { - if (!attribute_id.is_named()) { + if (attribute_id.is_anonymous()) { return {}; } Mesh *mesh = static_cast(owner); @@ -1100,7 +1100,7 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { bool try_delete(void *owner, const AttributeIDRef &attribute_id) const final { - if (!attribute_id.is_named()) { + if (attribute_id.is_anonymous()) { return false; } Mesh *mesh = static_cast(owner); diff --git a/source/blender/blenkernel/intern/geometry_fields.cc b/source/blender/blenkernel/intern/geometry_fields.cc index 82ffda57398..6fe822d6dc6 100644 --- a/source/blender/blenkernel/intern/geometry_fields.cc +++ b/source/blender/blenkernel/intern/geometry_fields.cc @@ -332,7 +332,7 @@ GVArray AnonymousAttributeFieldInput::get_varray_for_context(const GeometryField const IndexMask /*mask*/) const { const eCustomDataType data_type = cpp_type_to_custom_data_type(*type_); - return context.attributes()->lookup(anonymous_id_.get(), context.domain(), data_type); + return context.attributes()->lookup(*anonymous_id_, context.domain(), data_type); } std::string AnonymousAttributeFieldInput::socket_inspection_name() const @@ -363,8 +363,7 @@ std::optional AnonymousAttributeFieldInput::preferred_domain( if (!attributes.has_value()) { return std::nullopt; } - const std::optional meta_data = attributes->lookup_meta_data( - anonymous_id_.get()); + const std::optional meta_data = attributes->lookup_meta_data(*anonymous_id_); if (!meta_data.has_value()) { return std::nullopt; } diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index 1e03b8d235a..8fff80e709f 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -581,6 +581,7 @@ void GeometrySet::gather_attributes_for_propagation( const Span component_types, const GeometryComponentType dst_component_type, bool include_instances, + const blender::bke::AnonymousAttributePropagationInfo &propagation_info, blender::Map &r_attributes) const { using namespace blender; @@ -605,7 +606,8 @@ void GeometrySet::gather_attributes_for_propagation( /* Propagating string attributes is not supported yet. */ return; } - if (!attribute_id.should_be_kept()) { + if (attribute_id.is_anonymous() && + !propagation_info.propagate(attribute_id.anonymous_id())) { return; } diff --git a/source/blender/blenkernel/intern/instances.cc b/source/blender/blenkernel/intern/instances.cc index a2c344e918b..d98ad27f02c 100644 --- a/source/blender/blenkernel/intern/instances.cc +++ b/source/blender/blenkernel/intern/instances.cc @@ -105,7 +105,8 @@ blender::Span Instances::references() const return references_; } -void Instances::remove(const IndexMask mask) +void Instances::remove(const IndexMask mask, + const AnonymousAttributePropagationInfo &propagation_info) { using namespace blender; if (mask.is_range() && mask.as_range().start() == 0) { @@ -132,7 +133,7 @@ void Instances::remove(const IndexMask mask) src_attributes.foreach_attribute( [&](const bke::AttributeIDRef &id, const bke::AttributeMetaData &meta_data) { - if (!id.should_be_kept()) { + if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) { return true; } diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc index c59210d9d47..bbd70a9d152 100644 --- a/source/blender/blenkernel/intern/mesh_convert.cc +++ b/source/blender/blenkernel/intern/mesh_convert.cc @@ -839,7 +839,9 @@ static Mesh *mesh_new_from_evaluated_curve_type_object(const Object *evaluated_o return BKE_mesh_copy_for_eval(mesh, false); } if (const Curves *curves = get_evaluated_curves_from_object(evaluated_object)) { - return blender::bke::curve_to_wire_mesh(blender::bke::CurvesGeometry::wrap(curves->geometry)); + const blender::bke::AnonymousAttributePropagationInfo propagation_info; + return blender::bke::curve_to_wire_mesh(blender::bke::CurvesGeometry::wrap(curves->geometry), + propagation_info); } return nullptr; } diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index eeba322e30d..f72ba7a1378 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -207,6 +207,11 @@ static void ntree_copy_data(Main * /*bmain*/, ID *id_dst, const ID *id_src, cons dst_runtime.field_inferencing_interface = std::make_unique( *ntree_src->runtime->field_inferencing_interface); } + if (ntree_src->runtime->anonymous_attribute_relations) { + dst_runtime.anonymous_attribute_relations = + std::make_unique( + *ntree_src->runtime->anonymous_attribute_relations); + } if (flag & LIB_ID_COPY_NO_PREVIEW) { ntree_dst->preview = nullptr; diff --git a/source/blender/blenkernel/intern/node_tree_anonymous_attributes.cc b/source/blender/blenkernel/intern/node_tree_anonymous_attributes.cc new file mode 100644 index 00000000000..9ade129afb5 --- /dev/null +++ b/source/blender/blenkernel/intern/node_tree_anonymous_attributes.cc @@ -0,0 +1,452 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "NOD_node_declaration.hh" + +#include "BKE_node_runtime.hh" + +#include "BLI_multi_value_map.hh" +#include "BLI_resource_scope.hh" +#include "BLI_set.hh" +#include "BLI_stack.hh" +#include "BLI_timeit.hh" + +namespace blender::bke::anonymous_attribute_inferencing { +namespace aal = nodes::aal; +using nodes::NodeDeclaration; + +static const aal::RelationsInNode &get_relations_in_node(const bNode &node, ResourceScope &scope) +{ + if (node.is_group()) { + if (const bNodeTree *group = reinterpret_cast(node.id)) { + BLI_assert(group->runtime->anonymous_attribute_relations); + return *group->runtime->anonymous_attribute_relations; + } + } + if (const NodeDeclaration *node_decl = node.declaration()) { + if (const aal::RelationsInNode *relations = node_decl->anonymous_attribute_relations()) { + return *relations; + } + } + return scope.construct(); +} + +Array get_relations_by_node(const bNodeTree &tree, + ResourceScope &scope) +{ + const Span nodes = tree.all_nodes(); + Array relations_by_node(nodes.size()); + for (const int i : nodes.index_range()) { + relations_by_node[i] = &get_relations_in_node(*nodes[i], scope); + } + return relations_by_node; +} + +static bool socket_is_field(const bNodeSocket &socket) +{ + return socket.display_shape == SOCK_DISPLAY_SHAPE_DIAMOND; +} + +/** + * Start at a group output socket and find all linked group inputs. + */ +static Vector find_linked_group_inputs( + const bNodeTree &tree, + const bNodeSocket &group_output_socket, + const FunctionRef(const bNodeSocket &)> get_linked_node_inputs) +{ + Set found_sockets; + Stack sockets_to_check; + + Vector input_indices; + + found_sockets.add_new(&group_output_socket); + sockets_to_check.push(&group_output_socket); + + while (!sockets_to_check.is_empty()) { + const bNodeSocket &socket = *sockets_to_check.pop(); + if (socket.is_input()) { + for (const bNodeLink *link : socket.directly_linked_links()) { + if (link->is_used()) { + const bNodeSocket &from_socket = *link->fromsock; + if (found_sockets.add(&from_socket)) { + sockets_to_check.push(&from_socket); + } + } + } + } + else { + const bNode &node = socket.owner_node(); + for (const int input_index : get_linked_node_inputs(socket)) { + const bNodeSocket &input_socket = node.input_socket(input_index); + if (input_socket.is_available()) { + if (found_sockets.add(&input_socket)) { + sockets_to_check.push(&input_socket); + } + } + } + } + } + + for (const bNode *node : tree.group_input_nodes()) { + for (const bNodeSocket *socket : node->output_sockets()) { + if (found_sockets.contains(socket)) { + input_indices.append_non_duplicates(socket->index()); + } + } + } + + return input_indices; +} + +static void infer_propagate_relations(const bNodeTree &tree, + const Span relations_by_node, + const bNode &group_output_node, + aal::RelationsInNode &r_relations) +{ + for (const bNodeSocket *group_output_socket : group_output_node.input_sockets().drop_back(1)) { + if (group_output_socket->type != SOCK_GEOMETRY) { + continue; + } + const Vector input_indices = find_linked_group_inputs( + tree, *group_output_socket, [&](const bNodeSocket &output_socket) { + Vector indices; + for (const aal::PropagateRelation &relation : + relations_by_node[output_socket.owner_node().index()]->propagate_relations) { + if (relation.to_geometry_output == output_socket.index()) { + indices.append(relation.from_geometry_input); + } + } + return indices; + }); + for (const int input_index : input_indices) { + aal::PropagateRelation relation; + relation.from_geometry_input = input_index; + relation.to_geometry_output = group_output_socket->index(); + r_relations.propagate_relations.append(relation); + } + } +} + +static void infer_reference_relations(const bNodeTree &tree, + const Span relations_by_node, + const bNode &group_output_node, + aal::RelationsInNode &r_relations) +{ + for (const bNodeSocket *group_output_socket : group_output_node.input_sockets().drop_back(1)) { + if (!socket_is_field(*group_output_socket)) { + continue; + } + const Vector input_indices = find_linked_group_inputs( + tree, *group_output_socket, [&](const bNodeSocket &output_socket) { + Vector indices; + for (const aal::ReferenceRelation &relation : + relations_by_node[output_socket.owner_node().index()]->reference_relations) { + if (relation.to_field_output == output_socket.index()) { + indices.append(relation.from_field_input); + } + } + return indices; + }); + for (const int input_index : input_indices) { + if (tree.runtime->field_inferencing_interface->inputs[input_index] != + nodes::InputSocketFieldType::None) { + aal::ReferenceRelation relation; + relation.from_field_input = input_index; + relation.to_field_output = group_output_socket->index(); + r_relations.reference_relations.append(relation); + } + } + } +} + +/** + * Find group output geometries that contain anonymous attributes referenced by the field. + * If `nullopt` is returned, the field does not depend on any anonymous attribute created in this + * node tree. + */ +static std::optional> find_available_on_outputs( + const bNodeSocket &initial_group_output_socket, + const bNode &group_output_node, + const Span relations_by_node) +{ + Set geometry_sockets; + + { + /* Find the nodes that added anonymous attributes to the field. */ + Set found_sockets; + Stack sockets_to_check; + + found_sockets.add_new(&initial_group_output_socket); + sockets_to_check.push(&initial_group_output_socket); + + while (!sockets_to_check.is_empty()) { + const bNodeSocket &socket = *sockets_to_check.pop(); + if (socket.is_input()) { + for (const bNodeLink *link : socket.directly_linked_links()) { + if (link->is_used()) { + const bNodeSocket &from_socket = *link->fromsock; + if (found_sockets.add(&from_socket)) { + sockets_to_check.push(&from_socket); + } + } + } + } + else { + const bNode &node = socket.owner_node(); + const aal::RelationsInNode &relations = *relations_by_node[node.index()]; + for (const aal::AvailableRelation &relation : relations.available_relations) { + if (socket.index() == relation.field_output) { + const bNodeSocket &geometry_output = node.output_socket(relation.geometry_output); + if (geometry_output.is_available()) { + geometry_sockets.add(&geometry_output); + } + } + } + for (const aal::ReferenceRelation &relation : relations.reference_relations) { + if (socket.index() == relation.to_field_output) { + const bNodeSocket &field_input = node.input_socket(relation.from_field_input); + if (field_input.is_available()) { + if (found_sockets.add(&field_input)) { + sockets_to_check.push(&field_input); + } + } + } + } + } + } + } + + if (geometry_sockets.is_empty()) { + /* The field does not depend on any anonymous attribute created within this node tree. */ + return std::nullopt; + } + + /* Find the group output geometries that contain the anonymous attribute referenced by the field + * output. */ + Set found_sockets; + Stack sockets_to_check; + + for (const bNodeSocket *socket : geometry_sockets) { + found_sockets.add_new(socket); + sockets_to_check.push(socket); + } + + while (!sockets_to_check.is_empty()) { + const bNodeSocket &socket = *sockets_to_check.pop(); + if (socket.is_input()) { + const bNode &node = socket.owner_node(); + const aal::RelationsInNode &relations = *relations_by_node[node.index()]; + for (const aal::PropagateRelation &relation : relations.propagate_relations) { + if (socket.index() == relation.from_geometry_input) { + const bNodeSocket &output_socket = node.output_socket(relation.to_geometry_output); + if (output_socket.is_available()) { + if (found_sockets.add(&output_socket)) { + sockets_to_check.push(&output_socket); + } + } + } + } + } + else { + for (const bNodeLink *link : socket.directly_linked_links()) { + if (link->is_used()) { + const bNodeSocket &to_socket = *link->tosock; + if (found_sockets.add(&to_socket)) { + sockets_to_check.push(&to_socket); + } + } + } + } + } + + Vector output_indices; + for (const bNodeSocket *socket : group_output_node.input_sockets().drop_back(1)) { + if (found_sockets.contains(socket)) { + output_indices.append(socket->index()); + } + } + return output_indices; +} + +static void infer_available_relations(const Span relations_by_node, + const bNode &group_output_node, + aal::RelationsInNode &r_relations) +{ + for (const bNodeSocket *group_output_socket : group_output_node.input_sockets().drop_back(1)) { + if (!socket_is_field(*group_output_socket)) { + continue; + } + const std::optional> output_indices = find_available_on_outputs( + *group_output_socket, group_output_node, relations_by_node); + if (output_indices.has_value()) { + if (output_indices->is_empty()) { + r_relations.available_on_none.append(group_output_socket->index()); + } + else { + for (const int output_index : *output_indices) { + aal::AvailableRelation relation; + relation.field_output = group_output_socket->index(); + relation.geometry_output = output_index; + r_relations.available_relations.append(relation); + } + } + } + } +} + +/** + * Returns a list of all the geometry inputs that the field input may be evaluated on. + */ +static Vector find_eval_on_inputs(const bNodeTree &tree, + const int field_input_index, + const Span relations_by_node) +{ + const Span group_input_nodes = tree.group_input_nodes(); + Set geometry_sockets; + + { + /* Find all the nodes that evaluate the input field. */ + Set found_sockets; + Stack sockets_to_check; + + for (const bNode *node : group_input_nodes) { + const bNodeSocket &socket = node->output_socket(field_input_index); + found_sockets.add_new(&socket); + sockets_to_check.push(&socket); + } + + while (!sockets_to_check.is_empty()) { + const bNodeSocket &socket = *sockets_to_check.pop(); + if (socket.is_input()) { + const bNode &node = socket.owner_node(); + const aal::RelationsInNode &relations = *relations_by_node[node.index()]; + for (const aal::EvalRelation &relation : relations.eval_relations) { + if (socket.index() == relation.field_input) { + const bNodeSocket &geometry_input = node.input_socket(relation.geometry_input); + if (geometry_input.is_available()) { + geometry_sockets.add(&geometry_input); + } + } + } + for (const aal::ReferenceRelation &relation : relations.reference_relations) { + if (socket.index() == relation.from_field_input) { + const bNodeSocket &field_output = node.output_socket(relation.to_field_output); + if (field_output.is_available()) { + if (found_sockets.add(&field_output)) { + sockets_to_check.push(&field_output); + } + } + } + } + } + else { + for (const bNodeLink *link : socket.directly_linked_links()) { + if (link->is_used()) { + const bNodeSocket &to_socket = *link->tosock; + if (found_sockets.add(&to_socket)) { + sockets_to_check.push(&to_socket); + } + } + } + } + } + } + + if (geometry_sockets.is_empty()) { + return {}; + } + + /* Find the group input geometries whose attributes are propagated to the nodes that evaluate the + * field. */ + Set found_sockets; + Stack sockets_to_check; + + Vector geometry_input_indices; + + for (const bNodeSocket *socket : geometry_sockets) { + found_sockets.add_new(socket); + sockets_to_check.push(socket); + } + + while (!sockets_to_check.is_empty()) { + const bNodeSocket &socket = *sockets_to_check.pop(); + if (socket.is_input()) { + for (const bNodeLink *link : socket.directly_linked_links()) { + if (link->is_used()) { + const bNodeSocket &from_socket = *link->fromsock; + if (found_sockets.add(&from_socket)) { + sockets_to_check.push(&from_socket); + } + } + } + } + else { + const bNode &node = socket.owner_node(); + if (node.is_group_input()) { + geometry_input_indices.append_non_duplicates(socket.index()); + } + else { + const aal::RelationsInNode &relations = *relations_by_node[node.index()]; + for (const aal::PropagateRelation &relation : relations.propagate_relations) { + if (socket.index() == relation.to_geometry_output) { + const bNodeSocket &input_socket = node.input_socket(relation.from_geometry_input); + if (input_socket.is_available()) { + if (found_sockets.add(&input_socket)) { + sockets_to_check.push(&input_socket); + } + } + } + } + } + } + } + + return geometry_input_indices; +} + +static void infer_eval_relations(const bNodeTree &tree, + const Span relations_by_node, + aal::RelationsInNode &r_relations) +{ + for (const int input_index : tree.interface_inputs().index_range()) { + if (tree.runtime->field_inferencing_interface->inputs[input_index] == + nodes::InputSocketFieldType::None) { + continue; + } + const Vector geometry_input_indices = find_eval_on_inputs( + tree, input_index, relations_by_node); + for (const int geometry_input : geometry_input_indices) { + aal::EvalRelation relation; + relation.field_input = input_index; + relation.geometry_input = geometry_input; + r_relations.eval_relations.append(std::move(relation)); + } + } +} + +bool update_anonymous_attribute_relations(bNodeTree &tree) +{ + tree.ensure_topology_cache(); + + ResourceScope scope; + Array relations_by_node = get_relations_by_node(tree, scope); + + std::unique_ptr new_relations = std::make_unique(); + if (!tree.has_available_link_cycle()) { + if (const bNode *group_output_node = tree.group_output_node()) { + infer_propagate_relations(tree, relations_by_node, *group_output_node, *new_relations); + infer_reference_relations(tree, relations_by_node, *group_output_node, *new_relations); + infer_available_relations(relations_by_node, *group_output_node, *new_relations); + } + infer_eval_relations(tree, relations_by_node, *new_relations); + } + + const bool group_interface_changed = !tree.runtime->anonymous_attribute_relations || + *tree.runtime->anonymous_attribute_relations != + *new_relations; + tree.runtime->anonymous_attribute_relations = std::move(new_relations); + + return group_interface_changed; +} + +} // namespace blender::bke::anonymous_attribute_inferencing diff --git a/source/blender/blenkernel/intern/node_tree_update.cc b/source/blender/blenkernel/intern/node_tree_update.cc index d01cbd1d62d..93e8f24787a 100644 --- a/source/blender/blenkernel/intern/node_tree_update.cc +++ b/source/blender/blenkernel/intern/node_tree_update.cc @@ -473,6 +473,9 @@ class NodeTreeMainUpdater { if (node_field_inferencing::update_field_inferencing(ntree)) { result.interface_changed = true; } + if (anonymous_attribute_inferencing::update_anonymous_attribute_relations(ntree)) { + result.interface_changed = true; + } } result.output_changed = this->check_if_output_changed(ntree); diff --git a/source/blender/editors/mesh/mesh_data.cc b/source/blender/editors/mesh/mesh_data.cc index a2310dddc61..204fb7012ab 100644 --- a/source/blender/editors/mesh/mesh_data.cc +++ b/source/blender/editors/mesh/mesh_data.cc @@ -1484,5 +1484,6 @@ void ED_mesh_split_faces(Mesh *mesh) return; } - geometry::split_edges(*mesh, split_mask); + const bke::AnonymousAttributePropagationInfo propagation_info; + geometry::split_edges(*mesh, split_mask, propagation_info); } diff --git a/source/blender/functions/FN_lazy_function_graph.hh b/source/blender/functions/FN_lazy_function_graph.hh index 460e858774f..6d66afafe82 100644 --- a/source/blender/functions/FN_lazy_function_graph.hh +++ b/source/blender/functions/FN_lazy_function_graph.hh @@ -53,6 +53,10 @@ class Socket : NonCopyable, NonMovable { * Index of the socket. E.g. 0 for the first input and the first output socket. */ int index_in_node_; + /** + * Index of the socket in the entire graph. Every socket has a different index. + */ + int index_in_graph_; friend Graph; @@ -61,6 +65,7 @@ class Socket : NonCopyable, NonMovable { bool is_output() const; int index() const; + int index_in_graph() const; InputSocket &as_input(); OutputSocket &as_output(); @@ -179,6 +184,20 @@ class DummyDebugInfo { virtual std::string output_name(const int i) const; }; +/** + * Just stores a string per socket in a dummy node. + */ +class SimpleDummyDebugInfo : public DummyDebugInfo { + public: + std::string name; + Vector input_names; + Vector output_names; + + std::string node_name() const override; + std::string input_name(const int i) const override; + std::string output_name(const int i) const override; +}; + /** * A #Node that does *not* correspond to a #LazyFunction. Instead it can be used to indicate inputs * and outputs of the entire graph. It can have an arbitrary number of inputs and outputs. @@ -205,6 +224,11 @@ class Graph : NonCopyable, NonMovable { * Contains all nodes in the graph so that it is efficient to iterate over them. */ Vector nodes_; + /** + * Number of sockets in the graph. Can be used as array size when indexing using + * `Socket::index_in_graph`. + */ + int socket_num_ = 0; public: ~Graph(); @@ -213,6 +237,7 @@ class Graph : NonCopyable, NonMovable { * Get all nodes in the graph. The index in the span corresponds to #Node::index_in_graph. */ Span nodes() const; + Span nodes(); /** * Add a new function node with sockets that match the passed in #LazyFunction. @@ -232,10 +257,24 @@ class Graph : NonCopyable, NonMovable { */ void add_link(OutputSocket &from, InputSocket &to); + /** + * If the socket is linked, remove the link. + */ + void clear_origin(InputSocket &socket); + /** * Make sure that #Node::index_in_graph is up to date. */ void update_node_indices(); + /** + * Make sure that #Socket::index_in_graph is up to date. + */ + void update_socket_indices(); + + /** + * Number of sockets in the graph. + */ + int socket_num() const; /** * Can be used to assert that #update_node_indices has been called. @@ -280,6 +319,11 @@ inline int Socket::index() const return index_in_node_; } +inline int Socket::index_in_graph() const +{ + return index_in_graph_; +} + inline InputSocket &Socket::as_input() { BLI_assert(this->is_input()); @@ -445,6 +489,16 @@ inline Span Graph::nodes() const return nodes_; } +inline Span Graph::nodes() +{ + return nodes_; +} + +inline int Graph::socket_num() const +{ + return socket_num_; +} + /** \} */ } // namespace blender::fn::lazy_function diff --git a/source/blender/functions/intern/lazy_function_graph.cc b/source/blender/functions/intern/lazy_function_graph.cc index e8a20fbf9a3..e07cce7204b 100644 --- a/source/blender/functions/intern/lazy_function_graph.cc +++ b/source/blender/functions/intern/lazy_function_graph.cc @@ -86,6 +86,14 @@ void Graph::add_link(OutputSocket &from, InputSocket &to) from.targets_.append(&to); } +void Graph::clear_origin(InputSocket &socket) +{ + if (socket.origin_ != nullptr) { + socket.origin_->targets_.remove_first_occurrence_and_reorder(&socket); + socket.origin_ = nullptr; + } +} + void Graph::update_node_indices() { for (const int i : nodes_.index_range()) { @@ -93,6 +101,20 @@ void Graph::update_node_indices() } } +void Graph::update_socket_indices() +{ + int socket_counter = 0; + for (const int i : nodes_.index_range()) { + for (InputSocket *socket : nodes_[i]->inputs()) { + socket->index_in_graph_ = socket_counter++; + } + for (OutputSocket *socket : nodes_[i]->outputs()) { + socket->index_in_graph_ = socket_counter++; + } + } + socket_num_ = socket_counter; +} + bool Graph::node_indices_are_valid() const { for (const int i : nodes_.index_range()) { @@ -152,6 +174,21 @@ std::string DummyDebugInfo::output_name(const int /*i*/) const return fallback_name; } +std::string SimpleDummyDebugInfo::node_name() const +{ + return this->name; +} + +std::string SimpleDummyDebugInfo::input_name(const int i) const +{ + return this->input_names[i]; +} + +std::string SimpleDummyDebugInfo::output_name(const int i) const +{ + return this->output_names[i]; +} + std::string Graph::ToDotOptions::socket_name(const Socket &socket) const { return socket.name(); diff --git a/source/blender/geometry/GEO_fillet_curves.hh b/source/blender/geometry/GEO_fillet_curves.hh index 1f832f8b6cc..8019e6bf7ed 100644 --- a/source/blender/geometry/GEO_fillet_curves.hh +++ b/source/blender/geometry/GEO_fillet_curves.hh @@ -9,15 +9,19 @@ namespace blender::geometry { -bke::CurvesGeometry fillet_curves_poly(const bke::CurvesGeometry &src_curves, - IndexMask curve_selection, - const VArray &radius, - const VArray &counts, - bool limit_radius); +bke::CurvesGeometry fillet_curves_poly( + const bke::CurvesGeometry &src_curves, + IndexMask curve_selection, + const VArray &radius, + const VArray &counts, + bool limit_radius, + const bke::AnonymousAttributePropagationInfo &propagation_info); -bke::CurvesGeometry fillet_curves_bezier(const bke::CurvesGeometry &src_curves, - IndexMask curve_selection, - const VArray &radius, - bool limit_radius); +bke::CurvesGeometry fillet_curves_bezier( + const bke::CurvesGeometry &src_curves, + IndexMask curve_selection, + const VArray &radius, + bool limit_radius, + const bke::AnonymousAttributePropagationInfo &propagation_info); } // namespace blender::geometry diff --git a/source/blender/geometry/GEO_mesh_split_edges.hh b/source/blender/geometry/GEO_mesh_split_edges.hh index f104a55ae4d..cb0e3de8bd1 100644 --- a/source/blender/geometry/GEO_mesh_split_edges.hh +++ b/source/blender/geometry/GEO_mesh_split_edges.hh @@ -4,10 +4,14 @@ #include "BLI_index_mask.hh" +#include "BKE_attribute.hh" + struct Mesh; namespace blender::geometry { -void split_edges(Mesh &mesh, IndexMask mask); +void split_edges(Mesh &mesh, + IndexMask mask, + const bke::AnonymousAttributePropagationInfo &propagation_info); } // namespace blender::geometry diff --git a/source/blender/geometry/GEO_mesh_to_curve.hh b/source/blender/geometry/GEO_mesh_to_curve.hh index b0f24853dbc..17eb311ccc2 100644 --- a/source/blender/geometry/GEO_mesh_to_curve.hh +++ b/source/blender/geometry/GEO_mesh_to_curve.hh @@ -19,11 +19,16 @@ namespace blender::geometry { * intersections of more than three edges will become breaks in curves. Attributes that * are not built-in on meshes and not curves are transferred to the result curve. */ -bke::CurvesGeometry mesh_to_curve_convert(const Mesh &mesh, const IndexMask selection); +bke::CurvesGeometry mesh_to_curve_convert( + const Mesh &mesh, + const IndexMask selection, + const bke::AnonymousAttributePropagationInfo &propagation_info); -bke::CurvesGeometry create_curve_from_vert_indices(const Mesh &mesh, - Span vert_indices, - Span curve_offsets, - IndexRange cyclic_curves); +bke::CurvesGeometry create_curve_from_vert_indices( + const Mesh &mesh, + Span vert_indices, + Span curve_offsets, + IndexRange cyclic_curves, + const bke::AnonymousAttributePropagationInfo &propagation_info); } // namespace blender::geometry diff --git a/source/blender/geometry/GEO_point_merge_by_distance.hh b/source/blender/geometry/GEO_point_merge_by_distance.hh index 92d871c62ab..054ecffd358 100644 --- a/source/blender/geometry/GEO_point_merge_by_distance.hh +++ b/source/blender/geometry/GEO_point_merge_by_distance.hh @@ -2,6 +2,8 @@ #include "BLI_index_mask.hh" +#include "BKE_attribute.hh" + #pragma once struct PointCloud; @@ -17,8 +19,10 @@ namespace blender::geometry { * Merge selected points into other selected points within the \a merge_distance. The merged * indices favor speed over accuracy, since the results will depend on the order of the points. */ -PointCloud *point_merge_by_distance(const PointCloud &src_points, - const float merge_distance, - const IndexMask selection); +PointCloud *point_merge_by_distance( + const PointCloud &src_points, + const float merge_distance, + const IndexMask selection, + const bke::AnonymousAttributePropagationInfo &propagation_info); } // namespace blender::geometry diff --git a/source/blender/geometry/GEO_realize_instances.hh b/source/blender/geometry/GEO_realize_instances.hh index 9c46de6fdca..b13d764c685 100644 --- a/source/blender/geometry/GEO_realize_instances.hh +++ b/source/blender/geometry/GEO_realize_instances.hh @@ -19,6 +19,8 @@ struct RealizeInstancesOptions { * instances. Otherwise, instance attributes are ignored. */ bool realize_instance_attributes = true; + + bke::AnonymousAttributePropagationInfo propagation_info; }; /** diff --git a/source/blender/geometry/GEO_resample_curves.hh b/source/blender/geometry/GEO_resample_curves.hh index 35365167eba..fd1e6b7dad3 100644 --- a/source/blender/geometry/GEO_resample_curves.hh +++ b/source/blender/geometry/GEO_resample_curves.hh @@ -4,7 +4,7 @@ #include "FN_field.hh" -#include "BKE_anonymous_attribute.hh" +#include "BKE_anonymous_attribute_id.hh" #include "BKE_curves.hh" namespace blender::geometry { diff --git a/source/blender/geometry/GEO_set_curve_type.hh b/source/blender/geometry/GEO_set_curve_type.hh index f38e63b1fc8..3860664c1c5 100644 --- a/source/blender/geometry/GEO_set_curve_type.hh +++ b/source/blender/geometry/GEO_set_curve_type.hh @@ -27,6 +27,7 @@ bool try_curves_conversion_in_place(IndexMask selection, */ bke::CurvesGeometry convert_curves(const bke::CurvesGeometry &src_curves, IndexMask selection, - CurveType dst_type); + CurveType dst_type, + const bke::AnonymousAttributePropagationInfo &propagation_info); } // namespace blender::geometry diff --git a/source/blender/geometry/GEO_subdivide_curves.hh b/source/blender/geometry/GEO_subdivide_curves.hh index ba55118baa4..46e63839056 100644 --- a/source/blender/geometry/GEO_subdivide_curves.hh +++ b/source/blender/geometry/GEO_subdivide_curves.hh @@ -17,8 +17,10 @@ namespace blender::geometry { * * \param selection: A selection of curves to consider when subdividing. */ -bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves, - IndexMask selection, - const VArray &cuts); +bke::CurvesGeometry subdivide_curves( + const bke::CurvesGeometry &src_curves, + IndexMask selection, + const VArray &cuts, + const bke::AnonymousAttributePropagationInfo &propagation_info); } // namespace blender::geometry diff --git a/source/blender/geometry/GEO_trim_curves.hh b/source/blender/geometry/GEO_trim_curves.hh index 197ef79c25b..1cae65875e2 100644 --- a/source/blender/geometry/GEO_trim_curves.hh +++ b/source/blender/geometry/GEO_trim_curves.hh @@ -19,6 +19,7 @@ bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves, IndexMask selection, const VArray &starts, const VArray &ends, - GeometryNodeCurveSampleMode mode); + GeometryNodeCurveSampleMode mode, + const bke::AnonymousAttributePropagationInfo &propagation_info); } // namespace blender::geometry diff --git a/source/blender/geometry/intern/add_curves_on_mesh.cc b/source/blender/geometry/intern/add_curves_on_mesh.cc index 0e9b89b35b4..55f0398fd3c 100644 --- a/source/blender/geometry/intern/add_curves_on_mesh.cc +++ b/source/blender/geometry/intern/add_curves_on_mesh.cc @@ -372,7 +372,7 @@ AddCurvesOnMeshOutputs add_curves_on_mesh(CurvesGeometry &curves, Set attributes_to_skip{{"position", "curve_type", "surface_uv_coordinate"}}; attributes.for_all( [&](const bke::AttributeIDRef &id, const bke::AttributeMetaData /*meta_data*/) { - if (id.is_named() && attributes_to_skip.contains(id.name())) { + if (attributes_to_skip.contains(id.name())) { return true; } bke::GSpanAttributeWriter attribute = attributes.lookup_for_write_span(id); diff --git a/source/blender/geometry/intern/fillet_curves.cc b/source/blender/geometry/intern/fillet_curves.cc index cb513641a5f..5e4f2e35c67 100644 --- a/source/blender/geometry/intern/fillet_curves.cc +++ b/source/blender/geometry/intern/fillet_curves.cc @@ -397,12 +397,14 @@ static void calculate_bezier_handles_poly_mode(const Span src_handles_l, }); } -static bke::CurvesGeometry fillet_curves(const bke::CurvesGeometry &src_curves, - const IndexMask curve_selection, - const VArray &radius_input, - const VArray &counts, - const bool limit_radius, - const bool use_bezier_mode) +static bke::CurvesGeometry fillet_curves( + const bke::CurvesGeometry &src_curves, + const IndexMask curve_selection, + const VArray &radius_input, + const VArray &counts, + const bool limit_radius, + const bool use_bezier_mode, + const bke::AnonymousAttributePropagationInfo &propagation_info) { const Vector unselected_ranges = curve_selection.extract_ranges_invert( src_curves.curves_range()); @@ -520,6 +522,7 @@ static bke::CurvesGeometry fillet_curves(const bke::CurvesGeometry &src_curves, src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT, + propagation_info, {"position", "handle_type_left", "handle_type_right", "handle_right", "handle_left"})) { duplicate_fillet_point_data( src_curves, dst_curves, curve_selection, point_offsets, attribute.src, attribute.dst.span); @@ -528,7 +531,7 @@ static bke::CurvesGeometry fillet_curves(const bke::CurvesGeometry &src_curves, if (!unselected_ranges.is_empty()) { for (auto &attribute : bke::retrieve_attributes_for_transfer( - src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT)) { + src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT, propagation_info)) { bke::curves::copy_point_data( src_curves, dst_curves, unselected_ranges, attribute.src, attribute.dst.span); attribute.dst.finish(); @@ -538,26 +541,32 @@ static bke::CurvesGeometry fillet_curves(const bke::CurvesGeometry &src_curves, return dst_curves; } -bke::CurvesGeometry fillet_curves_poly(const bke::CurvesGeometry &src_curves, - const IndexMask curve_selection, - const VArray &radius, - const VArray &count, - const bool limit_radius) +bke::CurvesGeometry fillet_curves_poly( + const bke::CurvesGeometry &src_curves, + const IndexMask curve_selection, + const VArray &radius, + const VArray &count, + const bool limit_radius, + const bke::AnonymousAttributePropagationInfo &propagation_info) { - return fillet_curves(src_curves, curve_selection, radius, count, limit_radius, false); + return fillet_curves( + src_curves, curve_selection, radius, count, limit_radius, false, propagation_info); } -bke::CurvesGeometry fillet_curves_bezier(const bke::CurvesGeometry &src_curves, - const IndexMask curve_selection, - const VArray &radius, - const bool limit_radius) +bke::CurvesGeometry fillet_curves_bezier( + const bke::CurvesGeometry &src_curves, + const IndexMask curve_selection, + const VArray &radius, + const bool limit_radius, + const bke::AnonymousAttributePropagationInfo &propagation_info) { return fillet_curves(src_curves, curve_selection, radius, VArray::ForSingle(1, src_curves.points_num()), limit_radius, - true); + true, + propagation_info); } } // namespace blender::geometry diff --git a/source/blender/geometry/intern/mesh_split_edges.cc b/source/blender/geometry/intern/mesh_split_edges.cc index 19db9cbfc03..5cf7d4bb935 100644 --- a/source/blender/geometry/intern/mesh_split_edges.cc +++ b/source/blender/geometry/intern/mesh_split_edges.cc @@ -3,6 +3,7 @@ #include "BLI_array_utils.hh" #include "BLI_devirtualize_parameters.hh" #include "BLI_index_mask.hh" +#include "BLI_user_counter.hh" #include "BKE_attribute.hh" #include "BKE_attribute_math.hh" @@ -59,35 +60,36 @@ static void add_new_vertices(Mesh &mesh, const Span new_to_old_verts_map) static void add_new_edges(Mesh &mesh, const Span new_edges, - const Span new_to_old_edges_map) + const Span new_to_old_edges_map, + const bke::AnonymousAttributePropagationInfo &propagation_info) { bke::MutableAttributeAccessor attributes = mesh.attributes_for_write(); /* Store a copy of the IDs locally since we will remove the existing attributes which * can also free the names, since the API does not provide pointer stability. */ Vector named_ids; - Vector anonymous_ids; + Vector> anonymous_ids; for (const bke::AttributeIDRef &id : attributes.all_ids()) { if (attributes.lookup_meta_data(id)->domain != ATTR_DOMAIN_EDGE) { continue; } - if (!id.should_be_kept()) { + if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) { continue; } - if (id.is_named()) { + if (!id.is_anonymous()) { named_ids.append(id.name()); } else { - anonymous_ids.append(bke::WeakAnonymousAttributeID(&id.anonymous_id())); - BKE_anonymous_attribute_id_increment_weak(&id.anonymous_id()); + anonymous_ids.append(&id.anonymous_id()); + id.anonymous_id().user_add(); } } Vector local_edge_ids; for (const StringRef name : named_ids) { local_edge_ids.append(name); } - for (const bke::WeakAnonymousAttributeID &id : anonymous_ids) { - local_edge_ids.append(id.get()); + for (const UserCounter &id : anonymous_ids) { + local_edge_ids.append(*id); } /* Build new arrays for the copied edge attributes. Unlike vertices, new edges aren't all at the @@ -348,7 +350,9 @@ static void split_edge_per_poly(const int edge_i, edge_to_loop_map[edge_i].resize(1); } -void split_edges(Mesh &mesh, const IndexMask mask) +void split_edges(Mesh &mesh, + const IndexMask mask, + const bke::AnonymousAttributePropagationInfo &propagation_info) { /* Flag vertices that need to be split. */ Array should_split_vert(mesh.totvert, false); @@ -483,7 +487,7 @@ void split_edges(Mesh &mesh, const IndexMask mask) /* Step 5: Resize the mesh to add the new vertices and rebuild the edges. */ add_new_vertices(mesh, new_to_old_verts_map); - add_new_edges(mesh, new_edges, new_to_old_edges_map); + add_new_edges(mesh, new_edges, new_to_old_edges_map, propagation_info); BKE_mesh_tag_edges_split(&mesh); } diff --git a/source/blender/geometry/intern/mesh_to_curve_convert.cc b/source/blender/geometry/intern/mesh_to_curve_convert.cc index c2a9b16c8b6..8047f1dc312 100644 --- a/source/blender/geometry/intern/mesh_to_curve_convert.cc +++ b/source/blender/geometry/intern/mesh_to_curve_convert.cc @@ -19,10 +19,12 @@ namespace blender::geometry { -bke::CurvesGeometry create_curve_from_vert_indices(const Mesh &mesh, - const Span vert_indices, - const Span curve_offsets, - const IndexRange cyclic_curves) +bke::CurvesGeometry create_curve_from_vert_indices( + const Mesh &mesh, + const Span vert_indices, + const Span curve_offsets, + const IndexRange cyclic_curves, + const bke::AnonymousAttributePropagationInfo &propagation_info) { bke::CurvesGeometry curves(vert_indices.size(), curve_offsets.size()); curves.offsets_for_write().drop_back(1).copy_from(curve_offsets); @@ -43,7 +45,7 @@ bke::CurvesGeometry create_curve_from_vert_indices(const Mesh &mesh, continue; } - if (!attribute_id.should_be_kept()) { + if (attribute_id.is_anonymous() && !propagation_info.propagate(attribute_id.anonymous_id())) { continue; } @@ -209,14 +211,17 @@ static Vector> get_selected_edges(const Mesh &mesh, const In return selected_edges; } -bke::CurvesGeometry mesh_to_curve_convert(const Mesh &mesh, const IndexMask selection) +bke::CurvesGeometry mesh_to_curve_convert( + const Mesh &mesh, + const IndexMask selection, + const bke::AnonymousAttributePropagationInfo &propagation_info) { Vector> selected_edges = get_selected_edges(mesh, selection); const Span verts = mesh.verts(); CurveFromEdgesOutput output = edges_to_curve_point_indices(verts, selected_edges); return create_curve_from_vert_indices( - mesh, output.vert_indices, output.curve_offsets, output.cyclic_curves); + mesh, output.vert_indices, output.curve_offsets, output.cyclic_curves, propagation_info); } } // namespace blender::geometry diff --git a/source/blender/geometry/intern/point_merge_by_distance.cc b/source/blender/geometry/intern/point_merge_by_distance.cc index 81f57f785a3..3387afc7279 100644 --- a/source/blender/geometry/intern/point_merge_by_distance.cc +++ b/source/blender/geometry/intern/point_merge_by_distance.cc @@ -15,7 +15,8 @@ namespace blender::geometry { PointCloud *point_merge_by_distance(const PointCloud &src_points, const float merge_distance, - const IndexMask selection) + const IndexMask selection, + const bke::AnonymousAttributePropagationInfo &propagation_info) { const bke::AttributeAccessor src_attributes = src_points.attributes(); VArraySpan positions = src_attributes.lookup_or_default( @@ -125,7 +126,7 @@ PointCloud *point_merge_by_distance(const PointCloud &src_points, /* Transfer all other attributes. */ for (const bke::AttributeIDRef &id : attribute_ids) { - if (!id.should_be_kept()) { + if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) { continue; } diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc index 57a4ae70b5f..9729d31779c 100644 --- a/source/blender/geometry/intern/realize_instances.cc +++ b/source/blender/geometry/intern/realize_instances.cc @@ -636,8 +636,11 @@ static OrderedAttributes gather_generic_pointcloud_attributes_to_propagate( } Map attributes_to_propagate; - in_geometry_set.gather_attributes_for_propagation( - src_component_types, GEO_COMPONENT_TYPE_POINT_CLOUD, true, attributes_to_propagate); + in_geometry_set.gather_attributes_for_propagation(src_component_types, + GEO_COMPONENT_TYPE_POINT_CLOUD, + true, + options.propagation_info, + attributes_to_propagate); attributes_to_propagate.remove("position"); r_create_id = attributes_to_propagate.pop_try("id").has_value(); r_create_radii = attributes_to_propagate.pop_try("radius").has_value(); @@ -829,8 +832,11 @@ static OrderedAttributes gather_generic_mesh_attributes_to_propagate( } Map attributes_to_propagate; - in_geometry_set.gather_attributes_for_propagation( - src_component_types, GEO_COMPONENT_TYPE_MESH, true, attributes_to_propagate); + in_geometry_set.gather_attributes_for_propagation(src_component_types, + GEO_COMPONENT_TYPE_MESH, + true, + options.propagation_info, + attributes_to_propagate); attributes_to_propagate.remove("position"); attributes_to_propagate.remove("normal"); attributes_to_propagate.remove("shade_smooth"); @@ -1149,8 +1155,11 @@ static OrderedAttributes gather_generic_curve_attributes_to_propagate( } Map attributes_to_propagate; - in_geometry_set.gather_attributes_for_propagation( - src_component_types, GEO_COMPONENT_TYPE_CURVE, true, attributes_to_propagate); + in_geometry_set.gather_attributes_for_propagation(src_component_types, + GEO_COMPONENT_TYPE_CURVE, + true, + options.propagation_info, + attributes_to_propagate); attributes_to_propagate.remove("position"); attributes_to_propagate.remove("radius"); attributes_to_propagate.remove("resolution"); diff --git a/source/blender/geometry/intern/resample_curves.cc b/source/blender/geometry/intern/resample_curves.cc index c8f6e9a9226..cccf659ac5e 100644 --- a/source/blender/geometry/intern/resample_curves.cc +++ b/source/blender/geometry/intern/resample_curves.cc @@ -53,7 +53,7 @@ static fn::Field get_count_input_from_length(const fn::Field &length static bool interpolate_attribute_to_curves(const bke::AttributeIDRef &attribute_id, const std::array &type_counts) { - if (!attribute_id.is_named()) { + if (attribute_id.is_anonymous()) { return true; } if (ELEM(attribute_id.name(), @@ -81,7 +81,7 @@ static bool interpolate_attribute_to_poly_curve(const bke::AttributeIDRef &attri "handle_left", "nurbs_weight", }}; - return !(attribute_id.is_named() && no_interpolation.contains(attribute_id.name())); + return !no_interpolation.contains(attribute_id.name()); } /** diff --git a/source/blender/geometry/intern/set_curve_type.cc b/source/blender/geometry/intern/set_curve_type.cc index e069732ca9b..ab35f90b279 100644 --- a/source/blender/geometry/intern/set_curve_type.cc +++ b/source/blender/geometry/intern/set_curve_type.cc @@ -286,8 +286,10 @@ static void retrieve_curve_sizes(const bke::CurvesGeometry &curves, MutableSpan< }); } -static bke::CurvesGeometry convert_curves_to_bezier(const bke::CurvesGeometry &src_curves, - const IndexMask selection) +static bke::CurvesGeometry convert_curves_to_bezier( + const bke::CurvesGeometry &src_curves, + const IndexMask selection, + const bke::AnonymousAttributePropagationInfo &propagation_info) { const VArray src_knot_modes = src_curves.nurbs_knots_modes(); const VArray src_types = src_curves.curve_types(); @@ -315,6 +317,7 @@ static bke::CurvesGeometry convert_curves_to_bezier(const bke::CurvesGeometry &s src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT, + propagation_info, {"position", "handle_type_left", "handle_type_right", @@ -460,8 +463,10 @@ static bke::CurvesGeometry convert_curves_to_bezier(const bke::CurvesGeometry &s return dst_curves; } -static bke::CurvesGeometry convert_curves_to_nurbs(const bke::CurvesGeometry &src_curves, - const IndexMask selection) +static bke::CurvesGeometry convert_curves_to_nurbs( + const bke::CurvesGeometry &src_curves, + const IndexMask selection, + const bke::AnonymousAttributePropagationInfo &propagation_info) { const VArray src_types = src_curves.curve_types(); const VArray src_cyclic = src_curves.cyclic(); @@ -487,6 +492,7 @@ static bke::CurvesGeometry convert_curves_to_nurbs(const bke::CurvesGeometry &sr src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT, + propagation_info, {"position", "handle_type_left", "handle_type_right", @@ -639,16 +645,17 @@ static bke::CurvesGeometry convert_curves_trivial(const bke::CurvesGeometry &src bke::CurvesGeometry convert_curves(const bke::CurvesGeometry &src_curves, const IndexMask selection, - const CurveType dst_type) + const CurveType dst_type, + const bke::AnonymousAttributePropagationInfo &propagation_info) { switch (dst_type) { case CURVE_TYPE_CATMULL_ROM: case CURVE_TYPE_POLY: return convert_curves_trivial(src_curves, selection, dst_type); case CURVE_TYPE_BEZIER: - return convert_curves_to_bezier(src_curves, selection); + return convert_curves_to_bezier(src_curves, selection, propagation_info); case CURVE_TYPE_NURBS: - return convert_curves_to_nurbs(src_curves, selection); + return convert_curves_to_nurbs(src_curves, selection, propagation_info); } BLI_assert_unreachable(); return {}; diff --git a/source/blender/geometry/intern/subdivide_curves.cc b/source/blender/geometry/intern/subdivide_curves.cc index 021fa091364..e69a8398653 100644 --- a/source/blender/geometry/intern/subdivide_curves.cc +++ b/source/blender/geometry/intern/subdivide_curves.cc @@ -294,9 +294,11 @@ static void subdivide_bezier_positions(const Span src_positions, cyclic, dst_types_l, dst_types_r, dst_positions, dst_handles_l, dst_handles_r); } -bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves, - const IndexMask selection, - const VArray &cuts) +bke::CurvesGeometry subdivide_curves( + const bke::CurvesGeometry &src_curves, + const IndexMask selection, + const VArray &cuts, + const bke::AnonymousAttributePropagationInfo &propagation_info) { const Vector unselected_ranges = selection.extract_ranges_invert( src_curves.curves_range()); @@ -338,7 +340,7 @@ bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves, auto subdivide_catmull_rom = [&](IndexMask selection) { for (auto &attribute : bke::retrieve_attributes_for_transfer( - src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT)) { + src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT, propagation_info)) { subdivide_attribute_catmull_rom(src_curves, dst_curves, selection, @@ -352,7 +354,7 @@ bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves, auto subdivide_poly = [&](IndexMask selection) { for (auto &attribute : bke::retrieve_attributes_for_transfer( - src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT)) { + src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT, propagation_info)) { subdivide_attribute_linear( src_curves, dst_curves, selection, point_offsets, attribute.src, attribute.dst.span); attribute.dst.finish(); @@ -396,6 +398,7 @@ bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves, for (auto &attribute : bke::retrieve_attributes_for_transfer(src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT, + propagation_info, {"position", "handle_type_left", "handle_type_right", @@ -421,7 +424,7 @@ bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves, if (!unselected_ranges.is_empty()) { for (auto &attribute : bke::retrieve_attributes_for_transfer( - src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT)) { + src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT, propagation_info)) { bke::curves::copy_point_data( src_curves, dst_curves, unselected_ranges, attribute.src, attribute.dst.span); attribute.dst.finish(); diff --git a/source/blender/geometry/intern/trim_curves.cc b/source/blender/geometry/intern/trim_curves.cc index 630ee8b8bab..53a22b581b5 100644 --- a/source/blender/geometry/intern/trim_curves.cc +++ b/source/blender/geometry/intern/trim_curves.cc @@ -929,7 +929,8 @@ bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves, const IndexMask selection, const VArray &starts, const VArray &ends, - const GeometryNodeCurveSampleMode mode) + const GeometryNodeCurveSampleMode mode, + const bke::AnonymousAttributePropagationInfo &propagation_info) { BLI_assert(selection.size() > 0); BLI_assert(selection.last() <= src_curves.curves_num()); @@ -991,14 +992,19 @@ bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves, transfer_curve_skip.remove("nurbs_order"); transfer_curve_skip.remove("knots_mode"); } - bke::copy_attribute_domain( - src_attributes, dst_attributes, selection, ATTR_DOMAIN_CURVE, transfer_curve_skip); + bke::copy_attribute_domain(src_attributes, + dst_attributes, + selection, + ATTR_DOMAIN_CURVE, + propagation_info, + transfer_curve_skip); /* Fetch custom point domain attributes for transfer (copy). */ Vector transfer_attributes = bke::retrieve_attributes_for_transfer( src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT, + propagation_info, {"position", "handle_left", "handle_right", @@ -1063,8 +1069,12 @@ bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves, /* Copy unselected */ if (!inverse_selection.is_empty()) { transfer_curve_skip.remove("cyclic"); - bke::copy_attribute_domain( - src_attributes, dst_attributes, inverse_selection, ATTR_DOMAIN_CURVE, transfer_curve_skip); + bke::copy_attribute_domain(src_attributes, + dst_attributes, + inverse_selection, + ATTR_DOMAIN_CURVE, + propagation_info, + transfer_curve_skip); /* Trim curves are no longer cyclic. If all curves are trimmed, this will be set implicitly. */ dst_curves.cyclic_for_write().fill_indices(selection, false); @@ -1075,8 +1085,11 @@ bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves, } /* Copy point domain. */ - for (auto &attribute : bke::retrieve_attributes_for_transfer( - src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT, copy_point_skip)) { + for (auto &attribute : bke::retrieve_attributes_for_transfer(src_attributes, + dst_attributes, + ATTR_DOMAIN_MASK_POINT, + propagation_info, + copy_point_skip)) { bke::curves::copy_point_data( src_curves, dst_curves, inverse_selection, attribute.src, attribute.dst.span); attribute.dst.finish(); diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h index 3bff2f4316c..0710ed6d30f 100644 --- a/source/blender/makesdna/DNA_customdata_types.h +++ b/source/blender/makesdna/DNA_customdata_types.h @@ -15,7 +15,15 @@ extern "C" { #endif -struct AnonymousAttributeID; +/** Workaround to forward-declare C++ type in C header. */ +#ifdef __cplusplus +namespace blender::bke { +class AnonymousAttributeID; +} // namespace blender::bke +using AnonymousAttributeIDHandle = blender::bke::AnonymousAttributeID; +#else +typedef struct AnonymousAttributeIDHandle AnonymousAttributeIDHandle; +#endif /** Descriptor and storage for a custom data layer. */ typedef struct CustomDataLayer { @@ -40,12 +48,10 @@ typedef struct CustomDataLayer { /** Layer data. */ void *data; /** - * Run-time identifier for this layer. If no one has a strong reference to this id anymore, - * the layer can be removed. The custom data layer only has a weak reference to the id, because - * otherwise there will always be a strong reference and the attribute can't be removed - * automatically. + * Run-time identifier for this layer. Can be used to retrieve information about where this + * attribute was created. */ - const struct AnonymousAttributeID *anonymous_id; + const AnonymousAttributeIDHandle *anonymous_id; } CustomDataLayer; #define MAX_CUSTOMDATA_LAYER_NAME 64 diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index ebd5bf351ea..93b61ad76ab 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -884,12 +884,12 @@ static void find_side_effect_nodes_for_viewer_path( /* Not only mark the viewer node as having side effects, but also all group nodes it is contained * in. */ - r_side_effect_nodes.add(compute_context_builder.hash(), - &find_viewer_lf_node(*found_viewer_node)); + r_side_effect_nodes.add_non_duplicates(compute_context_builder.hash(), + &find_viewer_lf_node(*found_viewer_node)); compute_context_builder.pop(); while (!compute_context_builder.is_empty()) { - r_side_effect_nodes.add(compute_context_builder.hash(), - &find_group_lf_node(*group_node_stack.pop())); + r_side_effect_nodes.add_non_duplicates(compute_context_builder.hash(), + &find_group_lf_node(*group_node_stack.pop())); compute_context_builder.pop(); } } @@ -1124,12 +1124,11 @@ static GeometrySet compute_geometry( { const blender::nodes::GeometryNodeLazyFunctionGraphMapping &mapping = lf_graph_info.mapping; - Span graph_inputs = mapping.group_input_sockets; - Vector graph_outputs; - for (const bNodeSocket *bsocket : output_node.input_sockets().drop_back(1)) { - const lf::InputSocket &socket = mapping.dummy_socket_map.lookup(bsocket)->as_input(); - graph_outputs.append(&socket); - } + Vector graph_inputs = mapping.group_input_sockets; + graph_inputs.extend(mapping.group_output_used_sockets); + graph_inputs.extend(mapping.attribute_set_by_geometry_output.values().begin(), + mapping.attribute_set_by_geometry_output.values().end()); + Vector graph_outputs = mapping.standard_group_output_sockets; Array param_inputs(graph_inputs.size()); Array param_outputs(graph_outputs.size()); @@ -1166,21 +1165,36 @@ static GeometrySet compute_geometry( blender::LinearAllocator<> allocator; Vector inputs_to_destruct; - int input_index; - LISTBASE_FOREACH_INDEX (bNodeSocket *, interface_socket, &btree.inputs, input_index) { - if (interface_socket->type == SOCK_GEOMETRY && input_index == 0) { + int input_index = -1; + for (const int i : btree.interface_inputs().index_range()) { + input_index++; + const bNodeSocket &interface_socket = *btree.interface_inputs()[i]; + if (interface_socket.type == SOCK_GEOMETRY && input_index == 0) { param_inputs[input_index] = &input_geometry_set; continue; } - const CPPType *type = interface_socket->typeinfo->geometry_nodes_cpp_type; + const CPPType *type = interface_socket.typeinfo->geometry_nodes_cpp_type; BLI_assert(type != nullptr); void *value = allocator.allocate(type->size(), type->alignment()); - initialize_group_input(*nmd, *interface_socket, input_index, value); + initialize_group_input(*nmd, interface_socket, i, value); param_inputs[input_index] = {type, value}; inputs_to_destruct.append({type, value}); } + Array output_used_inputs(btree.interface_outputs().size(), true); + for (const int i : btree.interface_outputs().index_range()) { + input_index++; + param_inputs[input_index] = &output_used_inputs[i]; + } + + Array attributes_to_propagate( + mapping.attribute_set_by_geometry_output.size()); + for (const int i : attributes_to_propagate.index_range()) { + input_index++; + param_inputs[input_index] = &attributes_to_propagate[i]; + } + for (const int i : graph_outputs.index_range()) { const lf::InputSocket &socket = *graph_outputs[i]; const CPPType &type = socket.type(); diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh index 73e82f741ab..60f58f4c215 100644 --- a/source/blender/nodes/NOD_geometry_exec.hh +++ b/source/blender/nodes/NOD_geometry_exec.hh @@ -19,6 +19,8 @@ struct ModifierData; namespace blender::nodes { using bke::AnonymousAttributeFieldInput; +using bke::AnonymousAttributeID; +using bke::AnonymousAttributePropagationInfo; using bke::AttributeAccessor; using bke::AttributeFieldInput; using bke::AttributeIDRef; @@ -26,13 +28,12 @@ using bke::AttributeKind; using bke::AttributeMetaData; using bke::AttributeReader; using bke::AttributeWriter; +using bke::AutoAnonymousAttributeID; using bke::GAttributeReader; using bke::GAttributeWriter; using bke::GSpanAttributeWriter; using bke::MutableAttributeAccessor; using bke::SpanAttributeWriter; -using bke::StrongAnonymousAttributeID; -using bke::WeakAnonymousAttributeID; using fn::Field; using fn::FieldContext; using fn::FieldEvaluator; @@ -43,15 +44,38 @@ using fn::ValueOrField; using geo_eval_log::NamedAttributeUsage; using geo_eval_log::NodeWarningType; +/** + * An anonymous attribute created by a node. + */ +class NodeAnonymousAttributeID : public AnonymousAttributeID { + std::string long_name_; + + public: + NodeAnonymousAttributeID(const Object &object, + const ComputeContext &compute_context, + const bNode &bnode, + const StringRef identifier); +}; + class GeoNodeExecParams { private: const bNode &node_; lf::Params ¶ms_; const lf::Context &lf_context_; + const Map &lf_input_for_output_bsocket_usage_; + const Map &lf_input_for_attribute_propagation_to_output_; public: - GeoNodeExecParams(const bNode &node, lf::Params ¶ms, const lf::Context &lf_context) - : node_(node), params_(params), lf_context_(lf_context) + GeoNodeExecParams(const bNode &node, + lf::Params ¶ms, + const lf::Context &lf_context, + const Map &lf_input_for_output_bsocket_usage, + const Map &lf_input_for_attribute_propagation_to_output) + : node_(node), + params_(params), + lf_context_(lf_context), + lf_input_for_output_bsocket_usage_(lf_input_for_output_bsocket_usage), + lf_input_for_attribute_propagation_to_output_(lf_input_for_attribute_propagation_to_output) { } @@ -245,6 +269,49 @@ class GeoNodeExecParams { void used_named_attribute(StringRef attribute_name, NamedAttributeUsage usage); + /** + * Return true when the anonymous attribute referenced by the given output should be created. + */ + bool anonymous_attribute_output_is_required(const StringRef output_identifier) + { + const int lf_index = lf_input_for_output_bsocket_usage_.lookup(output_identifier); + return params_.get_input(lf_index); + } + + /** + * Return a new anonymous attribute id for the given output. None is returned if the anonymous + * attribute is not needed. + */ + AutoAnonymousAttributeID get_output_anonymous_attribute_id_if_needed( + const StringRef output_identifier) + { + if (!this->anonymous_attribute_output_is_required(output_identifier)) { + return {}; + } + const GeoNodesLFUserData &user_data = *this->user_data(); + const ComputeContext &compute_context = *user_data.compute_context; + return MEM_new(__func__, + *user_data.modifier_data->self_object, + compute_context, + node_, + output_identifier); + } + + /** + * Get information about which anonymous attributes should be propagated to the given output. + */ + AnonymousAttributePropagationInfo get_output_propagation_info( + const StringRef output_identifier) const + { + const int lf_index = lf_input_for_attribute_propagation_to_output_.lookup(output_identifier); + const bke::AnonymousAttributeSet &set = params_.get_input( + lf_index); + AnonymousAttributePropagationInfo info; + info.names = set.names; + info.propagate_all = false; + return info; + } + private: /* Utilities for detecting common errors at when using this class. */ void check_input_access(StringRef identifier, const CPPType *requested_type = nullptr) const; diff --git a/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh b/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh index 84c264f4976..7f49d067061 100644 --- a/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh +++ b/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh @@ -78,6 +78,28 @@ struct GeoNodesLFUserData : public lf::UserData { bool log_socket_values = true; }; +/** + * In the general case, this is #DynamicSocket. That means that to determine if a node group will + * use a particular input, it has to be partially executed. + * + * In other cases, it's not necessary to look into the node group to determine if an input is + * necessary. + */ +enum class InputUsageHintType { + /** The input socket is never used. */ + Never, + /** The input socket is used when a subset of the outputs is used. */ + DependsOnOutput, + /** Can't determine statically if the input is used, check the corresponding output socket. */ + DynamicSocket, +}; + +struct InputUsageHint { + InputUsageHintType type = InputUsageHintType::DependsOnOutput; + /** Used in depends-on-output mode. */ + Vector output_dependencies; +}; + /** * Contains the mapping between the #bNodeTree and the corresponding lazy-function graph. * This is *not* a one-to-one mapping. @@ -91,7 +113,32 @@ struct GeometryNodeLazyFunctionGraphMapping { * The inputs sockets in the graph. Multiple group input nodes are combined into one in the * lazy-function graph. */ - Vector group_input_sockets; + Vector group_input_sockets; + /** + * Dummy output sockets that correspond to the active group output node. If there is no such + * node, defaulted fallback outputs are created. + */ + Vector standard_group_output_sockets; + /** + * Dummy boolean sockets that have to be passed in from the outside and indicate whether a + * specific output will be used. + */ + Vector group_output_used_sockets; + /** + * Dummy boolean sockets that can be used as group output that indicate whether a specific input + * will be used (this may depend on the used outputs as well as other inputs). + */ + Vector group_input_usage_sockets; + /** + * This is an optimization to avoid partially evaluating a node group just to figure out which + * inputs are needed. + */ + Vector group_input_usage_hints; + /** + * If the node group propagates attributes from an input geometry to the output, it has to know + * which attributes should be propagated and which can be removed (for optimization purposes). + */ + Map attribute_set_by_geometry_output; /** * A mapping used for logging intermediate values. */ diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index d99b4c01ed7..ce6b4cd6cfe 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -50,10 +50,10 @@ Mesh *create_grid_mesh( int verts_x, int verts_y, float size_x, float size_y, const AttributeIDRef &uv_map_id); struct ConeAttributeOutputs { - StrongAnonymousAttributeID top_id; - StrongAnonymousAttributeID bottom_id; - StrongAnonymousAttributeID side_id; - StrongAnonymousAttributeID uv_map_id; + AutoAnonymousAttributeID top_id; + AutoAnonymousAttributeID bottom_id; + AutoAnonymousAttributeID side_id; + AutoAnonymousAttributeID uv_map_id; }; Mesh *create_cylinder_or_cone_mesh(float radius_top, @@ -81,6 +81,7 @@ void separate_geometry(GeometrySet &geometry_set, eAttrDomain domain, GeometryNodeDeleteGeometryMode mode, const Field &selection_field, + const AnonymousAttributePropagationInfo &propagation_info, bool &r_is_error); void get_closest_in_bvhtree(BVHTreeFromMesh &tree_data, diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc index 826f08e4c8d..a07cd1437d6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc @@ -142,9 +142,12 @@ static void node_geo_exec(GeoNodeExecParams params) const eAttrDomain domain = eAttrDomain(storage.domain); const std::string output_identifier = "Attribute" + identifier_suffix(data_type); + AutoAnonymousAttributeID attribute_id = params.get_output_anonymous_attribute_id_if_needed( + output_identifier); - if (!params.output_is_required(output_identifier)) { + if (!attribute_id) { params.set_output("Geometry", geometry_set); + params.set_default_remaining_outputs(); return; } @@ -171,7 +174,6 @@ static void node_geo_exec(GeoNodeExecParams params) break; } - WeakAnonymousAttributeID anonymous_id{"Attribute"}; const CPPType &type = field.cpp_type(); /* Run on the instances component separately to only affect the top level of instances. */ @@ -179,7 +181,7 @@ static void node_geo_exec(GeoNodeExecParams params) if (geometry_set.has_instances()) { GeometryComponent &component = geometry_set.get_component_for_write( GEO_COMPONENT_TYPE_INSTANCES); - bke::try_capture_field_on_geometry(component, anonymous_id.get(), domain, field); + bke::try_capture_field_on_geometry(component, *attribute_id, domain, field); } } else { @@ -190,14 +192,14 @@ static void node_geo_exec(GeoNodeExecParams params) for (const GeometryComponentType type : types) { if (geometry_set.has(type)) { GeometryComponent &component = geometry_set.get_component_for_write(type); - bke::try_capture_field_on_geometry(component, anonymous_id.get(), domain, field); + bke::try_capture_field_on_geometry(component, *attribute_id, domain, field); } } }); } GField output_field{std::make_shared( - std::move(anonymous_id), type, params.attribute_producer_name())}; + std::move(attribute_id), type, params.attribute_producer_name())}; switch (data_type) { case CD_PROP_FLOAT: { diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc index a1c4af79851..0135567c5e4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc @@ -30,7 +30,7 @@ static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) } struct AttributeOutputs { - StrongAnonymousAttributeID intersecting_edges_id; + AutoAnonymousAttributeID intersecting_edges_id; }; static void node_update(bNodeTree *ntree, bNode *node) @@ -125,9 +125,8 @@ static void node_geo_exec(GeoNodeExecParams params) } AttributeOutputs attribute_outputs; - if (params.output_is_required("Intersecting Edges")) { - attribute_outputs.intersecting_edges_id = StrongAnonymousAttributeID("Intersecting Edges"); - } + attribute_outputs.intersecting_edges_id = params.get_output_anonymous_attribute_id_if_needed( + "Intersecting Edges"); Vector intersecting_edges; Mesh *result = blender::meshintersect::direct_mesh_boolean( diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc index ef839bf2acb..627abcab861 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc @@ -67,6 +67,9 @@ static void node_geo_exec(GeoNodeExecParams params) count_field.emplace(params.extract_input>("Count")); } + const AnonymousAttributePropagationInfo &propagation_info = params.get_output_propagation_info( + "Curve"); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { if (!geometry_set.has_curves()) { return; @@ -82,7 +85,11 @@ static void node_geo_exec(GeoNodeExecParams params) case GEO_NODE_CURVE_FILLET_BEZIER: { evaluator.evaluate(); bke::CurvesGeometry dst_curves = geometry::fillet_curves_bezier( - curves, curves.curves_range(), evaluator.get_evaluated(0), limit_radius); + curves, + curves.curves_range(), + evaluator.get_evaluated(0), + limit_radius, + propagation_info); Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves)); bke::curves_copy_parameters(curves_id, *dst_curves_id); geometry_set.replace_curves(dst_curves_id); @@ -96,7 +103,8 @@ static void node_geo_exec(GeoNodeExecParams params) curves.curves_range(), evaluator.get_evaluated(0), evaluator.get_evaluated(1), - limit_radius); + limit_radius, + propagation_info); Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves)); bke::curves_copy_parameters(curves_id, *dst_curves_id); geometry_set.replace_curves(dst_curves_id); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc index 13353553379..873b66688c5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc @@ -59,10 +59,10 @@ static Curves *create_star_curve(const float inner_radius, } static void create_selection_output(CurveComponent &component, - StrongAnonymousAttributeID &r_attribute) + AutoAnonymousAttributeID &r_attribute) { SpanAttributeWriter selection = - component.attributes_for_write()->lookup_or_add_for_write_only_span(r_attribute.get(), + component.attributes_for_write()->lookup_or_add_for_write_only_span(*r_attribute, ATTR_DOMAIN_POINT); for (int i : selection.span.index_range()) { selection.span[i] = i % 2 == 0; @@ -78,12 +78,12 @@ static void node_geo_exec(GeoNodeExecParams params) std::max(params.extract_input("Points"), 3)); GeometrySet output = GeometrySet::create_with_curves(curves); - if (params.output_is_required("Outer Points")) { - StrongAnonymousAttributeID attribute_output("Outer Points"); - create_selection_output(output.get_component_for_write(), attribute_output); + if (AutoAnonymousAttributeID outer_points_id = + params.get_output_anonymous_attribute_id_if_needed("Outer Points")) { + create_selection_output(output.get_component_for_write(), outer_points_id); params.set_output("Outer Points", AnonymousAttributeFieldInput::Create( - std::move(attribute_output), params.attribute_producer_name())); + std::move(outer_points_id), params.attribute_producer_name())); } params.set_output("Curve", std::move(output)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc index 3d8d14473db..64a9ae08c9b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc @@ -67,7 +67,8 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - bke::CurvesGeometry dst_curves = geometry::convert_curves(src_curves, selection, dst_type); + bke::CurvesGeometry dst_curves = geometry::convert_curves( + src_curves, selection, dst_type, params.get_output_propagation_info("Curve")); Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves)); bke::curves_copy_parameters(src_curves_id, *dst_curves_id); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc index b01dd2e5361..479fdef56e7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -49,7 +49,7 @@ static void node_geo_exec(GeoNodeExecParams params) } bke::CurvesGeometry dst_curves = geometry::subdivide_curves( - src_curves, src_curves.curves_range(), cuts); + src_curves, src_curves.curves_range(), cuts, params.get_output_propagation_info("Curve")); Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves)); bke::curves_copy_parameters(src_curves_id, *dst_curves_id); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc index 057496144d5..0b46fe569d1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc @@ -25,7 +25,8 @@ static void node_declare(NodeDeclarationBuilder &b) static void geometry_set_curve_to_mesh(GeometrySet &geometry_set, const GeometrySet &profile_set, - const bool fill_caps) + const bool fill_caps, + const AnonymousAttributePropagationInfo &propagation_info) { const Curves &curves = *geometry_set.get_curves_for_read(); const Curves *profile_curves = profile_set.get_curves_for_read(); @@ -33,13 +34,15 @@ static void geometry_set_curve_to_mesh(GeometrySet &geometry_set, GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set); if (profile_curves == nullptr) { - Mesh *mesh = bke::curve_to_wire_mesh(bke::CurvesGeometry::wrap(curves.geometry)); + Mesh *mesh = bke::curve_to_wire_mesh(bke::CurvesGeometry::wrap(curves.geometry), + propagation_info); geometry_set.replace_mesh(mesh); } else { Mesh *mesh = bke::curve_to_mesh_sweep(bke::CurvesGeometry::wrap(curves.geometry), bke::CurvesGeometry::wrap(profile_curves->geometry), - fill_caps); + fill_caps, + propagation_info); geometry_set.replace_mesh(mesh); } } @@ -52,7 +55,8 @@ static void node_geo_exec(GeoNodeExecParams params) curve_set.modify_geometry_sets([&](GeometrySet &geometry_set) { if (geometry_set.has_curves()) { - geometry_set_curve_to_mesh(geometry_set, profile_set, fill_caps); + geometry_set_curve_to_mesh( + geometry_set, profile_set, fill_caps, params.get_output_propagation_info("Mesh")); } geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH}); }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc index 4442dfa0fdb..6e443f1efb7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc @@ -111,19 +111,12 @@ static void node_geo_exec(GeoNodeExecParams params) GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set); - StrongAnonymousAttributeID tangent_anonymous_id; - StrongAnonymousAttributeID normal_anonymous_id; - StrongAnonymousAttributeID rotation_anonymous_id; - const bool rotation_required = params.output_is_required("Rotation"); - if (params.output_is_required("Tangent") || rotation_required) { - tangent_anonymous_id = StrongAnonymousAttributeID("Tangent"); - } - if (params.output_is_required("Normal") || rotation_required) { - normal_anonymous_id = StrongAnonymousAttributeID("Normal"); - } - if (rotation_required) { - rotation_anonymous_id = StrongAnonymousAttributeID("Rotation"); - } + AutoAnonymousAttributeID tangent_anonymous_id = + params.get_output_anonymous_attribute_id_if_needed("Tangent"); + AutoAnonymousAttributeID normal_anonymous_id = + params.get_output_anonymous_attribute_id_if_needed("Normal"); + AutoAnonymousAttributeID rotation_anonymous_id = + params.get_output_anonymous_attribute_id_if_needed("Rotation"); geometry::ResampleCurvesOutputAttributeIDs resample_attributes; resample_attributes.tangent_id = tangent_anonymous_id.get(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index 97b72838b39..56912931571 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -112,7 +112,8 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set, const GeometryNodeCurveSampleMode mode, Field &selection_field, Field &start_field, - Field &end_field) + Field &end_field, + const AnonymousAttributePropagationInfo &propagation_info) { if (!geometry_set.has_curves()) { return; @@ -139,7 +140,7 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set, } bke::CurvesGeometry dst_curves = geometry::trim_curves( - src_curves, selection, starts, ends, mode); + src_curves, selection, starts, ends, mode, propagation_info); Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves)); bke::curves_copy_parameters(src_curves_id, *dst_curves_id); geometry_set.replace_curves(dst_curves_id); @@ -153,19 +154,24 @@ static void node_geo_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input("Curve"); GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set); + const AnonymousAttributePropagationInfo &propagation_info = params.get_output_propagation_info( + "Curve"); + Field selection_field = params.extract_input>("Selection"); if (mode == GEO_NODE_CURVE_SAMPLE_FACTOR) { Field start_field = params.extract_input>("Start"); Field end_field = params.extract_input>("End"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - geometry_set_curve_trim(geometry_set, mode, selection_field, start_field, end_field); + geometry_set_curve_trim( + geometry_set, mode, selection_field, start_field, end_field, propagation_info); }); } else if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { Field start_field = params.extract_input>("Start_001"); Field end_field = params.extract_input>("End_001"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - geometry_set_curve_trim(geometry_set, mode, selection_field, start_field, end_field); + geometry_set_curve_trim( + geometry_set, mode, selection_field, start_field, end_field, propagation_info); }); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc index 92937482116..e92fe1a613d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -306,7 +306,8 @@ static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, static void delete_curves_selection(GeometrySet &geometry_set, const Field &selection_field, - const eAttrDomain selection_domain) + const eAttrDomain selection_domain, + const bke::AnonymousAttributePropagationInfo &propagation_info) { const Curves &src_curves_id = *geometry_set.get_curves_for_read(); const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry); @@ -330,15 +331,17 @@ static void delete_curves_selection(GeometrySet &geometry_set, bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); if (selection_domain == ATTR_DOMAIN_POINT) { - curves.remove_points(selection); + curves.remove_points(selection, propagation_info); } else if (selection_domain == ATTR_DOMAIN_CURVE) { - curves.remove_curves(selection); + curves.remove_curves(selection, propagation_info); } } -static void separate_point_cloud_selection(GeometrySet &geometry_set, - const Field &selection_field) +static void separate_point_cloud_selection( + GeometrySet &geometry_set, + const Field &selection_field, + const AnonymousAttributePropagationInfo &propagation_info) { const PointCloud &src_pointcloud = *geometry_set.get_pointcloud_for_read(); @@ -355,8 +358,11 @@ static void separate_point_cloud_selection(GeometrySet &geometry_set, PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size()); Map attributes; - geometry_set.gather_attributes_for_propagation( - {GEO_COMPONENT_TYPE_POINT_CLOUD}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes); + geometry_set.gather_attributes_for_propagation({GEO_COMPONENT_TYPE_POINT_CLOUD}, + GEO_COMPONENT_TYPE_POINT_CLOUD, + false, + propagation_info, + attributes); copy_attributes_based_on_mask(attributes, src_pointcloud.attributes(), @@ -367,7 +373,8 @@ static void separate_point_cloud_selection(GeometrySet &geometry_set, } static void delete_selected_instances(GeometrySet &geometry_set, - const Field &selection_field) + const Field &selection_field, + const AnonymousAttributePropagationInfo &propagation_info) { bke::Instances &instances = *geometry_set.get_instances_for_write(); bke::InstancesFieldContext field_context{instances}; @@ -381,7 +388,7 @@ static void delete_selected_instances(GeometrySet &geometry_set, return; } - instances.remove(selection); + instances.remove(selection, propagation_info); } static void compute_selected_verts_from_vertex_selection(const Span vertex_selection, @@ -819,7 +826,8 @@ static void do_mesh_separation(GeometrySet &geometry_set, const Mesh &mesh_in, const Span selection, const eAttrDomain domain, - const GeometryNodeDeleteGeometryMode mode) + const GeometryNodeDeleteGeometryMode mode, + const AnonymousAttributePropagationInfo &propagation_info) { /* Needed in all cases. */ Vector selected_poly_indices; @@ -831,7 +839,7 @@ static void do_mesh_separation(GeometrySet &geometry_set, Map attributes; geometry_set.gather_attributes_for_propagation( - {GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_MESH, false, attributes); + {GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_MESH, false, propagation_info, attributes); switch (mode) { case GEO_NODE_DELETE_GEOMETRY_MODE_ALL: { @@ -1059,7 +1067,8 @@ static void do_mesh_separation(GeometrySet &geometry_set, static void separate_mesh_selection(GeometrySet &geometry_set, const Field &selection_field, const eAttrDomain selection_domain, - const GeometryNodeDeleteGeometryMode mode) + const GeometryNodeDeleteGeometryMode mode, + const AnonymousAttributePropagationInfo &propagation_info) { const Mesh &src_mesh = *geometry_set.get_mesh_for_read(); bke::MeshFieldContext field_context{src_mesh, selection_domain}; @@ -1074,7 +1083,8 @@ static void separate_mesh_selection(GeometrySet &geometry_set, const VArraySpan selection_span{selection}; - do_mesh_separation(geometry_set, src_mesh, selection_span, selection_domain, mode); + do_mesh_separation( + geometry_set, src_mesh, selection_span, selection_domain, mode, propagation_info); } } // namespace blender::nodes::node_geo_delete_geometry_cc @@ -1085,6 +1095,7 @@ void separate_geometry(GeometrySet &geometry_set, const eAttrDomain domain, const GeometryNodeDeleteGeometryMode mode, const Field &selection_field, + const AnonymousAttributePropagationInfo &propagation_info, bool &r_is_error) { namespace file_ns = blender::nodes::node_geo_delete_geometry_cc; @@ -1092,26 +1103,27 @@ void separate_geometry(GeometrySet &geometry_set, bool some_valid_domain = false; if (geometry_set.has_pointcloud()) { if (domain == ATTR_DOMAIN_POINT) { - file_ns::separate_point_cloud_selection(geometry_set, selection_field); + file_ns::separate_point_cloud_selection(geometry_set, selection_field, propagation_info); some_valid_domain = true; } } if (geometry_set.has_mesh()) { if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE, ATTR_DOMAIN_FACE, ATTR_DOMAIN_CORNER)) { - file_ns::separate_mesh_selection(geometry_set, selection_field, domain, mode); + file_ns::separate_mesh_selection( + geometry_set, selection_field, domain, mode, propagation_info); some_valid_domain = true; } } if (geometry_set.has_curves()) { if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) { file_ns::delete_curves_selection( - geometry_set, fn::invert_boolean_field(selection_field), domain); + geometry_set, fn::invert_boolean_field(selection_field), domain, propagation_info); some_valid_domain = true; } } if (geometry_set.has_instances()) { if (domain == ATTR_DOMAIN_INSTANCE) { - file_ns::delete_selected_instances(geometry_set, selection_field); + file_ns::delete_selected_instances(geometry_set, selection_field, propagation_info); some_valid_domain = true; } } @@ -1171,15 +1183,18 @@ static void node_geo_exec(GeoNodeExecParams params) const eAttrDomain domain = eAttrDomain(storage.domain); const GeometryNodeDeleteGeometryMode mode = (GeometryNodeDeleteGeometryMode)storage.mode; + const AnonymousAttributePropagationInfo &propagation_info = params.get_output_propagation_info( + "Geometry"); + if (domain == ATTR_DOMAIN_INSTANCE) { bool is_error; - separate_geometry(geometry_set, domain, mode, selection, is_error); + separate_geometry(geometry_set, domain, mode, selection, propagation_info, is_error); } else { geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { bool is_error; /* Invert here because we want to keep the things not in the selection. */ - separate_geometry(geometry_set, domain, mode, selection, is_error); + separate_geometry(geometry_set, domain, mode, selection, propagation_info, is_error); }); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc index a5bdc989a04..91fa215d117 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc @@ -320,8 +320,8 @@ BLI_NOINLINE static void propagate_existing_attributes( namespace { struct AttributeOutputs { - StrongAnonymousAttributeID normal_id; - StrongAnonymousAttributeID rotation_id; + AutoAnonymousAttributeID normal_id; + AutoAnonymousAttributeID rotation_id; }; } // namespace @@ -496,8 +496,11 @@ static void point_distribution_calculate(GeometrySet &geometry_set, geometry_set.replace_pointcloud(pointcloud); Map attributes; - geometry_set.gather_attributes_for_propagation( - {GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes); + geometry_set.gather_attributes_for_propagation({GEO_COMPONENT_TYPE_MESH}, + GEO_COMPONENT_TYPE_POINT_CLOUD, + false, + params.get_output_propagation_info("Points"), + attributes); /* Position is set separately. */ attributes.remove("position"); @@ -518,12 +521,8 @@ static void node_geo_exec(GeoNodeExecParams params) const Field selection_field = params.extract_input>("Selection"); AttributeOutputs attribute_outputs; - if (params.output_is_required("Normal")) { - attribute_outputs.normal_id = StrongAnonymousAttributeID("Normal"); - } - if (params.output_is_required("Rotation")) { - attribute_outputs.rotation_id = StrongAnonymousAttributeID("Rotation"); - } + attribute_outputs.normal_id = params.get_output_anonymous_attribute_id_if_needed("Normal"); + attribute_outputs.rotation_id = params.get_output_anonymous_attribute_id_if_needed("Rotation"); lazy_threading::send_hint(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc index bb48db48559..b076f0b7261 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc @@ -132,6 +132,7 @@ static void transfer_attributes( const Span new_to_old_edges_map, const Span new_to_old_face_corners_map, const Span> boundary_vertex_to_relevant_face_map, + const AnonymousAttributePropagationInfo &propagation_info, const AttributeAccessor src_attributes, MutableAttributeAccessor dst_attributes) { @@ -139,7 +140,9 @@ static void transfer_attributes( * Remove anonymous attributes that don't need to be propagated. */ Set attribute_ids = src_attributes.all_ids(); attribute_ids.remove("position"); - attribute_ids.remove_if([](const AttributeIDRef &id) { return !id.should_be_kept(); }); + attribute_ids.remove_if([&](const AttributeIDRef &id) { + return id.is_anonymous() && !propagation_info.propagate(id.anonymous_id()); + }); for (const AttributeIDRef &id : attribute_ids) { GAttributeReader src_attribute = src_attributes.lookup(id); @@ -606,7 +609,9 @@ static void dissolve_redundant_verts(const Span edges, * * Some special cases are needed for boundaries and non-manifold geometry. */ -static Mesh *calc_dual_mesh(const Mesh &src_mesh, const bool keep_boundaries) +static Mesh *calc_dual_mesh(const Mesh &src_mesh, + const bool keep_boundaries, + const AnonymousAttributePropagationInfo &propagation_info) { const Span src_verts = src_mesh.verts(); const Span src_edges = src_mesh.edges(); @@ -887,6 +892,7 @@ static Mesh *calc_dual_mesh(const Mesh &src_mesh, const bool keep_boundaries) new_to_old_edges_map, new_to_old_face_corners_map, boundary_vertex_to_relevant_face_map, + propagation_info, src_mesh.attributes(), mesh_out->attributes_for_write()); @@ -918,7 +924,8 @@ static void node_geo_exec(GeoNodeExecParams params) const bool keep_boundaries = params.extract_input("Keep Boundaries"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { if (const Mesh *mesh = geometry_set.get_mesh_for_read()) { - Mesh *new_mesh = calc_dual_mesh(*mesh, keep_boundaries); + Mesh *new_mesh = calc_dual_mesh( + *mesh, keep_boundaries, params.get_output_propagation_info("Dual Mesh")); geometry_set.replace_mesh(new_mesh); } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc index a53f92e3b9f..cd191fa8498 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc @@ -56,7 +56,7 @@ static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) } struct IndexAttributes { - StrongAnonymousAttributeID duplicate_index; + AutoAnonymousAttributeID duplicate_index; }; /* -------------------------------------------------------------------- */ @@ -64,11 +64,13 @@ struct IndexAttributes { * \{ */ static Map gather_attributes_without_id( - const GeometrySet &geometry_set, const GeometryComponentType component_type) + const GeometrySet &geometry_set, + const GeometryComponentType component_type, + const AnonymousAttributePropagationInfo &propagation_info) { Map attributes; geometry_set.gather_attributes_for_propagation( - {component_type}, component_type, false, attributes); + {component_type}, component_type, false, propagation_info, attributes); attributes.remove("id"); return attributes; }; @@ -181,11 +183,12 @@ static void copy_attributes_without_id(GeometrySet &geometry_set, const eAttrDomain domain, const Span offsets, const IndexMask selection, + const AnonymousAttributePropagationInfo &propagation_info, const bke::AttributeAccessor src_attributes, bke::MutableAttributeAccessor dst_attributes) { const Map attributes = gather_attributes_without_id( - geometry_set, component_type); + geometry_set, component_type, propagation_info); for (const Map::Item entry : attributes.items()) { const AttributeIDRef attribute_id = entry.key; @@ -221,14 +224,16 @@ static void copy_attributes_without_id(GeometrySet &geometry_set, * Copies the attributes for curve duplicates. If copying the curve domain, the attributes are * copied with an offset fill, otherwise a mapping is used. */ -static void copy_curve_attributes_without_id(const GeometrySet &geometry_set, - const bke::CurvesGeometry &src_curves, - const IndexMask selection, - const Span curve_offsets, - bke::CurvesGeometry &dst_curves) +static void copy_curve_attributes_without_id( + const GeometrySet &geometry_set, + const bke::CurvesGeometry &src_curves, + const IndexMask selection, + const Span curve_offsets, + const AnonymousAttributePropagationInfo &propagation_info, + bke::CurvesGeometry &dst_curves) { Map attributes = gather_attributes_without_id( - geometry_set, GEO_COMPONENT_TYPE_CURVE); + geometry_set, GEO_COMPONENT_TYPE_CURVE, propagation_info); for (const Map::Item entry : attributes.items()) { const AttributeIDRef attribute_id = entry.key; @@ -318,7 +323,8 @@ static void copy_stable_id_curves(const bke::CurvesGeometry &src_curves, static void duplicate_curves(GeometrySet &geometry_set, const Field &count_field, const Field &selection_field, - const IndexAttributes &attribute_outputs) + const IndexAttributes &attribute_outputs, + const AnonymousAttributePropagationInfo &propagation_info) { if (!geometry_set.has_curves()) { geometry_set.remove_geometry_during_modify(); @@ -373,7 +379,8 @@ static void duplicate_curves(GeometrySet &geometry_set, }); all_dst_offsets.last() = dst_points_num; - copy_curve_attributes_without_id(geometry_set, curves, selection, curve_offsets, new_curves); + copy_curve_attributes_without_id( + geometry_set, curves, selection, curve_offsets, propagation_info, new_curves); copy_stable_id_curves(curves, selection, curve_offsets, new_curves); @@ -398,17 +405,19 @@ static void duplicate_curves(GeometrySet &geometry_set, * Copies the attributes for face duplicates. If copying the face domain, the attributes are * copied with an offset fill, otherwise a mapping is used. */ -static void copy_face_attributes_without_id(GeometrySet &geometry_set, - const Span edge_mapping, - const Span vert_mapping, - const Span loop_mapping, - const Span offsets, - const IndexMask selection, - const bke::AttributeAccessor src_attributes, - bke::MutableAttributeAccessor dst_attributes) +static void copy_face_attributes_without_id( + GeometrySet &geometry_set, + const Span edge_mapping, + const Span vert_mapping, + const Span loop_mapping, + const Span offsets, + const IndexMask selection, + const AnonymousAttributePropagationInfo &propagation_info, + const bke::AttributeAccessor src_attributes, + bke::MutableAttributeAccessor dst_attributes) { Map attributes = gather_attributes_without_id( - geometry_set, GEO_COMPONENT_TYPE_MESH); + geometry_set, GEO_COMPONENT_TYPE_MESH, propagation_info); for (const Map::Item entry : attributes.items()) { const AttributeIDRef attribute_id = entry.key; @@ -506,7 +515,8 @@ static void copy_stable_id_faces(const Mesh &mesh, static void duplicate_faces(GeometrySet &geometry_set, const Field &count_field, const Field &selection_field, - const IndexAttributes &attribute_outputs) + const IndexAttributes &attribute_outputs, + const AnonymousAttributePropagationInfo &propagation_info) { if (!geometry_set.has_mesh()) { geometry_set.remove_geometry_during_modify(); @@ -588,6 +598,7 @@ static void duplicate_faces(GeometrySet &geometry_set, loop_mapping, offsets, selection, + propagation_info, mesh.attributes(), new_mesh->attributes_for_write()); @@ -612,15 +623,17 @@ static void duplicate_faces(GeometrySet &geometry_set, * Copies the attributes for edge duplicates. If copying the edge domain, the attributes are * copied with an offset fill, for point domain a mapping is used. */ -static void copy_edge_attributes_without_id(GeometrySet &geometry_set, - const Span point_mapping, - const Span offsets, - const IndexMask selection, - const bke::AttributeAccessor src_attributes, - bke::MutableAttributeAccessor dst_attributes) +static void copy_edge_attributes_without_id( + GeometrySet &geometry_set, + const Span point_mapping, + const Span offsets, + const IndexMask selection, + const AnonymousAttributePropagationInfo &propagation_info, + const bke::AttributeAccessor src_attributes, + bke::MutableAttributeAccessor dst_attributes) { Map attributes = gather_attributes_without_id( - geometry_set, GEO_COMPONENT_TYPE_MESH); + geometry_set, GEO_COMPONENT_TYPE_MESH, propagation_info); for (const Map::Item entry : attributes.items()) { const AttributeIDRef attribute_id = entry.key; @@ -704,7 +717,8 @@ static void copy_stable_id_edges(const Mesh &mesh, static void duplicate_edges(GeometrySet &geometry_set, const Field &count_field, const Field &selection_field, - const IndexAttributes &attribute_outputs) + const IndexAttributes &attribute_outputs, + const AnonymousAttributePropagationInfo &propagation_info) { if (!geometry_set.has_mesh()) { geometry_set.remove_geometry_during_modify(); @@ -756,6 +770,7 @@ static void duplicate_edges(GeometrySet &geometry_set, vert_orig_indices, edge_offsets, selection, + propagation_info, mesh.attributes(), new_mesh->attributes_for_write()); @@ -782,7 +797,8 @@ static void duplicate_edges(GeometrySet &geometry_set, static void duplicate_points_curve(GeometrySet &geometry_set, const Field &count_field, const Field &selection_field, - const IndexAttributes &attribute_outputs) + const IndexAttributes &attribute_outputs, + const AnonymousAttributePropagationInfo &propagation_info) { const Curves &src_curves_id = *geometry_set.get_curves_for_read(); const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry); @@ -819,7 +835,7 @@ static void duplicate_points_curve(GeometrySet &geometry_set, new_curve_offsets.last() = dst_num; Map attributes = gather_attributes_without_id( - geometry_set, GEO_COMPONENT_TYPE_CURVE); + geometry_set, GEO_COMPONENT_TYPE_CURVE, propagation_info); for (const Map::Item entry : attributes.items()) { const AttributeIDRef attribute_id = entry.key; @@ -885,7 +901,8 @@ static void duplicate_points_curve(GeometrySet &geometry_set, static void duplicate_points_mesh(GeometrySet &geometry_set, const Field &count_field, const Field &selection_field, - const IndexAttributes &attribute_outputs) + const IndexAttributes &attribute_outputs, + const AnonymousAttributePropagationInfo &propagation_info) { const Mesh &mesh = *geometry_set.get_mesh_for_read(); const Span src_verts = mesh.verts(); @@ -910,6 +927,7 @@ static void duplicate_points_mesh(GeometrySet &geometry_set, ATTR_DOMAIN_POINT, offsets, selection, + propagation_info, mesh.attributes(), new_mesh->attributes_for_write()); @@ -935,7 +953,8 @@ static void duplicate_points_mesh(GeometrySet &geometry_set, static void duplicate_points_pointcloud(GeometrySet &geometry_set, const Field &count_field, const Field &selection_field, - const IndexAttributes &attribute_outputs) + const IndexAttributes &attribute_outputs, + const AnonymousAttributePropagationInfo &propagation_info) { const PointCloud &src_points = *geometry_set.get_pointcloud_for_read(); @@ -956,6 +975,7 @@ static void duplicate_points_pointcloud(GeometrySet &geometry_set, ATTR_DOMAIN_POINT, offsets, selection, + propagation_info, src_points.attributes(), pointcloud->attributes_for_write()); @@ -980,7 +1000,8 @@ static void duplicate_points_pointcloud(GeometrySet &geometry_set, static void duplicate_points(GeometrySet &geometry_set, const Field &count_field, const Field &selection_field, - const IndexAttributes &attribute_outputs) + const IndexAttributes &attribute_outputs, + const AnonymousAttributePropagationInfo &propagation_info) { Vector component_types = geometry_set.gather_component_types(true, true); for (const GeometryComponentType component_type : component_types) { @@ -988,17 +1009,19 @@ static void duplicate_points(GeometrySet &geometry_set, case GEO_COMPONENT_TYPE_POINT_CLOUD: if (geometry_set.has_pointcloud()) { duplicate_points_pointcloud( - geometry_set, count_field, selection_field, attribute_outputs); + geometry_set, count_field, selection_field, attribute_outputs, propagation_info); } break; case GEO_COMPONENT_TYPE_MESH: if (geometry_set.has_mesh()) { - duplicate_points_mesh(geometry_set, count_field, selection_field, attribute_outputs); + duplicate_points_mesh( + geometry_set, count_field, selection_field, attribute_outputs, propagation_info); } break; case GEO_COMPONENT_TYPE_CURVE: if (geometry_set.has_curves()) { - duplicate_points_curve(geometry_set, count_field, selection_field, attribute_outputs); + duplicate_points_curve( + geometry_set, count_field, selection_field, attribute_outputs, propagation_info); } break; default: @@ -1018,7 +1041,8 @@ static void duplicate_points(GeometrySet &geometry_set, static void duplicate_instances(GeometrySet &geometry_set, const Field &count_field, const Field &selection_field, - const IndexAttributes &attribute_outputs) + const IndexAttributes &attribute_outputs, + const AnonymousAttributePropagationInfo &propagation_info) { if (!geometry_set.has_instances()) { geometry_set.clear(); @@ -1062,6 +1086,7 @@ static void duplicate_instances(GeometrySet &geometry_set, ATTR_DOMAIN_INSTANCE, offsets, selection, + propagation_info, src_instances.attributes(), dst_instances->attributes_for_write()); @@ -1092,27 +1117,34 @@ static void node_geo_exec(GeoNodeExecParams params) Field count_field = params.extract_input>("Amount"); Field selection_field = params.extract_input>("Selection"); IndexAttributes attribute_outputs; - if (params.output_is_required("Duplicate Index")) { - attribute_outputs.duplicate_index = StrongAnonymousAttributeID("duplicate_index"); - } + attribute_outputs.duplicate_index = params.get_output_anonymous_attribute_id_if_needed( + "Duplicate Index"); + + const AnonymousAttributePropagationInfo &propagation_info = params.get_output_propagation_info( + "Geometry"); if (duplicate_domain == ATTR_DOMAIN_INSTANCE) { - duplicate_instances(geometry_set, count_field, selection_field, attribute_outputs); + duplicate_instances( + geometry_set, count_field, selection_field, attribute_outputs, propagation_info); } else { geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { switch (duplicate_domain) { case ATTR_DOMAIN_CURVE: - duplicate_curves(geometry_set, count_field, selection_field, attribute_outputs); + duplicate_curves( + geometry_set, count_field, selection_field, attribute_outputs, propagation_info); break; case ATTR_DOMAIN_FACE: - duplicate_faces(geometry_set, count_field, selection_field, attribute_outputs); + duplicate_faces( + geometry_set, count_field, selection_field, attribute_outputs, propagation_info); break; case ATTR_DOMAIN_EDGE: - duplicate_edges(geometry_set, count_field, selection_field, attribute_outputs); + duplicate_edges( + geometry_set, count_field, selection_field, attribute_outputs, propagation_info); break; case ATTR_DOMAIN_POINT: - duplicate_points(geometry_set, count_field, selection_field, attribute_outputs); + duplicate_points( + geometry_set, count_field, selection_field, attribute_outputs, propagation_info); break; default: BLI_assert_unreachable(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_curves.cc index c70d45ce334..772c4721284 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_curves.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_curves.cc @@ -18,9 +18,11 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output(N_("Curves")).propagate_all(); } -static Curves *edge_paths_to_curves_convert(const Mesh &mesh, - const IndexMask start_verts_mask, - const Span next_indices) +static Curves *edge_paths_to_curves_convert( + const Mesh &mesh, + const IndexMask start_verts_mask, + const Span next_indices, + const AnonymousAttributePropagationInfo &propagation_info) { Vector vert_indices; Vector curve_offsets; @@ -58,8 +60,8 @@ static Curves *edge_paths_to_curves_convert(const Mesh &mesh, if (vert_indices.is_empty()) { return nullptr; } - Curves *curves_id = bke::curves_new_nomain( - geometry::create_curve_from_vert_indices(mesh, vert_indices, curve_offsets, IndexRange(0))); + Curves *curves_id = bke::curves_new_nomain(geometry::create_curve_from_vert_indices( + mesh, vert_indices, curve_offsets, IndexRange(0), propagation_info)); return curves_id; } @@ -87,7 +89,8 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - geometry_set.replace_curves(edge_paths_to_curves_convert(*mesh, start_verts, next_vert)); + geometry_set.replace_curves(edge_paths_to_curves_convert( + *mesh, start_verts, next_vert, params.get_output_propagation_info("Curves"))); geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES}); }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc index 057c09c9936..2948713852b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc @@ -33,7 +33,8 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - geometry::split_edges(*geometry_set.get_mesh_for_write(), mask); + geometry::split_edges( + *geometry_set.get_mesh_for_write(), mask, params.get_output_propagation_info("Mesh")); } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc index a36413e49b2..27f34db2f9f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc @@ -27,7 +27,7 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_input(N_("Selection")).default_value(true).field_on_all().hide_value(); b.add_input(N_("Offset")) .subtype(PROP_TRANSLATION) - .implicit_field(implicit_field_inputs::normal) + .implicit_field_on_all(implicit_field_inputs::normal) .hide_value(); b.add_input(N_("Offset Scale")).default_value(1.0f).field_on_all(); b.add_input(N_("Individual")).default_value(true); @@ -61,8 +61,8 @@ static void node_update(bNodeTree *ntree, bNode *node) } struct AttributeOutputs { - StrongAnonymousAttributeID top_id; - StrongAnonymousAttributeID side_id; + AutoAnonymousAttributeID top_id; + AutoAnonymousAttributeID side_id; }; static void save_selection_as_attribute(Mesh &mesh, @@ -1334,12 +1334,8 @@ static void node_geo_exec(GeoNodeExecParams params) const Field final_offset{std::move(multiply_op)}; AttributeOutputs attribute_outputs; - if (params.output_is_required("Top")) { - attribute_outputs.top_id = StrongAnonymousAttributeID("Top"); - } - if (params.output_is_required("Side")) { - attribute_outputs.side_id = StrongAnonymousAttributeID("Side"); - } + attribute_outputs.top_id = params.get_output_anonymous_attribute_id_if_needed("Top"); + attribute_outputs.side_id = params.get_output_anonymous_attribute_id_if_needed("Side"); const bool extrude_individual = mode == GEO_NODE_EXTRUDE_MESH_FACES && params.extract_input("Individual"); diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc index 44172cfee60..6c38c8a91ab 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc @@ -198,8 +198,11 @@ static void node_geo_exec(GeoNodeExecParams params) GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}; Map attributes_to_propagate; - geometry_set.gather_attributes_for_propagation( - types, GEO_COMPONENT_TYPE_INSTANCES, false, attributes_to_propagate); + geometry_set.gather_attributes_for_propagation(types, + GEO_COMPONENT_TYPE_INSTANCES, + false, + params.get_output_propagation_info("Instances"), + attributes_to_propagate); attributes_to_propagate.remove("position"); for (const GeometryComponentType type : types) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc index e43aac9d3ad..0be8743f9a1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc @@ -26,7 +26,8 @@ static void node_declare(NodeDeclarationBuilder &b) static void convert_instances_to_points(GeometrySet &geometry_set, Field position_field, Field radius_field, - const Field selection_field) + const Field selection_field, + const AnonymousAttributePropagationInfo &propagation_info) { const bke::Instances &instances = *geometry_set.get_instances_for_read(); @@ -62,6 +63,7 @@ static void convert_instances_to_points(GeometrySet &geometry_set, geometry_set.gather_attributes_for_propagation({GEO_COMPONENT_TYPE_INSTANCES}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, + propagation_info, attributes_to_propagate); /* These two attributes are added by the implicit inputs above. */ attributes_to_propagate.remove("position"); @@ -91,7 +93,8 @@ static void node_geo_exec(GeoNodeExecParams params) convert_instances_to_points(geometry_set, params.extract_input>("Position"), params.extract_input>("Radius"), - params.extract_input>("Selection")); + params.extract_input>("Selection"), + params.get_output_propagation_info("Points")); geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_EDIT}); params.set_output("Points", std::move(geometry_set)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc index 03ecb7c4c2f..d49f2583bde 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -28,7 +28,7 @@ static Map get_final_attribute_info( for (const GeometryComponent *component : components) { component->attributes()->for_all( [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - if (attribute_id.is_named() && ignored_attributes.contains(attribute_id.name())) { + if (ignored_attributes.contains(attribute_id.name())) { return true; } if (meta_data.data_type == CD_PROP_STRING) { @@ -143,7 +143,9 @@ static void join_components(Span /*src_components*/, } template -static void join_component_type(Span src_geometry_sets, GeometrySet &result) +static void join_component_type(Span src_geometry_sets, + GeometrySet &result, + const AnonymousAttributePropagationInfo &propagation_info) { Vector components; for (const GeometrySet &geometry_set : src_geometry_sets) { @@ -176,6 +178,7 @@ static void join_component_type(Span src_geometry_sets, GeometrySet geometry::RealizeInstancesOptions options; options.keep_original_ids = true; options.realize_instance_attributes = false; + options.propagation_info = propagation_info; GeometrySet joined_components = geometry::realize_instances( GeometrySet::create_with_instances(instances.release()), options); result.add(joined_components.get_component_for_write()); @@ -186,13 +189,17 @@ static void node_geo_exec(GeoNodeExecParams params) { Vector geometry_sets = params.extract_input>("Geometry"); + const AnonymousAttributePropagationInfo &propagation_info = params.get_output_propagation_info( + "Geometry"); + GeometrySet geometry_set_result; - join_component_type(geometry_sets, geometry_set_result); - join_component_type(geometry_sets, geometry_set_result); - join_component_type(geometry_sets, geometry_set_result); - join_component_type(geometry_sets, geometry_set_result); - join_component_type(geometry_sets, geometry_set_result); - join_component_type(geometry_sets, geometry_set_result); + join_component_type(geometry_sets, geometry_set_result, propagation_info); + join_component_type(geometry_sets, geometry_set_result, propagation_info); + join_component_type(geometry_sets, geometry_set_result, propagation_info); + join_component_type(geometry_sets, geometry_set_result, propagation_info); + join_component_type(geometry_sets, geometry_set_result, propagation_info); + join_component_type( + geometry_sets, geometry_set_result, propagation_info); params.set_output("Geometry", std::move(geometry_set_result)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc b/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc index 04c48a82e42..2fafc77bc45 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc @@ -38,9 +38,11 @@ static void node_init(bNodeTree * /*tree*/, bNode *node) node->storage = data; } -static PointCloud *pointcloud_merge_by_distance(const PointCloud &src_points, - const float merge_distance, - const Field &selection_field) +static PointCloud *pointcloud_merge_by_distance( + const PointCloud &src_points, + const float merge_distance, + const Field &selection_field, + const AnonymousAttributePropagationInfo &propagation_info) { bke::PointCloudFieldContext context{src_points}; FieldEvaluator evaluator{context, src_points.totpoint}; @@ -52,7 +54,8 @@ static PointCloud *pointcloud_merge_by_distance(const PointCloud &src_points, return nullptr; } - return geometry::point_merge_by_distance(src_points, merge_distance, selection); + return geometry::point_merge_by_distance( + src_points, merge_distance, selection, propagation_info); } static std::optional mesh_merge_by_distance_connected(const Mesh &mesh, @@ -97,7 +100,8 @@ static void node_geo_exec(GeoNodeExecParams params) geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { if (const PointCloud *pointcloud = geometry_set.get_pointcloud_for_read()) { - PointCloud *result = pointcloud_merge_by_distance(*pointcloud, merge_distance, selection); + PointCloud *result = pointcloud_merge_by_distance( + *pointcloud, merge_distance, selection, params.get_output_propagation_info("Geometry")); if (result) { geometry_set.replace_pointcloud(result); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc index 8f261ad9e66..9e92527f393 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc @@ -814,18 +814,10 @@ static void node_geo_exec(GeoNodeExecParams params) const float depth = params.extract_input("Depth"); ConeAttributeOutputs attribute_outputs; - if (params.output_is_required("Top")) { - attribute_outputs.top_id = StrongAnonymousAttributeID("top_selection"); - } - if (params.output_is_required("Bottom")) { - attribute_outputs.bottom_id = StrongAnonymousAttributeID("bottom_selection"); - } - if (params.output_is_required("Side")) { - attribute_outputs.side_id = StrongAnonymousAttributeID("side_selection"); - } - if (params.output_is_required("UV Map")) { - attribute_outputs.uv_map_id = StrongAnonymousAttributeID("uv_map"); - } + attribute_outputs.top_id = params.get_output_anonymous_attribute_id_if_needed("Top"); + attribute_outputs.bottom_id = params.get_output_anonymous_attribute_id_if_needed("Bottom"); + attribute_outputs.side_id = params.get_output_anonymous_attribute_id_if_needed("Side"); + attribute_outputs.uv_map_id = params.get_output_anonymous_attribute_id_if_needed("UV Map"); Mesh *mesh = create_cylinder_or_cone_mesh(radius_top, radius_bottom, diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc index 879de80e73d..88fb4c72b1a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc @@ -107,10 +107,8 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - StrongAnonymousAttributeID uv_map_id; - if (params.output_is_required("UV Map")) { - uv_map_id = StrongAnonymousAttributeID("uv_map"); - } + AutoAnonymousAttributeID uv_map_id = params.get_output_anonymous_attribute_id_if_needed( + "UV Map"); Mesh *mesh = create_cube_mesh(size, verts_x, verts_y, verts_z, uv_map_id.get()); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc index b0e857feb64..bc651cdb652 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc @@ -107,18 +107,10 @@ static void node_geo_exec(GeoNodeExecParams params) } ConeAttributeOutputs attribute_outputs; - if (params.output_is_required("Top")) { - attribute_outputs.top_id = StrongAnonymousAttributeID("top_selection"); - } - if (params.output_is_required("Bottom")) { - attribute_outputs.bottom_id = StrongAnonymousAttributeID("bottom_selection"); - } - if (params.output_is_required("Side")) { - attribute_outputs.side_id = StrongAnonymousAttributeID("side_selection"); - } - if (params.output_is_required("UV Map")) { - attribute_outputs.uv_map_id = StrongAnonymousAttributeID("uv_map"); - } + attribute_outputs.top_id = params.get_output_anonymous_attribute_id_if_needed("Top"); + attribute_outputs.bottom_id = params.get_output_anonymous_attribute_id_if_needed("Bottom"); + attribute_outputs.side_id = params.get_output_anonymous_attribute_id_if_needed("Side"); + attribute_outputs.uv_map_id = params.get_output_anonymous_attribute_id_if_needed("UV Map"); /* The cylinder is a special case of the cone mesh where the top and bottom radius are equal. */ Mesh *mesh = create_cylinder_or_cone_mesh(radius, diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc index 98ff797addc..b05a6d8019e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc @@ -194,10 +194,8 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - StrongAnonymousAttributeID uv_map_id; - if (params.output_is_required("UV Map")) { - uv_map_id = StrongAnonymousAttributeID("uv_map"); - } + AutoAnonymousAttributeID uv_map_id = params.get_output_anonymous_attribute_id_if_needed( + "UV Map"); Mesh *mesh = create_grid_mesh(verts_x, verts_y, size_x, size_y, uv_map_id.get()); BKE_id_material_eval_ensure_default_slot(&mesh->id); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc index cd8c6a19612..f0993da1ba2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc @@ -77,10 +77,8 @@ static void node_geo_exec(GeoNodeExecParams params) const int subdivisions = std::min(params.extract_input("Subdivisions"), 10); const float radius = params.extract_input("Radius"); - StrongAnonymousAttributeID uv_map_id; - if (params.output_is_required("UV Map")) { - uv_map_id = StrongAnonymousAttributeID("uv_map"); - } + AutoAnonymousAttributeID uv_map_id = params.get_output_anonymous_attribute_id_if_needed( + "UV Map"); Mesh *mesh = create_ico_sphere_mesh(subdivisions, radius, uv_map_id.get()); params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc index 5b424534d7b..ff40a970785 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc @@ -362,10 +362,8 @@ static void node_geo_exec(GeoNodeExecParams params) const float radius = params.extract_input("Radius"); - StrongAnonymousAttributeID uv_map_id; - if (params.output_is_required("UV Map")) { - uv_map_id = StrongAnonymousAttributeID("uv_map"); - } + AutoAnonymousAttributeID uv_map_id = params.get_output_anonymous_attribute_id_if_needed( + "UV Map"); Mesh *mesh = create_uv_sphere_mesh(radius, segments_num, rings_num, uv_map_id.get()); params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc index 40626a1c2c5..2b80c6df51f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc @@ -36,7 +36,8 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - bke::CurvesGeometry curves = geometry::mesh_to_curve_convert(*mesh, selection); + bke::CurvesGeometry curves = geometry::mesh_to_curve_convert( + *mesh, selection, params.get_output_propagation_info("Curve")); geometry_set.replace_curves(bke::curves_new_nomain(std::move(curves))); geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_CURVE}); }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc index 790f0ceda9c..f551faee2e0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc @@ -24,7 +24,7 @@ static void node_declare(NodeDeclarationBuilder &b) { b.add_input(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); b.add_input(N_("Selection")).default_value(true).field_on_all().hide_value(); - b.add_input(N_("Position")).implicit_field(implicit_field_inputs::position); + b.add_input(N_("Position")).implicit_field_on_all(implicit_field_inputs::position); b.add_input(N_("Radius")) .default_value(0.05f) .min(0.0f) @@ -49,7 +49,8 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set, Field &position_field, Field &radius_field, Field &selection_field, - const eAttrDomain domain) + const eAttrDomain domain, + const AnonymousAttributePropagationInfo &propagation_info) { const Mesh *mesh = geometry_set.get_mesh_for_read(); if (mesh == nullptr) { @@ -87,8 +88,11 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set, radius.finish(); Map attributes; - geometry_set.gather_attributes_for_propagation( - {GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes); + geometry_set.gather_attributes_for_propagation({GEO_COMPONENT_TYPE_MESH}, + GEO_COMPONENT_TYPE_POINT_CLOUD, + false, + propagation_info, + attributes); attributes.remove("position"); const AttributeAccessor src_attributes = mesh->attributes(); @@ -128,23 +132,42 @@ static void node_geo_exec(GeoNodeExecParams params) const NodeGeometryMeshToPoints &storage = node_storage(params.node()); const GeometryNodeMeshToPointsMode mode = (GeometryNodeMeshToPointsMode)storage.mode; + const AnonymousAttributePropagationInfo &propagation_info = params.get_output_propagation_info( + "Points"); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { switch (mode) { case GEO_NODE_MESH_TO_POINTS_VERTICES: - geometry_set_mesh_to_points( - geometry_set, position, positive_radius, selection, ATTR_DOMAIN_POINT); + geometry_set_mesh_to_points(geometry_set, + position, + positive_radius, + selection, + ATTR_DOMAIN_POINT, + propagation_info); break; case GEO_NODE_MESH_TO_POINTS_EDGES: - geometry_set_mesh_to_points( - geometry_set, position, positive_radius, selection, ATTR_DOMAIN_EDGE); + geometry_set_mesh_to_points(geometry_set, + position, + positive_radius, + selection, + ATTR_DOMAIN_EDGE, + propagation_info); break; case GEO_NODE_MESH_TO_POINTS_FACES: - geometry_set_mesh_to_points( - geometry_set, position, positive_radius, selection, ATTR_DOMAIN_FACE); + geometry_set_mesh_to_points(geometry_set, + position, + positive_radius, + selection, + ATTR_DOMAIN_FACE, + propagation_info); break; case GEO_NODE_MESH_TO_POINTS_CORNERS: - geometry_set_mesh_to_points( - geometry_set, position, positive_radius, selection, ATTR_DOMAIN_CORNER); + geometry_set_mesh_to_points(geometry_set, + position, + positive_radius, + selection, + ATTR_DOMAIN_CORNER, + propagation_info); break; } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc index 2e9d5037c00..5cd5bbe690e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc @@ -21,8 +21,10 @@ static void node_declare(NodeDeclarationBuilder &b) } /* One improvement would be to move the attribute arrays directly to the mesh when possible. */ -static void geometry_set_points_to_vertices(GeometrySet &geometry_set, - Field &selection_field) +static void geometry_set_points_to_vertices( + GeometrySet &geometry_set, + Field &selection_field, + const AnonymousAttributePropagationInfo &propagation_info) { const PointCloud *points = geometry_set.get_pointcloud_for_read(); if (points == nullptr) { @@ -41,8 +43,11 @@ static void geometry_set_points_to_vertices(GeometrySet &geometry_set, const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); Map attributes; - geometry_set.gather_attributes_for_propagation( - {GEO_COMPONENT_TYPE_POINT_CLOUD}, GEO_COMPONENT_TYPE_MESH, false, attributes); + geometry_set.gather_attributes_for_propagation({GEO_COMPONENT_TYPE_POINT_CLOUD}, + GEO_COMPONENT_TYPE_MESH, + false, + propagation_info, + attributes); Mesh *mesh = BKE_mesh_new_nomain(selection.size(), 0, 0, 0, 0); geometry_set.replace_mesh(mesh); @@ -73,7 +78,8 @@ static void node_geo_exec(GeoNodeExecParams params) Field selection_field = params.extract_input>("Selection"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - geometry_set_points_to_vertices(geometry_set, selection_field); + geometry_set_points_to_vertices( + geometry_set, selection_field, params.get_output_propagation_info("Mesh")); }); params.set_output("Mesh", std::move(geometry_set)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc index 920d3f77666..385b47a2f1e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc @@ -37,6 +37,7 @@ static void node_geo_exec(GeoNodeExecParams params) geometry::RealizeInstancesOptions options; options.keep_original_ids = legacy_behavior; options.realize_instance_attributes = !legacy_behavior; + options.propagation_info = params.get_output_propagation_info("Geometry"); geometry_set = geometry::realize_instances(geometry_set, options); params.set_output("Geometry", std::move(geometry_set)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc index 8b01e147d0d..28c0bf84160 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc @@ -47,29 +47,42 @@ static void node_geo_exec(GeoNodeExecParams params) const NodeGeometrySeparateGeometry &storage = node_storage(params.node()); const eAttrDomain domain = eAttrDomain(storage.domain); - auto separate_geometry_maybe_recursively = [&](GeometrySet &geometry_set, - const Field &selection) { - bool is_error; - if (domain == ATTR_DOMAIN_INSTANCE) { - /* Only delete top level instances. */ - separate_geometry( - geometry_set, domain, GEO_NODE_DELETE_GEOMETRY_MODE_ALL, selection, is_error); - } - else { - geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - separate_geometry( - geometry_set, domain, GEO_NODE_DELETE_GEOMETRY_MODE_ALL, selection, is_error); - }); - } - }; + auto separate_geometry_maybe_recursively = + [&](GeometrySet &geometry_set, + const Field &selection, + const AnonymousAttributePropagationInfo &propagation_info) { + bool is_error; + if (domain == ATTR_DOMAIN_INSTANCE) { + /* Only delete top level instances. */ + separate_geometry(geometry_set, + domain, + GEO_NODE_DELETE_GEOMETRY_MODE_ALL, + selection, + propagation_info, + is_error); + } + else { + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + separate_geometry(geometry_set, + domain, + GEO_NODE_DELETE_GEOMETRY_MODE_ALL, + selection, + propagation_info, + is_error); + }); + } + }; GeometrySet second_set(geometry_set); if (params.output_is_required("Selection")) { - separate_geometry_maybe_recursively(geometry_set, selection_field); + separate_geometry_maybe_recursively( + geometry_set, selection_field, params.get_output_propagation_info("Selection")); params.set_output("Selection", std::move(geometry_set)); } if (params.output_is_required("Inverted")) { - separate_geometry_maybe_recursively(second_set, fn::invert_boolean_field(selection_field)); + separate_geometry_maybe_recursively(second_set, + fn::invert_boolean_field(selection_field), + params.get_output_propagation_info("Inverted")); params.set_output("Inverted", std::move(second_set)); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc index 93dcdd37ed2..5eb98fe8f01 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc @@ -249,7 +249,7 @@ static std::optional get_text_layout(GeoNodeExecParams ¶ms) } } - if (params.output_is_required("Line")) { + if (params.anonymous_attribute_output_is_required("Line")) { layout.line_numbers.reinitialize(layout.positions.size()); for (const int i : layout.positions.index_range()) { CharTrans &ct = chartransdata[i]; @@ -278,7 +278,7 @@ static Map create_curve_instances(GeoNodeExecParams ¶ms, { VFont *vfont = reinterpret_cast(params.node().id); Map handles; - bool pivot_required = params.output_is_required("Pivot Point"); + bool pivot_required = params.anonymous_attribute_output_is_required("Pivot Point"); for (int i : layout.char_codes.index_range()) { if (handles.contains(layout.char_codes[i])) { @@ -341,10 +341,10 @@ static void create_attributes(GeoNodeExecParams ¶ms, { MutableAttributeAccessor attributes = instances.attributes_for_write(); - if (params.output_is_required("Line")) { - StrongAnonymousAttributeID line_id = StrongAnonymousAttributeID("Line"); + if (AutoAnonymousAttributeID line_id = params.get_output_anonymous_attribute_id_if_needed( + "Line")) { SpanAttributeWriter line_attribute = attributes.lookup_or_add_for_write_only_span( - line_id.get(), ATTR_DOMAIN_INSTANCE); + *line_id, ATTR_DOMAIN_INSTANCE); line_attribute.span.copy_from(layout.line_numbers); line_attribute.finish(); params.set_output("Line", @@ -352,10 +352,10 @@ static void create_attributes(GeoNodeExecParams ¶ms, params.attribute_producer_name())); } - if (params.output_is_required("Pivot Point")) { - StrongAnonymousAttributeID pivot_id = StrongAnonymousAttributeID("Pivot"); + if (AutoAnonymousAttributeID pivot_id = params.get_output_anonymous_attribute_id_if_needed( + "Pivot Point")) { SpanAttributeWriter pivot_attribute = - attributes.lookup_or_add_for_write_only_span(pivot_id.get(), ATTR_DOMAIN_INSTANCE); + attributes.lookup_or_add_for_write_only_span(*pivot_id, ATTR_DOMAIN_INSTANCE); for (const int i : layout.char_codes.index_range()) { pivot_attribute.span[i] = layout.pivot_points.lookup(layout.char_codes[i]); diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index 454fe4da23b..bfda854d4c2 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -17,6 +17,8 @@ #include "NOD_node_declaration.hh" #include "BLI_cpp_types.hh" +#include "BLI_dot_export.hh" +#include "BLI_hash.h" #include "BLI_lazy_threading.hh" #include "BLI_map.hh" @@ -111,6 +113,16 @@ class LazyFunctionForGeometryNode : public LazyFunction { const bNode &node_; public: + /** + * Index of a boolean input that indicates whether the output socket is used. + */ + Map lf_input_for_output_bsocket_usage_; + /** + * Index of an attribute set input that indicates which anonymous attributes should be + * propagated to the output. + */ + Map lf_input_for_attribute_propagation_to_output_; + LazyFunctionForGeometryNode(const bNode &node, Vector &r_used_inputs, Vector &r_used_outputs) @@ -119,6 +131,32 @@ class LazyFunctionForGeometryNode : public LazyFunction { BLI_assert(node.typeinfo->geometry_node_execute != nullptr); debug_name_ = node.name; lazy_function_interface_from_node(node, r_used_inputs, r_used_outputs, inputs_, outputs_); + + const NodeDeclaration &node_decl = *node.declaration(); + const aal::RelationsInNode *relations = node_decl.anonymous_attribute_relations(); + if (relations == nullptr) { + return; + } + Vector handled_field_outputs; + for (const aal::AvailableRelation &relation : relations->available_relations) { + const bNodeSocket &output_bsocket = node.output_socket(relation.field_output); + if (output_bsocket.is_available() && !handled_field_outputs.contains(&output_bsocket)) { + handled_field_outputs.append(&output_bsocket); + const int lf_index = inputs_.append_and_get_index_as("Output Used", CPPType::get()); + lf_input_for_output_bsocket_usage_.add(output_bsocket.identifier, lf_index); + } + } + + Vector handled_geometry_outputs; + for (const aal::PropagateRelation &relation : relations->propagate_relations) { + const bNodeSocket &output_bsocket = node.output_socket(relation.to_geometry_output); + if (output_bsocket.is_available() && !handled_geometry_outputs.contains(&output_bsocket)) { + handled_geometry_outputs.append(&output_bsocket); + const int lf_index = inputs_.append_and_get_index_as( + "Propagate to Output", CPPType::get()); + lf_input_for_attribute_propagation_to_output_.add(output_bsocket.identifier, lf_index); + } + } } void execute_impl(lf::Params ¶ms, const lf::Context &context) const override @@ -126,7 +164,11 @@ class LazyFunctionForGeometryNode : public LazyFunction { GeoNodesLFUserData *user_data = dynamic_cast(context.user_data); BLI_assert(user_data != nullptr); - GeoNodeExecParams geo_params{node_, params, context}; + GeoNodeExecParams geo_params{node_, + params, + context, + lf_input_for_output_bsocket_usage_, + lf_input_for_attribute_propagation_to_output_}; geo_eval_log::TimePoint start_time = geo_eval_log::Clock::now(); node_.typeinfo->geometry_node_execute(geo_params); @@ -138,6 +180,27 @@ class LazyFunctionForGeometryNode : public LazyFunction { tree_logger.node_execution_times.append({node_.identifier, start_time, end_time}); } } + + std::string input_name(const int index) const override + { + for (const auto [identifier, lf_index] : lf_input_for_output_bsocket_usage_.items()) { + if (index == lf_index) { + return "Use Output '" + identifier + "'"; + } + } + for (const auto [identifier, lf_index] : + lf_input_for_attribute_propagation_to_output_.items()) { + if (index == lf_index) { + return "Propagate to '" + identifier + "'"; + } + } + return inputs_[index].debug_name; + } + + std::string output_name(const int index) const override + { + return outputs_[index].debug_name; + } }; /** @@ -588,6 +651,35 @@ class LazyFunctionForViewerNode : public LazyFunction { } }; +/** + * Outputs true when a specific viewer node is used in the current context and false otherwise. + */ +class LazyFunctionForViewerInputUsage : public LazyFunction { + private: + const lf::FunctionNode &lf_viewer_node_; + + public: + LazyFunctionForViewerInputUsage(const lf::FunctionNode &lf_viewer_node) + : lf_viewer_node_(lf_viewer_node) + { + debug_name_ = "Viewer Input Usage"; + outputs_.append_as("Viewer is Used", CPPType::get()); + } + + void execute_impl(lf::Params ¶ms, const lf::Context &context) const override + { + GeoNodesLFUserData *user_data = dynamic_cast(context.user_data); + BLI_assert(user_data != nullptr); + const ComputeContextHash &context_hash = user_data->compute_context->hash(); + const GeoNodesModifierData &modifier_data = *user_data->modifier_data; + const Span nodes_with_side_effects = + modifier_data.side_effect_nodes->lookup(context_hash); + + const bool viewer_is_used = nodes_with_side_effects.contains(&lf_viewer_node_); + params.set_output(0, viewer_is_used); + } +}; + /** * This lazy-function wraps a group node. Internally it just executes the lazy-function graph of * the referenced group. @@ -596,7 +688,6 @@ class LazyFunctionForGroupNode : public LazyFunction { private: const bNode &group_node_; bool has_many_nodes_ = false; - bool use_fallback_outputs_ = false; std::optional lf_logger_; std::optional lf_side_effect_provider_; std::optional graph_executor_; @@ -608,40 +699,71 @@ class LazyFunctionForGroupNode : public LazyFunction { }; public: + /** + * For every input bsocket there is a corresponding boolean output that indicates whether that + * input is used. + */ + Map lf_output_for_input_bsocket_usage_; + /** + * For every output bsocket there is a corresponding boolean input that indicates whether the + * output is used. + */ + Map lf_input_for_output_bsocket_usage_; + /** + * For every geometry output that can propagate attributes from an input, there is an attribute + * set input. It indicates which attributes should be propagated to the output. + */ + Map lf_input_for_attribute_propagation_to_output_; + LazyFunctionForGroupNode(const bNode &group_node, - const GeometryNodesLazyFunctionGraphInfo &lf_graph_info, - Vector &r_used_inputs, - Vector &r_used_outputs) + const GeometryNodesLazyFunctionGraphInfo &lf_graph_info) : group_node_(group_node) { debug_name_ = group_node.name; - lazy_function_interface_from_node( - group_node, r_used_inputs, r_used_outputs, inputs_, outputs_); + allow_missing_requested_inputs_ = true; - bNodeTree *group_btree = reinterpret_cast(group_node_.id); - BLI_assert(group_btree != nullptr); + Vector tmp_inputs; + Vector tmp_outputs; + lazy_function_interface_from_node(group_node, tmp_inputs, tmp_outputs, inputs_, outputs_); has_many_nodes_ = lf_graph_info.num_inline_nodes_approximate > 1000; Vector graph_inputs; - for (const lf::OutputSocket *socket : lf_graph_info.mapping.group_input_sockets) { - if (socket != nullptr) { - graph_inputs.append(socket); - } + /* Add inputs that also exist on the bnode. */ + graph_inputs.extend(lf_graph_info.mapping.group_input_sockets); + + /* Add a boolean input for every output bsocket that indicates whether that socket is used. */ + for (const int i : group_node.output_sockets().index_range()) { + lf_input_for_output_bsocket_usage_.add_new( + i, + graph_inputs.append_and_get_index(lf_graph_info.mapping.group_output_used_sockets[i])); + inputs_.append_as("Output is Used", CPPType::get(), lf::ValueUsage::Maybe); } + graph_inputs.extend(lf_graph_info.mapping.group_output_used_sockets); + + /* Add an attribute set input for every output geometry socket that can propagate attributes + * from inputs. */ + for (auto [output_index, lf_socket] : + lf_graph_info.mapping.attribute_set_by_geometry_output.items()) { + const int lf_index = inputs_.append_and_get_index_as( + "Attribute Set", CPPType::get(), lf::ValueUsage::Maybe); + graph_inputs.append(lf_socket); + lf_input_for_attribute_propagation_to_output_.add(output_index, lf_index); + } + Vector graph_outputs; - if (const bNode *group_output_bnode = group_btree->group_output_node()) { - for (const bNodeSocket *bsocket : group_output_bnode->input_sockets().drop_back(1)) { - const lf::Socket *socket = lf_graph_info.mapping.dummy_socket_map.lookup_default(bsocket, - nullptr); - if (socket != nullptr) { - graph_outputs.append(&socket->as_input()); - } + /* Add outputs that also exist on the bnode. */ + graph_outputs.extend(lf_graph_info.mapping.standard_group_output_sockets); + /* Add a boolean output for every input bsocket that indicates whether that socket is used. */ + for (const int i : group_node.input_sockets().index_range()) { + const InputUsageHint &input_usage_hint = lf_graph_info.mapping.group_input_usage_hints[i]; + if (input_usage_hint.type == InputUsageHintType::DynamicSocket) { + const lf::InputSocket *lf_socket = lf_graph_info.mapping.group_input_usage_sockets[i]; + lf_output_for_input_bsocket_usage_.add_new(i, + graph_outputs.append_and_get_index(lf_socket)); + outputs_.append_as("Input is Used", CPPType::get()); } } - else { - use_fallback_outputs_ = true; - } lf_logger_.emplace(lf_graph_info); lf_side_effect_provider_.emplace(); @@ -662,11 +784,6 @@ class LazyFunctionForGroupNode : public LazyFunction { * if every individual node is very small. */ lazy_threading::send_hint(); } - if (use_fallback_outputs_) { - /* The node group itself does not have an output node, so use default values as outputs. - * The group should still be executed in case it has side effects. */ - params.set_default_remaining_outputs(); - } Storage *storage = static_cast(context.storage); @@ -702,6 +819,53 @@ class LazyFunctionForGroupNode : public LazyFunction { graph_executor_->destruct_storage(s->graph_executor_storage); std::destroy_at(s); } + + std::string name() const override + { + std::stringstream ss; + ss << "Group '" << (group_node_.id->name + 2) << "' (" << group_node_.name << ")"; + return ss.str(); + } + + std::string input_name(const int i) const override + { + if (i < group_node_.input_sockets().size()) { + return group_node_.input_socket(i).name; + } + for (const auto [bsocket_index, lf_socket_index] : + lf_input_for_output_bsocket_usage_.items()) { + if (i == lf_socket_index) { + std::stringstream ss; + ss << "'" << group_node_.output_socket(bsocket_index).name << "' output is used"; + return ss.str(); + } + } + for (const auto [bsocket_index, lf_index] : + lf_input_for_attribute_propagation_to_output_.items()) { + if (i == lf_index) { + std::stringstream ss; + ss << "Propagate to '" << group_node_.output_socket(bsocket_index).name << "'"; + return ss.str(); + } + } + return inputs_[i].debug_name; + } + + std::string output_name(const int i) const override + { + if (i < group_node_.output_sockets().size()) { + return group_node_.output_socket(i).name; + } + for (const auto [bsocket_index, lf_socket_index] : + lf_output_for_input_bsocket_usage_.items()) { + if (i == lf_socket_index) { + std::stringstream ss; + ss << "'" << group_node_.input_socket(bsocket_index).name << "' input is used"; + return ss.str(); + } + } + return outputs_[i].debug_name; + } }; static GMutablePointer get_socket_default_value(LinearAllocator<> &allocator, @@ -747,6 +911,230 @@ class GroupOutputDebugInfo : public lf::DummyDebugInfo { } }; +/** + * Computes the logical or of the inputs and supports short-circuit evaluation (i.e. if the first + * input is true already, the other inputs are not checked). + */ +class LazyFunctionForLogicalOr : public lf::LazyFunction { + public: + LazyFunctionForLogicalOr(const int inputs_num) + { + debug_name_ = "Logical Or"; + for ([[maybe_unused]] const int i : IndexRange(inputs_num)) { + inputs_.append_as("Input", CPPType::get(), lf::ValueUsage::Maybe); + } + outputs_.append_as("Output", CPPType::get()); + } + + void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override + { + int first_unavailable_input = -1; + for (const int i : inputs_.index_range()) { + if (const bool *value = params.try_get_input_data_ptr(i)) { + if (*value) { + params.set_output(0, true); + return; + } + } + else { + first_unavailable_input = i; + } + } + if (first_unavailable_input == -1) { + params.set_output(0, false); + return; + } + params.try_get_input_data_ptr_or_request(first_unavailable_input); + } +}; + +/** + * Outputs booleans that indicate which inputs of a switch node are used. Note that it's possible + * that both inputs are used when the condition is a field. + */ +class LazyFunctionForSwitchSocketUsage : public lf::LazyFunction { + public: + LazyFunctionForSwitchSocketUsage() + { + debug_name_ = "Switch Socket Usage"; + inputs_.append_as("Condition", CPPType::get>()); + outputs_.append_as("False", CPPType::get()); + outputs_.append_as("True", CPPType::get()); + } + + void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override + { + const ValueOrField &condition = params.get_input>(0); + if (condition.is_field()) { + params.set_output(0, true); + params.set_output(1, true); + } + else { + const bool value = condition.as_value(); + params.set_output(0, !value); + params.set_output(1, value); + } + } +}; + +/** + * Takes a field as input and extracts the set of anonymous attributes that it references. + */ +class LazyFunctionForAnonymousAttributeSetExtract : public lf::LazyFunction { + private: + const ValueOrFieldCPPType &type_; + + public: + LazyFunctionForAnonymousAttributeSetExtract(const ValueOrFieldCPPType &type) : type_(type) + { + debug_name_ = "Extract Attribute Set"; + inputs_.append_as("Field", type.self); + outputs_.append_as("Attributes", CPPType::get()); + } + + void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override + { + const void *value_or_field = params.try_get_input_data_ptr(0); + bke::AnonymousAttributeSet attributes; + if (type_.is_field(value_or_field)) { + const GField &field = *type_.get_field_ptr(value_or_field); + field.node().for_each_field_input_recursive([&](const FieldInput &field_input) { + if (const auto *attr_field_input = dynamic_cast( + &field_input)) { + if (!attributes.names) { + attributes.names = std::make_shared>(); + } + attributes.names->add_as(attr_field_input->anonymous_id()->name()); + } + }); + } + params.set_output(0, std::move(attributes)); + } +}; + +/** + * Conditionally joines multiple attribute sets. Each input attribute set can be disabled with a + * corresponding boolean input. + */ +class LazyFunctionForAnonymousAttributeSetJoin : public lf::LazyFunction { + const int amount_; + + public: + LazyFunctionForAnonymousAttributeSetJoin(const int amount) : amount_(amount) + { + debug_name_ = "Join Attribute Sets"; + for ([[maybe_unused]] const int i : IndexRange(amount)) { + inputs_.append_as("Use", CPPType::get()); + inputs_.append_as( + "Attribute Set", CPPType::get(), lf::ValueUsage::Maybe); + } + outputs_.append_as("Attribute Set", CPPType::get()); + } + + void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override + { + Vector sets; + bool set_is_missing = false; + for (const int i : IndexRange(amount_)) { + if (params.get_input(this->get_use_input(i))) { + if (bke::AnonymousAttributeSet *set = + params.try_get_input_data_ptr_or_request( + this->get_attribute_set_input(i))) { + sets.append(set); + } + else { + set_is_missing = true; + } + } + } + if (set_is_missing) { + return; + } + bke::AnonymousAttributeSet joined_set; + if (sets.is_empty()) { + /* Nothing to do. */ + } + else if (sets.size() == 1) { + joined_set.names = std::move(sets[0]->names); + } + else { + joined_set.names = std::make_shared>(); + for (const bke::AnonymousAttributeSet *set : sets) { + if (set->names) { + for (const std::string &name : *set->names) { + joined_set.names->add(name); + } + } + } + } + params.set_output(0, std::move(joined_set)); + } + + int get_use_input(const int i) const + { + return 2 * i; + } + + int get_attribute_set_input(const int i) const + { + return 2 * i + 1; + } +}; + +enum class AttributeReferenceKeyType { + /** Attribute referenced by a field passed into the group. */ + InputField, + /** Attributes referenced on the output geometry outside of the current group. */ + OutputGeometry, + /** Attribute referenced by a field created within the current group. */ + Socket, +}; + +/** + * Identifier for something that can reference anonymous attributes that should be propagated. + */ +struct AttributeReferenceKey { + AttributeReferenceKeyType type; + /* Used when type is InputField or OutputGeometry. */ + int index = 0; + /* Used when type is Socket. */ + const bNodeSocket *bsocket = nullptr; + + uint64_t hash() const + { + return get_default_hash_3(this->type, this->bsocket, this->index); + } + + friend bool operator==(const AttributeReferenceKey &a, const AttributeReferenceKey &b) + { + return a.type == b.type && a.bsocket == b.bsocket && a.index == b.index; + } + + friend std::ostream &operator<<(std::ostream &stream, const AttributeReferenceKey &value) + { + if (value.type == AttributeReferenceKeyType::InputField) { + stream << "Input Field: " << value.index; + } + else if (value.type == AttributeReferenceKeyType::OutputGeometry) { + stream << "Output Geometry: " << value.index; + } + else { + stream << "Socket: " << value.bsocket->owner_node().name << " -> " << value.bsocket->name; + } + return stream; + } +}; + +/** + * Additional information that corresponds to an #AttributeReferenceKey. + */ +struct AttributeReferenceInfo { + /** Output socket that contains an attribute set containing the referenced attributes. */ + lf::OutputSocket *lf_attribute_set_socket = nullptr; + /** Geometry sockets that contain the referenced attributes. */ + Vector initial_geometry_sockets; +}; + /** * Utility class to build a lazy-function graph based on a geometry nodes tree. * This is mainly a separate class because it makes it easier to have variables that can be @@ -762,12 +1150,34 @@ struct GeometryNodesLazyFunctionGraphBuilder { Map output_socket_map_; Map multi_input_socket_nodes_; const bke::DataTypeConversions *conversions_; + /** + * Maps bsockets to boolean sockets in the graph whereby each boolean socket indicates whether + * the bsocket is used. Sockets not contained in this map are not used. + * This is indexed by `bNodeSocket::index_in_tree()`. + */ + Array socket_is_used_map_; + /** + * Some built-in nodes get additional boolean inputs that indicate whether certain outputs are + * used (field output sockets that contain new anonymous attribute references). + */ + Vector> output_used_sockets_for_builtin_nodes_; + /** + * Maps from output geometry sockets to corresponding attribute set inputs. + */ + Map attribute_set_propagation_map_; + /** + * Boolean inputs that tell a node if some socket (of the same or another node) is used. If this + * socket is in a link-cycle, its input can become a constant true. + */ + Set socket_usage_inputs_; /** * All group input nodes are combined into one dummy node in the lazy-function graph. */ lf::DummyNode *group_input_lf_node_; + friend class UsedSocketVisualizeOptions; + public: GeometryNodesLazyFunctionGraphBuilder(const bNodeTree &btree, GeometryNodesLazyFunctionGraphInfo &lf_graph_info) @@ -783,12 +1193,28 @@ struct GeometryNodesLazyFunctionGraphBuilder { mapping_ = &lf_graph_info_->mapping; conversions_ = &bke::get_implicit_type_conversions(); + socket_is_used_map_.reinitialize(btree_.all_sockets().size()); + socket_is_used_map_.fill(nullptr); + this->prepare_node_multi_functions(); this->build_group_input_node(); + if (btree_.group_output_node() == nullptr) { + this->build_fallback_output_node(); + } this->handle_nodes(); this->handle_links(); this->add_default_inputs(); + this->build_attribute_propagation_input_node(); + this->build_output_usage_input_node(); + this->build_input_usage_output_node(); + this->build_socket_usages(); + + this->build_attribute_propagation_sets(); + this->fix_link_cycles(); + + // this->print_graph(); + lf_graph_->update_node_indices(); lf_graph_info_->num_inline_nodes_approximate += lf_graph_->nodes().size(); } @@ -818,6 +1244,29 @@ struct GeometryNodesLazyFunctionGraphBuilder { lf_graph_info_->dummy_debug_infos_.append(std::move(debug_info)); } + /** + * Build an output node that just outputs default values in the case when there is no Group + * Output node in the tree. + */ + void build_fallback_output_node() + { + Vector output_cpp_types; + auto debug_info = std::make_unique(); + for (const bNodeSocket *interface_output : btree_.interface_outputs()) { + output_cpp_types.append(interface_output->typeinfo->geometry_nodes_cpp_type); + debug_info->socket_names.append(interface_output->name); + } + + lf::Node &lf_node = lf_graph_->add_dummy(output_cpp_types, {}, debug_info.get()); + for (lf::InputSocket *lf_socket : lf_node.inputs()) { + const CPPType &type = lf_socket->type(); + lf_socket->set_default_value(type.default_value()); + } + mapping_->standard_group_output_sockets = lf_node.inputs(); + + lf_graph_info_->dummy_debug_infos_.append(std::move(debug_info)); + } + void handle_nodes() { /* Insert all nodes into the lazy function graph. */ @@ -935,22 +1384,25 @@ struct GeometryNodesLazyFunctionGraphBuilder { void handle_group_output_node(const bNode &bnode) { Vector output_cpp_types; - const Span interface_outputs = btree_.interface_outputs(); - for (const bNodeSocket *interface_input : interface_outputs) { + auto debug_info = std::make_unique(); + for (const bNodeSocket *interface_input : btree_.interface_outputs()) { output_cpp_types.append(interface_input->typeinfo->geometry_nodes_cpp_type); + debug_info->socket_names.append(interface_input->name); } - auto debug_info = std::make_unique(); lf::DummyNode &group_output_lf_node = lf_graph_->add_dummy( output_cpp_types, {}, debug_info.get()); - for (const int i : interface_outputs.index_range()) { + for (const int i : group_output_lf_node.inputs().index_range()) { const bNodeSocket &bsocket = bnode.input_socket(i); lf::InputSocket &lf_socket = group_output_lf_node.input(i); input_socket_map_.add(&bsocket, &lf_socket); mapping_->dummy_socket_map.add(&bsocket, &lf_socket); mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); - debug_info->socket_names.append(interface_outputs[i]->name); + } + + if (&bnode == btree_.group_output_node()) { + mapping_->standard_group_output_sockets = group_output_lf_node.inputs(); } lf_graph_info_->dummy_debug_infos_.append(std::move(debug_info)); @@ -968,21 +1420,18 @@ struct GeometryNodesLazyFunctionGraphBuilder { return; } - Vector used_inputs; - Vector used_outputs; - auto lazy_function = std::make_unique( - bnode, *group_lf_graph_info, used_inputs, used_outputs); + auto lazy_function = std::make_unique(bnode, *group_lf_graph_info); lf::FunctionNode &lf_node = lf_graph_->add_function(*lazy_function); - lf_graph_info_->functions.append(std::move(lazy_function)); - for (const int i : used_inputs.index_range()) { - const bNodeSocket &bsocket = *used_inputs[i]; + + for (const int i : bnode.input_sockets().index_range()) { + const bNodeSocket &bsocket = bnode.input_socket(i); BLI_assert(!bsocket.is_multi_input()); lf::InputSocket &lf_socket = lf_node.input(i); input_socket_map_.add(&bsocket, &lf_socket); mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); } - for (const int i : used_outputs.index_range()) { - const bNodeSocket &bsocket = *used_outputs[i]; + for (const int i : bnode.output_sockets().index_range()) { + const bNodeSocket &bsocket = bnode.output_socket(i); lf::OutputSocket &lf_socket = lf_node.output(i); output_socket_map_.add_new(&bsocket, &lf_socket); mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); @@ -990,6 +1439,18 @@ struct GeometryNodesLazyFunctionGraphBuilder { mapping_->group_node_map.add(&bnode, &lf_node); lf_graph_info_->num_inline_nodes_approximate += group_lf_graph_info->num_inline_nodes_approximate; + static const bool static_false = false; + for (const int i : lazy_function->lf_input_for_output_bsocket_usage_.values()) { + lf_node.input(i).set_default_value(&static_false); + socket_usage_inputs_.add(&lf_node.input(i)); + } + /* Keep track of attribute set inputs that need to be populated later. */ + for (const auto [output_index, lf_input_index] : + lazy_function->lf_input_for_attribute_propagation_to_output_.items()) { + attribute_set_propagation_map_.add(&bnode.output_socket(output_index), + &lf_node.input(lf_input_index)); + } + lf_graph_info_->functions.append(std::move(lazy_function)); } void handle_geometry_node(const bNode &bnode) @@ -999,7 +1460,6 @@ struct GeometryNodesLazyFunctionGraphBuilder { auto lazy_function = std::make_unique( bnode, used_inputs, used_outputs); lf::Node &lf_node = lf_graph_->add_function(*lazy_function); - lf_graph_info_->functions.append(std::move(lazy_function)); for (const int i : used_inputs.index_range()) { const bNodeSocket &bsocket = *used_inputs[i]; @@ -1026,6 +1486,21 @@ struct GeometryNodesLazyFunctionGraphBuilder { output_socket_map_.add_new(&bsocket, &lf_socket); mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); } + + for (const auto [identifier, lf_input_index] : + lazy_function->lf_input_for_output_bsocket_usage_.items()) { + output_used_sockets_for_builtin_nodes_.append_as(&bnode.output_by_identifier(identifier), + &lf_node.input(lf_input_index)); + socket_usage_inputs_.add_new(&lf_node.input(lf_input_index)); + } + /* Keep track of attribute set inputs that need to be populated later. */ + for (const auto [identifier, lf_input_index] : + lazy_function->lf_input_for_attribute_propagation_to_output_.items()) { + attribute_set_propagation_map_.add(&bnode.output_by_identifier(identifier), + &lf_node.input(lf_input_index)); + } + + lf_graph_info_->functions.append(std::move(lazy_function)); } void handle_multi_function_node(const bNode &bnode, const NodeMultiFunctions::Item &fn_item) @@ -1269,8 +1744,985 @@ struct GeometryNodesLazyFunctionGraphBuilder { lf_graph_->add_link(lf_node.output(0), input_lf_socket); return true; } + + /** + * Every output geometry socket that may propagate attributes has to know which attributes should + * be propagated. Therefore, every one of these outputs gets a corresponding attribute set input. + */ + void build_attribute_propagation_input_node() + { + const aal::RelationsInNode &tree_relations = *btree_.runtime->anonymous_attribute_relations; + Vector output_indices; + for (const aal::PropagateRelation &relation : tree_relations.propagate_relations) { + output_indices.append_non_duplicates(relation.to_geometry_output); + } + Vector cpp_types; + auto debug_info = std::make_unique(); + debug_info->name = "Attributes to Propagate to Output"; + cpp_types.append_n_times(&CPPType::get(), output_indices.size()); + lf::Node &lf_node = lf_graph_->add_dummy({}, cpp_types, debug_info.get()); + for (const int i : output_indices.index_range()) { + const int output_index = output_indices[i]; + mapping_->attribute_set_by_geometry_output.add(output_index, &lf_node.output(i)); + debug_info->output_names.append(btree_.interface_outputs()[output_index]->name); + } + lf_graph_info_->dummy_debug_infos_.append(std::move(debug_info)); + } + + /** + * Build new boolean group inputs that indicate which group outputs are used. + */ + void build_output_usage_input_node() + { + const Span interface_outputs = btree_.interface_outputs(); + + Vector cpp_types; + cpp_types.append_n_times(&CPPType::get(), interface_outputs.size()); + auto debug_info = std::make_unique(); + debug_info->name = "Output Socket Usage"; + lf::Node &lf_node = lf_graph_->add_dummy({}, cpp_types, debug_info.get()); + for (const int i : interface_outputs.index_range()) { + mapping_->group_output_used_sockets.append(&lf_node.output(i)); + debug_info->output_names.append(interface_outputs[i]->name); + } + lf_graph_info_->dummy_debug_infos_.append(std::move(debug_info)); + } + + /** + * Build new boolean group outputs that indicate which group inputs are used depending on other + * group inputs. + */ + void build_input_usage_output_node() + { + const Span interface_inputs = btree_.interface_inputs(); + + Vector cpp_types; + cpp_types.append_n_times(&CPPType::get(), interface_inputs.size()); + auto debug_info = std::make_unique(); + debug_info->name = "Input Socket Usage"; + lf::Node &lf_node = lf_graph_->add_dummy(cpp_types, {}, debug_info.get()); + for (const int i : interface_inputs.index_range()) { + mapping_->group_input_usage_sockets.append(&lf_node.input(i)); + debug_info->input_names.append(interface_inputs[i]->name); + } + lf_graph_info_->dummy_debug_infos_.append(std::move(debug_info)); + } + + /** + * For every socket we want to determine if it will be used depending on the inputs of the node + * group (just static analysis is not enough when there are e.g. Switch nodes). This function + * populates #socket_is_used_map_ with that information. + */ + void build_socket_usages() + { + OrSocketUsagesCache or_socket_usages_cache; + + if (const bNode *group_output_bnode = btree_.group_output_node()) { + /* Whether a group output is used is determined by a group input that has been created + * exactly for this purpose. */ + for (const bNodeSocket *bsocket : group_output_bnode->input_sockets().drop_back(1)) { + const int index = bsocket->index(); + socket_is_used_map_[bsocket->index_in_tree()] = const_cast( + mapping_->group_output_used_sockets[index]); + } + } + + /* Iterate over all nodes from right to left to determine when which sockets are used. */ + for (const bNode *bnode : btree_.toposort_right_to_left()) { + const bNodeType *node_type = bnode->typeinfo; + if (node_type == nullptr) { + /* Ignore. */ + continue; + } + + this->build_output_socket_usages(*bnode, or_socket_usages_cache); + + if (bnode->is_muted()) { + this->build_muted_node_usages(*bnode, or_socket_usages_cache); + continue; + } + + switch (node_type->type) { + case NODE_GROUP_OUTPUT: { + /* Handled before this loop already. */ + break; + } + case NODE_GROUP_INPUT: { + /* Handled after this loop. */ + break; + } + case NODE_FRAME: { + /* Ignored. */ + break; + } + case NODE_REROUTE: { + /* The input is used exactly when the output is used. */ + socket_is_used_map_[bnode->input_socket(0).index_in_tree()] = + socket_is_used_map_[bnode->output_socket(0).index_in_tree()]; + break; + } + case GEO_NODE_SWITCH: { + this->build_switch_node_socket_usage(*bnode); + break; + } + case GEO_NODE_VIEWER: { + this->build_viewer_node_socket_usage(*bnode); + break; + } + case NODE_GROUP: + case NODE_CUSTOM_GROUP: { + this->build_group_node_socket_usage(*bnode, or_socket_usages_cache); + break; + } + default: { + this->build_standard_node_input_socket_usage(*bnode, or_socket_usages_cache); + break; + } + } + } + + this->build_group_input_usages(or_socket_usages_cache); + this->link_output_used_sockets_for_builtin_nodes(); + } + + using OrSocketUsagesCache = Map, lf::OutputSocket *>; + + /** + * Combine multiple socket usages with a logical or. Inserts a new node for that purpose if + * necessary. + */ + lf::OutputSocket *or_socket_usages(MutableSpan usages, + OrSocketUsagesCache &cache) + { + if (usages.is_empty()) { + return nullptr; + } + if (usages.size() == 1) { + return usages[0]; + } + + std::sort(usages.begin(), usages.end()); + return cache.lookup_or_add_cb_as(usages, [&]() { + auto logical_or_fn = std::make_unique(usages.size()); + lf::Node &logical_or_node = lf_graph_->add_function(*logical_or_fn); + lf_graph_info_->functions.append(std::move(logical_or_fn)); + + for (const int i : usages.index_range()) { + lf_graph_->add_link(*usages[i], logical_or_node.input(i)); + } + return &logical_or_node.output(0); + }); + } + + void build_output_socket_usages(const bNode &bnode, OrSocketUsagesCache &or_socket_usages_cache) + { + /* Output sockets are used when any of their linked inputs are used. */ + for (const bNodeSocket *socket : bnode.output_sockets()) { + if (!socket->is_available()) { + continue; + } + /* Determine when linked target sockets are used. */ + Vector target_usages; + for (const bNodeLink *link : socket->directly_linked_links()) { + if (!link->is_used()) { + continue; + } + const bNodeSocket &target_socket = *link->tosock; + if (lf::OutputSocket *is_used_socket = + socket_is_used_map_[target_socket.index_in_tree()]) { + target_usages.append_non_duplicates(is_used_socket); + } + } + /* Combine target socket usages into the usage of the current socket. */ + socket_is_used_map_[socket->index_in_tree()] = this->or_socket_usages( + target_usages, or_socket_usages_cache); + } + } + + /** + * An input of a muted node is used when any of its internally linked outputs is used. + */ + void build_muted_node_usages(const bNode &bnode, OrSocketUsagesCache &or_socket_usages_cache) + { + /* Find all outputs that use a specific input. */ + MultiValueMap outputs_by_input; + for (const bNodeLink *blink : bnode.internal_links()) { + outputs_by_input.add(blink->fromsock, blink->tosock); + } + for (const auto item : outputs_by_input.items()) { + const bNodeSocket &input_bsocket = *item.key; + const Span output_bsockets = item.value; + + /* The input is used if any of the internally linked outputs is used. */ + Vector lf_socket_usages; + for (const bNodeSocket *output_bsocket : output_bsockets) { + if (lf::OutputSocket *lf_socket = socket_is_used_map_[output_bsocket->index_in_tree()]) { + lf_socket_usages.append(lf_socket); + } + } + socket_is_used_map_[input_bsocket.index_in_tree()] = this->or_socket_usages( + lf_socket_usages, or_socket_usages_cache); + } + } + + void build_switch_node_socket_usage(const bNode &bnode) + { + const bNodeSocket *switch_input_bsocket = nullptr; + const bNodeSocket *false_input_bsocket = nullptr; + const bNodeSocket *true_input_bsocket = nullptr; + const bNodeSocket *output_bsocket = nullptr; + for (const bNodeSocket *socket : bnode.input_sockets()) { + if (!socket->is_available()) { + continue; + } + if (socket->name == StringRef("Switch")) { + switch_input_bsocket = socket; + } + else if (socket->name == StringRef("False")) { + false_input_bsocket = socket; + } + else if (socket->name == StringRef("True")) { + true_input_bsocket = socket; + } + } + for (const bNodeSocket *socket : bnode.output_sockets()) { + if (socket->is_available()) { + output_bsocket = socket; + break; + } + } + lf::OutputSocket *output_is_used_socket = socket_is_used_map_[output_bsocket->index_in_tree()]; + if (output_is_used_socket == nullptr) { + return; + } + socket_is_used_map_[switch_input_bsocket->index_in_tree()] = output_is_used_socket; + lf::InputSocket *lf_switch_input = input_socket_map_.lookup(switch_input_bsocket)[0]; + if (lf::OutputSocket *lf_switch_origin = lf_switch_input->origin()) { + /* The condition input is dynamic, so the usage of the other inputs is as well. */ + static const LazyFunctionForSwitchSocketUsage switch_socket_usage_fn; + lf::Node &lf_node = lf_graph_->add_function(switch_socket_usage_fn); + lf_graph_->add_link(*lf_switch_origin, lf_node.input(0)); + socket_is_used_map_[false_input_bsocket->index_in_tree()] = &lf_node.output(0); + socket_is_used_map_[true_input_bsocket->index_in_tree()] = &lf_node.output(1); + } + else { + if (switch_input_bsocket->default_value_typed()->value) { + socket_is_used_map_[true_input_bsocket->index_in_tree()] = output_is_used_socket; + } + else { + socket_is_used_map_[false_input_bsocket->index_in_tree()] = output_is_used_socket; + } + } + } + + void build_viewer_node_socket_usage(const bNode &bnode) + { + const lf::FunctionNode &lf_viewer_node = *mapping_->viewer_node_map.lookup(&bnode); + auto lazy_function = std::make_unique(lf_viewer_node); + lf::Node &lf_node = lf_graph_->add_function(*lazy_function); + lf_graph_info_->functions.append(std::move(lazy_function)); + + for (const bNodeSocket *bsocket : bnode.input_sockets()) { + if (bsocket->is_available()) { + socket_is_used_map_[bsocket->index_in_tree()] = &lf_node.output(0); + } + } + } + + void build_group_node_socket_usage(const bNode &bnode, + OrSocketUsagesCache &or_socket_usages_cache) + { + const bNodeTree *bgroup = reinterpret_cast(bnode.id); + if (bgroup == nullptr) { + return; + } + const GeometryNodesLazyFunctionGraphInfo *group_lf_graph_info = + ensure_geometry_nodes_lazy_function_graph(*bgroup); + if (group_lf_graph_info == nullptr) { + return; + } + lf::FunctionNode &lf_group_node = const_cast( + *mapping_->group_node_map.lookup(&bnode)); + const auto &fn = static_cast(lf_group_node.function()); + + for (const bNodeSocket *input_bsocket : bnode.input_sockets()) { + const int input_index = input_bsocket->index(); + const InputUsageHint &input_usage_hint = + group_lf_graph_info->mapping.group_input_usage_hints[input_index]; + switch (input_usage_hint.type) { + case InputUsageHintType::Never: { + /* Nothing to do. */ + break; + } + case InputUsageHintType::DependsOnOutput: { + Vector output_usages; + for (const int i : input_usage_hint.output_dependencies) { + if (lf::OutputSocket *lf_socket = + socket_is_used_map_[bnode.output_socket(i).index_in_tree()]) { + output_usages.append(lf_socket); + } + } + socket_is_used_map_[input_bsocket->index_in_tree()] = this->or_socket_usages( + output_usages, or_socket_usages_cache); + break; + } + case InputUsageHintType::DynamicSocket: { + socket_is_used_map_[input_bsocket->index_in_tree()] = &const_cast( + lf_group_node.output(fn.lf_output_for_input_bsocket_usage_.lookup(input_index))); + break; + } + } + } + + for (const bNodeSocket *output_bsocket : bnode.output_sockets()) { + const int output_index = output_bsocket->index(); + const int lf_input_index = fn.lf_input_for_output_bsocket_usage_.lookup(output_index); + lf::InputSocket &lf_socket = lf_group_node.input(lf_input_index); + if (lf::OutputSocket *lf_output_is_used = + socket_is_used_map_[output_bsocket->index_in_tree()]) { + lf_graph_->add_link(*lf_output_is_used, lf_socket); + } + else { + static const bool static_false = false; + lf_socket.set_default_value(&static_false); + } + } + } + + void build_standard_node_input_socket_usage(const bNode &bnode, + OrSocketUsagesCache &or_socket_usages_cache) + { + if (bnode.input_sockets().is_empty()) { + return; + } + + Vector output_usages; + for (const bNodeSocket *output_socket : bnode.output_sockets()) { + if (!output_socket->is_available()) { + continue; + } + if (lf::OutputSocket *is_used_socket = socket_is_used_map_[output_socket->index_in_tree()]) { + output_usages.append_non_duplicates(is_used_socket); + } + } + + /* Assume every input is used when any output is used. */ + lf::OutputSocket *lf_usage = this->or_socket_usages(output_usages, or_socket_usages_cache); + if (lf_usage == nullptr) { + return; + } + + for (const bNodeSocket *input_socket : bnode.input_sockets()) { + if (input_socket->is_available()) { + socket_is_used_map_[input_socket->index_in_tree()] = lf_usage; + } + } + } + + void build_group_input_usages(OrSocketUsagesCache &or_socket_usages_cache) + { + const Span group_input_nodes = btree_.group_input_nodes(); + for (const int i : btree_.interface_inputs().index_range()) { + Vector target_usages; + for (const bNode *group_input_node : group_input_nodes) { + if (lf::OutputSocket *lf_socket = + socket_is_used_map_[group_input_node->output_socket(i).index_in_tree()]) { + target_usages.append_non_duplicates(lf_socket); + } + } + + lf::OutputSocket *lf_socket = this->or_socket_usages(target_usages, or_socket_usages_cache); + lf::InputSocket *lf_group_output = const_cast( + mapping_->group_input_usage_sockets[i]); + InputUsageHint input_usage_hint; + if (lf_socket == nullptr) { + static const bool static_false = false; + lf_group_output->set_default_value(&static_false); + input_usage_hint.type = InputUsageHintType::Never; + } + else { + lf_graph_->add_link(*lf_socket, *lf_group_output); + if (lf_socket->node().is_dummy()) { + /* Can support slightly more complex cases where it depends on more than one output in + * the future. */ + input_usage_hint.type = InputUsageHintType::DependsOnOutput; + input_usage_hint.output_dependencies = { + mapping_->group_output_used_sockets.first_index_of(lf_socket)}; + } + else { + input_usage_hint.type = InputUsageHintType::DynamicSocket; + } + } + lf_graph_info_->mapping.group_input_usage_hints.append(std::move(input_usage_hint)); + } + } + + void link_output_used_sockets_for_builtin_nodes() + { + for (const auto &[output_bsocket, lf_input] : output_used_sockets_for_builtin_nodes_) { + if (lf::OutputSocket *lf_is_used = socket_is_used_map_[output_bsocket->index_in_tree()]) { + lf_graph_->add_link(*lf_is_used, *lf_input); + } + else { + static const bool static_false = false; + lf_input->set_default_value(&static_false); + } + } + } + + void build_attribute_propagation_sets() + { + ResourceScope scope; + const Array relations_by_node = + bke::anonymous_attribute_inferencing::get_relations_by_node(btree_, scope); + + VectorSet attribute_reference_keys; + /* Indexed by reference key index. */ + Vector attribute_reference_infos; + this->build_attribute_references( + relations_by_node, attribute_reference_keys, attribute_reference_infos); + + MultiValueMap referenced_by_field_socket; + MultiValueMap propagated_to_geometry_socket; + this->gather_referenced_and_potentially_propagated_data(relations_by_node, + attribute_reference_keys, + attribute_reference_infos, + referenced_by_field_socket, + propagated_to_geometry_socket); + + MultiValueMap required_propagated_to_geometry_socket; + this->gather_required_propagated_data(relations_by_node, + attribute_reference_keys, + referenced_by_field_socket, + propagated_to_geometry_socket, + required_propagated_to_geometry_socket); + + this->build_attribute_sets_to_propagate(attribute_reference_keys, + attribute_reference_infos, + required_propagated_to_geometry_socket); + } + + void build_attribute_references(const Span relations_by_node, + VectorSet &r_attribute_reference_keys, + Vector &r_attribute_reference_infos) + { + auto add_get_attributes_node = [&](lf::OutputSocket &lf_field_socket) -> lf::OutputSocket & { + const ValueOrFieldCPPType &type = *ValueOrFieldCPPType::get_from_self( + lf_field_socket.type()); + auto lazy_function = std::make_unique(type); + lf::Node &lf_node = lf_graph_->add_function(*lazy_function); + lf_graph_->add_link(lf_field_socket, lf_node.input(0)); + lf_graph_info_->functions.append(std::move(lazy_function)); + return lf_node.output(0); + }; + + /* Find nodes that create new anonymous attributes. */ + for (const bNode *node : btree_.all_nodes()) { + const aal::RelationsInNode &relations = *relations_by_node[node->index()]; + for (const aal::AvailableRelation &relation : relations.available_relations) { + const bNodeSocket &geometry_bsocket = node->output_socket(relation.geometry_output); + const bNodeSocket &field_bsocket = node->output_socket(relation.field_output); + if (!field_bsocket.is_available()) { + continue; + } + if (!field_bsocket.is_directly_linked()) { + continue; + } + AttributeReferenceKey key; + key.type = AttributeReferenceKeyType::Socket; + key.bsocket = &field_bsocket; + const int key_index = r_attribute_reference_keys.index_of_or_add(key); + if (key_index >= r_attribute_reference_infos.size()) { + AttributeReferenceInfo info; + lf::OutputSocket &lf_field_socket = *output_socket_map_.lookup(&field_bsocket); + info.lf_attribute_set_socket = &add_get_attributes_node(lf_field_socket); + r_attribute_reference_infos.append(info); + } + AttributeReferenceInfo &info = r_attribute_reference_infos[key_index]; + if (geometry_bsocket.is_available()) { + info.initial_geometry_sockets.append(&geometry_bsocket); + } + } + } + + /* Find field group inputs that are evaluated within this node tree. */ + const aal::RelationsInNode &tree_relations = *btree_.runtime->anonymous_attribute_relations; + for (const aal::EvalRelation &relation : tree_relations.eval_relations) { + AttributeReferenceKey key; + key.type = AttributeReferenceKeyType::InputField; + key.index = relation.field_input; + r_attribute_reference_keys.add_new(key); + AttributeReferenceInfo info; + lf::OutputSocket &lf_field_socket = *const_cast( + mapping_->group_input_sockets[relation.field_input]); + info.lf_attribute_set_socket = &add_get_attributes_node(lf_field_socket); + for (const bNode *bnode : btree_.group_input_nodes()) { + info.initial_geometry_sockets.append(&bnode->output_socket(relation.geometry_input)); + } + r_attribute_reference_infos.append(std::move(info)); + } + /* Find group outputs that attributes need to be propagated to. */ + for (const aal::PropagateRelation &relation : tree_relations.propagate_relations) { + AttributeReferenceKey key; + key.type = AttributeReferenceKeyType::OutputGeometry; + key.index = relation.to_geometry_output; + const int key_index = r_attribute_reference_keys.index_of_or_add(key); + if (key_index >= r_attribute_reference_infos.size()) { + AttributeReferenceInfo info; + info.lf_attribute_set_socket = const_cast( + mapping_->attribute_set_by_geometry_output.lookup(relation.to_geometry_output)); + r_attribute_reference_infos.append(info); + } + AttributeReferenceInfo &info = r_attribute_reference_infos[key_index]; + for (const bNode *bnode : btree_.group_input_nodes()) { + info.initial_geometry_sockets.append(&bnode->output_socket(relation.from_geometry_input)); + } + } + } + + /** + * For every field socket, figure out which anonymous attributes it may reference. + * For every geometry socket, figure out which anonymous attributes may be propagated to it. + */ + void gather_referenced_and_potentially_propagated_data( + const Span relations_by_node, + const Span attribute_reference_keys, + const Span attribute_reference_infos, + MultiValueMap &r_referenced_by_field_socket, + MultiValueMap &r_propagated_to_geometry_socket) + { + /* Initialize maps. */ + for (const int key_index : attribute_reference_keys.index_range()) { + const AttributeReferenceKey &key = attribute_reference_keys[key_index]; + const AttributeReferenceInfo &info = attribute_reference_infos[key_index]; + switch (key.type) { + case AttributeReferenceKeyType::InputField: { + for (const bNode *bnode : btree_.group_input_nodes()) { + const bNodeSocket &bsocket = bnode->output_socket(key.index); + r_referenced_by_field_socket.add(&bsocket, key_index); + } + break; + } + case AttributeReferenceKeyType::OutputGeometry: { + break; + } + case AttributeReferenceKeyType::Socket: { + r_referenced_by_field_socket.add(key.bsocket, key_index); + break; + } + } + for (const bNodeSocket *geometry_bsocket : info.initial_geometry_sockets) { + r_propagated_to_geometry_socket.add(geometry_bsocket, key_index); + } + } + /* Propagate attribute usages from left to right. */ + for (const bNode *bnode : btree_.toposort_left_to_right()) { + for (const bNodeSocket *bsocket : bnode->input_sockets()) { + if (bsocket->is_available()) { + Vector referenced_keys; + Vector propagated_keys; + for (const bNodeLink *blink : bsocket->directly_linked_links()) { + if (blink->is_used()) { + referenced_keys.extend_non_duplicates( + r_referenced_by_field_socket.lookup(blink->fromsock)); + propagated_keys.extend_non_duplicates( + r_propagated_to_geometry_socket.lookup(blink->fromsock)); + } + } + if (!referenced_keys.is_empty()) { + r_referenced_by_field_socket.add_multiple(bsocket, referenced_keys); + } + if (!propagated_keys.is_empty()) { + r_propagated_to_geometry_socket.add_multiple(bsocket, propagated_keys); + } + } + } + const aal::RelationsInNode &relations = *relations_by_node[bnode->index()]; + for (const aal::ReferenceRelation &relation : relations.reference_relations) { + const bNodeSocket &input_bsocket = bnode->input_socket(relation.from_field_input); + const bNodeSocket &output_bsocket = bnode->output_socket(relation.to_field_output); + if (!input_bsocket.is_available() || !output_bsocket.is_available()) { + continue; + } + r_referenced_by_field_socket.add_multiple( + &output_bsocket, Vector(r_referenced_by_field_socket.lookup(&input_bsocket))); + } + for (const aal::PropagateRelation &relation : relations.propagate_relations) { + const bNodeSocket &input_bsocket = bnode->input_socket(relation.from_geometry_input); + const bNodeSocket &output_bsocket = bnode->output_socket(relation.to_geometry_output); + if (!input_bsocket.is_available() || !output_bsocket.is_available()) { + continue; + } + r_propagated_to_geometry_socket.add_multiple( + &output_bsocket, Vector(r_propagated_to_geometry_socket.lookup(&input_bsocket))); + } + } + } + + /** + * Determines which anonymous attributes should be propagated to which geometry sockets. + */ + void gather_required_propagated_data( + const Span relations_by_node, + const VectorSet &attribute_reference_keys, + const MultiValueMap &referenced_by_field_socket, + const MultiValueMap &propagated_to_geometry_socket, + MultiValueMap &r_required_propagated_to_geometry_socket) + { + const aal::RelationsInNode &tree_relations = *btree_.runtime->anonymous_attribute_relations; + MultiValueMap required_by_geometry_socket; + + /* Initialize required attributes at group output. */ + if (const bNode *group_output_bnode = btree_.group_output_node()) { + for (const aal::PropagateRelation &relation : tree_relations.propagate_relations) { + AttributeReferenceKey key; + key.type = AttributeReferenceKeyType::OutputGeometry; + key.index = relation.to_geometry_output; + const int key_index = attribute_reference_keys.index_of(key); + required_by_geometry_socket.add( + &group_output_bnode->input_socket(relation.to_geometry_output), key_index); + } + for (const aal::AvailableRelation &relation : tree_relations.available_relations) { + const bNodeSocket &geometry_bsocket = group_output_bnode->input_socket( + relation.geometry_output); + const bNodeSocket &field_bsocket = group_output_bnode->input_socket(relation.field_output); + required_by_geometry_socket.add_multiple( + &geometry_bsocket, referenced_by_field_socket.lookup(&field_bsocket)); + } + } + + /* Propagate attribute usages from right to left. */ + for (const bNode *bnode : btree_.toposort_right_to_left()) { + const aal::RelationsInNode &relations = *relations_by_node[bnode->index()]; + for (const bNodeSocket *bsocket : bnode->output_sockets()) { + if (!bsocket->is_available()) { + continue; + } + Vector required_attributes; + for (const bNodeLink *blink : bsocket->directly_linked_links()) { + if (blink->is_used()) { + const bNodeSocket &to_socket = *blink->tosock; + required_attributes.extend_non_duplicates( + required_by_geometry_socket.lookup(&to_socket)); + } + } + const Span available_attributes = propagated_to_geometry_socket.lookup(bsocket); + for (const int key_index : required_attributes) { + if (available_attributes.contains(key_index)) { + required_by_geometry_socket.add(bsocket, key_index); + + const AttributeReferenceKey &key = attribute_reference_keys[key_index]; + if (key.type != AttributeReferenceKeyType::Socket || + &key.bsocket->owner_node() != bnode) { + r_required_propagated_to_geometry_socket.add(bsocket, key_index); + } + } + } + } + + for (const bNodeSocket *bsocket : bnode->input_sockets()) { + if (!bsocket->is_available()) { + continue; + } + Vector required_attributes; + for (const aal::PropagateRelation &relation : relations.propagate_relations) { + if (relation.from_geometry_input == bsocket->index()) { + const bNodeSocket &output_bsocket = bnode->output_socket(relation.to_geometry_output); + required_attributes.extend_non_duplicates( + required_by_geometry_socket.lookup(&output_bsocket)); + } + } + for (const aal::EvalRelation &relation : relations.eval_relations) { + if (relation.geometry_input == bsocket->index()) { + const bNodeSocket &field_bsocket = bnode->input_socket(relation.field_input); + if (field_bsocket.is_available()) { + required_attributes.extend_non_duplicates( + referenced_by_field_socket.lookup(&field_bsocket)); + } + } + } + const Span available_attributes = propagated_to_geometry_socket.lookup(bsocket); + for (const int key_index : required_attributes) { + if (available_attributes.contains(key_index)) { + required_by_geometry_socket.add(bsocket, key_index); + } + } + } + } + } + + /** + * For every node that propagates attributes, prepare an attribute set containing information + * about which attributes should be propagated. + */ + void build_attribute_sets_to_propagate( + const Span attribute_reference_keys, + const Span attribute_reference_infos, + const MultiValueMap &required_propagated_to_geometry_socket) + { + JoinAttibuteSetsCache join_attribute_sets_cache; + + for (const auto [geometry_output_bsocket, lf_attribute_set_input] : + attribute_set_propagation_map_.items()) { + const Span required = required_propagated_to_geometry_socket.lookup( + geometry_output_bsocket); + + Vector attribute_set_sockets; + Vector used_sockets; + + for (const int i : required.index_range()) { + const int key_index = required[i]; + const AttributeReferenceKey &key = attribute_reference_keys[key_index]; + const AttributeReferenceInfo &info = attribute_reference_infos[key_index]; + lf::OutputSocket *lf_socket_usage = nullptr; + switch (key.type) { + case AttributeReferenceKeyType::InputField: { + lf_socket_usage = const_cast( + mapping_->group_input_usage_sockets[key.index]) + ->origin(); + break; + } + case AttributeReferenceKeyType::OutputGeometry: { + lf_socket_usage = const_cast( + mapping_->group_output_used_sockets[key.index]); + break; + } + case AttributeReferenceKeyType::Socket: { + lf_socket_usage = socket_is_used_map_[key.bsocket->index_in_tree()]; + break; + } + } + if (lf_socket_usage) { + attribute_set_sockets.append(info.lf_attribute_set_socket); + used_sockets.append(lf_socket_usage); + } + } + if (lf::OutputSocket *joined_attribute_set = this->join_attribute_sets( + attribute_set_sockets, used_sockets, join_attribute_sets_cache)) { + lf_graph_->add_link(*joined_attribute_set, *lf_attribute_set_input); + } + else { + static const bke::AnonymousAttributeSet empty_set; + lf_attribute_set_input->set_default_value(&empty_set); + } + } + } + + using JoinAttibuteSetsCache = Map, lf::OutputSocket *>; + + /** + * Join multiple attributes set into a single attribute set that can be passed into a node. + */ + lf::OutputSocket *join_attribute_sets(const Span attribute_set_sockets, + const Span used_sockets, + JoinAttibuteSetsCache &cache) + { + BLI_assert(attribute_set_sockets.size() == used_sockets.size()); + if (attribute_set_sockets.is_empty()) { + return nullptr; + } + if (attribute_set_sockets.size() == 1) { + return attribute_set_sockets[0]; + } + + Vector key; + key.extend(attribute_set_sockets); + key.extend(used_sockets); + std::sort(key.begin(), key.end()); + return cache.lookup_or_add_cb(key, [&]() { + auto lazy_function = std::make_unique( + attribute_set_sockets.size()); + lf::Node &lf_node = lf_graph_->add_function(*lazy_function); + for (const int i : attribute_set_sockets.index_range()) { + lf::InputSocket &lf_use_input = lf_node.input(lazy_function->get_use_input(i)); + socket_usage_inputs_.add(&lf_use_input); + lf::InputSocket &lf_attributes_input = lf_node.input( + lazy_function->get_attribute_set_input(i)); + lf_graph_->add_link(*used_sockets[i], lf_use_input); + lf_graph_->add_link(*attribute_set_sockets[i], lf_attributes_input); + } + lf_graph_info_->functions.append(std::move(lazy_function)); + return &lf_node.output(0); + }); + } + + /** + * By depending on "the future" (whether a specific socket is used in the future), it is possible + * to introduce cycles in the graph. This function finds those cycles and breaks them by removing + * specific links. + * + * Example for a cycle: There is a `Distribute Points on Faces` node and its `Normal` output is + * only used when the number of generated points is larger than 1000 because of some switch node + * later in the tree. In this case, to know whether the `Normal` output is needed, one first has + * to compute the points, but for that one has to know whether the normal information has to be + * added to the points. The fix is to always add the normal information in this case. + */ + void fix_link_cycles() + { + lf_graph_->update_socket_indices(); + const int sockets_num = lf_graph_->socket_num(); + + struct SocketState { + bool done = false; + bool in_stack = false; + }; + + Array socket_states(sockets_num); + + Stack lf_sockets_to_check; + for (lf::Node *lf_node : lf_graph_->nodes()) { + if (lf_node->is_function()) { + for (lf::OutputSocket *lf_socket : lf_node->outputs()) { + if (lf_socket->targets().is_empty()) { + lf_sockets_to_check.push(lf_socket); + } + } + } + if (lf_node->outputs().is_empty()) { + for (lf::InputSocket *lf_socket : lf_node->inputs()) { + lf_sockets_to_check.push(lf_socket); + } + } + } + Vector lf_socket_stack; + while (!lf_sockets_to_check.is_empty()) { + lf::Socket *lf_inout_socket = lf_sockets_to_check.peek(); + lf::Node &lf_node = lf_inout_socket->node(); + SocketState &state = socket_states[lf_inout_socket->index_in_graph()]; + lf_socket_stack.append(lf_inout_socket); + state.in_stack = true; + + Vector lf_origin_sockets; + if (lf_inout_socket->is_input()) { + lf::InputSocket &lf_input_socket = lf_inout_socket->as_input(); + if (lf::OutputSocket *lf_origin_socket = lf_input_socket.origin()) { + lf_origin_sockets.append(lf_origin_socket); + } + } + else { + lf::OutputSocket &lf_output_socket = lf_inout_socket->as_output(); + if (lf_node.is_function()) { + lf::FunctionNode &lf_function_node = static_cast(lf_node); + const lf::LazyFunction &fn = lf_function_node.function(); + fn.possible_output_dependencies( + lf_output_socket.index(), [&](const Span input_indices) { + for (const int input_index : input_indices) { + lf_origin_sockets.append(&lf_node.input(input_index)); + } + }); + } + } + + bool pushed_socket = false; + for (lf::Socket *lf_origin_socket : lf_origin_sockets) { + if (socket_states[lf_origin_socket->index_in_graph()].in_stack) { + const Span cycle = lf_socket_stack.as_span().drop_front( + lf_socket_stack.first_index_of(lf_origin_socket)); + + bool broke_cycle = false; + for (lf::Socket *lf_cycle_socket : cycle) { + if (lf_cycle_socket->is_input() && + socket_usage_inputs_.contains(&lf_cycle_socket->as_input())) { + lf::InputSocket &lf_cycle_input_socket = lf_cycle_socket->as_input(); + lf_graph_->clear_origin(lf_cycle_input_socket); + static const bool static_true = true; + lf_cycle_input_socket.set_default_value(&static_true); + broke_cycle = true; + } + } + if (!broke_cycle) { + BLI_assert_unreachable(); + } + } + else if (!socket_states[lf_origin_socket->index_in_graph()].done) { + lf_sockets_to_check.push(lf_origin_socket); + pushed_socket = true; + } + } + if (pushed_socket) { + continue; + } + + state.done = true; + state.in_stack = false; + lf_sockets_to_check.pop(); + lf_socket_stack.pop_last(); + } + } + + void print_graph(); }; +class UsedSocketVisualizeOptions : public lf::Graph::ToDotOptions { + private: + const GeometryNodesLazyFunctionGraphBuilder &builder_; + Map socket_font_colors_; + Map socket_name_suffixes_; + + public: + UsedSocketVisualizeOptions(const GeometryNodesLazyFunctionGraphBuilder &builder) + : builder_(builder) + { + VectorSet found; + for (const int bsocket_index : builder_.socket_is_used_map_.index_range()) { + const bNodeSocket *bsocket = builder_.btree_.all_sockets()[bsocket_index]; + lf::OutputSocket *lf_used_socket = builder_.socket_is_used_map_[bsocket_index]; + if (lf_used_socket == nullptr) { + continue; + } + const float hue = BLI_hash_int_01(uintptr_t(lf_used_socket)); + std::stringstream ss; + ss.precision(3); + ss << hue << " 0.9 0.5"; + const std::string color_str = ss.str(); + const std::string suffix = " (" + std::to_string(found.index_of_or_add(lf_used_socket)) + + ")"; + socket_font_colors_.add(lf_used_socket, color_str); + socket_name_suffixes_.add(lf_used_socket, suffix); + + if (bsocket->is_input()) { + for (const lf::InputSocket *lf_socket : builder_.input_socket_map_.lookup(bsocket)) { + socket_font_colors_.add(lf_socket, color_str); + socket_name_suffixes_.add(lf_socket, suffix); + } + } + else if (lf::OutputSocket *lf_socket = builder_.output_socket_map_.lookup(bsocket)) { + socket_font_colors_.add(lf_socket, color_str); + socket_name_suffixes_.add(lf_socket, suffix); + } + } + } + + std::optional socket_font_color(const lf::Socket &socket) const override + { + if (const std::string *color = socket_font_colors_.lookup_ptr(&socket)) { + return *color; + } + return std::nullopt; + } + + std::string socket_name(const lf::Socket &socket) const override + { + return socket.name() + socket_name_suffixes_.lookup_default(&socket, ""); + } + + void add_edge_attributes(const lf::OutputSocket & /*from*/, + const lf::InputSocket &to, + dot::DirectedEdge &dot_edge) const + { + if (builder_.socket_usage_inputs_.contains_as(&to)) { + // dot_edge.attributes.set("constraint", "false"); + dot_edge.attributes.set("color", "#00000055"); + } + } +}; + +void GeometryNodesLazyFunctionGraphBuilder::print_graph() +{ + UsedSocketVisualizeOptions options{*this}; + std::cout << "\n\n" << lf_graph_->to_dot(options) << "\n\n"; +} + const GeometryNodesLazyFunctionGraphInfo *ensure_geometry_nodes_lazy_function_graph( const bNodeTree &btree) { diff --git a/source/blender/nodes/intern/geometry_nodes_log.cc b/source/blender/nodes/intern/geometry_nodes_log.cc index 660f878c1f7..66ccab2f77f 100644 --- a/source/blender/nodes/intern/geometry_nodes_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_log.cc @@ -70,7 +70,7 @@ GeometryInfoLog::GeometryInfoLog(const GeometrySet &geometry_set) [&](const bke::AttributeIDRef &attribute_id, const bke::AttributeMetaData &meta_data, const GeometryComponent & /*component*/) { - if (attribute_id.is_named() && names.add(attribute_id.name())) { + if (!attribute_id.is_anonymous() && names.add(attribute_id.name())) { this->attributes.append({attribute_id.name(), meta_data.domain, meta_data.data_type}); } }); diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index 868b3b2c539..c338ecacbc9 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -9,10 +9,33 @@ #include "NOD_geometry_exec.hh" +#include "BLI_hash_md5.h" + #include "node_geometry_util.hh" namespace blender::nodes { +NodeAnonymousAttributeID::NodeAnonymousAttributeID(const Object &object, + const ComputeContext &compute_context, + const bNode &bnode, + const StringRef identifier) +{ + const ComputeContextHash &hash = compute_context.hash(); + { + std::stringstream ss; + ss << hash << "_" << object.id.name << "_" << bnode.identifier << "_" << identifier; + long_name_ = ss.str(); + } + { + uint64_t hash_result[2]; + BLI_hash_md5_buffer(long_name_.data(), long_name_.size(), hash_result); + std::stringstream ss; + ss << ".a_" << std::hex << hash_result[0] << hash_result[1]; + name_ = ss.str(); + BLI_assert(name_.size() < MAX_CUSTOMDATA_LAYER_NAME); + } +} + void GeoNodeExecParams::error_message_add(const NodeWarningType type, const StringRef message) const {