diff --git a/source/blender/blenkernel/BKE_lib_override.hh b/source/blender/blenkernel/BKE_lib_override.hh index 7c22df3e5d9..946357050e1 100644 --- a/source/blender/blenkernel/BKE_lib_override.hh +++ b/source/blender/blenkernel/BKE_lib_override.hh @@ -23,6 +23,8 @@ * of IDs in a given Main data-base. */ +#include + struct BlendFileReadReport; struct Collection; struct ID; @@ -338,11 +340,17 @@ bool BKE_lib_override_rna_property_find(PointerRNA *idpoin, /** * Find override property operation from given sub-item(s), if it exists. + * + * \param subitem_refid: + * \param subitem_locid: Only for RNA collections of ID pointers, the ID pointers + * referenced by the given names. Note that both must be set, or left unset. */ IDOverrideLibraryPropertyOperation *BKE_lib_override_library_property_operation_find( IDOverrideLibraryProperty *liboverride_property, const char *subitem_refname, const char *subitem_locname, + const std::optional &subitem_refid, + const std::optional &subitem_locid, int subitem_refindex, int subitem_locindex, bool strict, @@ -355,6 +363,8 @@ IDOverrideLibraryPropertyOperation *BKE_lib_override_library_property_operation_ short operation, const char *subitem_refname, const char *subitem_locname, + const std::optional &subitem_refid, + const std::optional &subitem_locid, int subitem_refindex, int subitem_locindex, bool strict, diff --git a/source/blender/blenkernel/intern/lib_override.cc b/source/blender/blenkernel/intern/lib_override.cc index 837d6199527..d5a97553ca1 100644 --- a/source/blender/blenkernel/intern/lib_override.cc +++ b/source/blender/blenkernel/intern/lib_override.cc @@ -3752,15 +3752,73 @@ void BKE_lib_override_library_property_delete(IDOverrideLibrary *liboverride, lib_override_library_property_delete(liboverride, liboverride_property, true); } +static IDOverrideLibraryPropertyOperation *liboverride_opop_find_name_lib_iterative( + ListBase *liboverride_operations, + const char *subitem_main_name, + const char *subitem_other_name, + const std::optional &subitem_main_id, + const std::optional &subitem_other_id, + const size_t offesetof_opop_main_name, + const size_t offesetof_opop_other_name, + const size_t offesetof_opop_main_id, + const size_t offesetof_opop_other_id) +{ + const bool do_ids(subitem_main_id); + IDOverrideLibraryPropertyOperation *opop; + for (opop = static_cast(BLI_findstring_ptr( + liboverride_operations, subitem_main_name, int(offesetof_opop_main_name))); + opop; + opop = static_cast(BLI_listbase_findafter_string_ptr( + reinterpret_cast(opop), subitem_main_name, int(offesetof_opop_main_name)))) + { + const char *opop_other_name = *reinterpret_cast(reinterpret_cast(opop) + + offesetof_opop_other_name); + const bool opop_use_id = (opop->flag & LIBOVERRIDE_OP_FLAG_IDPOINTER_ITEM_USE_ID) != 0; + + if (do_ids && opop_use_id) { + /* Skip if ID pointers are expected valid and they do not exactly match. */ + const ID *opop_main_id = *reinterpret_cast(reinterpret_cast(opop) + + offesetof_opop_main_id); + if (*subitem_main_id != opop_main_id) { + continue; + } + const ID *opop_other_id = *reinterpret_cast(reinterpret_cast(opop) + + offesetof_opop_other_id); + if (*subitem_other_id != opop_other_id) { + continue; + } + } + + /* Only check other name if ID handling is matching between given search parameters and + * current liboverride operation (i.e. if both have valid ID pointers, or both have none). */ + if ((do_ids && opop_use_id) || (!do_ids && !opop_use_id)) { + if (!subitem_other_name && !opop_other_name) { + return opop; + } + if (subitem_other_name && opop_other_name && STREQ(subitem_other_name, opop_other_name)) { + return opop; + } + } + + /* No exact match found, keep cheking the rest of the list of operations. */ + } + + return nullptr; +} + IDOverrideLibraryPropertyOperation *BKE_lib_override_library_property_operation_find( IDOverrideLibraryProperty *liboverride_property, const char *subitem_refname, const char *subitem_locname, + const std::optional &subitem_refid, + const std::optional &subitem_locid, const int subitem_refindex, const int subitem_locindex, const bool strict, bool *r_strict) { + BLI_assert(!subitem_refid == !subitem_locid); + IDOverrideLibraryPropertyOperation *opop; const int subitem_defindex = -1; @@ -3769,41 +3827,37 @@ IDOverrideLibraryPropertyOperation *BKE_lib_override_library_property_operation_ } if (subitem_locname != nullptr) { - opop = static_cast( - BLI_findstring_ptr(&liboverride_property->operations, - subitem_locname, - offsetof(IDOverrideLibraryPropertyOperation, subitem_local_name))); + opop = liboverride_opop_find_name_lib_iterative( + &liboverride_property->operations, + subitem_locname, + subitem_refname, + subitem_locid, + subitem_refid, + offsetof(IDOverrideLibraryPropertyOperation, subitem_local_name), + offsetof(IDOverrideLibraryPropertyOperation, subitem_reference_name), + offsetof(IDOverrideLibraryPropertyOperation, subitem_local_id), + offsetof(IDOverrideLibraryPropertyOperation, subitem_reference_id)); - if (opop == nullptr) { - return nullptr; + if (opop != nullptr) { + return opop; } - - if (subitem_refname == nullptr || opop->subitem_reference_name == nullptr) { - return subitem_refname == opop->subitem_reference_name ? opop : nullptr; - } - return (subitem_refname != nullptr && opop->subitem_reference_name != nullptr && - STREQ(subitem_refname, opop->subitem_reference_name)) ? - opop : - nullptr; } if (subitem_refname != nullptr) { - opop = static_cast( - BLI_findstring_ptr(&liboverride_property->operations, - subitem_refname, - offsetof(IDOverrideLibraryPropertyOperation, subitem_reference_name))); + opop = liboverride_opop_find_name_lib_iterative( + &liboverride_property->operations, + subitem_refname, + subitem_locname, + subitem_refid, + subitem_locid, + offsetof(IDOverrideLibraryPropertyOperation, subitem_reference_name), + offsetof(IDOverrideLibraryPropertyOperation, subitem_local_name), + offsetof(IDOverrideLibraryPropertyOperation, subitem_reference_id), + offsetof(IDOverrideLibraryPropertyOperation, subitem_local_id)); - if (opop == nullptr) { - return nullptr; + if (opop != nullptr) { + return opop; } - - if (subitem_locname == nullptr || opop->subitem_local_name == nullptr) { - return subitem_locname == opop->subitem_local_name ? opop : nullptr; - } - return (subitem_locname != nullptr && opop->subitem_local_name != nullptr && - STREQ(subitem_locname, opop->subitem_local_name)) ? - opop : - nullptr; } if ((opop = static_cast(BLI_listbase_bytes_find( @@ -3847,16 +3901,22 @@ IDOverrideLibraryPropertyOperation *BKE_lib_override_library_property_operation_ const short operation, const char *subitem_refname, const char *subitem_locname, + const std::optional &subitem_refid, + const std::optional &subitem_locid, const int subitem_refindex, const int subitem_locindex, const bool strict, bool *r_strict, bool *r_created) { + BLI_assert(!subitem_refid == !subitem_locid); + IDOverrideLibraryPropertyOperation *opop = BKE_lib_override_library_property_operation_find( liboverride_property, subitem_refname, subitem_locname, + subitem_refid, + subitem_locid, subitem_refindex, subitem_locindex, strict, @@ -3874,6 +3934,12 @@ IDOverrideLibraryPropertyOperation *BKE_lib_override_library_property_operation_ opop->subitem_local_index = subitem_locindex; opop->subitem_reference_index = subitem_refindex; + if (subitem_refid) { + opop->subitem_reference_id = *subitem_refid; + opop->subitem_local_id = *subitem_locid; + opop->flag |= LIBOVERRIDE_OP_FLAG_IDPOINTER_ITEM_USE_ID; + } + BLI_addtail(&liboverride_property->operations, opop); if (r_created) { @@ -4816,8 +4882,8 @@ void BKE_lib_override_debug_print(IDOverrideLibrary *liboverride, const char *in } std::cout << "] "; if (opop->subitem_reference_name || opop->subitem_local_name) { - std::cout << "(" << opop->subitem_reference_name << " -> " << opop->subitem_local_name - << ")"; + std::cout << "(" << opop->subitem_reference_name << " <" << opop->subitem_reference_id + << "> -> " << opop->subitem_local_name << " <" << opop->subitem_local_id << ">)"; } else if (opop->subitem_reference_index >= 0 || opop->subitem_local_index >= 0) { std::cout << "(" << opop->subitem_reference_index << " -> " << opop->subitem_local_index diff --git a/source/blender/blenkernel/intern/lib_query.cc b/source/blender/blenkernel/intern/lib_query.cc index 8fb29d0393a..3b1c7a61011 100644 --- a/source/blender/blenkernel/intern/lib_query.cc +++ b/source/blender/blenkernel/intern/lib_query.cc @@ -323,6 +323,14 @@ static bool library_foreach_ID_link(Main *bmain, IDWALK_CB_USER | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE); CALLBACK_INVOKE_ID(id->override_library->hierarchy_root, IDWALK_CB_LOOPBACK); + LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &id->override_library->properties) { + LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) { + CALLBACK_INVOKE_ID(opop->subitem_reference_id, + IDWALK_CB_DIRECT_WEAK_LINK | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE); + CALLBACK_INVOKE_ID(opop->subitem_local_id, + IDWALK_CB_DIRECT_WEAK_LINK | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE); + } + } } IDP_foreach_property(id->properties, diff --git a/source/blender/blenlib/BLI_listbase.h b/source/blender/blenlib/BLI_listbase.h index 1d526aa82ee..394f5b23f5d 100644 --- a/source/blender/blenlib/BLI_listbase.h +++ b/source/blender/blenlib/BLI_listbase.h @@ -63,6 +63,12 @@ void *BLI_findstring(const struct ListBase *listbase, void *BLI_findstring_ptr(const struct ListBase *listbase, const char *id, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); +/** + * Finds the first element in the listbase after the given \a link element which contains a pointer + * to the null-terminated string \a id at the specified offset, returning NULL if not found. + */ +void *BLI_listbase_findafter_string_ptr(struct Link *link, const char *id, const int offset); + /** * Finds the first element of listbase which contains the specified pointer value * at the specified offset, returning NULL if not found. diff --git a/source/blender/blenlib/intern/listbase.cc b/source/blender/blenlib/intern/listbase.cc index b4178569bfe..06d000d152f 100644 --- a/source/blender/blenlib/intern/listbase.cc +++ b/source/blender/blenlib/intern/listbase.cc @@ -660,6 +660,22 @@ void *BLI_rfindstring_ptr(const ListBase *listbase, const char *id, const int of return nullptr; } +void *BLI_listbase_findafter_string_ptr(Link *link, const char *id, const int offset) +{ + const char *id_iter; + + for (link = link->next; link; link = link->next) { + /* exact copy of BLI_findstring(), except for this line */ + id_iter = *((const char **)(((const char *)link) + offset)); + + if (id[0] == id_iter[0] && STREQ(id, id_iter)) { + return link; + } + } + + return nullptr; +} + void *BLI_findptr(const ListBase *listbase, const void *ptr, const int offset) { LISTBASE_FOREACH (Link *, link, listbase) { diff --git a/source/blender/blenloader/intern/readfile.cc b/source/blender/blenloader/intern/readfile.cc index fd8622e10b0..a5075f0d6b9 100644 --- a/source/blender/blenloader/intern/readfile.cc +++ b/source/blender/blenloader/intern/readfile.cc @@ -1922,6 +1922,13 @@ static void lib_link_id(BlendLibReader *reader, ID *id) BLO_read_id_address(reader, id, &id->override_library->reference); BLO_read_id_address(reader, id, &id->override_library->storage); BLO_read_id_address(reader, id, &id->override_library->hierarchy_root); + + LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &id->override_library->properties) { + LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) { + BLO_read_id_address(reader, id, &opop->subitem_reference_id); + BLO_read_id_address(reader, id, &opop->subitem_local_id); + } + } } lib_link_id_embedded_id(reader, id); diff --git a/source/blender/editors/interface/interface_ops.cc b/source/blender/editors/interface/interface_ops.cc index c0fe93fc55c..ca97cb467d2 100644 --- a/source/blender/editors/interface/interface_ops.cc +++ b/source/blender/editors/interface/interface_ops.cc @@ -699,7 +699,7 @@ static int override_remove_button_exec(bContext *C, wmOperator *op) /* Remove override operation for given item, * add singular operations for the other items as needed. */ IDOverrideLibraryPropertyOperation *opop = BKE_lib_override_library_property_operation_find( - oprop, nullptr, nullptr, index, index, false, &is_strict_find); + oprop, nullptr, nullptr, {}, {}, index, index, false, &is_strict_find); BLI_assert(opop != nullptr); if (!is_strict_find) { /* No specific override operation, we have to get generic one, @@ -708,7 +708,7 @@ static int override_remove_button_exec(bContext *C, wmOperator *op) for (int idx = RNA_property_array_length(&ptr, prop); idx--;) { if (idx != index) { BKE_lib_override_library_property_operation_get( - oprop, opop->operation, nullptr, nullptr, idx, idx, true, nullptr, nullptr); + oprop, opop->operation, nullptr, nullptr, {}, {}, idx, idx, true, nullptr, nullptr); } } } diff --git a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc index dd9bd4b07db..15bc8725b67 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc +++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc @@ -403,8 +403,15 @@ void OverrideRNAPathTreeBuilder::ensure_entire_collection( item_idx, nullptr); IDOverrideLibraryPropertyOperation *item_operation = - BKE_lib_override_library_property_operation_find( - &override_data.override_property, nullptr, nullptr, -1, item_idx, false, nullptr); + BKE_lib_override_library_property_operation_find(&override_data.override_property, + nullptr, + nullptr, + {}, + {}, + -1, + item_idx, + false, + nullptr); TreeElement *current_te = nullptr; TreeElement *existing_te = path_te_map.lookup_default(coll_item_path, nullptr); diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index e4f5406ad3a..6dff5c7b43a 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -244,6 +244,11 @@ typedef struct IDOverrideLibraryPropertyOperation { char *subitem_local_name; int subitem_reference_index; int subitem_local_index; + /** Additional pointer to an ID. Only used and relevant when the related RNA collection stores ID + * pointers, to help disambiguate cases where several IDs from different libraries have the exact + * same name. */ + struct ID *subitem_reference_id; + struct ID *subitem_local_id; } IDOverrideLibraryPropertyOperation; /* IDOverrideLibraryPropertyOperation->operation. */ @@ -278,6 +283,12 @@ enum { * reference linked data. */ LIBOVERRIDE_OP_FLAG_IDPOINTER_MATCH_REFERENCE = 1 << 8, + /** + * For overrides of ID pointers within RNA collections: this override is using the ID + * pointer in addition to the item name (to fully disambiguate the reference, since IDs from + * different libraries can have a same name). + */ + LIBOVERRIDE_OP_FLAG_IDPOINTER_ITEM_USE_ID = 1 << 9, }; /** A single overridden property, contain all operations on this one. */ diff --git a/source/blender/makesrna/intern/rna_ID.cc b/source/blender/makesrna/intern/rna_ID.cc index 9faf63dc216..f71acaad384 100644 --- a/source/blender/makesrna/intern/rna_ID.cc +++ b/source/blender/makesrna/intern/rna_ID.cc @@ -104,14 +104,14 @@ static const EnumPropertyItem rna_enum_override_library_property_operation_items "INSERT_AFTER", 0, "Insert After", - "Insert a new item into collection after the one referenced in subitem_reference_name or " - "_index"}, + "Insert a new item into collection after the one referenced in " + "subitem_reference_name/_id or _index"}, {LIBOVERRIDE_OP_INSERT_BEFORE, "INSERT_BEFORE", 0, "Insert Before", - "Insert a new item into collection before the one referenced in subitem_reference_name or " - "_index (NOT USED)"}, + "Insert a new item into collection before the one referenced in " + "subitem_reference_name/_id or _index (NOT USED)"}, {0, nullptr, 0, nullptr, nullptr}, }; @@ -931,8 +931,11 @@ static IDOverrideLibraryPropertyOperation *rna_ID_override_library_property_oper IDOverrideLibraryProperty *override_property, ReportList *reports, int operation, + const bool use_id, const char *subitem_refname, const char *subitem_locname, + ID *subitem_refid, + ID *subitem_locid, int subitem_refindex, int subitem_locindex) { @@ -943,6 +946,8 @@ static IDOverrideLibraryPropertyOperation *rna_ID_override_library_property_oper operation, subitem_refname, subitem_locname, + use_id ? std::optional(subitem_refid) : std::nullopt, + use_id ? std::optional(subitem_locid) : std::nullopt, subitem_refindex, subitem_locindex, false, @@ -1732,6 +1737,12 @@ static void rna_def_ID_override_library_property_operation(BlenderRNA *brna) 0, "Match Reference", "The ID pointer overridden by this operation is expected to match the reference hierarchy"}, + {LIBOVERRIDE_OP_FLAG_IDPOINTER_ITEM_USE_ID, + "IDPOINTER_ITEM_USE_ID", + 0, + "ID Item Use ID Pointer", + "RNA collections of IDs only, the reference to the item also uses the ID pointer itself, " + "not only its name"}, {0, nullptr, 0, nullptr, nullptr}, }; @@ -1758,7 +1769,7 @@ static void rna_def_ID_override_library_property_operation(BlenderRNA *brna) nullptr, INT_MAX, "Subitem Reference Name", - "Used to handle insertions into collection"); + "Used to handle changes into collection"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* For now. */ RNA_def_property_string_funcs(prop, "rna_ID_override_library_property_operation_refname_get", @@ -1770,20 +1781,36 @@ static void rna_def_ID_override_library_property_operation(BlenderRNA *brna) nullptr, INT_MAX, "Subitem Local Name", - "Used to handle insertions into collection"); + "Used to handle changes into collection"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* For now. */ RNA_def_property_string_funcs(prop, "rna_ID_override_library_property_operation_locname_get", "rna_ID_override_library_property_operation_locname_length", nullptr); + prop = RNA_def_pointer(srna, + "subitem_reference_id", + "ID", + "Subitem Reference ID", + "Collection of IDs only, used to disambiguate between potential IDs with " + "same name from different libraries"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* For now. */ + + prop = RNA_def_pointer(srna, + "subitem_local_id", + "ID", + "Subitem Local ID", + "Collection of IDs only, used to disambiguate between potential IDs with " + "same name from different libraries"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* For now. */ + prop = RNA_def_int(srna, "subitem_reference_index", -1, -1, INT_MAX, "Subitem Reference Index", - "Used to handle insertions into collection", + "Used to handle changes into collection", -1, INT_MAX); RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* For now. */ @@ -1794,7 +1821,7 @@ static void rna_def_ID_override_library_property_operation(BlenderRNA *brna) -1, INT_MAX, "Subitem Local Index", - "Used to handle insertions into collection", + "Used to handle changes into collection", -1, INT_MAX); RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* For now. */ @@ -1822,25 +1849,41 @@ static void rna_def_ID_override_library_property_operations(BlenderRNA *brna, Pr "Operation", "What override operation is performed"); RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); + parm = RNA_def_boolean( + func, + "use_id", + false, + "Use ID Pointer Subitem", + "Whether the found or created liboverride operation should use ID pointers or not"); parm = RNA_def_string(func, "subitem_reference_name", nullptr, INT_MAX, "Subitem Reference Name", - "Used to handle insertions into collection"); + "Used to handle insertions or ID replacements into collection"); parm = RNA_def_string(func, "subitem_local_name", nullptr, INT_MAX, "Subitem Local Name", - "Used to handle insertions into collection"); + "Used to handle insertions or ID replacements into collection"); + parm = RNA_def_pointer(func, + "subitem_reference_id", + "ID", + "Subitem Reference ID", + "Used to handle ID replacements into collection"); + parm = RNA_def_pointer(func, + "subitem_local_id", + "ID", + "Subitem Local ID", + "Used to handle ID replacements into collection"); parm = RNA_def_int(func, "subitem_reference_index", -1, -1, INT_MAX, "Subitem Reference Index", - "Used to handle insertions into collection", + "Used to handle insertions or ID replacements into collection", -1, INT_MAX); parm = RNA_def_int(func, @@ -1849,7 +1892,7 @@ static void rna_def_ID_override_library_property_operations(BlenderRNA *brna, Pr -1, INT_MAX, "Subitem Local Index", - "Used to handle insertions into collection", + "Used to handle insertions or ID replacements into collection", -1, INT_MAX); parm = RNA_def_pointer(func, diff --git a/source/blender/makesrna/intern/rna_access_compare_override.cc b/source/blender/makesrna/intern/rna_access_compare_override.cc index b532be312bf..1a4e84126d9 100644 --- a/source/blender/makesrna/intern/rna_access_compare_override.cc +++ b/source/blender/makesrna/intern/rna_access_compare_override.cc @@ -7,6 +7,7 @@ */ #include +#include #include @@ -874,6 +875,8 @@ bool RNA_struct_override_matches(Main *bmain, LIBOVERRIDE_OP_REPLACE, nullptr, nullptr, + {}, + {}, -1, -1, false, @@ -1000,10 +1003,91 @@ bool RNA_struct_override_store(Main *bmain, return changed; } +static bool rna_property_override_collection_subitem_name_id_match( + const char *item_name, + const int item_name_len, + const bool do_id_pointer, + const std::optional &item_id, + PointerRNA *ptr_item_name) +{ + BLI_assert(!do_id_pointer || RNA_struct_is_ID(ptr_item_name->type)); + + bool is_match = false; + + if (do_id_pointer) { + if (*item_id != static_cast(ptr_item_name->data)) { + /* If the ID pointer does not match, then there is no match, no need to check the + * name iteself. */ + return is_match; + } + } + + PropertyRNA *nameprop = ptr_item_name->type->nameproperty; + char name[256]; + char *nameptr; + int namelen; + + nameptr = RNA_property_string_get_alloc(ptr_item_name, nameprop, name, sizeof(name), &namelen); + + is_match = ((item_name_len == namelen) && STREQ(item_name, nameptr)); + + if (UNLIKELY(name != nameptr)) { + MEM_freeN(nameptr); + } + + return is_match; +} + +static bool rna_property_override_collection_subitem_name_id_lookup( + PointerRNA *ptr, + PropertyRNA *prop, + const char *item_name, + const int item_name_len, + const bool do_id_pointer, + const std::optional &item_id, + PointerRNA *r_ptr_item_name) +{ + /* NOTE: This code is very similar to the one from #RNA_property_collection_lookup_string_index, + * but it adds an extra early check on matching ID pointer. + * + * This custom code is needed because otherwise, it is only possible to check the first + * name-matched item found by #RNA_property_collection_lookup_string, and not potential other + * items having the same name. */ + if (do_id_pointer) { + BLI_assert(RNA_property_type(prop) == PROP_COLLECTION); + + /* We cannot use a potential `CollectionPropertyRNA->lookupstring` here. */ + CollectionPropertyIterator iter; + + RNA_property_collection_begin(ptr, prop, &iter); + for (; iter.valid; RNA_property_collection_next(&iter)) { + if (iter.ptr.data && iter.ptr.type->nameproperty) { + if (rna_property_override_collection_subitem_name_id_match( + item_name, item_name_len, do_id_pointer, item_id, &iter.ptr)) + { + *r_ptr_item_name = iter.ptr; + break; + } + } + } + RNA_property_collection_end(&iter); + + if (!iter.valid) { + memset(r_ptr_item_name, 0, sizeof(*r_ptr_item_name)); + } + + return bool(iter.valid); + } + else { + return bool(RNA_property_collection_lookup_string(ptr, prop, item_name, r_ptr_item_name)); + } +} + static void rna_property_override_collection_subitem_name_index_lookup( PointerRNA *ptr, PropertyRNA *prop, const char *item_name, + const std::optional &item_id, const int item_index, PointerRNA *r_ptr_item_name, PointerRNA *r_ptr_item_index) @@ -1011,21 +1095,23 @@ static void rna_property_override_collection_subitem_name_index_lookup( RNA_POINTER_INVALIDATE(r_ptr_item_name); RNA_POINTER_INVALIDATE(r_ptr_item_index); + PointerRNA collection_ptr_type; + RNA_property_collection_type_get(ptr, prop, &collection_ptr_type); + const bool do_id_pointer = item_id && RNA_struct_is_ID(collection_ptr_type.type); + + const int item_name_len = item_name ? int(strlen(item_name)) : 0; + /* First, lookup by index, but only validate if name also matches (or if there is no given name). + * + * Note that this is also beneficial on performances (when looking up in big collections), since + * typically index lookup will be faster than name lookup. */ if (item_index != -1) { if (RNA_property_collection_lookup_int(ptr, prop, item_index, r_ptr_item_index)) { - if (item_name != nullptr) { - PropertyRNA *nameprop = r_ptr_item_index->type->nameproperty; - char name[256], *nameptr; - int keylen = int(strlen(item_name)); - int namelen; - - nameptr = RNA_property_string_get_alloc( - r_ptr_item_index, nameprop, name, sizeof(name), &namelen); - - if (keylen == namelen && STREQ(nameptr, item_name)) { - /* Index and name both match. */ + if (item_name) { + if (rna_property_override_collection_subitem_name_id_match( + item_name, item_name_len, do_id_pointer, item_id, r_ptr_item_index)) + { *r_ptr_item_name = *r_ptr_item_index; return; } @@ -1033,18 +1119,20 @@ static void rna_property_override_collection_subitem_name_index_lookup( } } - if (item_name == nullptr) { + if (!item_name) { return; } - /* Then, lookup by name only. */ - if (RNA_property_collection_lookup_string(ptr, prop, item_name, r_ptr_item_name)) { + /* Then, lookup by name (+ id) only. */ + if (rna_property_override_collection_subitem_name_id_lookup( + ptr, prop, item_name, item_name_len, do_id_pointer, item_id, r_ptr_item_name)) + { RNA_POINTER_INVALIDATE(r_ptr_item_index); return; } - /* If name lookup failed, `r_ptr_item_name` is invalidated, so if index lookup was successful it - * will be the only valid return value. */ + /* If name (+ id) lookup failed, `r_ptr_item_name` is invalidated, so if index lookup was + * successful it will be the only valid return value. */ } static void rna_property_override_collection_subitem_lookup( @@ -1071,6 +1159,13 @@ static void rna_property_override_collection_subitem_lookup( return; } + const bool use_id_pointer = (opop->flag & LIBOVERRIDE_OP_FLAG_IDPOINTER_ITEM_USE_ID) != 0; + std::optional subitem_local_id = use_id_pointer ? std::optional(opop->subitem_local_id) : + std::nullopt; + std::optional subitem_reference_id = use_id_pointer ? + std::optional(opop->subitem_reference_id) : + std::nullopt; + RNA_POINTER_INVALIDATE(ptr_item_dst); RNA_POINTER_INVALIDATE(ptr_item_src); if (prop_storage != nullptr) { @@ -1083,12 +1178,14 @@ static void rna_property_override_collection_subitem_lookup( rna_property_override_collection_subitem_name_index_lookup(ptr_src, prop_src, opop->subitem_local_name, + subitem_local_id, opop->subitem_local_index, &ptr_item_src_name, &ptr_item_src_index); rna_property_override_collection_subitem_name_index_lookup(ptr_dst, prop_dst, opop->subitem_reference_name, + subitem_reference_id, opop->subitem_reference_index, &ptr_item_dst_name, &ptr_item_dst_index); @@ -1108,6 +1205,7 @@ static void rna_property_override_collection_subitem_lookup( ptr_dst, prop_dst, opop->subitem_local_name, + {}, opop->subitem_reference_index != -1 ? opop->subitem_reference_index : opop->subitem_local_index, &ptr_item_dst_name, @@ -1121,6 +1219,7 @@ static void rna_property_override_collection_subitem_lookup( ptr_dst, prop_dst, opop->subitem_local_name, + {}, opop->subitem_reference_index != -1 ? opop->subitem_reference_index : opop->subitem_local_index, &ptr_item_dst_name, @@ -1130,6 +1229,7 @@ static void rna_property_override_collection_subitem_lookup( rna_property_override_collection_subitem_name_index_lookup(ptr_src, prop_src, opop->subitem_reference_name, + {}, opop->subitem_local_index != -1 ? opop->subitem_local_index : opop->subitem_reference_index, @@ -1140,6 +1240,7 @@ static void rna_property_override_collection_subitem_lookup( rna_property_override_collection_subitem_name_index_lookup(ptr_dst, prop_dst, nullptr, + {}, opop->subitem_local_index, &ptr_item_dst_name, &ptr_item_dst_index); @@ -1148,6 +1249,7 @@ static void rna_property_override_collection_subitem_lookup( rna_property_override_collection_subitem_name_index_lookup(ptr_src, prop_src, nullptr, + {}, opop->subitem_reference_index, &ptr_item_src_name, &ptr_item_src_index); @@ -1158,6 +1260,7 @@ static void rna_property_override_collection_subitem_lookup( rna_property_override_collection_subitem_name_index_lookup(ptr_storage, prop_storage, opop->subitem_local_name, + subitem_local_id, opop->subitem_local_index, &ptr_item_storage_name, &ptr_item_storage_index); @@ -1165,6 +1268,7 @@ static void rna_property_override_collection_subitem_lookup( rna_property_override_collection_subitem_name_index_lookup(ptr_storage, prop_storage, opop->subitem_reference_name, + subitem_reference_id, opop->subitem_reference_index, &ptr_item_storage_name, &ptr_item_storage_index); @@ -1173,6 +1277,7 @@ static void rna_property_override_collection_subitem_lookup( rna_property_override_collection_subitem_name_index_lookup(ptr_storage, prop_storage, nullptr, + {}, opop->subitem_local_index, &ptr_item_storage_name, &ptr_item_storage_index); @@ -1593,7 +1698,7 @@ IDOverrideLibraryPropertyOperation *RNA_property_override_property_operation_fin } return BKE_lib_override_library_property_operation_find( - op, nullptr, nullptr, index, index, strict, r_strict); + op, nullptr, nullptr, {}, {}, index, index, strict, r_strict); } IDOverrideLibraryPropertyOperation *RNA_property_override_property_operation_get( @@ -1617,7 +1722,7 @@ IDOverrideLibraryPropertyOperation *RNA_property_override_property_operation_get } return BKE_lib_override_library_property_operation_get( - op, operation, nullptr, nullptr, index, index, strict, r_strict, r_created); + op, operation, nullptr, nullptr, {}, {}, index, index, strict, r_strict, r_created); } eRNAOverrideStatus RNA_property_override_library_status(Main *bmain, diff --git a/source/blender/makesrna/intern/rna_rna.cc b/source/blender/makesrna/intern/rna_rna.cc index da36b00a150..05acb94435b 100644 --- a/source/blender/makesrna/intern/rna_rna.cc +++ b/source/blender/makesrna/intern/rna_rna.cc @@ -1265,13 +1265,17 @@ struct RNACompareOverrideDiffPropPtrContext { /** RNA collection items: forcefully get an item property name, even if one of the items is * null/doesn't have one. Mutually exclusive with `no_prop_name`. */ bool do_force_name = false; + /** RNA collection ID items: also check and store item's ID pointers. */ + bool use_id_pointer = false; /** Information specific to RNA collections. */ - /* NOTE: names are typically set by a call to - * #rna_property_override_diff_propptr_validate_diffing. Indices are typically set directly from - * the loop over all RNA collections items in #rna_property_override_diff_default. */ + /* NOTE: names (and ID pointers, in case items are ID pointers) are typically set by a call + * to #rna_property_override_diff_propptr_validate_diffing. Indices are typically set directly + * from the loop over all RNA collections items in #rna_property_override_diff_default. */ std::optional rna_itemname_a; std::optional rna_itemname_b; + std::optional rna_itemid_a; + std::optional rna_itemid_b; int rna_itemindex_a = -1; int rna_itemindex_b = -1; @@ -1306,7 +1310,7 @@ static void rna_property_override_diff_propptr_validate_diffing( BLI_assert(propptr_a != nullptr); - if (do_force_name) { + if (do_force_name || ptrdiff_ctx.use_id_pointer) { BLI_assert(!no_prop_name); } @@ -1341,6 +1345,7 @@ static void rna_property_override_diff_propptr_validate_diffing( PropertyRNA *nameprop_b = (propptr_b != nullptr && propptr_b->type != nullptr) ? RNA_struct_name_property(propptr_b->type) : nullptr; + const bool do_id_pointer = ptrdiff_ctx.use_id_pointer && ptrdiff_ctx.is_id; /* NOTE: Until we have a `std::string` version of `RNA_property_string_get`, these C string * pointers and buffers are needed here. Otherwise, in case of C string allocation, if the @@ -1363,9 +1368,22 @@ static void rna_property_override_diff_propptr_validate_diffing( propptr_b, nameprop_b, buff_b, sizeof(buff_b), &rna_itemname_b_len); ptrdiff_ctx.rna_itemname_b = rna_itemname_b; } + + /* Note: This will always assign nullptr to these libpointers in case `do_id_lib` is false, + * which ensures that they will not affect the result of `ptrdiff_ctx.is_valid_for_diffing` in + * the last check below. */ + ID *rna_itemid_a = (do_id_pointer && propptr_a->data) ? static_cast(propptr_a->data) : + nullptr; + ID *rna_itemid_b = (do_id_pointer && propptr_b->data) ? static_cast(propptr_b->data) : + nullptr; + if (do_id_pointer) { + ptrdiff_ctx.rna_itemid_a = rna_itemid_a; + ptrdiff_ctx.rna_itemid_b = rna_itemid_b; + } + if (rna_itemname_a != nullptr && rna_itemname_b != nullptr) { - if (rna_itemname_a_len != rna_itemname_b_len || rna_itemname_a[0] != rna_itemname_b[0] || - !STREQ(rna_itemname_a, rna_itemname_b)) + if (rna_itemid_a != rna_itemid_b || rna_itemname_a_len != rna_itemname_b_len || + rna_itemname_a[0] != rna_itemname_b[0] || !STREQ(rna_itemname_a, rna_itemname_b)) { ptrdiff_ctx.is_valid_for_diffing = false; // printf("%s: different names\n", rna_path ? rna_path : ""); @@ -1469,6 +1487,8 @@ static void rna_property_override_diff_propptr(Main *bmain, LIBOVERRIDE_OP_REPLACE, subitem_refname, subitem_locname, + ptrdiff_ctx.rna_itemid_b, + ptrdiff_ctx.rna_itemid_a, rna_itemindex_b, rna_itemindex_a, true, @@ -1493,6 +1513,8 @@ static void rna_property_override_diff_propptr(Main *bmain, opop = BKE_lib_override_library_property_operation_find(op, subitem_refname, subitem_locname, + ptrdiff_ctx.rna_itemid_b, + ptrdiff_ctx.rna_itemid_a, rna_itemindex_b, rna_itemindex_a, true, @@ -1721,8 +1743,17 @@ void rna_property_override_diff_default(Main *bmain, RNAPropertyOverrideDiffCont op = BKE_lib_override_library_property_get(liboverride, rna_path, &created); if (op != nullptr && created) { - BKE_lib_override_library_property_operation_get( - op, LIBOVERRIDE_OP_REPLACE, nullptr, nullptr, -1, -1, true, nullptr, nullptr); + BKE_lib_override_library_property_operation_get(op, + LIBOVERRIDE_OP_REPLACE, + nullptr, + nullptr, + {}, + {}, + -1, + -1, + true, + nullptr, + nullptr); rnadiff_ctx.report_flag |= RNA_OVERRIDE_MATCH_RESULT_CREATED; } else { @@ -1746,8 +1777,17 @@ void rna_property_override_diff_default(Main *bmain, RNAPropertyOverrideDiffCont op = BKE_lib_override_library_property_get(liboverride, rna_path, &created); if (op != nullptr && created) { /* If not yet overridden... */ - BKE_lib_override_library_property_operation_get( - op, LIBOVERRIDE_OP_REPLACE, nullptr, nullptr, -1, -1, true, nullptr, nullptr); + BKE_lib_override_library_property_operation_get(op, + LIBOVERRIDE_OP_REPLACE, + nullptr, + nullptr, + {}, + {}, + -1, + -1, + true, + nullptr, + nullptr); rnadiff_ctx.report_flag |= RNA_OVERRIDE_MATCH_RESULT_CREATED; } } @@ -1777,8 +1817,17 @@ void rna_property_override_diff_default(Main *bmain, RNAPropertyOverrideDiffCont op = BKE_lib_override_library_property_get(liboverride, rna_path, &created); if (op != nullptr && created) { - BKE_lib_override_library_property_operation_get( - op, LIBOVERRIDE_OP_REPLACE, nullptr, nullptr, -1, -1, true, nullptr, nullptr); + BKE_lib_override_library_property_operation_get(op, + LIBOVERRIDE_OP_REPLACE, + nullptr, + nullptr, + {}, + {}, + -1, + -1, + true, + nullptr, + nullptr); if (created) { rnadiff_ctx.report_flag |= RNA_OVERRIDE_MATCH_RESULT_CREATED; } @@ -1804,8 +1853,17 @@ void rna_property_override_diff_default(Main *bmain, RNAPropertyOverrideDiffCont op = BKE_lib_override_library_property_get(liboverride, rna_path, &created); if (op != nullptr && created) { /* If not yet overridden... */ - BKE_lib_override_library_property_operation_get( - op, LIBOVERRIDE_OP_REPLACE, nullptr, nullptr, -1, -1, true, nullptr, nullptr); + BKE_lib_override_library_property_operation_get(op, + LIBOVERRIDE_OP_REPLACE, + nullptr, + nullptr, + {}, + {}, + -1, + -1, + true, + nullptr, + nullptr); rnadiff_ctx.report_flag |= RNA_OVERRIDE_MATCH_RESULT_CREATED; } } @@ -1835,8 +1893,17 @@ void rna_property_override_diff_default(Main *bmain, RNAPropertyOverrideDiffCont op = BKE_lib_override_library_property_get(liboverride, rna_path, &created); if (op != nullptr && created) { - BKE_lib_override_library_property_operation_get( - op, LIBOVERRIDE_OP_REPLACE, nullptr, nullptr, -1, -1, true, nullptr, nullptr); + BKE_lib_override_library_property_operation_get(op, + LIBOVERRIDE_OP_REPLACE, + nullptr, + nullptr, + {}, + {}, + -1, + -1, + true, + nullptr, + nullptr); rnadiff_ctx.report_flag |= RNA_OVERRIDE_MATCH_RESULT_CREATED; } else { @@ -1860,8 +1927,17 @@ void rna_property_override_diff_default(Main *bmain, RNAPropertyOverrideDiffCont op = BKE_lib_override_library_property_get(liboverride, rna_path, &created); if (op != nullptr && created) { /* If not yet overridden... */ - BKE_lib_override_library_property_operation_get( - op, LIBOVERRIDE_OP_REPLACE, nullptr, nullptr, -1, -1, true, nullptr, nullptr); + BKE_lib_override_library_property_operation_get(op, + LIBOVERRIDE_OP_REPLACE, + nullptr, + nullptr, + {}, + {}, + -1, + -1, + true, + nullptr, + nullptr); rnadiff_ctx.report_flag |= RNA_OVERRIDE_MATCH_RESULT_CREATED; } } @@ -1878,8 +1954,17 @@ void rna_property_override_diff_default(Main *bmain, RNAPropertyOverrideDiffCont op = BKE_lib_override_library_property_get(liboverride, rna_path, &created); if (op != nullptr && created) { /* If not yet overridden... */ - BKE_lib_override_library_property_operation_get( - op, LIBOVERRIDE_OP_REPLACE, nullptr, nullptr, -1, -1, true, nullptr, nullptr); + BKE_lib_override_library_property_operation_get(op, + LIBOVERRIDE_OP_REPLACE, + nullptr, + nullptr, + {}, + {}, + -1, + -1, + true, + nullptr, + nullptr); rnadiff_ctx.report_flag |= RNA_OVERRIDE_MATCH_RESULT_CREATED; } } @@ -1907,8 +1992,17 @@ void rna_property_override_diff_default(Main *bmain, RNAPropertyOverrideDiffCont op = BKE_lib_override_library_property_get(liboverride, rna_path, &created); if (op != nullptr && created) { /* If not yet overridden... */ - BKE_lib_override_library_property_operation_get( - op, LIBOVERRIDE_OP_REPLACE, nullptr, nullptr, -1, -1, true, nullptr, nullptr); + BKE_lib_override_library_property_operation_get(op, + LIBOVERRIDE_OP_REPLACE, + nullptr, + nullptr, + {}, + {}, + -1, + -1, + true, + nullptr, + nullptr); rnadiff_ctx.report_flag |= RNA_OVERRIDE_MATCH_RESULT_CREATED; } } @@ -1953,6 +2047,7 @@ void rna_property_override_diff_default(Main *bmain, RNAPropertyOverrideDiffCont int idx_a = 0; int idx_b = 0; std::optional prev_rna_itemname_a; + std::optional prev_rna_itemid_a; CollectionPropertyIterator iter_a, iter_b; RNA_property_collection_begin(ptr_a, rawprop_a, &iter_a); @@ -1985,6 +2080,7 @@ void rna_property_override_diff_default(Main *bmain, RNAPropertyOverrideDiffCont } ptrdiff_ctx.no_prop_name = no_prop_name; ptrdiff_ctx.do_force_name = !no_prop_name; + ptrdiff_ctx.use_id_pointer = !no_prop_name; ptrdiff_ctx.no_ownership = no_ownership; ptrdiff_ctx.property_type = PROP_COLLECTION; @@ -1994,6 +2090,7 @@ void rna_property_override_diff_default(Main *bmain, RNAPropertyOverrideDiffCont * when calling #rna_property_override_diff_propptr. */ ptrdiff_ctx.no_prop_name = true; ptrdiff_ctx.do_force_name = false; + ptrdiff_ctx.use_id_pointer = false; } const bool is_valid_for_diffing = ptrdiff_ctx.is_valid_for_diffing; @@ -2047,6 +2144,8 @@ void rna_property_override_diff_default(Main *bmain, RNAPropertyOverrideDiffCont (no_prop_name || !ptrdiff_ctx.rna_itemname_a) ? nullptr : ptrdiff_ctx.rna_itemname_a->c_str(), + prev_rna_itemid_a, + ptrdiff_ctx.rna_itemid_a, idx_a - 1, idx_a, true, @@ -2077,6 +2176,7 @@ void rna_property_override_diff_default(Main *bmain, RNAPropertyOverrideDiffCont else { prev_rna_itemname_a.reset(); } + prev_rna_itemid_a = ptrdiff_ctx.rna_itemid_a; if (!do_create && !equals) { abort = true; /* Early out in case we do not want to loop over whole collection. */ diff --git a/tests/python/bl_blendfile_library_overrides.py b/tests/python/bl_blendfile_library_overrides.py index 94b34895f13..6bc5d350f59 100644 --- a/tests/python/bl_blendfile_library_overrides.py +++ b/tests/python/bl_blendfile_library_overrides.py @@ -323,6 +323,19 @@ class TestLibraryOverridesComplex(TestHelper, unittest.TestCase): for coll_ in root_collection.children_recursive: liboverride_systemoverrideonly_hierarchy_validate(coll_, root_collection) + if coll_.override_library: + for op in coll_.override_library.properties: + for opop in op.operations: + assert 'IDPOINTER_ITEM_USE_ID' in opop.flag + print( + coll_, + opop.flag, + opop.subitem_reference_name, + opop.subitem_reference_id, + opop.subitem_local_name, + opop.subitem_local_id) + assert opop.subitem_reference_id.library is not None + assert opop.subitem_local_id.library is None if coll_.library is None else opop.subitem_local_id.library is not None for ob_ in root_collection.all_objects: liboverride_systemoverrideonly_hierarchy_validate(ob_, root_collection)