Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc')
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc264
1 files changed, 183 insertions, 81 deletions
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);
+ }
+
+ /* If the ID is an object, find and iterate over any child objects. */
+ if (GS(parent_id.name) == ID_OB) {
+ for (MainIDRelationsEntryItem *from_id_entry = relations_of_id->from_ids; from_id_entry;
+ from_id_entry = from_id_entry->next) {
+ ID &potential_child_id = *from_id_entry->id_pointer.from;
+
+ if (GS(potential_child_id.name) != ID_OB) {
+ continue;
+ }
+
+ Object &potential_child_ob = reinterpret_cast<Object &>(potential_child_id);
+ if (potential_child_ob.parent && &potential_child_ob.parent->id == &parent_id) {
+ fn(potential_child_id);
+ }
+ }
}
+}
- BuildHierarchyForeachIDCbData &build_data = *reinterpret_cast<BuildHierarchyForeachIDCbData *>(
- cb_data->user_data);
- /* Note that this may be an embedded ID (see #real_override_id). */
- ID &id = **cb_data->id_pointer;
+static bool id_is_in_override_hierarchy(const ID &id,
+ const ID &relationship_parent_id,
+ const ID &override_root_id)
+{
/* If #id is an embedded ID, this will be set to the owner, which is a real ID and contains the
* override data. So queries of override data should be done via this, but the actual tree
* element we add is the embedded ID. */
const ID *real_override_id = &id;
if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(&id)) {
+ /* This assumes that the parent ID is always the owner of the 'embedded' one, I.e. that no
+ * other ID directly uses the embedded one. Should be true, but the debug code adds some checks
+ * to validate this assumption. */
+ real_override_id = &relationship_parent_id;
+
+#ifndef NDEBUG
if (GS(id.name) == ID_KE) {
- Key *key = (Key *)&id;
- real_override_id = key->from;
+ const Key *key = (Key *)&id;
+ BLI_assert(real_override_id == key->from);
}
- else if (id.flag & LIB_EMBEDDED_DATA) {
- /* TODO Needs double-checking if this handles all embedded IDs correctly. */
- real_override_id = cb_data->id_owner;
+ else {
+ BLI_assert((id.flag & LIB_EMBEDDED_DATA) != 0);
}
+#endif
}
if (!ID_IS_OVERRIDE_LIBRARY(real_override_id)) {
- return IDWALK_RET_NOP;
+ return false;
}
/* Is this ID part of the same override hierarchy? */
- if (real_override_id->override_library->hierarchy_root != &build_data.override_root_id) {
- return IDWALK_RET_NOP;
- }
-
- /* 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 IDWALK_RET_NOP;
- }
-
- /* 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 IDWALK_RET_NOP;
+ if (real_override_id->override_library->hierarchy_root != &override_root_id) {
+ return false;
}
- TreeElement *new_te = outliner_add_element(&build_data.space_outliner,
- &build_data.parent_te->getLegacyElement().subtree,
- &id,
- &build_data.parent_te->getLegacyElement(),
- TSE_SOME_ID,
- 0,
- false);
- build_data.sibling_ids.add(&id);
-
- BuildHierarchyForeachIDCbData child_build_data{build_data.bmain,
- build_data.space_outliner,
- build_data.override_root_id,
- tree_element_cast<TreeElementID>(new_te)};
- child_build_data.parent_ids = build_data.parent_ids;
- child_build_data.parent_ids.add(&id);
- child_build_data.sibling_ids.reserve(10);
- BKE_library_foreach_ID_link(
- &build_data.bmain, &id, build_hierarchy_foreach_ID_cb, &child_build_data, IDWALK_READONLY);
-
- return IDWALK_RET_NOP;
+ return true;
}
-void TreeDisplayOverrideLibraryHierarchies::build_hierarchy_for_ID(Main *bmain,
- ID &override_root_id,
- TreeElementID &te_id) const
-{
- BuildHierarchyForeachIDCbData build_data{*bmain, space_outliner_, override_root_id, &te_id};
- build_data.parent_ids.add(&override_root_id);
-
- BKE_library_foreach_ID_link(
- bmain, &te_id.get_ID(), build_hierarchy_foreach_ID_cb, &build_data, IDWALK_READONLY);
-}
+/** \} */
} // namespace blender::ed::outliner