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
path: root/source
diff options
context:
space:
mode:
authorBastien Montagne <mont29>2022-07-20 19:01:58 +0300
committerBastien Montagne <bastien@blender.org>2022-07-21 11:18:43 +0300
commitee3facd0879d48895febdd66661567c2af1b9e33 (patch)
treefc66465c8c942480884a0c100ec6b17a187b9a55 /source
parent0dcee6a386645bb1e976d11aa2d1ae45b01f968a (diff)
LibOverride: support 'make override' for all selected items.
This commit allows to select several data-blocks in the outliner and create overrides from all of them, not only the active one. It properly creates a single hierarchy when several IDs from a same hierarchy root data are selected. Reviewed By: Severin Differential Revision: https://developer.blender.org/D15497
Diffstat (limited to 'source')
-rw-r--r--source/blender/editors/space_outliner/outliner_tools.cc408
1 files changed, 255 insertions, 153 deletions
diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc
index 9d52103a266..e1aa695a262 100644
--- a/source/blender/editors/space_outliner/outliner_tools.cc
+++ b/source/blender/editors/space_outliner/outliner_tools.cc
@@ -31,8 +31,11 @@
#include "BLI_blenlib.h"
#include "BLI_ghash.h"
+#include "BLI_linklist.h"
+#include "BLI_map.hh"
#include "BLI_set.hh"
#include "BLI_utildefines.h"
+#include "BLI_vector.hh"
#include "BKE_anim_data.h"
#include "BKE_animsys.h"
@@ -90,7 +93,9 @@ static CLG_LogRef LOG = {"ed.outliner.tools"};
using namespace blender::ed::outliner;
+using blender::Map;
using blender::Set;
+using blender::Vector;
/* -------------------------------------------------------------------- */
/** \name ID/Library/Data Set/Un-link Utilities
@@ -777,6 +782,25 @@ static void id_local_fn(bContext *C,
}
}
+struct OutlinerLiboverrideDataIDRoot {
+ /** The linked ID that was selected for override. */
+ ID *id_root_reference;
+
+ /** The root of the override hierarchy to which the override of `id_root` belongs, once
+ * known/created. */
+ ID *id_hierarchy_root_override;
+
+ /** The ID that was detected as being a good candidate as instanciation hint for newly overridden
+ * objects, may be null.
+ *
+ * \note Typically currently only used when the root ID to override is a collection instanced by
+ * an emtpy object. */
+ ID *id_instance_hint;
+
+ /** If this override comes from an instancing object (which would be `id_instance_hint` then). */
+ bool is_override_instancing_object;
+};
+
struct OutlinerLibOverrideData {
bool do_hierarchy;
@@ -789,21 +813,44 @@ struct OutlinerLibOverrideData {
* solving broken overrides while not losing *all* of your overrides. */
bool do_resync_hierarchy_enforce;
- /** The override hierarchy root, when known/created. */
- ID *id_hierarchy_root_override;
-
- /** A hash of the selected tree elements' ID 'uuid'. Used to clear 'system override' flags on
+ /** A set of the selected tree elements' ID 'uuid'. Used to clear 'system override' flags on
* their newly-created liboverrides in post-process step of override hierarchy creation. */
Set<uint> selected_id_uid;
+
+ /** A mapping from the found hierarchy roots to a linked list of IDs to override for each of
+ * these roots.
+ *
+ * \note the key may be either linked (in which case it will be replaced by the newly created
+ * override), or an actual already existing override. */
+ Map<ID *, Vector<OutlinerLiboverrideDataIDRoot>> id_hierarchy_roots;
+
+ /** All 'session_uuid' of all hierarchy root IDs used or created by the operation. */
+ Set<uint> id_hierarchy_roots_uid;
+
+ void id_root_add(ID *id_hierarchy_root_reference,
+ ID *id_root_reference,
+ ID *id_instance_hint,
+ const bool is_override_instancing_object)
+ {
+ OutlinerLiboverrideDataIDRoot id_root_data;
+ id_root_data.id_root_reference = id_root_reference;
+ id_root_data.id_hierarchy_root_override = nullptr;
+ id_root_data.id_instance_hint = id_instance_hint;
+ id_root_data.is_override_instancing_object = is_override_instancing_object;
+
+ Vector<OutlinerLiboverrideDataIDRoot> &value = id_hierarchy_roots.lookup_or_add_default(
+ id_hierarchy_root_reference);
+ value.append(id_root_data);
+ }
};
/* Store 'UUID' of IDs of selected elements in the Outliner tree, before generating the override
* hierarchy. */
-static void id_override_library_create_hierarchy_pre_process_fn(bContext *UNUSED(C),
- ReportList *UNUSED(reports),
+static void id_override_library_create_hierarchy_pre_process_fn(bContext *C,
+ ReportList *reports,
Scene *UNUSED(scene),
- TreeElement *UNUSED(te),
- TreeStoreElem *UNUSED(tsep),
+ TreeElement *te,
+ TreeStoreElem *tsep,
TreeStoreElem *tselem,
void *user_data)
{
@@ -829,28 +876,6 @@ static void id_override_library_create_hierarchy_pre_process_fn(bContext *UNUSED
}
FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
}
-}
-
-static void id_override_library_create_fn(bContext *C,
- ReportList *reports,
- Scene *scene,
- TreeElement *te,
- TreeStoreElem *tsep,
- TreeStoreElem *tselem,
- void *user_data)
-{
- BLI_assert(TSE_IS_REAL_ID(tselem));
-
- /* We can only safely apply this operation on one item at a time, so only do it on the active
- * one. */
- if ((tselem->flag & TSE_ACTIVE) == 0) {
- return;
- }
-
- ID *id_root_reference = tselem->id;
- OutlinerLibOverrideData *data = reinterpret_cast<OutlinerLibOverrideData *>(user_data);
- const bool do_hierarchy = data->do_hierarchy;
- bool success = false;
ID *id_instance_hint = nullptr;
bool is_override_instancing_object = false;
@@ -866,172 +891,239 @@ static void id_override_library_create_fn(bContext *C,
}
}
- if (ID_IS_OVERRIDABLE_LIBRARY(id_root_reference) ||
- (ID_IS_LINKED(id_root_reference) && do_hierarchy)) {
- Main *bmain = CTX_data_main(C);
+ if (!ID_IS_OVERRIDABLE_LIBRARY(id_root_reference) &&
+ !(ID_IS_LINKED(id_root_reference) && do_hierarchy)) {
+ return;
+ }
- id_root_reference->tag |= LIB_TAG_DOIT;
+ Main *bmain = CTX_data_main(C);
- /* For now, remap all local usages of linked ID to local override one here. */
- ID *id_iter;
- FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
- if (ID_IS_LINKED(id_iter) || ID_IS_OVERRIDE_LIBRARY(id_iter)) {
- id_iter->tag &= ~LIB_TAG_DOIT;
- }
- else {
- id_iter->tag |= LIB_TAG_DOIT;
+ if (do_hierarchy) {
+ /* Tag all linked parents in tree hierarchy to be also overridden. */
+ ID *id_hierarchy_root_reference = id_root_reference;
+ while ((te = te->parent) != nullptr) {
+ if (!TSE_IS_REAL_ID(te->store_elem)) {
+ continue;
}
- }
- FOREACH_MAIN_ID_END;
- if (do_hierarchy) {
- /* Tag all linked parents in tree hierarchy to be also overridden. */
- ID *id_hierarchy_root_reference = id_root_reference;
- while ((te = te->parent) != nullptr) {
- if (!TSE_IS_REAL_ID(te->store_elem)) {
+ /* Tentative hierarchy root. */
+ ID *id_current_hierarchy_root = te->store_elem->id;
+
+ /* If the parent ID is from a different library than the reference root one, we are done
+ * with upwards tree processing in any case. */
+ if (id_current_hierarchy_root->lib != id_root_reference->lib) {
+ if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(id_current_hierarchy_root)) {
+ /* Virtual overrides (i.e. embedded IDs), we can simply keep processing their parent to
+ * get an actual real override. */
continue;
}
- /* Tentative hierarchy root. */
- ID *id_current_hierarchy_root = te->store_elem->id;
-
- /* If the parent ID is from a different library than the reference root one, we are done
- * with upwards tree processing in any case. */
- if (id_current_hierarchy_root->lib != id_root_reference->lib) {
- if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(id_current_hierarchy_root)) {
- /* Virtual overrides (i.e. embedded IDs), we can simply keep processing their parent to
- * get an actual real override. */
- continue;
- }
-
- /* If the parent ID is already an override, and is valid (i.e. local override), we can
- * access its hierarchy root directly. */
- if (!ID_IS_LINKED(id_current_hierarchy_root) &&
- ID_IS_OVERRIDE_LIBRARY_REAL(id_current_hierarchy_root) &&
- id_current_hierarchy_root->override_library->reference->lib ==
- id_root_reference->lib) {
- id_hierarchy_root_reference =
- id_current_hierarchy_root->override_library->hierarchy_root;
- BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference));
- break;
- }
-
- if (ID_IS_LINKED(id_current_hierarchy_root)) {
- /* No local 'anchor' was found for the hierarchy to override, do not proceed, as this
- * would most likely generate invisible/confusing/hard to use and manage overrides. */
- BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
- BKE_reportf(reports,
- RPT_WARNING,
- "Invalid anchor ('%s') found, needed to create library override from "
- "data-block '%s'",
- id_current_hierarchy_root->name,
- id_root_reference->name);
- return;
- }
-
- /* In all other cases, `id_current_hierarchy_root` cannot be a valid hierarchy root, so
- * current `id_hierarchy_root_reference` is our best candidate. */
-
+ /* If the parent ID is already an override, and is valid (i.e. local override), we can
+ * access its hierarchy root directly. */
+ if (!ID_IS_LINKED(id_current_hierarchy_root) &&
+ ID_IS_OVERRIDE_LIBRARY_REAL(id_current_hierarchy_root) &&
+ id_current_hierarchy_root->override_library->reference->lib ==
+ id_root_reference->lib) {
+ id_hierarchy_root_reference =
+ id_current_hierarchy_root->override_library->hierarchy_root;
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference));
break;
}
- /* If some element in the tree needs to be overridden, but its ID is not overridable,
- * abort. */
- if (!ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(id_current_hierarchy_root)) {
+ if (ID_IS_LINKED(id_current_hierarchy_root)) {
+ /* No local 'anchor' was found for the hierarchy to override, do not proceed, as this
+ * would most likely generate invisible/confusing/hard to use and manage overrides. */
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
BKE_reportf(reports,
RPT_WARNING,
- "Could not create library override from data-block '%s', one of its parents "
- "is not overridable ('%s')",
- id_root_reference->name,
- id_current_hierarchy_root->name);
+ "Invalid anchor ('%s') found, needed to create library override from "
+ "data-block '%s'",
+ id_current_hierarchy_root->name,
+ id_root_reference->name);
return;
}
- id_current_hierarchy_root->tag |= LIB_TAG_DOIT;
- id_hierarchy_root_reference = id_current_hierarchy_root;
+
+ /* In all other cases, `id_current_hierarchy_root` cannot be a valid hierarchy root, so
+ * current `id_hierarchy_root_reference` is our best candidate. */
+
+ break;
}
- /* That case can happen when linked data is a complex mix involving several libraries and/or
- * linked overrides. E.g. a mix of overrides from one library, and indirectly linked data
- * from another library. Do not try to support such cases for now. */
- if (!((id_hierarchy_root_reference->lib == id_root_reference->lib) ||
- (!ID_IS_LINKED(id_hierarchy_root_reference) &&
- ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference) &&
- id_hierarchy_root_reference->override_library->reference->lib ==
- id_root_reference->lib))) {
+ /* If some element in the tree needs to be overridden, but its ID is not overridable,
+ * abort. */
+ if (!ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(id_current_hierarchy_root)) {
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
BKE_reportf(reports,
RPT_WARNING,
- "Invalid hierarchy root ('%s') found, needed to create library override from "
- "data-block '%s'",
- id_hierarchy_root_reference->name,
- id_root_reference->name);
+ "Could not create library override from data-block '%s', one of its parents "
+ "is not overridable ('%s')",
+ id_root_reference->name,
+ id_current_hierarchy_root->name);
return;
}
+ id_current_hierarchy_root->tag |= LIB_TAG_DOIT;
+ id_hierarchy_root_reference = id_current_hierarchy_root;
+ }
+
+ /* That case can happen when linked data is a complex mix involving several libraries and/or
+ * linked overrides. E.g. a mix of overrides from one library, and indirectly linked data
+ * from another library. Do not try to support such cases for now. */
+ if (!((id_hierarchy_root_reference->lib == id_root_reference->lib) ||
+ (!ID_IS_LINKED(id_hierarchy_root_reference) &&
+ ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference) &&
+ id_hierarchy_root_reference->override_library->reference->lib ==
+ id_root_reference->lib))) {
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+ BKE_reportf(reports,
+ RPT_WARNING,
+ "Invalid hierarchy root ('%s') found, needed to create library override from "
+ "data-block '%s'",
+ id_hierarchy_root_reference->name,
+ id_root_reference->name);
+ return;
+ }
+
+ data->id_root_add(id_hierarchy_root_reference,
+ id_root_reference,
+ id_instance_hint,
+ is_override_instancing_object);
+ }
+ else if (ID_IS_OVERRIDABLE_LIBRARY(id_root_reference)) {
+ data->id_root_add(
+ id_root_reference, id_root_reference, id_instance_hint, is_override_instancing_object);
+ }
+}
+
+static void id_override_library_create_hierarchy(
+ Main &bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ OutlinerLibOverrideData &data,
+ ID *id_hierarchy_root_reference,
+ Vector<OutlinerLiboverrideDataIDRoot> &data_idroots,
+ bool &r_aggregated_success)
+{
+ BLI_assert(ID_IS_LINKED(id_hierarchy_root_reference) ||
+ ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference));
+
+ const bool do_hierarchy = data.do_hierarchy;
+
+ /* NOTE: This process is not the most efficient, but allows to re-use existing code.
+ * If this becomes a bottle-neck at some point, we need to implement a new
+ * `BKE_lib_override_library_hierarchy_create()` function able to process several roots inside of
+ * a same hierarchy in a single call. */
+ for (OutlinerLiboverrideDataIDRoot &data_idroot : data_idroots) {
+ /* For now, remap all local usages of linked ID to local override one here. */
+ ID *id_iter;
+ FOREACH_MAIN_ID_BEGIN (&bmain, id_iter) {
+ if (ID_IS_LINKED(id_iter) || ID_IS_OVERRIDE_LIBRARY(id_iter)) {
+ id_iter->tag &= ~LIB_TAG_DOIT;
+ }
+ else {
+ id_iter->tag |= LIB_TAG_DOIT;
+ }
+ }
+ FOREACH_MAIN_ID_END;
+ bool success;
+ if (do_hierarchy) {
ID *id_root_override = nullptr;
- success = BKE_lib_override_library_create(bmain,
- CTX_data_scene(C),
- CTX_data_view_layer(C),
+ success = BKE_lib_override_library_create(&bmain,
+ scene,
+ view_layer,
nullptr,
- id_root_reference,
+ data_idroot.id_root_reference,
id_hierarchy_root_reference,
- id_instance_hint,
+ data_idroot.id_instance_hint,
&id_root_override,
- data->do_fully_editable);
-
- BLI_assert(id_root_override != nullptr);
- BLI_assert(!ID_IS_LINKED(id_root_override));
- BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root_override));
- if (ID_IS_LINKED(id_hierarchy_root_reference)) {
- BLI_assert(
- id_root_override->override_library->hierarchy_root->override_library->reference ==
- id_hierarchy_root_reference);
- data->id_hierarchy_root_override = id_root_override->override_library->hierarchy_root;
- }
- else {
- BLI_assert(id_root_override->override_library->hierarchy_root ==
- id_hierarchy_root_reference);
- data->id_hierarchy_root_override = id_root_override->override_library->hierarchy_root;
+ data.do_fully_editable);
+
+ if (success) {
+ BLI_assert(id_root_override != nullptr);
+ BLI_assert(!ID_IS_LINKED(id_root_override));
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root_override));
+
+ ID *id_hierarchy_root_override = id_root_override->override_library->hierarchy_root;
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_override));
+ if (ID_IS_LINKED(id_hierarchy_root_reference)) {
+ BLI_assert(id_hierarchy_root_override->override_library->reference ==
+ id_hierarchy_root_reference);
+ /* If the hierarchy root reference was a linked data, after the first iteration there is
+ * now a matching override, which shall be used for all further partial overrides with
+ * this same hierarchy. */
+ id_hierarchy_root_reference = id_hierarchy_root_override;
+ }
+ else {
+ BLI_assert(id_hierarchy_root_override == id_hierarchy_root_reference);
+ }
+ data_idroot.id_hierarchy_root_override = id_hierarchy_root_override;
+ data.id_hierarchy_roots_uid.add(id_hierarchy_root_override->session_uuid);
}
}
- else if (ID_IS_OVERRIDABLE_LIBRARY(id_root_reference)) {
- success = BKE_lib_override_library_create_from_id(bmain, id_root_reference, true) != nullptr;
+ else if (ID_IS_OVERRIDABLE_LIBRARY(data_idroot.id_root_reference)) {
+ ID *id_root_override = BKE_lib_override_library_create_from_id(
+ &bmain, data_idroot.id_root_reference, true);
+ success = id_root_override != nullptr;
+ if (success) {
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root_override));
+ id_root_override->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED;
+ }
/* Cleanup. */
- BKE_main_id_newptr_and_tag_clear(bmain);
- BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+ BKE_main_id_newptr_and_tag_clear(&bmain);
+ BKE_main_id_tag_all(&bmain, LIB_TAG_DOIT, false);
}
/* Remove the instance empty from this scene, the items now have an overridden collection
* instead. */
- if (success && is_override_instancing_object) {
- ED_object_base_free_and_unlink(bmain, scene, (Object *)id_instance_hint);
+ if (success && data_idroot.is_override_instancing_object) {
+ BLI_assert(GS(data_idroot.id_instance_hint) == ID_OB);
+ ED_object_base_free_and_unlink(
+ &bmain, scene, reinterpret_cast<Object *>(data_idroot.id_instance_hint));
}
- }
- if (!success) {
- BKE_reportf(reports,
- RPT_WARNING,
- "Could not create library override from data-block '%s'",
- id_root_reference->name);
+
+ r_aggregated_success = r_aggregated_success && success;
}
}
/* Clear system override flag from newly created overrides which linked reference were previously
* selected in the Outliner tree. */
-static void id_override_library_create_hierarchy_post_process(bContext *C,
- OutlinerLibOverrideData *data)
+static void id_override_library_create_hierarchy_process(bContext *C,
+ ReportList *reports,
+ OutlinerLibOverrideData &data)
{
Main *bmain = CTX_data_main(C);
- ID *id_hierarchy_root_override = data->id_hierarchy_root_override;
+ Scene *scene = CTX_data_scene(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ const bool do_hierarchy = data.do_hierarchy;
+
+ bool success = true;
+ for (auto &&[id_hierarchy_root_reference, data_idroots] : data.id_hierarchy_roots.items()) {
+ id_override_library_create_hierarchy(
+ *bmain, scene, view_layer, data, id_hierarchy_root_reference, data_idroots, success);
+ }
+
+ if (!success) {
+ BKE_reportf(reports,
+ RPT_WARNING,
+ "Could not create library override from one or more of the selected data-blocks");
+ }
+
+ if (!do_hierarchy) {
+ return;
+ }
ID *id_iter;
FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
- if (ID_IS_LINKED(id_iter) || !ID_IS_OVERRIDE_LIBRARY_REAL(id_iter) ||
- id_iter->override_library->hierarchy_root != id_hierarchy_root_override) {
+ if (ID_IS_LINKED(id_iter) || !ID_IS_OVERRIDE_LIBRARY_REAL(id_iter)) {
continue;
}
- if (data->selected_id_uid.contains(id_iter->override_library->reference->session_uuid)) {
+ if (!data.id_hierarchy_roots_uid.contains(
+ id_iter->override_library->hierarchy_root->session_uuid)) {
+ continue;
+ }
+ if (data.selected_id_uid.contains(id_iter->override_library->reference->session_uuid) ||
+ data.selected_id_uid.contains(id_iter->session_uuid)) {
id_iter->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED;
}
}
@@ -2254,8 +2346,18 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
}
case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE: {
OutlinerLibOverrideData override_data{};
- outliner_do_libdata_operation(
- C, op->reports, scene, space_outliner, id_override_library_create_fn, &override_data);
+ override_data.do_hierarchy = false;
+ override_data.do_fully_editable = true;
+
+ outliner_do_libdata_operation(C,
+ op->reports,
+ scene,
+ space_outliner,
+ id_override_library_create_hierarchy_pre_process_fn,
+ &override_data);
+
+ id_override_library_create_hierarchy_process(C, op->reports, override_data);
+
ED_undo_push(C, "Overridden Data");
break;
}
@@ -2263,15 +2365,15 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
OutlinerLibOverrideData override_data{};
override_data.do_hierarchy = true;
override_data.do_fully_editable = U.experimental.use_override_new_fully_editable;
+
outliner_do_libdata_operation(C,
op->reports,
scene,
space_outliner,
id_override_library_create_hierarchy_pre_process_fn,
&override_data);
- outliner_do_libdata_operation(
- C, op->reports, scene, space_outliner, id_override_library_create_fn, &override_data);
- id_override_library_create_hierarchy_post_process(C, &override_data);
+
+ id_override_library_create_hierarchy_process(C, op->reports, override_data);
ED_undo_push(C, "Overridden Data Hierarchy");
break;