Blender  V3.3
tree_display_override_library_hierarchies.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
7 #include "DNA_key_types.h"
8 #include "DNA_space_types.h"
9 
10 #include "BLI_function_ref.hh"
11 #include "BLI_ghash.h"
12 #include "BLI_map.hh"
13 
14 #include "BLI_set.hh"
15 
16 #include "BLT_translation.h"
17 
18 #include "BKE_lib_override.h"
19 #include "BKE_lib_query.h"
20 #include "BKE_main.h"
21 
22 #include "../outliner_intern.hh"
23 #include "common.hh"
24 #include "tree_display.hh"
25 
26 namespace blender::ed::outliner {
27 
28 class AbstractTreeElement;
29 
31  SpaceOutliner &space_outliner)
32  : AbstractTreeDisplay(space_outliner)
33 {
34 }
35 
37 {
38  ListBase tree = {nullptr};
39 
40  /* First step: Build "Current File" hierarchy. */
41  TreeElement *current_file_te = outliner_add_element(
42  &space_outliner_, &tree, source_data.bmain, nullptr, TSE_ID_BASE, -1);
43  current_file_te->name = IFACE_("Current File");
45  {
46  build_hierarchy_for_lib_or_main(source_data.bmain, *current_file_te);
47 
48  /* Add dummy child if there's nothing to display. */
49  if (BLI_listbase_is_empty(&current_file_te->subtree)) {
51  &space_outliner_, &current_file_te->subtree, nullptr, current_file_te, TSE_ID_BASE, 0);
52  dummy_te->name = IFACE_("No Library Overrides");
53  }
54  }
55 
56  /* Second step: Build hierarchies for external libraries. */
57  for (Library *lib = (Library *)source_data.bmain->libraries.first; lib;
58  lib = (Library *)lib->id.next) {
60  &space_outliner_, &tree, lib, nullptr, TSE_SOME_ID, 0);
61  build_hierarchy_for_lib_or_main(source_data.bmain, *tenlib, lib);
62  }
63 
64  /* Remove top level library elements again that don't contain any overrides. */
65  LISTBASE_FOREACH_MUTABLE (TreeElement *, top_level_te, &tree) {
66  if (top_level_te == current_file_te) {
67  continue;
68  }
69 
70  if (BLI_listbase_is_empty(&top_level_te->subtree)) {
71  outliner_free_tree_element(top_level_te, &tree);
72  }
73  }
74 
75  return tree;
76 }
77 
79 {
80  return true;
81 }
82 
83 /* -------------------------------------------------------------------- */
88  SpaceOutliner &space_outliner_;
89  Main &bmain_;
90  MainIDRelations &id_relations_;
91 
92  struct HierarchyBuildData {
93  const ID &override_root_id_;
94  /* The ancestor IDs leading to the current ID, to avoid IDs recursing into themselves. Changes
95  * with every level of recursion. */
96  Set<const ID *> parent_ids{};
97  /* The IDs that were already added to #parent_te, to avoid duplicates. Entirely new set with
98  * every level of recursion. */
99  Set<const ID *> sibling_ids{};
100  };
101 
102  public:
104  Main &bmain,
105  MainIDRelations &id_relations)
106  : space_outliner_(space_outliner), bmain_(bmain), id_relations_(id_relations)
107  {
108  }
109 
110  void build_hierarchy_for_ID(ID &root_id, TreeElement &te_to_expand);
111 
112  private:
113  void build_hierarchy_for_ID_recursive(const ID &parent_id,
114  HierarchyBuildData &build_data,
115  TreeElement &te_to_expand);
116 };
117 
118 ListBase TreeDisplayOverrideLibraryHierarchies::build_hierarchy_for_lib_or_main(
119  Main *bmain, TreeElement &parent_te, Library *lib)
120 {
121  ListBase tree = {nullptr};
122 
123  /* Ensure #Main.relations contains the latest mapping of relations. Must be freed before
124  * returning. */
125  BKE_main_relations_create(bmain, 0);
126 
127  OverrideIDHierarchyBuilder builder(space_outliner_, *bmain, *bmain->relations);
128 
129  /* Keep track over which ID base elements were already added, and expand them once added. */
130  Map<ID_Type, TreeElement *> id_base_te_map;
131  /* Index for the ID base elements ("Objects", "Materials", etc). */
132  int base_index = 0;
133 
134  ID *iter_id;
135  FOREACH_MAIN_ID_BEGIN (bmain, iter_id) {
137  continue;
138  }
139  if (iter_id->lib != lib) {
140  continue;
141  }
142 
143  TreeElement *new_base_te = id_base_te_map.lookup_or_add_cb(GS(iter_id->name), [&]() {
144  TreeElement *new_te = outliner_add_element(&space_outliner_,
145  &parent_te.subtree,
146  lib ? (void *)lib : bmain,
147  &parent_te,
148  TSE_ID_BASE,
149  base_index++);
150  new_te->name = outliner_idcode_to_plural(GS(iter_id->name));
151  return new_te;
152  });
153 
154  TreeElement *new_id_te = outliner_add_element(
155  &space_outliner_, &new_base_te->subtree, iter_id, new_base_te, TSE_SOME_ID, 0, false);
156 
157  builder.build_hierarchy_for_ID(*iter_id, *new_id_te);
158  }
160 
162 
163  return tree;
164 }
165 
167  TreeElement &te_to_expand)
168 {
169  HierarchyBuildData build_data{override_root_id};
170  build_hierarchy_for_ID_recursive(override_root_id, build_data, te_to_expand);
171 }
172 
176 };
177 /* Helpers (defined below). */
178 static void foreach_natural_hierarchy_child(const MainIDRelations &id_relations,
179  const ID &parent_id,
181 static bool id_is_in_override_hierarchy(const Main &bmain,
182  const ID &id,
183  const ID &relationship_parent_id,
184  const ID &override_root_id);
185 
186 void OverrideIDHierarchyBuilder::build_hierarchy_for_ID_recursive(const ID &parent_id,
187  HierarchyBuildData &build_data,
188  TreeElement &te_to_expand)
189 {
190  /* In case this isn't added to the parents yet (does nothing if already there). */
191  build_data.parent_ids.add(&parent_id);
192 
193  foreach_natural_hierarchy_child(id_relations_, parent_id, [&](ID &id) {
194  /* Some IDs can use themselves, early abort. */
195  if (&id == &parent_id) {
196  return FOREACH_CONTINUE;
197  }
198  if (!id_is_in_override_hierarchy(bmain_, id, parent_id, build_data.override_root_id_)) {
199  return FOREACH_CONTINUE;
200  }
201 
202  /* Avoid endless recursion: If there is an ancestor for this ID already, it recurses into
203  * itself. */
204  if (build_data.parent_ids.lookup_key_default(&id, nullptr)) {
205  return FOREACH_CONTINUE;
206  }
207 
208  /* Avoid duplicates: If there is a sibling for this ID already, the same ID is just used
209  * multiple times by the same parent. */
210  if (build_data.sibling_ids.lookup_key_default(&id, nullptr)) {
211  return FOREACH_CONTINUE;
212  }
213 
214  /* We only want to add children whose parent isn't collapsed. Otherwise, in complex scenes with
215  * thousands of relationships, the building can slow down tremendously. Tag the parent to allow
216  * un-collapsing, but don't actually add the children. */
217  if (!TSELEM_OPEN(TREESTORE(&te_to_expand), &space_outliner_)) {
218  te_to_expand.flag |= TE_PRETEND_HAS_CHILDREN;
219  return FOREACH_BREAK;
220  }
221 
223  &space_outliner_, &te_to_expand.subtree, &id, &te_to_expand, TSE_SOME_ID, 0, false);
224 
225  build_data.sibling_ids.add(&id);
226 
227  /* Recurse into this ID. */
228  HierarchyBuildData child_build_data{build_data.override_root_id_};
229  child_build_data.parent_ids = build_data.parent_ids;
230  child_build_data.parent_ids.add(&id);
231  child_build_data.sibling_ids.reserve(10);
232  build_hierarchy_for_ID_recursive(id, child_build_data, *new_te);
233 
234  return FOREACH_CONTINUE;
235  });
236 }
237 
240 /* -------------------------------------------------------------------- */
258 static void foreach_natural_hierarchy_child(const MainIDRelations &id_relations,
259  const ID &parent_id,
261 {
262  const MainIDRelationsEntry *relations_of_id = static_cast<MainIDRelationsEntry *>(
263  BLI_ghash_lookup(id_relations.relations_from_pointers, &parent_id));
264 
265  /* Iterate over all IDs used by the parent ID (e.g. the child-collections of a collection). */
266  for (MainIDRelationsEntryItem *to_id_entry = relations_of_id->to_ids; to_id_entry;
267  to_id_entry = to_id_entry->next) {
268  /* An ID pointed to (used) by the ID to recurse into. */
269  ID &target_id = **to_id_entry->id_pointer.to;
270 
271  /* Don't walk up the hierarchy, e.g. ignore pointers to parent collections. */
272  if (to_id_entry->usage_flag & IDWALK_CB_LOOPBACK) {
273  continue;
274  }
275 
276  /* Special case for objects: Process the parent object instead of the child object. Below the
277  * parent will add the child objects then. */
278  if (GS(target_id.name) == ID_OB) {
279  const Object &potential_child_ob = reinterpret_cast<const Object &>(target_id);
280  if (potential_child_ob.parent) {
281  if (fn(potential_child_ob.parent->id) == FOREACH_BREAK) {
282  return;
283  }
284  continue;
285  }
286  }
287 
288  if (fn(target_id) == FOREACH_BREAK) {
289  return;
290  }
291  }
292 
293  /* If the ID is an object, find and iterate over any child objects. */
294  if (GS(parent_id.name) == ID_OB) {
295  for (MainIDRelationsEntryItem *from_id_entry = relations_of_id->from_ids; from_id_entry;
296  from_id_entry = from_id_entry->next) {
297  ID &potential_child_id = *from_id_entry->id_pointer.from;
298 
299  if (GS(potential_child_id.name) != ID_OB) {
300  continue;
301  }
302 
303  const Object &potential_child_ob = reinterpret_cast<Object &>(potential_child_id);
304  if (!potential_child_ob.parent || &potential_child_ob.parent->id != &parent_id) {
305  continue;
306  }
307 
308  if (fn(potential_child_id) == FOREACH_BREAK) {
309  return;
310  }
311  }
312  }
313 }
314 
315 static bool id_is_in_override_hierarchy(const Main &bmain,
316  const ID &id,
317  const ID &relationship_parent_id,
318  const ID &override_root_id)
319 {
320  /* If #id is an embedded ID, this will be set to the owner, which is a real ID and contains the
321  * override data. So queries of override data should be done via this, but the actual tree
322  * element we add is the embedded ID. */
323  const ID *real_override_id = &id;
324 
326  /* In many cases, `relationship_parent_id` is the owner, but not always (e.g. there can be
327  * drivers directly between an object and a shapekey). */
328  BKE_lib_override_library_get(const_cast<Main *>(&bmain),
329  const_cast<ID *>(&id),
330  const_cast<ID *>(&relationship_parent_id),
331  const_cast<ID **>(&real_override_id));
332  }
333 
334  if (!ID_IS_OVERRIDE_LIBRARY(real_override_id)) {
335  return false;
336  }
337  /* Is this ID part of the same override hierarchy? */
338  if (real_override_id->override_library->hierarchy_root != &override_root_id) {
339  return false;
340  }
341 
342  return true;
343 }
344 
347 } // namespace blender::ed::outliner
IDOverrideLibrary * BKE_lib_override_library_get(struct Main *bmain, struct ID *id, struct ID *owner_id_hint, struct ID **r_owner_id)
@ IDWALK_CB_LOOPBACK
Definition: BKE_lib_query.h:54
#define FOREACH_MAIN_ID_END
Definition: BKE_main.h:367
void BKE_main_relations_create(struct Main *bmain, short flag)
Definition: main.c:276
void BKE_main_relations_free(struct Main *bmain)
Definition: main.c:311
#define FOREACH_MAIN_ID_BEGIN(_bmain, _id)
Definition: BKE_main.h:361
void * BLI_ghash_lookup(const GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:734
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
Definition: BLI_listbase.h:269
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
Definition: BLI_listbase.h:354
#define IFACE_(msgid)
#define ID_IS_OVERRIDE_LIBRARY_VIRTUAL(_id)
Definition: DNA_ID.h:585
#define ID_IS_OVERRIDE_LIBRARY_REAL(_id)
Definition: DNA_ID.h:581
#define ID_IS_OVERRIDE_LIBRARY(_id)
Definition: DNA_ID.h:588
#define ID_IS_OVERRIDE_LIBRARY_HIERARCHY_ROOT(_id)
Definition: DNA_ID.h:591
@ ID_OB
Definition: DNA_ID_enums.h:47
@ TSE_ID_BASE
@ TSE_SOME_ID
Value & lookup_or_add_cb(const Key &key, const CreateValueF &create_value)
Definition: BLI_map.hh:561
Base Class For Tree-Displays.
Definition: tree_display.hh:62
static void uncollapse_by_default(TreeElement *legacy_te)
OverrideIDHierarchyBuilder(SpaceOutliner &space_outliner, Main &bmain, MainIDRelations &id_relations)
void * tree
DRWShaderLibrary * lib
#define GS(x)
Definition: iris.c:225
TreeElement * outliner_add_element(SpaceOutliner *space_outliner, ListBase *lb, void *idv, TreeElement *parent, short type, short index, const bool expand)
static void foreach_natural_hierarchy_child(const MainIDRelations &id_relations, const ID &parent_id, FunctionRef< ForeachChildReturn(ID &)> fn)
static bool id_is_in_override_hierarchy(const Main &bmain, const ID &id, const ID &relationship_parent_id, const ID &override_root_id)
@ TE_PRETEND_HAS_CHILDREN
#define TREESTORE(a)
void outliner_free_tree_element(TreeElement *element, ListBase *parent_subtree)
#define TSELEM_OPEN(telm, sv)
struct ID * hierarchy_root
Definition: DNA_ID.h:303
Definition: DNA_ID.h:368
struct Library * lib
Definition: DNA_ID.h:372
IDOverrideLibrary * override_library
Definition: DNA_ID.h:412
char name[66]
Definition: DNA_ID.h:378
void * first
Definition: DNA_listBase.h:31
struct MainIDRelationsEntryItem * next
Definition: BKE_main.h:50
struct MainIDRelationsEntryItem * to_ids
Definition: BKE_main.h:68
struct MainIDRelationsEntryItem * from_ids
Definition: BKE_main.h:66
struct GHash * relations_from_pointers
Definition: BKE_main.h:107
Definition: BKE_main.h:121
ListBase libraries
Definition: BKE_main.h:169
struct MainIDRelations * relations
Definition: BKE_main.h:218
struct Object * parent
ListBase subtree
const char * name
The data to build the tree from.
Definition: tree_display.hh:43
Establish and manage Outliner trees for different display modes.