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