From c6509775341ddc68b7f3fcd5b6ca4fea664c3100 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 7 Feb 2025 12:28:56 +0100 Subject: [PATCH] Grease Pencil: Make brush asset pinned materials local Keeping these as linked datablocks to the brush does not match the idea that assets should generally be appended, and leads to some confusing situations with linked materials on objects. Now use either a local material with matching weak library reference or make a local copy if it does not exist yet. This also add weak library references to the materials in the 2D Animation template, so they will be reused. A problem is that weak library references include a full path to assets blend files, including the Blender version for the essentials assets files. This means weak library references do not work across platforms and Blender versions. Another known limitation is that if the (linked) Brush Asset material is edited, and there is already a local copy of it, this local copy will remain unchanged and will be used by future strokes as well. Ref #131186 Pull Request: https://projects.blender.org/blender/blender/pulls/134226 --- source/blender/blenkernel/BKE_asset_edit.hh | 5 +++ source/blender/blenkernel/BKE_main.hh | 15 ++++++++ .../blender/blenkernel/intern/asset_edit.cc | 33 +++++++++++++++++ .../blenkernel/intern/grease_pencil.cc | 14 ++++++-- source/blender/blenkernel/intern/main.cc | 35 ++++++++++++++++--- .../blenloader/intern/versioning_defaults.cc | 11 ++++++ 6 files changed, 106 insertions(+), 7 deletions(-) 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;