diff --git a/source/blender/blenkernel/BKE_asset_edit.hh b/source/blender/blenkernel/BKE_asset_edit.hh index bdfc150b709..d7dcacb9e41 100644 --- a/source/blender/blenkernel/BKE_asset_edit.hh +++ b/source/blender/blenkernel/BKE_asset_edit.hh @@ -68,4 +68,9 @@ bool asset_edit_id_save(Main &global_main, const ID &id, ReportList &reports); ID *asset_edit_id_revert(Main &global_main, ID &id, ReportList &reports); bool asset_edit_id_delete(Main &global_main, ID &id, ReportList &reports); +/** Find a local copy of the asset. */ +ID *asset_edit_id_find_local(Main &global_main, ID &id); +/** Ensure a local copy of the asset exists. */ +ID *asset_edit_id_ensure_local(Main &global_main, ID &id); + } // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_main.hh b/source/blender/blenkernel/BKE_main.hh index a7d55e3299d..0d3efa1f411 100644 --- a/source/blender/blenkernel/BKE_main.hh +++ b/source/blender/blenkernel/BKE_main.hh @@ -476,6 +476,21 @@ void BKE_main_library_weak_reference_remove_item( const char *library_id_name, ID *old_id) ATTR_NONNULL(); +/** + * Find local ID with weak library reference matching library and ID name. + * For cases where creating a full MainLibraryWeakReferenceMap is unnecessary. + */ +ID *BKE_main_library_weak_reference_find(Main *bmain, + const char *library_filepath, + const char *library_id_name); + +/** + * Add library weak reference to ID, referencing the specified library and ID name. + * For cases where creating a full MainLibraryWeakReferenceMap is unnecessary.*/ +void BKE_main_library_weak_reference_add(ID *id, + const char *library_filepath, + const char *library_id_name); + /* *** Generic utils to loop over whole Main database. *** */ #define FOREACH_MAIN_LISTBASE_ID_BEGIN(_lb, _id) \ diff --git a/source/blender/blenkernel/intern/asset_edit.cc b/source/blender/blenkernel/intern/asset_edit.cc index 6c9a9903069..f3400222213 100644 --- a/source/blender/blenkernel/intern/asset_edit.cc +++ b/source/blender/blenkernel/intern/asset_edit.cc @@ -10,6 +10,7 @@ #include "BLI_path_utils.hh" #include "BLI_string.h" +#include "DNA_ID.h" #include "DNA_asset_types.h" #include "DNA_space_types.h" @@ -386,4 +387,36 @@ bool asset_edit_id_is_writable(const ID &id) return asset_edit_id_is_editable(id) && (id.lib->runtime.tag & LIBRARY_ASSET_FILE_WRITABLE); } +ID *asset_edit_id_find_local(Main &global_main, ID &id) +{ + if (!asset_edit_id_is_editable(id)) { + return &id; + } + + /* Make filepath relative to match weak ref, it might not be if Library datablock is new. */ + char lib_filepath[FILE_MAX]; + STRNCPY(lib_filepath, id.lib->filepath); + BLI_path_rel(lib_filepath, BKE_main_blendfile_path(&global_main)); + + return BKE_main_library_weak_reference_find(&global_main, lib_filepath, id.name); +} + +ID *asset_edit_id_ensure_local(Main &global_main, ID &id) +{ + ID *local_id = asset_edit_id_find_local(global_main, id); + if (local_id) { + return local_id; + } + + /* Make local and create weak library reference for reuse. */ + BKE_lib_id_make_local(&global_main, + &id, + LIB_ID_MAKELOCAL_FORCE_COPY | LIB_ID_MAKELOCAL_INDIRECT | + LIB_ID_MAKELOCAL_ASSET_DATA_CLEAR); + BLI_assert(id.newid != nullptr); + BKE_main_library_weak_reference_add(id.newid, id.lib->filepath, id.name); + + return id.newid; +} + } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/grease_pencil.cc b/source/blender/blenkernel/intern/grease_pencil.cc index 2187b2053d8..5f4a2059093 100644 --- a/source/blender/blenkernel/intern/grease_pencil.cc +++ b/source/blender/blenkernel/intern/grease_pencil.cc @@ -12,6 +12,7 @@ #include "BKE_action.hh" #include "BKE_anim_data.hh" #include "BKE_animsys.h" +#include "BKE_asset_edit.hh" #include "BKE_bake_data_block_id.hh" #include "BKE_curves.hh" #include "BKE_customdata.hh" @@ -2465,6 +2466,11 @@ static Material *grease_pencil_object_material_ensure_from_brush_pinned(Main *bm { Material *ma = (brush->gpencil_settings) ? brush->gpencil_settings->material : nullptr; + if (ma) { + /* Ensure we assign a local datablock if this is an editable asset. */ + ma = reinterpret_cast(blender::bke::asset_edit_id_ensure_local(*bmain, ma->id)); + } + /* check if the material is already on object material slots and add it if missing */ if (ma && BKE_object_material_index_get(ob, ma) < 0) { /* The object's active material is what's used for the unpinned material. Do not touch it @@ -2509,8 +2515,12 @@ Material *BKE_grease_pencil_object_material_alt_ensure_from_brush(Main *bmain, { Material *material_alt = (brush->gpencil_settings) ? brush->gpencil_settings->material_alt : nullptr; - if (material_alt && BKE_object_material_slot_find_index(ob, material_alt) != -1) { - return material_alt; + if (material_alt) { + material_alt = reinterpret_cast( + blender::bke::asset_edit_id_find_local(*bmain, material_alt->id)); + if (material_alt && BKE_object_material_slot_find_index(ob, material_alt) != -1) { + return material_alt; + } } return BKE_grease_pencil_object_material_ensure_from_brush(bmain, ob, brush); diff --git a/source/blender/blenkernel/intern/main.cc b/source/blender/blenkernel/intern/main.cc index 4f45a69de19..56eb2a0f621 100644 --- a/source/blender/blenkernel/intern/main.cc +++ b/source/blender/blenkernel/intern/main.cc @@ -711,14 +711,10 @@ void BKE_main_library_weak_reference_add_item( BLI_assert(new_id->library_weak_reference == nullptr); BLI_assert(BKE_idtype_idcode_append_is_reusable(GS(new_id->name))); - new_id->library_weak_reference = static_cast( - MEM_mallocN(sizeof(*(new_id->library_weak_reference)), __func__)); - const LibWeakRefKey key{library_filepath, library_id_name}; library_weak_reference_mapping->map.add_new(key, new_id); - STRNCPY(new_id->library_weak_reference->library_filepath, library_filepath); - STRNCPY(new_id->library_weak_reference->library_id_name, library_id_name); + BKE_main_library_weak_reference_add(new_id, library_filepath, library_id_name); } void BKE_main_library_weak_reference_update_item( @@ -760,6 +756,35 @@ void BKE_main_library_weak_reference_remove_item( MEM_SAFE_FREE(old_id->library_weak_reference); } +ID *BKE_main_library_weak_reference_find(Main *bmain, + const char *library_filepath, + const char *library_id_name) +{ + ListBase *id_list = which_libbase(bmain, GS(library_id_name)); + LISTBASE_FOREACH (ID *, existing_id, id_list) { + if (existing_id->library_weak_reference && + STREQ(existing_id->library_weak_reference->library_id_name, library_id_name) && + STREQ(existing_id->library_weak_reference->library_filepath, library_filepath)) + { + return existing_id; + } + } + + return nullptr; +} + +void BKE_main_library_weak_reference_add(ID *local_id, + const char *library_filepath, + const char *library_id_name) +{ + if (local_id->library_weak_reference == nullptr) { + local_id->library_weak_reference = MEM_cnew(__func__); + } + + STRNCPY(local_id->library_weak_reference->library_filepath, library_filepath); + STRNCPY(local_id->library_weak_reference->library_id_name, library_id_name); +} + BlendThumbnail *BKE_main_thumbnail_from_buffer(Main *bmain, const uint8_t *rect, const int size[2]) { BlendThumbnail *data = nullptr; diff --git a/source/blender/blenloader/intern/versioning_defaults.cc b/source/blender/blenloader/intern/versioning_defaults.cc index 89dd41e8fb5..5328a3f9482 100644 --- a/source/blender/blenloader/intern/versioning_defaults.cc +++ b/source/blender/blenloader/intern/versioning_defaults.cc @@ -505,6 +505,17 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) } } + /* Add library weak references to avoid duplicating materials from essentials. */ + const std::optional assets_path = BKE_appdir_folder_id(BLENDER_SYSTEM_DATAFILES, + "assets/brushes"); + if (assets_path.has_value()) { + const std::string assets_blend_path = *assets_path + "/essentials_brushes-gp_draw.blend"; + LISTBASE_FOREACH (Material *, material, &bmain->materials) { + BKE_main_library_weak_reference_add( + &material->id, assets_blend_path.c_str(), material->id.name); + } + } + /* Reset grease pencil paint modes. */ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { ToolSettings *ts = scene->toolsettings;