Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/space_outliner/outliner_tools.cc
| Show All 25 Lines | |||||
| #include "DNA_scene_types.h" | #include "DNA_scene_types.h" | ||||
| #include "DNA_sequence_types.h" | #include "DNA_sequence_types.h" | ||||
| #include "DNA_simulation_types.h" | #include "DNA_simulation_types.h" | ||||
| #include "DNA_volume_types.h" | #include "DNA_volume_types.h" | ||||
| #include "DNA_world_types.h" | #include "DNA_world_types.h" | ||||
| #include "BLI_blenlib.h" | #include "BLI_blenlib.h" | ||||
| #include "BLI_ghash.h" | #include "BLI_ghash.h" | ||||
| #include "BLI_linklist.h" | |||||
| #include "BLI_map.hh" | |||||
| #include "BLI_memarena.h" | |||||
| #include "BLI_set.hh" | #include "BLI_set.hh" | ||||
| #include "BLI_utildefines.h" | #include "BLI_utildefines.h" | ||||
| #include "BKE_anim_data.h" | #include "BKE_anim_data.h" | ||||
| #include "BKE_animsys.h" | #include "BKE_animsys.h" | ||||
| #include "BKE_armature.h" | #include "BKE_armature.h" | ||||
| #include "BKE_collection.h" | #include "BKE_collection.h" | ||||
| #include "BKE_constraint.h" | #include "BKE_constraint.h" | ||||
| ▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | |||||
| #include "tree/tree_element_rna.hh" | #include "tree/tree_element_rna.hh" | ||||
| #include "tree/tree_element_seq.hh" | #include "tree/tree_element_seq.hh" | ||||
| #include "tree/tree_iterator.hh" | #include "tree/tree_iterator.hh" | ||||
| static CLG_LogRef LOG = {"ed.outliner.tools"}; | static CLG_LogRef LOG = {"ed.outliner.tools"}; | ||||
| using namespace blender::ed::outliner; | using namespace blender::ed::outliner; | ||||
| using blender::Map; | |||||
| using blender::Set; | using blender::Set; | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name ID/Library/Data Set/Un-link Utilities | /** \name ID/Library/Data Set/Un-link Utilities | ||||
| * \{ */ | * \{ */ | ||||
| static void get_element_operation_type( | static void get_element_operation_type( | ||||
| TreeElement *te, int *scenelevel, int *objectlevel, int *idlevel, int *datalevel) | TreeElement *te, int *scenelevel, int *objectlevel, int *idlevel, int *datalevel) | ||||
| ▲ Show 20 Lines • Show All 671 Lines • ▼ Show 20 Lines | if (BKE_lib_id_make_local(bmain, tselem->id, 0)) { | ||||
| BKE_id_newptr_and_tag_clear(tselem->id); | BKE_id_newptr_and_tag_clear(tselem->id); | ||||
| } | } | ||||
| } | } | ||||
| else if (ID_IS_OVERRIDE_LIBRARY_REAL(tselem->id)) { | else if (ID_IS_OVERRIDE_LIBRARY_REAL(tselem->id)) { | ||||
| BKE_lib_override_library_make_local(tselem->id); | BKE_lib_override_library_make_local(tselem->id); | ||||
| } | } | ||||
| } | } | ||||
| 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 { | struct OutlinerLibOverrideData { | ||||
| bool do_hierarchy; | bool do_hierarchy; | ||||
| /** When creating new overrides, make them all user-editable. */ | /** When creating new overrides, make them all user-editable. */ | ||||
| bool do_fully_editable; | bool do_fully_editable; | ||||
| /** | /** | ||||
| * For resync operation, force keeping newly created override IDs (or original linked IDs) | * For resync operation, force keeping newly created override IDs (or original linked IDs) | ||||
| * instead of re-applying relevant existing ID pointer property override operations. Helps | * instead of re-applying relevant existing ID pointer property override operations. Helps | ||||
| * solving broken overrides while not losing *all* of your overrides. */ | * solving broken overrides while not losing *all* of your overrides. */ | ||||
| bool do_resync_hierarchy_enforce; | bool do_resync_hierarchy_enforce; | ||||
| /** The override hierarchy root, when known/created. */ | /** A set of the selected tree elements' ID 'uuid'. Used to clear 'system override' flags on | ||||
| ID *id_hierarchy_root_override; | |||||
| /** A hash 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. */ | * their newly-created liboverrides in post-process step of override hierarchy creation. */ | ||||
| Set<uint> selected_id_uid; | 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 *, LinkNode *> id_hierarchy_roots; | |||||
| /** All 'session_uuid' of all hierarchy root IDs used or created by the operation. */ | |||||
| Set<uint> id_hierarchy_roots_uid; | |||||
| MemArena *memarena; | |||||
| }; | }; | ||||
| static void override_data_init(OutlinerLibOverrideData &data) | |||||
| { | |||||
| data.memarena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); | |||||
| } | |||||
| static void override_data_id_root_add(OutlinerLibOverrideData &data, | |||||
| ID *id_hierarchy_root_reference, | |||||
| ID *id_root_reference, | |||||
| ID *id_instance_hint, | |||||
| const bool is_override_instancing_object) | |||||
| { | |||||
| OutlinerLiboverrideDataIDRoot *id_root_data = static_cast<OutlinerLiboverrideDataIDRoot *>( | |||||
| BLI_memarena_alloc(data.memarena, sizeof(*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; | |||||
| printf("Adding idroot %s (%p) as part of hierarchy root %s (%p)\n", | |||||
| id_root_reference->name, | |||||
| id_root_reference->lib, | |||||
| id_hierarchy_root_reference->name, | |||||
| id_hierarchy_root_reference->lib); | |||||
Severin: I guess this should be disabled for the commit? | |||||
mont29AuthorUnsubmitted Done Inline ActionsYes indeed. mont29: Yes indeed. | |||||
| data.id_hierarchy_roots.add_or_modify( | |||||
| id_hierarchy_root_reference, | |||||
| [id_root_data, data](LinkNode **value) { | |||||
| *value = nullptr; | |||||
| BLI_linklist_prepend_arena(value, id_root_data, data.memarena); | |||||
| }, | |||||
| [id_root_data, data](LinkNode **value) { | |||||
| BLI_linklist_prepend_arena(value, id_root_data, data.memarena); | |||||
| }); | |||||
| } | |||||
| static void override_data_clear(OutlinerLibOverrideData &data) | |||||
| { | |||||
| data.selected_id_uid.clear(); | |||||
| data.id_hierarchy_roots_uid.clear(); | |||||
| data.id_hierarchy_roots.clear(); | |||||
| BLI_memarena_free(data.memarena); | |||||
| data.memarena = nullptr; | |||||
| } | |||||
| /* Store 'UUID' of IDs of selected elements in the Outliner tree, before generating the override | /* Store 'UUID' of IDs of selected elements in the Outliner tree, before generating the override | ||||
| * hierarchy. */ | * hierarchy. */ | ||||
| static void id_override_library_create_hierarchy_pre_process_fn(bContext *UNUSED(C), | static void id_override_library_create_hierarchy_pre_process_fn(bContext *C, | ||||
| ReportList *UNUSED(reports), | ReportList *reports, | ||||
| Scene *UNUSED(scene), | Scene *UNUSED(scene), | ||||
| TreeElement *UNUSED(te), | TreeElement *te, | ||||
| TreeStoreElem *UNUSED(tsep), | TreeStoreElem *tsep, | ||||
| TreeStoreElem *tselem, | TreeStoreElem *tselem, | ||||
| void *user_data) | void *user_data) | ||||
| { | { | ||||
| BLI_assert(TSE_IS_REAL_ID(tselem)); | BLI_assert(TSE_IS_REAL_ID(tselem)); | ||||
| OutlinerLibOverrideData *data = reinterpret_cast<OutlinerLibOverrideData *>(user_data); | OutlinerLibOverrideData *data = reinterpret_cast<OutlinerLibOverrideData *>(user_data); | ||||
| const bool do_hierarchy = data->do_hierarchy; | const bool do_hierarchy = data->do_hierarchy; | ||||
| ID *id_root_reference = tselem->id; | ID *id_root_reference = tselem->id; | ||||
| Show All 9 Lines | if (GS(id_root_reference->name) == ID_GR && (tselem->flag & TSE_CLOSED) != 0) { | ||||
| Collection *root_collection = reinterpret_cast<Collection *>(id_root_reference); | Collection *root_collection = reinterpret_cast<Collection *>(id_root_reference); | ||||
| FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (root_collection, object_iter) { | FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (root_collection, object_iter) { | ||||
| if (id_root_reference->lib == object_iter->id.lib && object_iter->type == OB_ARMATURE) { | if (id_root_reference->lib == object_iter->id.lib && object_iter->type == OB_ARMATURE) { | ||||
| data->selected_id_uid.add(object_iter->id.session_uuid); | data->selected_id_uid.add(object_iter->id.session_uuid); | ||||
| } | } | ||||
| } | } | ||||
| FOREACH_COLLECTION_OBJECT_RECURSIVE_END; | 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; | ID *id_instance_hint = nullptr; | ||||
| bool is_override_instancing_object = false; | bool is_override_instancing_object = false; | ||||
| if (tsep != nullptr && tsep->type == TSE_SOME_ID && tsep->id != nullptr && | if (tsep != nullptr && tsep->type == TSE_SOME_ID && tsep->id != nullptr && | ||||
| GS(tsep->id->name) == ID_OB && !ID_IS_OVERRIDE_LIBRARY(tsep->id)) { | GS(tsep->id->name) == ID_OB && !ID_IS_OVERRIDE_LIBRARY(tsep->id)) { | ||||
| Object *ob = reinterpret_cast<Object *>(tsep->id); | Object *ob = reinterpret_cast<Object *>(tsep->id); | ||||
| if (ob->type == OB_EMPTY && &ob->instance_collection->id == id_root_reference) { | if (ob->type == OB_EMPTY && &ob->instance_collection->id == id_root_reference) { | ||||
| BLI_assert(GS(id_root_reference->name) == ID_GR); | BLI_assert(GS(id_root_reference->name) == ID_GR); | ||||
| /* Empty instantiating the collection we override, we need to pass it to BKE overriding code | /* Empty instantiating the collection we override, we need to pass it to BKE overriding code | ||||
| * for proper handling. */ | * for proper handling. */ | ||||
| id_instance_hint = tsep->id; | id_instance_hint = tsep->id; | ||||
| is_override_instancing_object = true; | is_override_instancing_object = true; | ||||
| } | } | ||||
| } | } | ||||
| if (ID_IS_OVERRIDABLE_LIBRARY(id_root_reference) || | if (!ID_IS_OVERRIDABLE_LIBRARY(id_root_reference) && | ||||
| (ID_IS_LINKED(id_root_reference) && do_hierarchy)) { | !(ID_IS_LINKED(id_root_reference) && do_hierarchy)) { | ||||
| Main *bmain = CTX_data_main(C); | return; | ||||
| id_root_reference->tag |= LIB_TAG_DOIT; | |||||
| /* 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; | |||||
| Main *bmain = CTX_data_main(C); | |||||
| if (do_hierarchy) { | if (do_hierarchy) { | ||||
| /* Tag all linked parents in tree hierarchy to be also overridden. */ | /* Tag all linked parents in tree hierarchy to be also overridden. */ | ||||
| ID *id_hierarchy_root_reference = id_root_reference; | ID *id_hierarchy_root_reference = id_root_reference; | ||||
| while ((te = te->parent) != nullptr) { | while ((te = te->parent) != nullptr) { | ||||
| if (!TSE_IS_REAL_ID(te->store_elem)) { | if (!TSE_IS_REAL_ID(te->store_elem)) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| /* Tentative hierarchy root. */ | /* Tentative hierarchy root. */ | ||||
| ID *id_current_hierarchy_root = te->store_elem->id; | 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 | /* If the parent ID is from a different library than the reference root one, we are done | ||||
| * with upwards tree processing in any case. */ | * with upwards tree processing in any case. */ | ||||
| if (id_current_hierarchy_root->lib != id_root_reference->lib) { | if (id_current_hierarchy_root->lib != id_root_reference->lib) { | ||||
| if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(id_current_hierarchy_root)) { | if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(id_current_hierarchy_root)) { | ||||
| /* Virtual overrides (i.e. embedded IDs), we can simply keep processing their parent to | /* Virtual overrides (i.e. embedded IDs), we can simply keep processing their parent to | ||||
| * get an actual real override. */ | * get an actual real override. */ | ||||
| continue; | continue; | ||||
| } | } | ||||
| /* If the parent ID is already an override, and is valid (i.e. local override), we can | /* If the parent ID is already an override, and is valid (i.e. local override), we can | ||||
| * access its hierarchy root directly. */ | * access its hierarchy root directly. */ | ||||
| if (!ID_IS_LINKED(id_current_hierarchy_root) && | if (!ID_IS_LINKED(id_current_hierarchy_root) && | ||||
| ID_IS_OVERRIDE_LIBRARY_REAL(id_current_hierarchy_root) && | ID_IS_OVERRIDE_LIBRARY_REAL(id_current_hierarchy_root) && | ||||
| id_current_hierarchy_root->override_library->reference->lib == | id_current_hierarchy_root->override_library->reference->lib == | ||||
| id_root_reference->lib) { | id_root_reference->lib) { | ||||
| id_hierarchy_root_reference = | id_hierarchy_root_reference = | ||||
| id_current_hierarchy_root->override_library->hierarchy_root; | id_current_hierarchy_root->override_library->hierarchy_root; | ||||
| BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference)); | BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference)); | ||||
| break; | break; | ||||
| } | } | ||||
| if (ID_IS_LINKED(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 | /* 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. */ | * would most likely generate invisible/confusing/hard to use and manage overrides. */ | ||||
| BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); | BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); | ||||
| BKE_reportf(reports, | BKE_reportf(reports, | ||||
| RPT_WARNING, | RPT_WARNING, | ||||
| "Invalid anchor ('%s') found, needed to create library override from " | "Invalid anchor ('%s') found, needed to create library override from " | ||||
| "data-block '%s'", | "data-block '%s'", | ||||
| id_current_hierarchy_root->name, | id_current_hierarchy_root->name, | ||||
| id_root_reference->name); | id_root_reference->name); | ||||
| return; | return; | ||||
| } | } | ||||
| /* In all other cases, `id_current_hierarchy_root` cannot be a valid hierarchy root, so | /* In all other cases, `id_current_hierarchy_root` cannot be a valid hierarchy root, so | ||||
| * current `id_hierarchy_root_reference` is our best candidate. */ | * current `id_hierarchy_root_reference` is our best candidate. */ | ||||
| break; | break; | ||||
| } | } | ||||
| /* If some element in the tree needs to be overridden, but its ID is not overridable, | /* If some element in the tree needs to be overridden, but its ID is not overridable, | ||||
| * abort. */ | * abort. */ | ||||
| if (!ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(id_current_hierarchy_root)) { | if (!ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(id_current_hierarchy_root)) { | ||||
| BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); | BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); | ||||
| BKE_reportf(reports, | BKE_reportf(reports, | ||||
| RPT_WARNING, | RPT_WARNING, | ||||
| "Could not create library override from data-block '%s', one of its parents " | "Could not create library override from data-block '%s', one of its parents " | ||||
| "is not overridable ('%s')", | "is not overridable ('%s')", | ||||
| id_root_reference->name, | id_root_reference->name, | ||||
| id_current_hierarchy_root->name); | id_current_hierarchy_root->name); | ||||
| return; | return; | ||||
| } | } | ||||
| id_current_hierarchy_root->tag |= LIB_TAG_DOIT; | id_current_hierarchy_root->tag |= LIB_TAG_DOIT; | ||||
| id_hierarchy_root_reference = id_current_hierarchy_root; | id_hierarchy_root_reference = id_current_hierarchy_root; | ||||
| } | } | ||||
| /* That case can happen when linked data is a complex mix involving several libraries and/or | /* 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 | * 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. */ | * from another library. Do not try to support such cases for now. */ | ||||
| if (!((id_hierarchy_root_reference->lib == id_root_reference->lib) || | if (!((id_hierarchy_root_reference->lib == id_root_reference->lib) || | ||||
| (!ID_IS_LINKED(id_hierarchy_root_reference) && | (!ID_IS_LINKED(id_hierarchy_root_reference) && | ||||
| ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference) && | ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference) && | ||||
| id_hierarchy_root_reference->override_library->reference->lib == | id_hierarchy_root_reference->override_library->reference->lib == | ||||
| id_root_reference->lib))) { | id_root_reference->lib))) { | ||||
| BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); | BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); | ||||
| BKE_reportf(reports, | BKE_reportf(reports, | ||||
| RPT_WARNING, | RPT_WARNING, | ||||
| "Invalid hierarchy root ('%s') found, needed to create library override from " | "Invalid hierarchy root ('%s') found, needed to create library override from " | ||||
| "data-block '%s'", | "data-block '%s'", | ||||
| id_hierarchy_root_reference->name, | id_hierarchy_root_reference->name, | ||||
| id_root_reference->name); | id_root_reference->name); | ||||
| return; | return; | ||||
| } | } | ||||
| override_data_id_root_add(*data, | |||||
| id_hierarchy_root_reference, | |||||
| id_root_reference, | |||||
| id_instance_hint, | |||||
| is_override_instancing_object); | |||||
| } | |||||
| else if (ID_IS_OVERRIDABLE_LIBRARY(id_root_reference)) { | |||||
| override_data_id_root_add(*data, | |||||
| 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, | |||||
| LinkNode *id_root_list, | |||||
| 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 (; id_root_list != nullptr; id_root_list = id_root_list->next) { | |||||
| OutlinerLiboverrideDataIDRoot *data_idroot = static_cast<OutlinerLiboverrideDataIDRoot *>( | |||||
| id_root_list->link); | |||||
| /* 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; | ID *id_root_override = nullptr; | ||||
| success = BKE_lib_override_library_create(bmain, | success = BKE_lib_override_library_create(&bmain, | ||||
| CTX_data_scene(C), | scene, | ||||
| CTX_data_view_layer(C), | view_layer, | ||||
| nullptr, | nullptr, | ||||
| id_root_reference, | data_idroot->id_root_reference, | ||||
| id_hierarchy_root_reference, | id_hierarchy_root_reference, | ||||
| id_instance_hint, | data_idroot->id_instance_hint, | ||||
| &id_root_override, | &id_root_override, | ||||
| data->do_fully_editable); | data.do_fully_editable); | ||||
| if (success) { | |||||
| BLI_assert(id_root_override != nullptr); | BLI_assert(id_root_override != nullptr); | ||||
| BLI_assert(!ID_IS_LINKED(id_root_override)); | BLI_assert(!ID_IS_LINKED(id_root_override)); | ||||
| BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(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)) { | if (ID_IS_LINKED(id_hierarchy_root_reference)) { | ||||
| BLI_assert( | BLI_assert(id_hierarchy_root_override->override_library->reference == | ||||
| id_root_override->override_library->hierarchy_root->override_library->reference == | |||||
| id_hierarchy_root_reference); | id_hierarchy_root_reference); | ||||
| data->id_hierarchy_root_override = id_root_override->override_library->hierarchy_root; | /* 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 { | else { | ||||
| BLI_assert(id_root_override->override_library->hierarchy_root == | BLI_assert(id_hierarchy_root_override == id_hierarchy_root_reference); | ||||
| id_hierarchy_root_reference); | |||||
| data->id_hierarchy_root_override = id_root_override->override_library->hierarchy_root; | |||||
| } | } | ||||
| 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. */ | /* Cleanup. */ | ||||
| BKE_main_id_newptr_and_tag_clear(bmain); | BKE_main_id_newptr_and_tag_clear(&bmain); | ||||
| BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); | BKE_main_id_tag_all(&bmain, LIB_TAG_DOIT, false); | ||||
| } | } | ||||
| /* Remove the instance empty from this scene, the items now have an overridden collection | /* Remove the instance empty from this scene, the items now have an overridden collection | ||||
| * instead. */ | * instead. */ | ||||
| if (success && is_override_instancing_object) { | if (success && data_idroot->is_override_instancing_object) { | ||||
| ED_object_base_free_and_unlink(bmain, scene, (Object *)id_instance_hint); | 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, | r_aggregated_success = r_aggregated_success && success; | ||||
| RPT_WARNING, | |||||
| "Could not create library override from data-block '%s'", | |||||
| id_root_reference->name); | |||||
| } | } | ||||
| } | } | ||||
| /* Clear system override flag from newly created overrides which linked reference were previously | /* Clear system override flag from newly created overrides which linked reference were previously | ||||
| * selected in the Outliner tree. */ | * selected in the Outliner tree. */ | ||||
| static void id_override_library_create_hierarchy_post_process(bContext *C, | static void id_override_library_create_hierarchy_process(bContext *C, | ||||
| OutlinerLibOverrideData *data) | ReportList *reports, | ||||
| OutlinerLibOverrideData &data) | |||||
| { | { | ||||
| Main *bmain = CTX_data_main(C); | 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 item : data.id_hierarchy_roots.items()) { | |||||
SeverinUnsubmitted Done Inline ActionsI will leave it up to, but you could do this here: for (auto [id_hierarchy_root_reference, id_root_list] : data.id_hierarchy_roots.items()) {
...
}Handy syntax for these kind of pair/tuple bindings. Severin: I will leave it up to, but you //could// do this here:
```
for (auto… | |||||
mont29AuthorUnsubmitted Done Inline ActionsNice, like that! Similar to python unpacking. mont29: Nice, like that! Similar to python unpacking. | |||||
| ID *id_hierarchy_root_reference = item.key; | |||||
SeverinUnsubmitted Done Inline ActionsAny reason for the _reference suffix? hierarchy_root_id/id_hierarchy_root seems fine. Severin: Any reason for the `_reference` suffix? `hierarchy_root_id`/`id_hierarchy_root` seems fine. | |||||
mont29AuthorUnsubmitted Done Inline Actions_reference reffer to the fact that this is a (linked) reference of an override (so _reference vs _override suffixes). mont29: `_reference` reffer to the fact that this is a (linked) reference of an override (so… | |||||
| LinkNode *id_root_list = item.value; | |||||
| id_override_library_create_hierarchy( | |||||
| *bmain, scene, view_layer, data, id_hierarchy_root_reference, id_root_list, 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; | ID *id_iter; | ||||
| FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { | FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { | ||||
| if (ID_IS_LINKED(id_iter) || !ID_IS_OVERRIDE_LIBRARY_REAL(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) { | |||||
| continue; | 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; | id_iter->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED; | ||||
| } | } | ||||
| } | } | ||||
| FOREACH_MAIN_ID_END; | FOREACH_MAIN_ID_END; | ||||
| } | } | ||||
| static void id_override_library_toggle_flag_fn(bContext *UNUSED(C), | static void id_override_library_toggle_flag_fn(bContext *UNUSED(C), | ||||
| ReportList *UNUSED(reports), | ReportList *UNUSED(reports), | ||||
| ▲ Show 20 Lines • Show All 1,206 Lines • ▼ Show 20 Lines | switch (event) { | ||||
| case OUTLINER_IDOP_LOCAL: { | case OUTLINER_IDOP_LOCAL: { | ||||
| /* make local */ | /* make local */ | ||||
| outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_local_fn, nullptr); | outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_local_fn, nullptr); | ||||
| ED_undo_push(C, "Localized Data"); | ED_undo_push(C, "Localized Data"); | ||||
| break; | break; | ||||
| } | } | ||||
| case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE: { | case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE: { | ||||
| OutlinerLibOverrideData override_data{}; | OutlinerLibOverrideData override_data{}; | ||||
| outliner_do_libdata_operation( | override_data.do_hierarchy = false; | ||||
| C, op->reports, scene, space_outliner, id_override_library_create_fn, &override_data); | override_data.do_fully_editable = true; | ||||
| override_data_init(override_data); | |||||
| 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); | |||||
| override_data_clear(override_data); | |||||
| ED_undo_push(C, "Overridden Data"); | ED_undo_push(C, "Overridden Data"); | ||||
| break; | break; | ||||
| } | } | ||||
| case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY: { | case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY: { | ||||
| OutlinerLibOverrideData override_data{}; | OutlinerLibOverrideData override_data{}; | ||||
| override_data.do_hierarchy = true; | override_data.do_hierarchy = true; | ||||
| override_data.do_fully_editable = U.experimental.use_override_new_fully_editable; | override_data.do_fully_editable = U.experimental.use_override_new_fully_editable; | ||||
| override_data_init(override_data); | |||||
| outliner_do_libdata_operation(C, | outliner_do_libdata_operation(C, | ||||
| op->reports, | op->reports, | ||||
| scene, | scene, | ||||
| space_outliner, | space_outliner, | ||||
| id_override_library_create_hierarchy_pre_process_fn, | id_override_library_create_hierarchy_pre_process_fn, | ||||
| &override_data); | &override_data); | ||||
| outliner_do_libdata_operation( | |||||
| C, op->reports, scene, space_outliner, id_override_library_create_fn, &override_data); | id_override_library_create_hierarchy_process(C, op->reports, override_data); | ||||
| id_override_library_create_hierarchy_post_process(C, &override_data); | |||||
| override_data_clear(override_data); | |||||
| ED_undo_push(C, "Overridden Data Hierarchy"); | ED_undo_push(C, "Overridden Data Hierarchy"); | ||||
| break; | break; | ||||
| } | } | ||||
| case OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE: { | case OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE: { | ||||
| outliner_do_libdata_operation(C, | outliner_do_libdata_operation(C, | ||||
| op->reports, | op->reports, | ||||
| scene, | scene, | ||||
| ▲ Show 20 Lines • Show All 866 Lines • Show Last 20 Lines | |||||
I guess this should be disabled for the commit?