diff options
Diffstat (limited to 'source/blender')
5 files changed, 145 insertions, 19 deletions
diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 2ce8ce5749f..d94b2e7902b 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -186,7 +186,7 @@ class GeometryComponent { const CustomDataType data_type); blender::Set<std::string> attribute_names() const; - void attribute_foreach(const AttributeForeachCallback callback) const; + bool attribute_foreach(const AttributeForeachCallback callback) const; virtual bool is_empty() const; diff --git a/source/blender/blenkernel/BKE_geometry_set_instances.hh b/source/blender/blenkernel/BKE_geometry_set_instances.hh index c0d9c3f74ec..25876296a47 100644 --- a/source/blender/blenkernel/BKE_geometry_set_instances.hh +++ b/source/blender/blenkernel/BKE_geometry_set_instances.hh @@ -39,6 +39,10 @@ struct GeometryInstanceGroup { Vector<float4x4> transforms; }; +void geometry_set_instances_attribute_foreach(const GeometrySet &geometry_set, + const AttributeForeachCallback callback, + const int limit); + void geometry_set_gather_instances(const GeometrySet &geometry_set, Vector<GeometryInstanceGroup> &r_instance_groups); diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index 52f89ca302b..5bd3b990a63 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -822,12 +822,16 @@ Set<std::string> GeometryComponent::attribute_names() const return attributes; } -void GeometryComponent::attribute_foreach(const AttributeForeachCallback callback) const +/** + * \return False if the callback explicitly returned false at any point, otherwise true, + * meaning the callback made it all the way through. + */ +bool GeometryComponent::attribute_foreach(const AttributeForeachCallback callback) const { using namespace blender::bke; const ComponentAttributeProviders *providers = this->get_attribute_providers(); if (providers == nullptr) { - return; + return true; } /* Keep track handled attribute names to make sure that we do not return the same name twice. */ @@ -838,7 +842,7 @@ void GeometryComponent::attribute_foreach(const AttributeForeachCallback callbac if (provider->exists(*this)) { AttributeMetaData meta_data{provider->domain(), provider->data_type()}; if (!callback(provider->name(), meta_data)) { - return; + return false; } handled_attribute_names.add_new(provider->name()); } @@ -852,9 +856,11 @@ void GeometryComponent::attribute_foreach(const AttributeForeachCallback callbac return true; }); if (!continue_loop) { - return; + return false; } } + + return true; } bool GeometryComponent::attribute_exists(const blender::StringRef attribute_name) const diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index 1a45eac8cab..baeed4fc3bc 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -162,6 +162,122 @@ void geometry_set_gather_instances(const GeometrySet &geometry_set, geometry_set_collect_recursive(geometry_set, unit_transform, r_instance_groups); } +static bool collection_instance_attribute_foreach(const Collection &collection, + const AttributeForeachCallback callback, + const int limit, + int &count); + +static bool instances_attribute_foreach_recursive(const GeometrySet &geometry_set, + const AttributeForeachCallback callback, + const int limit, + int &count); + +static bool object_instance_attribute_foreach(const Object &object, + const AttributeForeachCallback callback, + const int limit, + int &count) +{ + GeometrySet instance_geometry_set = object_get_geometry_set_for_read(object); + if (!instances_attribute_foreach_recursive(instance_geometry_set, callback, limit, count)) { + return false; + } + + if (object.type == OB_EMPTY) { + const Collection *collection_instance = object.instance_collection; + if (collection_instance != nullptr) { + if (!collection_instance_attribute_foreach(*collection_instance, callback, limit, count)) { + return false; + } + } + } + return true; +} + +static bool collection_instance_attribute_foreach(const Collection &collection, + const AttributeForeachCallback callback, + const int limit, + int &count) +{ + LISTBASE_FOREACH (const CollectionObject *, collection_object, &collection.gobject) { + BLI_assert(collection_object->ob != nullptr); + const Object &object = *collection_object->ob; + if (!object_instance_attribute_foreach(object, callback, limit, count)) { + return false; + } + } + LISTBASE_FOREACH (const CollectionChild *, collection_child, &collection.children) { + BLI_assert(collection_child->collection != nullptr); + const Collection &collection = *collection_child->collection; + if (!collection_instance_attribute_foreach(collection, callback, limit, count)) { + return false; + } + } + return true; +} + +/** + * \return True if the recursive iteration should continue, false if the limit is reached or the + * callback has returned false indicating it should stop. + */ +static bool instances_attribute_foreach_recursive(const GeometrySet &geometry_set, + const AttributeForeachCallback callback, + const int limit, + int &count) +{ + for (const GeometryComponent *component : geometry_set.get_components_for_read()) { + if (!component->attribute_foreach(callback)) { + return false; + } + } + + /* Now that this this geometry set is visited, increase the count and check with the limit. */ + if (limit > 0 && count++ > limit) { + return false; + } + + const InstancesComponent *instances_component = + geometry_set.get_component_for_read<InstancesComponent>(); + if (instances_component == nullptr) { + return true; + } + + for (const InstancedData &data : instances_component->instanced_data()) { + if (data.type == INSTANCE_DATA_TYPE_OBJECT) { + BLI_assert(data.data.object != nullptr); + const Object &object = *data.data.object; + if (!object_instance_attribute_foreach(object, callback, limit, count)) { + return false; + } + } + else if (data.type == INSTANCE_DATA_TYPE_COLLECTION) { + BLI_assert(data.data.collection != nullptr); + const Collection &collection = *data.data.collection; + if (!collection_instance_attribute_foreach(collection, callback, limit, count)) { + return false; + } + } + } + + return true; +} + +/** + * Call the callback on all of this geometry set's components, including geometry sets from + * instances and recursive instances. This is necessary to access available attributes without + * making all of the set's geometry real. + * + * \param limit: The total number of geometry sets to visit before returning early. This is used + * to avoid looking through too many geometry sets recursively, as an explicit tradeoff in favor + * of performance at the cost of visiting every unique attribute. + */ +void geometry_set_instances_attribute_foreach(const GeometrySet &geometry_set, + const AttributeForeachCallback callback, + const int limit) +{ + int count = 0; + instances_attribute_foreach_recursive(geometry_set, callback, limit, count); +} + void geometry_set_gather_instances_attribute_info(Span<GeometryInstanceGroup> set_groups, Span<GeometryComponentType> component_types, const Set<std::string> &ignored_attributes, diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index c7d822e5418..7a2f8640202 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -47,6 +47,7 @@ #include "DNA_windowmanager_types.h" #include "BKE_customdata.h" +#include "BKE_geometry_set_instances.hh" #include "BKE_global.h" #include "BKE_idprop.h" #include "BKE_lib_query.h" @@ -489,20 +490,19 @@ class GeometryNodesEvaluator { const NodeTreeEvaluationContext context(*self_object_, *modifier_); const GeometrySet &geometry_set = params.get_input<GeometrySet>(socket_ref->identifier()); - const Vector<const GeometryComponent *> components = geometry_set.get_components_for_read(); - - for (const GeometryComponent *component : components) { - component->attribute_foreach( - [&](StringRefNull attribute_name, const AttributeMetaData &meta_data) { - BKE_nodetree_attribute_hint_add(*btree_original, - context, - *node->bnode(), - attribute_name, - meta_data.domain, - meta_data.data_type); - return true; - }); - } + + blender::bke::geometry_set_instances_attribute_foreach( + geometry_set, + [&](StringRefNull attribute_name, const AttributeMetaData &meta_data) { + BKE_nodetree_attribute_hint_add(*btree_original, + context, + *node->bnode(), + attribute_name, + meta_data.domain, + meta_data.data_type); + return true; + }, + 8); } } |