[Bf-blender-cvs] [1ec9ac20165] master: Geometry Nodes: Support instances in attribute search

Hans Goudey noreply at git.blender.org
Thu Apr 8 19:19:18 CEST 2021


Commit: 1ec9ac201652be6031379e39e98e7dd9dc4a2375
Author: Hans Goudey
Date:   Thu Apr 8 12:19:09 2021 -0500
Branches: master
https://developer.blender.org/rB1ec9ac201652be6031379e39e98e7dd9dc4a2375

Geometry Nodes: Support instances in attribute search

Previously only attributes of "real" geometry were displayed in
attribute search. This commit adds code to look through attributes
on instances and add those to the search drop-down too.

This required implementing the same sort of recursive traversal as
the realize instances code. The situation is a bit different though,
this can return early and doesn't need to keep track of transforms.

I added a limit so that it doesn't look through the attributes of
too many instanced geometry sets. I think this is important, since
this isn't a trivial operation and it could potentially happen for
every node in a large node tree. Currently the limit is set at 8
geometry sets, which I expect will be enough, since the set of
attributes is mostly not very unique anyway.

Fixes T86282

Diffrential Revision: https://developer.blender.org/D10919

===================================================================

M	source/blender/blenkernel/BKE_geometry_set.hh
M	source/blender/blenkernel/BKE_geometry_set_instances.hh
M	source/blender/blenkernel/intern/attribute_access.cc
M	source/blender/blenkernel/intern/geometry_set_instances.cc
M	source/blender/modifiers/intern/MOD_nodes.cc

===================================================================

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,

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list