From 44d253d0c4f6ee26d6c5e379a6d6522c2558cc91 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 31 Jan 2024 19:53:30 +0100 Subject: [PATCH 01/14] Brush Assets: experiment storing brush asset outside Main This is an experiment, more restructuring and renaming would be needed if we do this. * One main database per asset blend file, so that there are no conflicts between the regular main and different asset blend files. * Various code for avoiding conflicts and untangling databases can be removed because of this. * Assets are appended to this, no linking and overrides. Revert and changed property highlighting is current broken due to this. This change does not necessarily need to be made along with the rest but it makes the brush name editable. * Some basic checks to avoid linking between regular main and asset main, but much more would be needed. * Tool settings brush pointers were changed to do no user counting, but really should not be part of ID management at all and handled purely through some runtime lookup outside of it. --- .../startup/bl_ui/properties_paint_common.py | 8 +- scripts/startup/bl_ui/space_view3d_toolbar.py | 8 +- source/blender/blenkernel/BKE_asset.hh | 5 + source/blender/blenkernel/BKE_brush.hh | 4 - source/blender/blenkernel/BKE_lib_override.hh | 11 - .../blenkernel/intern/asset_weak_reference.cc | 136 +++++++++ source/blender/blenkernel/intern/blendfile.cc | 193 +------------ source/blender/blenkernel/intern/brush.cc | 57 ---- .../blender/blenkernel/intern/lib_override.cc | 36 --- source/blender/blenkernel/intern/paint.cc | 19 +- .../blenkernel/intern/paint_toolslots.cc | 6 - source/blender/blenkernel/intern/scene.cc | 4 +- .../editors/interface/interface_templates.cc | 31 ++- .../blender/editors/sculpt_paint/paint_ops.cc | 47 ++-- source/blender/makesdna/DNA_ID.h | 9 + source/blender/makesrna/intern/makesrna.cc | 2 + source/blender/makesrna/intern/rna_ID.cc | 29 +- .../blender/makesrna/intern/rna_main_api.cc | 13 + .../blender/windowmanager/intern/wm_files.cc | 261 +----------------- .../windowmanager/intern/wm_init_exit.cc | 3 + 20 files changed, 250 insertions(+), 632 deletions(-) diff --git a/scripts/startup/bl_ui/properties_paint_common.py b/scripts/startup/bl_ui/properties_paint_common.py index 7b779860e3f..b91f0613e3c 100644 --- a/scripts/startup/bl_ui/properties_paint_common.py +++ b/scripts/startup/bl_ui/properties_paint_common.py @@ -157,13 +157,7 @@ class BrushSelectPanel(BrushPanel): col = row.column() col.menu("VIEW3D_MT_brush_context_menu", icon='DOWNARROW_HLT', text="") - # TODO: Need actual check if this is an asset from library. - # TODO: why is brush.asset_data None for these? - is_linked = brush.library - is_override = brush.override_library and brush.override_library.reference - is_asset_brush = is_linked or is_override - - if not is_asset_brush: + if not brush.is_asset_library_data and not brush.asset_data: # Legacy custom icon, mostly replaced by asset preview. layout.use_property_split = True layout.use_property_decorate = False diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index cdd8085fc28..d1d2f84777d 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -46,13 +46,7 @@ class VIEW3D_MT_brush_context_menu(Menu): layout.label(text="No brush selected", icon='INFO') return - # TODO: Need actual check if this is an asset from library. - # TODO: why is brush.asset_data None for these? - is_linked = brush.library - is_override = brush.override_library and brush.override_library.reference - is_asset_brush = is_linked or is_override - - if is_asset_brush: + if brush.is_asset_library_data: layout.operator("brush.asset_save_as", text="Duplicate Asset...", icon='DUPLICATE') layout.operator("brush.asset_delete", text="Delete Asset") diff --git a/source/blender/blenkernel/BKE_asset.hh b/source/blender/blenkernel/BKE_asset.hh index 110c0af7469..0a24997d70c 100644 --- a/source/blender/blenkernel/BKE_asset.hh +++ b/source/blender/blenkernel/BKE_asset.hh @@ -20,6 +20,7 @@ struct BlendDataReader; struct BlendWriter; struct ID; struct IDProperty; +struct Main; struct PreviewImage; using PreSaveFn = void (*)(void *asset_ptr, AssetMetaData *asset_data); @@ -81,3 +82,7 @@ void BKE_asset_weak_reference_free(AssetWeakReference **weak_ref); AssetWeakReference *BKE_asset_weak_reference_copy(AssetWeakReference *weak_ref); void BKE_asset_weak_reference_write(BlendWriter *writer, const AssetWeakReference *weak_ref); void BKE_asset_weak_reference_read(BlendDataReader *reader, AssetWeakReference *weak_ref); + +Main *BKE_asset_weak_reference_main(Main *global_main, const ID *id); +void BKE_asset_weak_reference_main_free(); +ID *BKE_asset_weak_reference_ensure(Main *global_main, const AssetWeakReference *weak_ref); diff --git a/source/blender/blenkernel/BKE_brush.hh b/source/blender/blenkernel/BKE_brush.hh index 5d5d66a7e13..67244f2b6e6 100644 --- a/source/blender/blenkernel/BKE_brush.hh +++ b/source/blender/blenkernel/BKE_brush.hh @@ -31,10 +31,6 @@ struct UnifiedPaintSettings; void BKE_brush_system_init(); void BKE_brush_system_exit(); -/* TODO: Should be somewhere else not specific to brushes. */ -Brush *BKE_brush_asset_runtime_ensure(Main *bmain, - const AssetWeakReference *brush_asset_reference); - /* Data-block functions. */ /** diff --git a/source/blender/blenkernel/BKE_lib_override.hh b/source/blender/blenkernel/BKE_lib_override.hh index 02716511e0b..54243dd2722 100644 --- a/source/blender/blenkernel/BKE_lib_override.hh +++ b/source/blender/blenkernel/BKE_lib_override.hh @@ -84,17 +84,6 @@ bool BKE_lib_override_library_is_user_edited(const ID *id); */ bool BKE_lib_override_library_is_system_defined(const Main *bmain, const ID *id); -/** - * Count the number of liboverride IDs of given `id_type`, using a refererence linked ID from given - * `library`, that are user-edited. - * - * \param r_reports: If not NULL, add one report for each relevant ID. - */ -int BKE_lib_override_user_edited_from_library_count(Main *bmain, - ID_Type id_type, - Library *library, - ReportList *r_reports); - /** * Check if given Override Property for given ID is animated (through a F-Curve in an Action, or * from a driver). diff --git a/source/blender/blenkernel/intern/asset_weak_reference.cc b/source/blender/blenkernel/intern/asset_weak_reference.cc index f1d0d06f72a..2804f57130b 100644 --- a/source/blender/blenkernel/intern/asset_weak_reference.cc +++ b/source/blender/blenkernel/intern/asset_weak_reference.cc @@ -7,15 +7,25 @@ */ #include +#include #include "BLI_string.h" +#include "DNA_space_types.h" + #include "AS_asset_identifier.hh" #include "AS_asset_library.hh" #include "BKE_asset.hh" +#include "BKE_blendfile_link_append.hh" +#include "BKE_idtype.hh" +#include "BKE_lib_id.hh" +#include "BKE_main.hh" + +#include "BLI_vector.hh" #include "BLO_read_write.hh" +#include "BLO_readfile.h" #include "DNA_asset_types.h" @@ -97,3 +107,129 @@ void BKE_asset_weak_reference_read(BlendDataReader *reader, AssetWeakReference * BLO_read_data_address(reader, &weak_ref->asset_library_identifier); BLO_read_data_address(reader, &weak_ref->relative_asset_identifier); } + +/* Main database for each brush asset blend file. + * + * This avoids mixing asset datablocks in the regular main, which leads to naming conflicts and + * confusing user interface. + * + * TODO: Heavily WIP code. */ + +struct AssetWeakReferenceMain { + /* TODO: not sure if this is the best unique identifier. */ + std::string lib_path; + Main *main; + + AssetWeakReferenceMain(const char *lib_path) : lib_path(lib_path), main(BKE_main_new()) {} + AssetWeakReferenceMain(const AssetWeakReferenceMain &) = delete; + AssetWeakReferenceMain(AssetWeakReferenceMain &&other) : main(std::exchange(other.main, nullptr)) + { + } + + ~AssetWeakReferenceMain() + { + if (main) { + BKE_main_free(main); + } + } +}; + +blender::Vector ASSET_WEAK_REFERENCE_MAINS; + +Main *BKE_asset_weak_reference_main(Main *global_main, const ID *id) +{ + if (!(id->tag & LIB_TAG_ASSET_MAIN)) { + return global_main; + } + + for (const AssetWeakReferenceMain &weak_ref_main : ASSET_WEAK_REFERENCE_MAINS) { + /* TODO: just loop over listbase of same type, or make this whole thing + * more efficient. */ + ID *other_id; + FOREACH_MAIN_ID_BEGIN (weak_ref_main.main, other_id) { + if (id == other_id) { + return weak_ref_main.main; + } + } + FOREACH_MAIN_ID_END; + } + + BLI_assert_unreachable(); + return nullptr; +} + +static Main *asset_weak_reference_main_ensure(const char *lib_path) +{ + for (const AssetWeakReferenceMain &weak_ref_main : ASSET_WEAK_REFERENCE_MAINS) { + if (weak_ref_main.lib_path == lib_path) { + return weak_ref_main.main; + } + } + + ASSET_WEAK_REFERENCE_MAINS.append(lib_path); + return ASSET_WEAK_REFERENCE_MAINS.last().main; +} + +void BKE_asset_weak_reference_main_free() +{ + ASSET_WEAK_REFERENCE_MAINS.clear_and_shrink(); +} + +ID *BKE_asset_weak_reference_ensure(Main *global_main, const AssetWeakReference *weak_ref) +{ + BLI_assert(weak_ref != nullptr); + + char asset_full_path_buffer[FILE_MAX_LIBEXTRA]; + char *asset_lib_path, *asset_group, *asset_name; + + AS_asset_full_path_explode_from_weak_ref( + weak_ref, asset_full_path_buffer, &asset_lib_path, &asset_group, &asset_name); + + if (asset_lib_path == nullptr && asset_group == nullptr && asset_name == nullptr) { + return nullptr; + } + + // TODO: make not brush specific + BLI_assert(STREQ(asset_group, IDType_ID_BR.name)); + BLI_assert(asset_name != nullptr); + + /* If weak reference resolves to a null library path, assume we are in local asset case. */ + Main *asset_main = global_main; + + if (asset_lib_path != nullptr) { + /* TODO: avoid appending twice? */ + asset_main = asset_weak_reference_main_ensure(asset_lib_path); + + LibraryLink_Params lapp_parameters{}; + lapp_parameters.bmain = asset_main; + BlendfileLinkAppendContext *lapp_context = BKE_blendfile_link_append_context_new( + &lapp_parameters); + BKE_blendfile_link_append_context_flag_set(lapp_context, BLO_LIBLINK_FORCE_INDIRECT, true); + BKE_blendfile_link_append_context_flag_set(lapp_context, 0, true); + + BKE_blendfile_link_append_context_library_add(lapp_context, asset_lib_path, nullptr); + + // TODO: make not brush specific + BlendfileLinkAppendContextItem *lapp_item = BKE_blendfile_link_append_context_item_add( + lapp_context, asset_name, ID_BR, nullptr); + BKE_blendfile_link_append_context_item_library_index_enable(lapp_context, lapp_item, 0); + + BKE_blendfile_link(lapp_context, nullptr); + BKE_blendfile_append(lapp_context, nullptr); + + BKE_blendfile_link_append_context_free(lapp_context); + + /* TODO: only do for new ones? */ + BKE_main_id_tag_all(asset_main, LIB_TAG_ASSET_MAIN, true); + } + + /* TODO: are we sure it will not get renamed? */ + ID *local_asset = reinterpret_cast( + BLI_findstring(&asset_main->brushes, asset_name, offsetof(ID, name) + 2)); + + if (local_asset == nullptr || !ID_IS_ASSET(local_asset)) { + return nullptr; + } + return local_asset; +} + diff --git a/source/blender/blenkernel/intern/blendfile.cc b/source/blender/blenkernel/intern/blendfile.cc index 0156bc7f33f..08262b8f363 100644 --- a/source/blender/blenkernel/intern/blendfile.cc +++ b/source/blender/blenkernel/intern/blendfile.cc @@ -305,188 +305,6 @@ static bool reuse_bmain_data_remapper_is_id_remapped(IDRemapper *remapper, ID *i return false; } -static Library *reuse_bmain_data_dependencies_new_library_get(ReuseOldBMainData *reuse_data, - Library *old_lib) -{ - if (old_lib == nullptr) { - return nullptr; - } - - IDRemapper *remapper = reuse_data->remapper; - Library *new_lib = old_lib; - IDRemapperApplyResult result = BKE_id_remapper_apply( - remapper, reinterpret_cast(&new_lib), ID_REMAP_APPLY_DEFAULT); - if (result == ID_REMAP_RESULT_SOURCE_UNAVAILABLE) { - /* No matching new library found. Avoid nullptr case when original data was linked, which would - * match against all local IDs. */ - } - BLI_assert_msg(result == ID_REMAP_RESULT_SOURCE_UNASSIGNED || new_lib != nullptr, - "`new_lib` should only ever be NULL here in case the library of the old linked " - "ID is the newly opened .blend file."); - return new_lib; -} - -static int reuse_bmain_data_dependencies_process_cb(LibraryIDLinkCallbackData *cb_data) -{ - ID *id = *cb_data->id_pointer; - - if (id == nullptr) { - return IDWALK_RET_NOP; - } - - ReuseOldBMainData *reuse_data = static_cast(cb_data->user_data); - Main *new_bmain = reuse_data->new_bmain; - Main *old_bmain = reuse_data->old_bmain; - - /* First check if it has already been remapped. */ - IDRemapper *remapper = reuse_bmain_data_remapper_ensure(reuse_data); - if (reuse_bmain_data_remapper_is_id_remapped(remapper, id)) { - return IDWALK_RET_STOP_RECURSION; - } - - ListBase *new_lb = which_libbase(new_bmain, GS(id->name)); - ListBase *old_lb = which_libbase(old_bmain, GS(id->name)); - - /* if ID is already in the new_bmain, this should have been detected by the check on `remapper` - * above. */ - BLI_assert(BLI_findindex(new_lb, id) < 0); - BLI_assert(BLI_findindex(old_lb, id) >= 0); - - /* There may be a new library pointer in new_bmain, matching a library in old_bmain, even - * though pointer values are not the same. So we need to check new linked IDs in new_bmain - * against both potential library pointers. */ - Library *old_id_new_lib = reuse_bmain_data_dependencies_new_library_get(reuse_data, id->lib); - - if (ID_IS_LINKED(id)) { - /* In case of linked ID, a 'new' version of the same data may already exist in new_bmain. In - * such case, do not move the old linked ID, but remap its usages to the new one instead. */ - for (ID *id_iter = static_cast(new_lb->last); id_iter != nullptr; - id_iter = static_cast(id_iter->prev)) - { - if (!ELEM(id_iter->lib, id->lib, old_id_new_lib)) { - continue; - } - if (!STREQ(id_iter->name + 2, id->name + 2)) { - continue; - } - - /* NOTE: In case the old ID was from a library that is the newly opened .blend file (i.e. - * `old_id_new_lib` is NULL), this will remap to a local new ID in new_bmain. - * - * This has a potential impact on other ported over linked IDs (which are not allowed to - * use local data), and liboverrides (which are not allowed to have a local reference). - * - * Such cases are checked and 'fixed' later by the call to - * #reuse_bmain_data_invalid_local_usages_fix. */ - BKE_id_remapper_add(remapper, id, id_iter); - return IDWALK_RET_STOP_RECURSION; - } - } - - BLI_remlink_safe(old_lb, id); - BKE_main_namemap_remove_name(old_bmain, id, id->name + 2); - - id->lib = old_id_new_lib; - BLI_addtail(new_lb, id); - BKE_id_new_name_validate(new_bmain, new_lb, id, nullptr, true); - /* Remap to itself, to avoid re-processing this ID again. */ - BKE_id_remapper_add(remapper, id, id); - - return IDWALK_RET_NOP; -} - -/** - * Selectively 'import' data from old Main into new Main, provided it does not conflict with data - * already present in the new Main (name-wise and library-wise). - * - * Dependencies from moved over old data are also imported into the new Main, (unless, in case of - * linked data, a matching linked ID is already available in new Main). - * - * When a conflict is found, usages of the conflicted ID by the old data are stored in the - * `remapper` of `ReuseOldBMainData` to be remapped to the matching data in the new Main later. - * - * NOTE: This function will never remove any original new data from the new Main, it only moves - * (some of) the old data to the new Main. - */ -static void reuse_old_bmain_data_for_blendfile(ReuseOldBMainData *reuse_data, const short id_code) -{ - Main *new_bmain = reuse_data->new_bmain; - Main *old_bmain = reuse_data->old_bmain; - - ListBase *new_lb = which_libbase(new_bmain, id_code); - ListBase *old_lb = which_libbase(old_bmain, id_code); - - IDRemapper *remapper = reuse_bmain_data_remapper_ensure(reuse_data); - - /* Bring back local existing IDs from old_lb into new_lb, if there are no name/library - * conflicts. */ - for (ID *old_id_iter_next, *old_id_iter = static_cast(old_lb->first); - old_id_iter != nullptr; - old_id_iter = old_id_iter_next) - { - old_id_iter_next = static_cast(old_id_iter->next); - - /* Fully local assets are never re-used, since in this case the old file can be considered as - * an asset repository, and its assets should be accessed through the asset system by other - * files. */ - if (!ID_IS_LINKED(old_id_iter) && !ID_IS_OVERRIDE_LIBRARY(old_id_iter) && - ID_IS_ASSET(old_id_iter)) - { - continue; - } - - /* All other IDs can be re-used, provided there is no name/library conflict (i.e. the new bmain - * does not already have the 'same' data). */ - bool can_use_old_id = true; - Library *old_id_new_lib = reuse_bmain_data_dependencies_new_library_get(reuse_data, - old_id_iter->lib); - for (ID *new_id_iter = static_cast(new_lb->first); new_id_iter != nullptr; - new_id_iter = static_cast(new_id_iter->next)) - { - if (!ELEM(new_id_iter->lib, old_id_iter->lib, old_id_new_lib)) { - continue; - } - if (!STREQ(old_id_iter->name + 2, new_id_iter->name + 2)) { - continue; - } - - /* In case we found a matching ID in new_bmain, it can be considered as 'the same' - * as the old ID, so usages of old ID ported over to new main can be remapped. - * - * This is only valid if the old ID was linked though. */ - if (ID_IS_LINKED(old_id_iter)) { - BKE_id_remapper_add(remapper, old_id_iter, new_id_iter); - } - can_use_old_id = false; - break; - } - - if (can_use_old_id) { - BLI_remlink_safe(old_lb, old_id_iter); - BKE_main_namemap_remove_name(old_bmain, old_id_iter, old_id_iter->name + 2); - - BLI_addtail(new_lb, old_id_iter); - old_id_iter->lib = old_id_new_lib; - BKE_id_new_name_validate(new_bmain, new_lb, old_id_iter, nullptr, true); - BKE_lib_libblock_session_uid_renew(old_id_iter); - - /* Remap to itself, to avoid re-processing this ID again. */ - BKE_id_remapper_add(remapper, old_id_iter, old_id_iter); - - /* Port over dependencies of re-used ID, unless matching already existing ones in - * new_bmain can be found. - * - * NOTE : No pointers are remapped here, this code only moves dependencies from old_bmain - * to new_bmain if needed, and add necessary remapping rules to the reuse_data.remapper. */ - BKE_library_foreach_ID_link(new_bmain, - old_id_iter, - reuse_bmain_data_dependencies_process_cb, - reuse_data, - IDWALK_RECURSE | IDWALK_DO_LIBRARY_POINTER); - } - } -} - /** * Does a complete replacement of data in `new_bmain` by data from `old_bmain. Original new data * are moved to the `old_bmain`, and will be freed together with it. @@ -494,9 +312,8 @@ static void reuse_old_bmain_data_for_blendfile(ReuseOldBMainData *reuse_data, co * WARNING: Currently only expects to work on local data, won't work properly if some of the IDs of * given type are linked. * - * NOTE: Unlike with #reuse_old_bmain_data_for_blendfile, there is no support at all for potential - * dependencies of the IDs moved around. This is not expected to be necessary for the current use - * cases (UI-related IDs). + * NOTE: There is no support at all for potential dependencies of the IDs moved around. This is not + * expected to be necessary for the current use cases (UI-related IDs). */ static void swap_old_bmain_data_for_blendfile(ReuseOldBMainData *reuse_data, const short id_code) { @@ -909,12 +726,6 @@ static void setup_app_data(bContext *C, } BKE_main_idmap_destroy(reuse_data.id_map); - - if (!is_startup) { - /* Keeping old brushes has different conditions, and different behavior, than UI-like ID - * types when actually reading a blendfile. */ - reuse_old_bmain_data_for_blendfile(&reuse_data, ID_BR); - } } /* Logic for 'track_undo_scene' is to keep using the scene which the active screen has, as long diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index 6a366947982..ddca1b97d89 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -27,7 +27,6 @@ #include "BLT_translation.h" #include "BKE_asset.hh" -#include "BKE_blendfile_link_append.hh" #include "BKE_bpath.h" #include "BKE_brush.hh" #include "BKE_colortools.hh" @@ -527,62 +526,6 @@ static void brush_defaults(Brush *brush) #undef FROM_DEFAULT_PTR } -Brush *BKE_brush_asset_runtime_ensure(Main *bmain, const AssetWeakReference *brush_asset_reference) -{ - BLI_assert(brush_asset_reference != nullptr); - - char asset_full_path_buffer[FILE_MAX_LIBEXTRA]; - char *asset_lib_path, *asset_group, *asset_name; - - AS_asset_full_path_explode_from_weak_ref( - brush_asset_reference, asset_full_path_buffer, &asset_lib_path, &asset_group, &asset_name); - - if (asset_lib_path == nullptr && asset_group == nullptr && asset_name == nullptr) { - return nullptr; - } - - BLI_assert(STREQ(asset_group, IDType_ID_BR.name)); - BLI_assert(asset_name != nullptr); - - /* If the weakreference resolves to a null library path, assume that we are in local asset case. - */ - if (asset_lib_path == nullptr) { - Brush *local_brush_asset = reinterpret_cast( - BLI_findstring(&bmain->brushes, asset_name, offsetof(ID, name) + 2)); - - if (local_brush_asset == nullptr || !ID_IS_ASSET(local_brush_asset)) { - return nullptr; - } - return local_brush_asset; - } - - LibraryLink_Params lapp_parameters{}; - lapp_parameters.bmain = bmain; - BlendfileLinkAppendContext *lapp_context = BKE_blendfile_link_append_context_new( - &lapp_parameters); - BKE_blendfile_link_append_context_flag_set(lapp_context, BLO_LIBLINK_FORCE_INDIRECT, true); - BKE_blendfile_link_append_context_flag_set(lapp_context, FILE_LINK, true); - - BKE_blendfile_link_append_context_library_add(lapp_context, asset_lib_path, nullptr); - - BlendfileLinkAppendContextItem *lapp_item = BKE_blendfile_link_append_context_item_add( - lapp_context, asset_name, ID_BR, nullptr); - BKE_blendfile_link_append_context_item_library_index_enable(lapp_context, lapp_item, 0); - - BKE_blendfile_link(lapp_context, nullptr); - BKE_blendfile_override(lapp_context, - eBKELibLinkOverride(BKE_LIBLINK_OVERRIDE_USE_EXISTING_LIBOVERRIDES | - BKE_LIBLINK_OVERRIDE_CREATE_RUNTIME), - nullptr); - - Brush *liboverride_brush = reinterpret_cast( - BKE_blendfile_link_append_context_item_liboverrideid_get(lapp_context, lapp_item)); - - BKE_blendfile_link_append_context_free(lapp_context); - - return liboverride_brush; -} - /* Datablock add/copy/free/make_local */ Brush *BKE_brush_add(Main *bmain, const char *name, const eObjectMode ob_mode) diff --git a/source/blender/blenkernel/intern/lib_override.cc b/source/blender/blenkernel/intern/lib_override.cc index e62ec05ba0b..3c94bd2cb6a 100644 --- a/source/blender/blenkernel/intern/lib_override.cc +++ b/source/blender/blenkernel/intern/lib_override.cc @@ -339,42 +339,6 @@ bool BKE_lib_override_library_is_system_defined(const Main *bmain, const ID *id) return false; } -int BKE_lib_override_user_edited_from_library_count(Main *bmain, - const ID_Type id_type, - Library *library, - ReportList *r_reports) -{ - ListBase *lb = which_libbase(bmain, id_type); - int num_user_edited = 0; - - for (ID *id_iter = static_cast(lb->first); id_iter != nullptr; - id_iter = static_cast(id_iter->next)) - { - if (ID_IS_LINKED(id_iter)) { - break; - } - if (!ID_IS_OVERRIDE_LIBRARY(id_iter)) { - continue; - } - if (id_iter->override_library->reference->lib != library) { - continue; - } - if (!BKE_lib_override_library_is_user_edited(id_iter)) { - continue; - } - - /* NOTE: If changes have been saved in a draft, then the local override is based on said - * draft (using the linked ID from the draft file as reference), so there should be no user - * edited changes anymore. */ - num_user_edited++; - if (r_reports) { - BKE_report(r_reports, RPT_INFO, id_iter->name + 2); - } - } - - return num_user_edited; -} - bool BKE_lib_override_library_property_is_animated( const ID *id, const IDOverrideLibraryProperty *liboverride_prop, diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 27984ab9f19..fa9bf8c9a87 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -666,8 +666,6 @@ const Brush *BKE_paint_brush_for_read(const Paint *p) void BKE_paint_brush_set(Paint *p, Brush *br) { if (p) { - id_us_min((ID *)p->brush); - id_us_plus((ID *)br); p->brush = br; BKE_paint_toolslots_brush_update(p); @@ -676,9 +674,7 @@ void BKE_paint_brush_set(Paint *p, Brush *br) bool BKE_paint_brush_is_valid_asset(const Brush *brush) { - return brush && (ID_IS_ASSET(&brush->id) || - (!ID_IS_LINKED(&brush->id) && ID_IS_OVERRIDE_LIBRARY_REAL(&brush->id) && - ID_IS_ASSET(brush->id.override_library->reference))); + return brush && ID_IS_ASSET(&brush->id); } static void paint_brush_asset_update(Paint &paint, @@ -689,9 +685,7 @@ static void paint_brush_asset_update(Paint &paint, BKE_asset_weak_reference_free(&paint.brush_asset_reference); } - if (brush == nullptr || brush != paint.brush || !ID_IS_OVERRIDE_LIBRARY_REAL(paint.brush) || - !(ID_IS_ASSET(paint.brush) || ID_IS_ASSET(paint.brush->id.override_library->reference))) - { + if (brush == nullptr || brush != paint.brush || !(brush->id.tag & LIB_TAG_ASSET_MAIN)) { BKE_asset_weak_reference_free(&brush_asset_reference); return; } @@ -743,7 +737,8 @@ void BKE_paint_brush_asset_restore(Main *bmain, Paint *paint) AssetWeakReference *brush_asset_reference = paint->brush_asset_reference; paint->brush_asset_reference = nullptr; - Brush *brush_asset = BKE_brush_asset_runtime_ensure(bmain, brush_asset_reference); + Brush *brush_asset = reinterpret_cast( + BKE_asset_weak_reference_ensure(bmain, brush_asset_reference)); /* Will either re-assign the brush_asset_reference to `paint`, or free it if loading a brush ID * from it failed. */ @@ -1302,13 +1297,7 @@ void BKE_paint_copy(const Paint *src, Paint *tar, const int flag) tar->brush_asset_reference = BKE_asset_weak_reference_copy(src->brush_asset_reference); if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) { - id_us_plus((ID *)tar->brush); id_us_plus((ID *)tar->palette); - if (src->tool_slots != nullptr) { - for (int i = 0; i < tar->tool_slots_len; i++) { - id_us_plus((ID *)tar->tool_slots[i].brush); - } - } } } diff --git a/source/blender/blenkernel/intern/paint_toolslots.cc b/source/blender/blenkernel/intern/paint_toolslots.cc index 18770584f56..d9d18958567 100644 --- a/source/blender/blenkernel/intern/paint_toolslots.cc +++ b/source/blender/blenkernel/intern/paint_toolslots.cc @@ -54,7 +54,6 @@ static void paint_toolslots_init(Main *bmain, Paint *paint) BKE_paint_toolslots_len_ensure(paint, slot_index + 1); if (paint->tool_slots[slot_index].brush == nullptr) { paint->tool_slots[slot_index].brush = brush; - id_us_plus(&brush->id); } } } @@ -121,10 +120,6 @@ void BKE_paint_toolslots_brush_update_ex(Paint *paint, Brush *brush) const int slot_index = BKE_brush_tool_get(brush, paint); BKE_paint_toolslots_len_ensure(paint, slot_index + 1); PaintToolSlot *tslot = &paint->tool_slots[slot_index]; - id_us_plus(&brush->id); - if (tslot->brush) { - id_us_min(&tslot->brush->id); - } tslot->brush = brush; } @@ -148,7 +143,6 @@ void BKE_paint_toolslots_brush_validate(Main *bmain, Paint *paint) if (tslot->brush) { if ((i != BKE_brush_tool_get(tslot->brush, paint)) || (tslot->brush->ob_mode & ob_mode) == 0) { - id_us_min(&tslot->brush->id); tslot->brush = nullptr; } } diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc index 078ff2cdba0..48387fbddfc 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -594,7 +594,7 @@ static void scene_foreach_paint(LibraryForeachIDData *data, SCENE_FOREACH_UNDO_RESTORE, reader, &paint_old->brush, - IDWALK_CB_USER); + IDWALK_CB_NOP); for (int i = 0; i < paint_old->tool_slots_len; i++) { /* This is a bit tricky. @@ -613,7 +613,7 @@ static void scene_foreach_paint(LibraryForeachIDData *data, SCENE_FOREACH_UNDO_RESTORE, reader, &paint_old->tool_slots[i].brush, - IDWALK_CB_USER); + IDWALK_CB_NOP); } Palette *palette_tmp = nullptr; diff --git a/source/blender/editors/interface/interface_templates.cc b/source/blender/editors/interface/interface_templates.cc index 097c436f667..8a2e1a28a42 100644 --- a/source/blender/editors/interface/interface_templates.cc +++ b/source/blender/editors/interface/interface_templates.cc @@ -47,6 +47,7 @@ #include "BLT_translation.h" #include "BKE_action.h" +#include "BKE_asset.hh" #include "BKE_blender_version.h" #include "BKE_blendfile.hh" #include "BKE_cachefile.h" @@ -949,6 +950,8 @@ static void template_id_liboverride_hierarchy_make(bContext *C, static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) { + /* TODO: select appropriate bmain for ID, might need to be asset main, changes in this functions + * are most likely wrong still. */ TemplateID *template_ui = (TemplateID *)arg_litem; PointerRNA idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop); ID *id = static_cast(idptr.data); @@ -1003,11 +1006,13 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) case UI_ID_LOCAL: if (id) { Main *bmain = CTX_data_main(C); + Main *id_main = BKE_asset_weak_reference_main(bmain, id); if (CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) { - template_id_liboverride_hierarchy_make(C, bmain, template_ui, &idptr, &undo_push_label); + template_id_liboverride_hierarchy_make( + C, id_main, template_ui, &idptr, &undo_push_label); } else { - if (BKE_lib_id_make_local(bmain, id, 0)) { + if (BKE_lib_id_make_local(id_main, id, 0)) { BKE_id_newptr_and_tag_clear(id); /* Reassign to get proper updates/notifiers. */ @@ -1024,11 +1029,13 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) case UI_ID_OVERRIDE: if (id && ID_IS_OVERRIDE_LIBRARY(id)) { Main *bmain = CTX_data_main(C); + Main *id_main = BKE_asset_weak_reference_main(bmain, id); if (CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) { - template_id_liboverride_hierarchy_make(C, bmain, template_ui, &idptr, &undo_push_label); + template_id_liboverride_hierarchy_make( + C, id_main, template_ui, &idptr, &undo_push_label); } else { - BKE_lib_override_library_make_local(bmain, id); + BKE_lib_override_library_make_local(id_main, id); /* Reassign to get proper updates/notifiers. */ idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop); RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr, nullptr); @@ -1043,15 +1050,16 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) (template_ui->ptr.type == &RNA_LayerObjects)); /* make copy */ + Main *bmain = CTX_data_main(C); + Main *id_main = BKE_asset_weak_reference_main(bmain, id); + if (do_scene_obj) { - Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); - ED_object_single_user(bmain, scene, (Object *)id); + ED_object_single_user(id_main, scene, (Object *)id); WM_event_add_notifier(C, NC_WINDOW, nullptr); DEG_relations_tag_update(bmain); } else { - Main *bmain = CTX_data_main(C); id_single_user(C, id, &template_ui->ptr, template_ui->prop); DEG_relations_tag_update(bmain); } @@ -1342,7 +1350,7 @@ static void template_ID(const bContext *C, /* text button with name */ if (id) { char name[UI_MAX_NAME_STR]; - const bool user_alert = (id->us <= 0); + const bool user_alert = (id->us <= 0) && !(id->tag & LIB_TAG_ASSET_MAIN); const int width = template_search_textbut_width(&idptr, RNA_struct_find_property(&idptr, "name")); @@ -1773,10 +1781,15 @@ static void ui_template_id(uiLayout *layout, flag |= UI_ID_OPEN; } + Main *id_main = CTX_data_main(C); + if (ptr->owner_id) { + id_main = BKE_asset_weak_reference_main(id_main, ptr->owner_id); + } + StructRNA *type = RNA_property_pointer_type(ptr, prop); short idcode = RNA_type_to_ID_code(type); template_ui->idcode = idcode; - template_ui->idlb = which_libbase(CTX_data_main(C), idcode); + template_ui->idlb = which_libbase(id_main, idcode); /* create UI elements for this template * - template_ID makes a copy of the template data and assigns it to the relevant buttons diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index c1340c19810..9835d6c6741 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -37,6 +37,7 @@ #include "BKE_image.h" #include "BKE_lib_id.hh" #include "BKE_lib_override.hh" +#include "BKE_lib_remap.hh" #include "BKE_main.hh" #include "BKE_paint.hh" #include "BKE_preferences.h" @@ -69,7 +70,7 @@ static int brush_add_exec(bContext *C, wmOperator * /*op*/) // int type = RNA_enum_get(op->ptr, "type"); Paint *paint = BKE_paint_get_active_from_context(C); Brush *br = BKE_paint_brush(paint); - Main *bmain = CTX_data_main(C); + Main *bmain = CTX_data_main(C); // TODO: add to asset main? PaintMode mode = BKE_paintmode_get_active_from_context(C); if (br) { @@ -191,7 +192,7 @@ static int brush_add_gpencil_exec(bContext *C, wmOperator * /*op*/) { Paint *paint = BKE_paint_get_active_from_context(C); Brush *br = BKE_paint_brush(paint); - Main *bmain = CTX_data_main(C); + Main *bmain = CTX_data_main(C); // TODO: add to asset main? if (br) { br = (Brush *)BKE_id_copy(bmain, &br->id); @@ -941,6 +942,7 @@ static int brush_select_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + // TODO: won't work with asset brushes, needs different main. if (brush_generic_tool_set(C, bmain, paint, tool, tool_name, create_missing, toggle)) { return OPERATOR_FINISHED; } @@ -1001,10 +1003,12 @@ static int brush_asset_select_exec(bContext *C, wmOperator *op) /* This operator currently covers both cases: the file/asset browser file list and the asset list * used for the asset-view template. Once the asset list design is used by the Asset Browser, * this can be simplified to just that case. */ + Main *bmain = CTX_data_main(C); blender::asset_system::AssetRepresentation *asset = CTX_wm_asset(C); AssetWeakReference *brush_asset_reference = asset->make_weak_reference(); - Brush *brush = BKE_brush_asset_runtime_ensure(CTX_data_main(C), brush_asset_reference); + Brush *brush = reinterpret_cast( + BKE_asset_weak_reference_ensure(bmain, brush_asset_reference)); Paint *paint = BKE_paint_get_active_from_context(C); @@ -1212,6 +1216,7 @@ static bool brush_asset_save_as_poll(bContext *C) static int brush_asset_save_as_exec(bContext *C, wmOperator *op) { + Main *bmain = CTX_data_main(C); Paint *paint = BKE_paint_get_active_from_context(C); Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr; if (paint == nullptr || brush == nullptr) { @@ -1241,9 +1246,11 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) BLI_assert(BKE_paint_brush_is_valid_asset(brush)); /* Save to asset library. */ + Main *asset_main = BKE_asset_weak_reference_main(bmain, &brush->id); + std::string final_full_asset_filepath; const bool sucess = brush_asset_write_in_library( - CTX_data_main(C), brush, name, filepath, final_full_asset_filepath, op->reports); + asset_main, brush, name, filepath, final_full_asset_filepath, op->reports); if (!sucess) { BKE_report(op->reports, RPT_ERROR, "Failed to write to asset library"); @@ -1258,8 +1265,7 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) /* TODO: maybe not needed, even less so if there is more visual confirmation of change. */ BKE_reportf(op->reports, RPT_INFO, "Saved \"%s\"", filepath.c_str()); - Main *bmain = CTX_data_main(C); - brush = BKE_brush_asset_runtime_ensure(bmain, new_brush_weak_ref); + brush = reinterpret_cast(BKE_asset_weak_reference_ensure(bmain, new_brush_weak_ref)); if (!BKE_paint_brush_asset_set(paint, brush, new_brush_weak_ref)) { /* Note brush sset was still saved in editable asset library, so was not a no-op. */ @@ -1347,9 +1353,10 @@ static bool brush_asset_delete_poll(bContext *C) static int brush_asset_delete_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); Paint *paint = BKE_paint_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); + Main *bmain = CTX_data_main(C); + Main *asset_main = BKE_asset_weak_reference_main(bmain, &brush->id); if (paint->brush_asset_reference && BKE_paint_brush_is_valid_asset(brush)) { /* Delete from asset library on disk. */ @@ -1363,15 +1370,12 @@ static int brush_asset_delete_exec(bContext *C, wmOperator *op) } } - /* Delete from session. If local override, also delete linked one. - * TODO: delete both in one step? */ - ID *original_brush = (!ID_IS_LINKED(&brush->id) && ID_IS_OVERRIDE_LIBRARY_REAL(&brush->id)) ? - brush->id.override_library->reference : - nullptr; - BKE_id_delete(bmain, brush); - if (original_brush) { - BKE_id_delete(bmain, original_brush); + if (asset_main != bmain) { + // TODO: hack: no pointer should exist, should do runtime lookup + BKE_libblock_remap(bmain, brush, nullptr, 0); } + BKE_id_delete(asset_main, brush); + // TODO: delete whole asset main if empty? brush_asset_refresh_editable_library(C); WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_REMOVED, nullptr); @@ -1415,11 +1419,15 @@ static bool brush_asset_update_poll(bContext *C) static int brush_asset_update_exec(bContext *C, wmOperator *op) { + Main *bmain = CTX_data_main(C); Paint *paint = BKE_paint_get_active_from_context(C); Brush *brush = nullptr; const AssetWeakReference *brush_weak_ref = BKE_paint_brush_asset_get(paint, &brush).value_or(nullptr); + // TODO: maybe can check directly in poll + BLI_assert((brush->id.tag & LIB_TAG_ASSET_MAIN) != 0); + char path_buffer[FILE_MAX_LIBEXTRA]; char *filepath; AS_asset_full_path_explode_from_weak_ref( @@ -1427,13 +1435,10 @@ static int brush_asset_update_exec(bContext *C, wmOperator *op) BLI_assert(BKE_paint_brush_is_valid_asset(brush)); + Main *asset_main = BKE_asset_weak_reference_main(bmain, &brush->id); std::string final_full_asset_filepath; - brush_asset_write_in_library(CTX_data_main(C), - brush, - brush->id.name + 2, - filepath, - final_full_asset_filepath, - op->reports); + brush_asset_write_in_library( + asset_main, brush, brush->id.name + 2, filepath, final_full_asset_filepath, op->reports); return OPERATOR_FINISHED; } diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 78f84409ca6..d55b79b8918 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -998,6 +998,15 @@ enum { * RESET_NEVER */ LIB_TAG_NOT_ALLOCATED = 1 << 26, + /** + * ID is part of an asset #Main separate from the regular main database. + * + * RESET_NEVER + * + * Datablocks like this can not be linked to and from datablocks in regular main. + * They should stay isolated from each other. + */ + LIB_TAG_ASSET_MAIN = 1 << 20, /* ------------------------------------------------------------------------------------------- */ diff --git a/source/blender/makesrna/intern/makesrna.cc b/source/blender/makesrna/intern/makesrna.cc index eca5d6301e0..3b67152f241 100644 --- a/source/blender/makesrna/intern/makesrna.cc +++ b/source/blender/makesrna/intern/makesrna.cc @@ -1271,6 +1271,8 @@ static char *rna_def_property_set_func( fprintf(f, " }\n"); } + /* TODO: check here for wrong linking between regular and asset main? */ + if (prop->flag & PROP_ID_REFCOUNT) { fprintf(f, "\n if (data->%s) {\n", dp->dnaname); fprintf(f, " id_us_min((ID *)data->%s);\n", dp->dnaname); diff --git a/source/blender/makesrna/intern/rna_ID.cc b/source/blender/makesrna/intern/rna_ID.cc index f70cdb097c2..e07afb4cb97 100644 --- a/source/blender/makesrna/intern/rna_ID.cc +++ b/source/blender/makesrna/intern/rna_ID.cc @@ -16,6 +16,7 @@ #include "BLI_utildefines.h" +#include "BKE_asset.hh" #include "BKE_icons.h" #include "BKE_lib_id.hh" #include "BKE_main_namemap.hh" @@ -283,10 +284,13 @@ int rna_ID_name_length(PointerRNA *ptr) void rna_ID_name_set(PointerRNA *ptr, const char *value) { ID *id = (ID *)ptr->data; - BKE_main_namemap_remove_name(G_MAIN, id, id->name + 2); + Main *id_main = BKE_asset_weak_reference_main(G_MAIN, id); + BKE_main_namemap_remove_name(id_main, id, id->name + 2); BLI_strncpy_utf8(id->name + 2, value, sizeof(id->name) - 2); - BLI_assert(BKE_id_is_in_global_main(id)); - BKE_libblock_ensure_unique_name(G_MAIN, id); + /* TODO: add BKE_id_is_in_editable_main? */ + /* TODO: this does update immediately in the asset shelf. */ + BLI_assert(BKE_id_is_in_global_main(id) || (id->tag & LIB_TAG_ASSET_MAIN)); + BKE_libblock_ensure_unique_name(id_main, id); if (GS(id->name) == ID_OB) { Object *ob = (Object *)id; @@ -306,7 +310,8 @@ static int rna_ID_name_editable(PointerRNA *ptr, const char ** /*r_info*/) return 0; } } - else if (!BKE_id_is_in_global_main(id)) { + /* TODO: add BKE_id_is_in_editable_main? */ + else if (!(BKE_id_is_in_global_main(id) || (id->tag & LIB_TAG_ASSET_MAIN))) { return 0; } @@ -1135,9 +1140,11 @@ int rna_IDMaterials_assign_int(PointerRNA *ptr, int key, const PointerRNA *assig short *totcol = BKE_id_material_len_p(id); Material *mat_id = (Material *)assign_ptr->owner_id; if (totcol && (key >= 0 && key < *totcol)) { + Main *id_main = BKE_asset_weak_reference_main(G_MAIN, id); + /* TODO: BKE_id_is_in_editable_main? */ BLI_assert(BKE_id_is_in_global_main(id)); BLI_assert(BKE_id_is_in_global_main(&mat_id->id)); - BKE_id_material_assign(G_MAIN, id, mat_id, key + 1); + BKE_id_material_assign(id_main, id, mat_id, key + 1); return 1; } else { @@ -1193,8 +1200,10 @@ static void rna_IDMaterials_clear_id(ID *id, Main *bmain) static void rna_Library_filepath_set(PointerRNA *ptr, const char *value) { Library *lib = (Library *)ptr->data; + Main *id_main = BKE_asset_weak_reference_main(G_MAIN, &lib->id); + /* TODO: BKE_id_is_in_editable_main? */ BLI_assert(BKE_id_is_in_global_main(&lib->id)); - BKE_library_filepath_set(G_MAIN, lib, value); + BKE_library_filepath_set(id_main, lib, value); } /* ***** ImagePreview ***** */ @@ -2282,6 +2291,14 @@ static void rna_def_ID(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Is Indirect", "Is this ID block linked indirectly"); + prop = RNA_def_property(srna, "is_asset_library_data", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "tag", LIB_TAG_ASSET_MAIN); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, + "Asset Library Data", + "This data-block is part of an asset library blend file, not the blend " + "file opened for editing"); + prop = RNA_def_property(srna, "library", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, nullptr, "lib"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); diff --git a/source/blender/makesrna/intern/rna_main_api.cc b/source/blender/makesrna/intern/rna_main_api.cc index d37c34eb60b..aaa67f28d94 100644 --- a/source/blender/makesrna/intern/rna_main_api.cc +++ b/source/blender/makesrna/intern/rna_main_api.cc @@ -27,6 +27,7 @@ # include "BKE_action.h" # include "BKE_armature.hh" +# include "BKE_asset.hh" # include "BKE_brush.hh" # include "BKE_camera.h" # include "BKE_collection.h" @@ -125,6 +126,18 @@ static void rna_Main_ID_remove(Main *bmain, id->name + 2); return; } + /* TODO: also check reverse case somehow? */ + if (bmain != BKE_asset_weak_reference_main(bmain, id)) { + BKE_reportf(reports, + RPT_ERROR, + "%s '%s' is part of a difference main database and should be removed from there", + BKE_idtype_idcode_to_name(GS(id->name)), + id->name + 2); + return; + } + /* TODO: this will not clear pointers from regular main to this asset. + * Those probably should not exist, and be purely runtime lookups? */ + if (do_unlink) { BKE_id_delete(bmain, id); RNA_POINTER_INVALIDATE(id_ptr); diff --git a/source/blender/windowmanager/intern/wm_files.cc b/source/blender/windowmanager/intern/wm_files.cc index ccc6015e4e7..391c9f1ac2f 100644 --- a/source/blender/windowmanager/intern/wm_files.cc +++ b/source/blender/windowmanager/intern/wm_files.cc @@ -146,11 +146,6 @@ static void wm_history_file_write(); static void wm_test_autorun_revert_action_exec(bContext *C); -static bool wm_operator_open_file_draft_check_dialog(bContext *C, - wmOperator *op, - const int num_user_edited_lost, - ReportList *user_edited_lost_reports); - static CLG_LogRef LOG = {"wm.files"}; /* -------------------------------------------------------------------- */ @@ -2830,7 +2825,6 @@ static int operator_state_dispatch(bContext *C, wmOperator *op, OperatorDispatch enum { OPEN_MAINFILE_STATE_DISCARD_CHANGES, OPEN_MAINFILE_STATE_SELECT_FILE_PATH, - OPEN_MAINFILE_STATE_DISCARD_ASSET_DRAFTS, OPEN_MAINFILE_STATE_OPEN, }; @@ -2848,7 +2842,7 @@ static int wm_open_mainfile__discard_changes(bContext *C, wmOperator *op) set_next_operator_state(op, OPEN_MAINFILE_STATE_SELECT_FILE_PATH); } else { - set_next_operator_state(op, OPEN_MAINFILE_STATE_DISCARD_ASSET_DRAFTS); + set_next_operator_state(op, OPEN_MAINFILE_STATE_OPEN); } if (wm_operator_close_file_dialog_if_needed(C, op, wm_open_mainfile_after_dialog_callback)) { @@ -2888,44 +2882,6 @@ static int wm_open_mainfile__select_file_path(bContext *C, wmOperator *op) return OPERATOR_RUNNING_MODAL; } -static int wm_open_mainfile__discard_asset_drafts(bContext *C, wmOperator *op) -{ - Main *bmain = CTX_data_main(C); - - char filepath[FILE_MAX]; - RNA_string_get(op->ptr, "filepath", filepath); - - set_next_operator_state(op, OPEN_MAINFILE_STATE_OPEN); - - Library *lib = static_cast( - BLI_findstring(&bmain->libraries, filepath, offsetof(Library, filepath_abs))); - - if (lib == nullptr) { - return wm_open_mainfile_dispatch(C, op); - } - - /* If new to-be-opened blendfile was a library of the currently opened one, check for potential - * persistent edited assets that would be reset to their library status (only Brushes IDs - * currently). */ - ReportList *reports = MEM_cnew(__func__); - BKE_reports_init(reports, RPT_STORE); - const int num_user_edited_brushes = BKE_lib_override_user_edited_from_library_count( - bmain, ID_BR, lib, reports); - if (num_user_edited_brushes > 0) { - /* NOTE: steals ownership of `user_edited_lost_reports` regardless of its result. */ - if (wm_operator_open_file_draft_check_dialog(C, op, num_user_edited_brushes, reports)) { - return OPERATOR_INTERFACE; - } - reports = nullptr; - } - else { - BKE_reports_clear(reports); - MEM_delete(reports); - } - - return wm_open_mainfile_dispatch(C, op); -} - static int wm_open_mainfile__open(bContext *C, wmOperator *op) { char filepath[FILE_MAX]; @@ -2958,7 +2914,6 @@ static int wm_open_mainfile__open(bContext *C, wmOperator *op) static OperatorDispatchTarget wm_open_mainfile_dispatch_targets[] = { {OPEN_MAINFILE_STATE_DISCARD_CHANGES, wm_open_mainfile__discard_changes}, {OPEN_MAINFILE_STATE_SELECT_FILE_PATH, wm_open_mainfile__select_file_path}, - {OPEN_MAINFILE_STATE_DISCARD_ASSET_DRAFTS, wm_open_mainfile__discard_asset_drafts}, {OPEN_MAINFILE_STATE_OPEN, wm_open_mainfile__open}, {0, nullptr}, }; @@ -4533,217 +4488,3 @@ bool wm_operator_close_file_dialog_if_needed(bContext *C, } /** \} */ - -/** - * \name Open Asset Library File Dialog. - * - * This handles cases where user is opening a file that is an asset library, which assets are used - * in the 'user preference' way (i.e. assets are linked, and have runtime-only, session-persistant, - * user-editable overrides of these. - * - * Special warning is necessary because when opening the library file, all non-drafted user edits - * of the relevant assets will be lost. */ -/** \{ */ - -static const char *open_file_draft_check_dialog_name = "open_file_draft_check_popup"; - -typedef struct wmOpenDraftCheckCallback { - IDProperty *op_properties; - - ReportList *user_edited_lost_reports; - int num_user_edited_lost; - - char new_filepath[FILE_MAX]; -} wmOpenDraftCheckCallback; - -static void wm_block_open_file_draft_cancel(bContext *C, void *arg_block, void * /*arg_data*/) -{ - wmWindow *win = CTX_wm_window(C); - UI_popup_block_close(C, win, static_cast(arg_block)); -} - -static void wm_block_open_file_draft_discard(bContext *C, void *arg_block, void *arg_data) -{ - wmGenericCallback *callback = WM_generic_callback_steal((wmGenericCallback *)arg_data); - - /* Close the popup before executing the callback. Otherwise - * the popup might be closed by the callback, which will lead - * to a crash. */ - wmWindow *win = CTX_wm_window(C); - UI_popup_block_close(C, win, static_cast(arg_block)); - - callback->exec(C, callback->user_data); - WM_generic_callback_free(callback); -} - -static void wm_block_open_file_draft_save(bContext *C, void *arg_block, void *arg_data) -{ - wmGenericCallback *callback = WM_generic_callback_steal((wmGenericCallback *)arg_data); - bool execute_callback = true; - - wmWindow *win = CTX_wm_window(C); - UI_popup_block_close(C, win, static_cast(arg_block)); - - /* TODO: Actually save the edited lost local runtime assets overrides into drafts. */ - - if (execute_callback) { - callback->exec(C, callback->user_data); - } - WM_generic_callback_free(callback); -} - -static void wm_block_open_file_draft_cancel_button(uiBlock *block, wmGenericCallback *post_action) -{ - uiBut *but = uiDefIconTextBut( - block, UI_BTYPE_BUT, 0, 0, IFACE_("Cancel"), 0, 0, 0, UI_UNIT_Y, 0, 0, 0, 0, 0, ""); - UI_but_func_set(but, wm_block_open_file_draft_cancel, block, post_action); - UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT); -} - -static void wm_block_open_file_draft_discard_button(uiBlock *block, wmGenericCallback *post_action) -{ - uiBut *but = uiDefIconTextBut( - block, UI_BTYPE_BUT, 0, 0, IFACE_("Ignore"), 0, 0, 0, UI_UNIT_Y, 0, 0, 0, 0, 0, ""); - UI_but_func_set(but, wm_block_open_file_draft_discard, block, post_action); - UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT); -} - -static void wm_block_open_file_draft_save_button(uiBlock *block, wmGenericCallback *post_action) -{ - uiBut *but = uiDefIconTextBut( - block, UI_BTYPE_BUT, 0, 0, IFACE_("Save To Draft"), 0, 0, 0, UI_UNIT_Y, 0, 0, 0, 0, 0, ""); - UI_but_func_set(but, wm_block_open_file_draft_save, block, post_action); - UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT); - UI_but_flag_enable(but, UI_BUT_ACTIVE_DEFAULT); -} - -static void wm_open_file_after_draft_check_dialog_callback(bContext *C, void *user_data) -{ - wmOpenDraftCheckCallback *callback_data = static_cast(user_data); - - WM_operator_name_call_with_properties( - C, "WM_OT_open_mainfile", WM_OP_INVOKE_DEFAULT, callback_data->op_properties, nullptr); -} - -static uiBlock *block_create__open_draft_check_file_dialog(struct bContext *C, - struct ARegion *region, - void *arg1) -{ - wmGenericCallback *post_action = static_cast(arg1); - wmOpenDraftCheckCallback *callback_data = static_cast( - post_action->user_data); - - uiBlock *block = UI_block_begin(C, region, open_file_draft_check_dialog_name, UI_EMBOSS); - UI_block_flag_enable( - block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_LOOP | UI_BLOCK_NO_WIN_CLIP | UI_BLOCK_NUMSELECT); - UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP); - - uiLayout *layout = uiItemsAlertBox(block, 34, ALERT_ICON_QUESTION); - - /* Title. */ - uiItemL_ex(layout, TIP_("Save User-Edited Assets to Draft?"), ICON_NONE, true, false); - - char message[2048]; - SNPRINTF(message, - TIP_("The following %d assets have local edits that will be lost"), - callback_data->num_user_edited_lost); - uiItemL(layout, message, ICON_NONE); - uiItemL(layout, "by opening the chosen Blender Asset Library file", ICON_NONE); - uiItemL(layout, callback_data->new_filepath, ICON_NONE); - - /* Draw available report messages. */ - LISTBASE_FOREACH (Report *, report, &callback_data->user_edited_lost_reports->list) { - uiLayout *row = uiLayoutColumn(layout, false); - uiLayoutSetScaleY(row, 0.6f); - uiItemS(row); - - uiItemL_ex(row, report->message, ICON_NONE, false, true); - } - - uiItemS_ex(layout, 4.0f); - - /* Buttons. */ -#ifdef _WIN32 - const bool windows_layout = true; -#else - const bool windows_layout = false; -#endif - - if (windows_layout) { - /* Windows standard layout. */ - - uiLayout *split = uiLayoutSplit(layout, 0.0f, true); - uiLayoutSetScaleY(split, 1.2f); - - uiLayoutColumn(split, false); - wm_block_open_file_draft_save_button(block, post_action); - - uiLayoutColumn(split, false); - wm_block_open_file_draft_discard_button(block, post_action); - - uiLayoutColumn(split, false); - wm_block_open_file_draft_cancel_button(block, post_action); - } - else { - /* Non-Windows layout (macOS and Linux). */ - - uiLayout *split = uiLayoutSplit(layout, 0.3f, true); - uiLayoutSetScaleY(split, 1.2f); - - uiLayoutColumn(split, false); - wm_block_open_file_draft_discard_button(block, post_action); - - uiLayout *split_right = uiLayoutSplit(split, 0.1f, true); - - uiLayoutColumn(split_right, false); - /* Empty space. */ - - uiLayoutColumn(split_right, false); - wm_block_open_file_draft_cancel_button(block, post_action); - - uiLayoutColumn(split_right, false); - wm_block_open_file_draft_save_button(block, post_action); - } - - UI_block_bounds_set_centered(block, int(14 * U.scale_factor)); - return block; -} - -static void wm_free_open_file_draft_check_callback(void *user_data) -{ - wmOpenDraftCheckCallback *callback_data = static_cast(user_data); - - IDP_FreeProperty(callback_data->op_properties); - BKE_reports_clear(callback_data->user_edited_lost_reports); - MEM_delete(callback_data->user_edited_lost_reports); - - MEM_delete(callback_data); -} - -/* NOTE: steals ownership of `user_edited_lost_reports`. */ -static bool wm_operator_open_file_draft_check_dialog(bContext *C, - wmOperator *op, - const int num_user_edited_lost, - ReportList *user_edited_lost_reports) -{ - wmOpenDraftCheckCallback *callback_data = MEM_cnew(__func__); - callback_data->op_properties = IDP_CopyProperty(op->properties); - RNA_string_get(op->ptr, "filepath", callback_data->new_filepath); - callback_data->num_user_edited_lost = num_user_edited_lost; - callback_data->user_edited_lost_reports = user_edited_lost_reports; - - wmGenericCallback *callback = MEM_cnew(__func__); - callback->exec = wm_open_file_after_draft_check_dialog_callback; - callback->user_data = callback_data; - callback->free_user_data = wm_free_open_file_draft_check_callback; - - if (!UI_popup_block_name_exists(CTX_wm_screen(C), open_file_draft_check_dialog_name)) { - UI_popup_block_invoke( - C, block_create__open_draft_check_file_dialog, callback, free_post_file_close_action); - return true; - } - - WM_generic_callback_free(callback); - return false; -} -/** \} */ diff --git a/source/blender/windowmanager/intern/wm_init_exit.cc b/source/blender/windowmanager/intern/wm_init_exit.cc index b9b00bdaead..d52533ce4fe 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.cc +++ b/source/blender/windowmanager/intern/wm_init_exit.cc @@ -33,8 +33,10 @@ #include "BLO_undofile.hh" #include "BLO_writefile.hh" +#include "BKE_asset.hh" #include "BKE_blender.h" #include "BKE_blendfile.hh" +#include "BKE_brush.hh" #include "BKE_callbacks.h" #include "BKE_context.hh" #include "BKE_global.h" @@ -580,6 +582,7 @@ void WM_exit_ex(bContext *C, const bool do_python_exit, const bool do_user_exit_ ED_preview_restart_queue_free(); ed::asset::list::storage_exit(); + BKE_asset_weak_reference_main_free(); BKE_tracking_clipboard_free(); BKE_mask_clipboard_free(); BKE_vfont_clipboard_free(); -- 2.30.2 From aefbdf8cee08ac887076c7349662c45301303742 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 1 Feb 2024 21:45:43 +0100 Subject: [PATCH 02/14] Fix: same asset getting loaded multiple times --- .../blenkernel/intern/asset_weak_reference.cc | 80 ++++++++++--------- 1 file changed, 42 insertions(+), 38 deletions(-) diff --git a/source/blender/blenkernel/intern/asset_weak_reference.cc b/source/blender/blenkernel/intern/asset_weak_reference.cc index 2804f57130b..68129158c4e 100644 --- a/source/blender/blenkernel/intern/asset_weak_reference.cc +++ b/source/blender/blenkernel/intern/asset_weak_reference.cc @@ -117,12 +117,13 @@ void BKE_asset_weak_reference_read(BlendDataReader *reader, AssetWeakReference * struct AssetWeakReferenceMain { /* TODO: not sure if this is the best unique identifier. */ - std::string lib_path; + std::string filepath; Main *main; - AssetWeakReferenceMain(const char *lib_path) : lib_path(lib_path), main(BKE_main_new()) {} + AssetWeakReferenceMain(const char *filepath) : filepath(filepath), main(BKE_main_new()) {} AssetWeakReferenceMain(const AssetWeakReferenceMain &) = delete; - AssetWeakReferenceMain(AssetWeakReferenceMain &&other) : main(std::exchange(other.main, nullptr)) + AssetWeakReferenceMain(AssetWeakReferenceMain &&other) + : filepath(std::exchange(other.filepath, "")), main(std::exchange(other.main, nullptr)) { } @@ -158,15 +159,15 @@ Main *BKE_asset_weak_reference_main(Main *global_main, const ID *id) return nullptr; } -static Main *asset_weak_reference_main_ensure(const char *lib_path) +static Main *asset_weak_reference_main_ensure(const char *filepath) { for (const AssetWeakReferenceMain &weak_ref_main : ASSET_WEAK_REFERENCE_MAINS) { - if (weak_ref_main.lib_path == lib_path) { + if (weak_ref_main.filepath == filepath) { return weak_ref_main.main; } } - ASSET_WEAK_REFERENCE_MAINS.append(lib_path); + ASSET_WEAK_REFERENCE_MAINS.append(filepath); return ASSET_WEAK_REFERENCE_MAINS.last().main; } @@ -194,42 +195,45 @@ ID *BKE_asset_weak_reference_ensure(Main *global_main, const AssetWeakReference BLI_assert(asset_name != nullptr); /* If weak reference resolves to a null library path, assume we are in local asset case. */ - Main *asset_main = global_main; + Main *asset_main = (asset_lib_path) ? asset_weak_reference_main_ensure(asset_lib_path) : + global_main; - if (asset_lib_path != nullptr) { - /* TODO: avoid appending twice? */ - asset_main = asset_weak_reference_main_ensure(asset_lib_path); - - LibraryLink_Params lapp_parameters{}; - lapp_parameters.bmain = asset_main; - BlendfileLinkAppendContext *lapp_context = BKE_blendfile_link_append_context_new( - &lapp_parameters); - BKE_blendfile_link_append_context_flag_set(lapp_context, BLO_LIBLINK_FORCE_INDIRECT, true); - BKE_blendfile_link_append_context_flag_set(lapp_context, 0, true); - - BKE_blendfile_link_append_context_library_add(lapp_context, asset_lib_path, nullptr); - - // TODO: make not brush specific - BlendfileLinkAppendContextItem *lapp_item = BKE_blendfile_link_append_context_item_add( - lapp_context, asset_name, ID_BR, nullptr); - BKE_blendfile_link_append_context_item_library_index_enable(lapp_context, lapp_item, 0); - - BKE_blendfile_link(lapp_context, nullptr); - BKE_blendfile_append(lapp_context, nullptr); - - BKE_blendfile_link_append_context_free(lapp_context); - - /* TODO: only do for new ones? */ - BKE_main_id_tag_all(asset_main, LIB_TAG_ASSET_MAIN, true); - } - - /* TODO: are we sure it will not get renamed? */ + /* Check if we have the asset already, or if it's global main and there is nothing we can add. */ ID *local_asset = reinterpret_cast( BLI_findstring(&asset_main->brushes, asset_name, offsetof(ID, name) + 2)); - - if (local_asset == nullptr || !ID_IS_ASSET(local_asset)) { - return nullptr; + if (local_asset || asset_lib_path == nullptr) { + BLI_assert(local_asset == nullptr || ID_IS_ASSET(local_asset)); + return local_asset; } + + /* Load asset from asset library. */ + LibraryLink_Params lapp_parameters{}; + lapp_parameters.bmain = asset_main; + BlendfileLinkAppendContext *lapp_context = BKE_blendfile_link_append_context_new( + &lapp_parameters); + BKE_blendfile_link_append_context_flag_set(lapp_context, BLO_LIBLINK_FORCE_INDIRECT, true); + BKE_blendfile_link_append_context_flag_set(lapp_context, 0, true); + + BKE_blendfile_link_append_context_library_add(lapp_context, asset_lib_path, nullptr); + + // TODO: make not brush specific + BlendfileLinkAppendContextItem *lapp_item = BKE_blendfile_link_append_context_item_add( + lapp_context, asset_name, ID_BR, nullptr); + BKE_blendfile_link_append_context_item_library_index_enable(lapp_context, lapp_item, 0); + + BKE_blendfile_link(lapp_context, nullptr); + BKE_blendfile_append(lapp_context, nullptr); + + local_asset = BKE_blendfile_link_append_context_item_newid_get(lapp_context, lapp_item); + + BKE_blendfile_link_append_context_free(lapp_context); + + /* TODO: only do for new ones? */ + BKE_main_id_tag_all(asset_main, LIB_TAG_ASSET_MAIN, true); + + /* Verify that the name matches. It must for referencing the same asset again to work. */ + BLI_asset(local_asset == nullptr || STREQ(local_asset->name + 2, asset_name)); + return local_asset; } -- 2.30.2 From ef90ec70ce0a0a3900ae21611928c51f1eddd708 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 1 Feb 2024 21:08:44 +0100 Subject: [PATCH 03/14] Fix Revert to Asset not working --- .../blender/editors/sculpt_paint/paint_ops.cc | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 9835d6c6741..bab10ae2453 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -1470,12 +1470,21 @@ static int brush_asset_revert_exec(bContext *C, wmOperator * /*op*/) Main *bmain = CTX_data_main(C); Paint *paint = BKE_paint_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); + Main *asset_main = BKE_asset_weak_reference_main(bmain, &brush->id); - /* TODO: check if doing this for the hierarchy is ok. */ - /* TODO: the overrides don't update immediately when tweaking brush settings. */ - BKE_lib_override_library_id_hierarchy_reset(bmain, &brush->id, false); + // TODO: delete and reload dependencies too? + // TODO: hack to make remapping work, should not be needed + // if we can make brush pointer not part of ID management at all + BLI_remlink(&asset_main->brushes, brush); - WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush); + Brush *new_brush = reinterpret_cast( + BKE_asset_weak_reference_ensure(bmain, paint->brush_asset_reference)); + + BKE_libblock_remap(bmain, brush, new_brush, 0); + BLI_addtail(&asset_main->brushes, brush); + BKE_id_delete(asset_main, brush); + + WM_main_add_notifier(NC_BRUSH | NA_EDITED, new_brush); return OPERATOR_FINISHED; } -- 2.30.2 From a93300fe20966b836f0fef69ef670f9fd7f92c27 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 1 Feb 2024 21:08:54 +0100 Subject: [PATCH 04/14] Fix creating textures and images for brush not working --- .../blenkernel/intern/asset_weak_reference.cc | 2 +- .../blender/editors/render/render_shading.cc | 17 +++++++++++------ .../blender/editors/space_image/image_ops.cc | 18 +++++++++++------- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/source/blender/blenkernel/intern/asset_weak_reference.cc b/source/blender/blenkernel/intern/asset_weak_reference.cc index 68129158c4e..113f19ebf1a 100644 --- a/source/blender/blenkernel/intern/asset_weak_reference.cc +++ b/source/blender/blenkernel/intern/asset_weak_reference.cc @@ -232,7 +232,7 @@ ID *BKE_asset_weak_reference_ensure(Main *global_main, const AssetWeakReference BKE_main_id_tag_all(asset_main, LIB_TAG_ASSET_MAIN, true); /* Verify that the name matches. It must for referencing the same asset again to work. */ - BLI_asset(local_asset == nullptr || STREQ(local_asset->name + 2, asset_name)); + BLI_assert(local_asset == nullptr || STREQ(local_asset->name + 2, asset_name)); return local_asset; } diff --git a/source/blender/editors/render/render_shading.cc b/source/blender/editors/render/render_shading.cc index 86303f94c2a..9471af89fcc 100644 --- a/source/blender/editors/render/render_shading.cc +++ b/source/blender/editors/render/render_shading.cc @@ -35,6 +35,7 @@ #include "BKE_anim_data.h" #include "BKE_animsys.h" #include "BKE_appdir.hh" +#include "BKE_asset.hh" #include "BKE_blender_copybuffer.hh" #include "BKE_brush.hh" #include "BKE_context.hh" @@ -821,22 +822,26 @@ void MATERIAL_OT_new(wmOperatorType *ot) static int new_texture_exec(bContext *C, wmOperator * /*op*/) { - Tex *tex = static_cast(CTX_data_pointer_get_type(C, "texture", &RNA_Texture).data); - Main *bmain = CTX_data_main(C); PointerRNA ptr; PropertyRNA *prop; + UI_context_active_but_prop_get_templateID(C, &ptr, &prop); + + Main *id_main = CTX_data_main(C); + if (ptr.owner_id) { + id_main = BKE_asset_weak_reference_main(id_main, ptr.owner_id); + } + /* add or copy texture */ + Tex *tex = static_cast(CTX_data_pointer_get_type(C, "texture", &RNA_Texture).data); if (tex) { - tex = (Tex *)BKE_id_copy(bmain, &tex->id); + tex = (Tex *)BKE_id_copy(id_main, &tex->id); } else { - tex = BKE_texture_add(bmain, DATA_("Texture")); + tex = BKE_texture_add(id_main, DATA_("Texture")); } /* hook into UI */ - UI_context_active_but_prop_get_templateID(C, &ptr, &prop); - if (prop) { /* when creating new ID blocks, use is already 1, but RNA * pointer use also increases user, so this compensates it */ diff --git a/source/blender/editors/space_image/image_ops.cc b/source/blender/editors/space_image/image_ops.cc index a48dd4fbbec..0d6ccae10a0 100644 --- a/source/blender/editors/space_image/image_ops.cc +++ b/source/blender/editors/space_image/image_ops.cc @@ -34,6 +34,7 @@ #include "DNA_scene_types.h" #include "DNA_screen_types.h" +#include "BKE_asset.hh" #include "BKE_colortools.hh" #include "BKE_context.hh" #include "BKE_global.h" @@ -2537,7 +2538,7 @@ static int image_new_exec(bContext *C, wmOperator *op) { SpaceImage *sima; Image *ima; - Main *bmain; + Main *id_main; PropertyRNA *prop; char name_buffer[MAX_ID_NAME - 2]; const char *name; @@ -2545,9 +2546,14 @@ static int image_new_exec(bContext *C, wmOperator *op) int width, height, floatbuf, gen_type, alpha; int stereo3d; + ImageNewData *data = image_new_init(C, op); + /* retrieve state */ sima = CTX_wm_space_image(C); - bmain = CTX_data_main(C); + id_main = CTX_data_main(C); + if (data->pprop.ptr.owner_id) { + id_main = BKE_asset_weak_reference_main(id_main, data->pprop.ptr.owner_id); + } prop = RNA_struct_find_property(op->ptr, "name"); RNA_property_string_get(op->ptr, prop, name_buffer); @@ -2571,7 +2577,7 @@ static int image_new_exec(bContext *C, wmOperator *op) color[3] = 1.0f; } - ima = BKE_image_add_generated(bmain, + ima = BKE_image_add_generated(id_main, width, height, name, @@ -2589,8 +2595,6 @@ static int image_new_exec(bContext *C, wmOperator *op) } /* hook into UI */ - ImageNewData *data = image_new_init(C, op); - if (data->pprop.prop) { /* when creating new ID blocks, use is already 1, but RNA * pointer use also increases user, so this compensates it */ @@ -2601,7 +2605,7 @@ static int image_new_exec(bContext *C, wmOperator *op) RNA_property_update(C, &data->pprop.ptr, data->pprop.prop); } else if (sima) { - ED_space_image_set(bmain, sima, ima, false); + ED_space_image_set(id_main, sima, ima, false); } else { /* #BKE_image_add_generated creates one user by default, remove it if image is not linked to @@ -2609,7 +2613,7 @@ static int image_new_exec(bContext *C, wmOperator *op) id_us_min(&ima->id); } - BKE_image_signal(bmain, ima, (sima) ? &sima->iuser : nullptr, IMA_SIGNAL_USER_NEW_IMAGE); + BKE_image_signal(id_main, ima, (sima) ? &sima->iuser : nullptr, IMA_SIGNAL_USER_NEW_IMAGE); WM_event_add_notifier(C, NC_IMAGE | NA_ADDED, ima); -- 2.30.2 From ef391974ff56f7b48d1cb8ddb2b820b1b64be394 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 21 Feb 2024 12:25:22 -0500 Subject: [PATCH 05/14] Cleanup: Use construct after first use idiom Removes the need to manually free the mains, since the static variable will be destructed in the proper order. But still keep that for now, just in case. --- .../blenkernel/intern/asset_weak_reference.cc | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/source/blender/blenkernel/intern/asset_weak_reference.cc b/source/blender/blenkernel/intern/asset_weak_reference.cc index b70e796d3d7..cbefb1a6dc6 100644 --- a/source/blender/blenkernel/intern/asset_weak_reference.cc +++ b/source/blender/blenkernel/intern/asset_weak_reference.cc @@ -156,7 +156,11 @@ struct AssetWeakReferenceMain { } }; -blender::Vector ASSET_WEAK_REFERENCE_MAINS; +static Vector &get_weak_reference_mains() +{ + static Vector mains; + return mains; +} Main *BKE_asset_weak_reference_main(Main *global_main, const ID *id) { @@ -164,7 +168,7 @@ Main *BKE_asset_weak_reference_main(Main *global_main, const ID *id) return global_main; } - for (const AssetWeakReferenceMain &weak_ref_main : ASSET_WEAK_REFERENCE_MAINS) { + for (const AssetWeakReferenceMain &weak_ref_main : get_weak_reference_mains()) { /* TODO: just loop over listbase of same type, or make this whole thing * more efficient. */ ID *other_id; @@ -182,19 +186,19 @@ Main *BKE_asset_weak_reference_main(Main *global_main, const ID *id) static Main *asset_weak_reference_main_ensure(const char *filepath) { - for (const AssetWeakReferenceMain &weak_ref_main : ASSET_WEAK_REFERENCE_MAINS) { + for (const AssetWeakReferenceMain &weak_ref_main : get_weak_reference_mains()) { if (weak_ref_main.filepath == filepath) { return weak_ref_main.main; } } - ASSET_WEAK_REFERENCE_MAINS.append(filepath); - return ASSET_WEAK_REFERENCE_MAINS.last().main; + get_weak_reference_mains().append(filepath); + return get_weak_reference_mains().last().main; } void BKE_asset_weak_reference_main_free() { - ASSET_WEAK_REFERENCE_MAINS.clear_and_shrink(); + get_weak_reference_mains().clear_and_shrink(); } ID *BKE_asset_weak_reference_ensure(Main *global_main, const AssetWeakReference *weak_ref) -- 2.30.2 From a5b136bbd649e83aefeaff16aca26bdd14651f75 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 21 Feb 2024 12:29:23 -0500 Subject: [PATCH 06/14] Only search for Main in ListBase of data-block type --- source/blender/blenkernel/intern/asset_weak_reference.cc | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/source/blender/blenkernel/intern/asset_weak_reference.cc b/source/blender/blenkernel/intern/asset_weak_reference.cc index cbefb1a6dc6..40b07cd6c2d 100644 --- a/source/blender/blenkernel/intern/asset_weak_reference.cc +++ b/source/blender/blenkernel/intern/asset_weak_reference.cc @@ -169,15 +169,13 @@ Main *BKE_asset_weak_reference_main(Main *global_main, const ID *id) } for (const AssetWeakReferenceMain &weak_ref_main : get_weak_reference_mains()) { - /* TODO: just loop over listbase of same type, or make this whole thing - * more efficient. */ - ID *other_id; - FOREACH_MAIN_ID_BEGIN (weak_ref_main.main, other_id) { + /* TODO: Look into make this whole thing more efficient. */ + ListBase *lb = which_libbase(weak_ref_main.main, GS(id->name)); + LISTBASE_FOREACH (ID *, other_id, lb) { if (id == other_id) { return weak_ref_main.main; } } - FOREACH_MAIN_ID_END; } BLI_assert_unreachable(); -- 2.30.2 From 64d07edd0ca5ad7636e61dae22fbaa1ece7a064e Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 21 Feb 2024 12:29:56 -0500 Subject: [PATCH 07/14] Cleanup: Avoid c strings --- source/blender/blenkernel/intern/asset_weak_reference.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/intern/asset_weak_reference.cc b/source/blender/blenkernel/intern/asset_weak_reference.cc index 40b07cd6c2d..5dfdb8ca3bd 100644 --- a/source/blender/blenkernel/intern/asset_weak_reference.cc +++ b/source/blender/blenkernel/intern/asset_weak_reference.cc @@ -141,7 +141,10 @@ struct AssetWeakReferenceMain { std::string filepath; Main *main; - AssetWeakReferenceMain(const char *filepath) : filepath(filepath), main(BKE_main_new()) {} + AssetWeakReferenceMain(std::string filepath) + : filepath(std::move(filepath)), main(BKE_main_new()) + { + } AssetWeakReferenceMain(const AssetWeakReferenceMain &) = delete; AssetWeakReferenceMain(AssetWeakReferenceMain &&other) : filepath(std::exchange(other.filepath, "")), main(std::exchange(other.main, nullptr)) @@ -182,7 +185,7 @@ Main *BKE_asset_weak_reference_main(Main *global_main, const ID *id) return nullptr; } -static Main *asset_weak_reference_main_ensure(const char *filepath) +static Main *asset_weak_reference_main_ensure(const StringRef filepath) { for (const AssetWeakReferenceMain &weak_ref_main : get_weak_reference_mains()) { if (weak_ref_main.filepath == filepath) { @@ -190,7 +193,7 @@ static Main *asset_weak_reference_main_ensure(const char *filepath) } } - get_weak_reference_mains().append(filepath); + get_weak_reference_mains().append_as(filepath); return get_weak_reference_mains().last().main; } -- 2.30.2 From 0475797e05af56f540e6a023e963016c596a0af6 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 21 Feb 2024 13:39:35 -0500 Subject: [PATCH 08/14] Make BKE_asset_weak_reference_ensure ID type agnostic --- source/blender/blenkernel/BKE_asset.hh | 5 ++- .../blenkernel/intern/asset_weak_reference.cc | 34 ++++++++----------- source/blender/blenkernel/intern/paint.cc | 2 +- .../blender/editors/sculpt_paint/paint_ops.cc | 7 ++-- 4 files changed, 23 insertions(+), 25 deletions(-) diff --git a/source/blender/blenkernel/BKE_asset.hh b/source/blender/blenkernel/BKE_asset.hh index 66da318bc9b..3920d9e0d08 100644 --- a/source/blender/blenkernel/BKE_asset.hh +++ b/source/blender/blenkernel/BKE_asset.hh @@ -11,6 +11,7 @@ #include "BLI_compiler_attrs.h" #include "BLI_utildefines.h" +#include "DNA_ID_enums.h" #include "DNA_asset_types.h" struct AssetLibraryReference; @@ -82,4 +83,6 @@ void BKE_asset_weak_reference_read(BlendDataReader *reader, AssetWeakReference * Main *BKE_asset_weak_reference_main(Main *global_main, const ID *id); void BKE_asset_weak_reference_main_free(); -ID *BKE_asset_weak_reference_ensure(Main *global_main, const AssetWeakReference *weak_ref); +ID *BKE_asset_weak_reference_ensure(Main &global_main, + ID_Type id_type, + const AssetWeakReference &weak_ref); diff --git a/source/blender/blenkernel/intern/asset_weak_reference.cc b/source/blender/blenkernel/intern/asset_weak_reference.cc index 5dfdb8ca3bd..fe844e13d34 100644 --- a/source/blender/blenkernel/intern/asset_weak_reference.cc +++ b/source/blender/blenkernel/intern/asset_weak_reference.cc @@ -185,16 +185,16 @@ Main *BKE_asset_weak_reference_main(Main *global_main, const ID *id) return nullptr; } -static Main *asset_weak_reference_main_ensure(const StringRef filepath) +static Main &asset_weak_reference_main_ensure(const StringRef filepath) { for (const AssetWeakReferenceMain &weak_ref_main : get_weak_reference_mains()) { if (weak_ref_main.filepath == filepath) { - return weak_ref_main.main; + return *weak_ref_main.main; } } get_weak_reference_mains().append_as(filepath); - return get_weak_reference_mains().last().main; + return *get_weak_reference_mains().last().main; } void BKE_asset_weak_reference_main_free() @@ -202,49 +202,43 @@ void BKE_asset_weak_reference_main_free() get_weak_reference_mains().clear_and_shrink(); } -ID *BKE_asset_weak_reference_ensure(Main *global_main, const AssetWeakReference *weak_ref) +ID *BKE_asset_weak_reference_ensure(Main &global_main, + const ID_Type id_type, + const AssetWeakReference &weak_ref) { - BLI_assert(weak_ref != nullptr); - char asset_full_path_buffer[FILE_MAX_LIBEXTRA]; char *asset_lib_path, *asset_group, *asset_name; AS_asset_full_path_explode_from_weak_ref( - weak_ref, asset_full_path_buffer, &asset_lib_path, &asset_group, &asset_name); + &weak_ref, asset_full_path_buffer, &asset_lib_path, &asset_group, &asset_name); if (asset_lib_path == nullptr && asset_group == nullptr && asset_name == nullptr) { return nullptr; } - // TODO: make not brush specific - BLI_assert(STREQ(asset_group, IDType_ID_BR.name)); BLI_assert(asset_name != nullptr); /* If weak reference resolves to a null library path, assume we are in local asset case. */ - Main *asset_main = (asset_lib_path) ? asset_weak_reference_main_ensure(asset_lib_path) : - global_main; + Main &bmain = asset_lib_path ? asset_weak_reference_main_ensure(asset_lib_path) : global_main; /* Check if we have the asset already, or if it's global main and there is nothing we can add. */ - ID *local_asset = reinterpret_cast( - BLI_findstring(&asset_main->brushes, asset_name, offsetof(ID, name) + 2)); + ID *local_asset = BKE_libblock_find_name(&bmain, id_type, asset_name); if (local_asset || asset_lib_path == nullptr) { BLI_assert(local_asset == nullptr || ID_IS_ASSET(local_asset)); return local_asset; } /* Load asset from asset library. */ - LibraryLink_Params lapp_parameters{}; - lapp_parameters.bmain = asset_main; - BlendfileLinkAppendContext *lapp_context = BKE_blendfile_link_append_context_new( - &lapp_parameters); + LibraryLink_Params lapp_params{}; + lapp_params.bmain = &bmain; + BlendfileLinkAppendContext *lapp_context = BKE_blendfile_link_append_context_new(&lapp_params); BKE_blendfile_link_append_context_flag_set(lapp_context, BLO_LIBLINK_FORCE_INDIRECT, true); BKE_blendfile_link_append_context_flag_set(lapp_context, 0, true); BKE_blendfile_link_append_context_library_add(lapp_context, asset_lib_path, nullptr); - // TODO: make not brush specific BlendfileLinkAppendContextItem *lapp_item = BKE_blendfile_link_append_context_item_add( - lapp_context, asset_name, ID_BR, nullptr); + lapp_context, asset_name, id_type, nullptr); BKE_blendfile_link_append_context_item_library_index_enable(lapp_context, lapp_item, 0); BKE_blendfile_link(lapp_context, nullptr); @@ -255,7 +249,7 @@ ID *BKE_asset_weak_reference_ensure(Main *global_main, const AssetWeakReference BKE_blendfile_link_append_context_free(lapp_context); /* TODO: only do for new ones? */ - BKE_main_id_tag_all(asset_main, LIB_TAG_ASSET_MAIN, true); + BKE_main_id_tag_all(&bmain, LIB_TAG_ASSET_MAIN, true); /* Verify that the name matches. It must for referencing the same asset again to work. */ BLI_assert(local_asset == nullptr || STREQ(local_asset->name + 2, asset_name)); diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 690c9e2783b..9d64f7e053b 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -737,7 +737,7 @@ void BKE_paint_brush_asset_restore(Main *bmain, Paint *paint) paint->brush_asset_reference = nullptr; Brush *brush_asset = reinterpret_cast( - BKE_asset_weak_reference_ensure(bmain, &weak_ref)); + BKE_asset_weak_reference_ensure(*bmain, ID_BR, weak_ref)); /* Will either re-assign the brush_asset_reference to `paint`, or free it if loading a brush ID * from it failed. */ diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index e01d294e7cd..60736242078 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -1008,7 +1008,7 @@ static int brush_asset_select_exec(bContext *C, wmOperator *op) AssetWeakReference brush_asset_reference = asset->make_weak_reference(); Brush *brush = reinterpret_cast( - BKE_asset_weak_reference_ensure(bmain, &brush_asset_reference)); + BKE_asset_weak_reference_ensure(*bmain, ID_BR, brush_asset_reference)); Paint *paint = BKE_paint_get_active_from_context(C); @@ -1371,7 +1371,8 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) /* TODO: maybe not needed, even less so if there is more visual confirmation of change. */ BKE_reportf(op->reports, RPT_INFO, "Saved \"%s\"", filepath.c_str()); - brush = reinterpret_cast(BKE_asset_weak_reference_ensure(bmain, &new_brush_weak_ref)); + brush = reinterpret_cast( + BKE_asset_weak_reference_ensure(*bmain, ID_BR, new_brush_weak_ref)); if (!BKE_paint_brush_asset_set(paint, brush, new_brush_weak_ref)) { /* Note brush sset was still saved in editable asset library, so was not a no-op. */ @@ -1623,7 +1624,7 @@ static int brush_asset_revert_exec(bContext *C, wmOperator * /*op*/) BLI_remlink(&asset_main->brushes, brush); Brush *new_brush = reinterpret_cast( - BKE_asset_weak_reference_ensure(bmain, paint->brush_asset_reference)); + BKE_asset_weak_reference_ensure(*bmain, ID_BR, *paint->brush_asset_reference)); BKE_libblock_remap(bmain, brush, new_brush, 0); BLI_addtail(&asset_main->brushes, brush); -- 2.30.2 From 629d1c3c4b82d231452ae72104d0fc12c2a6f019 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 21 Feb 2024 13:59:40 -0500 Subject: [PATCH 09/14] Cleanup: Remove some unnecessary temporary main variables Maybe this makes it a bit harder to use the wrong main by mistake --- source/blender/editors/interface/interface_templates.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/interface/interface_templates.cc b/source/blender/editors/interface/interface_templates.cc index be85442392f..0b3687ad732 100644 --- a/source/blender/editors/interface/interface_templates.cc +++ b/source/blender/editors/interface/interface_templates.cc @@ -998,8 +998,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) break; case UI_ID_LOCAL: if (id) { - Main *bmain = CTX_data_main(C); - Main *id_main = BKE_asset_weak_reference_main(bmain, id); + Main *id_main = BKE_asset_weak_reference_main(CTX_data_main(C), id); if (CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) { template_id_liboverride_hierarchy_make( C, id_main, template_ui, &idptr, &undo_push_label); @@ -1021,8 +1020,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) break; case UI_ID_OVERRIDE: if (id && ID_IS_OVERRIDE_LIBRARY(id)) { - Main *bmain = CTX_data_main(C); - Main *id_main = BKE_asset_weak_reference_main(bmain, id); + Main *id_main = BKE_asset_weak_reference_main(CTX_data_main(C), id); if (CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) { template_id_liboverride_hierarchy_make( C, id_main, template_ui, &idptr, &undo_push_label); -- 2.30.2 From 45b5c20f204d6911ed9c833aa28c1bb47c91e4c5 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 21 Feb 2024 14:02:47 -0500 Subject: [PATCH 10/14] Remove BRUSH_OT_add operator This doesn't seem to be visible anywhere in the UI. New brushes are more easily created by duplicating existing brushes anyway. --- .../startup/bl_ui/properties_paint_common.py | 2 +- .../blender/editors/sculpt_paint/paint_ops.cc | 37 ------------------- 2 files changed, 1 insertion(+), 38 deletions(-) diff --git a/scripts/startup/bl_ui/properties_paint_common.py b/scripts/startup/bl_ui/properties_paint_common.py index b91f0613e3c..a1ebc5b66c8 100644 --- a/scripts/startup/bl_ui/properties_paint_common.py +++ b/scripts/startup/bl_ui/properties_paint_common.py @@ -149,7 +149,7 @@ class BrushSelectPanel(BrushPanel): row = layout.row() # TODO: hide buttons since they are confusing with menu entries. # But some of this functionality may still be needed. - row.column().template_ID_preview(settings, "brush", new="brush.add", rows=3, cols=8, hide_buttons=True) + row.column().template_ID_preview(settings, "brush", rows=3, cols=8, hide_buttons=True) if brush is None: return diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 60736242078..80094da286f 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -67,42 +67,6 @@ #include "paint_intern.hh" #include "sculpt_intern.hh" -/* Brush operators */ -static int brush_add_exec(bContext *C, wmOperator * /*op*/) -{ - // int type = RNA_enum_get(op->ptr, "type"); - Paint *paint = BKE_paint_get_active_from_context(C); - Brush *br = BKE_paint_brush(paint); - Main *bmain = CTX_data_main(C); // TODO: add to asset main? - PaintMode mode = BKE_paintmode_get_active_from_context(C); - - if (br) { - br = (Brush *)BKE_id_copy(bmain, &br->id); - } - else { - br = BKE_brush_add(bmain, "Brush", BKE_paint_object_mode_from_paintmode(mode)); - } - id_us_min(&br->id); /* fake user only */ - - BKE_paint_brush_set(paint, br); - - return OPERATOR_FINISHED; -} - -static void BRUSH_OT_add(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Add Brush"; - ot->description = "Add brush by mode type"; - ot->idname = "BRUSH_OT_add"; - - /* api callbacks */ - ot->exec = brush_add_exec; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - static eGPBrush_Presets gpencil_get_brush_preset_from_tool(bToolRef *tool, enum eContextObjectMode mode) { @@ -2100,7 +2064,6 @@ void ED_operatortypes_paint() WM_operatortype_append(PAINTCURVE_OT_cursor); /* brush */ - WM_operatortype_append(BRUSH_OT_add); WM_operatortype_append(BRUSH_OT_add_gpencil); WM_operatortype_append(BRUSH_OT_scale_size); WM_operatortype_append(BRUSH_OT_curve_preset); -- 2.30.2 From 716a5825833f96fc3b0f1b4ff7fb115423f2702a Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 21 Feb 2024 14:13:44 -0500 Subject: [PATCH 11/14] Remove unnecessary tool -> brush connection These were adding brushes and finding brushes in the active (non-asset) main. This sort of thing should be handled differently now anyway --- .../windowmanager/intern/wm_toolsystem.cc | 37 ------------------- 1 file changed, 37 deletions(-) diff --git a/source/blender/windowmanager/intern/wm_toolsystem.cc b/source/blender/windowmanager/intern/wm_toolsystem.cc index 1da5f507b3c..91c585f02db 100644 --- a/source/blender/windowmanager/intern/wm_toolsystem.cc +++ b/source/blender/windowmanager/intern/wm_toolsystem.cc @@ -174,43 +174,6 @@ static void toolsystem_ref_link(bContext *C, WorkSpace *workspace, bToolRef *tre } } } - else { - const PaintMode paint_mode = BKE_paintmode_get_from_tool(tref); - BLI_assert(paint_mode != PaintMode::Invalid); - const EnumPropertyItem *items = BKE_paint_get_tool_enum_from_paintmode(paint_mode); - BLI_assert(items != nullptr); - - const int i = items ? RNA_enum_from_identifier(items, tref_rt->data_block) : -1; - if (i != -1) { - const int slot_index = items[i].value; - wmWindowManager *wm = static_cast(bmain->wm.first); - LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { - if (workspace == WM_window_get_active_workspace(win)) { - Scene *scene = WM_window_get_active_scene(win); - BKE_paint_ensure_from_paintmode(bmain, scene, paint_mode); - Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode); - Brush *brush = BKE_paint_toolslots_brush_get(paint, slot_index); - if (brush == nullptr) { - /* Could make into a function. */ - brush = (Brush *)BKE_libblock_find_name(bmain, ID_BR, items[i].name); - if (brush && slot_index == BKE_brush_tool_get(brush, paint)) { - /* pass */ - } - else { - brush = BKE_brush_add(bmain, items[i].name, eObjectMode(paint->runtime.ob_mode)); - - BKE_brush_tool_set(brush, paint, slot_index); - - if (paint_mode == PaintMode::Sculpt) { - BKE_brush_sculpt_reset(brush); - } - } - } - BKE_paint_brush_set(paint, brush); - } - } - } - } } } -- 2.30.2 From 99e6409a2e1154d68f7a55936237a456544fbeca Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 21 Feb 2024 14:14:34 -0500 Subject: [PATCH 12/14] Remove creating a new brush on Paint init when none is found Now instead of creating a brush from scratch, the default asset should be used instead. I'm pretty sure that is done elsewhere, and if it isn't it should be fixed. --- source/blender/blenkernel/intern/paint.cc | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 9d64f7e053b..8afb6735391 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1254,20 +1254,6 @@ void BKE_paint_init(Main *bmain, Scene *sce, PaintMode mode, const uchar col[3]) BKE_paint_ensure_from_paintmode(bmain, sce, mode); - /* If there's no brush, create one */ - if (PAINT_MODE_HAS_BRUSH(mode)) { - Brush *brush = BKE_paint_brush(paint); - if (brush == nullptr) { - eObjectMode ob_mode = BKE_paint_object_mode_from_paintmode(mode); - brush = BKE_brush_first_search(bmain, ob_mode); - if (!brush) { - brush = BKE_brush_add(bmain, "Brush", ob_mode); - id_us_min(&brush->id); /* Fake user only. */ - } - BKE_paint_brush_set(paint, brush); - } - } - copy_v3_v3_uchar(paint->paint_cursor_col, col); paint->paint_cursor_col[3] = 128; ups->last_stroke_valid = false; -- 2.30.2 From 67bb4a80b9c122e82adcf719b75a6868b18c36b5 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 21 Feb 2024 14:25:24 -0500 Subject: [PATCH 13/14] Remove "brush select" operator Selecting a brush based on the tool type isn't done anymore, and this operator doesn't really make sense in the context of prioritizing brush selection instead of tool selection / the asset shelf. It would otherwise have to be fixed for storing brush data-blocks in other mains. --- .../bl_keymap_utils/keymap_from_toolbar.py | 44 ---- .../keyconfig/keymap_data/blender_default.py | 21 -- .../interface/interface_region_tooltip.cc | 31 --- .../blender/editors/sculpt_paint/paint_ops.cc | 203 ------------------ 4 files changed, 299 deletions(-) diff --git a/scripts/modules/bl_keymap_utils/keymap_from_toolbar.py b/scripts/modules/bl_keymap_utils/keymap_from_toolbar.py index d2bcc07ab47..b0a75563892 100644 --- a/scripts/modules/bl_keymap_utils/keymap_from_toolbar.py +++ b/scripts/modules/bl_keymap_utils/keymap_from_toolbar.py @@ -107,10 +107,6 @@ def generate(context, space_type, *, use_fallback_keys=True, use_reset=True): kmi_hack_properties = kmi_hack.properties kmi_hack.active = False - kmi_hack_brush_select = keymap.keymap_items.new("paint.brush_select", 'NONE', 'PRESS') - kmi_hack_brush_select_properties = kmi_hack_brush_select.properties - kmi_hack_brush_select.active = False - if use_release_confirm or use_tap_reset: kmi_toolbar = wm.keyconfigs.find_item_from_operator( idname="wm.toolbar", @@ -169,46 +165,6 @@ def generate(context, space_type, *, use_fallback_keys=True, use_reset=True): include={'KEYBOARD'}, )[1] - if kmi_found is None: - if item.data_block: - # PAINT_OT_brush_select - mode = context.active_object.mode - # See: BKE_paint_get_tool_prop_id_from_paintmode - if space_type == 'IMAGE_EDITOR': - if context.space_data.mode == 'PAINT': - attr = "image_tool" - else: - attr = None - elif space_type == 'VIEW_3D': - attr = { - 'SCULPT': "sculpt_tool", - 'VERTEX_PAINT': "vertex_tool", - 'WEIGHT_PAINT': "weight_tool", - 'TEXTURE_PAINT': "image_tool", - 'PAINT_GPENCIL': "gpencil_tool", - 'VERTEX_GPENCIL': "gpencil_vertex_tool", - 'SCULPT_GPENCIL': "gpencil_sculpt_tool", - 'WEIGHT_GPENCIL': "gpencil_weight_tool", - 'SCULPT_CURVES': "curves_sculpt_tool", - }.get(mode, None) - else: - attr = None - - if attr is not None: - setattr(kmi_hack_brush_select_properties, attr, item.data_block) - kmi_found = wm.keyconfigs.find_item_from_operator( - idname="paint.brush_select", - context='INVOKE_REGION_WIN', - properties=kmi_hack_brush_select_properties, - include={'KEYBOARD'}, - )[1] - elif mode in {'EDIT', 'PARTICLE_EDIT', 'SCULPT_GPENCIL'}: - # Doesn't use brushes - pass - else: - print("Unsupported mode:", mode) - del mode, attr - else: kmi_found = None diff --git a/scripts/presets/keyconfig/keymap_data/blender_default.py b/scripts/presets/keyconfig/keymap_data/blender_default.py index cd8951f7a54..6fe35665b0d 100644 --- a/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -5501,27 +5501,6 @@ def km_sculpt(params): op_menu_pie("VIEW3D_MT_sculpt_automasking_pie", {"type": 'A', "alt": True, "value": 'PRESS'}), op_menu_pie("VIEW3D_MT_sculpt_face_sets_edit_pie", {"type": 'W', "value": 'PRESS', "alt": True}), *_template_items_context_panel("VIEW3D_PT_sculpt_context_menu", params.context_menu_event), - # Tools - ("paint.brush_select", {"type": 'V', "value": 'PRESS'}, - {"properties": [("sculpt_tool", 'DRAW')]}), - ("paint.brush_select", {"type": 'S', "value": 'PRESS'}, - {"properties": [("sculpt_tool", 'SMOOTH')]}), - ("paint.brush_select", {"type": 'P', "value": 'PRESS'}, - {"properties": [("sculpt_tool", 'PINCH')]}), - ("paint.brush_select", {"type": 'I', "value": 'PRESS'}, - {"properties": [("sculpt_tool", 'INFLATE')]}), - ("paint.brush_select", {"type": 'G', "value": 'PRESS'}, - {"properties": [("sculpt_tool", 'GRAB')]}), - ("paint.brush_select", {"type": 'T', "value": 'PRESS', "shift": True}, - {"properties": [("sculpt_tool", 'SCRAPE')]}), - ("paint.brush_select", {"type": 'C', "value": 'PRESS'}, - {"properties": [("sculpt_tool", 'CLAY_STRIPS')]}), - ("paint.brush_select", {"type": 'C', "value": 'PRESS', "shift": True}, - {"properties": [("sculpt_tool", 'CREASE')]}), - ("paint.brush_select", {"type": 'K', "value": 'PRESS'}, - {"properties": [("sculpt_tool", 'SNAKE_HOOK')]}), - ("paint.brush_select", {"type": 'M', "value": 'PRESS'}, - {"properties": [("sculpt_tool", 'MASK'), ("toggle", True), ("create_missing", True)]}), ]) # Lasso Masking. diff --git a/source/blender/editors/interface/interface_region_tooltip.cc b/source/blender/editors/interface/interface_region_tooltip.cc index 23f78aa1d59..e6c8b34ab4e 100644 --- a/source/blender/editors/interface/interface_region_tooltip.cc +++ b/source/blender/editors/interface/interface_region_tooltip.cc @@ -559,7 +559,6 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is /* There are different kinds of shortcuts: * * - Direct access to the tool (as if the toolbar button is pressed). - * - The key is bound to a brush type (not the exact brush name). * - The key is assigned to the operator itself * (bypassing the tool, executing the operator). * @@ -567,36 +566,6 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is */ std::string shortcut = UI_but_string_get_operator_keymap(*C, *but); - if (shortcut.empty()) { - const PaintMode paint_mode = BKE_paintmode_get_active_from_context(C); - const char *tool_attr = BKE_paint_get_tool_prop_id_from_paintmode(paint_mode); - if (tool_attr != nullptr) { - const EnumPropertyItem *items = BKE_paint_get_tool_enum_from_paintmode(paint_mode); - const char *tool_id_lstrip = strrchr(tool_id, '.'); - const int tool_id_offset = tool_id_lstrip ? ((tool_id_lstrip - tool_id) + 1) : 0; - const int i = RNA_enum_from_name(items, tool_id + tool_id_offset); - - if (i != -1) { - wmOperatorType *ot = WM_operatortype_find("paint.brush_select", true); - PointerRNA op_props; - WM_operator_properties_create_ptr(&op_props, ot); - RNA_enum_set(&op_props, tool_attr, items[i].value); - - /* Check for direct access to the tool. */ - if (std::optional shortcut_brush = WM_key_event_operator_string( - C, - ot->idname, - WM_OP_INVOKE_REGION_WIN, - static_cast(op_props.data), - true)) - { - shortcut = *shortcut_brush; - } - WM_operator_properties_free(&op_props); - } - } - } - if (shortcut.empty()) { /* Check for direct access to the tool. */ if (std::optional shortcut_toolbar = WM_key_event_operator_string( diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 80094da286f..8ead0d67d20 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -754,206 +754,6 @@ static void BRUSH_OT_reset(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -static int brush_tool(const Brush *brush, size_t tool_offset) -{ - return *(((char *)brush) + tool_offset); -} - -static void brush_tool_set(const Brush *brush, size_t tool_offset, int tool) -{ - *(((char *)brush) + tool_offset) = tool; -} - -static Brush *brush_tool_cycle(Main *bmain, Paint *paint, Brush *brush_orig, const int tool) -{ - Brush *brush, *first_brush; - - if (!brush_orig && !(brush_orig = static_cast(bmain->brushes.first))) { - return nullptr; - } - - if (brush_tool(brush_orig, paint->runtime.tool_offset) != tool) { - /* If current brush's tool is different from what we need, - * start cycling from the beginning of the list. - * Such logic will activate the same exact brush not relating from - * which tool user requests other tool. - */ - - /* Try to tool-slot first. */ - first_brush = BKE_paint_toolslots_brush_get(paint, tool); - if (first_brush == nullptr) { - first_brush = static_cast(bmain->brushes.first); - } - } - else { - /* If user wants to switch to brush with the same tool as - * currently active brush do a cycling via all possible - * brushes with requested tool. */ - first_brush = brush_orig->id.next ? static_cast(brush_orig->id.next) : - static_cast(bmain->brushes.first); - } - - /* get the next brush with the active tool */ - brush = first_brush; - do { - if ((brush->ob_mode & paint->runtime.ob_mode) && - (brush_tool(brush, paint->runtime.tool_offset) == tool)) - { - return brush; - } - - brush = brush->id.next ? static_cast(brush->id.next) : - static_cast(bmain->brushes.first); - } while (brush != first_brush); - - return nullptr; -} - -static Brush *brush_tool_toggle(Main *bmain, Paint *paint, Brush *brush_orig, const int tool) -{ - if (!brush_orig || brush_tool(brush_orig, paint->runtime.tool_offset) != tool) { - Brush *br; - /* if the current brush is not using the desired tool, look - * for one that is */ - br = brush_tool_cycle(bmain, paint, brush_orig, tool); - /* store the previously-selected brush */ - if (br) { - br->toggle_brush = brush_orig; - } - - return br; - } - if (brush_orig->toggle_brush) { - /* if current brush is using the desired tool, try to toggle - * back to the previously selected brush. */ - return brush_orig->toggle_brush; - } - return nullptr; -} - -static bool brush_generic_tool_set(bContext *C, - Main *bmain, - Paint *paint, - const int tool, - const char *tool_name, - const bool create_missing, - const bool toggle) -{ - Brush *brush, *brush_orig = BKE_paint_brush(paint); - - if (toggle) { - brush = brush_tool_toggle(bmain, paint, brush_orig, tool); - } - else { - brush = brush_tool_cycle(bmain, paint, brush_orig, tool); - } - - if (((brush == nullptr) && create_missing) && - ((brush_orig == nullptr) || brush_tool(brush_orig, paint->runtime.tool_offset) != tool)) - { - brush = BKE_brush_add(bmain, tool_name, eObjectMode(paint->runtime.ob_mode)); - id_us_min(&brush->id); /* fake user only */ - brush_tool_set(brush, paint->runtime.tool_offset, tool); - brush->toggle_brush = brush_orig; - } - - if (brush) { - BKE_paint_brush_set(paint, brush); - BKE_paint_invalidate_overlay_all(); - - WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush); - WM_toolsystem_ref_set_by_id(C, "builtin.brush"); - return true; - } - return false; -} - -static const PaintMode brush_select_paint_modes[] = { - PaintMode::Sculpt, - PaintMode::Vertex, - PaintMode::Weight, - PaintMode::Texture3D, - PaintMode::GPencil, - PaintMode::VertexGPencil, - PaintMode::SculptGPencil, - PaintMode::WeightGPencil, - PaintMode::SculptCurves, -}; - -static int brush_select_exec(bContext *C, wmOperator *op) -{ - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - const bool create_missing = RNA_boolean_get(op->ptr, "create_missing"); - const bool toggle = RNA_boolean_get(op->ptr, "toggle"); - const char *tool_name = "Brush"; - int tool = 0; - - PaintMode paint_mode = PaintMode::Invalid; - for (int i = 0; i < ARRAY_SIZE(brush_select_paint_modes); i++) { - paint_mode = brush_select_paint_modes[i]; - const char *op_prop_id = BKE_paint_get_tool_prop_id_from_paintmode(paint_mode); - PropertyRNA *prop = RNA_struct_find_property(op->ptr, op_prop_id); - if (RNA_property_is_set(op->ptr, prop)) { - tool = RNA_property_enum_get(op->ptr, prop); - break; - } - } - - if (paint_mode == PaintMode::Invalid) { - return OPERATOR_CANCELLED; - } - - Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode); - if (paint == nullptr) { - return OPERATOR_CANCELLED; - } - - // TODO: won't work with asset brushes, needs different main. - if (brush_generic_tool_set(C, bmain, paint, tool, tool_name, create_missing, toggle)) { - return OPERATOR_FINISHED; - } - return OPERATOR_CANCELLED; -} - -static void PAINT_OT_brush_select(wmOperatorType *ot) -{ - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Brush Select"; - ot->description = "Select a paint mode's brush by tool type"; - ot->idname = "PAINT_OT_brush_select"; - - /* api callbacks */ - ot->exec = brush_select_exec; - - /* flags */ - ot->flag = 0; - - /* props */ - /* All properties are hidden, so as not to show the redo panel. */ - for (int i = 0; i < ARRAY_SIZE(brush_select_paint_modes); i++) { - const PaintMode paint_mode = brush_select_paint_modes[i]; - const char *prop_id = BKE_paint_get_tool_prop_id_from_paintmode(paint_mode); - prop = RNA_def_enum( - ot->srna, prop_id, BKE_paint_get_tool_enum_from_paintmode(paint_mode), 0, prop_id, ""); - RNA_def_property_translation_context( - prop, BKE_paint_get_tool_enum_translation_context_from_paintmode(paint_mode)); - RNA_def_property_flag(prop, PROP_HIDDEN); - } - - prop = RNA_def_boolean( - ot->srna, "toggle", false, "Toggle", "Toggle between two brushes rather than cycling"); - RNA_def_property_flag(prop, PropertyFlag(PROP_HIDDEN | PROP_SKIP_SAVE)); - prop = RNA_def_boolean(ot->srna, - "create_missing", - false, - "Create Missing", - "If the requested brush type does not exist, create a new brush"); - RNA_def_property_flag(prop, PropertyFlag(PROP_HIDDEN | PROP_SKIP_SAVE)); -} - namespace blender::ed::sculpt_paint { /**************************** Brush Assets **********************************/ @@ -2078,9 +1878,6 @@ void ED_operatortypes_paint() WM_operatortype_append(BRUSH_OT_asset_update); WM_operatortype_append(BRUSH_OT_asset_revert); - /* NOTE: particle uses a different system, can be added with existing operators in `wm.py`. */ - WM_operatortype_append(PAINT_OT_brush_select); - /* image */ WM_operatortype_append(PAINT_OT_texture_paint_toggle); WM_operatortype_append(PAINT_OT_image_paint); -- 2.30.2 From 38c0fd5579c4fadd00afbb8abb49c209f42fdd3f Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 21 Feb 2024 15:10:06 -0500 Subject: [PATCH 14/14] Add comment --- source/blender/makesdna/DNA_scene_types.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 3bc2beac71e..df8846ef1dd 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -962,6 +962,11 @@ typedef struct PaintToolSlot { /** Paint Tool Base. */ typedef struct Paint { + /** + * The active brush. Possibly null. Possibly stored in a separate #Main data-base and not user- + * counted. + * //TODO: Or always stored in a separate #Main? + */ struct Brush *brush; /** -- 2.30.2