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