[Bf-blender-cvs] [f9a805164a9] master: Outliner, Library Overrides: List child objects under parents

Julian Eisel noreply at git.blender.org
Thu Jul 7 17:20:14 CEST 2022


Commit: f9a805164a944c3b5762f07e535536b9f425651f
Author: Julian Eisel
Date:   Thu Jul 7 17:17:33 2022 +0200
Branches: master
https://developer.blender.org/rBf9a805164a944c3b5762f07e535536b9f425651f

Outliner, Library Overrides: List child objects under parents

Because children point to, or "use" their parent, the Library Overrides
Hierarchies mode in the Outliner would show parents contained in children, not
children contained in a parent. See D15339 for pictures.

In production files this would make the rig listed under all its children, so
it would appear many times in the tree. Now it appears once and the children
are collected under it.

Refactors the tree building, so instead of using
`BKE_library_foreach_ID_link()`, it now uses the ID relations mapping in
`MainIDRelations`.

Reviewed By: mont29

Differential Revision: https://developer.blender.org/D15339

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

M	source/blender/editors/space_outliner/tree/tree_display.hh
M	source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc

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

diff --git a/source/blender/editors/space_outliner/tree/tree_display.hh b/source/blender/editors/space_outliner/tree/tree_display.hh
index 190e35c81d6..f8e35655c26 100644
--- a/source/blender/editors/space_outliner/tree/tree_display.hh
+++ b/source/blender/editors/space_outliner/tree/tree_display.hh
@@ -161,7 +161,6 @@ class TreeDisplayOverrideLibraryHierarchies final : public AbstractTreeDisplay {
   ListBase build_hierarchy_for_lib_or_main(Main *bmain,
                                            TreeElement &parent_te,
                                            Library *lib = nullptr);
-  void build_hierarchy_for_ID(Main *bmain, ID &override_root_id, TreeElementID &te_id) const;
 };
 
 /* -------------------------------------------------------------------- */
diff --git a/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc b/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc
index 67798e978ab..f8705c3f0ad 100644
--- a/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc
+++ b/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc
@@ -4,25 +4,23 @@
  * \ingroup spoutliner
  */
 
-#include "DNA_ID.h"
-#include "DNA_collection_types.h"
 #include "DNA_key_types.h"
 #include "DNA_space_types.h"
 
-#include "BLI_listbase.h"
+#include "BLI_function_ref.hh"
+#include "BLI_ghash.h"
 #include "BLI_map.hh"
+
 #include "BLI_set.hh"
 
 #include "BLT_translation.h"
 
-#include "BKE_collection.h"
 #include "BKE_lib_query.h"
 #include "BKE_main.h"
 
 #include "../outliner_intern.hh"
 #include "common.hh"
 #include "tree_display.hh"
-#include "tree_element_id.hh"
 
 namespace blender::ed::outliner {
 
@@ -42,8 +40,8 @@ ListBase TreeDisplayOverrideLibraryHierarchies::buildTree(const TreeSourceData &
   TreeElement *current_file_te = outliner_add_element(
       &space_outliner_, &tree, source_data.bmain, nullptr, TSE_ID_BASE, -1);
   current_file_te->name = IFACE_("Current File");
+  AbstractTreeElement::uncollapse_by_default(current_file_te);
   {
-    AbstractTreeElement::uncollapse_by_default(current_file_te);
     build_hierarchy_for_lib_or_main(source_data.bmain, *current_file_te);
 
     /* Add dummy child if there's nothing to display. */
@@ -76,11 +74,49 @@ ListBase TreeDisplayOverrideLibraryHierarchies::buildTree(const TreeSourceData &
   return tree;
 }
 
+/* -------------------------------------------------------------------- */
+/** \name Library override hierarchy building
+ * \{ */
+
+class OverrideIDHierarchyBuilder {
+  SpaceOutliner &space_outliner_;
+  MainIDRelations &id_relations_;
+
+  struct HierarchyBuildData {
+    const ID &override_root_id_;
+    /* The ancestor IDs leading to the current ID, to avoid IDs recursing into themselves. Changes
+     * with every level of recursion. */
+    Set<const ID *> parent_ids{};
+    /* The IDs that were already added to #parent_te, to avoid duplicates. Entirely new set with
+     * every level of recursion. */
+    Set<const ID *> sibling_ids{};
+  };
+
+ public:
+  OverrideIDHierarchyBuilder(SpaceOutliner &space_outliner, MainIDRelations &id_relations)
+      : space_outliner_(space_outliner), id_relations_(id_relations)
+  {
+  }
+
+  void build_hierarchy_for_ID(ID &root_id, TreeElement &te_to_expand);
+
+ private:
+  void build_hierarchy_for_ID_recursive(const ID &parent_id,
+                                        HierarchyBuildData &build_data,
+                                        TreeElement &te_to_expand);
+};
+
 ListBase TreeDisplayOverrideLibraryHierarchies::build_hierarchy_for_lib_or_main(
     Main *bmain, TreeElement &parent_te, Library *lib)
 {
   ListBase tree = {nullptr};
 
+  /* Ensure #Main.relations contains the latest mapping of relations. Must be freed before
+   * returning. */
+  BKE_main_relations_create(bmain, 0);
+
+  OverrideIDHierarchyBuilder builder(space_outliner_, *bmain->relations);
+
   /* Keep track over which ID base elements were already added, and expand them once added. */
   Map<ID_Type, TreeElement *> id_base_te_map;
   /* Index for the ID base elements ("Objects", "Materials", etc). */
@@ -109,108 +145,174 @@ ListBase TreeDisplayOverrideLibraryHierarchies::build_hierarchy_for_lib_or_main(
     TreeElement *new_id_te = outliner_add_element(
         &space_outliner_, &new_base_te->subtree, iter_id, new_base_te, TSE_SOME_ID, 0, false);
 
-    build_hierarchy_for_ID(bmain, *iter_id, *tree_element_cast<TreeElementID>(new_id_te));
+    builder.build_hierarchy_for_ID(*iter_id, *new_id_te);
   }
   FOREACH_MAIN_ID_END;
 
+  BKE_main_relations_free(bmain);
+
   return tree;
 }
 
-struct BuildHierarchyForeachIDCbData {
-  /* Don't allow copies, the sets below would need deep copying. */
-  BuildHierarchyForeachIDCbData(const BuildHierarchyForeachIDCbData &) = delete;
-
-  Main &bmain;
-  SpaceOutliner &space_outliner;
-  ID &override_root_id;
-
-  /* The tree element to expand. Changes with every level of recursion. */
-  TreeElementID *parent_te;
-  /* The ancestor IDs leading to the current ID, to avoid IDs recursing into themselves. Changes
-   * with every level of recursion. */
-  Set<ID *> parent_ids{};
-  /* The IDs that were already added to #parent_te, to avoid duplicates. Entirely new set with
-   * every level of recursion. */
-  Set<ID *> sibling_ids{};
-};
+void OverrideIDHierarchyBuilder::build_hierarchy_for_ID(ID &override_root_id,
+                                                        TreeElement &te_to_expand)
+{
+  HierarchyBuildData build_data{override_root_id};
+  build_hierarchy_for_ID_recursive(override_root_id, build_data, te_to_expand);
+}
+
+/* Helpers (defined below). */
+static void foreach_natural_hierarchy_child(const MainIDRelations &id_relations,
+                                            const ID &parent_id,
+                                            FunctionRef<void(ID &)> fn);
+static bool id_is_in_override_hierarchy(const ID &id,
+                                        const ID &relationship_parent_id,
+                                        const ID &override_root_id);
+
+void OverrideIDHierarchyBuilder::build_hierarchy_for_ID_recursive(const ID &parent_id,
+                                                                  HierarchyBuildData &build_data,
+                                                                  TreeElement &te_to_expand)
+{
+  /* In case this isn't added to the parents yet (does nothing if already there). */
+  build_data.parent_ids.add(&parent_id);
+
+  foreach_natural_hierarchy_child(id_relations_, parent_id, [&](ID &id) {
+    if (!id_is_in_override_hierarchy(id, parent_id, build_data.override_root_id_)) {
+      return;
+    }
+
+    /* Avoid endless recursion: If there is an ancestor for this ID already, it recurses into
+     * itself. */
+    if (build_data.parent_ids.lookup_key_default(&id, nullptr)) {
+      return;
+    }
+
+    /* Avoid duplicates: If there is a sibling for this ID already, the same ID is just used
+     * multiple times by the same parent. */
+    if (build_data.sibling_ids.lookup_key_default(&id, nullptr)) {
+      return;
+    }
+
+    TreeElement *new_te = outliner_add_element(
+        &space_outliner_, &te_to_expand.subtree, &id, &te_to_expand, TSE_SOME_ID, 0, false);
+
+    build_data.sibling_ids.add(&id);
+
+    /* Recurse into this ID. */
+    HierarchyBuildData child_build_data{build_data.override_root_id_};
+    child_build_data.parent_ids = build_data.parent_ids;
+    child_build_data.parent_ids.add(&id);
+    child_build_data.sibling_ids.reserve(10);
+    build_hierarchy_for_ID_recursive(id, child_build_data, *new_te);
+  });
+}
 
-static int build_hierarchy_foreach_ID_cb(LibraryIDLinkCallbackData *cb_data)
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Helpers for library override hierarchy building
+ * \{ */
+
+/**
+ * Iterate over the IDs \a parent_id uses. E.g. the child collections and contained objects of a
+ * parent collection. Also does special handling for object parenting, so that:
+ * - When iterating over a child object, \a fn is executed for the parent instead.
+ * - When iterating over a parent object, \a fn is _additionally_ executed for all children. Given
+ *   that the parent object isn't skipped, the caller has to ensure it's not added in the hierarchy
+ *   twice.
+ * This allows us to build the hierarchy in the expected ("natural") order, where parent objects
+ * are actual parent elements in the hierarchy, even though in data, the relation goes the other
+ * way around (children point to or "use" the parent).
+ *
+ * Only handles regular object parenting, not cases like the "Child of" constraint. Other Outliner
+ * display modes don't show this as parent in the hierarchy either.
+ */
+static void foreach_natural_hierarchy_child(const MainIDRelations &id_relations,
+                                            const ID &parent_id,
+                                            FunctionRef<void(ID &)> fn)
 {
-  if (!*cb_data->id_pointer) {
-    return IDWALK_RET_NOP;
+  const MainIDRelationsEntry *relations_of_id = static_cast<MainIDRelationsEntry *>(
+      BLI_ghash_lookup(id_relations.relations_from_pointers, &parent_id));
+
+  /* Iterate over all IDs used by the parent ID (e.g. the child-collections of a collection). */
+  for (MainIDRelationsEntryItem *to_id_entry = relations_of_id->to_ids; to_id_entry;
+       to_id_entry = to_id_entry->next) {
+    /* An ID pointed to (used) by the ID to recurse into. */
+    ID &target_id = **to_id_entry->id_pointer.to;
+
+    /* Don't walk up the hierarchy, e.g. ignore pointers to parent collections. */
+    if (to_id_entry->usage_flag & IDWALK_CB_LOOPBACK) {
+      continue;
+    }
+
+    /* Special case for objects: Process the parent object instead of the child object. Below the
+     * parent will add the child objects then. */
+    if (GS(target_id.name) == ID_OB) {
+      const Object &potential_child_ob = reinterpret_cast<const Object &>(target_id);
+      if (potential_child_ob.parent) {
+        fn(potential_child_ob.parent->id);
+        continue;
+      }
+    }
+
+    fn(target_id);
+  }
+
+

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list