From 0c95e0e62734d54713001e6452acebaa159c305a Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 13 Jan 2023 14:32:12 +0100 Subject: [PATCH 001/244] Brush Assets: Add initial support for the new 'brush asset' usage. This commit mainly adds the low-level logic to link and create a runtime override for brushes. The biggest changes are in `setup_app_data` (post-loading of .blend file), to add support to move over some data from old bmain to newly loaded bmain. The logic is complex, and different depending on whether it is an undo (aka memfile read) step, or an actual .blend file reading: * On undo, we only port over brushes themselves, their dependencies are not handled. However, since memfile reading code already ported over linked IDs, these do not need to be swapped. * On real .blend file loading, brushes from the old bmain are being reused, as long as they do not conflict (name/lib) with brushes from the new bmain. Their dependencies are also re-used if needed. Only exception: local assets from old bmain are never re-used. There is also a new step added to the 'open file' process, which checks if the to-be-opened file is a library of the currently opened one, and if so, if there are local tweaked overrides of assets from the new file. The option to save these tweaks in draft is currently an empty mockup, but the rest is functional. Brush now has a new `AssetWeakReference` pointer to keep track of the last active brush asset, and restore it on fileread in case there is currently no active brush asset for the given paint/sculpt mode. Some parts of this commits are mockups/place-holders that are not expected to be committed as-is, if at all: * Some initial support for an asset-shelf (using code from `brush-asset-project` branch), very much WIP still. * Definition of test brush library with semi-hard-coded path. * There is also a drawing bug because the poll function of Brush panels return false initially (tool has no data-block ref). Very unclear what's hapening here. Ref. #101908. --- .../keyconfig/keymap_data/blender_default.py | 18 ++ scripts/startup/bl_ui/space_view3d.py | 13 + source/blender/blenkernel/BKE_brush.hh | 3 + source/blender/blenkernel/BKE_lib_override.hh | 11 + source/blender/blenkernel/BKE_paint.hh | 15 +- source/blender/blenkernel/intern/blendfile.cc | 191 ++++++++++++- source/blender/blenkernel/intern/brush.cc | 100 ++++--- .../blender/blenkernel/intern/lib_override.cc | 35 +++ source/blender/blenkernel/intern/paint.cc | 73 ++++- source/blender/blenkernel/intern/scene.cc | 3 + .../blenloader/intern/versioning_280.cc | 14 +- .../blenloader/intern/versioning_defaults.cc | 6 +- .../blender/editors/asset/ED_asset_handle.h | 4 + .../editors/asset/intern/asset_handle.cc | 13 + .../editors/gpencil_legacy/gpencil_edit.cc | 12 +- .../editors/gpencil_legacy/gpencil_utils.cc | 2 +- .../editors/sculpt_paint/CMakeLists.txt | 4 + .../editors/sculpt_paint/curves_sculpt_ops.cc | 59 +++- .../sculpt_paint/grease_pencil_draw_ops.cc | 2 +- .../editors/sculpt_paint/paint_intern.hh | 3 +- .../editors/sculpt_paint/paint_vertex.cc | 11 +- .../editors/sculpt_paint/sculpt_ops.cc | 2 +- source/blender/makesdna/DNA_ID.h | 5 + source/blender/makesdna/DNA_scene_types.h | 7 + source/blender/makesrna/intern/rna_brush.cc | 27 +- .../blender/windowmanager/intern/wm_files.cc | 261 +++++++++++++++++- .../windowmanager/intern/wm_toolsystem.cc | 2 +- 27 files changed, 821 insertions(+), 75 deletions(-) diff --git a/scripts/presets/keyconfig/keymap_data/blender_default.py b/scripts/presets/keyconfig/keymap_data/blender_default.py index 171b098d921..270cc869dfa 100644 --- a/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -6675,6 +6675,21 @@ def km_node_link_modal_map(_params): return keymap +def km_asset_shelf_brushes(_params): + items = [] + keymap = ( + "Asset Shelf", + {"space_type": 'EMPTY', "region_type": 'WINDOW'}, + {"items": items}, + ) + + items.extend([ + ("sculpt_curves.brush_asset_select", {"type": 'LEFTMOUSE', "value": 'CLICK'}, None), + ]) + + return keymap + + # Fallback for gizmos that don't have custom a custom key-map. def km_generic_gizmo(_params): keymap = ( @@ -8470,6 +8485,9 @@ def generate_keymaps(params=None): km_curve_pen_modal_map(params), km_node_link_modal_map(params), + # Asset Shelf Keymaps. + km_asset_shelf_brushes(params), + # Gizmos. km_generic_gizmo(params), km_generic_gizmo_drag(params), diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index 12f0c616b94..789a691f252 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -987,6 +987,18 @@ class VIEW3D_HT_header(Header): sub.popover(panel="VIEW3D_PT_shading", text="") +class VIEW3D_AST_brush_asset_shelf(bpy.types.AssetShelf): + bl_space_type = "VIEW_3D" + + @classmethod + def poll(cls, context): + return context.object and context.object.mode == 'SCULPT_CURVES' + + @classmethod + def asset_poll(cls, asset): + return asset.file_data.id_type == 'BRUSH' + + class VIEW3D_MT_editor_menus(Menu): bl_label = "" @@ -8389,6 +8401,7 @@ class VIEW3D_AST_sculpt_brushes(bpy.types.AssetShelf): classes = ( + VIEW3D_AST_brush_asset_shelf, VIEW3D_HT_header, VIEW3D_HT_tool_header, VIEW3D_MT_editor_menus, diff --git a/source/blender/blenkernel/BKE_brush.hh b/source/blender/blenkernel/BKE_brush.hh index 2a89dbef5f5..3809a225b43 100644 --- a/source/blender/blenkernel/BKE_brush.hh +++ b/source/blender/blenkernel/BKE_brush.hh @@ -31,6 +31,9 @@ struct UnifiedPaintSettings; void BKE_brush_system_init(); void BKE_brush_system_exit(); +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 7b433472e54..87c3e619bb2 100644 --- a/source/blender/blenkernel/BKE_lib_override.hh +++ b/source/blender/blenkernel/BKE_lib_override.hh @@ -82,6 +82,17 @@ 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 amount 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(struct Main *bmain, + const short id_type, + struct Library *library, + struct 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/BKE_paint.hh b/source/blender/blenkernel/BKE_paint.hh index 228165afee2..3299445ac7e 100644 --- a/source/blender/blenkernel/BKE_paint.hh +++ b/source/blender/blenkernel/BKE_paint.hh @@ -25,6 +25,7 @@ #include "bmesh.h" +struct AssetWeakReference; struct BMFace; struct BMesh; struct BlendDataReader; @@ -164,7 +165,7 @@ PaintCurve *BKE_paint_curve_add(Main *bmain, const char *name); /** * Call when entering each respective paint mode. */ -bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint); +bool BKE_paint_ensure(Main *bmain, ToolSettings *ts, Paint **r_paint); void BKE_paint_init(Main *bmain, Scene *sce, ePaintMode mode, const uchar col[3]); void BKE_paint_free(Paint *p); /** @@ -179,7 +180,7 @@ void BKE_paint_runtime_init(const ToolSettings *ts, Paint *paint); void BKE_paint_cavity_curve_preset(Paint *p, int preset); eObjectMode BKE_paint_object_mode_from_paintmode(ePaintMode mode); -bool BKE_paint_ensure_from_paintmode(Scene *sce, ePaintMode mode); +bool BKE_paint_ensure_from_paintmode(Main *bmain, Scene *sce, ePaintMode mode); Paint *BKE_paint_get_active_from_paintmode(Scene *sce, ePaintMode mode); const EnumPropertyItem *BKE_paint_get_tool_enum_from_paintmode(ePaintMode mode); const char *BKE_paint_get_tool_enum_translation_context_from_paintmode(ePaintMode mode); @@ -192,6 +193,14 @@ ePaintMode BKE_paintmode_get_from_tool(const bToolRef *tref); Brush *BKE_paint_brush(Paint *paint); const Brush *BKE_paint_brush_for_read(const Paint *p); void BKE_paint_brush_set(Paint *paint, Brush *br); + +/** Set the active brush of given paint struct, and store the weak asset reference to it. + * NOTE: Takes ownership of the given `weak_asset_reference`. */ +void BKE_paint_brush_asset_set(Paint *p, Brush *br, AssetWeakReference *weak_asset_reference); + +/** Attempt to restore a valid active brush in `p` from brush asset informations stored in `p`. */ +void BKE_paint_brush_asset_restore(Main *bmain, Paint *p); + Palette *BKE_paint_palette(Paint *paint); void BKE_paint_palette_set(Paint *p, Palette *palette); void BKE_paint_curve_set(Brush *br, PaintCurve *pc); @@ -866,7 +875,7 @@ int BKE_sculpt_mask_layers_ensure(Depsgraph *depsgraph, Main *bmain, Object *ob, MultiresModifierData *mmd); -void BKE_sculpt_toolsettings_data_ensure(Scene *scene); +void BKE_sculpt_toolsettings_data_ensure(Main *bmain, Scene *scene); PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob); diff --git a/source/blender/blenkernel/intern/blendfile.cc b/source/blender/blenkernel/intern/blendfile.cc index 11ca3cda8a7..9429f2e8d4b 100644 --- a/source/blender/blenkernel/intern/blendfile.cc +++ b/source/blender/blenkernel/intern/blendfile.cc @@ -306,14 +306,195 @@ 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 BMain into new BMain, provided it does not conflict with data + * already present in the new BMain (name-wise and library-wise). + * + * Dependencies from moved over old data are also imported into the new BMain, (unless, in case of + * linked data, a matching linked ID is already available in new BMain). + * + * 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 BMain later. + * + * NOTE: This function will never remove any original new data from the new BMain, it only moves + * (some of) the old data to the new BMain. + */ +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_uuid_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. * * WARNING: Currently only expects to work on local data, won't work properly if some of the IDs of * given type are linked. * - * 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). */ + * 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). */ static void swap_old_bmain_data_for_blendfile(ReuseOldBMainData *reuse_data, const short id_code) { Main *new_bmain = reuse_data->new_bmain; @@ -720,6 +901,12 @@ 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 6c926a71936..8e27d531641 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -14,13 +14,17 @@ #include "DNA_material_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_space_types.h" #include "BLI_listbase.h" #include "BLI_math_rotation.h" #include "BLI_rand.h" +#include "BLO_readfile.h" + #include "BLT_translation.h" +#include "BKE_blendfile_link_append.h" #include "BKE_bpath.h" #include "BKE_brush.hh" #include "BKE_colortools.h" @@ -36,6 +40,8 @@ #include "BKE_preview_image.hh" #include "BKE_texture.h" +#include "AS_asset_library.h" + #include "IMB_colormanagement.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" @@ -376,39 +382,6 @@ static void brush_blend_read_after_liblink(BlendLibReader * /*reader*/, ID *id) } } -static int brush_undo_preserve_cb(LibraryIDLinkCallbackData *cb_data) -{ - BlendLibReader *reader = (BlendLibReader *)cb_data->user_data; - ID *self_id = cb_data->self_id; - ID *id_old = *cb_data->id_pointer; - /* Old data has not been remapped to new values of the pointers, if we want to keep the old - * pointer here we need its new address. */ - ID *id_old_new = id_old != nullptr ? BLO_read_get_new_id_address( - reader, self_id, ID_IS_LINKED(self_id), id_old) : - nullptr; - BLI_assert(id_old_new == nullptr || ELEM(id_old, id_old_new, id_old_new->orig_id)); - if (cb_data->cb_flag & IDWALK_CB_USER) { - id_us_plus_no_lib(id_old_new); - id_us_min(id_old); - } - *cb_data->id_pointer = id_old_new; - return IDWALK_RET_NOP; -} - -static void brush_undo_preserve(BlendLibReader *reader, ID *id_new, ID *id_old) -{ - /* Whole Brush is preserved across undo-steps. */ - BKE_lib_id_swap(nullptr, id_new, id_old, false, 0); - - /* `id_new` now has content from `id_old`, we need to ensure those old ID pointers are valid. - * NOTE: Since we want to re-use all old pointers here, code is much simpler than for Scene. */ - BKE_library_foreach_ID_link(nullptr, id_new, brush_undo_preserve_cb, reader, IDWALK_NOP); - - /* NOTE: We do not swap IDProperties, as dealing with potential ID pointers in those would be - * fairly delicate. */ - std::swap(id_new->properties, id_old->properties); -} - IDTypeInfo IDType_ID_BR = { /*id_code*/ ID_BR, /*id_filter*/ FILTER_ID_BR, @@ -417,7 +390,7 @@ IDTypeInfo IDType_ID_BR = { /*name*/ "Brush", /*name_plural*/ "brushes", /*translation_context*/ BLT_I18NCONTEXT_ID_BRUSH, - /*flags*/ IDTYPE_FLAGS_NO_ANIMDATA, + /*flags*/ IDTYPE_FLAGS_NO_ANIMDATA | IDTYPE_FLAGS_NO_MEMFILE_UNDO, /*asset_type_info*/ nullptr, /*init_data*/ brush_init_data, @@ -433,7 +406,7 @@ IDTypeInfo IDType_ID_BR = { /*blend_read_data*/ brush_blend_read_data, /*blend_read_after_liblink*/ brush_blend_read_after_liblink, - /*blend_read_undo_preserve*/ brush_undo_preserve, + /*blend_read_undo_preserve*/ nullptr, /*lib_override_apply_post*/ nullptr, }; @@ -510,6 +483,63 @@ 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 = {nullptr}; + 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, + static_cast(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 f552cc1c7e3..6791e8ec721 100644 --- a/source/blender/blenkernel/intern/lib_override.cc +++ b/source/blender/blenkernel/intern/lib_override.cc @@ -362,6 +362,41 @@ 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 short 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)) { + /* 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 6f3235abe14..4b7ba9a900a 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -11,6 +11,7 @@ #include "MEM_guardedalloc.h" +#include "DNA_asset_types.h" #include "DNA_brush_types.h" #include "DNA_defaults.h" #include "DNA_gpencil_legacy_types.h" @@ -35,6 +36,7 @@ #include "BLT_translation.h" +#include "BKE_asset.h" #include "BKE_attribute.h" #include "BKE_attribute.hh" #include "BKE_brush.hh" @@ -309,7 +311,7 @@ void BKE_paint_reset_overlay_invalid(ePaintOverlayControlFlags flag) overlay_flags &= ~(flag); } -bool BKE_paint_ensure_from_paintmode(Scene *sce, ePaintMode mode) +bool BKE_paint_ensure_from_paintmode(Main *bmain, Scene *sce, ePaintMode mode) { ToolSettings *ts = sce->toolsettings; Paint **paint_ptr = nullptr; @@ -354,7 +356,7 @@ bool BKE_paint_ensure_from_paintmode(Scene *sce, ePaintMode mode) break; } if (paint_ptr) { - BKE_paint_ensure(ts, paint_ptr); + BKE_paint_ensure(bmain, ts, paint_ptr); return true; } return false; @@ -668,6 +670,50 @@ void BKE_paint_brush_set(Paint *p, Brush *br) } } +static void paint_brush_asset_update(Paint *p, + Brush *br, + AssetWeakReference *brush_asset_reference) +{ + if (p->brush_asset_reference != nullptr) { + BKE_asset_weak_reference_free(&p->brush_asset_reference); + } + + if (br == nullptr || br != p->brush || !ID_IS_OVERRIDE_LIBRARY_REAL(p->brush) || + !(ID_IS_ASSET(p->brush) || ID_IS_ASSET(p->brush->id.override_library->reference))) + { + BKE_asset_weak_reference_free(&brush_asset_reference); + return; + } + + p->brush_asset_reference = brush_asset_reference; +} + +void BKE_paint_brush_asset_set(Paint *p, Brush *br, AssetWeakReference *weak_asset_reference) +{ + BKE_paint_brush_set(p, br); + paint_brush_asset_update(p, br, weak_asset_reference); +} + +void BKE_paint_brush_asset_restore(Main *bmain, Paint *p) +{ + if (p->brush != nullptr) { + return; + } + + if (p->brush_asset_reference == nullptr) { + return; + } + + AssetWeakReference *brush_asset_reference = p->brush_asset_reference; + p->brush_asset_reference = nullptr; + + Brush *brush_asset = BKE_brush_asset_runtime_ensure(bmain, brush_asset_reference); + + /* Will either re-assign the brush_asset_reference to `p`, or free it if loading a brush ID from + * it failed. */ + BKE_paint_brush_asset_set(p, brush_asset, brush_asset_reference); +} + void BKE_paint_runtime_init(const ToolSettings *ts, Paint *paint) { if (paint == &ts->imapaint.paint) { @@ -1091,7 +1137,7 @@ eObjectMode BKE_paint_object_mode_from_paintmode(ePaintMode mode) } } -bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint) +bool BKE_paint_ensure(Main *bmain, ToolSettings *ts, Paint **r_paint) { Paint *paint = nullptr; if (*r_paint) { @@ -1125,6 +1171,7 @@ bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint) BLI_assert(paint_test.runtime.tool_offset == (*r_paint)->runtime.tool_offset); #endif } + BKE_paint_brush_asset_restore(bmain, *r_paint); return true; } @@ -1172,6 +1219,7 @@ bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint) *r_paint = paint; BKE_paint_runtime_init(ts, paint); + BKE_paint_brush_asset_restore(bmain, paint); return false; } @@ -1181,7 +1229,7 @@ void BKE_paint_init(Main *bmain, Scene *sce, ePaintMode mode, const uchar col[3] UnifiedPaintSettings *ups = &sce->toolsettings->unified_paint_settings; Paint *paint = BKE_paint_get_active_from_paintmode(sce, mode); - BKE_paint_ensure_from_paintmode(sce, mode); + BKE_paint_ensure_from_paintmode(bmain, sce, mode); /* If there's no brush, create one */ if (PAINT_MODE_HAS_BRUSH(mode)) { @@ -1211,6 +1259,9 @@ void BKE_paint_free(Paint *paint) { BKE_curvemapping_free(paint->cavity_curve); MEM_SAFE_FREE(paint->tool_slots); + if (paint->brush_asset_reference != nullptr) { + BKE_asset_weak_reference_free(&paint->brush_asset_reference); + } } void BKE_paint_copy(Paint *src, Paint *tar, const int flag) @@ -1219,6 +1270,8 @@ void BKE_paint_copy(Paint *src, Paint *tar, const int flag) tar->cavity_curve = BKE_curvemapping_copy(src->cavity_curve); tar->tool_slots = static_cast(MEM_dupallocN(src->tool_slots)); + 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); @@ -1247,6 +1300,9 @@ void BKE_paint_blend_write(BlendWriter *writer, Paint *p) if (p->cavity_curve) { BKE_curvemapping_blend_write(writer, p->cavity_curve); } + if (p->brush_asset_reference) { + BKE_asset_weak_reference_write(writer, p->brush_asset_reference); + } BLO_write_struct_array(writer, PaintToolSlot, p->tool_slots_len, p->tool_slots); } @@ -1264,6 +1320,11 @@ void BKE_paint_blend_read_data(BlendDataReader *reader, const Scene *scene, Pain BKE_paint_cavity_curve_preset(p, CURVE_PRESET_LINE); } + BLO_read_data_address(reader, &p->brush_asset_reference); + if (p->brush_asset_reference) { + BKE_asset_weak_reference_read(reader, p->brush_asset_reference); + } + BLO_read_data_address(reader, &p->tool_slots); /* Workaround for invalid data written in older versions. */ @@ -2062,9 +2123,9 @@ int BKE_sculpt_mask_layers_ensure(Depsgraph *depsgraph, return ret; } -void BKE_sculpt_toolsettings_data_ensure(Scene *scene) +void BKE_sculpt_toolsettings_data_ensure(Main *bmain, Scene *scene) { - BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->sculpt); + BKE_paint_ensure(bmain, scene->toolsettings, (Paint **)&scene->toolsettings->sculpt); Sculpt *sd = scene->toolsettings->sculpt; diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc index 036ca8a6c55..8757b4fac8f 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -1017,6 +1017,9 @@ static void scene_foreach_path(ID *id, BPathForeachPathData *bpath_data) if (scene->ed != nullptr) { SEQ_for_each_callback(&scene->ed->seqbase, seq_foreach_path_callback, bpath_data); } + + /* TODO: Also handle `asset_weak_reference` here? Probably not, conceptually this is not a file + * path. */ } static void scene_blend_write(BlendWriter *writer, ID *id, const void *id_address) diff --git a/source/blender/blenloader/intern/versioning_280.cc b/source/blender/blenloader/intern/versioning_280.cc index 7ff1d692848..a66ad7513e8 100644 --- a/source/blender/blenloader/intern/versioning_280.cc +++ b/source/blender/blenloader/intern/versioning_280.cc @@ -2844,10 +2844,10 @@ void do_versions_after_linking_280(FileData *fd, Main *bmain) ToolSettings *ts = scene->toolsettings; /* Ensure new Paint modes. */ - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_VERTEX_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_SCULPT_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_WEIGHT_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_VERTEX_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_SCULPT_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_WEIGHT_GPENCIL); /* Set default Draw brush. */ if (brush != nullptr) { @@ -2888,9 +2888,9 @@ void do_versions_after_linking_280(FileData *fd, Main *bmain) /* Reset all grease pencil brushes. */ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { /* Ensure new Paint modes. */ - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_VERTEX_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_SCULPT_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_WEIGHT_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_VERTEX_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_SCULPT_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_WEIGHT_GPENCIL); } } diff --git a/source/blender/blenloader/intern/versioning_defaults.cc b/source/blender/blenloader/intern/versioning_defaults.cc index 8bf2fbe14e1..3b6392f95c7 100644 --- a/source/blender/blenloader/intern/versioning_defaults.cc +++ b/source/blender/blenloader/intern/versioning_defaults.cc @@ -481,9 +481,9 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) BKE_brush_gpencil_weight_presets(bmain, scene->toolsettings, true); /* Ensure new Paint modes. */ - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_VERTEX_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_SCULPT_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_WEIGHT_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_VERTEX_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_SCULPT_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_WEIGHT_GPENCIL); /* Enable cursor. */ GpPaint *gp_paint = scene->toolsettings->gp_paint; diff --git a/source/blender/editors/asset/ED_asset_handle.h b/source/blender/editors/asset/ED_asset_handle.h index 2506dc15ab6..69df2285a82 100644 --- a/source/blender/editors/asset/ED_asset_handle.h +++ b/source/blender/editors/asset/ED_asset_handle.h @@ -43,6 +43,10 @@ void ED_asset_handle_get_full_library_path( /* `1024` for #FILE_MAX, * rely on warnings to let us know if this gets out of sync. */ char r_full_lib_path[1024]); +void ED_asset_handle_get_full_path(const AssetHandle *asset_handle, + /* `1024 + 66` for #FILE_MAX_LIBEXTRA, + * rely on warnings to let us know if this gets out of sync. */ + char r_full_lib_path[1024 + 66]); #ifdef __cplusplus } diff --git a/source/blender/editors/asset/intern/asset_handle.cc b/source/blender/editors/asset/intern/asset_handle.cc index 684ef1a0b52..b072b815247 100644 --- a/source/blender/editors/asset/intern/asset_handle.cc +++ b/source/blender/editors/asset/intern/asset_handle.cc @@ -57,3 +57,16 @@ void ED_asset_handle_get_full_library_path(const AssetHandle *asset_handle, BLI_strncpy(r_full_lib_path, library_path.c_str(), FILE_MAX); } + +void ED_asset_handle_get_full_path(const AssetHandle *asset_handle, + char r_full_lib_path[FILE_MAX_LIBEXTRA]) +{ + *r_full_lib_path = '\0'; + + std::string library_path = asset_handle->file_data->asset->get_identifier().full_path(); + if (library_path.empty()) { + return; + } + + BLI_strncpy(r_full_lib_path, library_path.c_str(), FILE_MAX); +} diff --git a/source/blender/editors/gpencil_legacy/gpencil_edit.cc b/source/blender/editors/gpencil_legacy/gpencil_edit.cc index 6c16eadfe8d..40930432313 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_edit.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_edit.cc @@ -395,8 +395,8 @@ static int gpencil_paintmode_toggle_exec(bContext *C, wmOperator *op) if (mode == OB_MODE_PAINT_GPENCIL_LEGACY) { /* Be sure we have brushes and Paint settings. * Need Draw and Vertex (used for Tint). */ - BKE_paint_ensure(ts, (Paint **)&ts->gp_paint); - BKE_paint_ensure(ts, (Paint **)&ts->gp_vertexpaint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_paint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_vertexpaint); BKE_brush_gpencil_paint_presets(bmain, ts, false); @@ -510,7 +510,7 @@ static int gpencil_sculptmode_toggle_exec(bContext *C, wmOperator *op) if (mode == OB_MODE_SCULPT_GPENCIL_LEGACY) { /* Be sure we have brushes. */ - BKE_paint_ensure(ts, (Paint **)&ts->gp_sculptpaint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_sculptpaint); const bool reset_mode = (ts->gp_sculptpaint->paint.brush == nullptr); BKE_brush_gpencil_sculpt_presets(bmain, ts, reset_mode); @@ -624,7 +624,7 @@ static int gpencil_weightmode_toggle_exec(bContext *C, wmOperator *op) if (mode == OB_MODE_WEIGHT_GPENCIL_LEGACY) { /* Be sure we have brushes. */ - BKE_paint_ensure(ts, (Paint **)&ts->gp_weightpaint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_weightpaint); const bool reset_mode = (ts->gp_weightpaint->paint.brush == nullptr); BKE_brush_gpencil_weight_presets(bmain, ts, reset_mode); @@ -731,8 +731,8 @@ static int gpencil_vertexmode_toggle_exec(bContext *C, wmOperator *op) if (mode == OB_MODE_VERTEX_GPENCIL_LEGACY) { /* Be sure we have brushes. * Need Draw as well (used for Palettes). */ - BKE_paint_ensure(ts, (Paint **)&ts->gp_paint); - BKE_paint_ensure(ts, (Paint **)&ts->gp_vertexpaint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_paint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_vertexpaint); const bool reset_mode = (ts->gp_vertexpaint->paint.brush == nullptr); BKE_brush_gpencil_vertex_presets(bmain, ts, reset_mode); diff --git a/source/blender/editors/gpencil_legacy/gpencil_utils.cc b/source/blender/editors/gpencil_legacy/gpencil_utils.cc index cd99d778a38..0aa1e454aac 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_utils.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_utils.cc @@ -1428,7 +1428,7 @@ void ED_gpencil_add_defaults(bContext *C, Object *ob) Main *bmain = CTX_data_main(C); ToolSettings *ts = CTX_data_tool_settings(C); - BKE_paint_ensure(ts, (Paint **)&ts->gp_paint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_paint); Paint *paint = &ts->gp_paint->paint; /* if not exist, create a new one */ if ((paint->brush == nullptr) || (paint->brush->gpencil_settings == nullptr)) { diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index 24cf65cdd91..9104a1b6dee 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -4,8 +4,12 @@ set(INC ../include + ../asset ../uvedit + ../../asset_system ../../blenkernel + ../../blenlib + ../../blenloader ../../blentranslation ../../bmesh ../../depsgraph diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc index 924b1782d12..be3cf1856d5 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc @@ -8,6 +8,8 @@ #include "BLI_utildefines.h" #include "BLI_vector_set.hh" +#include "BKE_asset.h" +#include "BKE_blendfile.h" #include "BKE_brush.hh" #include "BKE_bvhutils.h" #include "BKE_context.h" @@ -20,6 +22,7 @@ #include "WM_message.hh" #include "WM_toolsystem.h" +#include "ED_asset_handle.h" #include "ED_curves.hh" #include "ED_curves_sculpt.hh" #include "ED_image.hh" @@ -28,6 +31,8 @@ #include "ED_space_api.hh" #include "ED_view3d.hh" +#include "AS_asset_representation.hh" + #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" @@ -283,7 +288,8 @@ static void curves_sculptmode_enter(bContext *C) wmMsgBus *mbus = CTX_wm_message_bus(C); Object *ob = CTX_data_active_object(C); - BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->curves_sculpt); + BKE_paint_ensure( + CTX_data_main(C), scene->toolsettings, (Paint **)&scene->toolsettings->curves_sculpt); CurvesSculpt *curves_sculpt = scene->toolsettings->curves_sculpt; ob->mode = OB_MODE_SCULPT_CURVES; @@ -1167,6 +1173,55 @@ static void SCULPT_CURVES_OT_min_distance_edit(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_DEPENDS_ON_CURSOR; } +/* -------------------------------------------------------------------- */ + +static int brush_asset_select_exec(bContext *C, wmOperator * /*op*/) +{ + char *brush_name = nullptr; + char asset_libpath[FILE_MAX]; + char asset_libpath_full[FILE_MAX_LIBEXTRA]; + bool asset_handle_valid; + + const AssetHandle asset_handle = CTX_wm_asset_handle(C, &asset_handle_valid); + if (!asset_handle_valid) { + return OPERATOR_CANCELLED; + } + AssetRepresentationHandle *asset = ED_asset_handle_get_representation(&asset_handle); + AssetWeakReference *brush_asset_reference = asset->make_weak_reference(); + + ED_asset_handle_get_full_path(&asset_handle, asset_libpath_full); + BKE_blendfile_library_path_explode(asset_libpath_full, asset_libpath, nullptr, &brush_name); + BLI_assert(brush_name != nullptr); + + Brush *brush = BKE_brush_asset_runtime_ensure(CTX_data_main(C), brush_asset_reference); + + ToolSettings *tool_settings = CTX_data_tool_settings(C); + /* Either takes ownership of the brush_asset_reference, or frees it. */ + BKE_paint_brush_asset_set(&tool_settings->curves_sculpt->paint, brush, brush_asset_reference); + + return OPERATOR_FINISHED; +} + +/** + * 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. + */ +static void SCULPT_CURVES_OT_brush_asset_select(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Brush Asset"; + ot->description = "Select a brush asset as currently sculpt/paint tool - TESTING PURPOSE ONLY"; + ot->idname = "SCULPT_CURVES_OT_brush_asset_select"; + + /* api callbacks */ + ot->exec = brush_asset_select_exec; + ot->poll = CURVES_SCULPT_mode_poll; + + ot->prop = RNA_def_string( + ot->srna, "name", nullptr, MAX_NAME, "Brush Name", "name of the brush asset to select"); +} + } // namespace blender::ed::sculpt_paint /* -------------------------------------------------------------------- */ @@ -1181,6 +1236,8 @@ void ED_operatortypes_sculpt_curves() WM_operatortype_append(SCULPT_CURVES_OT_select_random); WM_operatortype_append(SCULPT_CURVES_OT_select_grow); WM_operatortype_append(SCULPT_CURVES_OT_min_distance_edit); + + WM_operatortype_append(SCULPT_CURVES_OT_brush_asset_select); } /** \} */ diff --git a/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc b/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc index 6a1e1d8810c..50be47a3829 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc @@ -192,7 +192,7 @@ static void grease_pencil_draw_mode_enter(bContext *C) Object *ob = CTX_data_active_object(C); GpPaint *grease_pencil_paint = scene->toolsettings->gp_paint; - BKE_paint_ensure(scene->toolsettings, (Paint **)&grease_pencil_paint); + BKE_paint_ensure(CTX_data_main(C), scene->toolsettings, (Paint **)&grease_pencil_paint); ob->mode = OB_MODE_PAINT_GREASE_PENCIL; diff --git a/source/blender/editors/sculpt_paint/paint_intern.hh b/source/blender/editors/sculpt_paint/paint_intern.hh index 619633536a1..0685dcd5e0b 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.hh +++ b/source/blender/editors/sculpt_paint/paint_intern.hh @@ -553,7 +553,8 @@ void get_brush_alpha_data(const Scene *scene, void init_stroke(Depsgraph *depsgraph, Object *ob); void init_session_data(const ToolSettings *ts, Object *ob); -void init_session(Depsgraph *depsgraph, Scene *scene, Object *ob, eObjectMode object_mode); +void init_session( + Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob, eObjectMode object_mode); Vector pbvh_gather_generic(Object *ob, VPaint *wp, Sculpt *sd, Brush *brush); diff --git a/source/blender/editors/sculpt_paint/paint_vertex.cc b/source/blender/editors/sculpt_paint/paint_vertex.cc index 4c23947e1b0..3fb57943e22 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.cc +++ b/source/blender/editors/sculpt_paint/paint_vertex.cc @@ -209,10 +209,11 @@ void init_stroke(Depsgraph *depsgraph, Object *ob) } /* Toggle operator for turning vertex paint mode on or off (copied from sculpt.cc) */ -void init_session(Depsgraph *depsgraph, Scene *scene, Object *ob, eObjectMode object_mode) +void init_session( + Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob, eObjectMode object_mode) { /* Create persistent sculpt mode data */ - BKE_sculpt_toolsettings_data_ensure(scene); + BKE_sculpt_toolsettings_data_ensure(bmain, scene); BLI_assert(ob->sculpt == nullptr); ob->sculpt = MEM_new(__func__); @@ -336,7 +337,7 @@ void mode_enter_generic( const ePaintMode paint_mode = PAINT_MODE_VERTEX; ED_mesh_color_ensure(me, nullptr); - BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->vpaint); + BKE_paint_ensure(bmain, scene->toolsettings, (Paint **)&scene->toolsettings->vpaint); Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode); ED_paint_cursor_start(paint, vertex_paint_poll); BKE_paint_init(bmain, scene, paint_mode, PAINT_CURSOR_VERTEX_PAINT); @@ -344,7 +345,7 @@ void mode_enter_generic( else if (mode_flag == OB_MODE_WEIGHT_PAINT) { const ePaintMode paint_mode = PAINT_MODE_WEIGHT; - BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->wpaint); + BKE_paint_ensure(bmain, scene->toolsettings, (Paint **)&scene->toolsettings->wpaint); Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode); ED_paint_cursor_start(paint, weight_paint_poll); BKE_paint_init(bmain, scene, paint_mode, PAINT_CURSOR_WEIGHT_PAINT); @@ -366,7 +367,7 @@ void mode_enter_generic( BKE_sculptsession_free(ob); } - vwpaint::init_session(depsgraph, scene, ob, mode_flag); + vwpaint::init_session(bmain, depsgraph, scene, ob, mode_flag); /* Flush object mode. */ DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.cc b/source/blender/editors/sculpt_paint/sculpt_ops.cc index 11a84841d7b..d1f230fc76f 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/sculpt_ops.cc @@ -267,7 +267,7 @@ static void SCULPT_OT_symmetrize(wmOperatorType *ot) static void sculpt_init_session(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob) { /* Create persistent sculpt mode data. */ - BKE_sculpt_toolsettings_data_ensure(scene); + BKE_sculpt_toolsettings_data_ensure(bmain, scene); /* Create sculpt mode session data. */ if (ob->sculpt != nullptr) { diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index ea6bb898998..f61639c22c0 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -543,6 +543,11 @@ typedef struct Library { */ char filepath_abs[1024]; + /** + * Weak reference to the Brush Asset. + */ + struct AssetWeakReference *asset_repository_weak_reference; + /** Set for indirectly linked libraries, used in the outliner and while reading. */ struct Library *parent; diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 3053a2c92ba..c963945b0a4 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -948,6 +948,13 @@ typedef struct PaintToolSlot { typedef struct Paint { struct Brush *brush; + /** + * A weak asset reference to the #brush, if not NULL. + * Used to attempt restoring the active brush from the AssetLibrary system, typically on + * file load. + */ + struct AssetWeakReference *brush_asset_reference; + /** * Each tool has its own active brush, * The currently active tool is defined by the current 'brush'. diff --git a/source/blender/makesrna/intern/rna_brush.cc b/source/blender/makesrna/intern/rna_brush.cc index b4787a0606c..825efa0332b 100644 --- a/source/blender/makesrna/intern/rna_brush.cc +++ b/source/blender/makesrna/intern/rna_brush.cc @@ -770,7 +770,12 @@ static void rna_Brush_reset_icon(Brush *br) static void rna_Brush_update(Main * /*bmain*/, Scene * /*scene*/, PointerRNA *ptr) { - Brush *br = (Brush *)ptr->data; + Brush *br = (Brush *)ptr->owner_id; + + if (ID_IS_OVERRIDE_LIBRARY_REAL(&br->id)) { + br->id.tag |= LIB_TAG_LIBOVERRIDE_AUTOREFRESH; + } + WM_main_add_notifier(NC_BRUSH | NA_EDITED, br); // WM_main_add_notifier(NC_SPACE | ND_SPACE_VIEW3D, nullptr); } @@ -2146,14 +2151,18 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) RNA_def_struct_sdna(srna, "BrushCurvesSculptSettings"); RNA_def_struct_ui_text(srna, "Curves Sculpt Brush Settings", ""); + RNA_define_lib_overridable(true); + prop = RNA_def_property(srna, "add_amount", PROP_INT, PROP_NONE); RNA_def_property_range(prop, 1, INT32_MAX); RNA_def_property_ui_text(prop, "Count", "Number of curves added by the Add brush"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "points_per_curve", PROP_INT, PROP_NONE); RNA_def_property_range(prop, 2, INT32_MAX); RNA_def_property_ui_text( prop, "Points per Curve", "Number of control points in a newly added curve"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "scale_uniform", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, nullptr, "flag", BRUSH_CURVES_SCULPT_FLAG_SCALE_UNIFORM); @@ -2161,17 +2170,20 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) "Scale Uniform", "Grow or shrink curves by changing their size uniformly instead of " "using trimming or extrapolation"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "minimum_length", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_range(prop, 0.0f, FLT_MAX); RNA_def_property_ui_text( prop, "Minimum Length", "Avoid shrinking curves shorter than this length"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "interpolate_length", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna( prop, nullptr, "flag", BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH); RNA_def_property_ui_text( prop, "Interpolate Length", "Use length of the curves in close proximity"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "interpolate_point_count", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna( @@ -2179,11 +2191,13 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Interpolate Point Count", "Use the number of points from the curves in close proximity"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "interpolate_shape", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, nullptr, "flag", BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE); RNA_def_property_ui_text( prop, "Interpolate Shape", "Use shape of the curves in close proximity"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "curve_length", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_range(prop, 0.0, FLT_MAX); @@ -2191,28 +2205,35 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) prop, "Curve Length", "Length of newly added curves when it is not interpolated from other curves"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "minimum_distance", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_range(prop, 0.0f, FLT_MAX); RNA_def_property_ui_range(prop, 0.0, 1000.0f, 0.001, 2); RNA_def_property_ui_text( prop, "Minimum Distance", "Goal distance between curve roots for the Density brush"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "density_add_attempts", PROP_INT, PROP_NONE); RNA_def_property_range(prop, 0, INT32_MAX); RNA_def_property_ui_text( prop, "Density Add Attempts", "How many times the Density brush tries to add a new curve"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "density_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, density_mode_items); RNA_def_property_ui_text( prop, "Density Mode", "Determines whether the brush adds or removes curves"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "curve_parameter_falloff", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "CurveMapping"); RNA_def_property_ui_text(prop, "Curve Parameter Falloff", "Falloff that is applied from the tip to the root of each curve"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + RNA_define_lib_overridable(false); } static void rna_def_brush(BlenderRNA *brna) @@ -2560,6 +2581,8 @@ static void rna_def_brush(BlenderRNA *brna) srna, "Brush", "Brush data-block for storing brush settings for painting and sculpting"); RNA_def_struct_ui_icon(srna, ICON_BRUSH_DATA); + RNA_define_lib_overridable(true); + /* enums */ prop = RNA_def_property(srna, "blend", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, prop_blend_items); @@ -3867,6 +3890,8 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_struct_type(prop, "BrushCurvesSculptSettings"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Curves Sculpt Settings", ""); + + RNA_define_lib_overridable(false); } /** diff --git a/source/blender/windowmanager/intern/wm_files.cc b/source/blender/windowmanager/intern/wm_files.cc index 299ea1267d5..0c1ad2bf11d 100644 --- a/source/blender/windowmanager/intern/wm_files.cc +++ b/source/blender/windowmanager/intern/wm_files.cc @@ -145,6 +145,11 @@ 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"}; /* -------------------------------------------------------------------- */ @@ -2766,6 +2771,7 @@ 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, }; @@ -2783,7 +2789,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_OPEN); + set_next_operator_state(op, OPEN_MAINFILE_STATE_DISCARD_ASSET_DRAFTS); } if (wm_operator_close_file_dialog_if_needed(C, op, wm_open_mainfile_after_dialog_callback)) { @@ -2823,6 +2829,44 @@ 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]; @@ -2855,6 +2899,7 @@ 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}, }; @@ -4334,3 +4379,217 @@ 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_toolsystem.cc b/source/blender/windowmanager/intern/wm_toolsystem.cc index 2366a2251c6..eddff828c65 100644 --- a/source/blender/windowmanager/intern/wm_toolsystem.cc +++ b/source/blender/windowmanager/intern/wm_toolsystem.cc @@ -187,7 +187,7 @@ static void toolsystem_ref_link(bContext *C, WorkSpace *workspace, bToolRef *tre 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(scene, paint_mode); + 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) { -- 2.30.2 From 477bb662d7ed2c5ea1ed55b519b8b30aba83af98 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 13 Jan 2023 14:32:12 +0100 Subject: [PATCH 002/244] Brush Assets: Add initial support for the new 'brush asset' usage. This commit mainly adds the low-level logic to link and create a runtime override for brushes. The biggest changes are in `setup_app_data` (post-loading of .blend file), to add support to move over some data from old bmain to newly loaded bmain. The logic is complex, and different depending on whether it is an undo (aka memfile read) step, or an actual .blend file reading: * On undo, we only port over brushes themselves, their dependencies are not handled. However, since memfile reading code already ported over linked IDs, these do not need to be swapped. * On real .blend file loading, brushes from the old bmain are being reused, as long as they do not conflict (name/lib) with brushes from the new bmain. Their dependencies are also re-used if needed. Only exception: local assets from old bmain are never re-used. There is also a new step added to the 'open file' process, which checks if the to-be-opened file is a library of the currently opened one, and if so, if there are local tweaked overrides of assets from the new file. The option to save these tweaks in draft is currently an empty mockup, but the rest is functional. Brush now has a new `AssetWeakReference` pointer to keep track of the last active brush asset, and restore it on fileread in case there is currently no active brush asset for the given paint/sculpt mode. Some parts of this commits are mockups/place-holders that are not expected to be committed as-is, if at all: * Some initial support for an asset-shelf (using code from `brush-asset-project` branch), very much WIP still. * Definition of test brush library with semi-hard-coded path. * There is also a drawing bug because the poll function of Brush panels return false initially (tool has no data-block ref). Very unclear what's hapening here. Ref. #101908. --- .../keyconfig/keymap_data/blender_default.py | 18 ++ scripts/startup/bl_ui/space_view3d.py | 13 + source/blender/blenkernel/BKE_brush.hh | 3 + source/blender/blenkernel/BKE_lib_override.hh | 11 + source/blender/blenkernel/BKE_paint.hh | 15 +- source/blender/blenkernel/intern/blendfile.cc | 191 ++++++++++++- source/blender/blenkernel/intern/brush.cc | 100 ++++--- .../blender/blenkernel/intern/lib_override.cc | 35 +++ source/blender/blenkernel/intern/paint.cc | 73 ++++- source/blender/blenkernel/intern/scene.cc | 3 + .../blenloader/intern/versioning_280.cc | 14 +- .../blenloader/intern/versioning_defaults.cc | 6 +- .../blender/editors/asset/ED_asset_handle.h | 4 + .../editors/asset/intern/asset_handle.cc | 13 + .../editors/gpencil_legacy/gpencil_edit.cc | 12 +- .../editors/gpencil_legacy/gpencil_utils.cc | 2 +- .../editors/sculpt_paint/CMakeLists.txt | 4 + .../editors/sculpt_paint/curves_sculpt_ops.cc | 59 +++- .../sculpt_paint/grease_pencil_draw_ops.cc | 2 +- .../editors/sculpt_paint/paint_intern.hh | 3 +- .../editors/sculpt_paint/paint_vertex.cc | 11 +- .../editors/sculpt_paint/sculpt_ops.cc | 2 +- source/blender/makesdna/DNA_ID.h | 5 + source/blender/makesdna/DNA_scene_types.h | 7 + source/blender/makesrna/intern/rna_brush.cc | 27 +- .../blender/windowmanager/intern/wm_files.cc | 261 +++++++++++++++++- .../windowmanager/intern/wm_toolsystem.cc | 2 +- 27 files changed, 821 insertions(+), 75 deletions(-) diff --git a/scripts/presets/keyconfig/keymap_data/blender_default.py b/scripts/presets/keyconfig/keymap_data/blender_default.py index cb00d7abf81..a0422b3c468 100644 --- a/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -6749,6 +6749,21 @@ def km_node_link_modal_map(_params): return keymap +def km_asset_shelf_brushes(_params): + items = [] + keymap = ( + "Asset Shelf", + {"space_type": 'EMPTY', "region_type": 'WINDOW'}, + {"items": items}, + ) + + items.extend([ + ("sculpt_curves.brush_asset_select", {"type": 'LEFTMOUSE', "value": 'CLICK'}, None), + ]) + + return keymap + + # Fallback for gizmos that don't have custom a custom key-map. def km_generic_gizmo(_params): keymap = ( @@ -8571,6 +8586,9 @@ def generate_keymaps(params=None): km_curve_pen_modal_map(params), km_node_link_modal_map(params), + # Asset Shelf Keymaps. + km_asset_shelf_brushes(params), + # Gizmos. km_generic_gizmo(params), km_generic_gizmo_drag(params), diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index 45dfb8c0d7a..d05a89398c1 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -1075,6 +1075,18 @@ class VIEW3D_HT_header(Header): sub.popover(panel="VIEW3D_PT_shading", text="") +class VIEW3D_AST_brush_asset_shelf(bpy.types.AssetShelf): + bl_space_type = "VIEW_3D" + + @classmethod + def poll(cls, context): + return context.object and context.object.mode == 'SCULPT_CURVES' + + @classmethod + def asset_poll(cls, asset): + return asset.file_data.id_type == 'BRUSH' + + class VIEW3D_MT_editor_menus(Menu): bl_label = "" @@ -8665,6 +8677,7 @@ class VIEW3D_AST_sculpt_brushes(bpy.types.AssetShelf): classes = ( + VIEW3D_AST_brush_asset_shelf, VIEW3D_HT_header, VIEW3D_HT_tool_header, VIEW3D_MT_editor_menus, diff --git a/source/blender/blenkernel/BKE_brush.hh b/source/blender/blenkernel/BKE_brush.hh index 2a89dbef5f5..3809a225b43 100644 --- a/source/blender/blenkernel/BKE_brush.hh +++ b/source/blender/blenkernel/BKE_brush.hh @@ -31,6 +31,9 @@ struct UnifiedPaintSettings; void BKE_brush_system_init(); void BKE_brush_system_exit(); +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 f987052d20e..b38b8b2012d 100644 --- a/source/blender/blenkernel/BKE_lib_override.hh +++ b/source/blender/blenkernel/BKE_lib_override.hh @@ -82,6 +82,17 @@ 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 amount 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(struct Main *bmain, + const short id_type, + struct Library *library, + struct 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/BKE_paint.hh b/source/blender/blenkernel/BKE_paint.hh index 9e8e57f2dc8..0ab1f8ab466 100644 --- a/source/blender/blenkernel/BKE_paint.hh +++ b/source/blender/blenkernel/BKE_paint.hh @@ -25,6 +25,7 @@ #include "bmesh.h" +struct AssetWeakReference; struct BMFace; struct BMesh; struct BlendDataReader; @@ -164,7 +165,7 @@ PaintCurve *BKE_paint_curve_add(Main *bmain, const char *name); /** * Call when entering each respective paint mode. */ -bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint); +bool BKE_paint_ensure(Main *bmain, ToolSettings *ts, Paint **r_paint); void BKE_paint_init(Main *bmain, Scene *sce, ePaintMode mode, const uchar col[3]); void BKE_paint_free(Paint *p); /** @@ -179,7 +180,7 @@ void BKE_paint_runtime_init(const ToolSettings *ts, Paint *paint); void BKE_paint_cavity_curve_preset(Paint *p, int preset); eObjectMode BKE_paint_object_mode_from_paintmode(ePaintMode mode); -bool BKE_paint_ensure_from_paintmode(Scene *sce, ePaintMode mode); +bool BKE_paint_ensure_from_paintmode(Main *bmain, Scene *sce, ePaintMode mode); Paint *BKE_paint_get_active_from_paintmode(Scene *sce, ePaintMode mode); const EnumPropertyItem *BKE_paint_get_tool_enum_from_paintmode(ePaintMode mode); const char *BKE_paint_get_tool_enum_translation_context_from_paintmode(ePaintMode mode); @@ -192,6 +193,14 @@ ePaintMode BKE_paintmode_get_from_tool(const bToolRef *tref); Brush *BKE_paint_brush(Paint *paint); const Brush *BKE_paint_brush_for_read(const Paint *p); void BKE_paint_brush_set(Paint *paint, Brush *br); + +/** Set the active brush of given paint struct, and store the weak asset reference to it. + * NOTE: Takes ownership of the given `weak_asset_reference`. */ +void BKE_paint_brush_asset_set(Paint *p, Brush *br, AssetWeakReference *weak_asset_reference); + +/** Attempt to restore a valid active brush in `p` from brush asset informations stored in `p`. */ +void BKE_paint_brush_asset_restore(Main *bmain, Paint *p); + Palette *BKE_paint_palette(Paint *paint); void BKE_paint_palette_set(Paint *p, Palette *palette); void BKE_paint_curve_set(Brush *br, PaintCurve *pc); @@ -866,7 +875,7 @@ int BKE_sculpt_mask_layers_ensure(Depsgraph *depsgraph, Main *bmain, Object *ob, MultiresModifierData *mmd); -void BKE_sculpt_toolsettings_data_ensure(Scene *scene); +void BKE_sculpt_toolsettings_data_ensure(Main *bmain, Scene *scene); PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob); diff --git a/source/blender/blenkernel/intern/blendfile.cc b/source/blender/blenkernel/intern/blendfile.cc index 620158b3c33..da0b7946f7e 100644 --- a/source/blender/blenkernel/intern/blendfile.cc +++ b/source/blender/blenkernel/intern/blendfile.cc @@ -306,14 +306,195 @@ 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 BMain into new BMain, provided it does not conflict with data + * already present in the new BMain (name-wise and library-wise). + * + * Dependencies from moved over old data are also imported into the new BMain, (unless, in case of + * linked data, a matching linked ID is already available in new BMain). + * + * 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 BMain later. + * + * NOTE: This function will never remove any original new data from the new BMain, it only moves + * (some of) the old data to the new BMain. + */ +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_uuid_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. * * WARNING: Currently only expects to work on local data, won't work properly if some of the IDs of * given type are linked. * - * 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). */ + * 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). */ static void swap_old_bmain_data_for_blendfile(ReuseOldBMainData *reuse_data, const short id_code) { Main *new_bmain = reuse_data->new_bmain; @@ -720,6 +901,12 @@ 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 561d0d70295..f864139490b 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -14,13 +14,17 @@ #include "DNA_material_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_space_types.h" #include "BLI_listbase.h" #include "BLI_math_rotation.h" #include "BLI_rand.h" +#include "BLO_readfile.h" + #include "BLT_translation.h" +#include "BKE_blendfile_link_append.h" #include "BKE_bpath.h" #include "BKE_brush.hh" #include "BKE_colortools.h" @@ -36,6 +40,8 @@ #include "BKE_preview_image.hh" #include "BKE_texture.h" +#include "AS_asset_library.h" + #include "IMB_colormanagement.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" @@ -376,39 +382,6 @@ static void brush_blend_read_after_liblink(BlendLibReader * /*reader*/, ID *id) } } -static int brush_undo_preserve_cb(LibraryIDLinkCallbackData *cb_data) -{ - BlendLibReader *reader = (BlendLibReader *)cb_data->user_data; - ID *self_id = cb_data->self_id; - ID *id_old = *cb_data->id_pointer; - /* Old data has not been remapped to new values of the pointers, if we want to keep the old - * pointer here we need its new address. */ - ID *id_old_new = id_old != nullptr ? BLO_read_get_new_id_address( - reader, self_id, ID_IS_LINKED(self_id), id_old) : - nullptr; - BLI_assert(id_old_new == nullptr || ELEM(id_old, id_old_new, id_old_new->orig_id)); - if (cb_data->cb_flag & IDWALK_CB_USER) { - id_us_plus_no_lib(id_old_new); - id_us_min(id_old); - } - *cb_data->id_pointer = id_old_new; - return IDWALK_RET_NOP; -} - -static void brush_undo_preserve(BlendLibReader *reader, ID *id_new, ID *id_old) -{ - /* Whole Brush is preserved across undo-steps. */ - BKE_lib_id_swap(nullptr, id_new, id_old, false, 0); - - /* `id_new` now has content from `id_old`, we need to ensure those old ID pointers are valid. - * NOTE: Since we want to re-use all old pointers here, code is much simpler than for Scene. */ - BKE_library_foreach_ID_link(nullptr, id_new, brush_undo_preserve_cb, reader, IDWALK_NOP); - - /* NOTE: We do not swap IDProperties, as dealing with potential ID pointers in those would be - * fairly delicate. */ - std::swap(id_new->properties, id_old->properties); -} - IDTypeInfo IDType_ID_BR = { /*id_code*/ ID_BR, /*id_filter*/ FILTER_ID_BR, @@ -417,7 +390,7 @@ IDTypeInfo IDType_ID_BR = { /*name*/ "Brush", /*name_plural*/ N_("brushes"), /*translation_context*/ BLT_I18NCONTEXT_ID_BRUSH, - /*flags*/ IDTYPE_FLAGS_NO_ANIMDATA, + /*flags*/ IDTYPE_FLAGS_NO_ANIMDATA | IDTYPE_FLAGS_NO_MEMFILE_UNDO, /*asset_type_info*/ nullptr, /*init_data*/ brush_init_data, @@ -433,7 +406,7 @@ IDTypeInfo IDType_ID_BR = { /*blend_read_data*/ brush_blend_read_data, /*blend_read_after_liblink*/ brush_blend_read_after_liblink, - /*blend_read_undo_preserve*/ brush_undo_preserve, + /*blend_read_undo_preserve*/ nullptr, /*lib_override_apply_post*/ nullptr, }; @@ -510,6 +483,63 @@ 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 = {nullptr}; + 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, + static_cast(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 11834c8507e..61c6a2e1156 100644 --- a/source/blender/blenkernel/intern/lib_override.cc +++ b/source/blender/blenkernel/intern/lib_override.cc @@ -362,6 +362,41 @@ 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 short 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)) { + /* 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 4f0f2987419..58d4c05903d 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -11,6 +11,7 @@ #include "MEM_guardedalloc.h" +#include "DNA_asset_types.h" #include "DNA_brush_types.h" #include "DNA_defaults.h" #include "DNA_gpencil_legacy_types.h" @@ -35,6 +36,7 @@ #include "BLT_translation.h" +#include "BKE_asset.h" #include "BKE_attribute.h" #include "BKE_attribute.hh" #include "BKE_brush.hh" @@ -309,7 +311,7 @@ void BKE_paint_reset_overlay_invalid(ePaintOverlayControlFlags flag) overlay_flags &= ~(flag); } -bool BKE_paint_ensure_from_paintmode(Scene *sce, ePaintMode mode) +bool BKE_paint_ensure_from_paintmode(Main *bmain, Scene *sce, ePaintMode mode) { ToolSettings *ts = sce->toolsettings; Paint **paint_ptr = nullptr; @@ -354,7 +356,7 @@ bool BKE_paint_ensure_from_paintmode(Scene *sce, ePaintMode mode) break; } if (paint_ptr) { - BKE_paint_ensure(ts, paint_ptr); + BKE_paint_ensure(bmain, ts, paint_ptr); return true; } return false; @@ -668,6 +670,50 @@ void BKE_paint_brush_set(Paint *p, Brush *br) } } +static void paint_brush_asset_update(Paint *p, + Brush *br, + AssetWeakReference *brush_asset_reference) +{ + if (p->brush_asset_reference != nullptr) { + BKE_asset_weak_reference_free(&p->brush_asset_reference); + } + + if (br == nullptr || br != p->brush || !ID_IS_OVERRIDE_LIBRARY_REAL(p->brush) || + !(ID_IS_ASSET(p->brush) || ID_IS_ASSET(p->brush->id.override_library->reference))) + { + BKE_asset_weak_reference_free(&brush_asset_reference); + return; + } + + p->brush_asset_reference = brush_asset_reference; +} + +void BKE_paint_brush_asset_set(Paint *p, Brush *br, AssetWeakReference *weak_asset_reference) +{ + BKE_paint_brush_set(p, br); + paint_brush_asset_update(p, br, weak_asset_reference); +} + +void BKE_paint_brush_asset_restore(Main *bmain, Paint *p) +{ + if (p->brush != nullptr) { + return; + } + + if (p->brush_asset_reference == nullptr) { + return; + } + + AssetWeakReference *brush_asset_reference = p->brush_asset_reference; + p->brush_asset_reference = nullptr; + + Brush *brush_asset = BKE_brush_asset_runtime_ensure(bmain, brush_asset_reference); + + /* Will either re-assign the brush_asset_reference to `p`, or free it if loading a brush ID from + * it failed. */ + BKE_paint_brush_asset_set(p, brush_asset, brush_asset_reference); +} + void BKE_paint_runtime_init(const ToolSettings *ts, Paint *paint) { if (paint == &ts->imapaint.paint) { @@ -1093,7 +1139,7 @@ eObjectMode BKE_paint_object_mode_from_paintmode(ePaintMode mode) } } -bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint) +bool BKE_paint_ensure(Main *bmain, ToolSettings *ts, Paint **r_paint) { Paint *paint = nullptr; if (*r_paint) { @@ -1127,6 +1173,7 @@ bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint) BLI_assert(paint_test.runtime.tool_offset == (*r_paint)->runtime.tool_offset); #endif } + BKE_paint_brush_asset_restore(bmain, *r_paint); return true; } @@ -1174,6 +1221,7 @@ bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint) *r_paint = paint; BKE_paint_runtime_init(ts, paint); + BKE_paint_brush_asset_restore(bmain, paint); return false; } @@ -1183,7 +1231,7 @@ void BKE_paint_init(Main *bmain, Scene *sce, ePaintMode mode, const uchar col[3] UnifiedPaintSettings *ups = &sce->toolsettings->unified_paint_settings; Paint *paint = BKE_paint_get_active_from_paintmode(sce, mode); - BKE_paint_ensure_from_paintmode(sce, mode); + BKE_paint_ensure_from_paintmode(bmain, sce, mode); /* If there's no brush, create one */ if (PAINT_MODE_HAS_BRUSH(mode)) { @@ -1213,6 +1261,9 @@ void BKE_paint_free(Paint *paint) { BKE_curvemapping_free(paint->cavity_curve); MEM_SAFE_FREE(paint->tool_slots); + if (paint->brush_asset_reference != nullptr) { + BKE_asset_weak_reference_free(&paint->brush_asset_reference); + } } void BKE_paint_copy(Paint *src, Paint *tar, const int flag) @@ -1221,6 +1272,8 @@ void BKE_paint_copy(Paint *src, Paint *tar, const int flag) tar->cavity_curve = BKE_curvemapping_copy(src->cavity_curve); tar->tool_slots = static_cast(MEM_dupallocN(src->tool_slots)); + 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); @@ -1249,6 +1302,9 @@ void BKE_paint_blend_write(BlendWriter *writer, Paint *p) if (p->cavity_curve) { BKE_curvemapping_blend_write(writer, p->cavity_curve); } + if (p->brush_asset_reference) { + BKE_asset_weak_reference_write(writer, p->brush_asset_reference); + } BLO_write_struct_array(writer, PaintToolSlot, p->tool_slots_len, p->tool_slots); } @@ -1266,6 +1322,11 @@ void BKE_paint_blend_read_data(BlendDataReader *reader, const Scene *scene, Pain BKE_paint_cavity_curve_preset(p, CURVE_PRESET_LINE); } + BLO_read_data_address(reader, &p->brush_asset_reference); + if (p->brush_asset_reference) { + BKE_asset_weak_reference_read(reader, p->brush_asset_reference); + } + BLO_read_data_address(reader, &p->tool_slots); /* Workaround for invalid data written in older versions. */ @@ -2083,9 +2144,9 @@ int BKE_sculpt_mask_layers_ensure(Depsgraph *depsgraph, return ret; } -void BKE_sculpt_toolsettings_data_ensure(Scene *scene) +void BKE_sculpt_toolsettings_data_ensure(Main *bmain, Scene *scene) { - BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->sculpt); + BKE_paint_ensure(bmain, scene->toolsettings, (Paint **)&scene->toolsettings->sculpt); Sculpt *sd = scene->toolsettings->sculpt; diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc index 74da723a9d0..184457b1a88 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -1017,6 +1017,9 @@ static void scene_foreach_path(ID *id, BPathForeachPathData *bpath_data) if (scene->ed != nullptr) { SEQ_for_each_callback(&scene->ed->seqbase, seq_foreach_path_callback, bpath_data); } + + /* TODO: Also handle `asset_weak_reference` here? Probably not, conceptually this is not a file + * path. */ } static void scene_blend_write(BlendWriter *writer, ID *id, const void *id_address) diff --git a/source/blender/blenloader/intern/versioning_280.cc b/source/blender/blenloader/intern/versioning_280.cc index d8f8d7819c3..d50fd53e742 100644 --- a/source/blender/blenloader/intern/versioning_280.cc +++ b/source/blender/blenloader/intern/versioning_280.cc @@ -2881,10 +2881,10 @@ void do_versions_after_linking_280(FileData *fd, Main *bmain) ToolSettings *ts = scene->toolsettings; /* Ensure new Paint modes. */ - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_VERTEX_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_SCULPT_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_WEIGHT_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_VERTEX_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_SCULPT_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_WEIGHT_GPENCIL); /* Set default Draw brush. */ if (brush != nullptr) { @@ -2925,9 +2925,9 @@ void do_versions_after_linking_280(FileData *fd, Main *bmain) /* Reset all grease pencil brushes. */ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { /* Ensure new Paint modes. */ - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_VERTEX_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_SCULPT_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_WEIGHT_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_VERTEX_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_SCULPT_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_WEIGHT_GPENCIL); } } diff --git a/source/blender/blenloader/intern/versioning_defaults.cc b/source/blender/blenloader/intern/versioning_defaults.cc index 6717966cc7e..700b288cb3d 100644 --- a/source/blender/blenloader/intern/versioning_defaults.cc +++ b/source/blender/blenloader/intern/versioning_defaults.cc @@ -494,9 +494,9 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) } /* Ensure new Paint modes. */ - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_VERTEX_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_SCULPT_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_WEIGHT_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_VERTEX_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_SCULPT_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_WEIGHT_GPENCIL); /* Enable cursor. */ if (ts->gp_paint) { diff --git a/source/blender/editors/asset/ED_asset_handle.h b/source/blender/editors/asset/ED_asset_handle.h index 2506dc15ab6..69df2285a82 100644 --- a/source/blender/editors/asset/ED_asset_handle.h +++ b/source/blender/editors/asset/ED_asset_handle.h @@ -43,6 +43,10 @@ void ED_asset_handle_get_full_library_path( /* `1024` for #FILE_MAX, * rely on warnings to let us know if this gets out of sync. */ char r_full_lib_path[1024]); +void ED_asset_handle_get_full_path(const AssetHandle *asset_handle, + /* `1024 + 66` for #FILE_MAX_LIBEXTRA, + * rely on warnings to let us know if this gets out of sync. */ + char r_full_lib_path[1024 + 66]); #ifdef __cplusplus } diff --git a/source/blender/editors/asset/intern/asset_handle.cc b/source/blender/editors/asset/intern/asset_handle.cc index 684ef1a0b52..b072b815247 100644 --- a/source/blender/editors/asset/intern/asset_handle.cc +++ b/source/blender/editors/asset/intern/asset_handle.cc @@ -57,3 +57,16 @@ void ED_asset_handle_get_full_library_path(const AssetHandle *asset_handle, BLI_strncpy(r_full_lib_path, library_path.c_str(), FILE_MAX); } + +void ED_asset_handle_get_full_path(const AssetHandle *asset_handle, + char r_full_lib_path[FILE_MAX_LIBEXTRA]) +{ + *r_full_lib_path = '\0'; + + std::string library_path = asset_handle->file_data->asset->get_identifier().full_path(); + if (library_path.empty()) { + return; + } + + BLI_strncpy(r_full_lib_path, library_path.c_str(), FILE_MAX); +} diff --git a/source/blender/editors/gpencil_legacy/gpencil_edit.cc b/source/blender/editors/gpencil_legacy/gpencil_edit.cc index 0441f998901..014327c22e9 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_edit.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_edit.cc @@ -396,8 +396,8 @@ static int gpencil_paintmode_toggle_exec(bContext *C, wmOperator *op) if (mode == OB_MODE_PAINT_GPENCIL_LEGACY) { /* Be sure we have brushes and Paint settings. * Need Draw and Vertex (used for Tint). */ - BKE_paint_ensure(ts, (Paint **)&ts->gp_paint); - BKE_paint_ensure(ts, (Paint **)&ts->gp_vertexpaint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_paint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_vertexpaint); BKE_brush_gpencil_paint_presets(bmain, ts, false); @@ -511,7 +511,7 @@ static int gpencil_sculptmode_toggle_exec(bContext *C, wmOperator *op) if (mode == OB_MODE_SCULPT_GPENCIL_LEGACY) { /* Be sure we have brushes. */ - BKE_paint_ensure(ts, (Paint **)&ts->gp_sculptpaint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_sculptpaint); const bool reset_mode = (ts->gp_sculptpaint->paint.brush == nullptr); BKE_brush_gpencil_sculpt_presets(bmain, ts, reset_mode); @@ -625,7 +625,7 @@ static int gpencil_weightmode_toggle_exec(bContext *C, wmOperator *op) if (mode == OB_MODE_WEIGHT_GPENCIL_LEGACY) { /* Be sure we have brushes. */ - BKE_paint_ensure(ts, (Paint **)&ts->gp_weightpaint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_weightpaint); const bool reset_mode = (ts->gp_weightpaint->paint.brush == nullptr); BKE_brush_gpencil_weight_presets(bmain, ts, reset_mode); @@ -732,8 +732,8 @@ static int gpencil_vertexmode_toggle_exec(bContext *C, wmOperator *op) if (mode == OB_MODE_VERTEX_GPENCIL_LEGACY) { /* Be sure we have brushes. * Need Draw as well (used for Palettes). */ - BKE_paint_ensure(ts, (Paint **)&ts->gp_paint); - BKE_paint_ensure(ts, (Paint **)&ts->gp_vertexpaint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_paint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_vertexpaint); const bool reset_mode = (ts->gp_vertexpaint->paint.brush == nullptr); BKE_brush_gpencil_vertex_presets(bmain, ts, reset_mode); diff --git a/source/blender/editors/gpencil_legacy/gpencil_utils.cc b/source/blender/editors/gpencil_legacy/gpencil_utils.cc index 94328d66699..e6cc4130322 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_utils.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_utils.cc @@ -1428,7 +1428,7 @@ void ED_gpencil_add_defaults(bContext *C, Object *ob) Main *bmain = CTX_data_main(C); ToolSettings *ts = CTX_data_tool_settings(C); - BKE_paint_ensure(ts, (Paint **)&ts->gp_paint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_paint); Paint *paint = &ts->gp_paint->paint; /* if not exist, create a new one */ if ((paint->brush == nullptr) || (paint->brush->gpencil_settings == nullptr)) { diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index 071d2625ed3..bee957e9285 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -4,8 +4,12 @@ set(INC ../include + ../asset ../uvedit + ../../asset_system ../../blenkernel + ../../blenlib + ../../blenloader ../../blentranslation ../../bmesh ../../depsgraph diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc index c608d3afa44..04508639d1a 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc @@ -8,6 +8,8 @@ #include "BLI_utildefines.h" #include "BLI_vector_set.hh" +#include "BKE_asset.h" +#include "BKE_blendfile.h" #include "BKE_brush.hh" #include "BKE_bvhutils.h" #include "BKE_context.h" @@ -20,6 +22,7 @@ #include "WM_message.hh" #include "WM_toolsystem.h" +#include "ED_asset_handle.h" #include "ED_curves.hh" #include "ED_curves_sculpt.hh" #include "ED_image.hh" @@ -28,6 +31,8 @@ #include "ED_space_api.hh" #include "ED_view3d.hh" +#include "AS_asset_representation.hh" + #include "DEG_depsgraph.hh" #include "DEG_depsgraph_query.hh" @@ -283,7 +288,8 @@ static void curves_sculptmode_enter(bContext *C) wmMsgBus *mbus = CTX_wm_message_bus(C); Object *ob = CTX_data_active_object(C); - BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->curves_sculpt); + BKE_paint_ensure( + CTX_data_main(C), scene->toolsettings, (Paint **)&scene->toolsettings->curves_sculpt); CurvesSculpt *curves_sculpt = scene->toolsettings->curves_sculpt; ob->mode = OB_MODE_SCULPT_CURVES; @@ -1167,6 +1173,55 @@ static void SCULPT_CURVES_OT_min_distance_edit(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_DEPENDS_ON_CURSOR; } +/* -------------------------------------------------------------------- */ + +static int brush_asset_select_exec(bContext *C, wmOperator * /*op*/) +{ + char *brush_name = nullptr; + char asset_libpath[FILE_MAX]; + char asset_libpath_full[FILE_MAX_LIBEXTRA]; + bool asset_handle_valid; + + const AssetHandle asset_handle = CTX_wm_asset_handle(C, &asset_handle_valid); + if (!asset_handle_valid) { + return OPERATOR_CANCELLED; + } + AssetRepresentationHandle *asset = ED_asset_handle_get_representation(&asset_handle); + AssetWeakReference *brush_asset_reference = asset->make_weak_reference(); + + ED_asset_handle_get_full_path(&asset_handle, asset_libpath_full); + BKE_blendfile_library_path_explode(asset_libpath_full, asset_libpath, nullptr, &brush_name); + BLI_assert(brush_name != nullptr); + + Brush *brush = BKE_brush_asset_runtime_ensure(CTX_data_main(C), brush_asset_reference); + + ToolSettings *tool_settings = CTX_data_tool_settings(C); + /* Either takes ownership of the brush_asset_reference, or frees it. */ + BKE_paint_brush_asset_set(&tool_settings->curves_sculpt->paint, brush, brush_asset_reference); + + return OPERATOR_FINISHED; +} + +/** + * 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. + */ +static void SCULPT_CURVES_OT_brush_asset_select(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Brush Asset"; + ot->description = "Select a brush asset as currently sculpt/paint tool - TESTING PURPOSE ONLY"; + ot->idname = "SCULPT_CURVES_OT_brush_asset_select"; + + /* api callbacks */ + ot->exec = brush_asset_select_exec; + ot->poll = CURVES_SCULPT_mode_poll; + + ot->prop = RNA_def_string( + ot->srna, "name", nullptr, MAX_NAME, "Brush Name", "name of the brush asset to select"); +} + } // namespace blender::ed::sculpt_paint /* -------------------------------------------------------------------- */ @@ -1181,6 +1236,8 @@ void ED_operatortypes_sculpt_curves() WM_operatortype_append(SCULPT_CURVES_OT_select_random); WM_operatortype_append(SCULPT_CURVES_OT_select_grow); WM_operatortype_append(SCULPT_CURVES_OT_min_distance_edit); + + WM_operatortype_append(SCULPT_CURVES_OT_brush_asset_select); } /** \} */ diff --git a/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc b/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc index 73bfc930c94..3a75edf81d2 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc @@ -228,7 +228,7 @@ static void grease_pencil_draw_mode_enter(bContext *C) Object *ob = CTX_data_active_object(C); GpPaint *grease_pencil_paint = scene->toolsettings->gp_paint; - BKE_paint_ensure(scene->toolsettings, (Paint **)&grease_pencil_paint); + BKE_paint_ensure(CTX_data_main(C), scene->toolsettings, (Paint **)&grease_pencil_paint); ob->mode = OB_MODE_PAINT_GREASE_PENCIL; diff --git a/source/blender/editors/sculpt_paint/paint_intern.hh b/source/blender/editors/sculpt_paint/paint_intern.hh index 619633536a1..0685dcd5e0b 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.hh +++ b/source/blender/editors/sculpt_paint/paint_intern.hh @@ -553,7 +553,8 @@ void get_brush_alpha_data(const Scene *scene, void init_stroke(Depsgraph *depsgraph, Object *ob); void init_session_data(const ToolSettings *ts, Object *ob); -void init_session(Depsgraph *depsgraph, Scene *scene, Object *ob, eObjectMode object_mode); +void init_session( + Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob, eObjectMode object_mode); Vector pbvh_gather_generic(Object *ob, VPaint *wp, Sculpt *sd, Brush *brush); diff --git a/source/blender/editors/sculpt_paint/paint_vertex.cc b/source/blender/editors/sculpt_paint/paint_vertex.cc index 7216928bf86..5a2030f2f03 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.cc +++ b/source/blender/editors/sculpt_paint/paint_vertex.cc @@ -209,10 +209,11 @@ void init_stroke(Depsgraph *depsgraph, Object *ob) } /* Toggle operator for turning vertex paint mode on or off (copied from sculpt.cc) */ -void init_session(Depsgraph *depsgraph, Scene *scene, Object *ob, eObjectMode object_mode) +void init_session( + Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob, eObjectMode object_mode) { /* Create persistent sculpt mode data */ - BKE_sculpt_toolsettings_data_ensure(scene); + BKE_sculpt_toolsettings_data_ensure(bmain, scene); BLI_assert(ob->sculpt == nullptr); ob->sculpt = MEM_new(__func__); @@ -336,7 +337,7 @@ void mode_enter_generic( const ePaintMode paint_mode = PAINT_MODE_VERTEX; ED_mesh_color_ensure(me, nullptr); - BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->vpaint); + BKE_paint_ensure(bmain, scene->toolsettings, (Paint **)&scene->toolsettings->vpaint); Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode); ED_paint_cursor_start(paint, vertex_paint_poll); BKE_paint_init(bmain, scene, paint_mode, PAINT_CURSOR_VERTEX_PAINT); @@ -344,7 +345,7 @@ void mode_enter_generic( else if (mode_flag == OB_MODE_WEIGHT_PAINT) { const ePaintMode paint_mode = PAINT_MODE_WEIGHT; - BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->wpaint); + BKE_paint_ensure(bmain, scene->toolsettings, (Paint **)&scene->toolsettings->wpaint); Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode); ED_paint_cursor_start(paint, weight_paint_poll); BKE_paint_init(bmain, scene, paint_mode, PAINT_CURSOR_WEIGHT_PAINT); @@ -366,7 +367,7 @@ void mode_enter_generic( BKE_sculptsession_free(ob); } - vwpaint::init_session(depsgraph, scene, ob, mode_flag); + vwpaint::init_session(bmain, depsgraph, scene, ob, mode_flag); /* Flush object mode. */ DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.cc b/source/blender/editors/sculpt_paint/sculpt_ops.cc index 7adc5e110dd..6417f65ebfa 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/sculpt_ops.cc @@ -267,7 +267,7 @@ static void SCULPT_OT_symmetrize(wmOperatorType *ot) static void sculpt_init_session(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob) { /* Create persistent sculpt mode data. */ - BKE_sculpt_toolsettings_data_ensure(scene); + BKE_sculpt_toolsettings_data_ensure(bmain, scene); /* Create sculpt mode session data. */ if (ob->sculpt != nullptr) { diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index ea6bb898998..f61639c22c0 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -543,6 +543,11 @@ typedef struct Library { */ char filepath_abs[1024]; + /** + * Weak reference to the Brush Asset. + */ + struct AssetWeakReference *asset_repository_weak_reference; + /** Set for indirectly linked libraries, used in the outliner and while reading. */ struct Library *parent; diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 3728d658346..69412504507 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -948,6 +948,13 @@ typedef struct PaintToolSlot { typedef struct Paint { struct Brush *brush; + /** + * A weak asset reference to the #brush, if not NULL. + * Used to attempt restoring the active brush from the AssetLibrary system, typically on + * file load. + */ + struct AssetWeakReference *brush_asset_reference; + /** * Each tool has its own active brush, * The currently active tool is defined by the current 'brush'. diff --git a/source/blender/makesrna/intern/rna_brush.cc b/source/blender/makesrna/intern/rna_brush.cc index 3bb64f8c779..704e620df09 100644 --- a/source/blender/makesrna/intern/rna_brush.cc +++ b/source/blender/makesrna/intern/rna_brush.cc @@ -770,7 +770,12 @@ static void rna_Brush_reset_icon(Brush *br) static void rna_Brush_update(Main * /*bmain*/, Scene * /*scene*/, PointerRNA *ptr) { - Brush *br = (Brush *)ptr->data; + Brush *br = (Brush *)ptr->owner_id; + + if (ID_IS_OVERRIDE_LIBRARY_REAL(&br->id)) { + br->id.tag |= LIB_TAG_LIBOVERRIDE_AUTOREFRESH; + } + WM_main_add_notifier(NC_BRUSH | NA_EDITED, br); // WM_main_add_notifier(NC_SPACE | ND_SPACE_VIEW3D, nullptr); } @@ -2146,14 +2151,18 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) RNA_def_struct_sdna(srna, "BrushCurvesSculptSettings"); RNA_def_struct_ui_text(srna, "Curves Sculpt Brush Settings", ""); + RNA_define_lib_overridable(true); + prop = RNA_def_property(srna, "add_amount", PROP_INT, PROP_NONE); RNA_def_property_range(prop, 1, INT32_MAX); RNA_def_property_ui_text(prop, "Count", "Number of curves added by the Add brush"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "points_per_curve", PROP_INT, PROP_NONE); RNA_def_property_range(prop, 2, INT32_MAX); RNA_def_property_ui_text( prop, "Points per Curve", "Number of control points in a newly added curve"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "scale_uniform", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, nullptr, "flag", BRUSH_CURVES_SCULPT_FLAG_SCALE_UNIFORM); @@ -2161,17 +2170,20 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) "Scale Uniform", "Grow or shrink curves by changing their size uniformly instead of " "using trimming or extrapolation"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "minimum_length", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_range(prop, 0.0f, FLT_MAX); RNA_def_property_ui_text( prop, "Minimum Length", "Avoid shrinking curves shorter than this length"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "interpolate_length", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna( prop, nullptr, "flag", BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH); RNA_def_property_ui_text( prop, "Interpolate Length", "Use length of the curves in close proximity"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "interpolate_point_count", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna( @@ -2179,11 +2191,13 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Interpolate Point Count", "Use the number of points from the curves in close proximity"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "interpolate_shape", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, nullptr, "flag", BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE); RNA_def_property_ui_text( prop, "Interpolate Shape", "Use shape of the curves in close proximity"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "curve_length", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_range(prop, 0.0, FLT_MAX); @@ -2191,28 +2205,35 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) prop, "Curve Length", "Length of newly added curves when it is not interpolated from other curves"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "minimum_distance", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_range(prop, 0.0f, FLT_MAX); RNA_def_property_ui_range(prop, 0.0, 1000.0f, 0.001, 2); RNA_def_property_ui_text( prop, "Minimum Distance", "Goal distance between curve roots for the Density brush"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "density_add_attempts", PROP_INT, PROP_NONE); RNA_def_property_range(prop, 0, INT32_MAX); RNA_def_property_ui_text( prop, "Density Add Attempts", "How many times the Density brush tries to add a new curve"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "density_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, density_mode_items); RNA_def_property_ui_text( prop, "Density Mode", "Determines whether the brush adds or removes curves"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "curve_parameter_falloff", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "CurveMapping"); RNA_def_property_ui_text(prop, "Curve Parameter Falloff", "Falloff that is applied from the tip to the root of each curve"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + RNA_define_lib_overridable(false); } static void rna_def_brush(BlenderRNA *brna) @@ -2560,6 +2581,8 @@ static void rna_def_brush(BlenderRNA *brna) srna, "Brush", "Brush data-block for storing brush settings for painting and sculpting"); RNA_def_struct_ui_icon(srna, ICON_BRUSH_DATA); + RNA_define_lib_overridable(true); + /* enums */ prop = RNA_def_property(srna, "blend", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, prop_blend_items); @@ -3867,6 +3890,8 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_struct_type(prop, "BrushCurvesSculptSettings"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Curves Sculpt Settings", ""); + + RNA_define_lib_overridable(false); } /** diff --git a/source/blender/windowmanager/intern/wm_files.cc b/source/blender/windowmanager/intern/wm_files.cc index 0e5d17aa069..0b62abfb635 100644 --- a/source/blender/windowmanager/intern/wm_files.cc +++ b/source/blender/windowmanager/intern/wm_files.cc @@ -145,6 +145,11 @@ 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"}; /* -------------------------------------------------------------------- */ @@ -2791,6 +2796,7 @@ 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, }; @@ -2808,7 +2814,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_OPEN); + set_next_operator_state(op, OPEN_MAINFILE_STATE_DISCARD_ASSET_DRAFTS); } if (wm_operator_close_file_dialog_if_needed(C, op, wm_open_mainfile_after_dialog_callback)) { @@ -2848,6 +2854,44 @@ 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]; @@ -2880,6 +2924,7 @@ 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}, }; @@ -4359,3 +4404,217 @@ 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_toolsystem.cc b/source/blender/windowmanager/intern/wm_toolsystem.cc index 2366a2251c6..eddff828c65 100644 --- a/source/blender/windowmanager/intern/wm_toolsystem.cc +++ b/source/blender/windowmanager/intern/wm_toolsystem.cc @@ -187,7 +187,7 @@ static void toolsystem_ref_link(bContext *C, WorkSpace *workspace, bToolRef *tre 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(scene, paint_mode); + 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) { -- 2.30.2 From e3ef1638161d1800aa73fbcc04d3c2f7023afe99 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 2 Nov 2023 16:18:21 +0100 Subject: [PATCH 003/244] Fixes from rebase on main. Note that current code is crashing on initial show of brush asset browser in curve sculpt mode. Need Julian to help on this. --- scripts/startup/bl_ui/space_view3d.py | 3 ++- .../editors/sculpt_paint/curves_sculpt_ops.cc | 16 +++------------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index d05a89398c1..c50de3cf7b7 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -1077,6 +1077,7 @@ class VIEW3D_HT_header(Header): class VIEW3D_AST_brush_asset_shelf(bpy.types.AssetShelf): bl_space_type = "VIEW_3D" + bl_options = {'NO_ASSET_DRAG'} @classmethod def poll(cls, context): @@ -1084,7 +1085,7 @@ class VIEW3D_AST_brush_asset_shelf(bpy.types.AssetShelf): @classmethod def asset_poll(cls, asset): - return asset.file_data.id_type == 'BRUSH' + return asset.id_type == 'BRUSH' class VIEW3D_MT_editor_menus(Menu): diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc index 04508639d1a..9da2148112f 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc @@ -1177,22 +1177,12 @@ static void SCULPT_CURVES_OT_min_distance_edit(wmOperatorType *ot) static int brush_asset_select_exec(bContext *C, wmOperator * /*op*/) { - char *brush_name = nullptr; - char asset_libpath[FILE_MAX]; - char asset_libpath_full[FILE_MAX_LIBEXTRA]; - bool asset_handle_valid; - - const AssetHandle asset_handle = CTX_wm_asset_handle(C, &asset_handle_valid); - if (!asset_handle_valid) { + blender::asset_system::AssetRepresentation *asset = CTX_wm_asset(C); + if (!asset) { return OPERATOR_CANCELLED; } - AssetRepresentationHandle *asset = ED_asset_handle_get_representation(&asset_handle); + AssetWeakReference *brush_asset_reference = asset->make_weak_reference(); - - ED_asset_handle_get_full_path(&asset_handle, asset_libpath_full); - BKE_blendfile_library_path_explode(asset_libpath_full, asset_libpath, nullptr, &brush_name); - BLI_assert(brush_name != nullptr); - Brush *brush = BKE_brush_asset_runtime_ensure(CTX_data_main(C), brush_asset_reference); ToolSettings *tool_settings = CTX_data_tool_settings(C); -- 2.30.2 From fe9e7eab1390c9b767579ca267841bbe49b3a328 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 13 Jan 2023 14:32:12 +0100 Subject: [PATCH 004/244] Brush Assets: Add initial support for the new 'brush asset' usage. This commit mainly adds the low-level logic to link and create a runtime override for brushes. The biggest changes are in `setup_app_data` (post-loading of .blend file), to add support to move over some data from old bmain to newly loaded bmain. The logic is complex, and different depending on whether it is an undo (aka memfile read) step, or an actual .blend file reading: * On undo, we only port over brushes themselves, their dependencies are not handled. However, since memfile reading code already ported over linked IDs, these do not need to be swapped. * On real .blend file loading, brushes from the old bmain are being reused, as long as they do not conflict (name/lib) with brushes from the new bmain. Their dependencies are also re-used if needed. Only exception: local assets from old bmain are never re-used. There is also a new step added to the 'open file' process, which checks if the to-be-opened file is a library of the currently opened one, and if so, if there are local tweaked overrides of assets from the new file. The option to save these tweaks in draft is currently an empty mockup, but the rest is functional. Brush now has a new `AssetWeakReference` pointer to keep track of the last active brush asset, and restore it on fileread in case there is currently no active brush asset for the given paint/sculpt mode. Some parts of this commits are mockups/place-holders that are not expected to be committed as-is, if at all: * Some initial support for an asset-shelf (using code from `brush-asset-project` branch), very much WIP still. * Definition of test brush library with semi-hard-coded path. * There is also a drawing bug because the poll function of Brush panels return false initially (tool has no data-block ref). Very unclear what's hapening here. Ref. #101908. --- .../keyconfig/keymap_data/blender_default.py | 18 ++ scripts/startup/bl_ui/space_view3d.py | 14 + source/blender/blenkernel/BKE_brush.hh | 3 + source/blender/blenkernel/BKE_lib_override.hh | 11 + source/blender/blenkernel/BKE_paint.hh | 15 +- source/blender/blenkernel/intern/blendfile.cc | 191 ++++++++++++- source/blender/blenkernel/intern/brush.cc | 100 ++++--- .../blender/blenkernel/intern/lib_override.cc | 35 +++ source/blender/blenkernel/intern/paint.cc | 73 ++++- source/blender/blenkernel/intern/scene.cc | 3 + .../blenloader/intern/versioning_280.cc | 14 +- .../blenloader/intern/versioning_defaults.cc | 6 +- .../blender/editors/asset/ED_asset_handle.h | 4 + .../editors/asset/intern/asset_handle.cc | 13 + .../editors/gpencil_legacy/gpencil_edit.cc | 12 +- .../editors/gpencil_legacy/gpencil_utils.cc | 2 +- .../editors/sculpt_paint/CMakeLists.txt | 4 + .../editors/sculpt_paint/curves_sculpt_ops.cc | 49 +++- .../sculpt_paint/grease_pencil_draw_ops.cc | 2 +- .../editors/sculpt_paint/paint_intern.hh | 3 +- .../editors/sculpt_paint/paint_vertex.cc | 11 +- .../editors/sculpt_paint/sculpt_ops.cc | 2 +- source/blender/makesdna/DNA_ID.h | 5 + source/blender/makesdna/DNA_scene_types.h | 7 + source/blender/makesrna/intern/rna_brush.cc | 27 +- .../blender/windowmanager/intern/wm_files.cc | 261 +++++++++++++++++- .../windowmanager/intern/wm_toolsystem.cc | 2 +- 27 files changed, 812 insertions(+), 75 deletions(-) diff --git a/scripts/presets/keyconfig/keymap_data/blender_default.py b/scripts/presets/keyconfig/keymap_data/blender_default.py index 8271499c6d9..cb6ba3d1c54 100644 --- a/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -6739,6 +6739,21 @@ def km_node_link_modal_map(_params): return keymap +def km_asset_shelf_brushes(_params): + items = [] + keymap = ( + "Asset Shelf", + {"space_type": 'EMPTY', "region_type": 'WINDOW'}, + {"items": items}, + ) + + items.extend([ + ("sculpt_curves.brush_asset_select", {"type": 'LEFTMOUSE', "value": 'CLICK'}, None), + ]) + + return keymap + + # Fallback for gizmos that don't have custom a custom key-map. def km_generic_gizmo(_params): keymap = ( @@ -8561,6 +8576,9 @@ def generate_keymaps(params=None): km_curve_pen_modal_map(params), km_node_link_modal_map(params), + # Asset Shelf Keymaps. + km_asset_shelf_brushes(params), + # Gizmos. km_generic_gizmo(params), km_generic_gizmo_drag(params), diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index 4ff9c3094fe..0bda0211fad 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -1075,6 +1075,19 @@ class VIEW3D_HT_header(Header): sub.popover(panel="VIEW3D_PT_shading", text="") +class VIEW3D_AST_brush_asset_shelf(bpy.types.AssetShelf): + bl_space_type = "VIEW_3D" + bl_options = {'NO_ASSET_DRAG'} + + @classmethod + def poll(cls, context): + return context.object and context.object.mode == 'SCULPT_CURVES' + + @classmethod + def asset_poll(cls, asset): + return asset.id_type == 'BRUSH' + + class VIEW3D_MT_editor_menus(Menu): bl_label = "" @@ -8656,6 +8669,7 @@ class VIEW3D_AST_sculpt_brushes(bpy.types.AssetShelf): classes = ( + VIEW3D_AST_brush_asset_shelf, VIEW3D_HT_header, VIEW3D_HT_tool_header, VIEW3D_MT_editor_menus, diff --git a/source/blender/blenkernel/BKE_brush.hh b/source/blender/blenkernel/BKE_brush.hh index 2a89dbef5f5..3809a225b43 100644 --- a/source/blender/blenkernel/BKE_brush.hh +++ b/source/blender/blenkernel/BKE_brush.hh @@ -31,6 +31,9 @@ struct UnifiedPaintSettings; void BKE_brush_system_init(); void BKE_brush_system_exit(); +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 f987052d20e..b38b8b2012d 100644 --- a/source/blender/blenkernel/BKE_lib_override.hh +++ b/source/blender/blenkernel/BKE_lib_override.hh @@ -82,6 +82,17 @@ 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 amount 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(struct Main *bmain, + const short id_type, + struct Library *library, + struct 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/BKE_paint.hh b/source/blender/blenkernel/BKE_paint.hh index 93cf9c39fb5..9e733eced14 100644 --- a/source/blender/blenkernel/BKE_paint.hh +++ b/source/blender/blenkernel/BKE_paint.hh @@ -27,6 +27,7 @@ #include "bmesh.h" +struct AssetWeakReference; struct BMFace; struct BMesh; struct BlendDataReader; @@ -166,7 +167,7 @@ PaintCurve *BKE_paint_curve_add(Main *bmain, const char *name); /** * Call when entering each respective paint mode. */ -bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint); +bool BKE_paint_ensure(Main *bmain, ToolSettings *ts, Paint **r_paint); void BKE_paint_init(Main *bmain, Scene *sce, ePaintMode mode, const uchar col[3]); void BKE_paint_free(Paint *p); /** @@ -181,7 +182,7 @@ void BKE_paint_runtime_init(const ToolSettings *ts, Paint *paint); void BKE_paint_cavity_curve_preset(Paint *p, int preset); eObjectMode BKE_paint_object_mode_from_paintmode(ePaintMode mode); -bool BKE_paint_ensure_from_paintmode(Scene *sce, ePaintMode mode); +bool BKE_paint_ensure_from_paintmode(Main *bmain, Scene *sce, ePaintMode mode); Paint *BKE_paint_get_active_from_paintmode(Scene *sce, ePaintMode mode); const EnumPropertyItem *BKE_paint_get_tool_enum_from_paintmode(ePaintMode mode); const char *BKE_paint_get_tool_enum_translation_context_from_paintmode(ePaintMode mode); @@ -194,6 +195,14 @@ ePaintMode BKE_paintmode_get_from_tool(const bToolRef *tref); Brush *BKE_paint_brush(Paint *paint); const Brush *BKE_paint_brush_for_read(const Paint *p); void BKE_paint_brush_set(Paint *paint, Brush *br); + +/** Set the active brush of given paint struct, and store the weak asset reference to it. + * NOTE: Takes ownership of the given `weak_asset_reference`. */ +void BKE_paint_brush_asset_set(Paint *p, Brush *br, AssetWeakReference *weak_asset_reference); + +/** Attempt to restore a valid active brush in `p` from brush asset informations stored in `p`. */ +void BKE_paint_brush_asset_restore(Main *bmain, Paint *p); + Palette *BKE_paint_palette(Paint *paint); void BKE_paint_palette_set(Paint *p, Palette *palette); void BKE_paint_curve_set(Brush *br, PaintCurve *pc); @@ -869,7 +878,7 @@ void BKE_sculpt_mask_layers_ensure(Depsgraph *depsgraph, Main *bmain, Object *ob, MultiresModifierData *mmd); -void BKE_sculpt_toolsettings_data_ensure(Scene *scene); +void BKE_sculpt_toolsettings_data_ensure(Main *bmain, Scene *scene); PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob); diff --git a/source/blender/blenkernel/intern/blendfile.cc b/source/blender/blenkernel/intern/blendfile.cc index e4d734cf6cd..b0b85e3b5d4 100644 --- a/source/blender/blenkernel/intern/blendfile.cc +++ b/source/blender/blenkernel/intern/blendfile.cc @@ -306,14 +306,195 @@ 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 BMain into new BMain, provided it does not conflict with data + * already present in the new BMain (name-wise and library-wise). + * + * Dependencies from moved over old data are also imported into the new BMain, (unless, in case of + * linked data, a matching linked ID is already available in new BMain). + * + * 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 BMain later. + * + * NOTE: This function will never remove any original new data from the new BMain, it only moves + * (some of) the old data to the new BMain. + */ +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_uuid_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. * * WARNING: Currently only expects to work on local data, won't work properly if some of the IDs of * given type are linked. * - * 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). */ + * 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). */ static void swap_old_bmain_data_for_blendfile(ReuseOldBMainData *reuse_data, const short id_code) { Main *new_bmain = reuse_data->new_bmain; @@ -720,6 +901,12 @@ 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 ce2bbb3dc1d..4e350207b8e 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -14,13 +14,17 @@ #include "DNA_material_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_space_types.h" #include "BLI_listbase.h" #include "BLI_math_rotation.h" #include "BLI_rand.h" +#include "BLO_readfile.h" + #include "BLT_translation.h" +#include "BKE_blendfile_link_append.h" #include "BKE_bpath.h" #include "BKE_brush.hh" #include "BKE_colortools.h" @@ -36,6 +40,8 @@ #include "BKE_preview_image.hh" #include "BKE_texture.h" +#include "AS_asset_library.h" + #include "IMB_colormanagement.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" @@ -376,39 +382,6 @@ static void brush_blend_read_after_liblink(BlendLibReader * /*reader*/, ID *id) } } -static int brush_undo_preserve_cb(LibraryIDLinkCallbackData *cb_data) -{ - BlendLibReader *reader = (BlendLibReader *)cb_data->user_data; - ID *self_id = cb_data->self_id; - ID *id_old = *cb_data->id_pointer; - /* Old data has not been remapped to new values of the pointers, if we want to keep the old - * pointer here we need its new address. */ - ID *id_old_new = id_old != nullptr ? BLO_read_get_new_id_address( - reader, self_id, ID_IS_LINKED(self_id), id_old) : - nullptr; - BLI_assert(id_old_new == nullptr || ELEM(id_old, id_old_new, id_old_new->orig_id)); - if (cb_data->cb_flag & IDWALK_CB_USER) { - id_us_plus_no_lib(id_old_new); - id_us_min(id_old); - } - *cb_data->id_pointer = id_old_new; - return IDWALK_RET_NOP; -} - -static void brush_undo_preserve(BlendLibReader *reader, ID *id_new, ID *id_old) -{ - /* Whole Brush is preserved across undo-steps. */ - BKE_lib_id_swap(nullptr, id_new, id_old, false, 0); - - /* `id_new` now has content from `id_old`, we need to ensure those old ID pointers are valid. - * NOTE: Since we want to re-use all old pointers here, code is much simpler than for Scene. */ - BKE_library_foreach_ID_link(nullptr, id_new, brush_undo_preserve_cb, reader, IDWALK_NOP); - - /* NOTE: We do not swap IDProperties, as dealing with potential ID pointers in those would be - * fairly delicate. */ - std::swap(id_new->properties, id_old->properties); -} - IDTypeInfo IDType_ID_BR = { /*id_code*/ ID_BR, /*id_filter*/ FILTER_ID_BR, @@ -417,7 +390,7 @@ IDTypeInfo IDType_ID_BR = { /*name*/ "Brush", /*name_plural*/ N_("brushes"), /*translation_context*/ BLT_I18NCONTEXT_ID_BRUSH, - /*flags*/ IDTYPE_FLAGS_NO_ANIMDATA, + /*flags*/ IDTYPE_FLAGS_NO_ANIMDATA | IDTYPE_FLAGS_NO_MEMFILE_UNDO, /*asset_type_info*/ nullptr, /*init_data*/ brush_init_data, @@ -433,7 +406,7 @@ IDTypeInfo IDType_ID_BR = { /*blend_read_data*/ brush_blend_read_data, /*blend_read_after_liblink*/ brush_blend_read_after_liblink, - /*blend_read_undo_preserve*/ brush_undo_preserve, + /*blend_read_undo_preserve*/ nullptr, /*lib_override_apply_post*/ nullptr, }; @@ -510,6 +483,63 @@ 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 = {nullptr}; + 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, + static_cast(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 084858891fc..7a4723a4b2e 100644 --- a/source/blender/blenkernel/intern/lib_override.cc +++ b/source/blender/blenkernel/intern/lib_override.cc @@ -362,6 +362,41 @@ 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 short 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)) { + /* 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 a9cd77934e1..cdbbe93112f 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -11,6 +11,7 @@ #include "MEM_guardedalloc.h" +#include "DNA_asset_types.h" #include "DNA_brush_types.h" #include "DNA_defaults.h" #include "DNA_gpencil_legacy_types.h" @@ -37,6 +38,7 @@ #include "BLT_translation.h" +#include "BKE_asset.hh" #include "BKE_attribute.h" #include "BKE_attribute.hh" #include "BKE_brush.hh" @@ -312,7 +314,7 @@ void BKE_paint_reset_overlay_invalid(ePaintOverlayControlFlags flag) overlay_flags &= ~(flag); } -bool BKE_paint_ensure_from_paintmode(Scene *sce, ePaintMode mode) +bool BKE_paint_ensure_from_paintmode(Main *bmain, Scene *sce, ePaintMode mode) { ToolSettings *ts = sce->toolsettings; Paint **paint_ptr = nullptr; @@ -357,7 +359,7 @@ bool BKE_paint_ensure_from_paintmode(Scene *sce, ePaintMode mode) break; } if (paint_ptr) { - BKE_paint_ensure(ts, paint_ptr); + BKE_paint_ensure(bmain, ts, paint_ptr); return true; } return false; @@ -671,6 +673,50 @@ void BKE_paint_brush_set(Paint *p, Brush *br) } } +static void paint_brush_asset_update(Paint *p, + Brush *br, + AssetWeakReference *brush_asset_reference) +{ + if (p->brush_asset_reference != nullptr) { + BKE_asset_weak_reference_free(&p->brush_asset_reference); + } + + if (br == nullptr || br != p->brush || !ID_IS_OVERRIDE_LIBRARY_REAL(p->brush) || + !(ID_IS_ASSET(p->brush) || ID_IS_ASSET(p->brush->id.override_library->reference))) + { + BKE_asset_weak_reference_free(&brush_asset_reference); + return; + } + + p->brush_asset_reference = brush_asset_reference; +} + +void BKE_paint_brush_asset_set(Paint *p, Brush *br, AssetWeakReference *weak_asset_reference) +{ + BKE_paint_brush_set(p, br); + paint_brush_asset_update(p, br, weak_asset_reference); +} + +void BKE_paint_brush_asset_restore(Main *bmain, Paint *p) +{ + if (p->brush != nullptr) { + return; + } + + if (p->brush_asset_reference == nullptr) { + return; + } + + AssetWeakReference *brush_asset_reference = p->brush_asset_reference; + p->brush_asset_reference = nullptr; + + Brush *brush_asset = BKE_brush_asset_runtime_ensure(bmain, brush_asset_reference); + + /* Will either re-assign the brush_asset_reference to `p`, or free it if loading a brush ID from + * it failed. */ + BKE_paint_brush_asset_set(p, brush_asset, brush_asset_reference); +} + void BKE_paint_runtime_init(const ToolSettings *ts, Paint *paint) { if (paint == &ts->imapaint.paint) { @@ -1096,7 +1142,7 @@ eObjectMode BKE_paint_object_mode_from_paintmode(ePaintMode mode) } } -bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint) +bool BKE_paint_ensure(Main *bmain, ToolSettings *ts, Paint **r_paint) { Paint *paint = nullptr; if (*r_paint) { @@ -1130,6 +1176,7 @@ bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint) BLI_assert(paint_test.runtime.tool_offset == (*r_paint)->runtime.tool_offset); #endif } + BKE_paint_brush_asset_restore(bmain, *r_paint); return true; } @@ -1177,6 +1224,7 @@ bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint) *r_paint = paint; BKE_paint_runtime_init(ts, paint); + BKE_paint_brush_asset_restore(bmain, paint); return false; } @@ -1186,7 +1234,7 @@ void BKE_paint_init(Main *bmain, Scene *sce, ePaintMode mode, const uchar col[3] UnifiedPaintSettings *ups = &sce->toolsettings->unified_paint_settings; Paint *paint = BKE_paint_get_active_from_paintmode(sce, mode); - BKE_paint_ensure_from_paintmode(sce, mode); + BKE_paint_ensure_from_paintmode(bmain, sce, mode); /* If there's no brush, create one */ if (PAINT_MODE_HAS_BRUSH(mode)) { @@ -1216,6 +1264,9 @@ void BKE_paint_free(Paint *paint) { BKE_curvemapping_free(paint->cavity_curve); MEM_SAFE_FREE(paint->tool_slots); + if (paint->brush_asset_reference != nullptr) { + BKE_asset_weak_reference_free(&paint->brush_asset_reference); + } } void BKE_paint_copy(Paint *src, Paint *tar, const int flag) @@ -1224,6 +1275,8 @@ void BKE_paint_copy(Paint *src, Paint *tar, const int flag) tar->cavity_curve = BKE_curvemapping_copy(src->cavity_curve); tar->tool_slots = static_cast(MEM_dupallocN(src->tool_slots)); + 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); @@ -1252,6 +1305,9 @@ void BKE_paint_blend_write(BlendWriter *writer, Paint *p) if (p->cavity_curve) { BKE_curvemapping_blend_write(writer, p->cavity_curve); } + if (p->brush_asset_reference) { + BKE_asset_weak_reference_write(writer, p->brush_asset_reference); + } BLO_write_struct_array(writer, PaintToolSlot, p->tool_slots_len, p->tool_slots); } @@ -1269,6 +1325,11 @@ void BKE_paint_blend_read_data(BlendDataReader *reader, const Scene *scene, Pain BKE_paint_cavity_curve_preset(p, CURVE_PRESET_LINE); } + BLO_read_data_address(reader, &p->brush_asset_reference); + if (p->brush_asset_reference) { + BKE_asset_weak_reference_read(reader, p->brush_asset_reference); + } + BLO_read_data_address(reader, &p->tool_slots); /* Workaround for invalid data written in older versions. */ @@ -2069,9 +2130,9 @@ void BKE_sculpt_mask_layers_ensure(Depsgraph *depsgraph, } } -void BKE_sculpt_toolsettings_data_ensure(Scene *scene) +void BKE_sculpt_toolsettings_data_ensure(Main *bmain, Scene *scene) { - BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->sculpt); + BKE_paint_ensure(bmain, scene->toolsettings, (Paint **)&scene->toolsettings->sculpt); Sculpt *sd = scene->toolsettings->sculpt; diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc index 44484d889a6..f00b393ff9f 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -1017,6 +1017,9 @@ static void scene_foreach_path(ID *id, BPathForeachPathData *bpath_data) if (scene->ed != nullptr) { SEQ_for_each_callback(&scene->ed->seqbase, seq_foreach_path_callback, bpath_data); } + + /* TODO: Also handle `asset_weak_reference` here? Probably not, conceptually this is not a file + * path. */ } static void scene_blend_write(BlendWriter *writer, ID *id, const void *id_address) diff --git a/source/blender/blenloader/intern/versioning_280.cc b/source/blender/blenloader/intern/versioning_280.cc index a18f8124b73..20c5d67205f 100644 --- a/source/blender/blenloader/intern/versioning_280.cc +++ b/source/blender/blenloader/intern/versioning_280.cc @@ -2881,10 +2881,10 @@ void do_versions_after_linking_280(FileData *fd, Main *bmain) ToolSettings *ts = scene->toolsettings; /* Ensure new Paint modes. */ - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_VERTEX_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_SCULPT_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_WEIGHT_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_VERTEX_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_SCULPT_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_WEIGHT_GPENCIL); /* Set default Draw brush. */ if (brush != nullptr) { @@ -2925,9 +2925,9 @@ void do_versions_after_linking_280(FileData *fd, Main *bmain) /* Reset all grease pencil brushes. */ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { /* Ensure new Paint modes. */ - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_VERTEX_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_SCULPT_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_WEIGHT_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_VERTEX_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_SCULPT_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_WEIGHT_GPENCIL); } } diff --git a/source/blender/blenloader/intern/versioning_defaults.cc b/source/blender/blenloader/intern/versioning_defaults.cc index 94507fd7bd3..325c9debba4 100644 --- a/source/blender/blenloader/intern/versioning_defaults.cc +++ b/source/blender/blenloader/intern/versioning_defaults.cc @@ -494,9 +494,9 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) } /* Ensure new Paint modes. */ - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_VERTEX_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_SCULPT_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_WEIGHT_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_VERTEX_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_SCULPT_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_WEIGHT_GPENCIL); /* Enable cursor. */ if (ts->gp_paint) { diff --git a/source/blender/editors/asset/ED_asset_handle.h b/source/blender/editors/asset/ED_asset_handle.h index 2506dc15ab6..69df2285a82 100644 --- a/source/blender/editors/asset/ED_asset_handle.h +++ b/source/blender/editors/asset/ED_asset_handle.h @@ -43,6 +43,10 @@ void ED_asset_handle_get_full_library_path( /* `1024` for #FILE_MAX, * rely on warnings to let us know if this gets out of sync. */ char r_full_lib_path[1024]); +void ED_asset_handle_get_full_path(const AssetHandle *asset_handle, + /* `1024 + 66` for #FILE_MAX_LIBEXTRA, + * rely on warnings to let us know if this gets out of sync. */ + char r_full_lib_path[1024 + 66]); #ifdef __cplusplus } diff --git a/source/blender/editors/asset/intern/asset_handle.cc b/source/blender/editors/asset/intern/asset_handle.cc index 684ef1a0b52..b072b815247 100644 --- a/source/blender/editors/asset/intern/asset_handle.cc +++ b/source/blender/editors/asset/intern/asset_handle.cc @@ -57,3 +57,16 @@ void ED_asset_handle_get_full_library_path(const AssetHandle *asset_handle, BLI_strncpy(r_full_lib_path, library_path.c_str(), FILE_MAX); } + +void ED_asset_handle_get_full_path(const AssetHandle *asset_handle, + char r_full_lib_path[FILE_MAX_LIBEXTRA]) +{ + *r_full_lib_path = '\0'; + + std::string library_path = asset_handle->file_data->asset->get_identifier().full_path(); + if (library_path.empty()) { + return; + } + + BLI_strncpy(r_full_lib_path, library_path.c_str(), FILE_MAX); +} diff --git a/source/blender/editors/gpencil_legacy/gpencil_edit.cc b/source/blender/editors/gpencil_legacy/gpencil_edit.cc index fc014a0a2ef..0d96e064b7f 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_edit.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_edit.cc @@ -396,8 +396,8 @@ static int gpencil_paintmode_toggle_exec(bContext *C, wmOperator *op) if (mode == OB_MODE_PAINT_GPENCIL_LEGACY) { /* Be sure we have brushes and Paint settings. * Need Draw and Vertex (used for Tint). */ - BKE_paint_ensure(ts, (Paint **)&ts->gp_paint); - BKE_paint_ensure(ts, (Paint **)&ts->gp_vertexpaint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_paint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_vertexpaint); BKE_brush_gpencil_paint_presets(bmain, ts, false); @@ -511,7 +511,7 @@ static int gpencil_sculptmode_toggle_exec(bContext *C, wmOperator *op) if (mode == OB_MODE_SCULPT_GPENCIL_LEGACY) { /* Be sure we have brushes. */ - BKE_paint_ensure(ts, (Paint **)&ts->gp_sculptpaint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_sculptpaint); const bool reset_mode = (ts->gp_sculptpaint->paint.brush == nullptr); BKE_brush_gpencil_sculpt_presets(bmain, ts, reset_mode); @@ -625,7 +625,7 @@ static int gpencil_weightmode_toggle_exec(bContext *C, wmOperator *op) if (mode == OB_MODE_WEIGHT_GPENCIL_LEGACY) { /* Be sure we have brushes. */ - BKE_paint_ensure(ts, (Paint **)&ts->gp_weightpaint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_weightpaint); const bool reset_mode = (ts->gp_weightpaint->paint.brush == nullptr); BKE_brush_gpencil_weight_presets(bmain, ts, reset_mode); @@ -732,8 +732,8 @@ static int gpencil_vertexmode_toggle_exec(bContext *C, wmOperator *op) if (mode == OB_MODE_VERTEX_GPENCIL_LEGACY) { /* Be sure we have brushes. * Need Draw as well (used for Palettes). */ - BKE_paint_ensure(ts, (Paint **)&ts->gp_paint); - BKE_paint_ensure(ts, (Paint **)&ts->gp_vertexpaint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_paint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_vertexpaint); const bool reset_mode = (ts->gp_vertexpaint->paint.brush == nullptr); BKE_brush_gpencil_vertex_presets(bmain, ts, reset_mode); diff --git a/source/blender/editors/gpencil_legacy/gpencil_utils.cc b/source/blender/editors/gpencil_legacy/gpencil_utils.cc index 84fea336e22..0eb3c7a765d 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_utils.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_utils.cc @@ -1428,7 +1428,7 @@ void ED_gpencil_add_defaults(bContext *C, Object *ob) Main *bmain = CTX_data_main(C); ToolSettings *ts = CTX_data_tool_settings(C); - BKE_paint_ensure(ts, (Paint **)&ts->gp_paint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_paint); Paint *paint = &ts->gp_paint->paint; /* if not exist, create a new one */ if ((paint->brush == nullptr) || (paint->brush->gpencil_settings == nullptr)) { diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index 071d2625ed3..bee957e9285 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -4,8 +4,12 @@ set(INC ../include + ../asset ../uvedit + ../../asset_system ../../blenkernel + ../../blenlib + ../../blenloader ../../blentranslation ../../bmesh ../../depsgraph diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc index e09340ad36e..767be7e86c4 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc @@ -8,6 +8,8 @@ #include "BLI_utildefines.h" #include "BLI_vector_set.hh" +#include "BKE_asset.hh" +#include "BKE_blendfile.h" #include "BKE_brush.hh" #include "BKE_bvhutils.hh" #include "BKE_context.hh" @@ -20,6 +22,7 @@ #include "WM_message.hh" #include "WM_toolsystem.h" +#include "ED_asset_handle.h" #include "ED_curves.hh" #include "ED_curves_sculpt.hh" #include "ED_image.hh" @@ -28,6 +31,8 @@ #include "ED_space_api.hh" #include "ED_view3d.hh" +#include "AS_asset_representation.hh" + #include "DEG_depsgraph.hh" #include "DEG_depsgraph_query.hh" @@ -283,7 +288,8 @@ static void curves_sculptmode_enter(bContext *C) wmMsgBus *mbus = CTX_wm_message_bus(C); Object *ob = CTX_data_active_object(C); - BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->curves_sculpt); + BKE_paint_ensure( + CTX_data_main(C), scene->toolsettings, (Paint **)&scene->toolsettings->curves_sculpt); CurvesSculpt *curves_sculpt = scene->toolsettings->curves_sculpt; ob->mode = OB_MODE_SCULPT_CURVES; @@ -1167,6 +1173,45 @@ static void SCULPT_CURVES_OT_min_distance_edit(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_DEPENDS_ON_CURSOR; } +/* -------------------------------------------------------------------- */ + +static int brush_asset_select_exec(bContext *C, wmOperator * /*op*/) +{ + blender::asset_system::AssetRepresentation *asset = CTX_wm_asset(C); + if (!asset) { + return OPERATOR_CANCELLED; + } + + AssetWeakReference *brush_asset_reference = asset->make_weak_reference(); + Brush *brush = BKE_brush_asset_runtime_ensure(CTX_data_main(C), brush_asset_reference); + + ToolSettings *tool_settings = CTX_data_tool_settings(C); + /* Either takes ownership of the brush_asset_reference, or frees it. */ + BKE_paint_brush_asset_set(&tool_settings->curves_sculpt->paint, brush, brush_asset_reference); + + return OPERATOR_FINISHED; +} + +/** + * 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. + */ +static void SCULPT_CURVES_OT_brush_asset_select(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Brush Asset"; + ot->description = "Select a brush asset as currently sculpt/paint tool - TESTING PURPOSE ONLY"; + ot->idname = "SCULPT_CURVES_OT_brush_asset_select"; + + /* api callbacks */ + ot->exec = brush_asset_select_exec; + ot->poll = CURVES_SCULPT_mode_poll; + + ot->prop = RNA_def_string( + ot->srna, "name", nullptr, MAX_NAME, "Brush Name", "name of the brush asset to select"); +} + } // namespace blender::ed::sculpt_paint /* -------------------------------------------------------------------- */ @@ -1181,6 +1226,8 @@ void ED_operatortypes_sculpt_curves() WM_operatortype_append(SCULPT_CURVES_OT_select_random); WM_operatortype_append(SCULPT_CURVES_OT_select_grow); WM_operatortype_append(SCULPT_CURVES_OT_min_distance_edit); + + WM_operatortype_append(SCULPT_CURVES_OT_brush_asset_select); } /** \} */ diff --git a/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc b/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc index 3785255ef11..c8232ecdcef 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc @@ -228,7 +228,7 @@ static void grease_pencil_draw_mode_enter(bContext *C) Object *ob = CTX_data_active_object(C); GpPaint *grease_pencil_paint = scene->toolsettings->gp_paint; - BKE_paint_ensure(scene->toolsettings, (Paint **)&grease_pencil_paint); + BKE_paint_ensure(CTX_data_main(C), scene->toolsettings, (Paint **)&grease_pencil_paint); ob->mode = OB_MODE_PAINT_GREASE_PENCIL; diff --git a/source/blender/editors/sculpt_paint/paint_intern.hh b/source/blender/editors/sculpt_paint/paint_intern.hh index 619633536a1..0685dcd5e0b 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.hh +++ b/source/blender/editors/sculpt_paint/paint_intern.hh @@ -553,7 +553,8 @@ void get_brush_alpha_data(const Scene *scene, void init_stroke(Depsgraph *depsgraph, Object *ob); void init_session_data(const ToolSettings *ts, Object *ob); -void init_session(Depsgraph *depsgraph, Scene *scene, Object *ob, eObjectMode object_mode); +void init_session( + Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob, eObjectMode object_mode); Vector pbvh_gather_generic(Object *ob, VPaint *wp, Sculpt *sd, Brush *brush); diff --git a/source/blender/editors/sculpt_paint/paint_vertex.cc b/source/blender/editors/sculpt_paint/paint_vertex.cc index 4c90f8e4fc3..e8086946204 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.cc +++ b/source/blender/editors/sculpt_paint/paint_vertex.cc @@ -209,10 +209,11 @@ void init_stroke(Depsgraph *depsgraph, Object *ob) } /* Toggle operator for turning vertex paint mode on or off (copied from sculpt.cc) */ -void init_session(Depsgraph *depsgraph, Scene *scene, Object *ob, eObjectMode object_mode) +void init_session( + Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob, eObjectMode object_mode) { /* Create persistent sculpt mode data */ - BKE_sculpt_toolsettings_data_ensure(scene); + BKE_sculpt_toolsettings_data_ensure(bmain, scene); BLI_assert(ob->sculpt == nullptr); ob->sculpt = MEM_new(__func__); @@ -336,7 +337,7 @@ void mode_enter_generic( const ePaintMode paint_mode = PAINT_MODE_VERTEX; ED_mesh_color_ensure(me, nullptr); - BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->vpaint); + BKE_paint_ensure(bmain, scene->toolsettings, (Paint **)&scene->toolsettings->vpaint); Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode); ED_paint_cursor_start(paint, vertex_paint_poll); BKE_paint_init(bmain, scene, paint_mode, PAINT_CURSOR_VERTEX_PAINT); @@ -344,7 +345,7 @@ void mode_enter_generic( else if (mode_flag == OB_MODE_WEIGHT_PAINT) { const ePaintMode paint_mode = PAINT_MODE_WEIGHT; - BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->wpaint); + BKE_paint_ensure(bmain, scene->toolsettings, (Paint **)&scene->toolsettings->wpaint); Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode); ED_paint_cursor_start(paint, weight_paint_poll); BKE_paint_init(bmain, scene, paint_mode, PAINT_CURSOR_WEIGHT_PAINT); @@ -366,7 +367,7 @@ void mode_enter_generic( BKE_sculptsession_free(ob); } - vwpaint::init_session(depsgraph, scene, ob, mode_flag); + vwpaint::init_session(bmain, depsgraph, scene, ob, mode_flag); /* Flush object mode. */ DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.cc b/source/blender/editors/sculpt_paint/sculpt_ops.cc index 5c70c21f1d9..500947f1da2 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/sculpt_ops.cc @@ -267,7 +267,7 @@ static void SCULPT_OT_symmetrize(wmOperatorType *ot) static void sculpt_init_session(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob) { /* Create persistent sculpt mode data. */ - BKE_sculpt_toolsettings_data_ensure(scene); + BKE_sculpt_toolsettings_data_ensure(bmain, scene); /* Create sculpt mode session data. */ if (ob->sculpt != nullptr) { diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 9ed5c7bbde6..8150b30494f 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -543,6 +543,11 @@ typedef struct Library { */ char filepath_abs[1024]; + /** + * Weak reference to the Brush Asset. + */ + struct AssetWeakReference *asset_repository_weak_reference; + /** Set for indirectly linked libraries, used in the outliner and while reading. */ struct Library *parent; diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index dcee0198981..ef0d47496ad 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -948,6 +948,13 @@ typedef struct PaintToolSlot { typedef struct Paint { struct Brush *brush; + /** + * A weak asset reference to the #brush, if not NULL. + * Used to attempt restoring the active brush from the AssetLibrary system, typically on + * file load. + */ + struct AssetWeakReference *brush_asset_reference; + /** * Each tool has its own active brush, * The currently active tool is defined by the current 'brush'. diff --git a/source/blender/makesrna/intern/rna_brush.cc b/source/blender/makesrna/intern/rna_brush.cc index 3bb64f8c779..704e620df09 100644 --- a/source/blender/makesrna/intern/rna_brush.cc +++ b/source/blender/makesrna/intern/rna_brush.cc @@ -770,7 +770,12 @@ static void rna_Brush_reset_icon(Brush *br) static void rna_Brush_update(Main * /*bmain*/, Scene * /*scene*/, PointerRNA *ptr) { - Brush *br = (Brush *)ptr->data; + Brush *br = (Brush *)ptr->owner_id; + + if (ID_IS_OVERRIDE_LIBRARY_REAL(&br->id)) { + br->id.tag |= LIB_TAG_LIBOVERRIDE_AUTOREFRESH; + } + WM_main_add_notifier(NC_BRUSH | NA_EDITED, br); // WM_main_add_notifier(NC_SPACE | ND_SPACE_VIEW3D, nullptr); } @@ -2146,14 +2151,18 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) RNA_def_struct_sdna(srna, "BrushCurvesSculptSettings"); RNA_def_struct_ui_text(srna, "Curves Sculpt Brush Settings", ""); + RNA_define_lib_overridable(true); + prop = RNA_def_property(srna, "add_amount", PROP_INT, PROP_NONE); RNA_def_property_range(prop, 1, INT32_MAX); RNA_def_property_ui_text(prop, "Count", "Number of curves added by the Add brush"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "points_per_curve", PROP_INT, PROP_NONE); RNA_def_property_range(prop, 2, INT32_MAX); RNA_def_property_ui_text( prop, "Points per Curve", "Number of control points in a newly added curve"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "scale_uniform", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, nullptr, "flag", BRUSH_CURVES_SCULPT_FLAG_SCALE_UNIFORM); @@ -2161,17 +2170,20 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) "Scale Uniform", "Grow or shrink curves by changing their size uniformly instead of " "using trimming or extrapolation"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "minimum_length", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_range(prop, 0.0f, FLT_MAX); RNA_def_property_ui_text( prop, "Minimum Length", "Avoid shrinking curves shorter than this length"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "interpolate_length", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna( prop, nullptr, "flag", BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH); RNA_def_property_ui_text( prop, "Interpolate Length", "Use length of the curves in close proximity"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "interpolate_point_count", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna( @@ -2179,11 +2191,13 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Interpolate Point Count", "Use the number of points from the curves in close proximity"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "interpolate_shape", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, nullptr, "flag", BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE); RNA_def_property_ui_text( prop, "Interpolate Shape", "Use shape of the curves in close proximity"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "curve_length", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_range(prop, 0.0, FLT_MAX); @@ -2191,28 +2205,35 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) prop, "Curve Length", "Length of newly added curves when it is not interpolated from other curves"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "minimum_distance", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_range(prop, 0.0f, FLT_MAX); RNA_def_property_ui_range(prop, 0.0, 1000.0f, 0.001, 2); RNA_def_property_ui_text( prop, "Minimum Distance", "Goal distance between curve roots for the Density brush"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "density_add_attempts", PROP_INT, PROP_NONE); RNA_def_property_range(prop, 0, INT32_MAX); RNA_def_property_ui_text( prop, "Density Add Attempts", "How many times the Density brush tries to add a new curve"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "density_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, density_mode_items); RNA_def_property_ui_text( prop, "Density Mode", "Determines whether the brush adds or removes curves"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "curve_parameter_falloff", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "CurveMapping"); RNA_def_property_ui_text(prop, "Curve Parameter Falloff", "Falloff that is applied from the tip to the root of each curve"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + RNA_define_lib_overridable(false); } static void rna_def_brush(BlenderRNA *brna) @@ -2560,6 +2581,8 @@ static void rna_def_brush(BlenderRNA *brna) srna, "Brush", "Brush data-block for storing brush settings for painting and sculpting"); RNA_def_struct_ui_icon(srna, ICON_BRUSH_DATA); + RNA_define_lib_overridable(true); + /* enums */ prop = RNA_def_property(srna, "blend", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, prop_blend_items); @@ -3867,6 +3890,8 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_struct_type(prop, "BrushCurvesSculptSettings"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Curves Sculpt Settings", ""); + + RNA_define_lib_overridable(false); } /** diff --git a/source/blender/windowmanager/intern/wm_files.cc b/source/blender/windowmanager/intern/wm_files.cc index bc7d176d8b3..bf4d511231d 100644 --- a/source/blender/windowmanager/intern/wm_files.cc +++ b/source/blender/windowmanager/intern/wm_files.cc @@ -145,6 +145,11 @@ 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"}; /* -------------------------------------------------------------------- */ @@ -2791,6 +2796,7 @@ 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, }; @@ -2808,7 +2814,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_OPEN); + set_next_operator_state(op, OPEN_MAINFILE_STATE_DISCARD_ASSET_DRAFTS); } if (wm_operator_close_file_dialog_if_needed(C, op, wm_open_mainfile_after_dialog_callback)) { @@ -2848,6 +2854,44 @@ 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]; @@ -2880,6 +2924,7 @@ 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}, }; @@ -4359,3 +4404,217 @@ 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_toolsystem.cc b/source/blender/windowmanager/intern/wm_toolsystem.cc index ff3d2ce44f7..570f78fede1 100644 --- a/source/blender/windowmanager/intern/wm_toolsystem.cc +++ b/source/blender/windowmanager/intern/wm_toolsystem.cc @@ -187,7 +187,7 @@ static void toolsystem_ref_link(bContext *C, WorkSpace *workspace, bToolRef *tre 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(scene, paint_mode); + 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) { -- 2.30.2 From b3964b1b0d3a6880bf0122989a7866298d917137 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 13 Jan 2023 14:32:12 +0100 Subject: [PATCH 005/244] Brush Assets: Add initial support for the new 'brush asset' usage. This commit mainly adds the low-level logic to link and create a runtime override for brushes. The biggest changes are in `setup_app_data` (post-loading of .blend file), to add support to move over some data from old bmain to newly loaded bmain. The logic is complex, and different depending on whether it is an undo (aka memfile read) step, or an actual .blend file reading: * On undo, we only port over brushes themselves, their dependencies are not handled. However, since memfile reading code already ported over linked IDs, these do not need to be swapped. * On real .blend file loading, brushes from the old bmain are being reused, as long as they do not conflict (name/lib) with brushes from the new bmain. Their dependencies are also re-used if needed. Only exception: local assets from old bmain are never re-used. There is also a new step added to the 'open file' process, which checks if the to-be-opened file is a library of the currently opened one, and if so, if there are local tweaked overrides of assets from the new file. The option to save these tweaks in draft is currently an empty mockup, but the rest is functional. Brush now has a new `AssetWeakReference` pointer to keep track of the last active brush asset, and restore it on fileread in case there is currently no active brush asset for the given paint/sculpt mode. Some parts of this commits are mockups/place-holders that are not expected to be committed as-is, if at all: * Some initial support for an asset-shelf (using code from `brush-asset-project` branch), very much WIP still. * Definition of test brush library with semi-hard-coded path. * There is also a drawing bug because the poll function of Brush panels return false initially (tool has no data-block ref). Very unclear what's hapening here. Ref. #101908. --- .../keyconfig/keymap_data/blender_default.py | 18 ++ scripts/startup/bl_ui/space_view3d.py | 14 + source/blender/blenkernel/BKE_brush.hh | 3 + source/blender/blenkernel/BKE_lib_override.hh | 11 + source/blender/blenkernel/BKE_paint.hh | 15 +- source/blender/blenkernel/intern/blendfile.cc | 191 ++++++++++++- source/blender/blenkernel/intern/brush.cc | 100 ++++--- .../blender/blenkernel/intern/lib_override.cc | 35 +++ source/blender/blenkernel/intern/paint.cc | 73 ++++- source/blender/blenkernel/intern/scene.cc | 3 + .../blenloader/intern/versioning_280.cc | 14 +- .../blenloader/intern/versioning_defaults.cc | 6 +- .../blender/editors/asset/ED_asset_handle.h | 4 + .../editors/asset/intern/asset_handle.cc | 13 + .../editors/gpencil_legacy/gpencil_edit.cc | 12 +- .../editors/gpencil_legacy/gpencil_utils.cc | 2 +- .../editors/sculpt_paint/CMakeLists.txt | 4 + .../editors/sculpt_paint/curves_sculpt_ops.cc | 49 +++- .../sculpt_paint/grease_pencil_draw_ops.cc | 2 +- .../editors/sculpt_paint/paint_intern.hh | 3 +- .../editors/sculpt_paint/paint_vertex.cc | 11 +- .../editors/sculpt_paint/sculpt_ops.cc | 2 +- source/blender/makesdna/DNA_ID.h | 5 + source/blender/makesdna/DNA_scene_types.h | 7 + source/blender/makesrna/intern/rna_brush.cc | 27 +- .../blender/windowmanager/intern/wm_files.cc | 261 +++++++++++++++++- .../windowmanager/intern/wm_toolsystem.cc | 2 +- 27 files changed, 812 insertions(+), 75 deletions(-) diff --git a/scripts/presets/keyconfig/keymap_data/blender_default.py b/scripts/presets/keyconfig/keymap_data/blender_default.py index 8271499c6d9..cb6ba3d1c54 100644 --- a/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -6739,6 +6739,21 @@ def km_node_link_modal_map(_params): return keymap +def km_asset_shelf_brushes(_params): + items = [] + keymap = ( + "Asset Shelf", + {"space_type": 'EMPTY', "region_type": 'WINDOW'}, + {"items": items}, + ) + + items.extend([ + ("sculpt_curves.brush_asset_select", {"type": 'LEFTMOUSE', "value": 'CLICK'}, None), + ]) + + return keymap + + # Fallback for gizmos that don't have custom a custom key-map. def km_generic_gizmo(_params): keymap = ( @@ -8561,6 +8576,9 @@ def generate_keymaps(params=None): km_curve_pen_modal_map(params), km_node_link_modal_map(params), + # Asset Shelf Keymaps. + km_asset_shelf_brushes(params), + # Gizmos. km_generic_gizmo(params), km_generic_gizmo_drag(params), diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index 4ff9c3094fe..0bda0211fad 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -1075,6 +1075,19 @@ class VIEW3D_HT_header(Header): sub.popover(panel="VIEW3D_PT_shading", text="") +class VIEW3D_AST_brush_asset_shelf(bpy.types.AssetShelf): + bl_space_type = "VIEW_3D" + bl_options = {'NO_ASSET_DRAG'} + + @classmethod + def poll(cls, context): + return context.object and context.object.mode == 'SCULPT_CURVES' + + @classmethod + def asset_poll(cls, asset): + return asset.id_type == 'BRUSH' + + class VIEW3D_MT_editor_menus(Menu): bl_label = "" @@ -8656,6 +8669,7 @@ class VIEW3D_AST_sculpt_brushes(bpy.types.AssetShelf): classes = ( + VIEW3D_AST_brush_asset_shelf, VIEW3D_HT_header, VIEW3D_HT_tool_header, VIEW3D_MT_editor_menus, diff --git a/source/blender/blenkernel/BKE_brush.hh b/source/blender/blenkernel/BKE_brush.hh index 2a89dbef5f5..3809a225b43 100644 --- a/source/blender/blenkernel/BKE_brush.hh +++ b/source/blender/blenkernel/BKE_brush.hh @@ -31,6 +31,9 @@ struct UnifiedPaintSettings; void BKE_brush_system_init(); void BKE_brush_system_exit(); +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 f987052d20e..b38b8b2012d 100644 --- a/source/blender/blenkernel/BKE_lib_override.hh +++ b/source/blender/blenkernel/BKE_lib_override.hh @@ -82,6 +82,17 @@ 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 amount 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(struct Main *bmain, + const short id_type, + struct Library *library, + struct 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/BKE_paint.hh b/source/blender/blenkernel/BKE_paint.hh index 93cf9c39fb5..9e733eced14 100644 --- a/source/blender/blenkernel/BKE_paint.hh +++ b/source/blender/blenkernel/BKE_paint.hh @@ -27,6 +27,7 @@ #include "bmesh.h" +struct AssetWeakReference; struct BMFace; struct BMesh; struct BlendDataReader; @@ -166,7 +167,7 @@ PaintCurve *BKE_paint_curve_add(Main *bmain, const char *name); /** * Call when entering each respective paint mode. */ -bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint); +bool BKE_paint_ensure(Main *bmain, ToolSettings *ts, Paint **r_paint); void BKE_paint_init(Main *bmain, Scene *sce, ePaintMode mode, const uchar col[3]); void BKE_paint_free(Paint *p); /** @@ -181,7 +182,7 @@ void BKE_paint_runtime_init(const ToolSettings *ts, Paint *paint); void BKE_paint_cavity_curve_preset(Paint *p, int preset); eObjectMode BKE_paint_object_mode_from_paintmode(ePaintMode mode); -bool BKE_paint_ensure_from_paintmode(Scene *sce, ePaintMode mode); +bool BKE_paint_ensure_from_paintmode(Main *bmain, Scene *sce, ePaintMode mode); Paint *BKE_paint_get_active_from_paintmode(Scene *sce, ePaintMode mode); const EnumPropertyItem *BKE_paint_get_tool_enum_from_paintmode(ePaintMode mode); const char *BKE_paint_get_tool_enum_translation_context_from_paintmode(ePaintMode mode); @@ -194,6 +195,14 @@ ePaintMode BKE_paintmode_get_from_tool(const bToolRef *tref); Brush *BKE_paint_brush(Paint *paint); const Brush *BKE_paint_brush_for_read(const Paint *p); void BKE_paint_brush_set(Paint *paint, Brush *br); + +/** Set the active brush of given paint struct, and store the weak asset reference to it. + * NOTE: Takes ownership of the given `weak_asset_reference`. */ +void BKE_paint_brush_asset_set(Paint *p, Brush *br, AssetWeakReference *weak_asset_reference); + +/** Attempt to restore a valid active brush in `p` from brush asset informations stored in `p`. */ +void BKE_paint_brush_asset_restore(Main *bmain, Paint *p); + Palette *BKE_paint_palette(Paint *paint); void BKE_paint_palette_set(Paint *p, Palette *palette); void BKE_paint_curve_set(Brush *br, PaintCurve *pc); @@ -869,7 +878,7 @@ void BKE_sculpt_mask_layers_ensure(Depsgraph *depsgraph, Main *bmain, Object *ob, MultiresModifierData *mmd); -void BKE_sculpt_toolsettings_data_ensure(Scene *scene); +void BKE_sculpt_toolsettings_data_ensure(Main *bmain, Scene *scene); PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob); diff --git a/source/blender/blenkernel/intern/blendfile.cc b/source/blender/blenkernel/intern/blendfile.cc index e4d734cf6cd..b0b85e3b5d4 100644 --- a/source/blender/blenkernel/intern/blendfile.cc +++ b/source/blender/blenkernel/intern/blendfile.cc @@ -306,14 +306,195 @@ 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 BMain into new BMain, provided it does not conflict with data + * already present in the new BMain (name-wise and library-wise). + * + * Dependencies from moved over old data are also imported into the new BMain, (unless, in case of + * linked data, a matching linked ID is already available in new BMain). + * + * 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 BMain later. + * + * NOTE: This function will never remove any original new data from the new BMain, it only moves + * (some of) the old data to the new BMain. + */ +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_uuid_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. * * WARNING: Currently only expects to work on local data, won't work properly if some of the IDs of * given type are linked. * - * 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). */ + * 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). */ static void swap_old_bmain_data_for_blendfile(ReuseOldBMainData *reuse_data, const short id_code) { Main *new_bmain = reuse_data->new_bmain; @@ -720,6 +901,12 @@ 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 ce2bbb3dc1d..4e350207b8e 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -14,13 +14,17 @@ #include "DNA_material_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_space_types.h" #include "BLI_listbase.h" #include "BLI_math_rotation.h" #include "BLI_rand.h" +#include "BLO_readfile.h" + #include "BLT_translation.h" +#include "BKE_blendfile_link_append.h" #include "BKE_bpath.h" #include "BKE_brush.hh" #include "BKE_colortools.h" @@ -36,6 +40,8 @@ #include "BKE_preview_image.hh" #include "BKE_texture.h" +#include "AS_asset_library.h" + #include "IMB_colormanagement.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" @@ -376,39 +382,6 @@ static void brush_blend_read_after_liblink(BlendLibReader * /*reader*/, ID *id) } } -static int brush_undo_preserve_cb(LibraryIDLinkCallbackData *cb_data) -{ - BlendLibReader *reader = (BlendLibReader *)cb_data->user_data; - ID *self_id = cb_data->self_id; - ID *id_old = *cb_data->id_pointer; - /* Old data has not been remapped to new values of the pointers, if we want to keep the old - * pointer here we need its new address. */ - ID *id_old_new = id_old != nullptr ? BLO_read_get_new_id_address( - reader, self_id, ID_IS_LINKED(self_id), id_old) : - nullptr; - BLI_assert(id_old_new == nullptr || ELEM(id_old, id_old_new, id_old_new->orig_id)); - if (cb_data->cb_flag & IDWALK_CB_USER) { - id_us_plus_no_lib(id_old_new); - id_us_min(id_old); - } - *cb_data->id_pointer = id_old_new; - return IDWALK_RET_NOP; -} - -static void brush_undo_preserve(BlendLibReader *reader, ID *id_new, ID *id_old) -{ - /* Whole Brush is preserved across undo-steps. */ - BKE_lib_id_swap(nullptr, id_new, id_old, false, 0); - - /* `id_new` now has content from `id_old`, we need to ensure those old ID pointers are valid. - * NOTE: Since we want to re-use all old pointers here, code is much simpler than for Scene. */ - BKE_library_foreach_ID_link(nullptr, id_new, brush_undo_preserve_cb, reader, IDWALK_NOP); - - /* NOTE: We do not swap IDProperties, as dealing with potential ID pointers in those would be - * fairly delicate. */ - std::swap(id_new->properties, id_old->properties); -} - IDTypeInfo IDType_ID_BR = { /*id_code*/ ID_BR, /*id_filter*/ FILTER_ID_BR, @@ -417,7 +390,7 @@ IDTypeInfo IDType_ID_BR = { /*name*/ "Brush", /*name_plural*/ N_("brushes"), /*translation_context*/ BLT_I18NCONTEXT_ID_BRUSH, - /*flags*/ IDTYPE_FLAGS_NO_ANIMDATA, + /*flags*/ IDTYPE_FLAGS_NO_ANIMDATA | IDTYPE_FLAGS_NO_MEMFILE_UNDO, /*asset_type_info*/ nullptr, /*init_data*/ brush_init_data, @@ -433,7 +406,7 @@ IDTypeInfo IDType_ID_BR = { /*blend_read_data*/ brush_blend_read_data, /*blend_read_after_liblink*/ brush_blend_read_after_liblink, - /*blend_read_undo_preserve*/ brush_undo_preserve, + /*blend_read_undo_preserve*/ nullptr, /*lib_override_apply_post*/ nullptr, }; @@ -510,6 +483,63 @@ 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 = {nullptr}; + 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, + static_cast(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 084858891fc..7a4723a4b2e 100644 --- a/source/blender/blenkernel/intern/lib_override.cc +++ b/source/blender/blenkernel/intern/lib_override.cc @@ -362,6 +362,41 @@ 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 short 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)) { + /* 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 a9cd77934e1..cdbbe93112f 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -11,6 +11,7 @@ #include "MEM_guardedalloc.h" +#include "DNA_asset_types.h" #include "DNA_brush_types.h" #include "DNA_defaults.h" #include "DNA_gpencil_legacy_types.h" @@ -37,6 +38,7 @@ #include "BLT_translation.h" +#include "BKE_asset.hh" #include "BKE_attribute.h" #include "BKE_attribute.hh" #include "BKE_brush.hh" @@ -312,7 +314,7 @@ void BKE_paint_reset_overlay_invalid(ePaintOverlayControlFlags flag) overlay_flags &= ~(flag); } -bool BKE_paint_ensure_from_paintmode(Scene *sce, ePaintMode mode) +bool BKE_paint_ensure_from_paintmode(Main *bmain, Scene *sce, ePaintMode mode) { ToolSettings *ts = sce->toolsettings; Paint **paint_ptr = nullptr; @@ -357,7 +359,7 @@ bool BKE_paint_ensure_from_paintmode(Scene *sce, ePaintMode mode) break; } if (paint_ptr) { - BKE_paint_ensure(ts, paint_ptr); + BKE_paint_ensure(bmain, ts, paint_ptr); return true; } return false; @@ -671,6 +673,50 @@ void BKE_paint_brush_set(Paint *p, Brush *br) } } +static void paint_brush_asset_update(Paint *p, + Brush *br, + AssetWeakReference *brush_asset_reference) +{ + if (p->brush_asset_reference != nullptr) { + BKE_asset_weak_reference_free(&p->brush_asset_reference); + } + + if (br == nullptr || br != p->brush || !ID_IS_OVERRIDE_LIBRARY_REAL(p->brush) || + !(ID_IS_ASSET(p->brush) || ID_IS_ASSET(p->brush->id.override_library->reference))) + { + BKE_asset_weak_reference_free(&brush_asset_reference); + return; + } + + p->brush_asset_reference = brush_asset_reference; +} + +void BKE_paint_brush_asset_set(Paint *p, Brush *br, AssetWeakReference *weak_asset_reference) +{ + BKE_paint_brush_set(p, br); + paint_brush_asset_update(p, br, weak_asset_reference); +} + +void BKE_paint_brush_asset_restore(Main *bmain, Paint *p) +{ + if (p->brush != nullptr) { + return; + } + + if (p->brush_asset_reference == nullptr) { + return; + } + + AssetWeakReference *brush_asset_reference = p->brush_asset_reference; + p->brush_asset_reference = nullptr; + + Brush *brush_asset = BKE_brush_asset_runtime_ensure(bmain, brush_asset_reference); + + /* Will either re-assign the brush_asset_reference to `p`, or free it if loading a brush ID from + * it failed. */ + BKE_paint_brush_asset_set(p, brush_asset, brush_asset_reference); +} + void BKE_paint_runtime_init(const ToolSettings *ts, Paint *paint) { if (paint == &ts->imapaint.paint) { @@ -1096,7 +1142,7 @@ eObjectMode BKE_paint_object_mode_from_paintmode(ePaintMode mode) } } -bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint) +bool BKE_paint_ensure(Main *bmain, ToolSettings *ts, Paint **r_paint) { Paint *paint = nullptr; if (*r_paint) { @@ -1130,6 +1176,7 @@ bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint) BLI_assert(paint_test.runtime.tool_offset == (*r_paint)->runtime.tool_offset); #endif } + BKE_paint_brush_asset_restore(bmain, *r_paint); return true; } @@ -1177,6 +1224,7 @@ bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint) *r_paint = paint; BKE_paint_runtime_init(ts, paint); + BKE_paint_brush_asset_restore(bmain, paint); return false; } @@ -1186,7 +1234,7 @@ void BKE_paint_init(Main *bmain, Scene *sce, ePaintMode mode, const uchar col[3] UnifiedPaintSettings *ups = &sce->toolsettings->unified_paint_settings; Paint *paint = BKE_paint_get_active_from_paintmode(sce, mode); - BKE_paint_ensure_from_paintmode(sce, mode); + BKE_paint_ensure_from_paintmode(bmain, sce, mode); /* If there's no brush, create one */ if (PAINT_MODE_HAS_BRUSH(mode)) { @@ -1216,6 +1264,9 @@ void BKE_paint_free(Paint *paint) { BKE_curvemapping_free(paint->cavity_curve); MEM_SAFE_FREE(paint->tool_slots); + if (paint->brush_asset_reference != nullptr) { + BKE_asset_weak_reference_free(&paint->brush_asset_reference); + } } void BKE_paint_copy(Paint *src, Paint *tar, const int flag) @@ -1224,6 +1275,8 @@ void BKE_paint_copy(Paint *src, Paint *tar, const int flag) tar->cavity_curve = BKE_curvemapping_copy(src->cavity_curve); tar->tool_slots = static_cast(MEM_dupallocN(src->tool_slots)); + 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); @@ -1252,6 +1305,9 @@ void BKE_paint_blend_write(BlendWriter *writer, Paint *p) if (p->cavity_curve) { BKE_curvemapping_blend_write(writer, p->cavity_curve); } + if (p->brush_asset_reference) { + BKE_asset_weak_reference_write(writer, p->brush_asset_reference); + } BLO_write_struct_array(writer, PaintToolSlot, p->tool_slots_len, p->tool_slots); } @@ -1269,6 +1325,11 @@ void BKE_paint_blend_read_data(BlendDataReader *reader, const Scene *scene, Pain BKE_paint_cavity_curve_preset(p, CURVE_PRESET_LINE); } + BLO_read_data_address(reader, &p->brush_asset_reference); + if (p->brush_asset_reference) { + BKE_asset_weak_reference_read(reader, p->brush_asset_reference); + } + BLO_read_data_address(reader, &p->tool_slots); /* Workaround for invalid data written in older versions. */ @@ -2069,9 +2130,9 @@ void BKE_sculpt_mask_layers_ensure(Depsgraph *depsgraph, } } -void BKE_sculpt_toolsettings_data_ensure(Scene *scene) +void BKE_sculpt_toolsettings_data_ensure(Main *bmain, Scene *scene) { - BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->sculpt); + BKE_paint_ensure(bmain, scene->toolsettings, (Paint **)&scene->toolsettings->sculpt); Sculpt *sd = scene->toolsettings->sculpt; diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc index 20aacd42a52..a32de96e475 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -1017,6 +1017,9 @@ static void scene_foreach_path(ID *id, BPathForeachPathData *bpath_data) if (scene->ed != nullptr) { SEQ_for_each_callback(&scene->ed->seqbase, seq_foreach_path_callback, bpath_data); } + + /* TODO: Also handle `asset_weak_reference` here? Probably not, conceptually this is not a file + * path. */ } static void scene_blend_write(BlendWriter *writer, ID *id, const void *id_address) diff --git a/source/blender/blenloader/intern/versioning_280.cc b/source/blender/blenloader/intern/versioning_280.cc index a18f8124b73..20c5d67205f 100644 --- a/source/blender/blenloader/intern/versioning_280.cc +++ b/source/blender/blenloader/intern/versioning_280.cc @@ -2881,10 +2881,10 @@ void do_versions_after_linking_280(FileData *fd, Main *bmain) ToolSettings *ts = scene->toolsettings; /* Ensure new Paint modes. */ - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_VERTEX_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_SCULPT_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_WEIGHT_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_VERTEX_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_SCULPT_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_WEIGHT_GPENCIL); /* Set default Draw brush. */ if (brush != nullptr) { @@ -2925,9 +2925,9 @@ void do_versions_after_linking_280(FileData *fd, Main *bmain) /* Reset all grease pencil brushes. */ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { /* Ensure new Paint modes. */ - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_VERTEX_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_SCULPT_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_WEIGHT_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_VERTEX_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_SCULPT_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_WEIGHT_GPENCIL); } } diff --git a/source/blender/blenloader/intern/versioning_defaults.cc b/source/blender/blenloader/intern/versioning_defaults.cc index 94507fd7bd3..325c9debba4 100644 --- a/source/blender/blenloader/intern/versioning_defaults.cc +++ b/source/blender/blenloader/intern/versioning_defaults.cc @@ -494,9 +494,9 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) } /* Ensure new Paint modes. */ - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_VERTEX_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_SCULPT_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_WEIGHT_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_VERTEX_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_SCULPT_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_WEIGHT_GPENCIL); /* Enable cursor. */ if (ts->gp_paint) { diff --git a/source/blender/editors/asset/ED_asset_handle.h b/source/blender/editors/asset/ED_asset_handle.h index 2506dc15ab6..69df2285a82 100644 --- a/source/blender/editors/asset/ED_asset_handle.h +++ b/source/blender/editors/asset/ED_asset_handle.h @@ -43,6 +43,10 @@ void ED_asset_handle_get_full_library_path( /* `1024` for #FILE_MAX, * rely on warnings to let us know if this gets out of sync. */ char r_full_lib_path[1024]); +void ED_asset_handle_get_full_path(const AssetHandle *asset_handle, + /* `1024 + 66` for #FILE_MAX_LIBEXTRA, + * rely on warnings to let us know if this gets out of sync. */ + char r_full_lib_path[1024 + 66]); #ifdef __cplusplus } diff --git a/source/blender/editors/asset/intern/asset_handle.cc b/source/blender/editors/asset/intern/asset_handle.cc index 684ef1a0b52..b072b815247 100644 --- a/source/blender/editors/asset/intern/asset_handle.cc +++ b/source/blender/editors/asset/intern/asset_handle.cc @@ -57,3 +57,16 @@ void ED_asset_handle_get_full_library_path(const AssetHandle *asset_handle, BLI_strncpy(r_full_lib_path, library_path.c_str(), FILE_MAX); } + +void ED_asset_handle_get_full_path(const AssetHandle *asset_handle, + char r_full_lib_path[FILE_MAX_LIBEXTRA]) +{ + *r_full_lib_path = '\0'; + + std::string library_path = asset_handle->file_data->asset->get_identifier().full_path(); + if (library_path.empty()) { + return; + } + + BLI_strncpy(r_full_lib_path, library_path.c_str(), FILE_MAX); +} diff --git a/source/blender/editors/gpencil_legacy/gpencil_edit.cc b/source/blender/editors/gpencil_legacy/gpencil_edit.cc index fc014a0a2ef..0d96e064b7f 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_edit.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_edit.cc @@ -396,8 +396,8 @@ static int gpencil_paintmode_toggle_exec(bContext *C, wmOperator *op) if (mode == OB_MODE_PAINT_GPENCIL_LEGACY) { /* Be sure we have brushes and Paint settings. * Need Draw and Vertex (used for Tint). */ - BKE_paint_ensure(ts, (Paint **)&ts->gp_paint); - BKE_paint_ensure(ts, (Paint **)&ts->gp_vertexpaint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_paint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_vertexpaint); BKE_brush_gpencil_paint_presets(bmain, ts, false); @@ -511,7 +511,7 @@ static int gpencil_sculptmode_toggle_exec(bContext *C, wmOperator *op) if (mode == OB_MODE_SCULPT_GPENCIL_LEGACY) { /* Be sure we have brushes. */ - BKE_paint_ensure(ts, (Paint **)&ts->gp_sculptpaint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_sculptpaint); const bool reset_mode = (ts->gp_sculptpaint->paint.brush == nullptr); BKE_brush_gpencil_sculpt_presets(bmain, ts, reset_mode); @@ -625,7 +625,7 @@ static int gpencil_weightmode_toggle_exec(bContext *C, wmOperator *op) if (mode == OB_MODE_WEIGHT_GPENCIL_LEGACY) { /* Be sure we have brushes. */ - BKE_paint_ensure(ts, (Paint **)&ts->gp_weightpaint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_weightpaint); const bool reset_mode = (ts->gp_weightpaint->paint.brush == nullptr); BKE_brush_gpencil_weight_presets(bmain, ts, reset_mode); @@ -732,8 +732,8 @@ static int gpencil_vertexmode_toggle_exec(bContext *C, wmOperator *op) if (mode == OB_MODE_VERTEX_GPENCIL_LEGACY) { /* Be sure we have brushes. * Need Draw as well (used for Palettes). */ - BKE_paint_ensure(ts, (Paint **)&ts->gp_paint); - BKE_paint_ensure(ts, (Paint **)&ts->gp_vertexpaint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_paint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_vertexpaint); const bool reset_mode = (ts->gp_vertexpaint->paint.brush == nullptr); BKE_brush_gpencil_vertex_presets(bmain, ts, reset_mode); diff --git a/source/blender/editors/gpencil_legacy/gpencil_utils.cc b/source/blender/editors/gpencil_legacy/gpencil_utils.cc index 84fea336e22..0eb3c7a765d 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_utils.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_utils.cc @@ -1428,7 +1428,7 @@ void ED_gpencil_add_defaults(bContext *C, Object *ob) Main *bmain = CTX_data_main(C); ToolSettings *ts = CTX_data_tool_settings(C); - BKE_paint_ensure(ts, (Paint **)&ts->gp_paint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_paint); Paint *paint = &ts->gp_paint->paint; /* if not exist, create a new one */ if ((paint->brush == nullptr) || (paint->brush->gpencil_settings == nullptr)) { diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index 071d2625ed3..bee957e9285 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -4,8 +4,12 @@ set(INC ../include + ../asset ../uvedit + ../../asset_system ../../blenkernel + ../../blenlib + ../../blenloader ../../blentranslation ../../bmesh ../../depsgraph diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc index e09340ad36e..767be7e86c4 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc @@ -8,6 +8,8 @@ #include "BLI_utildefines.h" #include "BLI_vector_set.hh" +#include "BKE_asset.hh" +#include "BKE_blendfile.h" #include "BKE_brush.hh" #include "BKE_bvhutils.hh" #include "BKE_context.hh" @@ -20,6 +22,7 @@ #include "WM_message.hh" #include "WM_toolsystem.h" +#include "ED_asset_handle.h" #include "ED_curves.hh" #include "ED_curves_sculpt.hh" #include "ED_image.hh" @@ -28,6 +31,8 @@ #include "ED_space_api.hh" #include "ED_view3d.hh" +#include "AS_asset_representation.hh" + #include "DEG_depsgraph.hh" #include "DEG_depsgraph_query.hh" @@ -283,7 +288,8 @@ static void curves_sculptmode_enter(bContext *C) wmMsgBus *mbus = CTX_wm_message_bus(C); Object *ob = CTX_data_active_object(C); - BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->curves_sculpt); + BKE_paint_ensure( + CTX_data_main(C), scene->toolsettings, (Paint **)&scene->toolsettings->curves_sculpt); CurvesSculpt *curves_sculpt = scene->toolsettings->curves_sculpt; ob->mode = OB_MODE_SCULPT_CURVES; @@ -1167,6 +1173,45 @@ static void SCULPT_CURVES_OT_min_distance_edit(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_DEPENDS_ON_CURSOR; } +/* -------------------------------------------------------------------- */ + +static int brush_asset_select_exec(bContext *C, wmOperator * /*op*/) +{ + blender::asset_system::AssetRepresentation *asset = CTX_wm_asset(C); + if (!asset) { + return OPERATOR_CANCELLED; + } + + AssetWeakReference *brush_asset_reference = asset->make_weak_reference(); + Brush *brush = BKE_brush_asset_runtime_ensure(CTX_data_main(C), brush_asset_reference); + + ToolSettings *tool_settings = CTX_data_tool_settings(C); + /* Either takes ownership of the brush_asset_reference, or frees it. */ + BKE_paint_brush_asset_set(&tool_settings->curves_sculpt->paint, brush, brush_asset_reference); + + return OPERATOR_FINISHED; +} + +/** + * 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. + */ +static void SCULPT_CURVES_OT_brush_asset_select(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Brush Asset"; + ot->description = "Select a brush asset as currently sculpt/paint tool - TESTING PURPOSE ONLY"; + ot->idname = "SCULPT_CURVES_OT_brush_asset_select"; + + /* api callbacks */ + ot->exec = brush_asset_select_exec; + ot->poll = CURVES_SCULPT_mode_poll; + + ot->prop = RNA_def_string( + ot->srna, "name", nullptr, MAX_NAME, "Brush Name", "name of the brush asset to select"); +} + } // namespace blender::ed::sculpt_paint /* -------------------------------------------------------------------- */ @@ -1181,6 +1226,8 @@ void ED_operatortypes_sculpt_curves() WM_operatortype_append(SCULPT_CURVES_OT_select_random); WM_operatortype_append(SCULPT_CURVES_OT_select_grow); WM_operatortype_append(SCULPT_CURVES_OT_min_distance_edit); + + WM_operatortype_append(SCULPT_CURVES_OT_brush_asset_select); } /** \} */ diff --git a/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc b/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc index 3785255ef11..c8232ecdcef 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc @@ -228,7 +228,7 @@ static void grease_pencil_draw_mode_enter(bContext *C) Object *ob = CTX_data_active_object(C); GpPaint *grease_pencil_paint = scene->toolsettings->gp_paint; - BKE_paint_ensure(scene->toolsettings, (Paint **)&grease_pencil_paint); + BKE_paint_ensure(CTX_data_main(C), scene->toolsettings, (Paint **)&grease_pencil_paint); ob->mode = OB_MODE_PAINT_GREASE_PENCIL; diff --git a/source/blender/editors/sculpt_paint/paint_intern.hh b/source/blender/editors/sculpt_paint/paint_intern.hh index 619633536a1..0685dcd5e0b 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.hh +++ b/source/blender/editors/sculpt_paint/paint_intern.hh @@ -553,7 +553,8 @@ void get_brush_alpha_data(const Scene *scene, void init_stroke(Depsgraph *depsgraph, Object *ob); void init_session_data(const ToolSettings *ts, Object *ob); -void init_session(Depsgraph *depsgraph, Scene *scene, Object *ob, eObjectMode object_mode); +void init_session( + Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob, eObjectMode object_mode); Vector pbvh_gather_generic(Object *ob, VPaint *wp, Sculpt *sd, Brush *brush); diff --git a/source/blender/editors/sculpt_paint/paint_vertex.cc b/source/blender/editors/sculpt_paint/paint_vertex.cc index 4c90f8e4fc3..e8086946204 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.cc +++ b/source/blender/editors/sculpt_paint/paint_vertex.cc @@ -209,10 +209,11 @@ void init_stroke(Depsgraph *depsgraph, Object *ob) } /* Toggle operator for turning vertex paint mode on or off (copied from sculpt.cc) */ -void init_session(Depsgraph *depsgraph, Scene *scene, Object *ob, eObjectMode object_mode) +void init_session( + Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob, eObjectMode object_mode) { /* Create persistent sculpt mode data */ - BKE_sculpt_toolsettings_data_ensure(scene); + BKE_sculpt_toolsettings_data_ensure(bmain, scene); BLI_assert(ob->sculpt == nullptr); ob->sculpt = MEM_new(__func__); @@ -336,7 +337,7 @@ void mode_enter_generic( const ePaintMode paint_mode = PAINT_MODE_VERTEX; ED_mesh_color_ensure(me, nullptr); - BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->vpaint); + BKE_paint_ensure(bmain, scene->toolsettings, (Paint **)&scene->toolsettings->vpaint); Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode); ED_paint_cursor_start(paint, vertex_paint_poll); BKE_paint_init(bmain, scene, paint_mode, PAINT_CURSOR_VERTEX_PAINT); @@ -344,7 +345,7 @@ void mode_enter_generic( else if (mode_flag == OB_MODE_WEIGHT_PAINT) { const ePaintMode paint_mode = PAINT_MODE_WEIGHT; - BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->wpaint); + BKE_paint_ensure(bmain, scene->toolsettings, (Paint **)&scene->toolsettings->wpaint); Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode); ED_paint_cursor_start(paint, weight_paint_poll); BKE_paint_init(bmain, scene, paint_mode, PAINT_CURSOR_WEIGHT_PAINT); @@ -366,7 +367,7 @@ void mode_enter_generic( BKE_sculptsession_free(ob); } - vwpaint::init_session(depsgraph, scene, ob, mode_flag); + vwpaint::init_session(bmain, depsgraph, scene, ob, mode_flag); /* Flush object mode. */ DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.cc b/source/blender/editors/sculpt_paint/sculpt_ops.cc index 5c70c21f1d9..500947f1da2 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/sculpt_ops.cc @@ -267,7 +267,7 @@ static void SCULPT_OT_symmetrize(wmOperatorType *ot) static void sculpt_init_session(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob) { /* Create persistent sculpt mode data. */ - BKE_sculpt_toolsettings_data_ensure(scene); + BKE_sculpt_toolsettings_data_ensure(bmain, scene); /* Create sculpt mode session data. */ if (ob->sculpt != nullptr) { diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 9ed5c7bbde6..8150b30494f 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -543,6 +543,11 @@ typedef struct Library { */ char filepath_abs[1024]; + /** + * Weak reference to the Brush Asset. + */ + struct AssetWeakReference *asset_repository_weak_reference; + /** Set for indirectly linked libraries, used in the outliner and while reading. */ struct Library *parent; diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index dcee0198981..ef0d47496ad 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -948,6 +948,13 @@ typedef struct PaintToolSlot { typedef struct Paint { struct Brush *brush; + /** + * A weak asset reference to the #brush, if not NULL. + * Used to attempt restoring the active brush from the AssetLibrary system, typically on + * file load. + */ + struct AssetWeakReference *brush_asset_reference; + /** * Each tool has its own active brush, * The currently active tool is defined by the current 'brush'. diff --git a/source/blender/makesrna/intern/rna_brush.cc b/source/blender/makesrna/intern/rna_brush.cc index 3bb64f8c779..704e620df09 100644 --- a/source/blender/makesrna/intern/rna_brush.cc +++ b/source/blender/makesrna/intern/rna_brush.cc @@ -770,7 +770,12 @@ static void rna_Brush_reset_icon(Brush *br) static void rna_Brush_update(Main * /*bmain*/, Scene * /*scene*/, PointerRNA *ptr) { - Brush *br = (Brush *)ptr->data; + Brush *br = (Brush *)ptr->owner_id; + + if (ID_IS_OVERRIDE_LIBRARY_REAL(&br->id)) { + br->id.tag |= LIB_TAG_LIBOVERRIDE_AUTOREFRESH; + } + WM_main_add_notifier(NC_BRUSH | NA_EDITED, br); // WM_main_add_notifier(NC_SPACE | ND_SPACE_VIEW3D, nullptr); } @@ -2146,14 +2151,18 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) RNA_def_struct_sdna(srna, "BrushCurvesSculptSettings"); RNA_def_struct_ui_text(srna, "Curves Sculpt Brush Settings", ""); + RNA_define_lib_overridable(true); + prop = RNA_def_property(srna, "add_amount", PROP_INT, PROP_NONE); RNA_def_property_range(prop, 1, INT32_MAX); RNA_def_property_ui_text(prop, "Count", "Number of curves added by the Add brush"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "points_per_curve", PROP_INT, PROP_NONE); RNA_def_property_range(prop, 2, INT32_MAX); RNA_def_property_ui_text( prop, "Points per Curve", "Number of control points in a newly added curve"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "scale_uniform", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, nullptr, "flag", BRUSH_CURVES_SCULPT_FLAG_SCALE_UNIFORM); @@ -2161,17 +2170,20 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) "Scale Uniform", "Grow or shrink curves by changing their size uniformly instead of " "using trimming or extrapolation"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "minimum_length", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_range(prop, 0.0f, FLT_MAX); RNA_def_property_ui_text( prop, "Minimum Length", "Avoid shrinking curves shorter than this length"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "interpolate_length", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna( prop, nullptr, "flag", BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH); RNA_def_property_ui_text( prop, "Interpolate Length", "Use length of the curves in close proximity"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "interpolate_point_count", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna( @@ -2179,11 +2191,13 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Interpolate Point Count", "Use the number of points from the curves in close proximity"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "interpolate_shape", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, nullptr, "flag", BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE); RNA_def_property_ui_text( prop, "Interpolate Shape", "Use shape of the curves in close proximity"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "curve_length", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_range(prop, 0.0, FLT_MAX); @@ -2191,28 +2205,35 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) prop, "Curve Length", "Length of newly added curves when it is not interpolated from other curves"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "minimum_distance", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_range(prop, 0.0f, FLT_MAX); RNA_def_property_ui_range(prop, 0.0, 1000.0f, 0.001, 2); RNA_def_property_ui_text( prop, "Minimum Distance", "Goal distance between curve roots for the Density brush"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "density_add_attempts", PROP_INT, PROP_NONE); RNA_def_property_range(prop, 0, INT32_MAX); RNA_def_property_ui_text( prop, "Density Add Attempts", "How many times the Density brush tries to add a new curve"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "density_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, density_mode_items); RNA_def_property_ui_text( prop, "Density Mode", "Determines whether the brush adds or removes curves"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "curve_parameter_falloff", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "CurveMapping"); RNA_def_property_ui_text(prop, "Curve Parameter Falloff", "Falloff that is applied from the tip to the root of each curve"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + RNA_define_lib_overridable(false); } static void rna_def_brush(BlenderRNA *brna) @@ -2560,6 +2581,8 @@ static void rna_def_brush(BlenderRNA *brna) srna, "Brush", "Brush data-block for storing brush settings for painting and sculpting"); RNA_def_struct_ui_icon(srna, ICON_BRUSH_DATA); + RNA_define_lib_overridable(true); + /* enums */ prop = RNA_def_property(srna, "blend", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, prop_blend_items); @@ -3867,6 +3890,8 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_struct_type(prop, "BrushCurvesSculptSettings"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Curves Sculpt Settings", ""); + + RNA_define_lib_overridable(false); } /** diff --git a/source/blender/windowmanager/intern/wm_files.cc b/source/blender/windowmanager/intern/wm_files.cc index bc7d176d8b3..bf4d511231d 100644 --- a/source/blender/windowmanager/intern/wm_files.cc +++ b/source/blender/windowmanager/intern/wm_files.cc @@ -145,6 +145,11 @@ 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"}; /* -------------------------------------------------------------------- */ @@ -2791,6 +2796,7 @@ 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, }; @@ -2808,7 +2814,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_OPEN); + set_next_operator_state(op, OPEN_MAINFILE_STATE_DISCARD_ASSET_DRAFTS); } if (wm_operator_close_file_dialog_if_needed(C, op, wm_open_mainfile_after_dialog_callback)) { @@ -2848,6 +2854,44 @@ 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]; @@ -2880,6 +2924,7 @@ 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}, }; @@ -4359,3 +4404,217 @@ 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_toolsystem.cc b/source/blender/windowmanager/intern/wm_toolsystem.cc index ff3d2ce44f7..570f78fede1 100644 --- a/source/blender/windowmanager/intern/wm_toolsystem.cc +++ b/source/blender/windowmanager/intern/wm_toolsystem.cc @@ -187,7 +187,7 @@ static void toolsystem_ref_link(bContext *C, WorkSpace *workspace, bToolRef *tre 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(scene, paint_mode); + 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) { -- 2.30.2 From eb505ad1c77d6dce8371eb886fdaede51c343cb1 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 13 Jan 2023 14:32:12 +0100 Subject: [PATCH 006/244] Brush Assets: Add initial support for the new 'brush asset' usage. This commit mainly adds the low-level logic to link and create a runtime override for brushes. The biggest changes are in `setup_app_data` (post-loading of .blend file), to add support to move over some data from old bmain to newly loaded bmain. The logic is complex, and different depending on whether it is an undo (aka memfile read) step, or an actual .blend file reading: * On undo, we only port over brushes themselves, their dependencies are not handled. However, since memfile reading code already ported over linked IDs, these do not need to be swapped. * On real .blend file loading, brushes from the old bmain are being reused, as long as they do not conflict (name/lib) with brushes from the new bmain. Their dependencies are also re-used if needed. Only exception: local assets from old bmain are never re-used. There is also a new step added to the 'open file' process, which checks if the to-be-opened file is a library of the currently opened one, and if so, if there are local tweaked overrides of assets from the new file. The option to save these tweaks in draft is currently an empty mockup, but the rest is functional. Brush now has a new `AssetWeakReference` pointer to keep track of the last active brush asset, and restore it on fileread in case there is currently no active brush asset for the given paint/sculpt mode. Some parts of this commits are mockups/place-holders that are not expected to be committed as-is, if at all: * Some initial support for an asset-shelf (using code from `brush-asset-project` branch), very much WIP still. * Definition of test brush library with semi-hard-coded path. * There is also a drawing bug because the poll function of Brush panels return false initially (tool has no data-block ref). Very unclear what's hapening here. Ref. #101908. --- .../keyconfig/keymap_data/blender_default.py | 18 ++ scripts/startup/bl_ui/space_view3d.py | 14 + source/blender/blenkernel/BKE_brush.hh | 3 + source/blender/blenkernel/BKE_lib_override.hh | 11 + source/blender/blenkernel/BKE_paint.hh | 15 +- source/blender/blenkernel/intern/blendfile.cc | 191 ++++++++++++- source/blender/blenkernel/intern/brush.cc | 100 ++++--- .../blender/blenkernel/intern/lib_override.cc | 35 +++ source/blender/blenkernel/intern/paint.cc | 73 ++++- source/blender/blenkernel/intern/scene.cc | 3 + .../blenloader/intern/versioning_280.cc | 14 +- .../blenloader/intern/versioning_defaults.cc | 6 +- .../blender/editors/asset/ED_asset_handle.h | 4 + .../editors/asset/intern/asset_handle.cc | 13 + .../editors/gpencil_legacy/gpencil_edit.cc | 12 +- .../editors/gpencil_legacy/gpencil_utils.cc | 2 +- .../editors/sculpt_paint/CMakeLists.txt | 4 + .../editors/sculpt_paint/curves_sculpt_ops.cc | 49 +++- .../sculpt_paint/grease_pencil_draw_ops.cc | 2 +- .../editors/sculpt_paint/paint_intern.hh | 3 +- .../editors/sculpt_paint/paint_vertex.cc | 11 +- .../editors/sculpt_paint/sculpt_ops.cc | 2 +- source/blender/makesdna/DNA_scene_types.h | 7 + source/blender/makesrna/intern/rna_brush.cc | 27 +- .../makesrna/intern/rna_sculpt_paint.cc | 7 + .../blender/windowmanager/intern/wm_files.cc | 261 +++++++++++++++++- .../windowmanager/intern/wm_toolsystem.cc | 2 +- 27 files changed, 814 insertions(+), 75 deletions(-) diff --git a/scripts/presets/keyconfig/keymap_data/blender_default.py b/scripts/presets/keyconfig/keymap_data/blender_default.py index 8271499c6d9..cb6ba3d1c54 100644 --- a/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -6739,6 +6739,21 @@ def km_node_link_modal_map(_params): return keymap +def km_asset_shelf_brushes(_params): + items = [] + keymap = ( + "Asset Shelf", + {"space_type": 'EMPTY', "region_type": 'WINDOW'}, + {"items": items}, + ) + + items.extend([ + ("sculpt_curves.brush_asset_select", {"type": 'LEFTMOUSE', "value": 'CLICK'}, None), + ]) + + return keymap + + # Fallback for gizmos that don't have custom a custom key-map. def km_generic_gizmo(_params): keymap = ( @@ -8561,6 +8576,9 @@ def generate_keymaps(params=None): km_curve_pen_modal_map(params), km_node_link_modal_map(params), + # Asset Shelf Keymaps. + km_asset_shelf_brushes(params), + # Gizmos. km_generic_gizmo(params), km_generic_gizmo_drag(params), diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index 4ff9c3094fe..0bda0211fad 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -1075,6 +1075,19 @@ class VIEW3D_HT_header(Header): sub.popover(panel="VIEW3D_PT_shading", text="") +class VIEW3D_AST_brush_asset_shelf(bpy.types.AssetShelf): + bl_space_type = "VIEW_3D" + bl_options = {'NO_ASSET_DRAG'} + + @classmethod + def poll(cls, context): + return context.object and context.object.mode == 'SCULPT_CURVES' + + @classmethod + def asset_poll(cls, asset): + return asset.id_type == 'BRUSH' + + class VIEW3D_MT_editor_menus(Menu): bl_label = "" @@ -8656,6 +8669,7 @@ class VIEW3D_AST_sculpt_brushes(bpy.types.AssetShelf): classes = ( + VIEW3D_AST_brush_asset_shelf, VIEW3D_HT_header, VIEW3D_HT_tool_header, VIEW3D_MT_editor_menus, diff --git a/source/blender/blenkernel/BKE_brush.hh b/source/blender/blenkernel/BKE_brush.hh index 2a89dbef5f5..3809a225b43 100644 --- a/source/blender/blenkernel/BKE_brush.hh +++ b/source/blender/blenkernel/BKE_brush.hh @@ -31,6 +31,9 @@ struct UnifiedPaintSettings; void BKE_brush_system_init(); void BKE_brush_system_exit(); +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 cf9e752abcb..3d9306f9035 100644 --- a/source/blender/blenkernel/BKE_lib_override.hh +++ b/source/blender/blenkernel/BKE_lib_override.hh @@ -82,6 +82,17 @@ 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 amount 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(struct Main *bmain, + const short id_type, + struct Library *library, + struct 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/BKE_paint.hh b/source/blender/blenkernel/BKE_paint.hh index 93cf9c39fb5..9e733eced14 100644 --- a/source/blender/blenkernel/BKE_paint.hh +++ b/source/blender/blenkernel/BKE_paint.hh @@ -27,6 +27,7 @@ #include "bmesh.h" +struct AssetWeakReference; struct BMFace; struct BMesh; struct BlendDataReader; @@ -166,7 +167,7 @@ PaintCurve *BKE_paint_curve_add(Main *bmain, const char *name); /** * Call when entering each respective paint mode. */ -bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint); +bool BKE_paint_ensure(Main *bmain, ToolSettings *ts, Paint **r_paint); void BKE_paint_init(Main *bmain, Scene *sce, ePaintMode mode, const uchar col[3]); void BKE_paint_free(Paint *p); /** @@ -181,7 +182,7 @@ void BKE_paint_runtime_init(const ToolSettings *ts, Paint *paint); void BKE_paint_cavity_curve_preset(Paint *p, int preset); eObjectMode BKE_paint_object_mode_from_paintmode(ePaintMode mode); -bool BKE_paint_ensure_from_paintmode(Scene *sce, ePaintMode mode); +bool BKE_paint_ensure_from_paintmode(Main *bmain, Scene *sce, ePaintMode mode); Paint *BKE_paint_get_active_from_paintmode(Scene *sce, ePaintMode mode); const EnumPropertyItem *BKE_paint_get_tool_enum_from_paintmode(ePaintMode mode); const char *BKE_paint_get_tool_enum_translation_context_from_paintmode(ePaintMode mode); @@ -194,6 +195,14 @@ ePaintMode BKE_paintmode_get_from_tool(const bToolRef *tref); Brush *BKE_paint_brush(Paint *paint); const Brush *BKE_paint_brush_for_read(const Paint *p); void BKE_paint_brush_set(Paint *paint, Brush *br); + +/** Set the active brush of given paint struct, and store the weak asset reference to it. + * NOTE: Takes ownership of the given `weak_asset_reference`. */ +void BKE_paint_brush_asset_set(Paint *p, Brush *br, AssetWeakReference *weak_asset_reference); + +/** Attempt to restore a valid active brush in `p` from brush asset informations stored in `p`. */ +void BKE_paint_brush_asset_restore(Main *bmain, Paint *p); + Palette *BKE_paint_palette(Paint *paint); void BKE_paint_palette_set(Paint *p, Palette *palette); void BKE_paint_curve_set(Brush *br, PaintCurve *pc); @@ -869,7 +878,7 @@ void BKE_sculpt_mask_layers_ensure(Depsgraph *depsgraph, Main *bmain, Object *ob, MultiresModifierData *mmd); -void BKE_sculpt_toolsettings_data_ensure(Scene *scene); +void BKE_sculpt_toolsettings_data_ensure(Main *bmain, Scene *scene); PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob); diff --git a/source/blender/blenkernel/intern/blendfile.cc b/source/blender/blenkernel/intern/blendfile.cc index e4d734cf6cd..b0b85e3b5d4 100644 --- a/source/blender/blenkernel/intern/blendfile.cc +++ b/source/blender/blenkernel/intern/blendfile.cc @@ -306,14 +306,195 @@ 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 BMain into new BMain, provided it does not conflict with data + * already present in the new BMain (name-wise and library-wise). + * + * Dependencies from moved over old data are also imported into the new BMain, (unless, in case of + * linked data, a matching linked ID is already available in new BMain). + * + * 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 BMain later. + * + * NOTE: This function will never remove any original new data from the new BMain, it only moves + * (some of) the old data to the new BMain. + */ +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_uuid_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. * * WARNING: Currently only expects to work on local data, won't work properly if some of the IDs of * given type are linked. * - * 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). */ + * 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). */ static void swap_old_bmain_data_for_blendfile(ReuseOldBMainData *reuse_data, const short id_code) { Main *new_bmain = reuse_data->new_bmain; @@ -720,6 +901,12 @@ 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 ce2bbb3dc1d..4e350207b8e 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -14,13 +14,17 @@ #include "DNA_material_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_space_types.h" #include "BLI_listbase.h" #include "BLI_math_rotation.h" #include "BLI_rand.h" +#include "BLO_readfile.h" + #include "BLT_translation.h" +#include "BKE_blendfile_link_append.h" #include "BKE_bpath.h" #include "BKE_brush.hh" #include "BKE_colortools.h" @@ -36,6 +40,8 @@ #include "BKE_preview_image.hh" #include "BKE_texture.h" +#include "AS_asset_library.h" + #include "IMB_colormanagement.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" @@ -376,39 +382,6 @@ static void brush_blend_read_after_liblink(BlendLibReader * /*reader*/, ID *id) } } -static int brush_undo_preserve_cb(LibraryIDLinkCallbackData *cb_data) -{ - BlendLibReader *reader = (BlendLibReader *)cb_data->user_data; - ID *self_id = cb_data->self_id; - ID *id_old = *cb_data->id_pointer; - /* Old data has not been remapped to new values of the pointers, if we want to keep the old - * pointer here we need its new address. */ - ID *id_old_new = id_old != nullptr ? BLO_read_get_new_id_address( - reader, self_id, ID_IS_LINKED(self_id), id_old) : - nullptr; - BLI_assert(id_old_new == nullptr || ELEM(id_old, id_old_new, id_old_new->orig_id)); - if (cb_data->cb_flag & IDWALK_CB_USER) { - id_us_plus_no_lib(id_old_new); - id_us_min(id_old); - } - *cb_data->id_pointer = id_old_new; - return IDWALK_RET_NOP; -} - -static void brush_undo_preserve(BlendLibReader *reader, ID *id_new, ID *id_old) -{ - /* Whole Brush is preserved across undo-steps. */ - BKE_lib_id_swap(nullptr, id_new, id_old, false, 0); - - /* `id_new` now has content from `id_old`, we need to ensure those old ID pointers are valid. - * NOTE: Since we want to re-use all old pointers here, code is much simpler than for Scene. */ - BKE_library_foreach_ID_link(nullptr, id_new, brush_undo_preserve_cb, reader, IDWALK_NOP); - - /* NOTE: We do not swap IDProperties, as dealing with potential ID pointers in those would be - * fairly delicate. */ - std::swap(id_new->properties, id_old->properties); -} - IDTypeInfo IDType_ID_BR = { /*id_code*/ ID_BR, /*id_filter*/ FILTER_ID_BR, @@ -417,7 +390,7 @@ IDTypeInfo IDType_ID_BR = { /*name*/ "Brush", /*name_plural*/ N_("brushes"), /*translation_context*/ BLT_I18NCONTEXT_ID_BRUSH, - /*flags*/ IDTYPE_FLAGS_NO_ANIMDATA, + /*flags*/ IDTYPE_FLAGS_NO_ANIMDATA | IDTYPE_FLAGS_NO_MEMFILE_UNDO, /*asset_type_info*/ nullptr, /*init_data*/ brush_init_data, @@ -433,7 +406,7 @@ IDTypeInfo IDType_ID_BR = { /*blend_read_data*/ brush_blend_read_data, /*blend_read_after_liblink*/ brush_blend_read_after_liblink, - /*blend_read_undo_preserve*/ brush_undo_preserve, + /*blend_read_undo_preserve*/ nullptr, /*lib_override_apply_post*/ nullptr, }; @@ -510,6 +483,63 @@ 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 = {nullptr}; + 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, + static_cast(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 001c228ffd6..cb6e110d8d3 100644 --- a/source/blender/blenkernel/intern/lib_override.cc +++ b/source/blender/blenkernel/intern/lib_override.cc @@ -362,6 +362,41 @@ 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 short 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)) { + /* 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 a9cd77934e1..cdbbe93112f 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -11,6 +11,7 @@ #include "MEM_guardedalloc.h" +#include "DNA_asset_types.h" #include "DNA_brush_types.h" #include "DNA_defaults.h" #include "DNA_gpencil_legacy_types.h" @@ -37,6 +38,7 @@ #include "BLT_translation.h" +#include "BKE_asset.hh" #include "BKE_attribute.h" #include "BKE_attribute.hh" #include "BKE_brush.hh" @@ -312,7 +314,7 @@ void BKE_paint_reset_overlay_invalid(ePaintOverlayControlFlags flag) overlay_flags &= ~(flag); } -bool BKE_paint_ensure_from_paintmode(Scene *sce, ePaintMode mode) +bool BKE_paint_ensure_from_paintmode(Main *bmain, Scene *sce, ePaintMode mode) { ToolSettings *ts = sce->toolsettings; Paint **paint_ptr = nullptr; @@ -357,7 +359,7 @@ bool BKE_paint_ensure_from_paintmode(Scene *sce, ePaintMode mode) break; } if (paint_ptr) { - BKE_paint_ensure(ts, paint_ptr); + BKE_paint_ensure(bmain, ts, paint_ptr); return true; } return false; @@ -671,6 +673,50 @@ void BKE_paint_brush_set(Paint *p, Brush *br) } } +static void paint_brush_asset_update(Paint *p, + Brush *br, + AssetWeakReference *brush_asset_reference) +{ + if (p->brush_asset_reference != nullptr) { + BKE_asset_weak_reference_free(&p->brush_asset_reference); + } + + if (br == nullptr || br != p->brush || !ID_IS_OVERRIDE_LIBRARY_REAL(p->brush) || + !(ID_IS_ASSET(p->brush) || ID_IS_ASSET(p->brush->id.override_library->reference))) + { + BKE_asset_weak_reference_free(&brush_asset_reference); + return; + } + + p->brush_asset_reference = brush_asset_reference; +} + +void BKE_paint_brush_asset_set(Paint *p, Brush *br, AssetWeakReference *weak_asset_reference) +{ + BKE_paint_brush_set(p, br); + paint_brush_asset_update(p, br, weak_asset_reference); +} + +void BKE_paint_brush_asset_restore(Main *bmain, Paint *p) +{ + if (p->brush != nullptr) { + return; + } + + if (p->brush_asset_reference == nullptr) { + return; + } + + AssetWeakReference *brush_asset_reference = p->brush_asset_reference; + p->brush_asset_reference = nullptr; + + Brush *brush_asset = BKE_brush_asset_runtime_ensure(bmain, brush_asset_reference); + + /* Will either re-assign the brush_asset_reference to `p`, or free it if loading a brush ID from + * it failed. */ + BKE_paint_brush_asset_set(p, brush_asset, brush_asset_reference); +} + void BKE_paint_runtime_init(const ToolSettings *ts, Paint *paint) { if (paint == &ts->imapaint.paint) { @@ -1096,7 +1142,7 @@ eObjectMode BKE_paint_object_mode_from_paintmode(ePaintMode mode) } } -bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint) +bool BKE_paint_ensure(Main *bmain, ToolSettings *ts, Paint **r_paint) { Paint *paint = nullptr; if (*r_paint) { @@ -1130,6 +1176,7 @@ bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint) BLI_assert(paint_test.runtime.tool_offset == (*r_paint)->runtime.tool_offset); #endif } + BKE_paint_brush_asset_restore(bmain, *r_paint); return true; } @@ -1177,6 +1224,7 @@ bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint) *r_paint = paint; BKE_paint_runtime_init(ts, paint); + BKE_paint_brush_asset_restore(bmain, paint); return false; } @@ -1186,7 +1234,7 @@ void BKE_paint_init(Main *bmain, Scene *sce, ePaintMode mode, const uchar col[3] UnifiedPaintSettings *ups = &sce->toolsettings->unified_paint_settings; Paint *paint = BKE_paint_get_active_from_paintmode(sce, mode); - BKE_paint_ensure_from_paintmode(sce, mode); + BKE_paint_ensure_from_paintmode(bmain, sce, mode); /* If there's no brush, create one */ if (PAINT_MODE_HAS_BRUSH(mode)) { @@ -1216,6 +1264,9 @@ void BKE_paint_free(Paint *paint) { BKE_curvemapping_free(paint->cavity_curve); MEM_SAFE_FREE(paint->tool_slots); + if (paint->brush_asset_reference != nullptr) { + BKE_asset_weak_reference_free(&paint->brush_asset_reference); + } } void BKE_paint_copy(Paint *src, Paint *tar, const int flag) @@ -1224,6 +1275,8 @@ void BKE_paint_copy(Paint *src, Paint *tar, const int flag) tar->cavity_curve = BKE_curvemapping_copy(src->cavity_curve); tar->tool_slots = static_cast(MEM_dupallocN(src->tool_slots)); + 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); @@ -1252,6 +1305,9 @@ void BKE_paint_blend_write(BlendWriter *writer, Paint *p) if (p->cavity_curve) { BKE_curvemapping_blend_write(writer, p->cavity_curve); } + if (p->brush_asset_reference) { + BKE_asset_weak_reference_write(writer, p->brush_asset_reference); + } BLO_write_struct_array(writer, PaintToolSlot, p->tool_slots_len, p->tool_slots); } @@ -1269,6 +1325,11 @@ void BKE_paint_blend_read_data(BlendDataReader *reader, const Scene *scene, Pain BKE_paint_cavity_curve_preset(p, CURVE_PRESET_LINE); } + BLO_read_data_address(reader, &p->brush_asset_reference); + if (p->brush_asset_reference) { + BKE_asset_weak_reference_read(reader, p->brush_asset_reference); + } + BLO_read_data_address(reader, &p->tool_slots); /* Workaround for invalid data written in older versions. */ @@ -2069,9 +2130,9 @@ void BKE_sculpt_mask_layers_ensure(Depsgraph *depsgraph, } } -void BKE_sculpt_toolsettings_data_ensure(Scene *scene) +void BKE_sculpt_toolsettings_data_ensure(Main *bmain, Scene *scene) { - BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->sculpt); + BKE_paint_ensure(bmain, scene->toolsettings, (Paint **)&scene->toolsettings->sculpt); Sculpt *sd = scene->toolsettings->sculpt; diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc index 20aacd42a52..a32de96e475 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -1017,6 +1017,9 @@ static void scene_foreach_path(ID *id, BPathForeachPathData *bpath_data) if (scene->ed != nullptr) { SEQ_for_each_callback(&scene->ed->seqbase, seq_foreach_path_callback, bpath_data); } + + /* TODO: Also handle `asset_weak_reference` here? Probably not, conceptually this is not a file + * path. */ } static void scene_blend_write(BlendWriter *writer, ID *id, const void *id_address) diff --git a/source/blender/blenloader/intern/versioning_280.cc b/source/blender/blenloader/intern/versioning_280.cc index a18f8124b73..20c5d67205f 100644 --- a/source/blender/blenloader/intern/versioning_280.cc +++ b/source/blender/blenloader/intern/versioning_280.cc @@ -2881,10 +2881,10 @@ void do_versions_after_linking_280(FileData *fd, Main *bmain) ToolSettings *ts = scene->toolsettings; /* Ensure new Paint modes. */ - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_VERTEX_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_SCULPT_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_WEIGHT_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_VERTEX_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_SCULPT_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_WEIGHT_GPENCIL); /* Set default Draw brush. */ if (brush != nullptr) { @@ -2925,9 +2925,9 @@ void do_versions_after_linking_280(FileData *fd, Main *bmain) /* Reset all grease pencil brushes. */ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { /* Ensure new Paint modes. */ - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_VERTEX_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_SCULPT_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_WEIGHT_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_VERTEX_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_SCULPT_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_WEIGHT_GPENCIL); } } diff --git a/source/blender/blenloader/intern/versioning_defaults.cc b/source/blender/blenloader/intern/versioning_defaults.cc index 94507fd7bd3..325c9debba4 100644 --- a/source/blender/blenloader/intern/versioning_defaults.cc +++ b/source/blender/blenloader/intern/versioning_defaults.cc @@ -494,9 +494,9 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) } /* Ensure new Paint modes. */ - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_VERTEX_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_SCULPT_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_WEIGHT_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_VERTEX_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_SCULPT_GPENCIL); + BKE_paint_ensure_from_paintmode(bmain, scene, PAINT_MODE_WEIGHT_GPENCIL); /* Enable cursor. */ if (ts->gp_paint) { diff --git a/source/blender/editors/asset/ED_asset_handle.h b/source/blender/editors/asset/ED_asset_handle.h index 2506dc15ab6..69df2285a82 100644 --- a/source/blender/editors/asset/ED_asset_handle.h +++ b/source/blender/editors/asset/ED_asset_handle.h @@ -43,6 +43,10 @@ void ED_asset_handle_get_full_library_path( /* `1024` for #FILE_MAX, * rely on warnings to let us know if this gets out of sync. */ char r_full_lib_path[1024]); +void ED_asset_handle_get_full_path(const AssetHandle *asset_handle, + /* `1024 + 66` for #FILE_MAX_LIBEXTRA, + * rely on warnings to let us know if this gets out of sync. */ + char r_full_lib_path[1024 + 66]); #ifdef __cplusplus } diff --git a/source/blender/editors/asset/intern/asset_handle.cc b/source/blender/editors/asset/intern/asset_handle.cc index 684ef1a0b52..b072b815247 100644 --- a/source/blender/editors/asset/intern/asset_handle.cc +++ b/source/blender/editors/asset/intern/asset_handle.cc @@ -57,3 +57,16 @@ void ED_asset_handle_get_full_library_path(const AssetHandle *asset_handle, BLI_strncpy(r_full_lib_path, library_path.c_str(), FILE_MAX); } + +void ED_asset_handle_get_full_path(const AssetHandle *asset_handle, + char r_full_lib_path[FILE_MAX_LIBEXTRA]) +{ + *r_full_lib_path = '\0'; + + std::string library_path = asset_handle->file_data->asset->get_identifier().full_path(); + if (library_path.empty()) { + return; + } + + BLI_strncpy(r_full_lib_path, library_path.c_str(), FILE_MAX); +} diff --git a/source/blender/editors/gpencil_legacy/gpencil_edit.cc b/source/blender/editors/gpencil_legacy/gpencil_edit.cc index fc014a0a2ef..0d96e064b7f 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_edit.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_edit.cc @@ -396,8 +396,8 @@ static int gpencil_paintmode_toggle_exec(bContext *C, wmOperator *op) if (mode == OB_MODE_PAINT_GPENCIL_LEGACY) { /* Be sure we have brushes and Paint settings. * Need Draw and Vertex (used for Tint). */ - BKE_paint_ensure(ts, (Paint **)&ts->gp_paint); - BKE_paint_ensure(ts, (Paint **)&ts->gp_vertexpaint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_paint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_vertexpaint); BKE_brush_gpencil_paint_presets(bmain, ts, false); @@ -511,7 +511,7 @@ static int gpencil_sculptmode_toggle_exec(bContext *C, wmOperator *op) if (mode == OB_MODE_SCULPT_GPENCIL_LEGACY) { /* Be sure we have brushes. */ - BKE_paint_ensure(ts, (Paint **)&ts->gp_sculptpaint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_sculptpaint); const bool reset_mode = (ts->gp_sculptpaint->paint.brush == nullptr); BKE_brush_gpencil_sculpt_presets(bmain, ts, reset_mode); @@ -625,7 +625,7 @@ static int gpencil_weightmode_toggle_exec(bContext *C, wmOperator *op) if (mode == OB_MODE_WEIGHT_GPENCIL_LEGACY) { /* Be sure we have brushes. */ - BKE_paint_ensure(ts, (Paint **)&ts->gp_weightpaint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_weightpaint); const bool reset_mode = (ts->gp_weightpaint->paint.brush == nullptr); BKE_brush_gpencil_weight_presets(bmain, ts, reset_mode); @@ -732,8 +732,8 @@ static int gpencil_vertexmode_toggle_exec(bContext *C, wmOperator *op) if (mode == OB_MODE_VERTEX_GPENCIL_LEGACY) { /* Be sure we have brushes. * Need Draw as well (used for Palettes). */ - BKE_paint_ensure(ts, (Paint **)&ts->gp_paint); - BKE_paint_ensure(ts, (Paint **)&ts->gp_vertexpaint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_paint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_vertexpaint); const bool reset_mode = (ts->gp_vertexpaint->paint.brush == nullptr); BKE_brush_gpencil_vertex_presets(bmain, ts, reset_mode); diff --git a/source/blender/editors/gpencil_legacy/gpencil_utils.cc b/source/blender/editors/gpencil_legacy/gpencil_utils.cc index 84fea336e22..0eb3c7a765d 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_utils.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_utils.cc @@ -1428,7 +1428,7 @@ void ED_gpencil_add_defaults(bContext *C, Object *ob) Main *bmain = CTX_data_main(C); ToolSettings *ts = CTX_data_tool_settings(C); - BKE_paint_ensure(ts, (Paint **)&ts->gp_paint); + BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_paint); Paint *paint = &ts->gp_paint->paint; /* if not exist, create a new one */ if ((paint->brush == nullptr) || (paint->brush->gpencil_settings == nullptr)) { diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index 071d2625ed3..bee957e9285 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -4,8 +4,12 @@ set(INC ../include + ../asset ../uvedit + ../../asset_system ../../blenkernel + ../../blenlib + ../../blenloader ../../blentranslation ../../bmesh ../../depsgraph diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc index e09340ad36e..767be7e86c4 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc @@ -8,6 +8,8 @@ #include "BLI_utildefines.h" #include "BLI_vector_set.hh" +#include "BKE_asset.hh" +#include "BKE_blendfile.h" #include "BKE_brush.hh" #include "BKE_bvhutils.hh" #include "BKE_context.hh" @@ -20,6 +22,7 @@ #include "WM_message.hh" #include "WM_toolsystem.h" +#include "ED_asset_handle.h" #include "ED_curves.hh" #include "ED_curves_sculpt.hh" #include "ED_image.hh" @@ -28,6 +31,8 @@ #include "ED_space_api.hh" #include "ED_view3d.hh" +#include "AS_asset_representation.hh" + #include "DEG_depsgraph.hh" #include "DEG_depsgraph_query.hh" @@ -283,7 +288,8 @@ static void curves_sculptmode_enter(bContext *C) wmMsgBus *mbus = CTX_wm_message_bus(C); Object *ob = CTX_data_active_object(C); - BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->curves_sculpt); + BKE_paint_ensure( + CTX_data_main(C), scene->toolsettings, (Paint **)&scene->toolsettings->curves_sculpt); CurvesSculpt *curves_sculpt = scene->toolsettings->curves_sculpt; ob->mode = OB_MODE_SCULPT_CURVES; @@ -1167,6 +1173,45 @@ static void SCULPT_CURVES_OT_min_distance_edit(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_DEPENDS_ON_CURSOR; } +/* -------------------------------------------------------------------- */ + +static int brush_asset_select_exec(bContext *C, wmOperator * /*op*/) +{ + blender::asset_system::AssetRepresentation *asset = CTX_wm_asset(C); + if (!asset) { + return OPERATOR_CANCELLED; + } + + AssetWeakReference *brush_asset_reference = asset->make_weak_reference(); + Brush *brush = BKE_brush_asset_runtime_ensure(CTX_data_main(C), brush_asset_reference); + + ToolSettings *tool_settings = CTX_data_tool_settings(C); + /* Either takes ownership of the brush_asset_reference, or frees it. */ + BKE_paint_brush_asset_set(&tool_settings->curves_sculpt->paint, brush, brush_asset_reference); + + return OPERATOR_FINISHED; +} + +/** + * 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. + */ +static void SCULPT_CURVES_OT_brush_asset_select(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Brush Asset"; + ot->description = "Select a brush asset as currently sculpt/paint tool - TESTING PURPOSE ONLY"; + ot->idname = "SCULPT_CURVES_OT_brush_asset_select"; + + /* api callbacks */ + ot->exec = brush_asset_select_exec; + ot->poll = CURVES_SCULPT_mode_poll; + + ot->prop = RNA_def_string( + ot->srna, "name", nullptr, MAX_NAME, "Brush Name", "name of the brush asset to select"); +} + } // namespace blender::ed::sculpt_paint /* -------------------------------------------------------------------- */ @@ -1181,6 +1226,8 @@ void ED_operatortypes_sculpt_curves() WM_operatortype_append(SCULPT_CURVES_OT_select_random); WM_operatortype_append(SCULPT_CURVES_OT_select_grow); WM_operatortype_append(SCULPT_CURVES_OT_min_distance_edit); + + WM_operatortype_append(SCULPT_CURVES_OT_brush_asset_select); } /** \} */ diff --git a/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc b/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc index 3785255ef11..c8232ecdcef 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc @@ -228,7 +228,7 @@ static void grease_pencil_draw_mode_enter(bContext *C) Object *ob = CTX_data_active_object(C); GpPaint *grease_pencil_paint = scene->toolsettings->gp_paint; - BKE_paint_ensure(scene->toolsettings, (Paint **)&grease_pencil_paint); + BKE_paint_ensure(CTX_data_main(C), scene->toolsettings, (Paint **)&grease_pencil_paint); ob->mode = OB_MODE_PAINT_GREASE_PENCIL; diff --git a/source/blender/editors/sculpt_paint/paint_intern.hh b/source/blender/editors/sculpt_paint/paint_intern.hh index 619633536a1..0685dcd5e0b 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.hh +++ b/source/blender/editors/sculpt_paint/paint_intern.hh @@ -553,7 +553,8 @@ void get_brush_alpha_data(const Scene *scene, void init_stroke(Depsgraph *depsgraph, Object *ob); void init_session_data(const ToolSettings *ts, Object *ob); -void init_session(Depsgraph *depsgraph, Scene *scene, Object *ob, eObjectMode object_mode); +void init_session( + Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob, eObjectMode object_mode); Vector pbvh_gather_generic(Object *ob, VPaint *wp, Sculpt *sd, Brush *brush); diff --git a/source/blender/editors/sculpt_paint/paint_vertex.cc b/source/blender/editors/sculpt_paint/paint_vertex.cc index 4c90f8e4fc3..e8086946204 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.cc +++ b/source/blender/editors/sculpt_paint/paint_vertex.cc @@ -209,10 +209,11 @@ void init_stroke(Depsgraph *depsgraph, Object *ob) } /* Toggle operator for turning vertex paint mode on or off (copied from sculpt.cc) */ -void init_session(Depsgraph *depsgraph, Scene *scene, Object *ob, eObjectMode object_mode) +void init_session( + Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob, eObjectMode object_mode) { /* Create persistent sculpt mode data */ - BKE_sculpt_toolsettings_data_ensure(scene); + BKE_sculpt_toolsettings_data_ensure(bmain, scene); BLI_assert(ob->sculpt == nullptr); ob->sculpt = MEM_new(__func__); @@ -336,7 +337,7 @@ void mode_enter_generic( const ePaintMode paint_mode = PAINT_MODE_VERTEX; ED_mesh_color_ensure(me, nullptr); - BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->vpaint); + BKE_paint_ensure(bmain, scene->toolsettings, (Paint **)&scene->toolsettings->vpaint); Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode); ED_paint_cursor_start(paint, vertex_paint_poll); BKE_paint_init(bmain, scene, paint_mode, PAINT_CURSOR_VERTEX_PAINT); @@ -344,7 +345,7 @@ void mode_enter_generic( else if (mode_flag == OB_MODE_WEIGHT_PAINT) { const ePaintMode paint_mode = PAINT_MODE_WEIGHT; - BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->wpaint); + BKE_paint_ensure(bmain, scene->toolsettings, (Paint **)&scene->toolsettings->wpaint); Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode); ED_paint_cursor_start(paint, weight_paint_poll); BKE_paint_init(bmain, scene, paint_mode, PAINT_CURSOR_WEIGHT_PAINT); @@ -366,7 +367,7 @@ void mode_enter_generic( BKE_sculptsession_free(ob); } - vwpaint::init_session(depsgraph, scene, ob, mode_flag); + vwpaint::init_session(bmain, depsgraph, scene, ob, mode_flag); /* Flush object mode. */ DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.cc b/source/blender/editors/sculpt_paint/sculpt_ops.cc index 5c70c21f1d9..500947f1da2 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/sculpt_ops.cc @@ -267,7 +267,7 @@ static void SCULPT_OT_symmetrize(wmOperatorType *ot) static void sculpt_init_session(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob) { /* Create persistent sculpt mode data. */ - BKE_sculpt_toolsettings_data_ensure(scene); + BKE_sculpt_toolsettings_data_ensure(bmain, scene); /* Create sculpt mode session data. */ if (ob->sculpt != nullptr) { diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index dcee0198981..ef0d47496ad 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -948,6 +948,13 @@ typedef struct PaintToolSlot { typedef struct Paint { struct Brush *brush; + /** + * A weak asset reference to the #brush, if not NULL. + * Used to attempt restoring the active brush from the AssetLibrary system, typically on + * file load. + */ + struct AssetWeakReference *brush_asset_reference; + /** * Each tool has its own active brush, * The currently active tool is defined by the current 'brush'. diff --git a/source/blender/makesrna/intern/rna_brush.cc b/source/blender/makesrna/intern/rna_brush.cc index 3bb64f8c779..704e620df09 100644 --- a/source/blender/makesrna/intern/rna_brush.cc +++ b/source/blender/makesrna/intern/rna_brush.cc @@ -770,7 +770,12 @@ static void rna_Brush_reset_icon(Brush *br) static void rna_Brush_update(Main * /*bmain*/, Scene * /*scene*/, PointerRNA *ptr) { - Brush *br = (Brush *)ptr->data; + Brush *br = (Brush *)ptr->owner_id; + + if (ID_IS_OVERRIDE_LIBRARY_REAL(&br->id)) { + br->id.tag |= LIB_TAG_LIBOVERRIDE_AUTOREFRESH; + } + WM_main_add_notifier(NC_BRUSH | NA_EDITED, br); // WM_main_add_notifier(NC_SPACE | ND_SPACE_VIEW3D, nullptr); } @@ -2146,14 +2151,18 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) RNA_def_struct_sdna(srna, "BrushCurvesSculptSettings"); RNA_def_struct_ui_text(srna, "Curves Sculpt Brush Settings", ""); + RNA_define_lib_overridable(true); + prop = RNA_def_property(srna, "add_amount", PROP_INT, PROP_NONE); RNA_def_property_range(prop, 1, INT32_MAX); RNA_def_property_ui_text(prop, "Count", "Number of curves added by the Add brush"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "points_per_curve", PROP_INT, PROP_NONE); RNA_def_property_range(prop, 2, INT32_MAX); RNA_def_property_ui_text( prop, "Points per Curve", "Number of control points in a newly added curve"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "scale_uniform", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, nullptr, "flag", BRUSH_CURVES_SCULPT_FLAG_SCALE_UNIFORM); @@ -2161,17 +2170,20 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) "Scale Uniform", "Grow or shrink curves by changing their size uniformly instead of " "using trimming or extrapolation"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "minimum_length", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_range(prop, 0.0f, FLT_MAX); RNA_def_property_ui_text( prop, "Minimum Length", "Avoid shrinking curves shorter than this length"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "interpolate_length", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna( prop, nullptr, "flag", BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH); RNA_def_property_ui_text( prop, "Interpolate Length", "Use length of the curves in close proximity"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "interpolate_point_count", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna( @@ -2179,11 +2191,13 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Interpolate Point Count", "Use the number of points from the curves in close proximity"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "interpolate_shape", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, nullptr, "flag", BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE); RNA_def_property_ui_text( prop, "Interpolate Shape", "Use shape of the curves in close proximity"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "curve_length", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_range(prop, 0.0, FLT_MAX); @@ -2191,28 +2205,35 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) prop, "Curve Length", "Length of newly added curves when it is not interpolated from other curves"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "minimum_distance", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_range(prop, 0.0f, FLT_MAX); RNA_def_property_ui_range(prop, 0.0, 1000.0f, 0.001, 2); RNA_def_property_ui_text( prop, "Minimum Distance", "Goal distance between curve roots for the Density brush"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "density_add_attempts", PROP_INT, PROP_NONE); RNA_def_property_range(prop, 0, INT32_MAX); RNA_def_property_ui_text( prop, "Density Add Attempts", "How many times the Density brush tries to add a new curve"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "density_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, density_mode_items); RNA_def_property_ui_text( prop, "Density Mode", "Determines whether the brush adds or removes curves"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "curve_parameter_falloff", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "CurveMapping"); RNA_def_property_ui_text(prop, "Curve Parameter Falloff", "Falloff that is applied from the tip to the root of each curve"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + RNA_define_lib_overridable(false); } static void rna_def_brush(BlenderRNA *brna) @@ -2560,6 +2581,8 @@ static void rna_def_brush(BlenderRNA *brna) srna, "Brush", "Brush data-block for storing brush settings for painting and sculpting"); RNA_def_struct_ui_icon(srna, ICON_BRUSH_DATA); + RNA_define_lib_overridable(true); + /* enums */ prop = RNA_def_property(srna, "blend", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, prop_blend_items); @@ -3867,6 +3890,8 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_struct_type(prop, "BrushCurvesSculptSettings"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Curves Sculpt Settings", ""); + + RNA_define_lib_overridable(false); } /** diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.cc b/source/blender/makesrna/intern/rna_sculpt_paint.cc index e038ad512aa..e4c994a5354 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.cc +++ b/source/blender/makesrna/intern/rna_sculpt_paint.cc @@ -627,6 +627,13 @@ static void rna_def_paint(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Brush", "Active Brush"); RNA_def_property_update(prop, 0, "rna_Paint_brush_update"); + prop = RNA_def_property(srna, "brush_asset_reference", PROP_POINTER, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, + "Brush Asset Reference", + "A weak reference to the matching brush asset, used e.g. to restore " + "the last used brush on file load"); + /* paint_tool_slots */ prop = RNA_def_property(srna, "tool_slots", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, nullptr, "tool_slots", "tool_slots_len"); diff --git a/source/blender/windowmanager/intern/wm_files.cc b/source/blender/windowmanager/intern/wm_files.cc index bc7d176d8b3..bf4d511231d 100644 --- a/source/blender/windowmanager/intern/wm_files.cc +++ b/source/blender/windowmanager/intern/wm_files.cc @@ -145,6 +145,11 @@ 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"}; /* -------------------------------------------------------------------- */ @@ -2791,6 +2796,7 @@ 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, }; @@ -2808,7 +2814,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_OPEN); + set_next_operator_state(op, OPEN_MAINFILE_STATE_DISCARD_ASSET_DRAFTS); } if (wm_operator_close_file_dialog_if_needed(C, op, wm_open_mainfile_after_dialog_callback)) { @@ -2848,6 +2854,44 @@ 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]; @@ -2880,6 +2924,7 @@ 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}, }; @@ -4359,3 +4404,217 @@ 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_toolsystem.cc b/source/blender/windowmanager/intern/wm_toolsystem.cc index ff3d2ce44f7..570f78fede1 100644 --- a/source/blender/windowmanager/intern/wm_toolsystem.cc +++ b/source/blender/windowmanager/intern/wm_toolsystem.cc @@ -187,7 +187,7 @@ static void toolsystem_ref_link(bContext *C, WorkSpace *workspace, bToolRef *tre 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(scene, paint_mode); + 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) { -- 2.30.2 From 1f94c11d53a30310e24e8f5496c82c57c706f0fd Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 16 Jan 2024 13:29:25 +0100 Subject: [PATCH 007/244] EEVEE: Fix Lights On High-res Monitors When using EEVEE on high resolution monitors the light buffers might not get initialized as there are range checks that pass in the first try. - number of tiles needed is larger than the max_tile_count_threshold - total_word_count is smaller than max_word_count_threshold as it is never set (still initialized to zero. Solution is to not exit on the first try. In a later stage we might want to use something that doesn't require any looping. Fixes: #117128 Pull Request: https://projects.blender.org/blender/blender/pulls/117164 --- source/blender/draw/engines/eevee_next/eevee_light.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_light.cc b/source/blender/draw/engines/eevee_next/eevee_light.cc index 0beb489270d..ed22ab6deab 100644 --- a/source/blender/draw/engines/eevee_next/eevee_light.cc +++ b/source/blender/draw/engines/eevee_next/eevee_light.cc @@ -354,6 +354,7 @@ void LightModule::end_sync() /* Default to 32 as this is likely to be the maximum * tile size used by hardware or compute shading. */ uint tile_size = 16; + bool tile_size_valid = false; do { tile_size *= 2; tiles_extent = math::divide_ceil(render_extent, int2(tile_size)); @@ -362,8 +363,9 @@ void LightModule::end_sync() continue; } total_word_count_ = tile_count * word_per_tile; + tile_size_valid = true; - } while (total_word_count_ > max_word_count_threshold); + } while (total_word_count_ > max_word_count_threshold || !tile_size_valid); /* Keep aligned with storage buffer requirements. */ total_word_count_ = ceil_to_multiple_u(total_word_count_, 32); -- 2.30.2 From 5b952c0085df3c1b341e9b1ae7bf598546c4773e Mon Sep 17 00:00:00 2001 From: Aras Pranckevicius Date: Tue, 16 Jan 2024 13:32:47 +0100 Subject: [PATCH 008/244] ImBuf: no need to use double precision inside IMB_transform Double precision pixel coordinate interpolation was added in 3a65d2f5911 to fix an issue of "wobbly" resulting image at very high scale factors. But the root cause of that was the fact that the scanline loop was repeatedly adding the floating point step for each pixel, instead of doing multiplication by pixel index (repeated floating point additions can "drift" due to imprecision, whereas multiplications are much more accurate). Change all that math back to use single precision floats. I checked the original issue the commit was fixing, it is still fine. Also added a gtest to cover this situation: `imbuf_transform, nearest_very_large_scale` This makes `IMB_transform` a tiny bit faster, on Windows/VS2022/Ryzen5950X scaling an image at 4K resolution: - Bilinear: 5.92 -> 5.83 ms - Subsampled3x3: 53.6 -> 52.7 ms Pull Request: https://projects.blender.org/blender/blender/pulls/117160 --- source/blender/imbuf/intern/transform.cc | 57 +++++++++---------- source/blender/imbuf/intern/transform_test.cc | 34 +++++++++++ 2 files changed, 61 insertions(+), 30 deletions(-) diff --git a/source/blender/imbuf/intern/transform.cc b/source/blender/imbuf/intern/transform.cc index 66a0f09a040..9eeca79a0ea 100644 --- a/source/blender/imbuf/intern/transform.cc +++ b/source/blender/imbuf/intern/transform.cc @@ -28,25 +28,25 @@ struct TransformUserData { /** \brief Destination image buffer to write to. */ ImBuf *dst; /** \brief UV coordinates at the origin (0,0) in source image space. */ - double2 start_uv; + float2 start_uv; /** * \brief delta UV coordinates along the source image buffer, when moving a single pixel in the X * axis of the dst image buffer. */ - double2 add_x; + float2 add_x; /** * \brief delta UV coordinate along the source image buffer, when moving a single pixel in the Y * axes of the dst image buffer. */ - double2 add_y; + float2 add_y; struct { /** * Contains per sub-sample a delta to be added to the uv of the source image buffer. */ - Vector delta_uvs; + Vector delta_uvs; } subsampling; struct { @@ -76,29 +76,29 @@ struct TransformUserData { private: void init_start_uv(const float4x4 &transform_matrix) { - start_uv = double2(transform_matrix.location().xy()); + start_uv = transform_matrix.location().xy(); } void init_add_x(const float4x4 &transform_matrix) { - add_x = double2(transform_matrix.x_axis()); + add_x = transform_matrix.x_axis().xy(); } void init_add_y(const float4x4 &transform_matrix) { - add_y = double2(transform_matrix.y_axis()); + add_y = transform_matrix.y_axis().xy(); } void init_subsampling(const int num_subsamples) { - double2 subsample_add_x = add_x / num_subsamples; - double2 subsample_add_y = add_y / num_subsamples; - double2 offset_x = -add_x * 0.5 + subsample_add_x * 0.5; - double2 offset_y = -add_y * 0.5 + subsample_add_y * 0.5; + float2 subsample_add_x = add_x / num_subsamples; + float2 subsample_add_y = add_y / num_subsamples; + float2 offset_x = -add_x * 0.5f + subsample_add_x * 0.5f; + float2 offset_y = -add_y * 0.5f + subsample_add_y * 0.5f; for (int y : IndexRange(0, num_subsamples)) { for (int x : IndexRange(0, num_subsamples)) { - double2 delta_uv = offset_x + offset_y; + float2 delta_uv = offset_x + offset_y; delta_uv += x * subsample_add_x; delta_uv += y * subsample_add_y; subsampling.delta_uvs.append(delta_uv); @@ -150,7 +150,7 @@ struct CropSource { * * Uses user_data.src_crop to determine if the uv coordinate should be skipped. */ - static bool should_discard(const TransformUserData &user_data, const double2 &uv) + static bool should_discard(const TransformUserData &user_data, const float2 &uv) { return uv.x < user_data.src_crop.xmin || uv.x >= user_data.src_crop.xmax || uv.y < user_data.src_crop.ymin || uv.y >= user_data.src_crop.ymax; @@ -166,7 +166,7 @@ struct NoDiscard { * * Will never discard any pixels. */ - static bool should_discard(const TransformUserData & /*user_data*/, const double2 & /*uv*/) + static bool should_discard(const TransformUserData & /*user_data*/, const float2 & /*uv*/) { return false; } @@ -301,10 +301,10 @@ class Sampler { static const int ChannelLen = NumChannels; using SampleType = Pixel; - void sample(const ImBuf *source, const double2 &uv, SampleType &r_sample) + void sample(const ImBuf *source, const float2 &uv, SampleType &r_sample) { - float u = float(uv.x); - float v = float(uv.y); + float u = uv.x; + float v = uv.y; if constexpr (UVWrapping) { u = wrap_uv(u, source->x); v = wrap_uv(v, source->y); @@ -519,21 +519,19 @@ class ScanlineProcessor { void process_one_sample_per_pixel(const TransformUserData *user_data, int scanline) { /* Note: sample at pixel center for proper filtering. */ - double pixel_x = user_data->destination_region.x_range.first() + 0.5; - double pixel_y = scanline + 0.5; - double2 uv = user_data->start_uv + user_data->add_x * pixel_x + user_data->add_y * pixel_y; + float pixel_x = 0.5f; + float pixel_y = scanline + 0.5f; + float2 uv0 = user_data->start_uv + user_data->add_x * pixel_x + user_data->add_y * pixel_y; output.init_pixel_pointer(user_data->dst, int2(user_data->destination_region.x_range.first(), scanline)); for (int xi : user_data->destination_region.x_range) { - UNUSED_VARS(xi); + float2 uv = uv0 + xi * user_data->add_x; if (!discarder.should_discard(*user_data, uv)) { typename Sampler::SampleType sample; sampler.sample(user_data->src, uv, sample); channel_converter.convert_and_store(sample, output); } - - uv += user_data->add_x; output.increase_pixel_pointer(); } } @@ -541,20 +539,20 @@ class ScanlineProcessor { void process_with_subsampling(const TransformUserData *user_data, int scanline) { /* Note: sample at pixel center for proper filtering. */ - double pixel_x = user_data->destination_region.x_range.first() + 0.5; - double pixel_y = scanline + 0.5; - double2 uv = user_data->start_uv + user_data->add_x * pixel_x + user_data->add_y * pixel_y; + float pixel_x = 0.5f; + float pixel_y = scanline + 0.5f; + float2 uv0 = user_data->start_uv + user_data->add_x * pixel_x + user_data->add_y * pixel_y; output.init_pixel_pointer(user_data->dst, int2(user_data->destination_region.x_range.first(), scanline)); for (int xi : user_data->destination_region.x_range) { - UNUSED_VARS(xi); + float2 uv = uv0 + xi * user_data->add_x; typename Sampler::SampleType sample; sample.clear(); int num_subsamples_added = 0; - for (const double2 &delta_uv : user_data->subsampling.delta_uvs) { - const double2 subsample_uv = uv + delta_uv; + for (const float2 &delta_uv : user_data->subsampling.delta_uvs) { + const float2 subsample_uv = uv + delta_uv; if (!discarder.should_discard(*user_data, subsample_uv)) { typename Sampler::SampleType sub_sample; sampler.sample(user_data->src, subsample_uv, sub_sample); @@ -568,7 +566,6 @@ class ScanlineProcessor { user_data->subsampling.delta_uvs.size(); channel_converter.mix_and_store(sample, output, mix_weight); } - uv += user_data->add_x; output.increase_pixel_pointer(); } } diff --git a/source/blender/imbuf/intern/transform_test.cc b/source/blender/imbuf/intern/transform_test.cc index d20c87732bd..f513cff1285 100644 --- a/source/blender/imbuf/intern/transform_test.cc +++ b/source/blender/imbuf/intern/transform_test.cc @@ -6,6 +6,7 @@ #include "BLI_color.hh" #include "BLI_math_matrix.hh" +#include "BLI_math_quaternion_types.hh" #include "IMB_imbuf.h" namespace blender::imbuf::tests { @@ -109,4 +110,37 @@ TEST(imbuf_transform, bicubic_fractional_larger) IMB_freeImBuf(res); } +TEST(imbuf_transform, nearest_very_large_scale) +{ + /* Create 511x1 black image, with three middle pixels being red/green/blue. */ + ImBuf *src = IMB_allocImBuf(511, 1, 32, IB_rect); + ColorTheme4b col_r = ColorTheme4b(255, 0, 0, 255); + ColorTheme4b col_g = ColorTheme4b(0, 255, 0, 255); + ColorTheme4b col_b = ColorTheme4b(0, 0, 255, 255); + ColorTheme4b col_0 = ColorTheme4b(0, 0, 0, 0); + ColorTheme4b *src_col = reinterpret_cast(src->byte_buffer.data); + src_col[254] = col_r; + src_col[255] = col_g; + src_col[256] = col_b; + + /* Create 3841x1 image, and scale the input image so that the three middle + * pixels cover almost all of it, except the rightmost pixel. */ + ImBuf *res = IMB_allocImBuf(3841, 1, 32, IB_rect); + float4x4 matrix = math::from_loc_rot_scale( + float3(254, 0, 0), math::Quaternion::identity(), float3(3.0f / 3840.0f, 1, 1)); + IMB_transform( + src, res, IMB_TRANSFORM_MODE_REGULAR, IMB_FILTER_NEAREST, 1, matrix.ptr(), nullptr); + + /* Check result: leftmost red, middle green, two rightmost pixels blue and black. + * If the transform code internally does not have enough precision while stepping + * through the scanline, the rightmost side will not come out correctly. */ + const ColorTheme4b *got = reinterpret_cast(res->byte_buffer.data); + EXPECT_EQ(got[0], col_r); + EXPECT_EQ(got[res->x / 2], col_g); + EXPECT_EQ(got[res->x - 2], col_b); + EXPECT_EQ(got[res->x - 1], col_0); + IMB_freeImBuf(src); + IMB_freeImBuf(res); +} + } // namespace blender::imbuf::tests -- 2.30.2 From 1c0d7ccc651c79c1bce2df93c47a19c6c7256f19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Tue, 16 Jan 2024 13:37:57 +0100 Subject: [PATCH 009/244] Fix #115727: Draw panel buttons in the node editor side bar In 13fac109 the node panels got support for individual option button callbacks, but these were not included in the node editor side bar. Only the older top-level buttons are drawn there. The panel structure is currently not accessible in python since it is part of the `NodeDeclaration` system. To draw node input sockets and buttons in the correct panel order as they appear on the node, a new template function `uiTemplateNodeInputs` has been added. This iterates over declared panels and their contents in the appropriate order and draws the buttons before sockets in the same panel. Pull Request: https://projects.blender.org/blender/blender/pulls/116936 --- scripts/startup/bl_ui/space_node.py | 26 +--- .../blender/editors/include/UI_interface_c.hh | 4 + .../blender/editors/interface/CMakeLists.txt | 1 + .../interface_template_node_inputs.cc | 142 ++++++++++++++++++ source/blender/makesrna/intern/rna_ui_api.cc | 6 + 5 files changed, 154 insertions(+), 25 deletions(-) create mode 100644 source/blender/editors/interface/interface_template_node_inputs.cc diff --git a/scripts/startup/bl_ui/space_node.py b/scripts/startup/bl_ui/space_node.py index 8212c2b82e3..d4ee6f2dcb9 100644 --- a/scripts/startup/bl_ui/space_node.py +++ b/scripts/startup/bl_ui/space_node.py @@ -705,31 +705,7 @@ class NODE_PT_active_node_properties(Panel): def draw(self, context): layout = self.layout node = context.active_node - # set "node" context pointer for the panel layout - layout.context_pointer_set("node", node) - - if hasattr(node, "draw_buttons_ext"): - node.draw_buttons_ext(context, layout) - elif hasattr(node, "draw_buttons"): - node.draw_buttons(context, layout) - - # XXX this could be filtered further to exclude socket types - # which don't have meaningful input values (e.g. cycles shader) - value_inputs = [socket for socket in node.inputs if self.show_socket_input(socket)] - if value_inputs: - layout.separator() - layout.label(text="Inputs:") - for socket in value_inputs: - row = layout.row() - socket.draw( - context, - row, - node, - iface_(socket.label if socket.label else socket.name, socket.bl_rna.translation_context), - ) - - def show_socket_input(self, socket): - return hasattr(socket, "draw") and socket.enabled and not socket.is_linked + layout.template_node_inputs(node) class NODE_PT_texture_mapping(Panel): diff --git a/source/blender/editors/include/UI_interface_c.hh b/source/blender/editors/include/UI_interface_c.hh index 8fb9090603c..9e2abe78625 100644 --- a/source/blender/editors/include/UI_interface_c.hh +++ b/source/blender/editors/include/UI_interface_c.hh @@ -2714,6 +2714,10 @@ void uiTemplateGreasePencilLayerTree(uiLayout *layout, bContext *C); #endif void uiTemplateNodeTreeInterface(uiLayout *layout, PointerRNA *ptr); +/** + * Draw all node buttons and socket default values with the same panel structure used by the node. + */ +void uiTemplateNodeInputs(uiLayout *layout, bContext *C, PointerRNA *ptr); /** * \return: A RNA pointer for the operator properties. diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index 5a1f98f5aa4..d31d67ee8c2 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -70,6 +70,7 @@ set(SRC interface_template_bone_collection_tree.cc interface_template_light_linking.cc interface_template_list.cc + interface_template_node_inputs.cc interface_template_node_tree_interface.cc interface_template_search_menu.cc interface_template_search_operator.cc diff --git a/source/blender/editors/interface/interface_template_node_inputs.cc b/source/blender/editors/interface/interface_template_node_inputs.cc new file mode 100644 index 00000000000..5b4c90f1417 --- /dev/null +++ b/source/blender/editors/interface/interface_template_node_inputs.cc @@ -0,0 +1,142 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edinterface + */ + +#include "BLI_vector.hh" + +#include "BKE_context.hh" +#include "BKE_node.hh" +#include "BKE_node_runtime.hh" +#include "BKE_screen.hh" + +#include "BLT_translation.h" + +#include "NOD_node_declaration.hh" + +#include "RNA_access.hh" +#include "RNA_prototypes.h" + +#include "UI_interface.hh" +#include "UI_resources.hh" + +/* -------------------------------------------------------------------- */ +/** \name Node Input Buttons Template + * \{ */ + +using blender::nodes::ItemDeclaration; +using blender::nodes::NodeDeclaration; +using blender::nodes::PanelDeclaration; +using blender::nodes::SocketDeclaration; + +using ItemIterator = blender::Vector::const_iterator; + +namespace blender::ui::nodes { + +static void draw_node_input(bContext *C, + uiLayout *layout, + PointerRNA *node_ptr, + bNodeSocket &socket) +{ + BLI_assert(socket.typeinfo != nullptr); + /* Ignore disabled sockets and linked sockets and sockets without a `draw` callback. */ + if (!socket.is_available() || (socket.flag & SOCK_IS_LINKED) || socket.typeinfo->draw == nullptr) + { + return; + } + + PointerRNA socket_ptr = RNA_pointer_create(node_ptr->owner_id, &RNA_NodeSocket, &socket); + const char *text = IFACE_(bke::nodeSocketLabel(&socket)); + socket.typeinfo->draw(C, layout, &socket_ptr, node_ptr, text); +} + +static void draw_node_input(bContext *C, + uiLayout *layout, + PointerRNA *node_ptr, + StringRefNull identifier) +{ + bNode &node = *static_cast(node_ptr->data); + bNodeSocket *socket = node.runtime->inputs_by_identifier.lookup(identifier); + draw_node_input(C, layout, node_ptr, *socket); +} + +/* Consume the item range, draw buttons if layout is not null. */ +static void handle_node_declaration_items(bContext *C, + Panel *root_panel, + uiLayout *layout, + PointerRNA *node_ptr, + ItemIterator &item_iter, + const ItemIterator item_end) +{ + while (item_iter != item_end) { + const ItemDeclaration *item_decl = item_iter->get(); + ++item_iter; + + if (const SocketDeclaration *socket_decl = dynamic_cast(item_decl)) + { + if (layout && socket_decl->in_out == SOCK_IN) { + draw_node_input(C, layout, node_ptr, socket_decl->identifier); + } + } + else if (const PanelDeclaration *panel_decl = dynamic_cast( + item_decl)) + { + const ItemIterator panel_item_end = item_iter + panel_decl->num_child_decls; + BLI_assert(panel_item_end <= item_end); + + /* Use a root panel property to toggle open/closed state. */ + const std::string panel_idname = "NodePanel" + std::to_string(panel_decl->identifier); + LayoutPanelState *state = BKE_panel_layout_panel_state_ensure( + root_panel, panel_idname.c_str(), panel_decl->default_collapsed); + PointerRNA state_ptr = RNA_pointer_create(nullptr, &RNA_LayoutPanelState, state); + uiLayout *panel_layout = uiLayoutPanel( + C, layout, IFACE_(panel_decl->name.c_str()), &state_ptr, "is_open"); + /* Draw panel buttons at the top of each panel section. */ + if (panel_layout && panel_decl->draw_buttons) { + panel_decl->draw_buttons(panel_layout, C, node_ptr); + } + + handle_node_declaration_items( + C, root_panel, panel_layout, node_ptr, item_iter, panel_item_end); + } + } +} + +} // namespace blender::ui::nodes + +void uiTemplateNodeInputs(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + bNodeTree &tree = *reinterpret_cast(ptr->owner_id); + bNode &node = *static_cast(ptr->data); + + tree.ensure_topology_cache(); + + BLI_assert(node.typeinfo != nullptr); + /* Draw top-level node buttons. */ + if (node.typeinfo->draw_buttons_ex) { + node.typeinfo->draw_buttons_ex(layout, C, ptr); + } + else if (node.typeinfo->draw_buttons) { + node.typeinfo->draw_buttons(layout, C, ptr); + } + + if (node.declaration()) { + /* Draw socket inputs and panel buttons in the order of declaration panels. */ + ItemIterator item_iter = node.declaration()->items.begin(); + const ItemIterator item_end = node.declaration()->items.end(); + Panel *root_panel = uiLayoutGetRootPanel(layout); + blender::ui::nodes::handle_node_declaration_items( + C, root_panel, layout, ptr, item_iter, item_end); + } + else { + /* Draw socket values using the flat inputs list. */ + for (bNodeSocket *input : node.runtime->inputs) { + blender::ui::nodes::draw_node_input(C, layout, ptr, *input); + } + } +} + +/** \} */ diff --git a/source/blender/makesrna/intern/rna_ui_api.cc b/source/blender/makesrna/intern/rna_ui_api.cc index 2f775dc3ea4..786a4820e95 100644 --- a/source/blender/makesrna/intern/rna_ui_api.cc +++ b/source/blender/makesrna/intern/rna_ui_api.cc @@ -2205,6 +2205,12 @@ void RNA_api_ui_layout(StructRNA *srna) "Node Tree Interface", "Interface of a node tree to display"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + + func = RNA_def_function(srna, "template_node_inputs", "uiTemplateNodeInputs"); + RNA_def_function_ui_description(func, "Show a node settings and input socket values"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT); + parm = RNA_def_pointer(func, "node", "Node", "Node", "Display inputs of this node"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); } #endif -- 2.30.2 From da6aae413e7554aca58630545be36eca571f5936 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 11 Jan 2024 18:23:41 +0100 Subject: [PATCH 010/244] Build: bump sse2neon version to latest hash This includes an additional fix for precision of some operations that affect Blender tests. --- build_files/build_environment/cmake/versions.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build_files/build_environment/cmake/versions.cmake b/build_files/build_environment/cmake/versions.cmake index 67fe1e7892b..4139ed24f6b 100644 --- a/build_files/build_environment/cmake/versions.cmake +++ b/build_files/build_environment/cmake/versions.cmake @@ -614,9 +614,9 @@ set(ZSTD_HASH_TYPE SHA256) set(ZSTD_FILE zstd-${ZSTD_VERSION}.tar.gz) set(ZSTD_CPE "cpe:2.3:a:facebook:zstandard:${ZSTD_VERSION}:*:*:*:*:*:*:*") -set(SSE2NEON_VERSION 4614a1a8b8be9ad8ac30f4d1e5e2348cc5212ef7) +set(SSE2NEON_VERSION cfaa59fc04fecb117c0a0f3fe9c82dece6f359ad) set(SSE2NEON_URI https://github.com/DLTcollab/sse2neon/archive/${SSE2NEON_VERSION}.tar.gz) -set(SSE2NEON_HASH dd55460db7b7db51e575071fd4200d54a2b53551844ac2e571b4d46631c5af45) +set(SSE2NEON_HASH 5491c5038a301a6b0ba0531e516568bb50d165d206360f03d8d56558a2490669) set(SSE2NEON_HASH_TYPE SHA256) set(SSE2NEON_FILE sse2neon-${SSE2NEON_VERSION}.tar.gz) -- 2.30.2 From c23d4869f54fb11e1a1ef1817298c012d0e33bb8 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 16 Jan 2024 14:18:57 +0100 Subject: [PATCH 011/244] Fix: broken make update on new macOS build Properly check for the existence of the Python binary in precompiled libs. --- GNUmakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GNUmakefile b/GNUmakefile index ecd1f6bbd6d..d18b5f01a30 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -230,7 +230,7 @@ endif ifndef PYTHON # If not overriden, first try using Python from LIBDIR. PYTHON:=$(LIBDIR)/python/bin/python$(PY_LIB_VERSION) - ifeq (, $(PYTHON)) + ifeq (, $(wildcard $(PYTHON))) # If not available, use system python3 or python command. PYTHON:=python3 ifeq (, $(shell command -v $(PYTHON))) -- 2.30.2 From af93b2877abeacf4ceb07b790834e6242c0cf2ae Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Tue, 16 Jan 2024 14:43:39 +0100 Subject: [PATCH 012/244] Report when `register_class` overrides classes with the same bl_idname The unregistering of already registered classes is expected behavior, however this was done silently and can be unsafe as two Addons may have the same names for operators and in case of name collision the addon registered first will break silently / start behave unexpectedly. So reporting the unregister step seems reasonable. During Addon development, this might seem noisy when classes are frequently updated, however catching the problematic cases instead of being silent might be beneficial. Part of #116370 Pull Request: https://projects.blender.org/blender/blender/pulls/116374 --- .../blender/makesrna/intern/rna_animation.cc | 7 ++++ .../blender/makesrna/intern/rna_nodetree.cc | 14 ++++++++ source/blender/makesrna/intern/rna_render.cc | 7 ++++ source/blender/makesrna/intern/rna_ui.cc | 33 +++++++++++++++++++ source/blender/makesrna/intern/rna_usd.cc | 7 ++++ source/blender/makesrna/intern/rna_userdef.cc | 7 ++++ source/blender/makesrna/intern/rna_wm.cc | 21 ++++++++++++ .../blender/makesrna/intern/rna_wm_gizmo.cc | 7 ++++ 8 files changed, 103 insertions(+) diff --git a/source/blender/makesrna/intern/rna_animation.cc b/source/blender/makesrna/intern/rna_animation.cc index 8fb25ae3e07..e57082cd685 100644 --- a/source/blender/makesrna/intern/rna_animation.cc +++ b/source/blender/makesrna/intern/rna_animation.cc @@ -325,6 +325,13 @@ static StructRNA *rna_KeyingSetInfo_register(Main *bmain, /* check if we have registered this info before, and remove it */ ksi = ANIM_keyingset_info_find_name(dummy_ksi.idname); if (ksi) { + BKE_reportf(reports, + RPT_INFO, + "%s '%s', bl_idname '%s' has been registered before, unregistering previous", + error_prefix, + identifier, + dummy_ksi.idname); + StructRNA *srna = ksi->rna_ext.srna; if (!(srna && rna_KeyingSetInfo_unregister(bmain, srna))) { BKE_reportf(reports, diff --git a/source/blender/makesrna/intern/rna_nodetree.cc b/source/blender/makesrna/intern/rna_nodetree.cc index 58ab0462d28..2e082fd5edc 100644 --- a/source/blender/makesrna/intern/rna_nodetree.cc +++ b/source/blender/makesrna/intern/rna_nodetree.cc @@ -991,6 +991,13 @@ static StructRNA *rna_NodeTree_register(Main *bmain, /* check if we have registered this tree type before, and remove it */ nt = ntreeTypeFind(dummy_nt.idname); if (nt) { + BKE_reportf(reports, + RPT_INFO, + "Registering node tree class: '%s', bl_idname '%s' has been registered before, " + "unregistering previous", + identifier, + dummy_nt.idname); + /* NOTE: unlike most types `nt->rna_ext.srna` doesn't need to be checked for nullptr. */ if (!rna_NodeTree_unregister(bmain, nt->rna_ext.srna)) { BKE_reportf(reports, @@ -1663,6 +1670,13 @@ static bNodeType *rna_Node_register_base(Main *bmain, return nullptr; } + BKE_reportf(reports, + RPT_INFO, + "Registering node class: '%s', bl_idname '%s' has been registered before, " + "unregistering previous", + identifier, + dummy_nt.idname); + /* NOTE: unlike most types `nt->rna_ext.srna` doesn't need to be checked for nullptr. */ if (!rna_Node_unregister(bmain, nt->rna_ext.srna)) { BKE_reportf(reports, diff --git a/source/blender/makesrna/intern/rna_render.cc b/source/blender/makesrna/intern/rna_render.cc index 02270519731..6fca1dcaa91 100644 --- a/source/blender/makesrna/intern/rna_render.cc +++ b/source/blender/makesrna/intern/rna_render.cc @@ -348,6 +348,13 @@ static StructRNA *rna_RenderEngine_register(Main *bmain, et = static_cast( BLI_findstring(&R_engines, dummy_et.idname, offsetof(RenderEngineType, idname))); if (et) { + BKE_reportf(reports, + RPT_INFO, + "%s '%s', bl_idname '%s' has been registered before, unregistering previous", + error_prefix, + identifier, + dummy_et.idname); + StructRNA *srna = et->rna_ext.srna; if (!(srna && rna_RenderEngine_unregister(bmain, srna))) { BKE_reportf(reports, diff --git a/source/blender/makesrna/intern/rna_ui.cc b/source/blender/makesrna/intern/rna_ui.cc index 9adbbb4d0f8..4488c0d303f 100644 --- a/source/blender/makesrna/intern/rna_ui.cc +++ b/source/blender/makesrna/intern/rna_ui.cc @@ -315,6 +315,12 @@ static StructRNA *rna_Panel_register(Main *bmain, PanelType *pt_next = pt->next; StructRNA *srna = pt->rna_ext.srna; if (srna) { + BKE_reportf(reports, + RPT_INFO, + "%s '%s', bl_idname '%s' has been registered before, unregistering previous", + error_prefix, + identifier, + dummy_pt.idname); if (!rna_Panel_unregister(bmain, srna)) { BKE_reportf(reports, RPT_ERROR, @@ -721,6 +727,13 @@ static StructRNA *rna_UIList_register(Main *bmain, /* Check if we have registered this UI-list type before, and remove it. */ ult = WM_uilisttype_find(dummy_ult.idname, true); if (ult) { + BKE_reportf(reports, + RPT_INFO, + "%s '%s', bl_idname '%s' has been registered before, unregistering previous", + error_prefix, + identifier, + dummy_ult.idname); + StructRNA *srna = ult->rna_ext.srna; if (!(srna && rna_UIList_unregister(bmain, srna))) { BKE_reportf(reports, @@ -852,6 +865,13 @@ static StructRNA *rna_Header_register(Main *bmain, ht = static_cast( BLI_findstring(&art->headertypes, dummy_ht.idname, offsetof(HeaderType, idname))); if (ht) { + BKE_reportf(reports, + RPT_INFO, + "%s '%s', bl_idname '%s' has been registered before, unregistering previous", + error_prefix, + identifier, + dummy_ht.idname); + StructRNA *srna = ht->rna_ext.srna; if (!(srna && rna_Header_unregister(bmain, srna))) { BKE_reportf(reports, @@ -1002,6 +1022,13 @@ static StructRNA *rna_Menu_register(Main *bmain, /* check if we have registered this menu type before, and remove it */ mt = WM_menutype_find(dummy_mt.idname, true); if (mt) { + BKE_reportf(reports, + RPT_INFO, + "%s '%s', bl_idname '%s' has been registered before, unregistering previous", + error_prefix, + identifier, + dummy_mt.idname); + StructRNA *srna = mt->rna_ext.srna; if (!(srna && rna_Menu_unregister(bmain, srna))) { BKE_reportf(reports, @@ -1207,6 +1234,12 @@ static StructRNA *rna_AssetShelf_register(Main *bmain, LISTBASE_FOREACH (AssetShelfType *, iter_shelf_type, &space_type->asset_shelf_types) { if (STREQ(iter_shelf_type->idname, dummy_shelf_type.idname)) { if (iter_shelf_type->rna_ext.srna) { + BKE_reportf(reports, + RPT_INFO, + "Registering asset shelf class: '%s' has been registered before, " + "unregistering previous", + dummy_shelf_type.idname); + rna_AssetShelf_unregister(bmain, iter_shelf_type->rna_ext.srna); } break; diff --git a/source/blender/makesrna/intern/rna_usd.cc b/source/blender/makesrna/intern/rna_usd.cc index 20f76dec39c..7bd1724bfb8 100644 --- a/source/blender/makesrna/intern/rna_usd.cc +++ b/source/blender/makesrna/intern/rna_usd.cc @@ -82,6 +82,13 @@ static StructRNA *rna_USDHook_register(Main *bmain, /* check if we have registered this hook before, and remove it */ hook = USD_find_hook_name(dummy_hook.idname); if (hook) { + BKE_reportf(reports, + RPT_INFO, + "%s '%s', bl_idname '%s' has been registered before, unregistering previous", + error_prefix, + identifier, + dummy_hook.idname); + StructRNA *srna = hook->rna_ext.srna; if (!rna_USDHook_unregister(bmain, srna)) { BKE_reportf(reports, diff --git a/source/blender/makesrna/intern/rna_userdef.cc b/source/blender/makesrna/intern/rna_userdef.cc index 2afa44e259e..e4a49ea4ae8 100644 --- a/source/blender/makesrna/intern/rna_userdef.cc +++ b/source/blender/makesrna/intern/rna_userdef.cc @@ -1059,6 +1059,13 @@ static StructRNA *rna_AddonPref_register(Main *bmain, /* Check if we have registered this add-on preference type before, and remove it. */ apt = BKE_addon_pref_type_find(dummy_addon.module, true); if (apt) { + BKE_reportf(reports, + RPT_INFO, + "%s '%s', bl_idname '%s' has been registered before, unregistering previous", + error_prefix, + identifier, + dummy_apt.idname); + StructRNA *srna = apt->rna_ext.srna; if (!(srna && rna_AddonPref_unregister(bmain, srna))) { BKE_reportf(reports, diff --git a/source/blender/makesrna/intern/rna_wm.cc b/source/blender/makesrna/intern/rna_wm.cc index 1440b90badd..cc13f5bd3f0 100644 --- a/source/blender/makesrna/intern/rna_wm.cc +++ b/source/blender/makesrna/intern/rna_wm.cc @@ -1185,6 +1185,13 @@ static StructRNA *rna_wmKeyConfigPref_register(Main *bmain, /* check if we have registered this keyconf-prefs type before, and remove it */ kpt_rt = BKE_keyconfig_pref_type_find(dummy_kpt.idname, true); if (kpt_rt) { + BKE_reportf(reports, + RPT_INFO, + "%s '%s', bl_idname '%s' has been registered before, unregistering previous", + error_prefix, + identifier, + dummy_kpt.idname); + StructRNA *srna = kpt_rt->rna_ext.srna; if (!(srna && rna_wmKeyConfigPref_unregister(bmain, srna))) { BKE_reportf(reports, @@ -1530,6 +1537,13 @@ static StructRNA *rna_Operator_register(Main *bmain, { wmOperatorType *ot = WM_operatortype_find(dummy_ot.idname, true); if (ot) { + BKE_reportf(reports, + RPT_INFO, + "%s '%s', bl_idname '%s' has been registered before, unregistering previous", + error_prefix, + identifier, + dummy_ot.idname); + StructRNA *srna = ot->rna_ext.srna; if (!(srna && rna_Operator_unregister(bmain, srna))) { BKE_reportf(reports, @@ -1700,6 +1714,13 @@ static StructRNA *rna_MacroOperator_register(Main *bmain, { wmOperatorType *ot = WM_operatortype_find(dummy_ot.idname, true); if (ot) { + BKE_reportf(reports, + RPT_INFO, + "%s '%s', bl_idname '%s' has been registered before, unregistering previous", + error_prefix, + identifier, + dummy_ot.idname); + StructRNA *srna = ot->rna_ext.srna; if (!(srna && rna_Operator_unregister(bmain, srna))) { BKE_reportf(reports, diff --git a/source/blender/makesrna/intern/rna_wm_gizmo.cc b/source/blender/makesrna/intern/rna_wm_gizmo.cc index 18c61fa8519..96d3ebb2b05 100644 --- a/source/blender/makesrna/intern/rna_wm_gizmo.cc +++ b/source/blender/makesrna/intern/rna_wm_gizmo.cc @@ -460,6 +460,13 @@ static StructRNA *rna_Gizmo_register(Main *bmain, { const wmGizmoType *gzt = WM_gizmotype_find(dummy_gt.idname, true); if (gzt) { + BKE_reportf(reports, + RPT_INFO, + "%s '%s', bl_idname '%s' has been registered before, unregistering previous", + error_prefix, + identifier, + dummy_gt.idname); + StructRNA *srna = gzt->rna_ext.srna; if (!(srna && rna_Gizmo_unregister(bmain, srna))) { BKE_reportf(reports, -- 2.30.2 From f0e840375e47af0c650e6f3beaee8994eaa08bbd Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 16 Jan 2024 08:52:31 -0500 Subject: [PATCH 013/244] Fix #117158: Multires sculpting crash The change to use `float3` in `frontface` didn't account for the fact that the values we sometimes null. --- source/blender/editors/sculpt_paint/sculpt.cc | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index c9eceb39a81..bfebfc4c8de 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -1636,24 +1636,13 @@ const float *SCULPT_brush_frontface_normal_from_falloff_shape(SculptSession *ss, return ss->cache->view_normal; } -static float frontface(const Brush *br, - const float3 &sculpt_normal, - const float3 &no, - const float3 &fno) +static float frontface(const Brush &brush, const float3 &view_normal, const float3 &normal) { using namespace blender; - if (!(br->flag & BRUSH_FRONTFACE)) { + if (!(brush.flag & BRUSH_FRONTFACE)) { return 1.0f; } - - float dot; - if (no) { - dot = math::dot(no, sculpt_normal); - } - else { - dot = math::dot(fno, sculpt_normal); - } - return dot > 0.0f ? dot : 0.0f; + return std::max(math::dot(normal, view_normal), 0.0f); } #if 0 @@ -2400,7 +2389,7 @@ float SCULPT_brush_strength_factor( /* Falloff curve. */ avg *= BKE_brush_curve_strength(brush, final_len, cache->radius); - avg *= frontface(brush, cache->view_normal, vno, fno); + avg *= frontface(*brush, cache->view_normal, vno ? vno : fno); /* Paint mask. */ avg *= 1.0f - mask; @@ -2435,7 +2424,7 @@ void SCULPT_brush_strength_color( /* Falloff curve. */ const float falloff = BKE_brush_curve_strength(brush, final_len, cache->radius) * - frontface(brush, cache->view_normal, vno, fno); + frontface(*brush, cache->view_normal, vno ? vno : fno); /* Paint mask. */ const float paint_mask = 1.0f - mask; -- 2.30.2 From 645b0e75c20d8b5c8e47c15c4ea6b84a1a86372a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Tue, 16 Jan 2024 15:32:33 +0100 Subject: [PATCH 014/244] Fix #116477: Node group sockets with subtypes have broken idnames Loading pre-4.0.20 node groups with sockets using subtypes causes broken socket identifiers. This is because the node tree interface now expects sockets to use the base identifiers ("NodeSocketFloat" instead of "NodeSocketFloatFactor" etc.). To correct this the conversion code now replaces socket idnames that include a subtype suffix with their base names. This fix is also applied to files between versions 4.0.20 and 4.1.10, where the socket types may have been converted incorrectly. Pull Request: https://projects.blender.org/blender/blender/pulls/117133 --- .../blender/blenkernel/BKE_blender_version.h | 2 +- source/blender/blenkernel/intern/node.cc | 60 ++++++++++++++++- .../blenloader/intern/versioning_400.cc | 65 ++++++++++++++++++- tests/python/bl_node_group_compat.py | 38 ++++++++--- 4 files changed, 154 insertions(+), 11 deletions(-) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 225c824e058..8a7484e375b 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -29,7 +29,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 10 +#define BLENDER_FILE_SUBVERSION 11 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and cancel loading the file, showing a warning to diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 0cf33edfd98..5e78cf0ecfb 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -476,6 +476,7 @@ static void write_node_socket_interface(BlendWriter *writer, const bNodeSocket * static bNodeSocket *make_socket(bNodeTree *ntree, const eNodeSocketInOut in_out, const StringRef idname, + const StringRef name, const StringRef identifier) { @@ -501,6 +502,59 @@ static bNodeSocket *make_socket(bNodeTree *ntree, return sock; } +/* Include the subtype suffix for old socket idnames. */ +static StringRef get_legacy_socket_subtype_idname(StringRef idname, const void *socket_data) +{ + if (idname == "NodeSocketFloat") { + const bNodeSocketValueFloat &float_data = *static_cast( + socket_data); + switch (float_data.subtype) { + case PROP_UNSIGNED: + return "NodeSocketFloatUnsigned"; + case PROP_PERCENTAGE: + return "NodeSocketFloatPercentage"; + case PROP_FACTOR: + return "NodeSocketFloatFactor"; + case PROP_ANGLE: + return "NodeSocketFloatAngle"; + case PROP_TIME: + return "NodeSocketFloatTime"; + case PROP_TIME_ABSOLUTE: + return "NodeSocketFloatTimeAbsolute"; + case PROP_DISTANCE: + return "NodeSocketFloatDistance"; + } + } + if (idname == "NodeSocketInt") { + const bNodeSocketValueInt &int_data = *static_cast(socket_data); + switch (int_data.subtype) { + case PROP_UNSIGNED: + return "NodeSocketIntUnsigned"; + case PROP_PERCENTAGE: + return "NodeSocketIntPercentage"; + case PROP_FACTOR: + return "NodeSocketIntFactor"; + } + } + if (idname == "NodeSocketVector") { + const bNodeSocketValueVector &vector_data = *static_cast( + socket_data); + switch (vector_data.subtype) { + case PROP_TRANSLATION: + return "NodeSocketVectorTranslation"; + case PROP_DIRECTION: + return "NodeSocketVectorDirection"; + case PROP_VELOCITY: + return "NodeSocketVectorVelocity"; + case PROP_ACCELERATION: + return "NodeSocketVectorAcceleration"; + case PROP_EULER: + return "NodeSocketVectorEuler"; + } + } + return idname; +} + /** * Socket interface reconstruction for forward compatibility. * To enable previous Blender versions to read the new interface DNA data, @@ -516,7 +570,11 @@ static void construct_interface_as_legacy_sockets(bNodeTree *ntree) auto make_legacy_socket = [&](const bNodeTreeInterfaceSocket &socket, eNodeSocketInOut in_out) -> bNodeSocket * { bNodeSocket *iosock = make_socket( - ntree, in_out, socket.socket_type, socket.name ? socket.name : "", socket.identifier); + ntree, + in_out, + get_legacy_socket_subtype_idname(socket.socket_type, socket.socket_data), + socket.name ? socket.name : "", + socket.identifier); if (!iosock) { return nullptr; } diff --git a/source/blender/blenloader/intern/versioning_400.cc b/source/blender/blenloader/intern/versioning_400.cc index c7b145cc921..5e82fd7fc28 100644 --- a/source/blender/blenloader/intern/versioning_400.cc +++ b/source/blender/blenloader/intern/versioning_400.cc @@ -1386,6 +1386,36 @@ static void version_geometry_nodes_use_rotation_socket(bNodeTree &ntree) } } } + +/* Find the base socket name for an idname that may include a subtype. */ +static blender::StringRef legacy_socket_idname_to_socket_type(blender::StringRef idname) +{ + using string_pair = std::pair; + static const string_pair subtypes_map[] = {{"NodeSocketFloatUnsigned", "NodeSocketFloat"}, + {"NodeSocketFloatPercentage", "NodeSocketFloat"}, + {"NodeSocketFloatFactor", "NodeSocketFloat"}, + {"NodeSocketFloatAngle", "NodeSocketFloat"}, + {"NodeSocketFloatTime", "NodeSocketFloat"}, + {"NodeSocketFloatTimeAbsolute", "NodeSocketFloat"}, + {"NodeSocketFloatDistance", "NodeSocketFloat"}, + {"NodeSocketIntUnsigned", "NodeSocketInt"}, + {"NodeSocketIntPercentage", "NodeSocketInt"}, + {"NodeSocketIntFactor", "NodeSocketInt"}, + {"NodeSocketVectorTranslation", "NodeSocketVector"}, + {"NodeSocketVectorDirection", "NodeSocketVector"}, + {"NodeSocketVectorVelocity", "NodeSocketVector"}, + {"NodeSocketVectorAcceleration", "NodeSocketVector"}, + {"NodeSocketVectorEuler", "NodeSocketVector"}, + {"NodeSocketVectorXYZ", "NodeSocketVector"}}; + for (const string_pair &pair : subtypes_map) { + if (pair.first == idname) { + return pair.second; + } + } + /* Unchanged socket idname. */ + return idname; +} + static bNodeTreeInterfaceItem *legacy_socket_move_to_interface(bNodeSocket &legacy_socket, const eNodeSocketInOut in_out) { @@ -1396,7 +1426,10 @@ static bNodeTreeInterfaceItem *legacy_socket_move_to_interface(bNodeSocket &lega new_socket->name = BLI_strdup(legacy_socket.name); new_socket->identifier = BLI_strdup(legacy_socket.identifier); new_socket->description = BLI_strdup(legacy_socket.description); - new_socket->socket_type = BLI_strdup(legacy_socket.idname); + /* If the socket idname includes a subtype (e.g. "NodeSocketFloatFactor") this will convert it to + * the base type name ("NodeSocketFloat"). */ + new_socket->socket_type = BLI_strdup( + legacy_socket_idname_to_socket_type(legacy_socket.idname).data()); new_socket->flag = (in_out == SOCK_IN ? NODE_INTERFACE_SOCKET_INPUT : NODE_INTERFACE_SOCKET_OUTPUT); SET_FLAG_FROM_TEST( @@ -1444,6 +1477,28 @@ static void versioning_convert_node_tree_socket_lists_to_interface(bNodeTree *nt } } +/** + * Original node tree interface conversion in did not convert socket idnames with subtype suffixes + * to correct socket base types (see #versioning_convert_node_tree_socket_lists_to_interface). + */ +static void versioning_fix_socket_subtype_idnames(bNodeTree *ntree) +{ + bNodeTreeInterface &tree_interface = ntree->tree_interface; + + tree_interface.foreach_item([](bNodeTreeInterfaceItem &item) -> bool { + if (item.item_type == NODE_INTERFACE_SOCKET) { + bNodeTreeInterfaceSocket &socket = reinterpret_cast(item); + blender::StringRef corrected_socket_type = legacy_socket_idname_to_socket_type( + socket.socket_type); + if (socket.socket_type != corrected_socket_type) { + MEM_freeN(socket.socket_type); + socket.socket_type = BLI_strdup(corrected_socket_type.data()); + } + } + return true; + }); +} + /* Convert coat inputs on the Principled BSDF. */ static void version_principled_bsdf_coat(bNodeTree *ntree) { @@ -2577,6 +2632,14 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) } } + if (MAIN_VERSION_FILE_ATLEAST(bmain, 400, 20) && !MAIN_VERSION_FILE_ATLEAST(bmain, 401, 11)) { + /* Convert old socket lists into new interface items. */ + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + versioning_fix_socket_subtype_idnames(ntree); + } + FOREACH_NODETREE_END; + } + /** * Always bump subversion in BKE_blender_version.h when adding versioning * code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check. diff --git a/tests/python/bl_node_group_compat.py b/tests/python/bl_node_group_compat.py index 3e3002618e8..6c2e0f23ad6 100644 --- a/tests/python/bl_node_group_compat.py +++ b/tests/python/bl_node_group_compat.py @@ -14,7 +14,25 @@ import bpy args = None -type_info = { +base_idname = { + "VALUE": "NodeSocketFloat", + "INT": "NodeSocketInt", + "BOOLEAN": "NodeSocketBool", + "ROTATION": "NodeSocketRotation", + "VECTOR": "NodeSocketVector", + "RGBA": "NodeSocketColor", + "STRING": "NodeSocketString", + "SHADER": "NodeSocketShader", + "OBJECT": "NodeSocketObject", + "IMAGE": "NodeSocketImage", + "GEOMETRY": "NodeSocketGeometry", + "COLLECTION": "NodeSocketCollection", + "TEXTURE": "NodeSocketTexture", + "MATERIAL": "NodeSocketMaterial", +} + + +subtype_idname = { ("VALUE", "NONE"): "NodeSocketFloat", ("VALUE", "UNSIGNED"): "NodeSocketFloatUnsigned", ("VALUE", "PERCENTAGE"): "NodeSocketFloatPercentage", @@ -63,8 +81,12 @@ class SocketSpec(): external_links: int = 1 @property - def idname(self): - return type_info[(self.type, self.subtype)] + def base_idname(self): + return base_idname[self.type] + + @property + def subtype_idname(self): + return subtype_idname[(self.type, self.subtype)] class AbstractNodeGroupInterfaceTest(unittest.TestCase): @@ -95,7 +117,7 @@ class AbstractNodeGroupInterfaceTest(unittest.TestCase): # Examine the interface item. self.assertEqual(item.name, spec.name) - self.assertEqual(item.bl_socket_idname, spec.idname) + self.assertEqual(item.bl_socket_idname, spec.base_idname) self.assertEqual(item.identifier, spec.identifier) # Types that have subtypes. @@ -134,7 +156,7 @@ class AbstractNodeGroupInterfaceTest(unittest.TestCase): socket = next(s for s in node.inputs if s.identifier == spec.identifier) self.assertIsNotNone(socket, f"Could not find socket for group input identifier {spec.identifier}") self.assertEqual(socket.name, spec.name) - self.assertEqual(socket.bl_idname, spec.idname) + self.assertEqual(socket.bl_idname, spec.subtype_idname) self.assertEqual(socket.type, spec.type) self.assertEqual(socket.hide_value, spec.hide_value) if test_links: @@ -147,7 +169,7 @@ class AbstractNodeGroupInterfaceTest(unittest.TestCase): self.assertIsNotNone( socket, f"Could not find group input socket for group input identifier {spec.identifier}") self.assertEqual(socket.name, spec.name) - self.assertEqual(socket.bl_idname, spec.idname) + self.assertEqual(socket.bl_idname, spec.subtype_idname) self.assertEqual(socket.type, spec.type) self.assertEqual(socket.hide_value, spec.hide_value) if test_links: @@ -158,7 +180,7 @@ class AbstractNodeGroupInterfaceTest(unittest.TestCase): socket = next(s for s in node.outputs if s.identifier == spec.identifier) self.assertIsNotNone(socket, f"Could not find socket for group output identifier {spec.identifier}") self.assertEqual(socket.name, spec.name) - self.assertEqual(socket.bl_idname, spec.idname) + self.assertEqual(socket.bl_idname, spec.subtype_idname) self.assertEqual(socket.type, spec.type) self.assertEqual(socket.hide_value, spec.hide_value) if test_links: @@ -171,7 +193,7 @@ class AbstractNodeGroupInterfaceTest(unittest.TestCase): self.assertIsNotNone( socket, f"Could not find group output socket for group output identifier {spec.identifier}") self.assertEqual(socket.name, spec.name) - self.assertEqual(socket.bl_idname, spec.idname) + self.assertEqual(socket.bl_idname, spec.subtype_idname) self.assertEqual(socket.type, spec.type) self.assertEqual(socket.hide_value, spec.hide_value) if test_links: -- 2.30.2 From 35d4c509e763c3202e7736a04d4d1827c8e5ef1f Mon Sep 17 00:00:00 2001 From: Pedro A Date: Tue, 16 Jan 2024 15:41:03 +0100 Subject: [PATCH 015/244] Fix: error when custom socket has no 'draw' function This `draw` method is optional. The same check is also done elsewhere. Pull Request: https://projects.blender.org/blender/blender/pulls/117061 --- scripts/startup/bl_ui/space_node.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/startup/bl_ui/space_node.py b/scripts/startup/bl_ui/space_node.py index d4ee6f2dcb9..d9606b16911 100644 --- a/scripts/startup/bl_ui/space_node.py +++ b/scripts/startup/bl_ui/space_node.py @@ -928,7 +928,8 @@ class NODE_PT_node_tree_interface(Panel): if 'OUTPUT' in active_item.in_out: layout.prop(active_item, "attribute_domain") layout.prop(active_item, "default_attribute_name") - active_item.draw(context, layout) + if hasattr(active_item, 'draw'): + active_item.draw(context, layout) if active_item.item_type == 'PANEL': layout.prop(active_item, "description") -- 2.30.2 From 35c362db89c3a2bcf416929bc905b71c2e9bcc21 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 16 Jan 2024 16:16:12 +0100 Subject: [PATCH 016/244] Fix: incorrect Exists output in Named Attribute node for grease pencil Grease pencil needs some special handling because it stores layer attributes independently of point/curve attributes. --- .../blender/blenkernel/intern/geometry_fields.cc | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/geometry_fields.cc b/source/blender/blenkernel/intern/geometry_fields.cc index 0655ef6631d..5ebaf2eeb71 100644 --- a/source/blender/blenkernel/intern/geometry_fields.cc +++ b/source/blender/blenkernel/intern/geometry_fields.cc @@ -406,8 +406,22 @@ GVArray AttributeFieldInput::get_varray_for_context(const GeometryFieldContext & GVArray AttributeExistsFieldInput::get_varray_for_context(const bke::GeometryFieldContext &context, const IndexMask & /*mask*/) const { + const AttrDomain domain = context.domain(); + if (context.type() == GeometryComponent::Type::GreasePencil) { + const AttributeAccessor layer_attributes = context.grease_pencil()->attributes(); + if (context.domain() == AttrDomain::Layer) { + const bool exists = layer_attributes.contains(name_); + const int domain_size = layer_attributes.domain_size(AttrDomain::Layer); + return VArray::ForSingle(exists, domain_size); + } + const greasepencil::Drawing *drawing = context.grease_pencil_layer_drawing(); + const AttributeAccessor curve_attributes = drawing->strokes().attributes(); + const bool exists = layer_attributes.contains(name_) || curve_attributes.contains(name_); + const int domain_size = curve_attributes.domain_size(domain); + return VArray::ForSingle(exists, domain_size); + } const bool exists = context.attributes()->contains(name_); - const int domain_size = context.attributes()->domain_size(context.domain()); + const int domain_size = context.attributes()->domain_size(domain); return VArray::ForSingle(exists, domain_size); } -- 2.30.2 From 619d83d5ea5f18da02a13a187b919f651acebda6 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 16 Jan 2024 16:19:28 +0100 Subject: [PATCH 017/244] Fix: crash trying to store an attribute on unsupported grease pencil domain This crash happened when trying to store an attribute on the edge domain on a grease pencil geometry. --- source/blender/blenkernel/intern/geometry_fields.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/geometry_fields.cc b/source/blender/blenkernel/intern/geometry_fields.cc index 5ebaf2eeb71..fe66a090c1e 100644 --- a/source/blender/blenkernel/intern/geometry_fields.cc +++ b/source/blender/blenkernel/intern/geometry_fields.cc @@ -766,7 +766,8 @@ bool try_capture_field_on_geometry(GeometryComponent &component, const fn::Field &selection, const fn::GField &field) { - if (component.type() == GeometryComponent::Type::GreasePencil && + const GeometryComponent::Type component_type = component.type(); + if (component_type == GeometryComponent::Type::GreasePencil && ELEM(domain, AttrDomain::Point, AttrDomain::Curve)) { /* Capture the field on every layer individually. */ @@ -798,6 +799,10 @@ bool try_capture_field_on_geometry(GeometryComponent &component, }); return any_success; } + if (component_type == GeometryComponent::Type::GreasePencil && domain != AttrDomain::Layer) { + /* The remaining code only handles the layer domain for grease pencil geometries. */ + return false; + } MutableAttributeAccessor attributes = *component.attributes_for_write(); const GeometryFieldContext field_context{component, domain}; -- 2.30.2 From dd67d941e510ced18002dc72450f9e2b8170097d Mon Sep 17 00:00:00 2001 From: Eugene Kuznetsov Date: Tue, 16 Jan 2024 16:37:40 +0100 Subject: [PATCH 018/244] Linux: Use huge pages in jemalloc to speed up allocations Enable huge pages for jemalloc. This requests the Linux kernel to use huge (2 MB) pages for large allocations. This has the effect of speeding up first accesses to those allocations, and possibly also speeds up future accesses by reducing TLB faults. By default, 4 KB pages are used unless the user enables huge pages through a kernel parameter or an obscure sysfs setting. For Cycles benchmarks, this gives about a 5% CPU rendering performance improvement. It likely also improves performance in other areas of Blender. Pull Request: https://projects.blender.org/blender/blender/pulls/116663 --- intern/guardedalloc/intern/mallocn.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/guardedalloc/intern/mallocn.c b/intern/guardedalloc/intern/mallocn.c index 3c5524b07ac..adc39f8eb6d 100644 --- a/intern/guardedalloc/intern/mallocn.c +++ b/intern/guardedalloc/intern/mallocn.c @@ -21,7 +21,7 @@ /* If JEMALLOC is used, it reads this global variable and enables background * threads to purge dirty pages. Otherwise we release memory too slowly or not * at all if the thread that did the allocation stays inactive. */ -const char *malloc_conf = "background_thread:true,dirty_decay_ms:4000"; +const char *malloc_conf = "background_thread:true,dirty_decay_ms:4000,thp:always,metadata_thp:always"; #endif /* NOTE: Keep in sync with MEM_use_lockfree_allocator(). */ -- 2.30.2 From 6c4a058e8b08c2a19e65c84a88d5390da361c03d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Tue, 16 Jan 2024 16:56:14 +0100 Subject: [PATCH 019/244] GPv3: Opacity modifier Opacity modifier implementation based on GP2. Functionality is largely unchanged. _Color Mode_ is either `Stroke` or `Fill` for modifying color opacity or `Hardness`. _Uniform Opacity_ does two things at once (!): - Sets the same opacity value for every point in a stroke. - Sets opacity as an absolute value rather than a factor. _Weight as Factor_ (button to the right of Opacity Factor): Use the vertex group as opacity __factor__ rather than an overall __influence__. This is confusing and hard to convey, but copies behavior from GP2. The _Influence_ panel contains the same filter settings as the GP2 modifier, with some small changes: - _Layer_ selects only strokes in the respective layer (with an _Invert_ option) - _Material_ selects only points with the respective material (with an _Invert_ option) - _Layer Pass_ and _Material Pass_ select only strokes/points which are rendered in the respective pass. _Note 1: Layers don't have UI for setting a pass yet, this will be a generic layer attribute. This can be set through the API for testing._ _Note 2: In GP2 a pass value of zero was used to disable pass filters. Since zero is a valid pass ID an explicit flag has been added for the purpose of turning pass filters on and off._ - _Vertex Group_: This can be used as an additional influence filter on points. If _Weight as Factor_ is enable the vertex group instead replaces the opacity factor. In _Fill_ mode the vertex group weight of the stroke's first point is used as influence for the entire stroke. - _Custom Curve_ is another possible influence factor per point. The curve input value is the relative position of a point along its stroke. The Influence settings have been moved into a separate DNA struct, which should help with reusability in various modifiers. Various utility functions can be found int `MOD_grease_pencil_util.hh` for handling influence settings and generating `IndexMasks` for modernized C++ code. Pull Request: https://projects.blender.org/blender/blender/pulls/116946 --- .../startup/bl_ui/properties_data_modifier.py | 4 +- .../blender/makesdna/DNA_modifier_defaults.h | 7 + source/blender/makesdna/DNA_modifier_types.h | 63 ++++ source/blender/makesdna/intern/dna_defaults.c | 2 + .../blender/makesrna/intern/rna_modifier.cc | 260 ++++++++++++++ source/blender/modifiers/CMakeLists.txt | 3 + source/blender/modifiers/MOD_modifiertypes.hh | 1 + .../intern/MOD_grease_pencil_opacity.cc | 340 ++++++++++++++++++ .../intern/MOD_grease_pencil_util.cc | 333 +++++++++++++++++ .../intern/MOD_grease_pencil_util.hh | 66 ++++ source/blender/modifiers/intern/MOD_util.cc | 1 + 11 files changed, 1079 insertions(+), 1 deletion(-) create mode 100644 source/blender/modifiers/intern/MOD_grease_pencil_opacity.cc create mode 100644 source/blender/modifiers/intern/MOD_grease_pencil_util.cc create mode 100644 source/blender/modifiers/intern/MOD_grease_pencil_util.hh diff --git a/scripts/startup/bl_ui/properties_data_modifier.py b/scripts/startup/bl_ui/properties_data_modifier.py index 36f164f4f01..7d046148de7 100644 --- a/scripts/startup/bl_ui/properties_data_modifier.py +++ b/scripts/startup/bl_ui/properties_data_modifier.py @@ -71,7 +71,7 @@ class OBJECT_MT_modifier_add(ModifierAddMenu, Menu): if geometry_nodes_supported: self.operator_modifier_add(layout, 'NODES') layout.separator() - if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE'}: + if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE', 'GREASEPENCIL'}: layout.menu("OBJECT_MT_modifier_add_edit") if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'VOLUME'}: layout.menu("OBJECT_MT_modifier_add_generate") @@ -105,6 +105,8 @@ class OBJECT_MT_modifier_add_edit(ModifierAddMenu, Menu): self.operator_modifier_add(layout, 'VERTEX_WEIGHT_EDIT') self.operator_modifier_add(layout, 'VERTEX_WEIGHT_MIX') self.operator_modifier_add(layout, 'VERTEX_WEIGHT_PROXIMITY') + if ob_type == 'GREASEPENCIL': + self.operator_modifier_add(layout, 'GREASE_PENCIL_OPACITY') layout.template_modifier_asset_menu_items(catalog_path=self.bl_label) diff --git a/source/blender/makesdna/DNA_modifier_defaults.h b/source/blender/makesdna/DNA_modifier_defaults.h index 4fc4a9341b1..288fa3b4473 100644 --- a/source/blender/makesdna/DNA_modifier_defaults.h +++ b/source/blender/makesdna/DNA_modifier_defaults.h @@ -801,4 +801,11 @@ .mat_ofs = 0, \ } +#define _DNA_DEFAULT_GreasePencilOpacityModifierData \ + { \ + .color_mode = MOD_GREASE_PENCIL_COLOR_BOTH, \ + .color_factor = 1.0f, \ + .hardness_factor = 1.0f, \ + } + /* clang-format off */ diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 0e31f383266..7bc475c24f1 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -93,6 +93,7 @@ typedef enum ModifierType { eModifierType_MeshToVolume = 58, eModifierType_VolumeDisplace = 59, eModifierType_VolumeToMesh = 60, + eModifierType_GreasePencilOpacity = 61, NUM_MODIFIER_TYPES, } ModifierType; @@ -2484,3 +2485,65 @@ typedef enum VolumeToMeshResolutionMode { typedef enum VolumeToMeshFlag { VOLUME_TO_MESH_USE_SMOOTH_SHADE = 1 << 0, } VolumeToMeshFlag; + +/** + * Common influence data for grease pencil modifiers. + * Not all parts may be used by all modifier types. + */ +typedef struct GreasePencilModifierInfluenceData { + /** GreasePencilModifierInfluenceFlag */ + int flag; + char _pad1[4]; + /** Filter by layer name. */ + char layer_name[64]; + /** Filter by stroke material. */ + struct Material *material; + /** Filter by layer pass. */ + int layer_pass; + /** Filter by material pass. */ + int material_pass; + /** #MAX_VGROUP_NAME. */ + char vertex_group_name[64]; + struct CurveMapping *custom_curve; + void *_pad2; +} GreasePencilModifierInfluenceData; + +typedef enum GreasePencilModifierInfluenceFlag { + GREASE_PENCIL_INFLUENCE_INVERT_LAYER_FILTER = (1 << 0), + GREASE_PENCIL_INFLUENCE_USE_LAYER_PASS_FILTER = (1 << 1), + GREASE_PENCIL_INFLUENCE_INVERT_LAYER_PASS_FILTER = (1 << 2), + GREASE_PENCIL_INFLUENCE_INVERT_MATERIAL_FILTER = (1 << 3), + GREASE_PENCIL_INFLUENCE_USE_MATERIAL_PASS_FILTER = (1 << 4), + GREASE_PENCIL_INFLUENCE_INVERT_MATERIAL_PASS_FILTER = (1 << 5), + GREASE_PENCIL_INFLUENCE_INVERT_VERTEX_GROUP = (1 << 6), + GREASE_PENCIL_INFLUENCE_USE_CUSTOM_CURVE = (1 << 7), +} GreasePencilModifierInfluenceFlag; + +typedef struct GreasePencilOpacityModifierData { + ModifierData modifier; + GreasePencilModifierInfluenceData influence; + /** GreasePencilOpacityModifierFlag */ + int flag; + /** GreasePencilModifierColorMode */ + char color_mode; + char _pad1[3]; + float color_factor; + float hardness_factor; + void *_pad2; +} GreasePencilOpacityModifierData; + +/** Which attributes are affected by color modifiers. */ +typedef enum GreasePencilModifierColorMode { + MOD_GREASE_PENCIL_COLOR_STROKE = 0, + MOD_GREASE_PENCIL_COLOR_FILL = 1, + MOD_GREASE_PENCIL_COLOR_BOTH = 2, + MOD_GREASE_PENCIL_COLOR_HARDNESS = 3, +} GreasePencilModifierColorMode; + +typedef enum GreasePencilOpacityModifierFlag { + MOD_GREASE_PENCIL_OPACITY_OPEN_INFLUENCE_PANEL = (1 << 0), + /* Use vertex group as opacity factors instead of influence. */ + MOD_GREASE_PENCIL_OPACITY_USE_WEIGHT_AS_FACTOR = (1 << 1), + /* Set the opacity for every point in a stroke, otherwise multiply existing opacity. */ + MOD_GREASE_PENCIL_OPACITY_USE_UNIFORM_OPACITY = (1 << 2), +} GreasePencilOpacityModifierFlag; diff --git a/source/blender/makesdna/intern/dna_defaults.c b/source/blender/makesdna/intern/dna_defaults.c index 82dfa47d76d..5249d684906 100644 --- a/source/blender/makesdna/intern/dna_defaults.c +++ b/source/blender/makesdna/intern/dna_defaults.c @@ -322,6 +322,7 @@ SDNA_DEFAULT_DECL_STRUCT(DashGpencilModifierData); SDNA_DEFAULT_DECL_STRUCT(DashGpencilModifierSegment); SDNA_DEFAULT_DECL_STRUCT(ShrinkwrapGpencilModifierData); SDNA_DEFAULT_DECL_STRUCT(EnvelopeGpencilModifierData); +SDNA_DEFAULT_DECL_STRUCT(GreasePencilOpacityModifierData); #undef SDNA_DEFAULT_DECL_STRUCT @@ -566,6 +567,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = { SDNA_DEFAULT_DECL(DashGpencilModifierSegment), SDNA_DEFAULT_DECL(ShrinkwrapGpencilModifierData), SDNA_DEFAULT_DECL(EnvelopeGpencilModifierData), + SDNA_DEFAULT_DECL(GreasePencilOpacityModifierData), }; #undef SDNA_DEFAULT_DECL #undef SDNA_DEFAULT_DECL_EX diff --git a/source/blender/makesrna/intern/rna_modifier.cc b/source/blender/makesrna/intern/rna_modifier.cc index 3dbc5db42bc..3b6306e3852 100644 --- a/source/blender/makesrna/intern/rna_modifier.cc +++ b/source/blender/makesrna/intern/rna_modifier.cc @@ -100,6 +100,11 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = { ICON_MOD_VERTEX_WEIGHT, "Vertex Weight Proximity", "Set the vertex group weights based on the distance to another target object"}, + {eModifierType_GreasePencilOpacity, + "GREASE_PENCIL_OPACITY", + ICON_MOD_OPACITY, + "Opacity", + "Change the opacity of the strokes"}, RNA_ENUM_ITEM_HEADING(N_("Generate"), nullptr), {eModifierType_Array, @@ -668,11 +673,13 @@ const EnumPropertyItem rna_enum_subdivision_boundary_smooth_items[] = { #ifdef RNA_RUNTIME # include "DNA_curve_types.h" # include "DNA_fluid_types.h" +# include "DNA_material_types.h" # include "DNA_particle_types.h" # include "BKE_cachefile.h" # include "BKE_context.hh" # include "BKE_deform.h" +# include "BKE_material.h" # include "BKE_mesh_runtime.hh" # include "BKE_modifier.hh" # include "BKE_object.hh" @@ -1705,6 +1712,78 @@ static IDProperty **rna_NodesModifier_properties(PointerRNA *ptr) NodesModifierSettings *settings = &nmd->settings; return &settings->properties; } + +bool rna_GreasePencilModifier_material_poll(PointerRNA *ptr, PointerRNA value) +{ + Object *ob = reinterpret_cast(ptr->owner_id); + Material *ma = reinterpret_cast(value.owner_id); + + return BKE_object_material_index_get(ob, ma) != -1; +} + +/* Write material to a generic target pointer without the final modifier struct. */ +static void rna_GreasePencilModifier_material_set(PointerRNA *ptr, + PointerRNA value, + ReportList *reports, + Material **ma_target) +{ + Object *ob = reinterpret_cast(ptr->owner_id); + Material *ma = reinterpret_cast(value.owner_id); + + if (ma == nullptr || BKE_object_material_index_get(ob, ma) != -1) { + id_lib_extern(reinterpret_cast(ob)); + *ma_target = ma; + } + else { + BKE_reportf( + reports, + RPT_ERROR, + "Cannot assign material '%s', it has to be used by the grease pencil object already", + ma->id.name); + } +} + +# define RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(_type) \ + static void rna_##_type##Modifier_material_filter_set( \ + PointerRNA *ptr, PointerRNA value, ReportList *reports) \ + { \ + _type##ModifierData *tmd = static_cast<_type##ModifierData *>(ptr->data); \ + rna_GreasePencilModifier_material_set(ptr, value, reports, &tmd->influence.material); \ + } + +# define RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(_type) \ + static void rna_##_type##Modifier_vertex_group_name_set(PointerRNA *ptr, const char *value) \ + { \ + _type##ModifierData *tmd = static_cast<_type##ModifierData *>(ptr->data); \ + rna_object_vgroup_name_set(ptr, \ + value, \ + tmd->influence.vertex_group_name, \ + sizeof(tmd->influence.vertex_group_name)); \ + } + +RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilOpacity); +RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilOpacity); + +static void rna_GreasePencilOpacityModifier_opacity_factor_range( + PointerRNA *ptr, float *min, float *max, float *softmin, float *softmax) +{ + GreasePencilOpacityModifierData *omd = static_cast(ptr->data); + + *min = 0.0f; + *softmin = 0.0f; + *softmax = (omd->flag & MOD_GREASE_PENCIL_OPACITY_USE_UNIFORM_OPACITY) ? 1.0f : 2.0f; + *max = *softmax; +} + +static void rna_GreasePencilOpacityModifier_opacity_factor_max_set(PointerRNA *ptr, float value) +{ + GreasePencilOpacityModifierData *omd = static_cast(ptr->data); + + omd->color_factor = (omd->flag & MOD_GREASE_PENCIL_OPACITY_USE_UNIFORM_OPACITY) ? + std::min(value, 1.0f) : + value; +} + #else static void rna_def_modifier_panel_open_prop(StructRNA *srna, const char *identifier, const int id) @@ -7423,6 +7502,186 @@ static void rna_def_modifier_volume_to_mesh(BlenderRNA *brna) RNA_define_lib_overridable(false); } +static void rna_def_modifier_grease_pencil_layer_filter(StructRNA *srna) +{ + PropertyRNA *prop; + + prop = RNA_def_property(srna, "layer_filter", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, nullptr, "influence.layer_name"); + RNA_def_property_ui_text(prop, "Layer", "Layer name"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_layer_pass_filter", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "influence.flag", GREASE_PENCIL_INFLUENCE_USE_LAYER_PASS_FILTER); + RNA_def_property_ui_text(prop, "Use Layer Pass", "Use layer pass filter"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "layer_pass_filter", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, nullptr, "influence.layer_pass"); + RNA_def_property_range(prop, 0, 100); + RNA_def_property_ui_text(prop, "Layer Pass", "Layer pass filter"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "invert_layer_filter", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "influence.flag", GREASE_PENCIL_INFLUENCE_INVERT_LAYER_FILTER); + RNA_def_property_ui_text(prop, "Invert Layer", "Invert layer filter"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "invert_layer_pass_filter", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "influence.flag", GREASE_PENCIL_INFLUENCE_INVERT_LAYER_PASS_FILTER); + RNA_def_property_ui_text(prop, "Invert Layer Pass", "Invert layer pass filter"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); +} + +static void rna_def_modifier_grease_pencil_material_filter(StructRNA *srna, + const char *material_set_fn) +{ + PropertyRNA *prop; + + prop = RNA_def_property(srna, "material_filter", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, nullptr, "influence.material"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_pointer_funcs( + prop, nullptr, material_set_fn, nullptr, "rna_GreasePencilModifier_material_poll"); + RNA_def_property_ui_text(prop, "Material", "Material used for filtering"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_material_pass_filter", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "influence.flag", GREASE_PENCIL_INFLUENCE_USE_MATERIAL_PASS_FILTER); + RNA_def_property_ui_text(prop, "Use Material Pass", "Use material pass filter"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "material_pass_filter", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, nullptr, "influence.material_pass"); + RNA_def_property_range(prop, 0, 100); + RNA_def_property_ui_text(prop, "Material Pass", "Material pass"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "invert_material_filter", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "influence.flag", GREASE_PENCIL_INFLUENCE_INVERT_MATERIAL_FILTER); + RNA_def_property_ui_text(prop, "Invert Material", "Invert material filter"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "invert_material_pass_filter", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "influence.flag", GREASE_PENCIL_INFLUENCE_INVERT_MATERIAL_PASS_FILTER); + RNA_def_property_ui_text(prop, "Invert Material Pass", "Invert material pass filter"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); +} + +static void rna_def_modifier_grease_pencil_vertex_group(StructRNA *srna, + const char *vertex_group_name_set_fn) +{ + PropertyRNA *prop; + + prop = RNA_def_property(srna, "vertex_group_name", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, nullptr, "influence.vertex_group_name"); + RNA_def_property_ui_text(prop, "Vertex Group", "Vertex group name for modulating the deform"); + RNA_def_property_string_funcs(prop, nullptr, nullptr, vertex_group_name_set_fn); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "invert_vertex_group", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "influence.flag", GREASE_PENCIL_INFLUENCE_INVERT_VERTEX_GROUP); + RNA_def_property_ui_text(prop, "Invert Vertex Group", "Invert vertex group weights"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); +} + +static void rna_def_modifier_grease_pencil_custom_curve(StructRNA *srna) +{ + PropertyRNA *prop; + + prop = RNA_def_property(srna, "use_custom_curve", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "influence.flag", GREASE_PENCIL_INFLUENCE_USE_CUSTOM_CURVE); + RNA_def_property_ui_text( + prop, "Use Custom Curve", "Use a custom curve to define a factor along the strokes"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "custom_curve", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, nullptr, "influence.custom_curve"); + RNA_def_property_ui_text(prop, "Curve", "Custom curve to apply effect"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); +} + +static void rna_def_modifier_grease_pencil_opacity(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + static const EnumPropertyItem color_mode_items[] = { + {MOD_GREASE_PENCIL_COLOR_BOTH, "BOTH", 0, "Stroke & Fill", "Modify fill and stroke colors"}, + {MOD_GREASE_PENCIL_COLOR_STROKE, "STROKE", 0, "Stroke", "Modify stroke color only"}, + {MOD_GREASE_PENCIL_COLOR_FILL, "FILL", 0, "Fill", "Modify fill color only"}, + {MOD_GREASE_PENCIL_COLOR_HARDNESS, "HARDNESS", 0, "Hardness", "Modify stroke hardness"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + srna = RNA_def_struct(brna, "GreasePencilOpacityModifier", "Modifier"); + RNA_def_struct_ui_text(srna, "Grease Pencil Opacity Modifier", ""); + RNA_def_struct_sdna(srna, "GreasePencilOpacityModifierData"); + RNA_def_struct_ui_icon(srna, ICON_MOD_OPACITY); + + rna_def_modifier_grease_pencil_layer_filter(srna); + rna_def_modifier_grease_pencil_material_filter( + srna, "rna_GreasePencilOpacityModifier_material_filter_set"); + rna_def_modifier_grease_pencil_vertex_group( + srna, "rna_GreasePencilOpacityModifier_vertex_group_name_set"); + rna_def_modifier_grease_pencil_custom_curve(srna); + + prop = RNA_def_property(srna, "open_influence_panel", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "flag", MOD_GREASE_PENCIL_OPACITY_OPEN_INFLUENCE_PANEL); + RNA_def_property_ui_text(prop, "Open Influence Panel", "Open the influence panel"); + RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, nullptr); + + RNA_define_lib_overridable(true); + + prop = RNA_def_property(srna, "color_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, color_mode_items); + RNA_def_property_ui_text(prop, "Mode", "Attributes to modify"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "color_factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, nullptr, "color_factor"); + RNA_def_property_ui_range(prop, 0, 2.0, 0.1, 2); + RNA_def_property_float_funcs(prop, + nullptr, + "rna_GreasePencilOpacityModifier_opacity_factor_max_set", + "rna_GreasePencilOpacityModifier_opacity_factor_range"); + RNA_def_property_ui_text(prop, "Opacity Factor", "Factor of opacity"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "hardness_factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, nullptr, "hardness_factor"); + RNA_def_property_range(prop, 0.0, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0, FLT_MAX, 0.1, 2); + RNA_def_property_ui_text(prop, "Hardness Factor", "Factor of stroke hardness"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_weight_as_factor", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "flag", MOD_GREASE_PENCIL_OPACITY_USE_WEIGHT_AS_FACTOR); + RNA_def_property_ui_text( + prop, "Use Weight as Factor", "Use vertex group weight as factor instead of influence"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_uniform_opacity", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "flag", MOD_GREASE_PENCIL_OPACITY_USE_UNIFORM_OPACITY); + RNA_def_property_ui_text( + prop, "Uniform Opacity", "Replace the stroke opacity instead of modulating each point"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + RNA_define_lib_overridable(false); +} + void RNA_def_modifier(BlenderRNA *brna) { StructRNA *srna; @@ -7583,6 +7842,7 @@ void RNA_def_modifier(BlenderRNA *brna) rna_def_modifier_mesh_to_volume(brna); rna_def_modifier_volume_displace(brna); rna_def_modifier_volume_to_mesh(brna); + rna_def_modifier_grease_pencil_opacity(brna); } #endif diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index ef9ff57b606..f3920f2560f 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -44,6 +44,8 @@ set(SRC intern/MOD_edgesplit.cc intern/MOD_explode.cc intern/MOD_fluid.cc + intern/MOD_grease_pencil_opacity.cc + intern/MOD_grease_pencil_util.cc intern/MOD_hook.cc intern/MOD_laplaciandeform.cc intern/MOD_laplaciansmooth.cc @@ -97,6 +99,7 @@ set(SRC MOD_modifiertypes.hh MOD_nodes.hh + intern/MOD_grease_pencil_util.hh intern/MOD_meshcache_util.hh intern/MOD_solidify_util.hh intern/MOD_ui_common.hh diff --git a/source/blender/modifiers/MOD_modifiertypes.hh b/source/blender/modifiers/MOD_modifiertypes.hh index 64f95d4479f..909e6b3ce18 100644 --- a/source/blender/modifiers/MOD_modifiertypes.hh +++ b/source/blender/modifiers/MOD_modifiertypes.hh @@ -73,6 +73,7 @@ extern ModifierTypeInfo modifierType_Nodes; extern ModifierTypeInfo modifierType_MeshToVolume; extern ModifierTypeInfo modifierType_VolumeDisplace; extern ModifierTypeInfo modifierType_VolumeToMesh; +extern ModifierTypeInfo modifierType_GreasePencilOpacity; /* MOD_util.cc */ diff --git a/source/blender/modifiers/intern/MOD_grease_pencil_opacity.cc b/source/blender/modifiers/intern/MOD_grease_pencil_opacity.cc new file mode 100644 index 00000000000..52d31d7bc06 --- /dev/null +++ b/source/blender/modifiers/intern/MOD_grease_pencil_opacity.cc @@ -0,0 +1,340 @@ +/* SPDX-FileCopyrightText: 2024 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup modifiers + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_defaults.h" +#include "DNA_modifier_types.h" +#include "DNA_scene_types.h" + +#include "BKE_colortools.hh" +#include "BKE_curves.hh" +#include "BKE_geometry_set.hh" +#include "BKE_grease_pencil.hh" +#include "BKE_modifier.hh" + +#include "BLO_read_write.hh" + +#include "DEG_depsgraph_query.hh" + +#include "UI_interface.hh" +#include "UI_resources.hh" + +#include "BLT_translation.h" + +#include "WM_types.hh" + +#include "RNA_access.hh" +#include "RNA_enum_types.hh" +#include "RNA_prototypes.h" + +#include "MOD_grease_pencil_util.hh" +#include "MOD_modifiertypes.hh" +#include "MOD_ui_common.hh" + +#include + +namespace blender { + +using bke::greasepencil::Drawing; +using bke::greasepencil::FramesMapKey; +using bke::greasepencil::Layer; + +static void init_data(ModifierData *md) +{ + auto *omd = reinterpret_cast(md); + + BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(omd, modifier)); + + MEMCPY_STRUCT_AFTER(omd, DNA_struct_default_get(GreasePencilOpacityModifierData), modifier); + modifier::greasepencil::init_influence_data(&omd->influence, true); +} + +static void copy_data(const ModifierData *md, ModifierData *target, const int flag) +{ + const auto *omd = reinterpret_cast(md); + auto *tomd = reinterpret_cast(target); + + modifier::greasepencil::free_influence_data(&tomd->influence); + + BKE_modifier_copydata_generic(md, target, flag); + modifier::greasepencil::copy_influence_data(&omd->influence, &tomd->influence, flag); +} + +static void free_data(ModifierData *md) +{ + auto *omd = reinterpret_cast(md); + modifier::greasepencil::free_influence_data(&omd->influence); +} + +static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data) +{ + auto *omd = reinterpret_cast(md); + modifier::greasepencil::foreach_influence_ID_link(&omd->influence, ob, walk, user_data); +} + +static void modify_stroke_color(const GreasePencilOpacityModifierData &omd, + bke::CurvesGeometry &curves, + const IndexMask &curves_mask) +{ + const bool use_uniform_opacity = (omd.flag & MOD_GREASE_PENCIL_OPACITY_USE_UNIFORM_OPACITY); + const bool use_weight_as_factor = (omd.flag & MOD_GREASE_PENCIL_OPACITY_USE_WEIGHT_AS_FACTOR); + const bool invert_vertex_group = (omd.influence.flag & + GREASE_PENCIL_INFLUENCE_INVERT_VERTEX_GROUP); + const bool use_curve = (omd.influence.flag & GREASE_PENCIL_INFLUENCE_USE_CUSTOM_CURVE); + const OffsetIndices points_by_curve = curves.points_by_curve(); + + bke::MutableAttributeAccessor attributes = curves.attributes_for_write(); + bke::SpanAttributeWriter opacities = attributes.lookup_or_add_for_write_span( + "opacity", bke::AttrDomain::Point); + const VArray vgroup_weights = + attributes + .lookup_or_default(omd.influence.vertex_group_name, bke::AttrDomain::Point, 1.0f) + .varray; + + curves_mask.foreach_index(GrainSize(512), [&](const int64_t curve_i) { + const IndexRange points = points_by_curve[curve_i]; + for (const int64_t point_i : points) { + const float curve_input = points.size() >= 2 ? + (float(point_i - points.first()) / float(points.size() - 1)) : + 0.0f; + const float curve_factor = use_curve ? BKE_curvemapping_evaluateF( + omd.influence.custom_curve, 0, curve_input) : + 1.0f; + + if (use_uniform_opacity) { + opacities.span[point_i] = std::clamp(omd.color_factor * curve_factor, 0.0f, 1.0f); + } + else if (use_weight_as_factor) { + /* Use vertex group weights as opacity factors. */ + opacities.span[point_i] = std::clamp( + omd.color_factor * curve_factor * vgroup_weights[point_i], 0.0f, 1.0f); + } + else { + /* Use vertex group weights as influence factors. */ + const float vgroup_weight = vgroup_weights[point_i]; + const float vgroup_influence = invert_vertex_group ? 1.0f - vgroup_weight : vgroup_weight; + opacities.span[point_i] = std::clamp( + opacities.span[point_i] + omd.color_factor * curve_factor * vgroup_influence - 1.0f, + 0.0f, + 1.0f); + } + } + }); + + opacities.finish(); +} + +static void modify_fill_color(const GreasePencilOpacityModifierData &omd, + bke::CurvesGeometry &curves, + const IndexMask &curves_mask) +{ + const bool use_vgroup_opacity = (omd.flag & MOD_GREASE_PENCIL_OPACITY_USE_WEIGHT_AS_FACTOR); + const bool invert_vertex_group = (omd.influence.flag & + GREASE_PENCIL_INFLUENCE_INVERT_VERTEX_GROUP); + const OffsetIndices points_by_curve = curves.points_by_curve(); + + bke::MutableAttributeAccessor attributes = curves.attributes_for_write(); + /* Fill color opacity per stroke. */ + bke::SpanAttributeWriter fill_opacities = attributes.lookup_or_add_for_write_span( + "fill_opacity", bke::AttrDomain::Curve); + VArray vgroup_weights = attributes + .lookup_or_default(omd.influence.vertex_group_name, + bke::AttrDomain::Point, + 1.0f) + .varray; + + curves_mask.foreach_index(GrainSize(512), [&](int64_t curve_i) { + if (use_vgroup_opacity) { + /* Use the first stroke point as vertex weight. */ + const IndexRange points = points_by_curve[curve_i]; + const float stroke_weight = points.is_empty() ? 1.0f : vgroup_weights[points.first()]; + const float stroke_influence = invert_vertex_group ? 1.0f - stroke_weight : stroke_weight; + + fill_opacities.span[curve_i] = std::clamp(stroke_influence, 0.0f, 1.0f); + } + else { + fill_opacities.span[curve_i] = std::clamp(omd.color_factor, 0.0f, 1.0f); + } + }); + + fill_opacities.finish(); +} + +static void modify_hardness(const GreasePencilOpacityModifierData &omd, + bke::CurvesGeometry &curves, + const IndexMask &curves_mask) +{ + bke::MutableAttributeAccessor attributes = curves.attributes_for_write(); + bke::SpanAttributeWriter hardnesses = attributes.lookup_for_write_span("hardness"); + if (!hardnesses) { + return; + } + + curves_mask.foreach_index(GrainSize(512), [&](int64_t curve_i) { + hardnesses.span[curve_i] = std::clamp( + hardnesses.span[curve_i] * omd.hardness_factor, 0.0f, 1.0f); + }); + + hardnesses.finish(); +} + +static void modify_curves(ModifierData *md, + const ModifierEvalContext *ctx, + bke::CurvesGeometry &curves) +{ + auto *omd = reinterpret_cast(md); + + IndexMaskMemory mask_memory; + IndexMask curves_mask = modifier::greasepencil::get_filtered_stroke_mask( + ctx->object, curves, omd->influence, mask_memory); + + switch (omd->color_mode) { + case MOD_GREASE_PENCIL_COLOR_STROKE: + modify_stroke_color(*omd, curves, curves_mask); + break; + case MOD_GREASE_PENCIL_COLOR_FILL: + modify_fill_color(*omd, curves, curves_mask); + break; + case MOD_GREASE_PENCIL_COLOR_BOTH: + modify_stroke_color(*omd, curves, curves_mask); + modify_fill_color(*omd, curves, curves_mask); + break; + case MOD_GREASE_PENCIL_COLOR_HARDNESS: + modify_hardness(*omd, curves, curves_mask); + break; + } +} + +static void modify_geometry_set(ModifierData *md, + const ModifierEvalContext *ctx, + bke::GeometrySet *geometry_set) +{ + auto *omd = reinterpret_cast(md); + const Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph); + const int frame = scene->r.cfra; + + GreasePencil *grease_pencil = geometry_set->get_grease_pencil_for_write(); + if (grease_pencil == nullptr) { + return; + } + + IndexMaskMemory mask_memory; + IndexMask layer_mask = modifier::greasepencil::get_filtered_layer_mask( + *grease_pencil, omd->influence, mask_memory); + Vector drawings = modifier::greasepencil::get_drawings_for_write( + *grease_pencil, layer_mask, frame); + threading::parallel_for_each(drawings.index_range(), [&](int64_t i) { + modify_curves(md, ctx, drawings[i]->strokes_for_write()); + }); +} + +static void panel_draw(const bContext *C, Panel *panel) +{ + uiLayout *layout = panel->layout; + + PointerRNA ob_ptr; + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); + + uiLayoutSetPropSep(layout, true); + + const GreasePencilModifierColorMode color_mode = GreasePencilModifierColorMode( + RNA_enum_get(ptr, "color_mode")); + + uiItemR(layout, ptr, "color_mode", UI_ITEM_NONE, nullptr, ICON_NONE); + + if (color_mode == MOD_GREASE_PENCIL_COLOR_HARDNESS) { + uiItemR(layout, ptr, "hardness_factor", UI_ITEM_NONE, nullptr, ICON_NONE); + } + else { + const bool use_uniform_opacity = RNA_boolean_get(ptr, "use_uniform_opacity"); + const bool use_weight_as_factor = RNA_boolean_get(ptr, "use_weight_as_factor"); + + uiItemR(layout, ptr, "use_uniform_opacity", UI_ITEM_NONE, nullptr, ICON_NONE); + const char *text = (use_uniform_opacity) ? IFACE_("Opacity") : IFACE_("Opacity Factor"); + + uiLayout *row = uiLayoutRow(layout, true); + uiLayoutSetActive(row, !use_weight_as_factor || use_uniform_opacity); + uiItemR(row, ptr, "color_factor", UI_ITEM_NONE, text, ICON_NONE); + if (!use_uniform_opacity) { + uiLayout *sub = uiLayoutRow(row, true); + uiLayoutSetActive(sub, true); + uiItemR(row, ptr, "use_weight_as_factor", UI_ITEM_NONE, "", ICON_MOD_VERTEX_WEIGHT); + } + } + + if (uiLayout *influence_panel = uiLayoutPanel( + C, layout, "Influence", ptr, "open_influence_panel")) + { + modifier::greasepencil::draw_layer_filter_settings(C, influence_panel, ptr); + modifier::greasepencil::draw_material_filter_settings(C, influence_panel, ptr); + modifier::greasepencil::draw_vertex_group_settings(C, influence_panel, ptr); + modifier::greasepencil::draw_custom_curve_settings(C, influence_panel, ptr); + } + + modifier_panel_end(layout, ptr); +} + +static void panel_register(ARegionType *region_type) +{ + modifier_panel_register(region_type, eModifierType_GreasePencilOpacity, panel_draw); +} + +static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const ModifierData *md) +{ + const auto *omd = reinterpret_cast(md); + + BLO_write_struct(writer, GreasePencilOpacityModifierData, omd); + modifier::greasepencil::write_influence_data(writer, &omd->influence); +} + +static void blend_read(BlendDataReader *reader, ModifierData *md) +{ + auto *omd = reinterpret_cast(md); + + modifier::greasepencil::read_influence_data(reader, &omd->influence); +} + +} // namespace blender + +ModifierTypeInfo modifierType_GreasePencilOpacity = { + /*idname*/ "GreasePencilOpacity", + /*name*/ N_("GreasePencilOpacity"), + /*struct_name*/ "GreasePencilOpacityModifierData", + /*struct_size*/ sizeof(GreasePencilOpacityModifierData), + /*srna*/ &RNA_GreasePencilOpacityModifier, + /*type*/ ModifierTypeType::Nonconstructive, + /*flags*/ + static_cast( + eModifierTypeFlag_AcceptsGreasePencil | eModifierTypeFlag_SupportsEditmode | + eModifierTypeFlag_EnableInEditmode | eModifierTypeFlag_SupportsMapping), + /*icon*/ ICON_MOD_OPACITY, + + /*copy_data*/ blender::copy_data, + + /*deform_verts*/ nullptr, + /*deform_matrices*/ nullptr, + /*deform_verts_EM*/ nullptr, + /*deform_matrices_EM*/ nullptr, + /*modify_mesh*/ nullptr, + /*modify_geometry_set*/ blender::modify_geometry_set, + + /*init_data*/ blender::init_data, + /*required_data_mask*/ nullptr, + /*free_data*/ blender::free_data, + /*is_disabled*/ nullptr, + /*update_depsgraph*/ nullptr, + /*depends_on_time*/ nullptr, + /*depends_on_normals*/ nullptr, + /*foreach_ID_link*/ blender::foreach_ID_link, + /*foreach_tex_link*/ nullptr, + /*free_runtime_data*/ nullptr, + /*panel_register*/ blender::panel_register, + /*blend_write*/ blender::blend_write, + /*blend_read*/ blender::blend_read, +}; diff --git a/source/blender/modifiers/intern/MOD_grease_pencil_util.cc b/source/blender/modifiers/intern/MOD_grease_pencil_util.cc new file mode 100644 index 00000000000..fdb2e4c496b --- /dev/null +++ b/source/blender/modifiers/intern/MOD_grease_pencil_util.cc @@ -0,0 +1,333 @@ +/* SPDX-FileCopyrightText: 2024 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup modifiers + */ + +#include "MOD_grease_pencil_util.hh" + +#include "BLI_set.hh" + +#include "DNA_grease_pencil_types.h" +#include "DNA_material_types.h" +#include "DNA_modifier_types.h" +#include "DNA_screen_types.h" + +#include "BKE_colortools.hh" +#include "BKE_curves.hh" +#include "BKE_grease_pencil.hh" +#include "BKE_lib_query.h" +#include "BKE_material.h" + +#include "BLO_read_write.hh" + +#include "DNA_defaults.h" + +#include "DEG_depsgraph_query.hh" + +#include "MOD_ui_common.hh" + +#include "RNA_access.hh" +#include "RNA_prototypes.h" + +#include "UI_interface.hh" + +namespace blender::modifier::greasepencil { + +using bke::greasepencil::Drawing; +using bke::greasepencil::Layer; + +void init_influence_data(GreasePencilModifierInfluenceData *influence_data, + const bool has_custom_curve) +{ + if (has_custom_curve) { + influence_data->custom_curve = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + BKE_curvemapping_init(influence_data->custom_curve); + } +} + +void copy_influence_data(const GreasePencilModifierInfluenceData *influence_data_src, + GreasePencilModifierInfluenceData *influence_data_dst, + const int /*flag*/) +{ + memcpy(influence_data_dst, influence_data_src, sizeof(GreasePencilModifierInfluenceData)); + influence_data_dst->custom_curve = BKE_curvemapping_copy(influence_data_src->custom_curve); +} + +void free_influence_data(GreasePencilModifierInfluenceData *influence_data) +{ + if (influence_data->custom_curve) { + BKE_curvemapping_free(influence_data->custom_curve); + influence_data->custom_curve = nullptr; + } +} + +void foreach_influence_ID_link(GreasePencilModifierInfluenceData *influence_data, + Object *ob, + IDWalkFunc walk, + void *user_data) +{ + walk(user_data, ob, (ID **)&influence_data->material, IDWALK_CB_USER); +} + +void write_influence_data(BlendWriter *writer, + const GreasePencilModifierInfluenceData *influence_data) +{ + if (influence_data->custom_curve) { + BKE_curvemapping_blend_write(writer, influence_data->custom_curve); + } +} + +void read_influence_data(BlendDataReader *reader, + GreasePencilModifierInfluenceData *influence_data) +{ + BLO_read_data_address(reader, &influence_data->custom_curve); + if (influence_data->custom_curve) { + BKE_curvemapping_blend_read(reader, influence_data->custom_curve); + /* Make sure the internal table exists. */ + BKE_curvemapping_init(influence_data->custom_curve); + } +} + +void draw_layer_filter_settings(const bContext * /*C*/, uiLayout *layout, PointerRNA *ptr) +{ + PointerRNA ob_ptr = RNA_pointer_create(ptr->owner_id, &RNA_Object, ptr->owner_id); + PointerRNA obj_data_ptr = RNA_pointer_get(&ob_ptr, "data"); + const bool use_layer_pass = RNA_boolean_get(ptr, "use_layer_pass_filter"); + uiLayout *row, *col, *sub, *subsub; + + uiLayoutSetPropSep(layout, true); + + col = uiLayoutColumn(layout, true); + row = uiLayoutRow(col, true); + uiLayoutSetPropDecorate(row, false); + uiItemPointerR(row, ptr, "layer_filter", &obj_data_ptr, "layers", nullptr, ICON_GREASEPENCIL); + sub = uiLayoutRow(row, true); + uiItemR(sub, ptr, "invert_layer_filter", UI_ITEM_NONE, "", ICON_ARROW_LEFTRIGHT); + + row = uiLayoutRowWithHeading(col, true, "Layer Pass"); + uiLayoutSetPropDecorate(row, false); + sub = uiLayoutRow(row, true); + uiItemR(sub, ptr, "use_layer_pass_filter", UI_ITEM_NONE, "", ICON_NONE); + subsub = uiLayoutRow(sub, true); + uiLayoutSetActive(subsub, use_layer_pass); + uiItemR(subsub, ptr, "layer_pass_filter", UI_ITEM_NONE, "", ICON_NONE); + uiItemR(subsub, ptr, "invert_layer_pass_filter", UI_ITEM_NONE, "", ICON_ARROW_LEFTRIGHT); +} + +void draw_material_filter_settings(const bContext * /*C*/, uiLayout *layout, PointerRNA *ptr) +{ + PointerRNA ob_ptr = RNA_pointer_create(ptr->owner_id, &RNA_Object, ptr->owner_id); + PointerRNA obj_data_ptr = RNA_pointer_get(&ob_ptr, "data"); + const bool use_material_pass = RNA_boolean_get(ptr, "use_material_pass_filter"); + uiLayout *row, *col, *sub, *subsub; + + uiLayoutSetPropSep(layout, true); + + col = uiLayoutColumn(layout, true); + row = uiLayoutRow(col, true); + uiLayoutSetPropDecorate(row, false); + uiItemPointerR( + row, ptr, "material_filter", &obj_data_ptr, "materials", nullptr, ICON_SHADING_TEXTURE); + sub = uiLayoutRow(row, true); + uiItemR(sub, ptr, "invert_material_filter", UI_ITEM_NONE, "", ICON_ARROW_LEFTRIGHT); + + row = uiLayoutRowWithHeading(col, true, "Material Pass"); + uiLayoutSetPropDecorate(row, false); + sub = uiLayoutRow(row, true); + uiItemR(sub, ptr, "use_material_pass_filter", UI_ITEM_NONE, "", ICON_NONE); + subsub = uiLayoutRow(sub, true); + uiLayoutSetActive(subsub, use_material_pass); + uiItemR(subsub, ptr, "material_pass_filter", UI_ITEM_NONE, "", ICON_NONE); + uiItemR(subsub, ptr, "invert_material_pass_filter", UI_ITEM_NONE, "", ICON_ARROW_LEFTRIGHT); +} + +void draw_vertex_group_settings(const bContext * /*C*/, uiLayout *layout, PointerRNA *ptr) +{ + PointerRNA ob_ptr = RNA_pointer_create(ptr->owner_id, &RNA_Object, ptr->owner_id); + bool has_vertex_group = RNA_string_length(ptr, "vertex_group_name") != 0; + uiLayout *row, *sub; + + uiLayoutSetPropSep(layout, true); + + row = uiLayoutRow(layout, true); + uiItemPointerR(row, ptr, "vertex_group_name", &ob_ptr, "vertex_groups", nullptr, ICON_NONE); + sub = uiLayoutRow(row, true); + uiLayoutSetActive(sub, has_vertex_group); + uiLayoutSetPropDecorate(sub, false); + uiItemR(sub, ptr, "invert_vertex_group", UI_ITEM_NONE, "", ICON_ARROW_LEFTRIGHT); +} + +void draw_custom_curve_settings(const bContext * /*C*/, uiLayout *layout, PointerRNA *ptr) +{ + bool use_custom_curve = RNA_boolean_get(ptr, "use_custom_curve"); + + uiLayoutSetPropSep(layout, true); + + uiItemR(layout, ptr, "use_custom_curve", UI_ITEM_NONE, nullptr, ICON_NONE); + if (use_custom_curve) { + uiTemplateCurveMapping(layout, ptr, "custom_curve", 0, false, false, false, false); + } +} + +/** + * Get a list of pass IDs used by grease pencil materials. + * This way the material pass can be looked up by index instead of having to get the material for + * each curve. + */ +static Vector get_grease_pencil_material_passes(const Object *ob) +{ + short *totcol = BKE_object_material_len_p(const_cast(ob)); + Vector result(*totcol); + Material *ma = nullptr; + for (short i = 0; i < *totcol; i++) { + ma = BKE_object_material_get(const_cast(ob), i + 1); + /* Pass index of the grease pencil material. */ + result[i] = ma->gp_style->index; + } + return result; +} + +static IndexMask get_filtered_layer_mask(const GreasePencil &grease_pencil, + const std::optional layer_name_filter, + const std::optional layer_pass_filter, + const bool layer_filter_invert, + const bool layer_pass_filter_invert, + IndexMaskMemory &memory) +{ + const IndexMask full_mask = grease_pencil.layers().index_range(); + if (!layer_name_filter && !layer_pass_filter) { + return full_mask; + } + + bke::AttributeAccessor layer_attributes = grease_pencil.attributes(); + const Span layers = grease_pencil.layers(); + const VArray layer_passes = + layer_attributes.lookup_or_default("pass", bke::AttrDomain::Layer, 0).varray; + + IndexMask result = IndexMask::from_predicate( + full_mask, GrainSize(4096), memory, [&](const int64_t layer_i) { + if (layer_name_filter) { + const Layer &layer = *layers[layer_i]; + const bool match = (layer.name() == layer_name_filter.value()); + if (match == layer_filter_invert) { + return false; + } + } + if (layer_pass_filter) { + const int layer_pass = layer_passes.get(layer_i); + const bool match = (layer_pass == layer_pass_filter.value()); + if (match == layer_pass_filter_invert) { + return false; + } + } + return true; + }); + return result; +} + +IndexMask get_filtered_layer_mask(const GreasePencil &grease_pencil, + const GreasePencilModifierInfluenceData &influence_data, + IndexMaskMemory &memory) +{ + return get_filtered_layer_mask( + grease_pencil, + influence_data.layer_name[0] != '\0' ? + std::make_optional(influence_data.layer_name) : + std::nullopt, + (influence_data.flag & GREASE_PENCIL_INFLUENCE_USE_LAYER_PASS_FILTER) ? + std::make_optional(influence_data.layer_pass) : + std::nullopt, + influence_data.flag & GREASE_PENCIL_INFLUENCE_INVERT_LAYER_FILTER, + influence_data.flag & GREASE_PENCIL_INFLUENCE_INVERT_LAYER_PASS_FILTER, + memory); +} + +static IndexMask get_filtered_stroke_mask(const Object *ob, + const bke::CurvesGeometry &curves, + const Material *material_filter, + const std::optional material_pass_filter, + const bool material_filter_invert, + const bool material_pass_filter_invert, + IndexMaskMemory &memory) +{ + const IndexMask full_mask = curves.curves_range(); + if (!material_filter && !material_pass_filter) { + return full_mask; + } + + const int material_filter_index = BKE_object_material_index_get( + const_cast(ob), const_cast(material_filter)); + const Vector material_pass_by_index = get_grease_pencil_material_passes(ob); + + bke::AttributeAccessor attributes = curves.attributes(); + VArray stroke_materials = + attributes.lookup_or_default("material_index", bke::AttrDomain::Curve, 0).varray; + + IndexMask result = IndexMask::from_predicate( + full_mask, GrainSize(4096), memory, [&](const int64_t stroke_i) { + const int material_index = stroke_materials.get(stroke_i); + if (material_filter != nullptr) { + const bool match = (material_index == material_filter_index); + if (match == material_filter_invert) { + return false; + } + } + if (material_pass_filter) { + const int material_pass = material_pass_by_index[material_index]; + const bool match = (material_pass == material_pass_filter.value()); + if (match == material_pass_filter_invert) { + return false; + } + } + return true; + }); + return result; +} + +IndexMask get_filtered_stroke_mask(const Object *ob, + const bke::CurvesGeometry &curves, + const GreasePencilModifierInfluenceData &influence_data, + IndexMaskMemory &memory) +{ + return get_filtered_stroke_mask( + ob, + curves, + influence_data.material, + (influence_data.flag & GREASE_PENCIL_INFLUENCE_USE_MATERIAL_PASS_FILTER) ? + std::make_optional(influence_data.material_pass) : + std::nullopt, + influence_data.flag & GREASE_PENCIL_INFLUENCE_INVERT_MATERIAL_FILTER, + influence_data.flag & GREASE_PENCIL_INFLUENCE_INVERT_MATERIAL_PASS_FILTER, + memory); +} + +Vector get_drawings_for_write(GreasePencil &grease_pencil, + const IndexMask &layer_mask, + int frame) +{ + /* Set of unique drawing indices. */ + Set drawing_indices; + for (const int64_t i : layer_mask.index_range()) { + Layer *layer = grease_pencil.layers_for_write()[layer_mask[i]]; + const int drawing_index = layer->drawing_index_at(frame); + if (drawing_index >= 0) { + drawing_indices.add(drawing_index); + } + } + + /* List of owned drawings, ignore drawing references to other data blocks. */ + Vector drawings; + for (const int drawing_index : drawing_indices) { + GreasePencilDrawingBase *drawing_base = grease_pencil.drawing(drawing_index); + if (drawing_base->type == GP_DRAWING) { + GreasePencilDrawing *drawing = reinterpret_cast(drawing_base); + drawings.append(&drawing->wrap()); + } + } + return drawings; +} + +} // namespace blender::modifier::greasepencil diff --git a/source/blender/modifiers/intern/MOD_grease_pencil_util.hh b/source/blender/modifiers/intern/MOD_grease_pencil_util.hh new file mode 100644 index 00000000000..9652ff47e0d --- /dev/null +++ b/source/blender/modifiers/intern/MOD_grease_pencil_util.hh @@ -0,0 +1,66 @@ +/* SPDX-FileCopyrightText: 2024 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup modifiers + */ + +#pragma once + +#include "BLI_index_mask.hh" +#include "BLI_vector.hh" + +#include "BKE_modifier.hh" + +struct ARegionType; +struct bContext; +struct GreasePencil; +struct GreasePencilModifierInfluenceData; +struct GreasePencilModifierLayerFilter; +struct GreasePencilModifierMaterialFilter; +struct PanelType; +struct PointerRNA; +struct uiLayout; +namespace blender::bke { +class CurvesGeometry; +namespace greasepencil { +class Drawing; +} +} // namespace blender::bke + +namespace blender::modifier::greasepencil { + +void init_influence_data(GreasePencilModifierInfluenceData *influence_data, bool has_custom_curve); +void copy_influence_data(const GreasePencilModifierInfluenceData *influence_data_src, + GreasePencilModifierInfluenceData *influence_data_dst, + int flag); +void free_influence_data(GreasePencilModifierInfluenceData *influence_data); +void foreach_influence_ID_link(GreasePencilModifierInfluenceData *influence_data, + Object *ob, + IDWalkFunc walk, + void *user_data); +void write_influence_data(BlendWriter *writer, + const GreasePencilModifierInfluenceData *influence_data); +void read_influence_data(BlendDataReader *reader, + GreasePencilModifierInfluenceData *influence_data); + +void draw_layer_filter_settings(const bContext *C, uiLayout *layout, PointerRNA *ptr); +void draw_material_filter_settings(const bContext *C, uiLayout *layout, PointerRNA *ptr); +void draw_vertex_group_settings(const bContext *C, uiLayout *layout, PointerRNA *ptr); +void draw_custom_curve_settings(const bContext *C, uiLayout *layout, PointerRNA *ptr); + +IndexMask get_filtered_layer_mask(const GreasePencil &grease_pencil, + const GreasePencilModifierInfluenceData &influence_data, + IndexMaskMemory &memory); + +IndexMask get_filtered_stroke_mask(const Object *ob, + const bke::CurvesGeometry &curves, + const GreasePencilModifierInfluenceData &influence_data, + IndexMaskMemory &memory); + +Vector get_drawings_for_write(GreasePencil &grease_pencil, + const IndexMask &layer_mask, + int frame); + +} // namespace blender::modifier::greasepencil diff --git a/source/blender/modifiers/intern/MOD_util.cc b/source/blender/modifiers/intern/MOD_util.cc index 2e1913ff938..7db69740186 100644 --- a/source/blender/modifiers/intern/MOD_util.cc +++ b/source/blender/modifiers/intern/MOD_util.cc @@ -270,5 +270,6 @@ void modifier_type_init(ModifierTypeInfo *types[]) INIT_TYPE(VolumeDisplace); INIT_TYPE(VolumeToMesh); INIT_TYPE(Nodes); + INIT_TYPE(GreasePencilOpacity); #undef INIT_TYPE } -- 2.30.2 From c7d228c1deb980e4cd8af0a1fd23a9f7a43f255a Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 16 Jan 2024 10:56:44 -0500 Subject: [PATCH 020/244] Cleanup: Make format --- intern/guardedalloc/intern/mallocn.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/intern/guardedalloc/intern/mallocn.c b/intern/guardedalloc/intern/mallocn.c index adc39f8eb6d..04f06e950ce 100644 --- a/intern/guardedalloc/intern/mallocn.c +++ b/intern/guardedalloc/intern/mallocn.c @@ -21,7 +21,8 @@ /* If JEMALLOC is used, it reads this global variable and enables background * threads to purge dirty pages. Otherwise we release memory too slowly or not * at all if the thread that did the allocation stays inactive. */ -const char *malloc_conf = "background_thread:true,dirty_decay_ms:4000,thp:always,metadata_thp:always"; +const char *malloc_conf = + "background_thread:true,dirty_decay_ms:4000,thp:always,metadata_thp:always"; #endif /* NOTE: Keep in sync with MEM_use_lockfree_allocator(). */ -- 2.30.2 From f49b856bb0a485014af96a24e382e4a1aa8a055b Mon Sep 17 00:00:00 2001 From: Falk David Date: Tue, 16 Jan 2024 18:17:26 +0100 Subject: [PATCH 021/244] Fix: GPv3: Opacity modifier compilation error The parameters for `threading::parallel_for_each` were not correct. --- source/blender/modifiers/intern/MOD_grease_pencil_opacity.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_grease_pencil_opacity.cc b/source/blender/modifiers/intern/MOD_grease_pencil_opacity.cc index 52d31d7bc06..5595aea498e 100644 --- a/source/blender/modifiers/intern/MOD_grease_pencil_opacity.cc +++ b/source/blender/modifiers/intern/MOD_grease_pencil_opacity.cc @@ -229,8 +229,8 @@ static void modify_geometry_set(ModifierData *md, *grease_pencil, omd->influence, mask_memory); Vector drawings = modifier::greasepencil::get_drawings_for_write( *grease_pencil, layer_mask, frame); - threading::parallel_for_each(drawings.index_range(), [&](int64_t i) { - modify_curves(md, ctx, drawings[i]->strokes_for_write()); + threading::parallel_for_each(drawings, [&](Drawing *drawing) { + modify_curves(md, ctx, drawing->strokes_for_write()); }); } -- 2.30.2 From e010e9957916e587951d9067002fb71241df9027 Mon Sep 17 00:00:00 2001 From: Falk David Date: Tue, 16 Jan 2024 18:19:24 +0100 Subject: [PATCH 022/244] GPv3: Change display name of modifier to "Opacity" --- source/blender/modifiers/intern/MOD_grease_pencil_opacity.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/modifiers/intern/MOD_grease_pencil_opacity.cc b/source/blender/modifiers/intern/MOD_grease_pencil_opacity.cc index 5595aea498e..ac58cf1eaa8 100644 --- a/source/blender/modifiers/intern/MOD_grease_pencil_opacity.cc +++ b/source/blender/modifiers/intern/MOD_grease_pencil_opacity.cc @@ -304,7 +304,7 @@ static void blend_read(BlendDataReader *reader, ModifierData *md) ModifierTypeInfo modifierType_GreasePencilOpacity = { /*idname*/ "GreasePencilOpacity", - /*name*/ N_("GreasePencilOpacity"), + /*name*/ N_("Opacity"), /*struct_name*/ "GreasePencilOpacityModifierData", /*struct_size*/ sizeof(GreasePencilOpacityModifierData), /*srna*/ &RNA_GreasePencilOpacityModifier, -- 2.30.2 From a622a8573fd33cdeab0e97e6ee6b79bd3640e3df Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 16 Jan 2024 18:32:41 +0100 Subject: [PATCH 023/244] Fix #117168: crash when using layout panel in popover Using layout panels in this context is not yet supported. Therefor, the fix is to raise an exception instead of crashing. The problem here is that layout panels need an a parent panel which has a persistent state. The popover currently does not only has a very short-lived panel currently that is freed directly after drawing. Also see `ui_paneltype_draw_impl`. It's likely that support for this can be added in the future but it's currently unclear how many changes this would require. --- source/blender/makesrna/intern/rna_ui_api.cc | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/source/blender/makesrna/intern/rna_ui_api.cc b/source/blender/makesrna/intern/rna_ui_api.cc index 786a4820e95..e472d160be0 100644 --- a/source/blender/makesrna/intern/rna_ui_api.cc +++ b/source/blender/makesrna/intern/rna_ui_api.cc @@ -782,6 +782,7 @@ static uiLayout *rna_uiLayoutColumnWithHeading( struct uiLayout *rna_uiLayoutPanelProp(uiLayout *layout, bContext *C, + ReportList *reports, PointerRNA *data, const char *property, const char *text, @@ -789,11 +790,17 @@ struct uiLayout *rna_uiLayoutPanelProp(uiLayout *layout, const bool translate) { text = rna_translate_ui_text(text, text_ctxt, nullptr, nullptr, translate); + Panel *panel = uiLayoutGetRootPanel(layout); + if (panel == nullptr) { + BKE_reportf(reports, RPT_ERROR, "Layout panels can not be used in this context"); + return nullptr; + } return uiLayoutPanel(C, layout, text, data, property); } struct uiLayout *rna_uiLayoutPanel(uiLayout *layout, bContext *C, + ReportList *reports, const char *idname, const char *text, const char *text_ctxt, @@ -802,6 +809,10 @@ struct uiLayout *rna_uiLayoutPanel(uiLayout *layout, { text = RNA_translate_ui_text(text, text_ctxt, nullptr, nullptr, translate); Panel *panel = uiLayoutGetRootPanel(layout); + if (panel == nullptr) { + BKE_reportf(reports, RPT_ERROR, "Layout panels can not be used in this context"); + return nullptr; + } LayoutPanelState *state = BKE_panel_layout_panel_state_ensure(panel, idname, default_closed); PointerRNA state_ptr = RNA_pointer_create(nullptr, &RNA_LayoutPanelState, state); return uiLayoutPanel(C, layout, text, &state_ptr, "is_open"); @@ -1079,7 +1090,7 @@ void RNA_api_ui_layout(StructRNA *srna) RNA_def_function_ui_description(func, "Creates a collapsable panel. Whether it is open or closed is " "stored in the region using the given idname"); - RNA_def_function_flag(func, FUNC_USE_CONTEXT); + RNA_def_function_flag(func, FUNC_USE_CONTEXT | FUNC_USE_REPORTS); parm = RNA_def_string(func, "idname", nullptr, 0, "", "Identifier of the panel"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); api_ui_item_common_text(func); @@ -1102,7 +1113,7 @@ void RNA_api_ui_layout(StructRNA *srna) "region, it is stored in the provided boolean property. This should be used when multiple " "instances of the same panel can exist. For example one for every item in a collection " "property or list"); - RNA_def_function_flag(func, FUNC_USE_CONTEXT); + RNA_def_function_flag(func, FUNC_USE_CONTEXT | FUNC_USE_REPORTS); parm = RNA_def_pointer( func, "data", "AnyType", "", "Data from which to take the open-state property"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); -- 2.30.2 From ceeb7d62a8ea5891fd2587dee561fbd0f9c6ed5a Mon Sep 17 00:00:00 2001 From: Falk David Date: Tue, 16 Jan 2024 18:37:28 +0100 Subject: [PATCH 024/244] Cleanup: GPv3: Modifier filter UI --- .../modifiers/intern/MOD_grease_pencil_util.cc | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_grease_pencil_util.cc b/source/blender/modifiers/intern/MOD_grease_pencil_util.cc index fdb2e4c496b..28aa1cec404 100644 --- a/source/blender/modifiers/intern/MOD_grease_pencil_util.cc +++ b/source/blender/modifiers/intern/MOD_grease_pencil_util.cc @@ -148,11 +148,13 @@ void draw_vertex_group_settings(const bContext * /*C*/, uiLayout *layout, Pointe { PointerRNA ob_ptr = RNA_pointer_create(ptr->owner_id, &RNA_Object, ptr->owner_id); bool has_vertex_group = RNA_string_length(ptr, "vertex_group_name") != 0; - uiLayout *row, *sub; + uiLayout *row, *col, *sub; uiLayoutSetPropSep(layout, true); - row = uiLayoutRow(layout, true); + col = uiLayoutColumn(layout, true); + row = uiLayoutRow(col, true); + uiLayoutSetPropDecorate(row, false); uiItemPointerR(row, ptr, "vertex_group_name", &ob_ptr, "vertex_groups", nullptr, ICON_NONE); sub = uiLayoutRow(row, true); uiLayoutSetActive(sub, has_vertex_group); @@ -163,12 +165,14 @@ void draw_vertex_group_settings(const bContext * /*C*/, uiLayout *layout, Pointe void draw_custom_curve_settings(const bContext * /*C*/, uiLayout *layout, PointerRNA *ptr) { bool use_custom_curve = RNA_boolean_get(ptr, "use_custom_curve"); + uiLayout *row; uiLayoutSetPropSep(layout, true); - - uiItemR(layout, ptr, "use_custom_curve", UI_ITEM_NONE, nullptr, ICON_NONE); + row = uiLayoutRow(layout, true); + uiLayoutSetPropDecorate(row, false); + uiItemR(row, ptr, "use_custom_curve", UI_ITEM_NONE, "Custom Curve", ICON_NONE); if (use_custom_curve) { - uiTemplateCurveMapping(layout, ptr, "custom_curve", 0, false, false, false, false); + uiTemplateCurveMapping(row, ptr, "custom_curve", 0, false, false, false, false); } } -- 2.30.2 From 93a9dde14357da997ac5bfeacbc4004642a30e20 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 16 Jan 2024 13:25:46 -0500 Subject: [PATCH 025/244] Cleanup: Make format --- source/blender/modifiers/intern/MOD_grease_pencil_opacity.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_grease_pencil_opacity.cc b/source/blender/modifiers/intern/MOD_grease_pencil_opacity.cc index ac58cf1eaa8..24fd3d10b8b 100644 --- a/source/blender/modifiers/intern/MOD_grease_pencil_opacity.cc +++ b/source/blender/modifiers/intern/MOD_grease_pencil_opacity.cc @@ -229,9 +229,8 @@ static void modify_geometry_set(ModifierData *md, *grease_pencil, omd->influence, mask_memory); Vector drawings = modifier::greasepencil::get_drawings_for_write( *grease_pencil, layer_mask, frame); - threading::parallel_for_each(drawings, [&](Drawing *drawing) { - modify_curves(md, ctx, drawing->strokes_for_write()); - }); + threading::parallel_for_each( + drawings, [&](Drawing *drawing) { modify_curves(md, ctx, drawing->strokes_for_write()); }); } static void panel_draw(const bContext *C, Panel *panel) -- 2.30.2 From 85c9bd526800e47bd485fcc54203bcf8c877f77d Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 16 Jan 2024 13:35:38 -0500 Subject: [PATCH 026/244] Cleanup: Remove unnecessary extern symbols in undofile header --- source/blender/blenloader/BLO_undofile.hh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/blender/blenloader/BLO_undofile.hh b/source/blender/blenloader/BLO_undofile.hh index c15a2302d6c..7503460d7bb 100644 --- a/source/blender/blenloader/BLO_undofile.hh +++ b/source/blender/blenloader/BLO_undofile.hh @@ -80,25 +80,25 @@ void BLO_memfile_chunk_add(MemFileWriteData *mem_data, const char *buf, size_t s */ /* **************** support for memory-write, for undo buffers *************** */ -extern void BLO_memfile_free(MemFile *memfile); +void BLO_memfile_free(MemFile *memfile); /** * Result is that 'first' is being freed. * To keep the #MemFile linked list of consistent, `first` is always first in list. */ -extern void BLO_memfile_merge(MemFile *first, MemFile *second); +void BLO_memfile_merge(MemFile *first, MemFile *second); /** * Clear is_identical_future before adding next memfile. */ -extern void BLO_memfile_clear_future(MemFile *memfile); +void BLO_memfile_clear_future(MemFile *memfile); /* Utilities. */ -extern Main *BLO_memfile_main_get(MemFile *memfile, Main *bmain, Scene **r_scene); +Main *BLO_memfile_main_get(MemFile *memfile, Main *bmain, Scene **r_scene); /** * Saves .blend using undo buffer. * * \return success. */ -extern bool BLO_memfile_write_file(MemFile *memfile, const char *filepath); +bool BLO_memfile_write_file(MemFile *memfile, const char *filepath); FileReader *BLO_memfile_new_filereader(MemFile *memfile, int undo_direction); -- 2.30.2 From 602d593b30364da7968aa075533d50e94852f85c Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 16 Jan 2024 13:36:43 -0500 Subject: [PATCH 027/244] Cleanup: Use LISTBASE_FOREACH file in memfile merge --- source/blender/blenloader/intern/undofile.cc | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/source/blender/blenloader/intern/undofile.cc b/source/blender/blenloader/intern/undofile.cc index 4cfc214ac90..1cf23e38aa4 100644 --- a/source/blender/blenloader/intern/undofile.cc +++ b/source/blender/blenloader/intern/undofile.cc @@ -58,9 +58,7 @@ void BLO_memfile_merge(MemFile *first, MemFile *second) BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); /* First, detect all memchunks in second memfile that are not owned by it. */ - for (MemFileChunk *sc = static_cast(second->chunks.first); sc != nullptr; - sc = static_cast(sc->next)) - { + LISTBASE_FOREACH (MemFileChunk *, sc, &second->chunks) { if (sc->is_identical) { BLI_ghash_insert(buffer_to_second_memchunk, (void *)sc->buf, sc); } @@ -68,9 +66,7 @@ void BLO_memfile_merge(MemFile *first, MemFile *second) /* Now, check all chunks from first memfile (the one we are removing), and if a memchunk owned by * it is also used by the second memfile, transfer the ownership. */ - for (MemFileChunk *fc = static_cast(first->chunks.first); fc != nullptr; - fc = static_cast(fc->next)) - { + LISTBASE_FOREACH (MemFileChunk *, fc, &first->chunks) { if (!fc->is_identical) { MemFileChunk *sc = static_cast( BLI_ghash_lookup(buffer_to_second_memchunk, fc->buf)); -- 2.30.2 From b341e6ea7db3189ccc2359af132af5eac89ec39a Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 16 Jan 2024 19:41:30 +0100 Subject: [PATCH 028/244] Cleanup: Use Map instead of GHash for memfile writing The goal is to simplify future work on #106903. Pull Request: https://projects.blender.org/blender/blender/pulls/117141 --- source/blender/blenloader/BLO_undofile.hh | 4 +-- source/blender/blenloader/intern/undofile.cc | 29 ++++--------------- source/blender/blenloader/intern/writefile.cc | 13 ++++----- 3 files changed, 13 insertions(+), 33 deletions(-) diff --git a/source/blender/blenloader/BLO_undofile.hh b/source/blender/blenloader/BLO_undofile.hh index 7503460d7bb..8301bc7c579 100644 --- a/source/blender/blenloader/BLO_undofile.hh +++ b/source/blender/blenloader/BLO_undofile.hh @@ -11,8 +11,8 @@ #include "BLI_filereader.h" #include "BLI_listbase.h" +#include "BLI_map.hh" -struct GHash; struct Main; struct Scene; @@ -45,7 +45,7 @@ struct MemFileWriteData { MemFileChunk *reference_current_chunk; /** Maps an ID session uuid to its first reference MemFileChunk, if existing. */ - GHash *id_session_uuid_mapping; + blender::Map id_session_uuid_mapping; }; struct MemFileUndoData { diff --git a/source/blender/blenloader/intern/undofile.cc b/source/blender/blenloader/intern/undofile.cc index 1cf23e38aa4..245d713a3fe 100644 --- a/source/blender/blenloader/intern/undofile.cc +++ b/source/blender/blenloader/intern/undofile.cc @@ -25,7 +25,6 @@ #include "DNA_listBase.h" #include "BLI_blenlib.h" -#include "BLI_ghash.h" #include "BLO_readfile.h" #include "BLO_undofile.hh" @@ -54,13 +53,12 @@ void BLO_memfile_merge(MemFile *first, MemFile *second) { /* We use this mapping to store the memory buffers from second memfile chunks which are not owned * by it (i.e. shared with some previous memory steps). */ - GHash *buffer_to_second_memchunk = BLI_ghash_new( - BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); + blender::Map buffer_to_second_memchunk; /* First, detect all memchunks in second memfile that are not owned by it. */ LISTBASE_FOREACH (MemFileChunk *, sc, &second->chunks) { if (sc->is_identical) { - BLI_ghash_insert(buffer_to_second_memchunk, (void *)sc->buf, sc); + buffer_to_second_memchunk.add(sc->buf, sc); } } @@ -68,9 +66,7 @@ void BLO_memfile_merge(MemFile *first, MemFile *second) * it is also used by the second memfile, transfer the ownership. */ LISTBASE_FOREACH (MemFileChunk *, fc, &first->chunks) { if (!fc->is_identical) { - MemFileChunk *sc = static_cast( - BLI_ghash_lookup(buffer_to_second_memchunk, fc->buf)); - if (sc != nullptr) { + if (MemFileChunk *sc = buffer_to_second_memchunk.lookup_default(fc->buf, nullptr)) { BLI_assert(sc->is_identical); sc->is_identical = false; fc->is_identical = true; @@ -81,8 +77,6 @@ void BLO_memfile_merge(MemFile *first, MemFile *second) } } - BLI_ghash_free(buffer_to_second_memchunk, nullptr, nullptr); - BLO_memfile_free(first); } @@ -109,22 +103,11 @@ void BLO_memfile_write_init(MemFileWriteData *mem_data, * current Main data-base broke the order matching with the memchunks from previous step. */ if (reference_memfile != nullptr) { - mem_data->id_session_uuid_mapping = BLI_ghash_new( - BLI_ghashutil_inthash_p_simple, BLI_ghashutil_intcmp, __func__); uint current_session_uuid = MAIN_ID_SESSION_UUID_UNSET; LISTBASE_FOREACH (MemFileChunk *, mem_chunk, &reference_memfile->chunks) { if (!ELEM(mem_chunk->id_session_uuid, MAIN_ID_SESSION_UUID_UNSET, current_session_uuid)) { current_session_uuid = mem_chunk->id_session_uuid; - void **entry; - if (!BLI_ghash_ensure_p(mem_data->id_session_uuid_mapping, - POINTER_FROM_UINT(current_session_uuid), - &entry)) - { - *entry = mem_chunk; - } - else { - BLI_assert_unreachable(); - } + mem_data->id_session_uuid_mapping.add_new(current_session_uuid, mem_chunk); } } } @@ -132,9 +115,7 @@ void BLO_memfile_write_init(MemFileWriteData *mem_data, void BLO_memfile_write_finalize(MemFileWriteData *mem_data) { - if (mem_data->id_session_uuid_mapping != nullptr) { - BLI_ghash_free(mem_data->id_session_uuid_mapping, nullptr, nullptr); - } + mem_data->id_session_uuid_mapping.clear_and_shrink(); } void BLO_memfile_chunk_add(MemFileWriteData *mem_data, const char *buf, size_t size) diff --git a/source/blender/blenloader/intern/writefile.cc b/source/blender/blenloader/intern/writefile.cc index 4eeb342a036..00292239111 100644 --- a/source/blender/blenloader/intern/writefile.cc +++ b/source/blender/blenloader/intern/writefile.cc @@ -435,7 +435,7 @@ struct BlendWriter { static WriteData *writedata_new(WriteWrap *ww) { - WriteData *wd = static_cast(MEM_callocN(sizeof(*wd), "writedata")); + WriteData *wd = MEM_new(__func__); wd->sdna = DNA_sdna_current_get(); @@ -487,7 +487,7 @@ static void writedata_free(WriteData *wd) if (wd->buffer.buf) { MEM_freeN(wd->buffer.buf); } - MEM_freeN(wd); + MEM_delete(wd); } /** \} */ @@ -620,14 +620,13 @@ static void mywrite_id_begin(WriteData *wd, ID *id) MemFileChunk *prev_memchunk = curr_memchunk != nullptr ? static_cast(curr_memchunk->prev) : nullptr; - if (wd->mem.id_session_uuid_mapping != nullptr && - (curr_memchunk == nullptr || curr_memchunk->id_session_uuid != id->session_uuid || + if ((curr_memchunk == nullptr || curr_memchunk->id_session_uuid != id->session_uuid || (prev_memchunk != nullptr && (prev_memchunk->id_session_uuid == curr_memchunk->id_session_uuid)))) { - void *ref = BLI_ghash_lookup(wd->mem.id_session_uuid_mapping, - POINTER_FROM_UINT(id->session_uuid)); - if (ref != nullptr) { + if (MemFileChunk *ref = wd->mem.id_session_uuid_mapping.lookup_default(id->session_uuid, + nullptr)) + { wd->mem.reference_current_chunk = static_cast(ref); } /* Else, no existing memchunk found, i.e. this is supposed to be a new ID. */ -- 2.30.2 From 22087e908ab470253c642b55d615226057267733 Mon Sep 17 00:00:00 2001 From: Aras Pranckevicius Date: Tue, 16 Jan 2024 20:39:09 +0100 Subject: [PATCH 029/244] Cleanup: simplify code in IMB_transform There were some abstractions inside `IMB_transform` implementation that were kinda getting in the way. I want to speed up Subsampled3x3 filtering by reformulating the math being done, but that needs to sample byte images while storing intermediate result as a float -- but the whole `Pixel`, `PixelPointer` et al. machinery was not quite prepared to deal with that. Feels like those helper classes do not achieve much, just make the code longer. So remove them (`Pixel`, `PixelPointer`, `ChannelConverter` etc.) and use simple functions. Now the code is more straightforward (IMHO) and 270 lines shorter (40% of the whole file!), and allows me to do the follow-up optimization easier. No functional changes. Pull Request: https://projects.blender.org/blender/blender/pulls/117182 --- source/blender/imbuf/intern/transform.cc | 837 ++++++++--------------- 1 file changed, 282 insertions(+), 555 deletions(-) diff --git a/source/blender/imbuf/intern/transform.cc b/source/blender/imbuf/intern/transform.cc index 9eeca79a0ea..f531f527df4 100644 --- a/source/blender/imbuf/intern/transform.cc +++ b/source/blender/imbuf/intern/transform.cc @@ -6,7 +6,6 @@ * \ingroup imbuf */ -#include #include #include "BLI_math_color_blend.h" @@ -22,73 +21,39 @@ namespace blender::imbuf::transform { -struct TransformUserData { - /** \brief Source image buffer to read from. */ +struct TransformContext { const ImBuf *src; - /** \brief Destination image buffer to write to. */ ImBuf *dst; - /** \brief UV coordinates at the origin (0,0) in source image space. */ + eIMBTransformMode mode; + + /* UV coordinates at the destination origin (0,0) in source image space. */ float2 start_uv; - /** - * \brief delta UV coordinates along the source image buffer, when moving a single pixel in the X - * axis of the dst image buffer. - */ + /* Source UV step delta, when moving along one destination pixel in X axis. */ float2 add_x; - /** - * \brief delta UV coordinate along the source image buffer, when moving a single pixel in the Y - * axes of the dst image buffer. - */ + /* Source UV step delta, when moving along one destination pixel in Y axis. */ float2 add_y; - struct { - /** - * Contains per sub-sample a delta to be added to the uv of the source image buffer. - */ - Vector delta_uvs; - } subsampling; + /* Per-subsample source image delta UVs. */ + Vector subsampling_deltas; - struct { - IndexRange x_range; - IndexRange y_range; - } destination_region; + IndexRange dst_region_x_range; + IndexRange dst_region_y_range; - /** - * \brief Cropping region in source image pixel space. - */ + /* Cropping region in source image pixel space. */ rctf src_crop; - /** - * \brief Initialize the start_uv, add_x and add_y fields based on the given transform matrix. - */ - void init(const float4x4 &transform_matrix, - const int num_subsamples, - const bool do_crop_destination_region) + void init(const float4x4 &transform_matrix, const int num_subsamples, const bool has_source_crop) { - init_start_uv(transform_matrix); - init_add_x(transform_matrix); - init_add_y(transform_matrix); + start_uv = transform_matrix.location().xy(); + add_x = transform_matrix.x_axis().xy(); + add_y = transform_matrix.y_axis().xy(); init_subsampling(num_subsamples); - init_destination_region(transform_matrix, do_crop_destination_region); + init_destination_region(transform_matrix, has_source_crop); } private: - void init_start_uv(const float4x4 &transform_matrix) - { - start_uv = transform_matrix.location().xy(); - } - - void init_add_x(const float4x4 &transform_matrix) - { - add_x = transform_matrix.x_axis().xy(); - } - - void init_add_y(const float4x4 &transform_matrix) - { - add_y = transform_matrix.y_axis().xy(); - } - void init_subsampling(const int num_subsamples) { float2 subsample_add_x = add_x / num_subsamples; @@ -101,17 +66,16 @@ struct TransformUserData { float2 delta_uv = offset_x + offset_y; delta_uv += x * subsample_add_x; delta_uv += y * subsample_add_y; - subsampling.delta_uvs.append(delta_uv); + subsampling_deltas.append(delta_uv); } } } - void init_destination_region(const float4x4 &transform_matrix, - const bool do_crop_destination_region) + void init_destination_region(const float4x4 &transform_matrix, const bool has_source_crop) { - if (!do_crop_destination_region) { - destination_region.x_range = IndexRange(dst->x); - destination_region.y_range = IndexRange(dst->y); + if (!has_source_crop) { + dst_region_x_range = IndexRange(dst->x); + dst_region_y_range = IndexRange(dst->y); return; } @@ -136,99 +100,28 @@ struct TransformUserData { rcti dest_rect; BLI_rcti_init(&dest_rect, 0, dst->x, 0, dst->y); BLI_rcti_isect(&rect, &dest_rect, &rect); - destination_region.x_range = IndexRange(rect.xmin, BLI_rcti_size_x(&rect)); - destination_region.y_range = IndexRange(rect.ymin, BLI_rcti_size_y(&rect)); + dst_region_x_range = IndexRange(rect.xmin, BLI_rcti_size_x(&rect)); + dst_region_y_range = IndexRange(rect.ymin, BLI_rcti_size_y(&rect)); } }; -/** - * \brief Crop uv-coordinates that are outside the user data src_crop rect. - */ -struct CropSource { - /** - * \brief Should the source pixel at the given uv coordinate be discarded. - * - * Uses user_data.src_crop to determine if the uv coordinate should be skipped. - */ - static bool should_discard(const TransformUserData &user_data, const float2 &uv) - { - return uv.x < user_data.src_crop.xmin || uv.x >= user_data.src_crop.xmax || - uv.y < user_data.src_crop.ymin || uv.y >= user_data.src_crop.ymax; - } -}; +/* Crop uv-coordinates that are outside the user data src_crop rect. */ +static bool should_discard(const TransformContext &ctx, const float2 &uv) +{ + return uv.x < ctx.src_crop.xmin || uv.x >= ctx.src_crop.xmax || uv.y < ctx.src_crop.ymin || + uv.y >= ctx.src_crop.ymax; +} -/** - * \brief Discard that does not discard anything. - */ -struct NoDiscard { - /** - * \brief Should the source pixel at the given uv coordinate be discarded. - * - * Will never discard any pixels. - */ - static bool should_discard(const TransformUserData & /*user_data*/, const float2 & /*uv*/) - { - return false; - } -}; +template static T *init_pixel_pointer(const ImBuf *image, int x, int y); +template<> uchar *init_pixel_pointer(const ImBuf *image, int x, int y) +{ + return image->byte_buffer.data + (size_t(y) * image->x + x) * image->channels; +} +template<> float *init_pixel_pointer(const ImBuf *image, int x, int y) +{ + return image->float_buffer.data + (size_t(y) * image->x + x) * image->channels; +} -/** - * \brief Pointer to a pixel to write to in serial. - */ -template< - /** - * \brief Kind of buffer. - * Possible options: float, uchar. - */ - typename StorageType = float, - - /** - * \brief Number of channels of a single pixel. - */ - int NumChannels = 4> -class PixelPointer { - public: - static const int ChannelLen = NumChannels; - - private: - StorageType *pointer; - - public: - void init_pixel_pointer(const ImBuf *image_buffer, int2 start_coordinate) - { - const size_t offset = (start_coordinate.y * size_t(image_buffer->x) + start_coordinate.x) * - NumChannels; - - if constexpr (std::is_same_v) { - pointer = image_buffer->float_buffer.data + offset; - } - else if constexpr (std::is_same_v) { - pointer = const_cast( - static_cast(static_cast(image_buffer->byte_buffer.data)) + - offset); - } - else { - pointer = nullptr; - } - } - - /** - * \brief Get pointer to the current pixel to write to. - */ - StorageType *get_pointer() - { - return pointer; - } - - void increase_pixel_pointer() - { - pointer += NumChannels; - } -}; - -/** - * \brief Repeats UV coordinate. - */ static float wrap_uv(float value, int size) { int x = int(floorf(value)); @@ -241,416 +134,246 @@ static float wrap_uv(float value, int size) return x; } -/* TODO: should we use math_vectors for this. */ -template -class Pixel : public std::array { - public: - void clear() - { - for (int channel_index : IndexRange(NumChannels)) { - (*this)[channel_index] = 0; +template +static void add_subsample(const T *src, T *dst, int sample_number) +{ + BLI_STATIC_ASSERT((is_same_any_v), "Only uchar and float channels supported."); + + float factor = 1.0 / (sample_number + 1); + if constexpr (std::is_same_v) { + BLI_STATIC_ASSERT(NumChannels == 4, "Pixels using uchar requires to have 4 channels."); + blend_color_interpolate_byte(dst, dst, src, factor); + } + else if constexpr (std::is_same_v && NumChannels == 4) { + blend_color_interpolate_float(dst, dst, src, factor); + } + else if constexpr (std::is_same_v) { + for (int i : IndexRange(NumChannels)) { + dst[i] = dst[i] * (1.0f - factor) + src[i] * factor; } } +} - void add_subsample(const Pixel other, int sample_number) - { - BLI_STATIC_ASSERT((std::is_same_v) || (std::is_same_v), - "Only uchar and float channels supported."); +template +static void sample_nearest_float(const ImBuf *source, float u, float v, float *r_sample) +{ + int x1 = int(u); + int y1 = int(v); - float factor = 1.0 / (sample_number + 1); - if constexpr (std::is_same_v) { - BLI_STATIC_ASSERT(NumChannels == 4, "Pixels using uchar requires to have 4 channels."); - blend_color_interpolate_byte(this->data(), this->data(), other.data(), factor); - } - else if constexpr (std::is_same_v && NumChannels == 4) { - blend_color_interpolate_float(this->data(), this->data(), other.data(), factor); - } - else if constexpr (std::is_same_v) { - for (int channel_index : IndexRange(NumChannels)) { - (*this)[channel_index] = (*this)[channel_index] * (1.0 - factor) + - other[channel_index] * factor; - } - } - } -}; - -/** - * \brief Read a sample from an image buffer. - * - * A sampler can read from an image buffer. - */ -template< - /** \brief Interpolation mode to use when sampling. */ - eIMBInterpolationFilterMode Filter, - - /** \brief storage type of a single pixel channel (uchar or float). */ - typename StorageType, - /** - * \brief number of channels if the image to read. - * - * Must match the actual channels of the image buffer that is sampled. - */ - int NumChannels, - /** - * \brief Should UVs wrap - */ - bool UVWrapping> -class Sampler { - public: - using ChannelType = StorageType; - static const int ChannelLen = NumChannels; - using SampleType = Pixel; - - void sample(const ImBuf *source, const float2 &uv, SampleType &r_sample) - { - float u = uv.x; - float v = uv.y; - if constexpr (UVWrapping) { - u = wrap_uv(u, source->x); - v = wrap_uv(v, source->y); - } - /* BLI_bilinear_interpolation functions use `floor(uv)` and `floor(uv)+1` - * texels. For proper mapping between pixel and texel spaces, need to - * subtract 0.5. Same for bicubic. */ - if constexpr (Filter == IMB_FILTER_BILINEAR || Filter == IMB_FILTER_BICUBIC) { - u -= 0.5f; - v -= 0.5f; - } - if constexpr (Filter == IMB_FILTER_BILINEAR && std::is_same_v && - NumChannels == 4) - { - bilinear_interpolation_color_fl(source, r_sample.data(), u, v); - } - else if constexpr (Filter == IMB_FILTER_NEAREST && std::is_same_v && - NumChannels == 4) - { - nearest_interpolation_color_char(source, r_sample.data(), nullptr, u, v); - } - else if constexpr (Filter == IMB_FILTER_BILINEAR && std::is_same_v && - NumChannels == 4) - { - bilinear_interpolation_color_char(source, r_sample.data(), u, v); - } - else if constexpr (Filter == IMB_FILTER_BILINEAR && std::is_same_v) { - if constexpr (UVWrapping) { - BLI_bilinear_interpolation_wrap_fl(source->float_buffer.data, - r_sample.data(), - source->x, - source->y, - NumChannels, - UNPACK2(uv), - true, - true); - } - else { - BLI_bilinear_interpolation_fl( - source->float_buffer.data, r_sample.data(), source->x, source->y, NumChannels, u, v); - } - } - else if constexpr (Filter == IMB_FILTER_NEAREST && std::is_same_v) { - sample_nearest_float(source, u, v, r_sample); - } - else if constexpr (Filter == IMB_FILTER_BICUBIC && std::is_same_v) { - BLI_bicubic_interpolation_fl( - source->float_buffer.data, r_sample.data(), source->x, source->y, NumChannels, u, v); - } - else if constexpr (Filter == IMB_FILTER_BICUBIC && std::is_same_v && - NumChannels == 4) - { - BLI_bicubic_interpolation_char( - source->byte_buffer.data, r_sample.data(), source->x, source->y, u, v); - } - else { - /* Unsupported sampler. */ - BLI_assert_unreachable(); - } - } - - private: - void sample_nearest_float(const ImBuf *source, - const float u, - const float v, - SampleType &r_sample) - { - BLI_STATIC_ASSERT(std::is_same_v); - - /* ImBuf in must have a valid rect or rect_float, assume this is already checked */ - int x1 = int(u); - int y1 = int(v); - - /* Break when sample outside image is requested. */ - if (x1 < 0 || x1 >= source->x || y1 < 0 || y1 >= source->y) { - for (int i = 0; i < NumChannels; i++) { - r_sample[i] = 0.0f; - } - return; - } - - const size_t offset = (size_t(source->x) * y1 + x1) * NumChannels; - const float *dataF = source->float_buffer.data + offset; + /* Break when sample outside image is requested. */ + if (x1 < 0 || x1 >= source->x || y1 < 0 || y1 >= source->y) { for (int i = 0; i < NumChannels; i++) { - r_sample[i] = dataF[i]; - } - } -}; - -/** - * \brief Change the number of channels and store it. - * - * Template class to convert and store a sample in a PixelPointer. - * It supports: - * - 4 channel uchar -> 4 channel uchar. - * - 4 channel float -> 4 channel float. - * - 3 channel float -> 4 channel float. - * - 2 channel float -> 4 channel float. - * - 1 channel float -> 4 channel float. - */ -template -class ChannelConverter { - public: - using SampleType = Pixel; - using PixelType = PixelPointer; - - /** - * \brief Convert the number of channels of the given sample to match the pixel pointer and - * store it at the location the pixel_pointer points at. - */ - void convert_and_store(const SampleType &sample, PixelType &pixel_pointer) - { - if constexpr (std::is_same_v) { - BLI_STATIC_ASSERT(SourceNumChannels == 4, "Unsigned chars always have 4 channels."); - BLI_STATIC_ASSERT(DestinationNumChannels == 4, "Unsigned chars always have 4 channels."); - - copy_v4_v4_uchar(pixel_pointer.get_pointer(), sample.data()); - } - else if constexpr (std::is_same_v && SourceNumChannels == 4 && - DestinationNumChannels == 4) - { - copy_v4_v4(pixel_pointer.get_pointer(), sample.data()); - } - else if constexpr (std::is_same_v && SourceNumChannels == 3 && - DestinationNumChannels == 4) - { - copy_v4_fl4(pixel_pointer.get_pointer(), sample[0], sample[1], sample[2], 1.0f); - } - else if constexpr (std::is_same_v && SourceNumChannels == 2 && - DestinationNumChannels == 4) - { - copy_v4_fl4(pixel_pointer.get_pointer(), sample[0], sample[1], 0.0f, 1.0f); - } - else if constexpr (std::is_same_v && SourceNumChannels == 1 && - DestinationNumChannels == 4) - { - copy_v4_fl4(pixel_pointer.get_pointer(), sample[0], sample[0], sample[0], 1.0f); - } - else { - BLI_assert_unreachable(); + r_sample[i] = 0.0f; } + return; } - void mix_and_store(const SampleType &sample, PixelType &pixel_pointer, const float mix_factor) - { - if constexpr (std::is_same_v) { - BLI_STATIC_ASSERT(SourceNumChannels == 4, "Unsigned chars always have 4 channels."); - BLI_STATIC_ASSERT(DestinationNumChannels == 4, "Unsigned chars always have 4 channels."); - blend_color_interpolate_byte( - pixel_pointer.get_pointer(), pixel_pointer.get_pointer(), sample.data(), mix_factor); - } - else if constexpr (std::is_same_v && SourceNumChannels == 4 && - DestinationNumChannels == 4) - { - blend_color_interpolate_float( - pixel_pointer.get_pointer(), pixel_pointer.get_pointer(), sample.data(), mix_factor); - } - else { - BLI_assert_unreachable(); - } + size_t offset = (size_t(source->x) * y1 + x1) * NumChannels; + const float *dataF = source->float_buffer.data + offset; + for (int i = 0; i < NumChannels; i++) { + r_sample[i] = dataF[i]; } -}; - -/** - * \brief Processor for a scanline. - */ -template< - /** - * \brief Discard functor that implements `should_discard`. - */ - typename Discard, - - /** - * \brief Color interpolation function to read from the source buffer. - */ - typename Sampler, - - /** - * \brief Kernel to store to the destination buffer. - * Should be an PixelPointer - */ - typename OutputPixelPointer> -class ScanlineProcessor { - Discard discarder; - OutputPixelPointer output; - Sampler sampler; - - /** - * \brief Channels sizzling logic to convert between the input image buffer and the output - * image buffer. - */ - ChannelConverter - channel_converter; - - public: - /** - * \brief Inner loop of the transformations, processing a full scanline. - */ - void process(const TransformUserData *user_data, int scanline) - { - if (user_data->subsampling.delta_uvs.size() > 1) { - process_with_subsampling(user_data, scanline); - } - else { - process_one_sample_per_pixel(user_data, scanline); - } - } - - private: - void process_one_sample_per_pixel(const TransformUserData *user_data, int scanline) - { - /* Note: sample at pixel center for proper filtering. */ - float pixel_x = 0.5f; - float pixel_y = scanline + 0.5f; - float2 uv0 = user_data->start_uv + user_data->add_x * pixel_x + user_data->add_y * pixel_y; - - output.init_pixel_pointer(user_data->dst, - int2(user_data->destination_region.x_range.first(), scanline)); - for (int xi : user_data->destination_region.x_range) { - float2 uv = uv0 + xi * user_data->add_x; - if (!discarder.should_discard(*user_data, uv)) { - typename Sampler::SampleType sample; - sampler.sample(user_data->src, uv, sample); - channel_converter.convert_and_store(sample, output); - } - output.increase_pixel_pointer(); - } - } - - void process_with_subsampling(const TransformUserData *user_data, int scanline) - { - /* Note: sample at pixel center for proper filtering. */ - float pixel_x = 0.5f; - float pixel_y = scanline + 0.5f; - float2 uv0 = user_data->start_uv + user_data->add_x * pixel_x + user_data->add_y * pixel_y; - - output.init_pixel_pointer(user_data->dst, - int2(user_data->destination_region.x_range.first(), scanline)); - for (int xi : user_data->destination_region.x_range) { - float2 uv = uv0 + xi * user_data->add_x; - typename Sampler::SampleType sample; - sample.clear(); - int num_subsamples_added = 0; - - for (const float2 &delta_uv : user_data->subsampling.delta_uvs) { - const float2 subsample_uv = uv + delta_uv; - if (!discarder.should_discard(*user_data, subsample_uv)) { - typename Sampler::SampleType sub_sample; - sampler.sample(user_data->src, subsample_uv, sub_sample); - sample.add_subsample(sub_sample, num_subsamples_added); - num_subsamples_added += 1; - } - } - - if (num_subsamples_added != 0) { - const float mix_weight = float(num_subsamples_added) / - user_data->subsampling.delta_uvs.size(); - channel_converter.mix_and_store(sample, output, mix_weight); - } - output.increase_pixel_pointer(); - } - } -}; - -/** - * \brief callback function for threaded transformation. - */ -template void transform_scanline_function(void *custom_data, int scanline) -{ - const TransformUserData *user_data = static_cast(custom_data); - Processor processor; - processor.process(user_data, scanline); } +/* Read a pixel from an image buffer, with filtering/wrapping parameters. */ +template +static void sample_image(const ImBuf *source, float u, float v, T *r_sample) +{ + if constexpr (WrapUV) { + u = wrap_uv(u, source->x); + v = wrap_uv(v, source->y); + } + /* BLI_bilinear_interpolation functions use `floor(uv)` and `floor(uv)+1` + * texels. For proper mapping between pixel and texel spaces, need to + * subtract 0.5. Same for bicubic. */ + if constexpr (Filter == IMB_FILTER_BILINEAR || Filter == IMB_FILTER_BICUBIC) { + u -= 0.5f; + v -= 0.5f; + } + if constexpr (Filter == IMB_FILTER_BILINEAR && std::is_same_v && NumChannels == 4) { + bilinear_interpolation_color_fl(source, r_sample, u, v); + } + else if constexpr (Filter == IMB_FILTER_NEAREST && std::is_same_v && NumChannels == 4) + { + nearest_interpolation_color_char(source, r_sample, nullptr, u, v); + } + else if constexpr (Filter == IMB_FILTER_BILINEAR && std::is_same_v && NumChannels == 4) + { + bilinear_interpolation_color_char(source, r_sample, u, v); + } + else if constexpr (Filter == IMB_FILTER_BILINEAR && std::is_same_v) { + if constexpr (WrapUV) { + BLI_bilinear_interpolation_wrap_fl(source->float_buffer.data, + r_sample, + source->x, + source->y, + NumChannels, + u, + v, + true, + true); + } + else { + BLI_bilinear_interpolation_fl( + source->float_buffer.data, r_sample, source->x, source->y, NumChannels, u, v); + } + } + else if constexpr (Filter == IMB_FILTER_NEAREST && std::is_same_v) { + sample_nearest_float(source, u, v, r_sample); + } + else if constexpr (Filter == IMB_FILTER_BICUBIC && std::is_same_v) { + BLI_bicubic_interpolation_fl( + source->float_buffer.data, r_sample, source->x, source->y, NumChannels, u, v); + } + else if constexpr (Filter == IMB_FILTER_BICUBIC && std::is_same_v && NumChannels == 4) + { + BLI_bicubic_interpolation_char(source->byte_buffer.data, r_sample, source->x, source->y, u, v); + } + else { + /* Unsupported sampler. */ + BLI_assert_unreachable(); + } +} + +template static void store_sample(const T *sample, T *dst) +{ + if constexpr (std::is_same_v) { + BLI_STATIC_ASSERT(SrcChannels == 4, "Unsigned chars always have 4 channels."); + copy_v4_v4_uchar(dst, sample); + } + else if constexpr (std::is_same_v && SrcChannels == 4) { + copy_v4_v4(dst, sample); + } + else if constexpr (std::is_same_v && SrcChannels == 3) { + copy_v4_fl4(dst, sample[0], sample[1], sample[2], 1.0f); + } + else if constexpr (std::is_same_v && SrcChannels == 2) { + copy_v4_fl4(dst, sample[0], sample[1], 0.0f, 1.0f); + } + else if constexpr (std::is_same_v && SrcChannels == 1) { + /* Note: single channel sample is stored as grayscale. */ + copy_v4_fl4(dst, sample[0], sample[0], sample[0], 1.0f); + } + else { + BLI_assert_unreachable(); + } +} + +template +static void mix_and_store_sample(const T *sample, T *dst, const float mix_factor) +{ + if constexpr (std::is_same_v) { + BLI_STATIC_ASSERT(SrcChannels == 4, "Unsigned chars always have 4 channels."); + blend_color_interpolate_byte(dst, dst, sample, mix_factor); + } + else if constexpr (std::is_same_v && SrcChannels == 4) { + blend_color_interpolate_float(dst, dst, sample, mix_factor); + } + else { + BLI_assert_unreachable(); + } +} + +/* Process a block of destination image scanlines. */ template -ScanlineThreadFunc get_scanline_function(const eIMBTransformMode mode) - + typename T, + int SrcChannels, + bool CropSource, + bool WrapUV> +static void process_scanlines(const TransformContext &ctx, IndexRange y_range) { - switch (mode) { - case IMB_TRANSFORM_MODE_REGULAR: - return transform_scanline_function< - ScanlineProcessor, - PixelPointer>>; - case IMB_TRANSFORM_MODE_CROP_SRC: - return transform_scanline_function< - ScanlineProcessor, - PixelPointer>>; - case IMB_TRANSFORM_MODE_WRAP_REPEAT: - return transform_scanline_function< - ScanlineProcessor, - PixelPointer>>; - } + /* Note: sample at pixel center for proper filtering. */ + float2 uv_start = ctx.start_uv + ctx.add_x * 0.5f + ctx.add_y * 0.5f; - BLI_assert_unreachable(); - return nullptr; -} + if (ctx.subsampling_deltas.size() > 1) { + /* Multiple samples per pixel. */ + for (int yi : y_range) { + T *output = init_pixel_pointer(ctx.dst, ctx.dst_region_x_range.first(), yi); + float2 uv_row = uv_start + yi * ctx.add_y; + for (int xi : ctx.dst_region_x_range) { + float2 uv = uv_row + xi * ctx.add_x; + T sample[4] = {}; + int num_subsamples_added = 0; -template -ScanlineThreadFunc get_scanline_function(const TransformUserData *user_data, - const eIMBTransformMode mode) -{ - const ImBuf *src = user_data->src; - const ImBuf *dst = user_data->dst; + for (const float2 &delta_uv : ctx.subsampling_deltas) { + const float2 sub_uv = uv + delta_uv; + if (!CropSource || !should_discard(ctx, sub_uv)) { + T sub_sample[4]; + sample_image(ctx.src, sub_uv.x, sub_uv.y, sub_sample); + add_subsample(sub_sample, sample, num_subsamples_added); + num_subsamples_added += 1; + } + } - if (src->channels == 4 && dst->channels == 4) { - return get_scanline_function(mode); - } - if (src->channels == 3 && dst->channels == 4) { - return get_scanline_function(mode); - } - if (src->channels == 2 && dst->channels == 4) { - return get_scanline_function(mode); - } - if (src->channels == 1 && dst->channels == 4) { - return get_scanline_function(mode); - } - return nullptr; -} - -template -static void transform_threaded(TransformUserData *user_data, const eIMBTransformMode mode) -{ - ScanlineThreadFunc scanline_func = nullptr; - - if (user_data->dst->float_buffer.data && user_data->src->float_buffer.data) { - scanline_func = get_scanline_function(user_data, mode); - } - else if (user_data->dst->byte_buffer.data && user_data->src->byte_buffer.data) { - /* Number of channels is always 4 when using uchar buffers (sRGB + straight alpha). */ - scanline_func = get_scanline_function(mode); - } - - if (scanline_func != nullptr) { - threading::parallel_for(user_data->destination_region.y_range, 8, [&](IndexRange range) { - for (int scanline : range) { - scanline_func(user_data, scanline); + if (num_subsamples_added != 0) { + const float mix_weight = float(num_subsamples_added) / ctx.subsampling_deltas.size(); + mix_and_store_sample(sample, output, mix_weight); + } + output += 4; } - }); + } + } + else { + /* One sample per pixel. */ + for (int yi : y_range) { + T *output = init_pixel_pointer(ctx.dst, ctx.dst_region_x_range.first(), yi); + float2 uv_row = uv_start + yi * ctx.add_y; + for (int xi : ctx.dst_region_x_range) { + float2 uv = uv_row + xi * ctx.add_x; + if (!CropSource || !should_discard(ctx, uv)) { + T sample[4]; + sample_image(ctx.src, uv.x, uv.y, sample); + store_sample(sample, output); + } + output += 4; + } + } + } +} + +template +static void transform_scanlines(const TransformContext &ctx, IndexRange y_range) +{ + switch (ctx.mode) { + case IMB_TRANSFORM_MODE_REGULAR: + process_scanlines(ctx, y_range); + break; + case IMB_TRANSFORM_MODE_CROP_SRC: + process_scanlines(ctx, y_range); + break; + case IMB_TRANSFORM_MODE_WRAP_REPEAT: + process_scanlines(ctx, y_range); + break; + default: + BLI_assert_unreachable(); + break; + } +} + +template +static void transform_scanlines_filter(const TransformContext &ctx, IndexRange y_range) +{ + int channels = ctx.src->channels; + if (ctx.dst->float_buffer.data && ctx.src->float_buffer.data) { + /* Float images. */ + if (channels == 4) { + transform_scanlines(ctx, y_range); + } + else if (channels == 3) { + transform_scanlines(ctx, y_range); + } + else if (channels == 2) { + transform_scanlines(ctx, y_range); + } + else if (channels == 1) { + transform_scanlines(ctx, y_range); + } + } + else if (ctx.dst->byte_buffer.data && ctx.src->byte_buffer.data) { + /* Byte images. */ + if (channels == 4) { + transform_scanlines(ctx, y_range); + } } } @@ -659,6 +382,7 @@ static void transform_threaded(TransformUserData *user_data, const eIMBTransform extern "C" { using namespace blender::imbuf::transform; +using namespace blender; void IMB_transform(const ImBuf *src, ImBuf *dst, @@ -671,25 +395,28 @@ void IMB_transform(const ImBuf *src, BLI_assert_msg(mode != IMB_TRANSFORM_MODE_CROP_SRC || src_crop != nullptr, "No source crop rect given, but crop source is requested. Or source crop rect " "was given, but crop source was not requested."); + BLI_assert_msg(dst->channels == 4, "Destination image must have 4 channels."); - TransformUserData user_data; - user_data.src = src; - user_data.dst = dst; - if (mode == IMB_TRANSFORM_MODE_CROP_SRC) { - user_data.src_crop = *src_crop; + TransformContext ctx; + ctx.src = src; + ctx.dst = dst; + ctx.mode = mode; + bool crop = mode == IMB_TRANSFORM_MODE_CROP_SRC; + if (crop) { + ctx.src_crop = *src_crop; } - user_data.init(blender::float4x4(transform_matrix), - num_subsamples, - ELEM(mode, IMB_TRANSFORM_MODE_CROP_SRC)); + ctx.init(blender::float4x4(transform_matrix), num_subsamples, crop); - if (filter == IMB_FILTER_NEAREST) { - transform_threaded(&user_data, mode); - } - else if (filter == IMB_FILTER_BILINEAR) { - transform_threaded(&user_data, mode); - } - else if (filter == IMB_FILTER_BICUBIC) { - transform_threaded(&user_data, mode); - } + threading::parallel_for(ctx.dst_region_y_range, 8, [&](IndexRange y_range) { + if (filter == IMB_FILTER_NEAREST) { + transform_scanlines_filter(ctx, y_range); + } + else if (filter == IMB_FILTER_BILINEAR) { + transform_scanlines_filter(ctx, y_range); + } + else if (filter == IMB_FILTER_BICUBIC) { + transform_scanlines_filter(ctx, y_range); + } + }); } } -- 2.30.2 From 96041c9516abbbc5c0f922fa1b6fab8cf5a1001e Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 16 Jan 2024 21:04:17 +0100 Subject: [PATCH 030/244] Cleanup: Use std::string to store UI button string This simplifies memory management and button string manipulation in general. Just change one of the few strings stored in `uiBut` for now. Pull Request: https://projects.blender.org/blender/blender/pulls/117183 --- source/blender/editors/interface/interface.cc | 131 +++++------------- .../editors/interface/interface_intern.hh | 4 +- .../editors/interface/interface_layout.cc | 12 +- .../interface_region_color_picker.cc | 2 +- .../interface/interface_region_menu_pie.cc | 2 +- .../interface/interface_region_menu_popup.cc | 7 +- .../interface_template_search_menu.cc | 1 - .../editors/interface/interface_widgets.cc | 5 +- 8 files changed, 55 insertions(+), 109 deletions(-) diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc index b5e4af3d03c..b511980e3d9 100644 --- a/source/blender/editors/interface/interface.cc +++ b/source/blender/editors/interface/interface.cc @@ -13,6 +13,8 @@ #include /* `offsetof()` */ #include +#include + #include "MEM_guardedalloc.h" #include "DNA_object_types.h" @@ -466,7 +468,7 @@ void ui_block_bounds_calc(uiBlock *block) /* hardcoded exception... but that one is annoying with larger safety */ uiBut *bt = static_cast(block->buttons.first); - const int xof = ((bt && STRPREFIX(bt->str, "ERROR")) ? 10 : 40) * UI_SCALE_FAC; + const int xof = ((bt && STRPREFIX(bt->str.c_str(), "ERROR")) ? 10 : 40) * UI_SCALE_FAC; block->safety.xmin = block->rect.xmin - xof; block->safety.ymin = block->rect.ymin - xof; @@ -918,22 +920,7 @@ static void ui_but_update_old_active_from_new(uiBut *oldbut, uiBut *but) /* move/copy string from the new button to the old */ /* needed for alt+mouse wheel over enums */ - if (but->str != but->strdata) { - if (oldbut->str != oldbut->strdata) { - std::swap(but->str, oldbut->str); - } - else { - oldbut->str = but->str; - but->str = but->strdata; - } - } - else { - if (oldbut->str != oldbut->strdata) { - MEM_freeN(oldbut->str); - oldbut->str = oldbut->strdata; - } - STRNCPY(oldbut->strdata, but->strdata); - } + std::swap(but->str, oldbut->str); if (but->dragpoin) { std::swap(but->dragpoin, oldbut->dragpoin); @@ -1161,11 +1148,11 @@ static void ui_menu_block_set_keyaccels(uiBlock *block) continue; } - if (but->str == nullptr || but->str[0] == '\0') { + if (but->str.empty()) { continue; } - const char *str_pt = but->str; + const char *str_pt = but->str.c_str(); uchar menu_key; do { menu_key = tolower(*str_pt); @@ -1214,9 +1201,9 @@ static void ui_menu_block_set_keyaccels(uiBlock *block) void ui_but_add_shortcut(uiBut *but, const char *shortcut_str, const bool do_strip) { if (do_strip && (but->flag & UI_BUT_HAS_SEP_CHAR)) { - char *cpoin = strrchr(but->str, UI_SEP_CHAR); - if (cpoin) { - *cpoin = '\0'; + const size_t sep_index = but->str.find_first_of(UI_SEP_CHAR); + if (sep_index != std::string::npos) { + but->str = but->str.substr(0, sep_index); } but->flag &= ~UI_BUT_HAS_SEP_CHAR; } @@ -1226,16 +1213,7 @@ void ui_but_add_shortcut(uiBut *but, const char *shortcut_str, const bool do_str return; } - char *butstr_orig; - if (but->str != but->strdata) { - butstr_orig = but->str; /* free after using as source buffer */ - } - else { - butstr_orig = BLI_strdup(but->str); - } - SNPRINTF(but->strdata, "%s" UI_SEP_CHAR_S "%s", butstr_orig, shortcut_str); - MEM_freeN(butstr_orig); - but->str = but->strdata; + but->str = fmt::format("{}" UI_SEP_CHAR_S "{}", but->str, shortcut_str); but->flag |= UI_BUT_HAS_SEP_CHAR; ui_but_update(but); } @@ -3138,27 +3116,7 @@ bool ui_but_string_eval_number(bContext *C, const uiBut *but, const char *str, d static void ui_but_string_set_internal(uiBut *but, const char *str, size_t str_len) { BLI_assert(str_len == strlen(str)); - BLI_assert(but->str == nullptr); - str_len += 1; - - if (str_len > UI_MAX_NAME_STR) { - but->str = static_cast(MEM_mallocN(str_len, "ui_def_but str")); - } - else { - but->str = but->strdata; - } - memcpy(but->str, str, str_len); -} - -static void ui_but_string_free_internal(uiBut *but) -{ - if (but->str) { - if (but->str != but->strdata) { - MEM_freeN(but->str); - } - /* must call 'ui_but_string_set_internal' after */ - but->str = nullptr; - } + but->str = std::string(str, str_len); } bool ui_but_string_set(bContext *C, uiBut *but, const char *str) @@ -3509,9 +3467,6 @@ static void ui_but_free(const bContext *C, uiBut *but) } } } - if (but->str && but->str != but->strdata) { - MEM_freeN(but->str); - } if ((but->type == UI_BTYPE_IMAGE) && but->poin) { IMB_freeImBuf((ImBuf *)but->poin); @@ -3736,7 +3691,7 @@ void UI_block_set_search_only(uiBlock *block, bool search_only) static void ui_but_build_drawstr_float(uiBut *but, double value) { size_t slen = 0; - STR_CONCAT(but->drawstr, slen, but->str); + STR_CONCAT(but->drawstr, slen, but->str.c_str()); PropertySubType subtype = PROP_NONE; if (but->rnaprop) { @@ -3784,7 +3739,7 @@ static void ui_but_build_drawstr_float(uiBut *but, double value) static void ui_but_build_drawstr_int(uiBut *but, int value) { size_t slen = 0; - STR_CONCAT(but->drawstr, slen, but->str); + STR_CONCAT(but->drawstr, slen, but->str.c_str()); PropertySubType subtype = PROP_NONE; if (but->rnaprop) { @@ -3878,13 +3833,13 @@ static void ui_but_update_ex(uiBut *but, const bool validate) &item)) { const size_t slen = strlen(item.name); - ui_but_string_free_internal(but); + but->str.clear(); ui_but_string_set_internal(but, item.name, slen); but->icon = item.icon; } } } - STRNCPY(but->drawstr, but->str); + STRNCPY(but->drawstr, but->str.c_str()); } break; @@ -3906,10 +3861,10 @@ static void ui_but_update_ex(uiBut *but, const bool validate) if (ui_but_is_float(but)) { UI_GET_BUT_VALUE_INIT(but, value); const int prec = ui_but_calc_float_precision(but, value); - SNPRINTF(but->drawstr, "%s%.*f", but->str, prec, value); + SNPRINTF(but->drawstr, "%s%.*f", but->str.c_str(), prec, value); } else { - STRNCPY(but->drawstr, but->str); + STRNCPY(but->drawstr, but->str.c_str()); } break; @@ -3920,7 +3875,7 @@ static void ui_but_update_ex(uiBut *but, const bool validate) char str[UI_MAX_DRAW_STR]; ui_but_string_get(but, str, UI_MAX_DRAW_STR); - SNPRINTF(but->drawstr, "%s%s", but->str, str); + SNPRINTF(but->drawstr, "%s%s", but->str.c_str(), str); } break; @@ -3933,7 +3888,7 @@ static void ui_but_update_ex(uiBut *but, const bool validate) UI_GET_BUT_VALUE_INIT(but, value); str = WM_key_event_string(short(value), false); } - SNPRINTF(but->drawstr, "%s%s", but->str, str); + SNPRINTF(but->drawstr, "%s%s", but->str.c_str(), str); break; } case UI_BTYPE_HOTKEY_EVENT: @@ -3954,7 +3909,7 @@ static void ui_but_update_ex(uiBut *but, const bool validate) } } else { - STRNCPY_UTF8(but->drawstr, but->str); + STRNCPY_UTF8(but->drawstr, but->str.c_str()); } break; @@ -3963,7 +3918,7 @@ static void ui_but_update_ex(uiBut *but, const bool validate) case UI_BTYPE_HSVCIRCLE: break; default: - STRNCPY(but->drawstr, but->str); + STRNCPY(but->drawstr, but->str.c_str()); break; } @@ -4082,7 +4037,6 @@ uiBut *ui_but_change_type(uiBut *but, eButType new_type) const uiBut *old_but_ptr = but; /* Button may have pointer to a member within itself, this will have to be updated. */ - const bool has_str_ptr_to_self = but->str == but->strdata; const bool has_poin_ptr_to_self = but->poin == (char *)but; /* Copy construct button with the new type. */ @@ -4090,9 +4044,6 @@ uiBut *ui_but_change_type(uiBut *but, eButType new_type) *but = *old_but_ptr; /* We didn't mean to override this :) */ but->type = new_type; - if (has_str_ptr_to_self) { - but->str = but->strdata; - } if (has_poin_ptr_to_self) { but->poin = (char *)but; } @@ -4201,18 +4152,16 @@ static uiBut *ui_def_but(uiBlock *block, but->pos = -1; /* cursor invisible */ if (ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER)) { /* add a space to name */ - /* slen remains unchanged from previous assignment, ensure this stays true */ if (slen > 0 && slen < UI_MAX_NAME_STR - 2) { - if (but->str[slen - 1] != ' ') { - but->str[slen] = ' '; - but->str[slen + 1] = 0; + if (but->str[but->str.size() - 1] != ' ') { + but->str += ' '; } } } if (block->flag & UI_BLOCK_RADIAL) { but->drawflag |= UI_BUT_TEXT_LEFT; - if (but->str && but->str[0]) { + if (!but->str.empty()) { but->drawflag |= UI_BUT_ICON_LEFT; } } @@ -4296,7 +4245,7 @@ void ui_def_but_icon(uiBut *but, const int icon, const int flag) but->icon = icon; but->flag |= flag; - if (but->str && but->str[0]) { + if (!but->str.empty()) { but->drawflag |= UI_BUT_ICON_LEFT; } } @@ -6628,28 +6577,24 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...) va_start(args, but); while ((si = (uiStringInfo *)va_arg(args, void *))) { uiStringInfoType type = si->type; - char *tmp = nullptr; + std::string tmp; if (type == BUT_GET_TIP_LABEL) { if (but->tip_label_func) { - const std::string tooltip_label = but->tip_label_func(but); - tmp = BLI_strdupn(tooltip_label.c_str(), tooltip_label.size()); + tmp = but->tip_label_func(but); } } if (type == BUT_GET_LABEL) { - if (but->str && but->str[0]) { - const char *str_sep; - size_t str_len; - - if ((but->flag & UI_BUT_HAS_SEP_CHAR) && (str_sep = strrchr(but->str, UI_SEP_CHAR))) { - str_len = (str_sep - but->str); + if (!but->str.empty()) { + size_t str_len = but->str.size(); + if (but->flag & UI_BUT_HAS_SEP_CHAR) { + const size_t sep_index = but->str.find_first_of(UI_SEP_CHAR); + if (sep_index != std::string::npos) { + str_len = sep_index; + } } - else { - str_len = strlen(but->str); - } - - tmp = BLI_strdupn(but->str, str_len); + tmp = but->str.substr(0, str_len); } else { type = BUT_GET_RNA_LABEL; /* Fail-safe solution... */ @@ -6734,7 +6679,7 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...) } } - if (tmp == nullptr) { + if (tmp.empty()) { wmOperatorType *ot = UI_but_operatortype_get_from_enum_menu(but, nullptr); if (ot) { if (type == BUT_GET_RNA_LABEL) { @@ -6746,7 +6691,7 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...) } } - if (tmp == nullptr) { + if (tmp.empty()) { PanelType *pt = UI_but_paneltype_get(but); if (pt) { if (type == BUT_GET_RNA_LABEL) { @@ -6859,7 +6804,7 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...) } } - si->strinfo = tmp; + si->strinfo = BLI_strdupn(tmp.c_str(), tmp.size()); } va_end(args); diff --git a/source/blender/editors/interface/interface_intern.hh b/source/blender/editors/interface/interface_intern.hh index 37cd867baff..576c2c51ce1 100644 --- a/source/blender/editors/interface/interface_intern.hh +++ b/source/blender/editors/interface/interface_intern.hh @@ -178,8 +178,8 @@ struct uiBut { short bit = 0, bitnr = 0, retval = 0, strwidth = 0, alignnr = 0; short ofs = 0, pos = 0, selsta = 0, selend = 0; - char *str = nullptr; - char strdata[UI_MAX_NAME_STR] = ""; + std::string str; + char drawstr[UI_MAX_DRAW_STR] = ""; char *placeholder = nullptr; diff --git a/source/blender/editors/interface/interface_layout.cc b/source/blender/editors/interface/interface_layout.cc index 7f8b72e77a9..a1b8fb8f01e 100644 --- a/source/blender/editors/interface/interface_layout.cc +++ b/source/blender/editors/interface/interface_layout.cc @@ -2536,7 +2536,7 @@ void uiItemFullR(uiLayout *layout, /* ensure text isn't added to icon_only buttons */ if (but && icon_only) { - BLI_assert(but->str[0] == '\0'); + BLI_assert(but->str.empty()); } } @@ -2863,7 +2863,7 @@ uiBut *ui_but_add_search(uiBut *but, if (RNA_property_type(prop) == PROP_ENUM) { /* XXX, this will have a menu string, * but in this case we just want the text */ - but->str[0] = 0; + but->str.clear(); } UI_but_func_search_set_results_are_suggestions(but, results_are_suggestions); @@ -3602,7 +3602,7 @@ static int menu_item_enum_opname_menu_active(bContext *C, uiBut *but, MenuItemLe WM_operator_properties_sanitize(&ptr, false); PropertyRNA *prop = RNA_struct_find_property(&ptr, lvl->propname); RNA_property_enum_items_gettexted(C, &ptr, prop, &item_array, &totitem, &free); - int active = RNA_enum_from_name(item_array, but->str); + int active = RNA_enum_from_name(item_array, but->str.c_str()); if (free) { MEM_freeN((void *)item_array); } @@ -5405,7 +5405,7 @@ static bool block_search_panel_label_matches(const uiBlock *block, const char *s static bool button_matches_search_filter(uiBut *but, const char *search_filter) { /* Do the shorter checks first for better performance in case there is a match. */ - if (BLI_strcasestr(but->str, search_filter)) { + if (BLI_strcasestr(but->str.c_str(), search_filter)) { return true; } @@ -5891,7 +5891,7 @@ void ui_layout_add_but(uiLayout *layout, uiBut *but) ui_item_size((uiItem *)bitem, &w, &h); /* XXX uiBut hasn't scaled yet * we can flag the button as not expandable, depending on its size */ - if (w <= 2 * UI_UNIT_X && (!but->str || but->str[0] == '\0')) { + if (w <= 2 * UI_UNIT_X && but->str.empty()) { bitem->item.flag |= UI_ITEM_FIXED_SIZE; } @@ -6166,7 +6166,7 @@ static bool ui_layout_has_panel_label(const uiLayout *layout, const PanelType *p if (subitem->type == ITEM_BUTTON) { uiButtonItem *bitem = (uiButtonItem *)subitem; if (!(bitem->but->flag & UI_HIDDEN) && - STREQ(bitem->but->str, CTX_IFACE_(pt->translation_context, pt->label))) + STREQ(bitem->but->str.c_str(), CTX_IFACE_(pt->translation_context, pt->label))) { return true; } diff --git a/source/blender/editors/interface/interface_region_color_picker.cc b/source/blender/editors/interface/interface_region_color_picker.cc index 3075f05154f..670651adc5a 100644 --- a/source/blender/editors/interface/interface_region_color_picker.cc +++ b/source/blender/editors/interface/interface_region_color_picker.cc @@ -199,7 +199,7 @@ static void ui_update_color_picker_buts_rgb(uiBut *from_but, * push, so disable it on RNA buttons in the color picker block */ UI_but_flag_disable(bt, UI_BUT_UNDO); } - else if (STREQ(bt->str, "Hex:")) { + else if (bt->str == "Hex:") { float rgb_hex[3]; uchar rgb_hex_uchar[3]; char col[16]; diff --git a/source/blender/editors/interface/interface_region_menu_pie.cc b/source/blender/editors/interface/interface_region_menu_pie.cc index 0866cf8f62a..88a59114bc5 100644 --- a/source/blender/editors/interface/interface_region_menu_pie.cc +++ b/source/blender/editors/interface/interface_region_menu_pie.cc @@ -180,7 +180,7 @@ uiPieMenu *UI_pie_menu_begin(bContext *C, const char *title, int icon, const wmE } /* do not align left */ but->drawflag &= ~UI_BUT_TEXT_LEFT; - pie->block_radial->pie_data.title = but->str; + pie->block_radial->pie_data.title = but->str.c_str(); pie->block_radial->pie_data.icon = icon; } diff --git a/source/blender/editors/interface/interface_region_menu_popup.cc b/source/blender/editors/interface/interface_region_menu_popup.cc index 01be642743a..b13cc9ab88a 100644 --- a/source/blender/editors/interface/interface_region_menu_popup.cc +++ b/source/blender/editors/interface/interface_region_menu_popup.cc @@ -71,7 +71,7 @@ int ui_but_menu_step(uiBut *but, int direction) direction); } - printf("%s: cannot cycle button '%s'\n", __func__, but->str); + printf("%s: cannot cycle button '%s'\n", __func__, but->str.c_str()); return 0; } @@ -124,7 +124,7 @@ static uiBut *ui_popup_menu_memory__internal(uiBlock *block, uiBut *but) if (but) { /* set */ - mem[hash_mod] = ui_popup_string_hash(but->str, but->flag & UI_BUT_HAS_SEP_CHAR); + mem[hash_mod] = ui_popup_string_hash(but->str.c_str(), but->flag & UI_BUT_HAS_SEP_CHAR); return nullptr; } @@ -136,7 +136,8 @@ static uiBut *ui_popup_menu_memory__internal(uiBlock *block, uiBut *but) if (ELEM(but_iter->type, UI_BTYPE_LABEL, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE)) { continue; } - if (mem[hash_mod] == ui_popup_string_hash(but_iter->str, but_iter->flag & UI_BUT_HAS_SEP_CHAR)) + if (mem[hash_mod] == + ui_popup_string_hash(but_iter->str.c_str(), but_iter->flag & UI_BUT_HAS_SEP_CHAR)) { return but_iter; } diff --git a/source/blender/editors/interface/interface_template_search_menu.cc b/source/blender/editors/interface/interface_template_search_menu.cc index c86af15437a..6569593871e 100644 --- a/source/blender/editors/interface/interface_template_search_menu.cc +++ b/source/blender/editors/interface/interface_template_search_menu.cc @@ -298,7 +298,6 @@ static bool menu_items_to_ui_button(MenuSearch_Item *item, uiBut *but) } but->icon = item->icon; - but->str = but->strdata; } return changed; diff --git a/source/blender/editors/interface/interface_widgets.cc b/source/blender/editors/interface/interface_widgets.cc index 67b09ea2b2c..4b0d22e5c05 100644 --- a/source/blender/editors/interface/interface_widgets.cc +++ b/source/blender/editors/interface/interface_widgets.cc @@ -1342,7 +1342,7 @@ static void widget_draw_icon( if (but->drawflag & UI_BUT_ICON_LEFT) { /* special case - icon_only pie buttons */ if (ui_block_is_pie_menu(but->block) && !ELEM(but->type, UI_BTYPE_MENU, UI_BTYPE_POPOVER) && - but->str && but->str[0] == '\0') + but->str.empty()) { xs = rect->xmin + 2.0f * ofs; } @@ -4960,7 +4960,8 @@ void ui_draw_but(const bContext *C, ARegion *region, uiStyle *style, uiBut *but, /* We could use a flag for this, but for now just check size, * add up/down arrows if there is room. */ - if ((!but->str[0] && but->icon && (BLI_rcti_size_x(rect) < BLI_rcti_size_y(rect) + 2)) || + if ((but->str.empty() && but->icon && + (BLI_rcti_size_x(rect) < BLI_rcti_size_y(rect) + 2)) || /* disable for brushes also */ (but->flag & UI_BUT_ICON_PREVIEW)) { -- 2.30.2 From 59f3f88e462918a4ec18f8f8dd4c21afb7815342 Mon Sep 17 00:00:00 2001 From: laurynas Date: Tue, 16 Jan 2024 21:59:15 +0100 Subject: [PATCH 031/244] Fix: Curves extrude with all points selected CurvesGeometry has no ".selection" attribute when all control points are selected. The earlier code assumed that the attribute always exists. Also Python tests are added for the "extrude" operator. Pull Request: https://projects.blender.org/blender/blender/pulls/117095 --- .../editors/curves/intern/curves_extrude.cc | 11 +- tests/python/CMakeLists.txt | 8 + tests/python/curves_extrude.py | 192 ++++++++++++++++++ 3 files changed, 208 insertions(+), 3 deletions(-) create mode 100644 tests/python/curves_extrude.py diff --git a/source/blender/editors/curves/intern/curves_extrude.cc b/source/blender/editors/curves/intern/curves_extrude.cc index a832ed2af1f..a37fe657b3f 100644 --- a/source/blender/editors/curves/intern/curves_extrude.cc +++ b/source/blender/editors/curves/intern/curves_extrude.cc @@ -17,7 +17,7 @@ namespace blender::ed::curves { /** * Merges copy intervals at curve endings to minimize number of copy operations. - * For example above intervals [0, 3, 4, 4, 4] became [0, 4, 4]. + * For example given in function 'extrude_curves' intervals [0, 3, 4, 4, 4] became [0, 4, 4]. * Leading to only two copy operations. */ static Span compress_intervals(const Span curve_interval_ranges, @@ -263,7 +263,8 @@ static void extrude_curves(Curves &curves_id) Array curve_interval_ranges(curves_num); /* Per curve boolean indicating if first interval in a curve is selected. - * Other can be calculated as in a curve two adjacent intervals can have same selection state. */ + * Other can be calculated as in a curve two adjacent intervals can not have same selection + * state. */ Array is_first_selected(curves_num); calc_curves_extrusion(extruded_points, @@ -276,7 +277,11 @@ static void extrude_curves(Curves &curves_id) new_curves.resize(new_offsets.last(), new_curves.curves_num()); const bke::AttributeAccessor src_attributes = curves.attributes(); - const GVArraySpan src_selection = *src_attributes.lookup(".selection", bke::AttrDomain::Point); + GVArray src_selection_array = *src_attributes.lookup(".selection", bke::AttrDomain::Point); + if (!src_selection_array) { + src_selection_array = VArray::ForSingle(true, curves.points_num()); + } + const GVArraySpan src_selection = src_selection_array; const CPPType &src_selection_type = src_selection.type(); bke::GSpanAttributeWriter dst_selection = ensure_selection_attribute( new_curves, diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index 6df6b903a0c..52b104f9382 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -265,6 +265,14 @@ add_blender_test( --run-all-tests ) +add_blender_test( + curves_extrude + ${TEST_SRC_DIR}/modeling/curves_extrude.blend + --python ${TEST_PYTHON_DIR}/curves_extrude.py + -- + --run-all-tests +) + # ------------------------------------------------------------------------------ # MODIFIERS TESTS add_blender_test( diff --git a/tests/python/curves_extrude.py b/tests/python/curves_extrude.py new file mode 100644 index 00000000000..34fcb740a56 --- /dev/null +++ b/tests/python/curves_extrude.py @@ -0,0 +1,192 @@ +# SPDX-FileCopyrightText: 2020-2023 Blender Authors +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from abc import ABC, abstractmethod +import bpy +import os +import sys + +sys.path.append(os.path.dirname(os.path.realpath(__file__))) +from modules.mesh_test import RunTest + + +class CurvesTest(ABC): + + def __init__(self, test_object_name, exp_object_name, test_name=None): + self.test_object_name = test_object_name + self.exp_object_name = exp_object_name + self.test_object = bpy.data.objects[self.test_object_name] + self.expected_object = bpy.data.objects[self.exp_object_name] + self.verbose = os.getenv("BLENDER_VERBOSE") is not None + + if test_name: + self.test_name = test_name + else: + filepath = bpy.data.filepath + self.test_name = bpy.path.display_name_from_filepath(filepath) + self._failed_tests_list = [] + + def create_evaluated_object(self): + """ + Creates an evaluated object. + """ + bpy.context.view_layer.objects.active = self.test_object + + # Duplicate test object. + bpy.ops.object.mode_set(mode="OBJECT") + bpy.ops.object.select_all(action="DESELECT") + bpy.context.view_layer.objects.active = self.test_object + + self.test_object.select_set(True) + bpy.ops.object.duplicate() + self.evaluated_object = bpy.context.active_object + self.evaluated_object.name = "evaluated_object" + + @staticmethod + def _print_result(result): + """ + Prints the comparison, selection and validation result. + """ + print("Results:") + for key in result: + print("{} : {}".format(key, result[key][1])) + print() + + def run_test(self): + """ + Runs a single test, runs it again if test file is updated. + """ + print("\nSTART {} test.".format(self.test_name)) + + self.create_evaluated_object() + self.apply_operations() + + result = self.compare_objects(self.evaluated_object, self.expected_object) + + # Initializing with True to get correct resultant of result_code booleans. + success = True + inside_loop_flag = False + for key in result: + inside_loop_flag = True + success = success and result[key][0] + + # Check "success" is actually evaluated and is not the default True value. + if not inside_loop_flag: + success = False + + if success: + self.print_passed_test_result(result) + # Clean up. + if self.verbose: + print("Cleaning up...") + # Delete evaluated_test_object. + bpy.ops.object.delete() + return True + + else: + self.print_failed_test_result(result) + return False + + @abstractmethod + def apply_operations(self, evaluated_test_object_name): + pass + + @staticmethod + def compare_curves(evaluated_curves, expected_curves): + if len(evaluated_curves.attributes.items()) != len(expected_curves.attributes.items()): + print("Attribute count doesn't match") + + for a_idx, attribute in evaluated_curves.attributes.items(): + expected_attribute = expected_curves.attributes[a_idx] + + if len(attribute.data.items()) != len(expected_attribute.data.items()): + print("Attribute data length doesn't match") + + value_attr_name = ('vector' if attribute.data_type == 'FLOAT_VECTOR' + or attribute.data_type == 'FLOAT2' else + 'color' if attribute.data_type == 'FLOAT_COLOR' else 'value') + + for v_idx, attribute_value in attribute.data.items(): + if getattr( + attribute_value, + value_attr_name) != getattr( + expected_attribute.data[v_idx], + value_attr_name): + print("Attribute '{}' values do not match".format(attribute.name)) + return False + + return True + + def compare_objects(self, evaluated_object, expected_object): + result_codes = {} + + equal = self.compare_curves(evaluated_object.data, expected_object.data) + + result_codes['Curves Comparison'] = (equal, evaluated_object.data) + return result_codes + + def print_failed_test_result(self, result): + """ + Print results for failed test. + """ + print("FAILED {} test with the following: ".format(self.test_name)) + + def print_passed_test_result(self, result): + """ + Print results for passing test. + """ + print("PASSED {} test successfully.".format(self.test_name)) + + +class CurvesOpTest(CurvesTest): + + def __init__(self, test_name, test_object_name, exp_object_name, operators_stack): + super().__init__(test_object_name, exp_object_name, test_name) + self.operators_stack = operators_stack + + def apply_operations(self): + for operator_name in self.operators_stack: + bpy.ops.object.mode_set(mode='EDIT') + curves_operator = getattr(bpy.ops.curves, operator_name) + + try: + retval = curves_operator() + except AttributeError: + raise AttributeError("bpy.ops.curves has no attribute {}".format(operator_name)) + except TypeError as ex: + raise TypeError("Incorrect operator parameters {!r} raised {!r}".format([], ex)) + + if retval != {'FINISHED'}: + raise RuntimeError("Unexpected operator return value: {}".format(operator_name)) + bpy.ops.object.mode_set(mode='OBJECT') + + +def main(): + tests = [ + CurvesOpTest("Extrude 1 Point Curve", "a_test1PointCurve", "a_test1PointCurveExpected", ['extrude']), + CurvesOpTest("Extrude Middle Points", "b_testMiddlePoints", "b_testMiddlePointsExpected", ['extrude']), + CurvesOpTest("Extrude End Points", "c_testEndPoints", "c_testEndPointsExpected", ['extrude']), + CurvesOpTest("Extrude Neighbors In Separate Curves", "d_testNeighborsInCurves", "d_testNeighborsInCurvesExpected", ['extrude']), + CurvesOpTest("Extrude Edge Curves", "e_testEdgeCurves", "e_testEdgeCurvesExpected", ['extrude']), + CurvesOpTest("Extrude Middle Curve", "f_testMiddleCurve", "f_testMiddleCurveExpected", ['extrude']), + CurvesOpTest("Extrude All Points", "g_testAllPoints", "g_testAllPointsExpected", ['extrude']) + ] + + curves_extrude_test = RunTest(tests) + + command = list(sys.argv) + for i, cmd in enumerate(command): + if cmd == "--run-all-tests": + curves_extrude_test.do_compare = True + curves_extrude_test.run_all_tests() + break + elif cmd == "--run-test": + curves_extrude_test.do_compare = False + name = command[i + 1] + curves_extrude_test.run_test(name) + break + + +if __name__ == "__main__": + main() -- 2.30.2 From 7c88cce9a230924eb54e369eb63e12b1d8334baf Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 16 Jan 2024 15:52:02 -0500 Subject: [PATCH 032/244] Tests: Enable new triangulate geometry node test files --- tests/python/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index 52b104f9382..9f14a057f61 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -824,6 +824,7 @@ set(geo_node_tests mesh mesh/extrude mesh/split_edges + mesh/triangulate points texture utilities -- 2.30.2 From 9ff2dec171934f476375f0f22e752c3e41feaff4 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 17 Jan 2024 10:30:04 +1100 Subject: [PATCH 033/244] Cleanup: remove outdated comment String suggestion now allow the operator list to be searched. --- scripts/modules/rna_keymap_ui.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/modules/rna_keymap_ui.py b/scripts/modules/rna_keymap_ui.py index a5b923bb769..46a7f01e23c 100644 --- a/scripts/modules/rna_keymap_ui.py +++ b/scripts/modules/rna_keymap_ui.py @@ -164,8 +164,6 @@ def draw_kmi(display_keymaps, kc, km, kmi, layout, level): if km.is_modal: sub.prop(kmi, "propvalue", text="") else: - # One day... - # sub.prop_search(kmi, "idname", bpy.context.window_manager, "operators_all", text="") sub.prop(kmi, "idname", text="") if map_type not in {'TEXTINPUT', 'TIMER'}: -- 2.30.2 From 03155f1316b973f24a2dbd2751e3d6a0c3c359e5 Mon Sep 17 00:00:00 2001 From: grady Date: Wed, 17 Jan 2024 00:45:43 +0100 Subject: [PATCH 034/244] Outliner: Double-click on item icon to select contents/hierarchy Allow double-clicking on Outliner items to select all child items. Pull Request: https://projects.blender.org/blender/blender/pulls/110151 --- .../keyconfig/keymap_data/blender_default.py | 8 + .../keymap_data/industry_compatible_data.py | 8 + .../editors/space_outliner/outliner_edit.cc | 2 +- .../editors/space_outliner/outliner_select.cc | 149 ++++++++++++++++-- 4 files changed, 149 insertions(+), 18 deletions(-) diff --git a/scripts/presets/keyconfig/keymap_data/blender_default.py b/scripts/presets/keyconfig/keymap_data/blender_default.py index 72eec0904fa..12d6c0fa662 100644 --- a/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -1246,6 +1246,14 @@ def km_outliner(params): {"properties": [("extend_range", True), ("deselect_all", not params.legacy)]}), ("outliner.item_activate", {"type": 'LEFTMOUSE', "value": 'CLICK', "ctrl": True, "shift": True}, {"properties": [("extend", True), ("extend_range", True), ("deselect_all", not params.legacy)]}), + ("outliner.item_activate", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, + {"properties": [("recurse", True), ("deselect_all", True)]}), + ("outliner.item_activate", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK', "ctrl": True}, + {"properties": [("recurse", True), ("extend", True), ("deselect_all", True)]}), + ("outliner.item_activate", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK', "shift": True}, + {"properties": [("recurse", True), ("extend_range", True), ("deselect_all", True)]}), + ("outliner.item_activate", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK', "ctrl": True, "shift": True}, + {"properties": [("recurse", True), ("extend", True), ("extend_range", True), ("deselect_all", True)]}), ("outliner.select_box", {"type": 'B', "value": 'PRESS'}, None), ("outliner.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, {"properties": [("tweak", True)]}), ("outliner.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "shift": True}, diff --git a/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py index bd6a971b934..9ccfd81d2fb 100644 --- a/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -486,6 +486,14 @@ def km_outliner(params): {"properties": [("extend", False), ("extend_range", True), ("deselect_all", True)]}), ("outliner.item_activate", {"type": 'LEFTMOUSE', "value": 'CLICK', "ctrl": True, "shift": True}, {"properties": [("extend", True), ("extend_range", True), ("deselect_all", True)]}), + ("outliner.item_activate", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, + {"properties": [("recurse", True), ("deselect_all", True)]}), + ("outliner.item_activate", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK', "ctrl": True}, + {"properties": [("recurse", True), ("extend", True), ("deselect_all", True)]}), + ("outliner.item_activate", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK', "shift": True}, + {"properties": [("recurse", True), ("extend_range", True), ("deselect_all", True)]}), + ("outliner.item_activate", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK', "ctrl": True, "shift": True}, + {"properties": [("recurse", True), ("extend", True), ("extend_range", True), ("deselect_all", True)]}), ("outliner.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, {"properties": [("tweak", True)]}), ("outliner.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "shift": True}, {"properties": [("tweak", True), ("mode", 'ADD')]}), diff --git a/source/blender/editors/space_outliner/outliner_edit.cc b/source/blender/editors/space_outliner/outliner_edit.cc index aa4088347f0..7734984970a 100644 --- a/source/blender/editors/space_outliner/outliner_edit.cc +++ b/source/blender/editors/space_outliner/outliner_edit.cc @@ -413,7 +413,7 @@ static int outliner_item_rename_invoke(bContext *C, wmOperator *op, const wmEven TreeElement *te = use_active ? outliner_item_rename_find_active(space_outliner, op->reports) : outliner_item_rename_find_hovered(space_outliner, region, event); if (!te) { - return OPERATOR_CANCELLED; + return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; } /* Force element into view. */ diff --git a/source/blender/editors/space_outliner/outliner_select.cc b/source/blender/editors/space_outliner/outliner_select.cc index 1e6ec29b4ab..6768d766210 100644 --- a/source/blender/editors/space_outliner/outliner_select.cc +++ b/source/blender/editors/space_outliner/outliner_select.cc @@ -1569,30 +1569,84 @@ void outliner_item_select(bContext *C, } } +static bool can_select_recursive(TreeElement *te, Collection *in_collection) +{ + if (te->store_elem->type == TSE_LAYER_COLLECTION) { + return true; + } + + if (te->store_elem->type == TSE_SOME_ID && te->idcode == ID_OB) { + /* Only actually select the object if + * 1. We are not restricted to any collection, or + * 2. The object is in fact in the given collection. */ + if (!in_collection || BKE_collection_has_object_recursive( + in_collection, reinterpret_cast(te->store_elem->id))) + { + return true; + } + } + + return false; +} + +static void do_outliner_select_recursive(ListBase *lb, bool selecting, Collection *in_collection) +{ + LISTBASE_FOREACH (TreeElement *, te, lb) { + TreeStoreElem *tselem = TREESTORE(te); + /* Recursive selection only on collections or objects. */ + if (can_select_recursive(te, in_collection)) { + tselem->flag = selecting ? (tselem->flag | TSE_SELECTED) : (tselem->flag & ~TSE_SELECTED); + if (tselem->type == TSE_LAYER_COLLECTION) { + /* Restrict sub-tree selections to this collection. This prevents undesirable behavior in + * the edge-case where there is an object which is part of this collection, but which has + * children that are part of another collection. */ + do_outliner_select_recursive( + &te->subtree, selecting, static_cast(te->directdata)->collection); + } + else { + do_outliner_select_recursive(&te->subtree, selecting, in_collection); + } + } + else { + tselem->flag &= ~TSE_SELECTED; + } + } +} + static bool do_outliner_range_select_recursive(ListBase *lb, TreeElement *active, TreeElement *cursor, - bool selecting) + bool selecting, + const bool recurse, + Collection *in_collection) { LISTBASE_FOREACH (TreeElement *, te, lb) { TreeStoreElem *tselem = TREESTORE(te); - if (selecting) { - tselem->flag |= TSE_SELECTED; - } + bool can_select = !recurse || can_select_recursive(te, in_collection); + + /* Remember if we are selecting before we potentially change the selecting state. */ + bool selecting_before = selecting; /* Set state for selection */ if (ELEM(te, active, cursor)) { selecting = !selecting; } - if (selecting) { + if (can_select && (selecting_before || selecting)) { tselem->flag |= TSE_SELECTED; } - /* Don't look inside closed elements */ - if (!(tselem->flag & TSE_CLOSED)) { - selecting = do_outliner_range_select_recursive(&te->subtree, active, cursor, selecting); + /* Don't look inside closed elements, unless we're forcing the recursion all the way down. */ + if (!(tselem->flag & TSE_CLOSED) || recurse) { + /* If this tree element is a collection, then it sets + * the precedent for inclusion of its subobjects. */ + Collection *child_collection = in_collection; + if (tselem->type == TSE_LAYER_COLLECTION) { + child_collection = static_cast(te->directdata)->collection; + } + selecting = do_outliner_range_select_recursive( + &te->subtree, active, cursor, selecting, recurse, child_collection); } } @@ -1603,7 +1657,9 @@ static bool do_outliner_range_select_recursive(ListBase *lb, static void do_outliner_range_select(bContext *C, SpaceOutliner *space_outliner, TreeElement *cursor, - const bool extend) + const bool extend, + const bool recurse, + Collection *in_collection) { TreeElement *active = outliner_find_element_with_flag(&space_outliner->tree, TSE_ACTIVE); @@ -1632,7 +1688,8 @@ static void do_outliner_range_select(bContext *C, return; } - do_outliner_range_select_recursive(&space_outliner->tree, active, cursor, false); + do_outliner_range_select_recursive( + &space_outliner->tree, active, cursor, false, recurse, in_collection); } static bool outliner_is_co_within_restrict_columns(const SpaceOutliner *space_outliner, @@ -1673,7 +1730,8 @@ static int outliner_item_do_activate_from_cursor(bContext *C, const int mval[2], const bool extend, const bool use_range, - const bool deselect_all) + const bool deselect_all, + const bool recurse) { ARegion *region = CTX_wm_region(C); SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); @@ -1716,21 +1774,73 @@ static int outliner_item_do_activate_from_cursor(bContext *C, TreeStoreElem *activate_tselem = TREESTORE(activate_te); + /* If we're recursing, we need to know the collection of the selected item in order + * to prevent selecting across collection boundaries. (Object hierarchies might cross + * collection boundaries, i.e., children may be in different collections from their + * parents.) */ + Collection *parent_collection = nullptr; + if (recurse) { + if (activate_tselem->type == TSE_LAYER_COLLECTION) { + parent_collection = static_cast(activate_te->directdata)->collection; + } + else if (activate_tselem->type == TSE_SOME_ID && activate_te->idcode == ID_OB) { + parent_collection = BKE_collection_object_find( + CTX_data_main(C), + CTX_data_scene(C), + nullptr, + reinterpret_cast(activate_tselem->id)); + } + } + + /* If we're not recursing (not double clicking), and we are extending or range selecting by + * holding CTRL or SHIFT, ignore events when the cursor is over the icon. This disambiguates + * the case where we are recursing *and* holding CTRL or SHIFT in order to extend or range + * select recursively. */ + if (!recurse && (extend || use_range) && + outliner_item_is_co_over_icon(activate_te, view_mval[0])) + { + return OPERATOR_CANCELLED; + } + if (use_range) { - do_outliner_range_select(C, space_outliner, activate_te, extend); + do_outliner_range_select(C, space_outliner, activate_te, extend, recurse, parent_collection); + if (recurse) { + do_outliner_select_recursive(&activate_te->subtree, true, parent_collection); + } } else { const bool is_over_name_icons = outliner_item_is_co_over_name_icons(activate_te, view_mval[0]); - /* Always select unless already active and selected */ - const bool select = !extend || !(activate_tselem->flag & TSE_ACTIVE && - activate_tselem->flag & TSE_SELECTED); + /* Always select unless already active and selected. */ + bool select = !extend || !(activate_tselem->flag & TSE_ACTIVE) || + !(activate_tselem->flag & TSE_SELECTED); + + /* If we're CTRL+double-clicking and the element is aleady + * selected, skip the activation and go straight to deselection. */ + if (extend && recurse && activate_tselem->flag & TSE_SELECTED) { + select = false; + } const short select_flag = OL_ITEM_ACTIVATE | (select ? OL_ITEM_SELECT : OL_ITEM_DESELECT) | (is_over_name_icons ? OL_ITEM_SELECT_DATA : 0) | (extend ? OL_ITEM_EXTEND : 0); - outliner_item_select(C, space_outliner, activate_te, select_flag); + /* The recurse flag is set when the user double-clicks + * to select everything in a collection or hierarchy. */ + if (recurse) { + if (outliner_item_is_co_over_icon(activate_te, view_mval[0])) { + /* Select or deselect object hierarchy recursively. */ + outliner_item_select(C, space_outliner, activate_te, select_flag); + do_outliner_select_recursive(&activate_te->subtree, select, parent_collection); + } + else { + /* Double-clicked, but it wasn't on the icon. */ + return OPERATOR_CANCELLED; + } + } + else { + outliner_item_select(C, space_outliner, activate_te, select_flag); + } /* Only switch properties editor tabs when icons are selected. */ if (is_over_icon) { @@ -1765,10 +1875,11 @@ static int outliner_item_activate_invoke(bContext *C, wmOperator *op, const wmEv const bool extend = RNA_boolean_get(op->ptr, "extend"); const bool use_range = RNA_boolean_get(op->ptr, "extend_range"); const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); + const bool recurse = RNA_boolean_get(op->ptr, "recurse"); int mval[2]; WM_event_drag_start_mval(event, region, mval); - return outliner_item_do_activate_from_cursor(C, mval, extend, use_range, deselect_all); + return outliner_item_do_activate_from_cursor(C, mval, extend, use_range, deselect_all, recurse); } void OUTLINER_OT_item_activate(wmOperatorType *ot) @@ -1796,6 +1907,10 @@ void OUTLINER_OT_item_activate(wmOperatorType *ot) "Deselect On Nothing", "Deselect all when nothing under the cursor"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); + + prop = RNA_def_boolean( + ot->srna, "recurse", false, "Recurse", "Select objects recursively from active element"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /** \} */ -- 2.30.2 From df57dc18824e69a82b94f6ce9d69956b5ba55896 Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Wed, 17 Jan 2024 01:09:12 +0100 Subject: [PATCH 035/244] UI: Cleanup Dialog to Manage Orphaned Data Creates a new "Cleanup" dialog that allows purging unused data blocks, as well as a window used for managing unused data blocks. Pull Request: https://projects.blender.org/blender/blender/pulls/106653 --- scripts/startup/bl_ui/space_topbar.py | 31 +- .../editors/space_outliner/CMakeLists.txt | 2 + .../editors/space_outliner/outliner_edit.cc | 269 +++++++++++++++++- .../editors/space_outliner/outliner_intern.hh | 2 + .../editors/space_outliner/outliner_ops.cc | 2 + 5 files changed, 273 insertions(+), 33 deletions(-) diff --git a/scripts/startup/bl_ui/space_topbar.py b/scripts/startup/bl_ui/space_topbar.py index ab1663c2ada..32ae9ff3736 100644 --- a/scripts/startup/bl_ui/space_topbar.py +++ b/scripts/startup/bl_ui/space_topbar.py @@ -237,35 +237,8 @@ class TOPBAR_MT_file_cleanup(Menu): def draw(self, _context): layout = self.layout layout.separator() - - props = layout.operator("outliner.orphans_purge", text="Unused Data-Blocks") - props.do_local_ids = True - props.do_linked_ids = True - props.do_recursive = False - props = layout.operator("outliner.orphans_purge", text="Recursive Unused Data-Blocks") - props.do_local_ids = True - props.do_linked_ids = True - props.do_recursive = True - - layout.separator() - props = layout.operator("outliner.orphans_purge", text="Unused Linked Data-Blocks") - props.do_local_ids = False - props.do_linked_ids = True - props.do_recursive = False - props = layout.operator("outliner.orphans_purge", text="Recursive Unused Linked Data-Blocks") - props.do_local_ids = False - props.do_linked_ids = True - props.do_recursive = True - - layout.separator() - props = layout.operator("outliner.orphans_purge", text="Unused Local Data-Blocks") - props.do_local_ids = True - props.do_linked_ids = False - props.do_recursive = False - props = layout.operator("outliner.orphans_purge", text="Recursive Unused Local Data-Blocks") - props.do_local_ids = True - props.do_linked_ids = False - props.do_recursive = True + layout.operator("outliner.orphans_cleanup") + layout.operator("outliner.orphans_manage") class TOPBAR_MT_file(Menu): diff --git a/source/blender/editors/space_outliner/CMakeLists.txt b/source/blender/editors/space_outliner/CMakeLists.txt index 26df8082a1c..8100f830dbf 100644 --- a/source/blender/editors/space_outliner/CMakeLists.txt +++ b/source/blender/editors/space_outliner/CMakeLists.txt @@ -12,6 +12,7 @@ set(INC ../../makesrna ../../sequencer ../../windowmanager + ../../../../extern/fmtlib/include # RNA_prototypes.h ${CMAKE_BINARY_DIR}/source/blender/makesrna @@ -137,6 +138,7 @@ set(LIB bf_editor_undo PRIVATE bf::intern::clog PRIVATE bf::intern::guardedalloc + extern_fmtlib ) diff --git a/source/blender/editors/space_outliner/outliner_edit.cc b/source/blender/editors/space_outliner/outliner_edit.cc index 7734984970a..08050e1d419 100644 --- a/source/blender/editors/space_outliner/outliner_edit.cc +++ b/source/blender/editors/space_outliner/outliner_edit.cc @@ -65,6 +65,10 @@ #include "tree/tree_element_rna.hh" #include "tree/tree_iterator.hh" +#include "wm_window.hh" + +#include + using namespace blender::ed::outliner; namespace blender::ed::outliner { @@ -2140,7 +2144,7 @@ static int outliner_orphans_purge_invoke(bContext *C, wmOperator *op, const wmEv RNA_int_set(op->ptr, "num_deleted", num_tagged[INDEX_ID_NULL]); if (num_tagged[INDEX_ID_NULL] == 0) { - BKE_report(op->reports, RPT_INFO, "No orphaned data-blocks to purge"); + BKE_report(op->reports, RPT_INFO, "No unused data-blocks to purge"); return OPERATOR_CANCELLED; } @@ -2188,7 +2192,7 @@ static int outliner_orphans_purge_exec(bContext *C, wmOperator *op) bmain, LIB_TAG_DOIT, do_local_ids, do_linked_ids, do_recursive_cleanup, num_tagged); if (num_tagged[INDEX_ID_NULL] == 0) { - BKE_report(op->reports, RPT_INFO, "No orphaned data-blocks to purge"); + BKE_report(op->reports, RPT_INFO, "No unused data-blocks to purge"); return OPERATOR_CANCELLED; } } @@ -2219,7 +2223,7 @@ void OUTLINER_OT_orphans_purge(wmOperatorType *ot) /* identifiers */ ot->idname = "OUTLINER_OT_orphans_purge"; ot->name = "Purge All"; - ot->description = "Clear all orphaned data-blocks without any users from the file"; + ot->description = "Remove all unused data-blocks without any users from the file"; /* callbacks */ ot->invoke = outliner_orphans_purge_invoke; @@ -2248,10 +2252,267 @@ void OUTLINER_OT_orphans_purge(wmOperatorType *ot) "do_recursive", false, "Recursive Delete", - "Recursively check for indirectly unused data-blocks, ensuring that no orphaned " + "Recursively check for indirectly unused data-blocks, ensuring that no unused " "data-blocks remain after execution"); } +static void wm_block_orphans_cancel(bContext *C, void *arg_block, void * /*arg_data*/) +{ + UI_popup_block_close(C, CTX_wm_window(C), (uiBlock *)arg_block); +} + +static void wm_block_orphans_cancel_button(uiBlock *block) +{ + 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_orphans_cancel, block, nullptr); + UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT); + UI_but_flag_enable(but, UI_BUT_ACTIVE_DEFAULT); +} + +struct OrphansPurgeData { + wmOperator *op = nullptr; + char local = true; + char linked = false; + char recursive = false; +}; + +static void wm_block_orphans_purge(bContext *C, void *arg_block, void *arg_data) +{ + OrphansPurgeData *purge_data = (OrphansPurgeData *)arg_data; + wmOperator *op = purge_data->op; + Main *bmain = CTX_data_main(C); + int num_tagged[INDEX_ID_MAX] = {0}; + + /* Tag all IDs to delete. */ + BKE_lib_query_unused_ids_tag(bmain, + LIB_TAG_DOIT, + purge_data->local, + purge_data->linked, + purge_data->recursive, + num_tagged); + + if (num_tagged[INDEX_ID_NULL] == 0) { + BKE_report(op->reports, RPT_INFO, "No unused data to remove"); + } + else { + BKE_id_multi_tagged_delete(bmain); + BKE_reportf(op->reports, RPT_INFO, "Deleted %d data block(s)", num_tagged[INDEX_ID_NULL]); + DEG_relations_tag_update(bmain); + WM_event_add_notifier(C, NC_ID | NA_REMOVED, nullptr); + /* Force full redraw of the UI. */ + WM_main_add_notifier(NC_WINDOW, nullptr); + } + + UI_popup_block_close(C, CTX_wm_window(C), (uiBlock *)arg_block); +} + +static void wm_block_orphans_purge_button(uiBlock *block, OrphansPurgeData *purge_data) +{ + uiBut *but = uiDefIconTextBut( + block, UI_BTYPE_BUT, 0, 0, IFACE_("Delete"), 0, 0, 0, UI_UNIT_Y, 0, 0, 0, 0, 0, ""); + UI_but_func_set(but, wm_block_orphans_purge, block, purge_data); + UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT); +} + +static std::string orphan_desc(const bContext *C, const bool local, bool linked, bool recursive) +{ + Main *bmain = CTX_data_main(C); + int num_tagged[INDEX_ID_MAX] = {0}; + std::string desc; + + BKE_lib_query_unused_ids_tag(bmain, LIB_TAG_DOIT, local, linked, recursive, num_tagged); + + bool is_first = true; + for (int i = 0; i < INDEX_ID_MAX - 2; i++) { + if (num_tagged[i] != 0) { + desc += fmt::format( + "{}{} {}", + (is_first) ? "" : ", ", + num_tagged[i], + (num_tagged[i] > 1) ? + TIP_(BKE_idtype_idcode_to_name_plural(BKE_idtype_idcode_from_index(i))) : + TIP_(BKE_idtype_idcode_to_name(BKE_idtype_idcode_from_index(i)))); + is_first = false; + } + } + + if (desc.empty()) { + desc = "Nothing"; + } + + return desc; +} + +static uiBlock *wm_block_create_orphans_cleanup(bContext *C, ARegion *region, void *arg) +{ + OrphansPurgeData *purge_data = static_cast(arg); + uiBlock *block = UI_block_begin(C, region, "orphans_remove_popup", 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, 40, ALERT_ICON_WARNING); + + /* Title. */ + uiItemL_ex(layout, TIP_("Purge Unused Data From This File"), 0, true, false); + + uiItemS(layout); + + std::string desc = "Local data: " + orphan_desc(C, true, false, false); + + uiDefButBitC(block, + UI_BTYPE_CHECKBOX, + 1, + 0, + desc.c_str(), + 0, + 0, + 0, + UI_UNIT_Y, + &purge_data->local, + 0, + 0, + 0, + 0, + "Delete unused local data"); + + desc = "Linked data: " + orphan_desc(C, false, true, false); + + uiDefButBitC(block, + UI_BTYPE_CHECKBOX, + 1, + 0, + desc.c_str(), + 0, + 0, + 0, + UI_UNIT_Y, + &purge_data->linked, + 0, + 0, + 0, + 0, + "Delete unused linked data"); + + uiDefButBitC( + block, + UI_BTYPE_CHECKBOX, + 1, + 0, + "Include indirect data", + 0, + 0, + 0, + UI_UNIT_Y, + &purge_data->recursive, + 0, + 0, + 0, + 0, + "Recursively check for indirectly unused data, ensuring that no unused data remains"); + + uiItemS_ex(layout, 2.0f); + + /* Buttons. */ +#ifdef _WIN32 + const bool windows_layout = true; +#else + const bool windows_layout = false; +#endif + + uiLayout *split = uiLayoutSplit(layout, 0.0f, true); + uiLayoutSetScaleY(split, 1.2f); + + if (windows_layout) { + /* Windows standard layout. */ + uiLayoutColumn(split, false); + wm_block_orphans_purge_button(block, purge_data); + uiLayoutColumn(split, false); + wm_block_orphans_cancel_button(block); + } + else { + /* Non-Windows layout (macOS and Linux). */ + uiLayoutColumn(split, false); + wm_block_orphans_cancel_button(block); + uiLayoutColumn(split, false); + wm_block_orphans_purge_button(block, purge_data); + } + + UI_block_bounds_set_centered(block, 14 * UI_SCALE_FAC); + return block; +} + +static int outliner_orphans_cleanup_exec(bContext *C, wmOperator *op) +{ + OrphansPurgeData *purge_data = MEM_new(__func__); + purge_data->op = op; + UI_popup_block_invoke(C, wm_block_create_orphans_cleanup, purge_data, MEM_freeN); + return OPERATOR_FINISHED; +} + +void OUTLINER_OT_orphans_cleanup(wmOperatorType *ot) +{ + /* identifiers */ + ot->idname = "OUTLINER_OT_orphans_cleanup"; + ot->name = "Purge Unused Data..."; + ot->description = "Remove unused data from this file"; + + /* callbacks */ + ot->exec = outliner_orphans_cleanup_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static int outliner_orphans_manage_exec(bContext *C, wmOperator * /*op*/) +{ + int width = 800; + int height = 500; + if (wm_get_screensize(&width, &height)) { + width /= 2; + height /= 2; + } + + const rcti window_rect = { + /*xmin*/ 0, + /*xmax*/ width, + /*ymin*/ 0, + /*ymax*/ height, + }; + + if (WM_window_open(C, + IFACE_("Manage Unused Data"), + &window_rect, + SPACE_OUTLINER, + false, + false, + true, + WIN_ALIGN_PARENT_CENTER, + nullptr, + nullptr) != nullptr) + { + SpaceOutliner *soutline = (SpaceOutliner *)CTX_wm_area(C)->spacedata.first; + soutline->outlinevis = SO_ID_ORPHANS; + return OPERATOR_FINISHED; + } + return OPERATOR_CANCELLED; +} + +void OUTLINER_OT_orphans_manage(wmOperatorType *ot) +{ + /* identifiers */ + ot->idname = "OUTLINER_OT_orphans_manage"; + ot->name = "Manage Unused Data..."; + ot->description = "Open a window to manage unused data"; + + /* callbacks */ + ot->exec = outliner_orphans_manage_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER; +} + /** \} */ } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/outliner_intern.hh b/source/blender/editors/space_outliner/outliner_intern.hh index b81ff4437f1..86397252928 100644 --- a/source/blender/editors/space_outliner/outliner_intern.hh +++ b/source/blender/editors/space_outliner/outliner_intern.hh @@ -511,7 +511,9 @@ void OUTLINER_OT_keyingset_remove_selected(wmOperatorType *ot); void OUTLINER_OT_drivers_add_selected(wmOperatorType *ot); void OUTLINER_OT_drivers_delete_selected(wmOperatorType *ot); +void OUTLINER_OT_orphans_cleanup(wmOperatorType *ot); void OUTLINER_OT_orphans_purge(wmOperatorType *ot); +void OUTLINER_OT_orphans_manage(wmOperatorType *ot); /* `outliner_query.cc` */ diff --git a/source/blender/editors/space_outliner/outliner_ops.cc b/source/blender/editors/space_outliner/outliner_ops.cc index b94df433274..166f7cbb67e 100644 --- a/source/blender/editors/space_outliner/outliner_ops.cc +++ b/source/blender/editors/space_outliner/outliner_ops.cc @@ -60,6 +60,8 @@ void outliner_operatortypes() WM_operatortype_append(OUTLINER_OT_drivers_delete_selected); WM_operatortype_append(OUTLINER_OT_orphans_purge); + WM_operatortype_append(OUTLINER_OT_orphans_cleanup); + WM_operatortype_append(OUTLINER_OT_orphans_manage); WM_operatortype_append(OUTLINER_OT_parent_drop); WM_operatortype_append(OUTLINER_OT_parent_clear); -- 2.30.2 From c3334fa10bcf33617f35c94e23d6c6fe1697d761 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 17 Jan 2024 11:11:12 +1100 Subject: [PATCH 036/244] Fix #107805: Render border without an active region crashes --- source/blender/editors/space_view3d/view3d_edit.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/space_view3d/view3d_edit.cc b/source/blender/editors/space_view3d/view3d_edit.cc index c2f0a0d2380..7ed51ba546f 100644 --- a/source/blender/editors/space_view3d/view3d_edit.cc +++ b/source/blender/editors/space_view3d/view3d_edit.cc @@ -340,7 +340,7 @@ void VIEW3D_OT_render_border(wmOperatorType *ot) ot->modal = WM_gesture_box_modal; ot->cancel = WM_gesture_box_cancel; - ot->poll = ED_operator_view3d_active; + ot->poll = ED_operator_region_view3d_active; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -- 2.30.2 From ef18df9db523cd930becc1522f8474de75af86e9 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 16 Jan 2024 16:36:18 -0500 Subject: [PATCH 037/244] Fix: Memory leaks after recent button string cleanup --- source/blender/editors/interface/interface.cc | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc index b511980e3d9..db46345183f 100644 --- a/source/blender/editors/interface/interface.cc +++ b/source/blender/editors/interface/interface.cc @@ -6605,7 +6605,7 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...) tmp = but->tip_func(C, but->tip_arg, but->tip); } else if (but->tip && but->tip[0]) { - tmp = BLI_strdup(but->tip); + tmp = but->tip; } else { type = BUT_GET_RNA_TIP; /* Fail-safe solution... */ @@ -6614,49 +6614,49 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...) if (type == BUT_GET_RNAPROP_IDENTIFIER) { if (but->rnaprop) { - tmp = BLI_strdup(RNA_property_identifier(but->rnaprop)); + tmp = RNA_property_identifier(but->rnaprop); } } else if (type == BUT_GET_RNASTRUCT_IDENTIFIER) { if (but->rnaprop && but->rnapoin.data) { - tmp = BLI_strdup(RNA_struct_identifier(but->rnapoin.type)); + tmp = RNA_struct_identifier(but->rnapoin.type); } else if (but->optype) { - tmp = BLI_strdup(but->optype->idname); + tmp = but->optype->idname; } else if (ELEM(but->type, UI_BTYPE_MENU, UI_BTYPE_PULLDOWN)) { MenuType *mt = UI_but_menutype_get(but); if (mt) { - tmp = BLI_strdup(mt->idname); + tmp = mt->idname; } } else if (but->type == UI_BTYPE_POPOVER) { PanelType *pt = UI_but_paneltype_get(but); if (pt) { - tmp = BLI_strdup(pt->idname); + tmp = pt->idname; } } } else if (ELEM(type, BUT_GET_RNA_LABEL, BUT_GET_RNA_TIP)) { if (but->rnaprop) { if (type == BUT_GET_RNA_LABEL) { - tmp = BLI_strdup(RNA_property_ui_name(but->rnaprop)); + tmp = RNA_property_ui_name(but->rnaprop); } else { const char *t = RNA_property_ui_description(but->rnaprop); if (t && t[0]) { - tmp = BLI_strdup(t); + tmp = t; } } } else if (but->optype) { if (type == BUT_GET_RNA_LABEL) { - tmp = BLI_strdup(WM_operatortype_name(but->optype, opptr).c_str()); + tmp = WM_operatortype_name(but->optype, opptr).c_str(); } else { const bContextStore *previous_ctx = CTX_store_get(C); CTX_store_set(C, but->context); - tmp = BLI_strdup(WM_operatortype_description(C, but->optype, opptr).c_str()); + tmp = WM_operatortype_description(C, but->optype, opptr).c_str(); CTX_store_set(C, previous_ctx); } } @@ -6665,14 +6665,14 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...) MenuType *mt = UI_but_menutype_get(but); if (mt) { if (type == BUT_GET_RNA_LABEL) { - tmp = BLI_strdup(CTX_TIP_(mt->translation_context, mt->label)); + tmp = CTX_TIP_(mt->translation_context, mt->label); } else { /* Not all menus are from Python. */ if (mt->rna_ext.srna) { const char *t = RNA_struct_ui_description(mt->rna_ext.srna); if (t && t[0]) { - tmp = BLI_strdup(t); + tmp = t; } } } @@ -6683,10 +6683,10 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...) wmOperatorType *ot = UI_but_operatortype_get_from_enum_menu(but, nullptr); if (ot) { if (type == BUT_GET_RNA_LABEL) { - tmp = BLI_strdup(WM_operatortype_name(ot, nullptr).c_str()); + tmp = WM_operatortype_name(ot, nullptr).c_str(); } else { - tmp = BLI_strdup(WM_operatortype_description(C, ot, nullptr).c_str()); + tmp = WM_operatortype_description(C, ot, nullptr).c_str(); } } } @@ -6695,7 +6695,7 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...) PanelType *pt = UI_but_paneltype_get(but); if (pt) { if (type == BUT_GET_RNA_LABEL) { - tmp = BLI_strdup(CTX_TIP_(pt->translation_context, pt->label)); + tmp = CTX_TIP_(pt->translation_context, pt->label); } else { /* Not all panels are from Python. */ @@ -6724,7 +6724,7 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...) if (BLT_is_default_context(_tmp)) { _tmp = BLT_I18NCONTEXT_DEFAULT_BPYRNA; } - tmp = BLI_strdup(_tmp); + tmp = _tmp; } else if (ELEM(type, BUT_GET_RNAENUM_IDENTIFIER, BUT_GET_RNAENUM_LABEL, BUT_GET_RNAENUM_TIP)) { PointerRNA *ptr = nullptr; @@ -6773,13 +6773,13 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...) } if (item && item->identifier) { if (type == BUT_GET_RNAENUM_IDENTIFIER) { - tmp = BLI_strdup(item->identifier); + tmp = item->identifier; } else if (type == BUT_GET_RNAENUM_LABEL) { - tmp = BLI_strdup(item->name); + tmp = item->name; } else if (item->description && item->description[0]) { - tmp = BLI_strdup(item->description); + tmp = item->description; } } } @@ -6791,7 +6791,7 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...) if (!(ui_block_is_menu(but->block) && !ui_block_is_pie_menu(but->block))) { char buf[128]; if (ui_but_event_operator_string(C, but, buf, sizeof(buf))) { - tmp = BLI_strdup(buf); + tmp = buf; } } } @@ -6799,7 +6799,7 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...) if (!(ui_block_is_menu(but->block) && !ui_block_is_pie_menu(but->block))) { char buf[128]; if (ui_but_event_property_operator_string(C, but, buf, sizeof(buf))) { - tmp = BLI_strdup(buf); + tmp = buf; } } } @@ -6823,19 +6823,19 @@ void UI_but_extra_icon_string_info_get(bContext *C, uiButExtraOpIcon *extra_icon va_start(args, extra_icon); while ((si = (uiStringInfo *)va_arg(args, void *))) { - char *tmp = nullptr; + std::string tmp; switch (si->type) { case BUT_GET_LABEL: - tmp = BLI_strdup(WM_operatortype_name(optype, opptr).c_str()); + tmp = WM_operatortype_name(optype, opptr); break; case BUT_GET_TIP: - tmp = BLI_strdup(WM_operatortype_description(C, optype, opptr).c_str()); + tmp = WM_operatortype_description(C, optype, opptr); break; case BUT_GET_OP_KEYMAP: { char buf[128]; if (ui_but_extra_icon_event_operator_string(C, extra_icon, buf, sizeof(buf))) { - tmp = BLI_strdup(buf); + tmp = buf; } break; } @@ -6845,7 +6845,7 @@ void UI_but_extra_icon_string_info_get(bContext *C, uiButExtraOpIcon *extra_icon break; } - si->strinfo = tmp; + si->strinfo = BLI_strdupn(tmp.c_str(), tmp.size()); } va_end(args); } -- 2.30.2 From 09871cec4e7a0c493350c5b7e6a61b9ee3c5d526 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 16 Jan 2024 19:32:05 -0500 Subject: [PATCH 038/244] Fix: Tooltip contains bogus text after recent button string cleanup The code still needs to differentiate a null string from an empty string in one cast I missed earlier. --- source/blender/editors/interface/interface.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc index db46345183f..1c7d8468c89 100644 --- a/source/blender/editors/interface/interface.cc +++ b/source/blender/editors/interface/interface.cc @@ -6577,7 +6577,7 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...) va_start(args, but); while ((si = (uiStringInfo *)va_arg(args, void *))) { uiStringInfoType type = si->type; - std::string tmp; + std::optional tmp; if (type == BUT_GET_TIP_LABEL) { if (but->tip_label_func) { @@ -6679,7 +6679,7 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...) } } - if (tmp.empty()) { + if (!tmp) { wmOperatorType *ot = UI_but_operatortype_get_from_enum_menu(but, nullptr); if (ot) { if (type == BUT_GET_RNA_LABEL) { @@ -6691,7 +6691,7 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...) } } - if (tmp.empty()) { + if (!tmp) { PanelType *pt = UI_but_paneltype_get(but); if (pt) { if (type == BUT_GET_RNA_LABEL) { @@ -6804,7 +6804,7 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...) } } - si->strinfo = BLI_strdupn(tmp.c_str(), tmp.size()); + si->strinfo = tmp ? BLI_strdupn(tmp->c_str(), tmp->size()) : nullptr; } va_end(args); -- 2.30.2 From 79804353ad85236cbe20b203e9c0cd6e5c84280e Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 17 Jan 2024 16:18:54 +1100 Subject: [PATCH 039/244] Fix #117188: Context.temp_override() failed with full-screen areas Using screen changing functions with screens used for full screen areas isn't supported & caused corrupt screen data. Add checks that the current and overriding screen support switching, raising an error when they don't. Also add a check when restoring the context not to change any full screen areas. --- .../blender/python/intern/bpy_rna_context.cc | 38 +++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/source/blender/python/intern/bpy_rna_context.cc b/source/blender/python/intern/bpy_rna_context.cc index 9d6ba06a28f..9a769c64786 100644 --- a/source/blender/python/intern/bpy_rna_context.cc +++ b/source/blender/python/intern/bpy_rna_context.cc @@ -15,6 +15,7 @@ #include "BKE_context.hh" #include "BKE_main.hh" +#include "BKE_screen.hh" #include "BKE_workspace.h" #include "WM_api.hh" @@ -49,6 +50,20 @@ static void bpy_rna_context_temp_set_screen_for_window(bContext *C, wmWindow *wi WM_window_set_active_screen(win, workspace, screen); } +/** + * Switching to or away from this screen is not supported. + */ +static bool wm_check_screen_switch_supported(const bScreen *screen) +{ + if (screen->temp != 0) { + return false; + } + if (BKE_screen_is_fullscreen_area(screen)) { + return false; + } + return true; +} + static bool wm_check_window_exists(const Main *bmain, const wmWindow *win) { LISTBASE_FOREACH (wmWindowManager *, wm, &bmain->wm) { @@ -233,9 +248,17 @@ static PyObject *bpy_rna_context_temp_override_enter(BPyContextTempOverride *sel /* Skip some checks when the screen is unchanged. */ if (self->ctx_init.screen_is_set) { - if (screen->temp != 0) { + /* Switching away from a temporary screen isn't supported. */ + if ((self->ctx_init.screen != nullptr) && + !wm_check_screen_switch_supported(self->ctx_init.screen)) + { PyErr_SetString(PyExc_TypeError, - "Overriding context with temporary screen is not supported"); + "Overriding context with an active temporary screen isn't supported"); + return nullptr; + } + if (!wm_check_screen_switch_supported(screen)) { + PyErr_SetString(PyExc_TypeError, + "Overriding context with temporary screen isn't supported"); return nullptr; } if (BKE_workspace_layout_find_global(bmain, screen, nullptr) == nullptr) { @@ -308,7 +331,12 @@ static PyObject *bpy_rna_context_temp_override_exit(BPyContextTempOverride *self if (self->ctx_temp_orig.screen && wm_check_screen_exists(bmain, self->ctx_temp_orig.screen)) { wmWindow *win = self->ctx_temp.win_is_set ? self->ctx_temp.win : self->ctx_init.win; if (win && wm_check_window_exists(bmain, win)) { - bpy_rna_context_temp_set_screen_for_window(C, win, self->ctx_temp_orig.screen); + /* Disallow switching away from temporary-screens & full-screen areas, while it could be + * useful to support this closing a these screens uses different and more involved logic + * compared with switching between user managed screens, see: #117188. */ + if (wm_check_screen_switch_supported(WM_window_get_active_screen(win))) { + bpy_rna_context_temp_set_screen_for_window(C, win, self->ctx_temp_orig.screen); + } } } } @@ -523,6 +551,10 @@ PyDoc_STRVAR(bpy_context_temp_override_doc, " :type window: :class:`bpy.types.Window`\n" " :arg screen: Screen override or None.\n" "\n" + " .. note:: Switching to or away from full-screen areas & temporary screens " + "isn't supported. Passing in these screens will raise an exception, " + "actions that leave the context such screens won't restore the prior screen.\n" + "\n" " .. note:: Changing the screen has wider implications " "than other arguments as it will also change the works-space " "and potentially the scene (when pinned).\n" -- 2.30.2 From 5b4391c95064723edae5104f42939192d6ec8f6e Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 17 Jan 2024 18:27:45 +1100 Subject: [PATCH 040/244] PyAPI: improve warnings when addons from extensions repos fail to load --- scripts/modules/addon_utils.py | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/scripts/modules/addon_utils.py b/scripts/modules/addon_utils.py index 51c9eb158c5..c45725ebd7b 100644 --- a/scripts/modules/addon_utils.py +++ b/scripts/modules/addon_utils.py @@ -373,8 +373,32 @@ def enable(module_name, *, default_set=False, persistent=False, handle_error=Non # If the add-on doesn't exist, don't print full trace-back because the back-trace is in this case # is verbose without any useful details. A missing path is better communicated in a short message. # Account for `ImportError` & `ModuleNotFoundError`. - if isinstance(ex, ImportError) and ex.name == module_name: - print("Add-on not loaded:", repr(module_name), "cause:", str(ex)) + if isinstance(ex, ImportError): + if ex.name == module_name: + print("Add-on not loaded: \"%s\", cause: %s" % (module_name, str(ex))) + + # Issue with an add-on from an extension repository, report a useful message. + elif module_name.startswith(ex.name + ".") and module_name.startswith(_ext_base_pkg_idname + "."): + repo_id = module_name[len(_ext_base_pkg_idname) + 1:].rpartition(".")[0] + repo = next( + (repo for repo in _preferences.filepaths.extension_repos if repo.module == repo_id), + None, + ) + if repo is None: + print( + "Add-on not loaded: \"%s\", cause: extension repository \"%s\" doesn't exist" % + (module_name, repo_id) + ) + elif not repo.enabled: + print( + "Add-on not loaded: \"%s\", cause: extension repository \"%s\" is disabled" % + (module_name, repo_id) + ) + else: + # The repository exists and is enabled, it should have imported. + print("Add-on not loaded: \"%s\", cause: %s" % (module_name, str(ex))) + else: + handle_error(ex) else: handle_error(ex) -- 2.30.2 From 3ce407e7573efa7772513d584a842cd6f82d0957 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 17 Jan 2024 18:36:54 +1100 Subject: [PATCH 041/244] Fix font sub-pixel AA impacting character spacing when AA is disabled --- source/blender/blenfont/BLF_api.h | 6 +++++- source/blender/blenfont/intern/blf_glyph.cc | 2 +- source/blender/editors/interface/interface_style.cc | 6 ++++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/source/blender/blenfont/BLF_api.h b/source/blender/blenfont/BLF_api.h index 15096f96ead..6824e63fc00 100644 --- a/source/blender/blenfont/BLF_api.h +++ b/source/blender/blenfont/BLF_api.h @@ -375,7 +375,11 @@ enum { BLF_BAD_FONT = 1 << 16, /** This font is managed by the FreeType cache subsystem. */ BLF_CACHED = 1 << 17, - /** At small sizes glyphs are rendered at multiple sub-pixel positions. */ + /** + * At small sizes glyphs are rendered at multiple sub-pixel positions. + * + * \note Can be checked without checking #BLF_MONOSPACED which can be assumed to be disabled. + */ BLF_RENDER_SUBPIXELAA = 1 << 18, }; diff --git a/source/blender/blenfont/intern/blf_glyph.cc b/source/blender/blenfont/intern/blf_glyph.cc index 1f5e4368d8d..9f12a727066 100644 --- a/source/blender/blenfont/intern/blf_glyph.cc +++ b/source/blender/blenfont/intern/blf_glyph.cc @@ -1322,7 +1322,7 @@ GlyphBLF *blf_glyph_ensure(FontBLF *font, GlyphCacheBLF *gc, const uint charcode #ifdef BLF_SUBPIXEL_AA GlyphBLF *blf_glyph_ensure_subpixel(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, int32_t pen_x) { - if (!(font->flags & BLF_RENDER_SUBPIXELAA) || (font->flags & BLF_MONOCHROME)) { + if (!(font->flags & BLF_RENDER_SUBPIXELAA)) { /* Not if we are in mono mode (aliased) or the feature is turned off. */ return g; } diff --git a/source/blender/editors/interface/interface_style.cc b/source/blender/editors/interface/interface_style.cc index 5b0be9f2abc..a6cbdae2fb5 100644 --- a/source/blender/editors/interface/interface_style.cc +++ b/source/blender/editors/interface/interface_style.cc @@ -460,8 +460,10 @@ void uiStyleInit() if (U.text_render & USER_TEXT_DISABLE_AA) { flag_enable |= BLF_MONOCHROME; } - if (U.text_render & USER_TEXT_RENDER_SUBPIXELAA) { - flag_enable |= BLF_RENDER_SUBPIXELAA; + else { + if (U.text_render & USER_TEXT_RENDER_SUBPIXELAA) { + flag_enable |= BLF_RENDER_SUBPIXELAA; + } } LISTBASE_FOREACH (uiFont *, font, &U.uifonts) { -- 2.30.2 From b1dcb37f767db6d132a7671a08bdb0af8ca8e95c Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 16 Jan 2024 19:37:50 +0100 Subject: [PATCH 042/244] Fix incorrect number of planes for ImBuf used for render passes Set the number of planes based on the number of pass channels. If the pass contains 2 passes or more than 4 passes set the number of planes to the previously used value of 32. This is needed because quite some areas check for the number of planes for various optimizations. For example, this is one of the factors which make IMB_create_gpu_texture() to choose the texture format. If the number of planes for the depth pass is set to the previously used this function will never consider using single channel GPU texture. Unfortunately, this change is not enough to make the GPU texture to use single channel format as the color space of the image buffer is also checked, and that is nullptr which means scene linear. --- source/blender/render/intern/render_result.cc | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/source/blender/render/intern/render_result.cc b/source/blender/render/intern/render_result.cc index 5e70e2a4186..0b78213ee02 100644 --- a/source/blender/render/intern/render_result.cc +++ b/source/blender/render/intern/render_result.cc @@ -167,6 +167,21 @@ void render_result_views_shallowdelete(RenderResult *rr) /** \name New * \{ */ +static int get_num_planes_for_pass_ibuf(const RenderPass &render_pass) +{ + switch (render_pass.channels) { + case 1: + return R_IMF_PLANES_BW; + case 3: + return R_IMF_PLANES_RGB; + case 4: + return R_IMF_PLANES_RGBA; + } + + /* Fallback to a commonly used default value of planes for odd-ball number of channel. */ + return R_IMF_PLANES_RGBA; +} + static void render_layer_allocate_pass(RenderResult *rr, RenderPass *rp) { if (rp->ibuf && rp->ibuf->float_buffer.data) { @@ -179,7 +194,7 @@ static void render_layer_allocate_pass(RenderResult *rr, RenderPass *rp) const size_t rectsize = size_t(rr->rectx) * rr->recty * rp->channels; float *buffer_data = MEM_cnew_array(rectsize, rp->name); - rp->ibuf = IMB_allocImBuf(rr->rectx, rr->recty, 32, 0); + rp->ibuf = IMB_allocImBuf(rr->rectx, rr->recty, get_num_planes_for_pass_ibuf(*rp), 0); rp->ibuf->channels = rp->channels; IMB_assign_float_buffer(rp->ibuf, buffer_data, IB_TAKE_OWNERSHIP); @@ -1302,7 +1317,8 @@ RenderResult *RE_DuplicateRenderResult(RenderResult *rr) ImBuf *RE_RenderPassEnsureImBuf(RenderPass *render_pass) { if (!render_pass->ibuf) { - render_pass->ibuf = IMB_allocImBuf(render_pass->rectx, render_pass->recty, 32, 0); + render_pass->ibuf = IMB_allocImBuf( + render_pass->rectx, render_pass->recty, get_num_planes_for_pass_ibuf(*render_pass), 0); render_pass->ibuf->channels = render_pass->channels; } -- 2.30.2 From 2592fa1dc7138be8ca7408646e7fc830cad3b45b Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 16 Jan 2024 20:24:12 +0100 Subject: [PATCH 043/244] Render: Set non-color colorspace for ImBuf of data passes This allows code outside of the render pipeline to make proper decisions about how the imbuf of render passes are to be handled. For example, IMB_create_gpu_texture() will now properly select single channel grayscale texture format for depth pass coming from multilayer EXR, additionally solving assert in the GPU compositor code which verifies expected and actual imbuf texture format. Pull Request: https://projects.blender.org/blender/blender/pulls/117184 --- .../blender/blenkernel/intern/image_save.cc | 2 +- source/blender/render/RE_pipeline.h | 3 +++ source/blender/render/intern/render_result.cc | 23 ++++++++++++++++++- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/image_save.cc b/source/blender/blenkernel/intern/image_save.cc index fa07199c356..115e8386629 100644 --- a/source/blender/blenkernel/intern/image_save.cc +++ b/source/blender/blenkernel/intern/image_save.cc @@ -813,7 +813,7 @@ bool BKE_image_render_write_exr(ReportList *reports, /* We only store RGBA passes as half float, for * others precision loss can be problematic. */ - const bool pass_RGBA = STR_ELEM(rp->chan_id, "RGB", "RGBA", "R", "G", "B", "A"); + const bool pass_RGBA = RE_RenderPassIsColor(rp); const bool pass_half_float = half_float && pass_RGBA; /* Color-space conversion only happens on RGBA passes. */ diff --git a/source/blender/render/RE_pipeline.h b/source/blender/render/RE_pipeline.h index b07f8868db5..88af53e3ab2 100644 --- a/source/blender/render/RE_pipeline.h +++ b/source/blender/render/RE_pipeline.h @@ -502,6 +502,9 @@ RenderResult *RE_DuplicateRenderResult(RenderResult *rr); struct ImBuf *RE_RenderPassEnsureImBuf(RenderPass *render_pass); struct ImBuf *RE_RenderViewEnsureImBuf(const RenderResult *render_result, RenderView *render_view); +/* Returns true if the pass is a color (as opposite of data) and needs to be color managed. */ +bool RE_RenderPassIsColor(const RenderPass *render_pass); + #ifdef __cplusplus } #endif diff --git a/source/blender/render/intern/render_result.cc b/source/blender/render/intern/render_result.cc index 0b78213ee02..a14957a3bdd 100644 --- a/source/blender/render/intern/render_result.cc +++ b/source/blender/render/intern/render_result.cc @@ -182,6 +182,16 @@ static int get_num_planes_for_pass_ibuf(const RenderPass &render_pass) return R_IMF_PLANES_RGBA; } +static void assign_render_pass_ibuf_colorspace(RenderPass &render_pass) +{ + if (RE_RenderPassIsColor(&render_pass)) { + return; + } + + const char *data_colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DATA); + IMB_colormanagement_assign_float_colorspace(render_pass.ibuf, data_colorspace); +} + static void render_layer_allocate_pass(RenderResult *rr, RenderPass *rp) { if (rp->ibuf && rp->ibuf->float_buffer.data) { @@ -197,6 +207,7 @@ static void render_layer_allocate_pass(RenderResult *rr, RenderPass *rp) rp->ibuf = IMB_allocImBuf(rr->rectx, rr->recty, get_num_planes_for_pass_ibuf(*rp), 0); rp->ibuf->channels = rp->channels; IMB_assign_float_buffer(rp->ibuf, buffer_data, IB_TAKE_OWNERSHIP); + assign_render_pass_ibuf_colorspace(*rp); if (STREQ(rp->name, RE_PASSNAME_VECTOR)) { /* initialize to max speed */ @@ -695,6 +706,7 @@ RenderResult *render_result_new_from_exr( RenderResult *rr = MEM_cnew(__func__); const char *to_colorspace = IMB_colormanagement_role_colorspace_name_get( COLOR_ROLE_SCENE_LINEAR); + const char *data_colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DATA); rr->rectx = rectx; rr->recty = recty; @@ -711,7 +723,7 @@ RenderResult *render_result_new_from_exr( rpass->rectx = rectx; rpass->recty = recty; - if (rpass->channels >= 3) { + if (RE_RenderPassIsColor(rpass)) { IMB_colormanagement_transform(rpass->ibuf->float_buffer.data, rpass->rectx, rpass->recty, @@ -720,6 +732,9 @@ RenderResult *render_result_new_from_exr( to_colorspace, predivide); } + else { + IMB_colormanagement_assign_float_colorspace(rpass->ibuf, data_colorspace); + } } } @@ -1320,6 +1335,7 @@ ImBuf *RE_RenderPassEnsureImBuf(RenderPass *render_pass) render_pass->ibuf = IMB_allocImBuf( render_pass->rectx, render_pass->recty, get_num_planes_for_pass_ibuf(*render_pass), 0); render_pass->ibuf->channels = render_pass->channels; + assign_render_pass_ibuf_colorspace(*render_pass); } return render_pass->ibuf; @@ -1334,4 +1350,9 @@ ImBuf *RE_RenderViewEnsureImBuf(const RenderResult *render_result, RenderView *r return render_view->ibuf; } +bool RE_RenderPassIsColor(const RenderPass *render_pass) +{ + return STR_ELEM(render_pass->chan_id, "RGB", "RGBA", "R", "G", "B", "A"); +} + /** \} */ -- 2.30.2 From 50ec05a245545104b44fe7a42b8748da306a7282 Mon Sep 17 00:00:00 2001 From: Aras Pranckevicius Date: Wed, 17 Jan 2024 10:26:50 +0100 Subject: [PATCH 044/244] VSE: Speedup Subsampled 3x3 image filter Make Subsampling 3x3 filter twice faster (on 4K UHD resolution, Windows/VS2022/Ryzen5950X: 52.7ms -> 28.3ms), by reformulating how it works: Conceptually Subsampling filter is a box filter: it sums up N source image pixels, computes their average and outputs the result. Critical thing is, that should be done in premultiplied space so that colors from fully or mostly transparent regions do not "override" opaque colors. Previously, when operating on byte images, the code achieved this by always working on byte values, doing "progressively smaller" lerp into byte color result, taking care of premultiplication and again storing the "straight" alpha for each sample being processed. This meant that for each sample, there are 3 divisions involved! This also led to some precision loss, since for all 9 samples all the intermediate results would only be stored at byte precision. Reformulate that by simply accumulating the premultiplied color as a float. This gets rid of all divisions, except the last step when said float needs to be written back into a byte color. The unit test results have a tiny difference, since now it is arguably better (as per above, previously it was having some precision loss). Pull Request: https://projects.blender.org/blender/blender/pulls/117125 --- source/blender/imbuf/intern/transform.cc | 105 ++++++++---------- source/blender/imbuf/intern/transform_test.cc | 6 +- 2 files changed, 50 insertions(+), 61 deletions(-) diff --git a/source/blender/imbuf/intern/transform.cc b/source/blender/imbuf/intern/transform.cc index f531f527df4..3629e23e911 100644 --- a/source/blender/imbuf/intern/transform.cc +++ b/source/blender/imbuf/intern/transform.cc @@ -134,26 +134,6 @@ static float wrap_uv(float value, int size) return x; } -template -static void add_subsample(const T *src, T *dst, int sample_number) -{ - BLI_STATIC_ASSERT((is_same_any_v), "Only uchar and float channels supported."); - - float factor = 1.0 / (sample_number + 1); - if constexpr (std::is_same_v) { - BLI_STATIC_ASSERT(NumChannels == 4, "Pixels using uchar requires to have 4 channels."); - blend_color_interpolate_byte(dst, dst, src, factor); - } - else if constexpr (std::is_same_v && NumChannels == 4) { - blend_color_interpolate_float(dst, dst, src, factor); - } - else if constexpr (std::is_same_v) { - for (int i : IndexRange(NumChannels)) { - dst[i] = dst[i] * (1.0f - factor) + src[i] * factor; - } - } -} - template static void sample_nearest_float(const ImBuf *source, float u, float v, float *r_sample) { @@ -235,39 +215,48 @@ static void sample_image(const ImBuf *source, float u, float v, T *r_sample) } } -template static void store_sample(const T *sample, T *dst) +static void add_subsample(const float src[4], float dst[4]) { - if constexpr (std::is_same_v) { - BLI_STATIC_ASSERT(SrcChannels == 4, "Unsigned chars always have 4 channels."); - copy_v4_v4_uchar(dst, sample); - } - else if constexpr (std::is_same_v && SrcChannels == 4) { - copy_v4_v4(dst, sample); - } - else if constexpr (std::is_same_v && SrcChannels == 3) { - copy_v4_fl4(dst, sample[0], sample[1], sample[2], 1.0f); - } - else if constexpr (std::is_same_v && SrcChannels == 2) { - copy_v4_fl4(dst, sample[0], sample[1], 0.0f, 1.0f); - } - else if constexpr (std::is_same_v && SrcChannels == 1) { - /* Note: single channel sample is stored as grayscale. */ - copy_v4_fl4(dst, sample[0], sample[0], sample[0], 1.0f); - } - else { - BLI_assert_unreachable(); - } + add_v4_v4(dst, src); } -template -static void mix_and_store_sample(const T *sample, T *dst, const float mix_factor) +static void add_subsample(const uchar src[4], float dst[4]) { - if constexpr (std::is_same_v) { - BLI_STATIC_ASSERT(SrcChannels == 4, "Unsigned chars always have 4 channels."); - blend_color_interpolate_byte(dst, dst, sample, mix_factor); + float premul[4]; + straight_uchar_to_premul_float(premul, src); + add_v4_v4(dst, premul); +} + +static void store_premul_float_sample(const float sample[4], float dst[4]) +{ + copy_v4_v4(dst, sample); +} + +static void store_premul_float_sample(const float sample[4], uchar dst[4]) +{ + premul_float_to_straight_uchar(dst, sample); +} + +template static void store_sample(const uchar *sample, uchar *dst) +{ + BLI_STATIC_ASSERT(SrcChannels == 4, "Unsigned chars always have 4 channels."); + copy_v4_v4_uchar(dst, sample); +} + +template static void store_sample(const float *sample, float *dst) +{ + if constexpr (SrcChannels == 4) { + copy_v4_v4(dst, sample); } - else if constexpr (std::is_same_v && SrcChannels == 4) { - blend_color_interpolate_float(dst, dst, sample, mix_factor); + else if constexpr (SrcChannels == 3) { + copy_v4_fl4(dst, sample[0], sample[1], sample[2], 1.0f); + } + else if constexpr (SrcChannels == 2) { + copy_v4_fl4(dst, sample[0], sample[1], 0.0f, 1.0f); + } + else if constexpr (SrcChannels == 1) { + /* Note: single channel sample is stored as grayscale. */ + copy_v4_fl4(dst, sample[0], sample[0], sample[0], 1.0f); } else { BLI_assert_unreachable(); @@ -286,29 +275,29 @@ static void process_scanlines(const TransformContext &ctx, IndexRange y_range) float2 uv_start = ctx.start_uv + ctx.add_x * 0.5f + ctx.add_y * 0.5f; if (ctx.subsampling_deltas.size() > 1) { - /* Multiple samples per pixel. */ + /* Multiple samples per pixel: accumulate them premultiplied, + * divide by sample count and write out (un-premultiplying if writing out + * to byte image). */ + const float inv_count = 1.0f / ctx.subsampling_deltas.size(); for (int yi : y_range) { T *output = init_pixel_pointer(ctx.dst, ctx.dst_region_x_range.first(), yi); float2 uv_row = uv_start + yi * ctx.add_y; for (int xi : ctx.dst_region_x_range) { float2 uv = uv_row + xi * ctx.add_x; - T sample[4] = {}; - int num_subsamples_added = 0; + float sample[4] = {}; for (const float2 &delta_uv : ctx.subsampling_deltas) { const float2 sub_uv = uv + delta_uv; if (!CropSource || !should_discard(ctx, sub_uv)) { T sub_sample[4]; sample_image(ctx.src, sub_uv.x, sub_uv.y, sub_sample); - add_subsample(sub_sample, sample, num_subsamples_added); - num_subsamples_added += 1; + add_subsample(sub_sample, sample); } } - if (num_subsamples_added != 0) { - const float mix_weight = float(num_subsamples_added) / ctx.subsampling_deltas.size(); - mix_and_store_sample(sample, output, mix_weight); - } + mul_v4_v4fl(sample, sample, inv_count); + store_premul_float_sample(sample, output); + output += 4; } } @@ -323,7 +312,7 @@ static void process_scanlines(const TransformContext &ctx, IndexRange y_range) if (!CropSource || !should_discard(ctx, uv)) { T sample[4]; sample_image(ctx.src, uv.x, uv.y, sample); - store_sample(sample, output); + store_sample(sample, output); } output += 4; } diff --git a/source/blender/imbuf/intern/transform_test.cc b/source/blender/imbuf/intern/transform_test.cc index f513cff1285..816926b5d88 100644 --- a/source/blender/imbuf/intern/transform_test.cc +++ b/source/blender/imbuf/intern/transform_test.cc @@ -71,9 +71,9 @@ TEST(imbuf_transform, nearest_subsample3_2x_smaller) { ImBuf *res = transform_2x_smaller(IMB_FILTER_NEAREST, 3); const ColorTheme4b *got = reinterpret_cast(res->byte_buffer.data); - EXPECT_EQ(got[0], ColorTheme4b(226, 168, 113, 255)); - EXPECT_EQ(got[1], ColorTheme4b(133, 55, 31, 16)); - EXPECT_EQ(got[2], ColorTheme4b(55, 22, 64, 254)); + EXPECT_EQ(got[0], ColorTheme4b(227, 170, 113, 255)); + EXPECT_EQ(got[1], ColorTheme4b(133, 55, 31, 17)); + EXPECT_EQ(got[2], ColorTheme4b(56, 22, 64, 253)); IMB_freeImBuf(res); } -- 2.30.2 From c98323fc145e9484d21ce763b46d348dcdaf6ad3 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 17 Jan 2024 10:33:39 +0100 Subject: [PATCH 045/244] Geometry Nodes: expose bake id in Python API This is necessary to be able to invoke baking of individual bake nodes or simulation zones with Python. --- source/blender/makesrna/intern/rna_modifier.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/source/blender/makesrna/intern/rna_modifier.cc b/source/blender/makesrna/intern/rna_modifier.cc index 3b6306e3852..a60a9ec56c4 100644 --- a/source/blender/makesrna/intern/rna_modifier.cc +++ b/source/blender/makesrna/intern/rna_modifier.cc @@ -7197,6 +7197,14 @@ static void rna_def_modifier_nodes_bake(BlenderRNA *brna) RNA_def_property_enum_items(prop, bake_mode_items); RNA_def_property_ui_text(prop, "Bake Mode", ""); RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "bake_id", PROP_INT, PROP_NONE); + RNA_def_property_ui_text(prop, + "Bake ID", + "Identifier for this bake which remains unchanged even when the bake " + "node is renamed, grouped or ungrouped"); + RNA_def_property_int_sdna(prop, nullptr, "id"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); } static void rna_def_modifier_nodes_bakes(BlenderRNA *brna) -- 2.30.2 From a2915e83e51a088901cb2cadd404b5579332fbda Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 17 Jan 2024 11:48:06 +0100 Subject: [PATCH 046/244] Geometry Nodes: expose NodesModifierBake.node property in Python API This allows scripts to filter the set of bakes by e.g. the node name. More filtering options will be necessary, but this is a good start. --- .../blender/blenkernel/intern/node_runtime.cc | 8 +++- source/blender/makesdna/DNA_node_types.h | 2 +- .../blender/makesrna/intern/rna_modifier.cc | 43 +++++++++++++++++++ 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/intern/node_runtime.cc b/source/blender/blenkernel/intern/node_runtime.cc index b7c474f07d2..74054663010 100644 --- a/source/blender/blenkernel/intern/node_runtime.cc +++ b/source/blender/blenkernel/intern/node_runtime.cc @@ -608,7 +608,8 @@ bool bNodeTree::node_id_path_from_nested_node_ref(const int32_t nested_node_id, return group->node_id_path_from_nested_node_ref(ref->path.id_in_node, r_node_ids); } -const bNode *bNodeTree::find_nested_node(const int32_t nested_node_id) const +const bNode *bNodeTree::find_nested_node(const int32_t nested_node_id, + const bNodeTree **r_tree) const { const bNestedNodeRef *ref = this->find_nested_node_ref(nested_node_id); if (ref == nullptr) { @@ -620,11 +621,14 @@ const bNode *bNodeTree::find_nested_node(const int32_t nested_node_id) const return nullptr; } if (!node->is_group()) { + if (r_tree) { + *r_tree = this; + } return node; } const bNodeTree *group = reinterpret_cast(node->id); if (group == nullptr) { return nullptr; } - return group->find_nested_node(ref->path.id_in_node); + return group->find_nested_node(ref->path.id_in_node, r_tree); } diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index f14b5d9f7c3..2d1cb8d02d0 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -728,7 +728,7 @@ typedef struct bNodeTree { const bNestedNodeRef *nested_node_ref_from_node_id_path(blender::Span node_ids) const; [[nodiscard]] bool node_id_path_from_nested_node_ref(const int32_t nested_node_id, blender::Vector &r_node_ids) const; - const bNode *find_nested_node(int32_t nested_node_id) const; + const bNode *find_nested_node(int32_t nested_node_id, const bNodeTree **r_tree = nullptr) const; /** * Update a run-time cache for the node tree based on its current state. This makes many methods diff --git a/source/blender/makesrna/intern/rna_modifier.cc b/source/blender/makesrna/intern/rna_modifier.cc index a60a9ec56c4..17216f78c6a 100644 --- a/source/blender/makesrna/intern/rna_modifier.cc +++ b/source/blender/makesrna/intern/rna_modifier.cc @@ -1713,6 +1713,39 @@ static IDProperty **rna_NodesModifier_properties(PointerRNA *ptr) return &settings->properties; } +static const NodesModifierData *find_nodes_modifier_by_bake(const Object &object, + const NodesModifierBake &bake) +{ + LISTBASE_FOREACH (const ModifierData *, md, &object.modifiers) { + if (md->type != eModifierType_Nodes) { + continue; + } + const NodesModifierData *nmd = reinterpret_cast(md); + const blender::Span bakes{nmd->bakes, nmd->bakes_num}; + if (bakes.contains_ptr(&bake)) { + return nmd; + } + } + return nullptr; +} + +static PointerRNA rna_NodesModifierBake_node_get(PointerRNA *ptr) +{ + const Object *ob = reinterpret_cast(ptr->owner_id); + const NodesModifierBake *bake = static_cast(ptr->data); + const NodesModifierData *nmd = find_nodes_modifier_by_bake(*ob, *bake); + if (!nmd->node_group) { + return PointerRNA_NULL; + } + const bNodeTree *tree; + const bNode *node = nmd->node_group->find_nested_node(bake->id, &tree); + if (!node) { + return PointerRNA_NULL; + } + BLI_assert(tree != nullptr); + return RNA_pointer_create(const_cast(&tree->id), &RNA_Node, const_cast(node)); +} + bool rna_GreasePencilModifier_material_poll(PointerRNA *ptr, PointerRNA value) { Object *ob = reinterpret_cast(ptr->owner_id); @@ -7205,6 +7238,16 @@ static void rna_def_modifier_nodes_bake(BlenderRNA *brna) "node is renamed, grouped or ungrouped"); RNA_def_property_int_sdna(prop, nullptr, "id"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); + + prop = RNA_def_property(srna, "node", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "Node"); + RNA_def_property_ui_text(prop, + "Node", + "Bake node or simulation output node that corresponds to this bake. " + "This node may be deeply nested in the modifier node group. It can be " + "none in some cases like missing linked data blocks"); + RNA_def_property_pointer_funcs( + prop, "rna_NodesModifierBake_node_get", nullptr, nullptr, nullptr); } static void rna_def_modifier_nodes_bakes(BlenderRNA *brna) -- 2.30.2 From a18b0405f0cdc53470e0b21f7ef8245ca52e4aed Mon Sep 17 00:00:00 2001 From: Pratik Borhade Date: Wed, 17 Jan 2024 12:20:28 +0100 Subject: [PATCH 047/244] Curves: Debug crash after switching to sculpt mode `bounds_min_max` returns an optional and it can be `nullopt` when curve points are 0. Pull Request: https://projects.blender.org/blender/blender/pulls/117163 --- source/blender/editors/sculpt_paint/paint_image.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/sculpt_paint/paint_image.cc b/source/blender/editors/sculpt_paint/paint_image.cc index b73b58dd75f..3b2cb4f5c9c 100644 --- a/source/blender/editors/sculpt_paint/paint_image.cc +++ b/source/blender/editors/sculpt_paint/paint_image.cc @@ -818,8 +818,11 @@ static blender::float3 paint_init_pivot_mesh(Object *ob) static blender::float3 paint_init_pivot_curves(Object *ob) { const Curves &curves = *static_cast(ob->data); - const blender::Bounds bounds = *curves.geometry.wrap().bounds_min_max(); - return blender::math::midpoint(bounds.min, bounds.max); + const std::optional> bounds = curves.geometry.wrap().bounds_min_max(); + if (bounds.has_value()) { + return blender::math::midpoint(bounds->min, bounds->max); + } + return blender::float3(0); } static blender::float3 paint_init_pivot_grease_pencil(Object *ob, const int frame) -- 2.30.2 From f5685e15704da4dd950e68b9ba8e8789d0765c77 Mon Sep 17 00:00:00 2001 From: Pratik Borhade Date: Wed, 17 Jan 2024 12:28:14 +0100 Subject: [PATCH 048/244] Fix #117191: Crash using the Extrude tool and Snap Base `GIZMO_GT_button_2d` has no modal/custom_modal callback function defined hence the crash due to calling `modal_fn()` Pull Request: https://projects.blender.org/blender/blender/pulls/117194 --- source/blender/editors/transform/transform_mode_snapsource.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/transform/transform_mode_snapsource.cc b/source/blender/editors/transform/transform_mode_snapsource.cc index 6e6cffa8231..f3385361048 100644 --- a/source/blender/editors/transform/transform_mode_snapsource.cc +++ b/source/blender/editors/transform/transform_mode_snapsource.cc @@ -237,7 +237,9 @@ void transform_mode_snap_source_init(TransInfo *t, wmOperator * /*op*/) const wmEvent *event = CTX_wm_window(t->context)->eventstate; # ifdef RESET_TRANSFORMATION wmGizmoFnModal modal_fn = gz->custom_modal ? gz->custom_modal : gz->type->modal; - modal_fn(t->context, gz, event, eWM_GizmoFlagTweak(0)); + if (modal_fn) { + modal_fn(t->context, gz, event, eWM_GizmoFlagTweak(0)); + } # endif WM_gizmo_modal_set_while_modal(t->region->gizmo_map, t->context, nullptr, event); -- 2.30.2 From 741c13d1e9d055bf17eae9cac3d5a2e46e443db8 Mon Sep 17 00:00:00 2001 From: Pratik Borhade Date: Wed, 17 Jan 2024 12:31:52 +0100 Subject: [PATCH 049/244] Cleanup: Curves: Remove redundant resizing in extrude operator Curve resizing is already handled in `copy_only_curve_domain` (i.e. copy curve nums/attributes, point_num = 0, fresh offset array) Pull Request: https://projects.blender.org/blender/blender/pulls/117166 --- source/blender/editors/curves/intern/curves_extrude.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/source/blender/editors/curves/intern/curves_extrude.cc b/source/blender/editors/curves/intern/curves_extrude.cc index a37fe657b3f..1ad9896b2e3 100644 --- a/source/blender/editors/curves/intern/curves_extrude.cc +++ b/source/blender/editors/curves/intern/curves_extrude.cc @@ -248,7 +248,6 @@ static void extrude_curves(Curves &curves_id) const int curves_num = curves.curves_num(); const int curve_intervals_size = extruded_points.size() * 2 + curves_num * 2; - new_curves.resize(0, curves_num); MutableSpan new_offsets = new_curves.offsets_for_write(); /* Buffer for intervals of all curves. Beginning and end of a curve can be determined only by -- 2.30.2 From d8fe96d6ce472a50ee37843f7cb7bd84a001383b Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 17 Jan 2024 12:45:06 +0100 Subject: [PATCH 050/244] Fix: crash when adding repeat zone from search The issue was that some code expected the "extend" sockets to always exist. This was already always true from the user point of view, but not internally. There are different possible fixes, but this patch makes sure that the extend socket will be available as expected. This is achieved by making them part of the static node declaration. We want to extend the static declaration of such dynamic nodes anyway, to improve reflection capabilities without having to instantiate nodes. --- .../geometry/nodes/node_geo_repeat_input.cc | 39 +++++++++---------- .../geometry/nodes/node_geo_repeat_output.cc | 27 +++++++------ 2 files changed, 31 insertions(+), 35 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_repeat_input.cc b/source/blender/nodes/geometry/nodes/node_geo_repeat_input.cc index 8a207e6d975..738164c7106 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_repeat_input.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_repeat_input.cc @@ -26,27 +26,24 @@ static void node_declare(NodeDeclarationBuilder &b) const bNode *node = b.node_or_null(); const bNodeTree *tree = b.tree_or_null(); - if (ELEM(nullptr, node, tree)) { - return; - } - - const NodeGeometryRepeatInput &storage = node_storage(*node); - const bNode *output_node = tree->node_by_id(storage.output_node_id); - if (output_node == nullptr) { - return; - } - const auto &output_storage = *static_cast( - output_node->storage); - for (const int i : IndexRange(output_storage.items_num)) { - const NodeRepeatItem &item = output_storage.items[i]; - const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type); - const StringRef name = item.name ? item.name : ""; - const std::string identifier = RepeatItemsAccessor::socket_identifier_for_item(item); - auto &input_decl = b.add_input(socket_type, name, identifier); - auto &output_decl = b.add_output(socket_type, name, identifier); - if (socket_type_supports_fields(socket_type)) { - input_decl.supports_field(); - output_decl.dependent_field({input_decl.input_index()}); + if (node && tree) { + const NodeGeometryRepeatInput &storage = node_storage(*node); + const bNode *output_node = tree->node_by_id(storage.output_node_id); + if (output_node) { + const auto &output_storage = *static_cast( + output_node->storage); + for (const int i : IndexRange(output_storage.items_num)) { + const NodeRepeatItem &item = output_storage.items[i]; + const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type); + const StringRef name = item.name ? item.name : ""; + const std::string identifier = RepeatItemsAccessor::socket_identifier_for_item(item); + auto &input_decl = b.add_input(socket_type, name, identifier); + auto &output_decl = b.add_output(socket_type, name, identifier); + if (socket_type_supports_fields(socket_type)) { + input_decl.supports_field(); + output_decl.dependent_field({input_decl.input_index()}); + } + } } } b.add_input("", "__extend__"); diff --git a/source/blender/nodes/geometry/nodes/node_geo_repeat_output.cc b/source/blender/nodes/geometry/nodes/node_geo_repeat_output.cc index 9cfad3557c9..069e71bcbc7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_repeat_output.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_repeat_output.cc @@ -27,20 +27,19 @@ NODE_STORAGE_FUNCS(NodeGeometryRepeatOutput); static void node_declare(NodeDeclarationBuilder &b) { const bNode *node = b.node_or_null(); - if (node == nullptr) { - return; - } - const NodeGeometryRepeatOutput &storage = node_storage(*node); - for (const int i : IndexRange(storage.items_num)) { - const NodeRepeatItem &item = storage.items[i]; - const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type); - const StringRef name = item.name ? item.name : ""; - const std::string identifier = RepeatItemsAccessor::socket_identifier_for_item(item); - auto &input_decl = b.add_input(socket_type, name, identifier); - auto &output_decl = b.add_output(socket_type, name, identifier); - if (socket_type_supports_fields(socket_type)) { - input_decl.supports_field(); - output_decl.dependent_field({input_decl.input_index()}); + if (node) { + const NodeGeometryRepeatOutput &storage = node_storage(*node); + for (const int i : IndexRange(storage.items_num)) { + const NodeRepeatItem &item = storage.items[i]; + const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type); + const StringRef name = item.name ? item.name : ""; + const std::string identifier = RepeatItemsAccessor::socket_identifier_for_item(item); + auto &input_decl = b.add_input(socket_type, name, identifier); + auto &output_decl = b.add_output(socket_type, name, identifier); + if (socket_type_supports_fields(socket_type)) { + input_decl.supports_field(); + output_decl.dependent_field({input_decl.input_index()}); + } } } b.add_input("", "__extend__"); -- 2.30.2 From 6454d989fdb845bbc70c3e916432f3730d4db459 Mon Sep 17 00:00:00 2001 From: Omar Emara Date: Wed, 17 Jan 2024 13:19:10 +0100 Subject: [PATCH 051/244] Compositor: Port redesigned Defocus node to CPU This patch ports the redesigned GPU Defocus node to the CPU. Pull Request: https://projects.blender.org/blender/blender/pulls/117174 --- .../compositor/nodes/COM_DefocusNode.cc | 63 ++--- .../compositor/nodes/COM_DefocusNode.h | 3 + .../COM_ConvertDepthToRadiusOperation.cc | 253 ++++++++++++------ .../COM_ConvertDepthToRadiusOperation.h | 77 +++--- 4 files changed, 240 insertions(+), 156 deletions(-) diff --git a/source/blender/compositor/nodes/COM_DefocusNode.cc b/source/blender/compositor/nodes/COM_DefocusNode.cc index 1c1ec174bf9..5330766f71b 100644 --- a/source/blender/compositor/nodes/COM_DefocusNode.cc +++ b/source/blender/compositor/nodes/COM_DefocusNode.cc @@ -2,9 +2,14 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ -#include "COM_DefocusNode.h" +#include "DNA_scene_types.h" + +#include "BKE_camera.h" + #include "COM_BokehImageOperation.h" #include "COM_ConvertDepthToRadiusOperation.h" +#include "COM_DefocusNode.h" +#include "COM_FastGaussianBlurOperation.h" #include "COM_GammaCorrectOperation.h" #include "COM_MathBaseOperation.h" #include "COM_SetValueOperation.h" @@ -22,8 +27,6 @@ void DefocusNode::convert_to_operations(NodeConverter &converter, { const bNode *node = this->get_bnode(); const NodeDefocus *data = (const NodeDefocus *)node->storage; - Scene *scene = node->id ? (Scene *)node->id : context.get_scene(); - Object *camob = scene ? scene->camera : nullptr; NodeOperation *radius_operation; if (data->no_zbuf) { @@ -48,22 +51,31 @@ void DefocusNode::convert_to_operations(NodeConverter &converter, } else { ConvertDepthToRadiusOperation *radius_op = new ConvertDepthToRadiusOperation(); - radius_op->set_camera_object(camob); - radius_op->setf_stop(data->fstop); - radius_op->set_max_radius(data->maxblur); + radius_op->set_data(data); + radius_op->set_scene(get_scene(context)); converter.add_operation(radius_op); - converter.map_input_socket(get_input_socket(1), radius_op->get_input_socket(0)); + converter.map_input_socket(get_input_socket(0), radius_op->get_input_socket(1)); - FastGaussianBlurValueOperation *blur = new FastGaussianBlurValueOperation(); - /* maintain close pixels so far Z values don't bleed into the foreground */ - blur->set_overlay(FAST_GAUSS_OVERLAY_MIN); - converter.add_operation(blur); + GaussianXBlurOperation *blur_x_operation = new GaussianXBlurOperation(); + converter.add_operation(blur_x_operation); + converter.add_link(radius_op->get_output_socket(), blur_x_operation->get_input_socket(0)); - converter.add_link(radius_op->get_output_socket(0), blur->get_input_socket(0)); - radius_op->set_post_blur(blur); + GaussianYBlurOperation *blur_y_operation = new GaussianYBlurOperation(); + converter.add_operation(blur_y_operation); + converter.add_link(blur_x_operation->get_output_socket(), + blur_y_operation->get_input_socket(0)); - radius_operation = blur; + MathMinimumOperation *minimum_operation = new MathMinimumOperation(); + converter.add_operation(minimum_operation); + converter.add_link(blur_y_operation->get_output_socket(), + minimum_operation->get_input_socket(0)); + converter.add_link(radius_op->get_output_socket(), minimum_operation->get_input_socket(1)); + + radius_op->set_blur_x_operation(blur_x_operation); + radius_op->set_blur_y_operation(blur_y_operation); + + radius_operation = minimum_operation; } NodeBokehImage *bokehdata = new NodeBokehImage(); @@ -82,30 +94,14 @@ void DefocusNode::convert_to_operations(NodeConverter &converter, bokeh->delete_data_on_finish(); converter.add_operation(bokeh); -#ifdef COM_DEFOCUS_SEARCH - InverseSearchRadiusOperation *search = new InverseSearchRadiusOperation(); - search->set_max_blur(data->maxblur); - converter.add_operation(search); - - converter.add_link(radius_operation->get_output_socket(0), search->get_input_socket(0)); -#endif - VariableSizeBokehBlurOperation *operation = new VariableSizeBokehBlurOperation(); - if (data->preview) { - operation->set_quality(eCompositorQuality::Low); - } - else { - operation->set_quality(context.get_quality()); - } + operation->set_quality(eCompositorQuality::High); operation->set_max_blur(data->maxblur); operation->set_threshold(data->bthresh); converter.add_operation(operation); converter.add_link(bokeh->get_output_socket(), operation->get_input_socket(1)); converter.add_link(radius_operation->get_output_socket(), operation->get_input_socket(2)); -#ifdef COM_DEFOCUS_SEARCH - converter.add_link(search->get_output_socket(), operation->get_input_socket(3)); -#endif if (data->gamco) { GammaCorrectOperation *correct = new GammaCorrectOperation(); @@ -124,4 +120,9 @@ void DefocusNode::convert_to_operations(NodeConverter &converter, } } +const Scene *DefocusNode::get_scene(const CompositorContext &context) const +{ + return get_bnode()->id ? reinterpret_cast(get_bnode()->id) : context.get_scene(); +} + } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_DefocusNode.h b/source/blender/compositor/nodes/COM_DefocusNode.h index 658c23b3dc0..98dfe47ed4e 100644 --- a/source/blender/compositor/nodes/COM_DefocusNode.h +++ b/source/blender/compositor/nodes/COM_DefocusNode.h @@ -4,6 +4,8 @@ #pragma once +#include "DNA_scene_types.h" + #include "COM_Node.h" namespace blender::compositor { @@ -17,6 +19,7 @@ class DefocusNode : public Node { DefocusNode(bNode *editor_node); void convert_to_operations(NodeConverter &converter, const CompositorContext &context) const override; + const Scene *get_scene(const CompositorContext &context) const; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc index d31b69375a3..7e8daa0a342 100644 --- a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc +++ b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc @@ -2,130 +2,213 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ -#include "COM_ConvertDepthToRadiusOperation.h" -#include "BKE_camera.h" +#include "BLI_math_base.hh" + #include "DNA_camera_types.h" +#include "DNA_node_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_camera.h" + +#include "COM_ConvertDepthToRadiusOperation.h" namespace blender::compositor { ConvertDepthToRadiusOperation::ConvertDepthToRadiusOperation() { this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Color); this->add_output_socket(DataType::Value); - input_operation_ = nullptr; - f_stop_ = 128.0f; - camera_object_ = nullptr; - max_radius_ = 32.0f; - blur_post_operation_ = nullptr; - flags_.can_be_constant = true; } -float ConvertDepthToRadiusOperation::determine_focal_distance() -{ - if (camera_object_ && camera_object_->type == OB_CAMERA) { - Camera *camera = (Camera *)camera_object_->data; - cam_lens_ = camera->lens; - return BKE_camera_object_dof_distance(camera_object_); - } - - return 10.0f; -} - void ConvertDepthToRadiusOperation::init_execution() { - float cam_sensor = DEFAULT_SENSOR_WIDTH; - Camera *camera = nullptr; + depth_input_operation_ = this->get_input_socket_reader(0); + image_input_operation_ = this->get_input_socket_reader(1); - if (camera_object_ && camera_object_->type == OB_CAMERA) { - camera = (Camera *)camera_object_->data; - cam_sensor = BKE_camera_sensor_size(camera->sensor_fit, camera->sensor_x, camera->sensor_y); - } + f_stop = get_f_stop(); + focal_length = get_focal_length(); + max_radius = data_->maxblur; + pixels_per_meter = compute_pixels_per_meter(); + distance_to_image_of_focus = compute_distance_to_image_of_focus(); - input_operation_ = this->get_input_socket_reader(0); - float focal_distance = determine_focal_distance(); - if (focal_distance == 0.0f) { - focal_distance = 1e10f; /* If the DOF is 0.0 then set it to be far away. */ - } - inverse_focal_distance_ = 1.0f / focal_distance; - aspect_ = (this->get_width() > this->get_height()) ? - (this->get_height() / float(this->get_width())) : - (this->get_width() / float(this->get_height())); - aperture_ = 0.5f * (cam_lens_ / (aspect_ * cam_sensor)) / f_stop_; - const float minsz = MIN2(get_width(), get_height()); - /* Equal to: `aspect * MIN2(img->x, img->y) / tan(0.5f * fov)`. */ - dof_sp_ = minsz / ((cam_sensor / 2.0f) / cam_lens_); + NodeBlurData blur_data; + blur_data.sizex = compute_maximum_defocus_radius(); + blur_data.sizey = blur_data.sizex; + blur_data.relative = false; + blur_data.filtertype = R_FILTER_GAUSS; - if (blur_post_operation_) { - blur_post_operation_->set_sigma(std::min(aperture_ * 128.0f, max_radius_)); - } + blur_x_operation_->set_data(&blur_data); + blur_x_operation_->set_size(1.0f); + blur_y_operation_->set_data(&blur_data); + blur_y_operation_->set_size(1.0f); } +/* Given a depth texture, compute the radius of the circle of confusion in pixels based on equation + * (8) of the paper: + * + * Potmesil, Michael, and Indranil Chakravarty. "A lens and aperture camera model for synthetic + * image generation." ACM SIGGRAPH Computer Graphics 15.3 (1981): 297-305. */ void ConvertDepthToRadiusOperation::execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) { float input_value[4]; - float z; - float radius; - input_operation_->read_sampled(input_value, x, y, sampler); - z = input_value[0]; - if (z != 0.0f) { - float iZ = (1.0f / z); + depth_input_operation_->read_sampled(input_value, x, y, sampler); + const float depth = input_value[0]; - /* bug #6656 part 2b, do not re-scale. */ -#if 0 - bcrad = 0.5f * fabs(aperture * (dof_sp * (cam_invfdist - iZ) - 1.0f)); - /* Scale crad back to original maximum and blend. */ - crad->rect[px] = bcrad + wts->rect[px] * (scf * crad->rect[px] - bcrad); -#endif - radius = 0.5f * fabsf(aperture_ * (dof_sp_ * (inverse_focal_distance_ - iZ) - 1.0f)); - /* 'bug' #6615, limit minimum radius to 1 pixel, - * not really a solution, but somewhat mitigates the problem. */ - if (radius < 0.0f) { - radius = 0.0f; - } - if (radius > max_radius_) { - radius = max_radius_; - } - output[0] = radius; - } - else { - output[0] = 0.0f; - } + /* Compute `Vu` in equation (7). */ + const float distance_to_image_of_object = (focal_length * depth) / (depth - focal_length); + + /* Compute C in equation (8). Notice that the last multiplier was included in the absolute since + * it is negative when the object distance is less than the focal length, as noted in equation + * (7). */ + float diameter = abs((distance_to_image_of_object - distance_to_image_of_focus) * + (focal_length / (f_stop * distance_to_image_of_object))); + + /* The diameter is in meters, so multiply by the pixels per meter. */ + float radius = (diameter / 2.0f) * pixels_per_meter; + + output[0] = math::min(max_radius, radius); } void ConvertDepthToRadiusOperation::deinit_execution() { - input_operation_ = nullptr; + depth_input_operation_ = nullptr; } +/* Given a depth texture, compute the radius of the circle of confusion in pixels based on equation + * (8) of the paper: + * + * Potmesil, Michael, and Indranil Chakravarty. "A lens and aperture camera model for synthetic + * image generation." ACM SIGGRAPH Computer Graphics 15.3 (1981): 297-305. */ void ConvertDepthToRadiusOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { - const float z = *it.in(0); - if (z == 0.0f) { - *it.out = 0.0f; - continue; - } + const float depth = *it.in(0); - const float inv_z = (1.0f / z); + /* Compute `Vu` in equation (7). */ + const float distance_to_image_of_object = (focal_length * depth) / (depth - focal_length); - /* Bug #6656 part 2b, do not re-scale. */ -#if 0 - bcrad = 0.5f * fabs(aperture * (dof_sp * (cam_invfdist - iZ) - 1.0f)); - /* Scale crad back to original maximum and blend: - * `crad->rect[px] = bcrad + wts->rect[px] * (scf * crad->rect[px] - bcrad);` */ -#endif - const float radius = 0.5f * - fabsf(aperture_ * (dof_sp_ * (inverse_focal_distance_ - inv_z) - 1.0f)); - /* Bug #6615, limit minimum radius to 1 pixel, - * not really a solution, but somewhat mitigates the problem. */ - *it.out = CLAMPIS(radius, 0.0f, max_radius_); + /* Compute C in equation (8). Notice that the last multiplier was included in the absolute + * since it is negative when the object distance is less than the focal length, as noted in + * equation (7). */ + float diameter = abs((distance_to_image_of_object - distance_to_image_of_focus) * + (focal_length / (f_stop * distance_to_image_of_object))); + + /* The diameter is in meters, so multiply by the pixels per meter. */ + float radius = (diameter / 2.0f) * pixels_per_meter; + + *it.out = math::min(max_radius, radius); } } +/* Computes the maximum possible defocus radius in pixels. */ +float ConvertDepthToRadiusOperation::compute_maximum_defocus_radius() const +{ + const float maximum_diameter = compute_maximum_diameter_of_circle_of_confusion(); + const float pixels_per_meter = compute_pixels_per_meter(); + const float radius = (maximum_diameter / 2.0f) * pixels_per_meter; + return math::min(radius, data_->maxblur); +} + +/* Computes the diameter of the circle of confusion at infinity. This computes the limit in + * figure (5) of the paper: + * + * Potmesil, Michael, and Indranil Chakravarty. "A lens and aperture camera model for synthetic + * image generation." ACM SIGGRAPH Computer Graphics 15.3 (1981): 297-305. + * + * Notice that the diameter is asymmetric around the focus point, and we are computing the + * limiting diameter at infinity, while another limiting diameter exist at zero distance from the + * lens. This is a limitation of the implementation, as it assumes far defocusing only. */ +float ConvertDepthToRadiusOperation::compute_maximum_diameter_of_circle_of_confusion() const +{ + const float f_stop = get_f_stop(); + const float focal_length = get_focal_length(); + const float distance_to_image_of_focus = compute_distance_to_image_of_focus(); + return math::abs((distance_to_image_of_focus / (f_stop * focal_length)) - + (focal_length / f_stop)); +} + +/* Computes the distance in meters to the image of the focus point across a lens of the specified + * focal length. This computes `Vp` in equation (7) of the paper: + * + * Potmesil, Michael, and Indranil Chakravarty. "A lens and aperture camera model for synthetic + * image generation." ACM SIGGRAPH Computer Graphics 15.3 (1981): 297-305. */ +float ConvertDepthToRadiusOperation::compute_distance_to_image_of_focus() const +{ + const float focal_length = get_focal_length(); + const float focus_distance = compute_focus_distance(); + return (focal_length * focus_distance) / (focus_distance - focal_length); +} + +/* Returns the focal length in meters. Fallback to 50 mm in case of an invalid camera. Ensure a + * minimum of 1e-6. */ +float ConvertDepthToRadiusOperation::get_focal_length() const +{ + const Camera *camera = get_camera(); + return camera ? math::max(1e-6f, camera->lens / 1000.0f) : 50.0f / 1000.0f; +} + +/* Computes the distance to the point that is completely in focus. */ +float ConvertDepthToRadiusOperation::compute_focus_distance() const +{ + return BKE_camera_object_dof_distance(get_camera_object()); +} + +/* Computes the number of pixels per meter of the sensor size. This is essentially the resolution + * over the sensor size, using the sensor fit axis. Fallback to DEFAULT_SENSOR_WIDTH in case of + * an invalid camera. Note that the stored sensor size is in millimeter, so convert to meters. */ +float ConvertDepthToRadiusOperation::compute_pixels_per_meter() const +{ + const int2 size = int2(image_input_operation_->get_width(), + image_input_operation_->get_height()); + const Camera *camera = get_camera(); + const float default_value = size.x / (DEFAULT_SENSOR_WIDTH / 1000.0f); + if (!camera) { + return default_value; + } + + switch (camera->sensor_fit) { + case CAMERA_SENSOR_FIT_HOR: + return size.x / (camera->sensor_x / 1000.0f); + case CAMERA_SENSOR_FIT_VERT: + return size.y / (camera->sensor_y / 1000.0f); + case CAMERA_SENSOR_FIT_AUTO: { + return size.x > size.y ? size.x / (camera->sensor_x / 1000.0f) : + size.y / (camera->sensor_y / 1000.0f); + } + default: + break; + } + + return default_value; +} + +/* Returns the f-stop number. Fallback to 1e-3 for zero f-stop. */ +float ConvertDepthToRadiusOperation::get_f_stop() const +{ + return math::max(1e-3f, data_->fstop); +} + +const Camera *ConvertDepthToRadiusOperation::get_camera() const +{ + const Object *camera_object = get_camera_object(); + if (!camera_object || camera_object->type != OB_CAMERA) { + return nullptr; + } + + return reinterpret_cast(camera_object->data); +} + +const Object *ConvertDepthToRadiusOperation::get_camera_object() const +{ + return scene_->camera; +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h index b789be34980..32eacf5a617 100644 --- a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h +++ b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h @@ -4,75 +4,72 @@ #pragma once -#include "COM_FastGaussianBlurOperation.h" +#include "COM_GaussianXBlurOperation.h" +#include "COM_GaussianYBlurOperation.h" #include "COM_MultiThreadedOperation.h" -#include "DNA_object_types.h" namespace blender::compositor { -/** - * this program converts an input color to an output value. - * it assumes we are in sRGB color space. - */ class ConvertDepthToRadiusOperation : public MultiThreadedOperation { private: - /** - * Cached reference to the input_program - */ - SocketReader *input_operation_; - float f_stop_; - float aspect_; - float max_radius_; - float inverse_focal_distance_; - float aperture_; - float cam_lens_; - float dof_sp_; - Object *camera_object_; + SocketReader *depth_input_operation_; + SocketReader *image_input_operation_; - FastGaussianBlurValueOperation *blur_post_operation_; + const Scene *scene_; + const NodeDefocus *data_; + + float f_stop; + float max_radius; + float focal_length; + float pixels_per_meter; + float distance_to_image_of_focus; + + GaussianXBlurOperation *blur_x_operation_; + GaussianYBlurOperation *blur_y_operation_; public: - /** - * Default constructor - */ ConvertDepthToRadiusOperation(); - /** - * The inner loop of this operation. - */ void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - /** - * Initialize the execution - */ void init_execution() override; - /** - * Deinitialize the execution - */ void deinit_execution() override; - void setf_stop(float f_stop) + void set_data(const NodeDefocus *data) { - f_stop_ = f_stop; + data_ = data; } - void set_max_radius(float max_radius) + + void set_scene(const Scene *scene) { - max_radius_ = max_radius; + scene_ = scene; } - void set_camera_object(Object *camera) + + void set_blur_x_operation(GaussianXBlurOperation *blur_x_operation) { - camera_object_ = camera; + blur_x_operation_ = blur_x_operation; } - float determine_focal_distance(); - void set_post_blur(FastGaussianBlurValueOperation *operation) + + void set_blur_y_operation(GaussianYBlurOperation *blur_y_operation) { - blur_post_operation_ = operation; + blur_y_operation_ = blur_y_operation; } void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) override; + + private: + float compute_maximum_defocus_radius() const; + float compute_maximum_diameter_of_circle_of_confusion() const; + float compute_distance_to_image_of_focus() const; + float get_focal_length() const; + float compute_focus_distance() const; + float compute_pixels_per_meter() const; + float get_f_stop() const; + const Camera *get_camera() const; + const Object *get_camera_object() const; }; } // namespace blender::compositor -- 2.30.2 From e296f9fcaac8618e1ccfd40fe1b7b7bc07368f3f Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 17 Jan 2024 13:32:04 +0100 Subject: [PATCH 052/244] Geometry Nodes: support synchronous baking Previously, calling the bake operator was always creating a job that will be run in parallel to Blender. This is good when baking from the UI, but may be undesired when baking with a script. Now, when the `exec` method of the bake operator is called, the baking happens synchronously. That means that the operator call will return only after the baking is done. The `invoke` method has the same behavior as before. This approach is similar to e.g. `FLUID_OT_bake_all`. I did notice that calling the invoke method from a script can still result in a crash sometimes, probably due to conflicting depsgraph evaluations. That has to be investigated more. --- .../editors/object/object_bake_simulation.cc | 84 +++++++++++++++---- 1 file changed, 66 insertions(+), 18 deletions(-) diff --git a/source/blender/editors/object/object_bake_simulation.cc b/source/blender/editors/object/object_bake_simulation.cc index ab36ce9104a..6164a55b8d3 100644 --- a/source/blender/editors/object/object_bake_simulation.cc +++ b/source/blender/editors/object/object_bake_simulation.cc @@ -369,6 +369,7 @@ static void bake_geometry_nodes_endjob(void *customdata) WM_set_locked_interface(job.wm, false); G.is_rendering = false; WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, nullptr); + WM_main_add_notifier(NC_NODE | ND_DISPLAY, nullptr); } static void reset_old_bake(NodeBakeRequest &request) @@ -393,7 +394,21 @@ static void reset_old_bake(NodeBakeRequest &request) } } -static int start_bake_job(bContext *C, Vector requests, wmOperator *op) +enum class BakeRequestsMode { + /** + * Bake all requests before returning from the function. + */ + Sync, + /** + * Start a parallel job and return before the baking is done. + */ + Async +}; + +static int start_bake_job(bContext *C, + Vector requests, + wmOperator *op, + const BakeRequestsMode mode) { for (NodeBakeRequest &request : requests) { reset_old_bake(request); @@ -406,6 +421,14 @@ static int start_bake_job(bContext *C, Vector requests, wmOpera job->scene = CTX_data_scene(C); job->bake_requests = std::move(requests); + if (mode == BakeRequestsMode::Sync) { + wmJobWorkerStatus worker_status{}; + bake_geometry_nodes_startjob(job, &worker_status); + bake_geometry_nodes_endjob(job); + MEM_delete(job); + return OPERATOR_FINISHED; + } + wmJob *wm_job = WM_jobs_get(job->wm, CTX_wm_window(C), job->scene, @@ -479,7 +502,7 @@ static Vector collect_simulations_to_bake(Main &bmain, return requests; } -static int bake_simulation_exec(bContext *C, wmOperator *op) +static Vector bake_simulation_gather_requests(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); Main *bmain = CTX_data_main(C); @@ -497,8 +520,13 @@ static int bake_simulation_exec(bContext *C, wmOperator *op) } } - Vector requests = collect_simulations_to_bake(*bmain, *scene, objects); - return start_bake_job(C, std::move(requests), op); + return collect_simulations_to_bake(*bmain, *scene, objects); +} + +static int bake_simulation_exec(bContext *C, wmOperator *op) +{ + Vector requests = bake_simulation_gather_requests(C, op); + return start_bake_job(C, std::move(requests), op, BakeRequestsMode::Sync); } struct PathStringHash { @@ -652,7 +680,8 @@ static int bake_simulation_invoke(bContext *C, wmOperator *op, const wmEvent * / if (has_existing_bake_data) { return WM_operator_confirm_message(C, op, "Overwrite existing bake data"); } - return bake_simulation_exec(C, op); + Vector requests = bake_simulation_gather_requests(C, op); + return start_bake_job(C, std::move(requests), op, BakeRequestsMode::Async); } static int bake_simulation_modal(bContext *C, wmOperator * /*op*/, const wmEvent * /*event*/) @@ -745,32 +774,32 @@ static int delete_baked_simulation_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -static int bake_single_node_exec(bContext *C, wmOperator *op) +static Vector bake_single_node_gather_bake_request(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); Object *object = reinterpret_cast( WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_OB)); if (object == nullptr) { - return OPERATOR_CANCELLED; + return {}; } char *modifier_name = RNA_string_get_alloc(op->ptr, "modifier_name", nullptr, 0, nullptr); if (modifier_name == nullptr) { - return OPERATOR_CANCELLED; + return {}; } BLI_SCOPED_DEFER([&]() { MEM_SAFE_FREE(modifier_name); }); ModifierData *md = BKE_modifiers_findby_name(object, modifier_name); if (md == nullptr) { - return OPERATOR_CANCELLED; + return {}; } NodesModifierData &nmd = *reinterpret_cast(md); if (nmd.node_group == nullptr) { - return OPERATOR_CANCELLED; + return {}; } if (!BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime)) { BKE_report(op->reports, RPT_ERROR, "Modifier containing the node is disabled"); - return OPERATOR_CANCELLED; + return {}; } if (StringRef(nmd.bake_directory).is_empty()) { @@ -781,10 +810,10 @@ static int bake_single_node_exec(bContext *C, wmOperator *op) const int bake_id = RNA_int_get(op->ptr, "bake_id"); const bNode *node = nmd.node_group->find_nested_node(bake_id); if (node == nullptr) { - return OPERATOR_CANCELLED; + return {}; } if (!ELEM(node->type, GEO_NODE_SIMULATION_OUTPUT, GEO_NODE_BAKE)) { - return OPERATOR_CANCELLED; + return {}; } NodeBakeRequest request; @@ -796,13 +825,13 @@ static int bake_single_node_exec(bContext *C, wmOperator *op) const NodesModifierBake *bake = nmd.find_bake(bake_id); if (!bake) { - return OPERATOR_CANCELLED; + return {}; } const std::optional bake_path = bake::get_node_bake_path( *bmain, *object, nmd, bake_id); if (!bake_path.has_value()) { BKE_report(op->reports, RPT_ERROR, "Can not determine bake location on disk"); - return OPERATOR_CANCELLED; + return {}; } request.path = std::move(*bake_path); @@ -818,10 +847,10 @@ static int bake_single_node_exec(bContext *C, wmOperator *op) const std::optional frame_range = bake::get_node_bake_frame_range( *scene, *object, nmd, bake_id); if (!frame_range.has_value()) { - return OPERATOR_CANCELLED; + return {}; } if (frame_range->is_empty()) { - return OPERATOR_CANCELLED; + return {}; } request.frame_start = frame_range->first(); request.frame_end = frame_range->last(); @@ -829,7 +858,25 @@ static int bake_single_node_exec(bContext *C, wmOperator *op) Vector requests; requests.append(std::move(request)); - return start_bake_job(C, std::move(requests), op); + return requests; +} + +static int bake_single_node_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) +{ + Vector requests = bake_single_node_gather_bake_request(C, op); + if (requests.is_empty()) { + return OPERATOR_CANCELLED; + } + return start_bake_job(C, std::move(requests), op, BakeRequestsMode::Async); +} + +static int bake_single_node_exec(bContext *C, wmOperator *op) +{ + Vector requests = bake_single_node_gather_bake_request(C, op); + if (requests.is_empty()) { + return OPERATOR_CANCELLED; + } + return start_bake_job(C, std::move(requests), op, BakeRequestsMode::Sync); } static int bake_single_node_modal(bContext *C, wmOperator * /*op*/, const wmEvent * /*event*/) @@ -965,6 +1012,7 @@ void OBJECT_OT_geometry_node_bake_single(wmOperatorType *ot) ot->idname = "OBJECT_OT_geometry_node_bake_single"; ot->poll = bake_poll; + ot->invoke = bake_single_node_invoke; ot->exec = bake_single_node_exec; ot->modal = bake_single_node_modal; -- 2.30.2 From 16875c82bba667245d3a70db4268175d7fa40153 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 17 Jan 2024 13:40:30 +0100 Subject: [PATCH 053/244] Geometry Nodes: reorganize panels in modifier The goal is to simplify the interface of the geometry nodes modifier while also making it more scalable. This allows creating better looking modifiers which feel more built-in. No functionality is removed by this patch. The following changes are done: * The `Output Attributes` panel is hidden when there are no output attributes. * The `Internal Dependencies` panel is replaced by a new `Manage` panel that contains all the stuff that is common to all geometry nodes modifiers. It contains new `Bake` and `Named Attributes` panels for now. Potential next steps: * Merge `Output Attributes` panel with the rest of the input list. * Support hiding the `Manage` panel under some circumstances. Pull Request: https://projects.blender.org/blender/blender/pulls/117170 --- .../blender/makesrna/intern/rna_modifier.cc | 4 +- source/blender/modifiers/intern/MOD_nodes.cc | 60 ++++++++++++++----- 2 files changed, 48 insertions(+), 16 deletions(-) diff --git a/source/blender/makesrna/intern/rna_modifier.cc b/source/blender/makesrna/intern/rna_modifier.cc index 17216f78c6a..42cb63b998d 100644 --- a/source/blender/makesrna/intern/rna_modifier.cc +++ b/source/blender/makesrna/intern/rna_modifier.cc @@ -7332,7 +7332,9 @@ static void rna_def_modifier_nodes(BlenderRNA *brna) RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, nullptr); rna_def_modifier_panel_open_prop(srna, "open_output_attributes_panel", 0); - rna_def_modifier_panel_open_prop(srna, "open_internal_dependencies_panel", 1); + rna_def_modifier_panel_open_prop(srna, "open_manage_panel", 1); + rna_def_modifier_panel_open_prop(srna, "open_bake_panel", 2); + rna_def_modifier_panel_open_prop(srna, "open_named_attributes_panel", 3); RNA_define_lib_overridable(false); } diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index d442b19e7b5..bb4201c6901 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -1931,37 +1931,48 @@ static void draw_interface_panel_content(const bContext *C, } } +static bool has_output_attribute(const NodesModifierData &nmd) +{ + if (!nmd.node_group) { + return false; + } + for (const bNodeTreeInterfaceSocket *interface_socket : nmd.node_group->interface_outputs()) { + const bNodeSocketType *typeinfo = interface_socket->socket_typeinfo(); + const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM; + if (nodes::socket_type_has_attribute_toggle(type)) { + return true; + } + } + return false; +} + static void draw_output_attributes_panel(const bContext *C, uiLayout *layout, const NodesModifierData &nmd, PointerRNA *ptr) { - bool has_output_attribute = false; if (nmd.node_group != nullptr && nmd.settings.properties != nullptr) { for (const bNodeTreeInterfaceSocket *socket : nmd.node_group->interface_outputs()) { const bNodeSocketType *typeinfo = socket->socket_typeinfo(); const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM; if (nodes::socket_type_has_attribute_toggle(type)) { - has_output_attribute = true; draw_property_for_output_socket(*C, layout, nmd, ptr, *socket); } } } - if (!has_output_attribute) { - uiItemL(layout, RPT_("No group output attributes connected"), ICON_INFO); - } } -static void draw_internal_dependencies_panel(uiLayout *layout, - PointerRNA *ptr, - const NodesModifierData &nmd) +static void draw_bake_panel(uiLayout *layout, PointerRNA *modifier_ptr) { uiLayout *col = uiLayoutColumn(layout, false); uiLayoutSetPropSep(col, true); uiLayoutSetPropDecorate(col, false); - uiItemR(col, ptr, "bake_directory", UI_ITEM_NONE, IFACE_("Bake"), ICON_NONE); + uiItemR(col, modifier_ptr, "bake_directory", UI_ITEM_NONE, IFACE_("Bake Path"), ICON_NONE); +} +static void draw_named_attributes_panel(uiLayout *layout, NodesModifierData &nmd) +{ geo_log::GeoTreeLog *tree_log = get_root_tree_log(nmd); if (tree_log == nullptr) { return; @@ -2026,6 +2037,23 @@ static void draw_internal_dependencies_panel(uiLayout *layout, } } +static void draw_manage_panel(const bContext *C, + uiLayout *layout, + PointerRNA *modifier_ptr, + NodesModifierData &nmd) +{ + if (uiLayout *panel_layout = uiLayoutPanel( + C, layout, IFACE_("Bake"), modifier_ptr, "open_bake_panel")) + { + draw_bake_panel(panel_layout, modifier_ptr); + } + if (uiLayout *panel_layout = uiLayoutPanel( + C, layout, IFACE_("Named Attributes"), modifier_ptr, "open_named_attributes_panel")) + { + draw_named_attributes_panel(panel_layout, nmd); + } +} + static void panel_draw(const bContext *C, Panel *panel) { uiLayout *layout = panel->layout; @@ -2069,15 +2097,17 @@ static void panel_draw(const bContext *C, Panel *panel) modifier_panel_end(layout, ptr); - if (uiLayout *panel_layout = uiLayoutPanel( - C, layout, IFACE_("Output Attributes"), ptr, "open_output_attributes_panel")) - { - draw_output_attributes_panel(C, panel_layout, *nmd, ptr); + if (has_output_attribute(*nmd)) { + if (uiLayout *panel_layout = uiLayoutPanel( + C, layout, IFACE_("Output Attributes"), ptr, "open_output_attributes_panel")) + { + draw_output_attributes_panel(C, panel_layout, *nmd, ptr); + } } if (uiLayout *panel_layout = uiLayoutPanel( - C, layout, IFACE_("Internal Dependencies"), ptr, "open_internal_dependencies_panel")) + C, layout, IFACE_("Manage"), ptr, "open_manage_panel")) { - draw_internal_dependencies_panel(panel_layout, ptr, *nmd); + draw_manage_panel(C, panel_layout, ptr, *nmd); } } -- 2.30.2 From 52493e26a0f6f6c877c88735781a03d3fdf3597b Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 17 Jan 2024 13:41:33 +0100 Subject: [PATCH 054/244] Cleanup: make format --- source/blender/editors/sculpt_paint/paint_image.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/sculpt_paint/paint_image.cc b/source/blender/editors/sculpt_paint/paint_image.cc index 3b2cb4f5c9c..9ca4632ef07 100644 --- a/source/blender/editors/sculpt_paint/paint_image.cc +++ b/source/blender/editors/sculpt_paint/paint_image.cc @@ -818,7 +818,8 @@ static blender::float3 paint_init_pivot_mesh(Object *ob) static blender::float3 paint_init_pivot_curves(Object *ob) { const Curves &curves = *static_cast(ob->data); - const std::optional> bounds = curves.geometry.wrap().bounds_min_max(); + const std::optional> bounds = + curves.geometry.wrap().bounds_min_max(); if (bounds.has_value()) { return blender::math::midpoint(bounds->min, bounds->max); } -- 2.30.2 From 058ec3613012ebd69fb0118561149e45e41e3e09 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 17 Jan 2024 10:30:58 -0500 Subject: [PATCH 055/244] Cleanup: Remove unused weak asset reference in Library A library probably shouldn't reference a single specific asset anyway. --- source/blender/makesdna/DNA_ID.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 0c2e4548163..23ee67b2a84 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -559,11 +559,6 @@ typedef struct Library { */ char filepath_abs[1024]; - /** - * Weak reference to the Brush Asset. - */ - struct AssetWeakReference *asset_repository_weak_reference; - /** Set for indirectly linked libraries, used in the outliner and while reading. */ struct Library *parent; -- 2.30.2 From 99a38e9c18239051a34fb4b6077a6ceb7e36dd25 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 17 Jan 2024 10:32:31 -0500 Subject: [PATCH 056/244] Cleanup: Comment style, clang format --- source/blender/blenkernel/BKE_lib_override.hh | 4 ++-- source/blender/blenkernel/BKE_paint.hh | 15 +++++++++++---- source/blender/blenkernel/intern/blendfile.cc | 18 ++++++++++-------- .../editors/sculpt_paint/curves_sculpt_ops.cc | 14 +++++--------- 4 files changed, 28 insertions(+), 23 deletions(-) diff --git a/source/blender/blenkernel/BKE_lib_override.hh b/source/blender/blenkernel/BKE_lib_override.hh index 12d8173f052..f44faac023f 100644 --- a/source/blender/blenkernel/BKE_lib_override.hh +++ b/source/blender/blenkernel/BKE_lib_override.hh @@ -83,10 +83,10 @@ 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 amount of liboverride IDs of given `id_type`, using a refererence linked ID from given + * 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. + * \param r_reports: If not NULL, add one report for each relevant ID. */ int BKE_lib_override_user_edited_from_library_count(struct Main *bmain, const short id_type, diff --git a/source/blender/blenkernel/BKE_paint.hh b/source/blender/blenkernel/BKE_paint.hh index c4e862a83ec..3e6d6857715 100644 --- a/source/blender/blenkernel/BKE_paint.hh +++ b/source/blender/blenkernel/BKE_paint.hh @@ -203,11 +203,18 @@ Brush *BKE_paint_brush(Paint *paint); const Brush *BKE_paint_brush_for_read(const Paint *p); void BKE_paint_brush_set(Paint *paint, Brush *br); -/** Set the active brush of given paint struct, and store the weak asset reference to it. - * NOTE: Takes ownership of the given `weak_asset_reference`. */ -void BKE_paint_brush_asset_set(Paint *p, Brush *br, AssetWeakReference *weak_asset_reference); +/** + * Set the active brush of given paint struct, and store the weak asset reference to it. + * \note Takes ownership of the given `weak_asset_reference`. + */ +void BKE_paint_brush_asset_set(Paint *paint, + Brush *brush, + AssetWeakReference *weak_asset_reference); -/** Attempt to restore a valid active brush in `p` from brush asset informations stored in `p`. */ +/** + * Attempt to restore a valid active brush in `paint` from brush asset information stored in + * `paint`. + */ void BKE_paint_brush_asset_restore(Main *bmain, Paint *p); Palette *BKE_paint_palette(Paint *paint); diff --git a/source/blender/blenkernel/intern/blendfile.cc b/source/blender/blenkernel/intern/blendfile.cc index 7f7c59a22c1..8ed7b6adadb 100644 --- a/source/blender/blenkernel/intern/blendfile.cc +++ b/source/blender/blenkernel/intern/blendfile.cc @@ -396,17 +396,18 @@ static int reuse_bmain_data_dependencies_process_cb(LibraryIDLinkCallbackData *c return IDWALK_RET_NOP; } -/* Selectively 'import' data from old BMain into new BMain, provided it does not conflict with data - * already present in the new BMain (name-wise and library-wise). +/** + * 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 BMain, (unless, in case of - * linked data, a matching linked ID is already available in new BMain). + * 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 BMain later. + * `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 BMain, it only moves - * (some of) the old data to the new BMain. + * 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) { @@ -430,7 +431,8 @@ static void reuse_old_bmain_data_for_blendfile(ReuseOldBMainData *reuse_data, co * 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)) { + ID_IS_ASSET(old_id_iter)) + { continue; } diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc index 3df4e35898e..cb043c27675 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc @@ -1167,7 +1167,10 @@ static void SCULPT_CURVES_OT_min_distance_edit(wmOperatorType *ot) static int brush_asset_select_exec(bContext *C, wmOperator * /*op*/) { - blender::asset_system::AssetRepresentation *asset = CTX_wm_asset(C); + /* 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. */ + asset_system::AssetRepresentation *asset = CTX_wm_asset(C); if (!asset) { return OPERATOR_CANCELLED; } @@ -1182,19 +1185,12 @@ static int brush_asset_select_exec(bContext *C, wmOperator * /*op*/) return OPERATOR_FINISHED; } -/** - * 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. - */ -static void SCULPT_CURVES_OT_brush_asset_select(struct wmOperatorType *ot) +static void SCULPT_CURVES_OT_brush_asset_select(wmOperatorType *ot) { - /* identifiers */ ot->name = "Select Brush Asset"; ot->description = "Select a brush asset as currently sculpt/paint tool - TESTING PURPOSE ONLY"; ot->idname = "SCULPT_CURVES_OT_brush_asset_select"; - /* api callbacks */ ot->exec = brush_asset_select_exec; ot->poll = CURVES_SCULPT_mode_poll; -- 2.30.2 From eedd55e7700dd4cff65d26ab5572a82909421b2b Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 17 Jan 2024 10:32:40 -0500 Subject: [PATCH 057/244] Cleanup: remove unnecessary struct keyword --- source/blender/blenkernel/BKE_lib_override.hh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/BKE_lib_override.hh b/source/blender/blenkernel/BKE_lib_override.hh index f44faac023f..479a8128e91 100644 --- a/source/blender/blenkernel/BKE_lib_override.hh +++ b/source/blender/blenkernel/BKE_lib_override.hh @@ -88,10 +88,10 @@ bool BKE_lib_override_library_is_system_defined(const Main *bmain, const ID *id) * * \param r_reports: If not NULL, add one report for each relevant ID. */ -int BKE_lib_override_user_edited_from_library_count(struct Main *bmain, +int BKE_lib_override_user_edited_from_library_count(Main *bmain, const short id_type, - struct Library *library, - struct ReportList *r_reports); + Library *library, + ReportList *r_reports); /** * Check if given Override Property for given ID is animated (through a F-Curve in an Action, or -- 2.30.2 From 173661713db60703595b0d5d6cad56e6baa901cd Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 17 Jan 2024 10:32:49 -0500 Subject: [PATCH 058/244] Cleanup: Functional style cast for enum --- source/blender/blenkernel/intern/brush.cc | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index 5982da7d12a..0b1ed00797b 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -526,11 +526,10 @@ Brush *BKE_brush_asset_runtime_ensure(Main *bmain, const AssetWeakReference *bru 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, - static_cast(BKE_LIBLINK_OVERRIDE_USE_EXISTING_LIBOVERRIDES | - BKE_LIBLINK_OVERRIDE_CREATE_RUNTIME), - 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)); -- 2.30.2 From 5fc94a3872d80dec74301079968f6988f389fdfc Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 17 Jan 2024 10:33:10 -0500 Subject: [PATCH 059/244] Cleanup: Avoid single/two letter variable names --- source/blender/blenkernel/intern/paint.cc | 38 ++++++++++++----------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 22d0f5536d6..5d5db90ed4c 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -673,48 +673,50 @@ void BKE_paint_brush_set(Paint *p, Brush *br) } } -static void paint_brush_asset_update(Paint *p, - Brush *br, +static void paint_brush_asset_update(Paint &paint, + Brush *brush, AssetWeakReference *brush_asset_reference) { - if (p->brush_asset_reference != nullptr) { - BKE_asset_weak_reference_free(&p->brush_asset_reference); + if (paint.brush_asset_reference != nullptr) { + BKE_asset_weak_reference_free(&paint.brush_asset_reference); } - if (br == nullptr || br != p->brush || !ID_IS_OVERRIDE_LIBRARY_REAL(p->brush) || - !(ID_IS_ASSET(p->brush) || ID_IS_ASSET(p->brush->id.override_library->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))) { BKE_asset_weak_reference_free(&brush_asset_reference); return; } - p->brush_asset_reference = brush_asset_reference; + paint.brush_asset_reference = brush_asset_reference; } -void BKE_paint_brush_asset_set(Paint *p, Brush *br, AssetWeakReference *weak_asset_reference) +void BKE_paint_brush_asset_set(Paint *paint, + Brush *brush, + AssetWeakReference *weak_asset_reference) { - BKE_paint_brush_set(p, br); - paint_brush_asset_update(p, br, weak_asset_reference); + BKE_paint_brush_set(paint, brush); + paint_brush_asset_update(*paint, brush, weak_asset_reference); } -void BKE_paint_brush_asset_restore(Main *bmain, Paint *p) +void BKE_paint_brush_asset_restore(Main *bmain, Paint *paint) { - if (p->brush != nullptr) { + if (paint->brush != nullptr) { return; } - if (p->brush_asset_reference == nullptr) { + if (paint->brush_asset_reference == nullptr) { return; } - AssetWeakReference *brush_asset_reference = p->brush_asset_reference; - p->brush_asset_reference = nullptr; + 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); - /* Will either re-assign the brush_asset_reference to `p`, or free it if loading a brush ID from - * it failed. */ - BKE_paint_brush_asset_set(p, brush_asset, brush_asset_reference); + /* Will either re-assign the brush_asset_reference to `paint`, or free it if loading a brush ID + * from it failed. */ + BKE_paint_brush_asset_set(paint, brush_asset, brush_asset_reference); } void BKE_paint_runtime_init(const ToolSettings *ts, Paint *paint) -- 2.30.2 From 448c30cc45bec23f20c89467d204b995f582874a Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 17 Jan 2024 10:33:25 -0500 Subject: [PATCH 060/244] Cleanup: Capitalization in operator UI description --- source/blender/editors/sculpt_paint/curves_sculpt_ops.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc index cb043c27675..dde42e006a7 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc @@ -1195,7 +1195,7 @@ static void SCULPT_CURVES_OT_brush_asset_select(wmOperatorType *ot) ot->poll = CURVES_SCULPT_mode_poll; ot->prop = RNA_def_string( - ot->srna, "name", nullptr, MAX_NAME, "Brush Name", "name of the brush asset to select"); + ot->srna, "name", nullptr, MAX_NAME, "Brush Name", "Name of the brush asset to select"); } } // namespace blender::ed::sculpt_paint -- 2.30.2 From 23f0914744927c839daf3f14fb7bc27e8079caf8 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 17 Jan 2024 10:36:34 -0500 Subject: [PATCH 061/244] Cleanup: Remove unused asset handle function These days the function on AssetRepresentation can probably be used instead --- source/blender/editors/asset/ED_asset_handle.h | 4 ---- source/blender/editors/asset/intern/asset_handle.cc | 13 ------------- 2 files changed, 17 deletions(-) diff --git a/source/blender/editors/asset/ED_asset_handle.h b/source/blender/editors/asset/ED_asset_handle.h index 69df2285a82..2506dc15ab6 100644 --- a/source/blender/editors/asset/ED_asset_handle.h +++ b/source/blender/editors/asset/ED_asset_handle.h @@ -43,10 +43,6 @@ void ED_asset_handle_get_full_library_path( /* `1024` for #FILE_MAX, * rely on warnings to let us know if this gets out of sync. */ char r_full_lib_path[1024]); -void ED_asset_handle_get_full_path(const AssetHandle *asset_handle, - /* `1024 + 66` for #FILE_MAX_LIBEXTRA, - * rely on warnings to let us know if this gets out of sync. */ - char r_full_lib_path[1024 + 66]); #ifdef __cplusplus } diff --git a/source/blender/editors/asset/intern/asset_handle.cc b/source/blender/editors/asset/intern/asset_handle.cc index 0cb64000153..b56c79a6905 100644 --- a/source/blender/editors/asset/intern/asset_handle.cc +++ b/source/blender/editors/asset/intern/asset_handle.cc @@ -57,16 +57,3 @@ void ED_asset_handle_get_full_library_path(const AssetHandle *asset_handle, BLI_strncpy(r_full_lib_path, library_path.c_str(), FILE_MAX); } - -void ED_asset_handle_get_full_path(const AssetHandle *asset_handle, - char r_full_lib_path[FILE_MAX_LIBEXTRA]) -{ - *r_full_lib_path = '\0'; - - std::string library_path = asset_handle->file_data->asset->get_identifier().full_path(); - if (library_path.empty()) { - return; - } - - BLI_strncpy(r_full_lib_path, library_path.c_str(), FILE_MAX); -} -- 2.30.2 From 9a553f3b7119b9b001228f5c264484fbc20319a9 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 17 Jan 2024 17:30:52 +0100 Subject: [PATCH 062/244] Fix: crash when selecting a brush asset from the wrong mode Ideally these should not appear at all in the asset shelf, but it's not obvious that will 100% ensure the right mode. Also fix a missing redraw of the properties editor when switching brushes. Pull Request: https://projects.blender.org/blender/blender/pulls/117233 --- source/blender/blenkernel/BKE_paint.hh | 2 +- source/blender/blenkernel/intern/paint.cc | 9 ++++++++- .../editors/sculpt_paint/curves_sculpt_ops.cc | 14 ++++++++++++-- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/source/blender/blenkernel/BKE_paint.hh b/source/blender/blenkernel/BKE_paint.hh index 3e6d6857715..788c4b78bd4 100644 --- a/source/blender/blenkernel/BKE_paint.hh +++ b/source/blender/blenkernel/BKE_paint.hh @@ -207,7 +207,7 @@ void BKE_paint_brush_set(Paint *paint, Brush *br); * Set the active brush of given paint struct, and store the weak asset reference to it. * \note Takes ownership of the given `weak_asset_reference`. */ -void BKE_paint_brush_asset_set(Paint *paint, +bool BKE_paint_brush_asset_set(Paint *paint, Brush *brush, AssetWeakReference *weak_asset_reference); diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 5d5db90ed4c..500c7636a09 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -691,10 +691,17 @@ static void paint_brush_asset_update(Paint &paint, paint.brush_asset_reference = brush_asset_reference; } -void BKE_paint_brush_asset_set(Paint *paint, +bool BKE_paint_brush_asset_set(Paint *paint, Brush *brush, AssetWeakReference *weak_asset_reference) { + /* Should not happen for users if brush assets are properly filtered by mode, but still protect + * against it in case of invalid API usage. */ + if (paint->runtime.ob_mode != brush->ob_mode) { + BKE_asset_weak_reference_free(&weak_asset_reference); + return false; + } + BKE_paint_brush_set(paint, brush); paint_brush_asset_update(*paint, brush, weak_asset_reference); } diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc index dde42e006a7..f597dd89014 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc @@ -18,6 +18,7 @@ #include "BKE_modifier.hh" #include "BKE_object.hh" #include "BKE_paint.hh" +#include "BKE_report.h" #include "WM_api.hh" #include "WM_message.hh" @@ -1165,7 +1166,7 @@ static void SCULPT_CURVES_OT_min_distance_edit(wmOperatorType *ot) /* -------------------------------------------------------------------- */ -static int brush_asset_select_exec(bContext *C, wmOperator * /*op*/) +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, @@ -1179,8 +1180,17 @@ static int brush_asset_select_exec(bContext *C, wmOperator * /*op*/) Brush *brush = BKE_brush_asset_runtime_ensure(CTX_data_main(C), brush_asset_reference); ToolSettings *tool_settings = CTX_data_tool_settings(C); + /* Either takes ownership of the brush_asset_reference, or frees it. */ - BKE_paint_brush_asset_set(&tool_settings->curves_sculpt->paint, brush, brush_asset_reference); + if (!BKE_paint_brush_asset_set( + &tool_settings->curves_sculpt->paint, brush, brush_asset_reference)) + { + /* Note brush datablock was still added, so was not a no-op. */ + BKE_report(op->reports, RPT_WARNING, "Unable to select brush, wrong object mode"); + return OPERATOR_FINISHED; + } + + WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, nullptr); return OPERATOR_FINISHED; } -- 2.30.2 From c65b0ad14403a8ff5bfb9889f39ebaba81cda464 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 16 Jan 2024 18:49:39 +0100 Subject: [PATCH 063/244] Bundle WIP brush assets as essentials Change for development/prototyping only: Include the brush assets from the `working/` part of the SVN libraries repository, not just the ones under `publish/`. This is managed by CMake during the installation build step. --- source/creator/CMakeLists.txt | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 21da2ccb439..46d78580ff7 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -1669,12 +1669,27 @@ if(NOT EXISTS "${ASSET_BUNDLE_DIR}") set(ASSET_BUNDLE_DIR ${CMAKE_SOURCE_DIR}/../lib/assets/publish/) endif() +set(ASSET_TARGETDIR ${TARGETDIR_VER}/datafiles/assets) if(EXISTS "${ASSET_BUNDLE_DIR}") install( DIRECTORY ${ASSET_BUNDLE_DIR} - DESTINATION ${TARGETDIR_VER}/datafiles/assets + DESTINATION ${ASSET_TARGETDIR} PATTERN ".svn" EXCLUDE ) + + # TODO temporary change for development only. Remove before merging. + set(ASSET_BRUSH_BUNDLE_DIR "${ASSET_BUNDLE_DIR}/../working/brushes/") + if(EXISTS "${ASSET_BRUSH_BUNDLE_DIR}") + install( + FILES ${ASSET_BRUSH_BUNDLE_DIR}/essentials_brushes.blend + DESTINATION ${ASSET_TARGETDIR}/brushes + ) + + install( + FILES ${ASSET_BRUSH_BUNDLE_DIR}/blender_assets.cats.txt + DESTINATION ${ASSET_TARGETDIR} + ) + endif() endif() -- 2.30.2 From d9f2fce6143334ba8a21c1dd3058f8816cc97a33 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 17 Jan 2024 19:10:08 +0100 Subject: [PATCH 064/244] Write compatible modes to brush asset metadata Had to add support for JSon (de)serializing of boolean properties. --- source/blender/blenkernel/intern/brush.cc | 39 ++++++++++- .../blenkernel/intern/idprop_serialize.cc | 68 +++++++++++++++++++ 2 files changed, 106 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index 0b1ed00797b..f25b4f65084 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -6,6 +6,8 @@ * \ingroup bke */ +#include + #include "MEM_guardedalloc.h" #include "DNA_brush_types.h" @@ -24,12 +26,14 @@ #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" #include "BKE_context.hh" #include "BKE_gpencil_legacy.h" +#include "BKE_idprop.hh" #include "BKE_idtype.h" #include "BKE_lib_id.hh" #include "BKE_lib_query.h" @@ -382,6 +386,39 @@ static void brush_blend_read_after_liblink(BlendLibReader * /*reader*/, ID *id) } } +static void brush_asset_metadata_ensure(void *asset_ptr, AssetMetaData *asset_data) +{ + using namespace blender; + using namespace blender::bke; + + Brush *brush = reinterpret_cast(asset_ptr); + BLI_assert(GS(brush->id.name) == ID_BR); + + static const Array> mode_map = { + {"use_paint_sculpt", OB_MODE_SCULPT}, + {"use_paint_uv_sculpt", OB_MODE_EDIT}, + {"use_paint_vertex", OB_MODE_VERTEX_PAINT}, + {"use_paint_weight", OB_MODE_WEIGHT_PAINT}, + {"use_paint_image", OB_MODE_TEXTURE_PAINT}, + {"use_paint_grease_pencil", OB_MODE_PAINT_GPENCIL_LEGACY}, + {"use_vertex_grease_pencil", OB_MODE_VERTEX_GPENCIL_LEGACY}, + {"use_paint_sculpt_curves", OB_MODE_SCULPT_CURVES}}; + + for (const auto &mode_mapping : mode_map) { + /* Only add bools for supported modes. */ + if (!(brush->ob_mode & mode_mapping.second)) { + continue; + } + auto mode_property = idprop::create_bool(mode_mapping.first, true); + BKE_asset_metadata_idprop_ensure(asset_data, mode_property.release()); + } +} + +static AssetTypeInfo AssetType_BR = { + /*pre_save_fn*/ brush_asset_metadata_ensure, + /*on_mark_asset_fn*/ brush_asset_metadata_ensure, +}; + IDTypeInfo IDType_ID_BR = { /*id_code*/ ID_BR, /*id_filter*/ FILTER_ID_BR, @@ -391,7 +428,7 @@ IDTypeInfo IDType_ID_BR = { /*name_plural*/ N_("brushes"), /*translation_context*/ BLT_I18NCONTEXT_ID_BRUSH, /*flags*/ IDTYPE_FLAGS_NO_ANIMDATA | IDTYPE_FLAGS_NO_MEMFILE_UNDO, - /*asset_type_info*/ nullptr, + /*asset_type_info*/ &AssetType_BR, /*init_data*/ brush_init_data, /*copy_data*/ brush_copy_data, diff --git a/source/blender/blenkernel/intern/idprop_serialize.cc b/source/blender/blenkernel/intern/idprop_serialize.cc index c2a52f1733a..4707e910031 100644 --- a/source/blender/blenkernel/intern/idprop_serialize.cc +++ b/source/blender/blenkernel/intern/idprop_serialize.cc @@ -31,6 +31,7 @@ static constexpr StringRef IDP_KEY_SUBTYPE("subtype"); static constexpr StringRef IDP_KEY_VALUE("value"); static constexpr StringRef IDP_PROPERTY_TYPENAME_STRING("IDP_STRING"); +static constexpr StringRef IDP_PROPERTY_TYPENAME_BOOL("IDP_BOOL"); static constexpr StringRef IDP_PROPERTY_TYPENAME_INT("IDP_INT"); static constexpr StringRef IDP_PROPERTY_TYPENAME_FLOAT("IDP_FLOAT"); static constexpr StringRef IDP_PROPERTY_TYPENAME_DOUBLE("IDP_DOUBLE"); @@ -125,6 +126,11 @@ struct DictionaryEntryParser { return get_string(IDP_KEY_VALUE); } + std::optional get_bool_value() const + { + return get_bool(IDP_KEY_VALUE); + } + std::optional get_int_value() const { return get_int(IDP_KEY_VALUE); @@ -196,6 +202,21 @@ struct DictionaryEntryParser { return value->as_array_value(); } + std::optional get_bool(StringRef key) const + { + const DictionaryValue::LookupValue *value_ptr = lookup.lookup_ptr(key); + if (value_ptr == nullptr) { + return std::nullopt; + } + const DictionaryValue::LookupValue &value = *value_ptr; + + if (value->type() != eValueType::Boolean) { + return std::nullopt; + } + + return value->as_boolean_value()->value(); + } + std::optional get_int(StringRef key) const { const DictionaryValue::LookupValue *value_ptr = lookup.lookup_ptr(key); @@ -321,6 +342,46 @@ class IDPStringSerializer : public IDPropertySerializer { } }; +/** \brief IDPSerializer for IDP_INT. */ +class IDPBoolSerializer : public IDPropertySerializer { + public: + constexpr IDPBoolSerializer() = default; + + std::string type_name() const override + { + return IDP_PROPERTY_TYPENAME_BOOL; + } + + std::optional property_type() const override + { + return IDP_BOOLEAN; + } + + std::shared_ptr idprop_to_dictionary( + const IDProperty *id_property) const override + { + std::shared_ptr result = create_dictionary(id_property); + DictionaryValue::Items &attributes = result->elements(); + attributes.append_as(std::pair(IDP_KEY_VALUE, new BooleanValue(IDP_Bool(id_property) != 0))); + return result; + } + + std::unique_ptr entry_to_idprop( + DictionaryEntryParser &entry_reader) const override + { + BLI_assert(*(entry_reader.get_type()) == IDP_BOOLEAN); + std::optional name = entry_reader.get_name(); + if (!name.has_value()) { + return nullptr; + } + std::optional extracted_value = entry_reader.get_bool_value(); + if (!extracted_value.has_value()) { + return nullptr; + } + return create_bool(name->c_str(), *extracted_value); + } +}; + /** \brief IDPSerializer for IDP_INT. */ class IDPIntSerializer : public IDPropertySerializer { public: @@ -704,6 +765,7 @@ class IDPUnknownSerializer : public IDPropertySerializer { /* Serializers are constructed statically to remove construction/destruction. */ static constexpr IDPStringSerializer IDP_SERIALIZER_STRING; +static constexpr IDPIntSerializer IDP_SERIALIZER_BOOL; static constexpr IDPIntSerializer IDP_SERIALIZER_INT; static constexpr IDPFloatSerializer IDP_SERIALIZER_FLOAT; static constexpr IDPDoubleSerializer IDP_SERIALIZER_DOUBLE; @@ -718,6 +780,9 @@ static const IDPropertySerializer &serializer_for(eIDPropertyType property_type) case IDP_STRING: return IDP_SERIALIZER_STRING; + case IDP_BOOLEAN: + return IDP_SERIALIZER_BOOL; + case IDP_INT: return IDP_SERIALIZER_INT; @@ -745,6 +810,9 @@ static const IDPropertySerializer &serializer_for(StringRef idprop_typename) if (idprop_typename == IDP_PROPERTY_TYPENAME_STRING) { return IDP_SERIALIZER_STRING; } + if (idprop_typename == IDP_PROPERTY_TYPENAME_BOOL) { + return IDP_SERIALIZER_BOOL; + } if (idprop_typename == IDP_PROPERTY_TYPENAME_INT) { return IDP_SERIALIZER_INT; } -- 2.30.2 From 335876b4cf58f1978902bf83edacfd049723baf8 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 17 Jan 2024 19:33:15 +0100 Subject: [PATCH 065/244] Fix error in IDProperty serializer for bools --- source/blender/blenkernel/intern/brush.cc | 3 ++- source/blender/blenkernel/intern/idprop_serialize.cc | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index f25b4f65084..c5c6b0ed228 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -402,7 +402,8 @@ static void brush_asset_metadata_ensure(void *asset_ptr, AssetMetaData *asset_da {"use_paint_image", OB_MODE_TEXTURE_PAINT}, {"use_paint_grease_pencil", OB_MODE_PAINT_GPENCIL_LEGACY}, {"use_vertex_grease_pencil", OB_MODE_VERTEX_GPENCIL_LEGACY}, - {"use_paint_sculpt_curves", OB_MODE_SCULPT_CURVES}}; + {"use_paint_sculpt_curves", OB_MODE_SCULPT_CURVES}, + }; for (const auto &mode_mapping : mode_map) { /* Only add bools for supported modes. */ diff --git a/source/blender/blenkernel/intern/idprop_serialize.cc b/source/blender/blenkernel/intern/idprop_serialize.cc index 4707e910031..bd59af19494 100644 --- a/source/blender/blenkernel/intern/idprop_serialize.cc +++ b/source/blender/blenkernel/intern/idprop_serialize.cc @@ -765,7 +765,7 @@ class IDPUnknownSerializer : public IDPropertySerializer { /* Serializers are constructed statically to remove construction/destruction. */ static constexpr IDPStringSerializer IDP_SERIALIZER_STRING; -static constexpr IDPIntSerializer IDP_SERIALIZER_BOOL; +static constexpr IDPBoolSerializer IDP_SERIALIZER_BOOL; static constexpr IDPIntSerializer IDP_SERIALIZER_INT; static constexpr IDPFloatSerializer IDP_SERIALIZER_FLOAT; static constexpr IDPDoubleSerializer IDP_SERIALIZER_DOUBLE; -- 2.30.2 From 5e813f1563d15a1ddc94764ad755efffca7fb759 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 17 Jan 2024 19:11:06 +0100 Subject: [PATCH 066/244] Only show curve sculpt brushes in curve sculpt mode --- scripts/startup/bl_ui/space_view3d.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index 38fec42405e..1787d88ee61 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -1091,7 +1091,7 @@ class VIEW3D_AST_brush_asset_shelf(bpy.types.AssetShelf): @classmethod def asset_poll(cls, asset): - return asset.id_type == 'BRUSH' + return asset.id_type == 'BRUSH' and asset.metadata.get("use_paint_sculpt_curves", False) class VIEW3D_MT_editor_menus(Menu): -- 2.30.2 From 01436255b5129dc2ca614052a93cffebe23da502 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 17 Jan 2024 14:07:44 -0500 Subject: [PATCH 067/244] Fix build error --- source/blender/blenkernel/intern/paint.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 500c7636a09..fbcb675935c 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -704,6 +704,7 @@ bool BKE_paint_brush_asset_set(Paint *paint, BKE_paint_brush_set(paint, brush); paint_brush_asset_update(*paint, brush, weak_asset_reference); + return true; } void BKE_paint_brush_asset_restore(Main *bmain, Paint *paint) -- 2.30.2 From 33cc4af2a2f1f71248b5fea6d1f164e22d1d275e Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 18 Jan 2024 08:12:16 -0500 Subject: [PATCH 068/244] Cleanup: Use slightly simpler constexpr array The need to repeat std::pair isn't great, but at least this won't result in a heap allocation. --- source/blender/blenkernel/intern/brush.cc | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index c5c6b0ed228..d89f79670c4 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -394,15 +394,15 @@ static void brush_asset_metadata_ensure(void *asset_ptr, AssetMetaData *asset_da Brush *brush = reinterpret_cast(asset_ptr); BLI_assert(GS(brush->id.name) == ID_BR); - static const Array> mode_map = { - {"use_paint_sculpt", OB_MODE_SCULPT}, - {"use_paint_uv_sculpt", OB_MODE_EDIT}, - {"use_paint_vertex", OB_MODE_VERTEX_PAINT}, - {"use_paint_weight", OB_MODE_WEIGHT_PAINT}, - {"use_paint_image", OB_MODE_TEXTURE_PAINT}, - {"use_paint_grease_pencil", OB_MODE_PAINT_GPENCIL_LEGACY}, - {"use_vertex_grease_pencil", OB_MODE_VERTEX_GPENCIL_LEGACY}, - {"use_paint_sculpt_curves", OB_MODE_SCULPT_CURVES}, + constexpr std::array mode_map{ + std::pair{"use_paint_sculpt", OB_MODE_SCULPT}, + std::pair{"use_paint_uv_sculpt", OB_MODE_EDIT}, + std::pair{"use_paint_vertex", OB_MODE_VERTEX_PAINT}, + std::pair{"use_paint_weight", OB_MODE_WEIGHT_PAINT}, + std::pair{"use_paint_image", OB_MODE_TEXTURE_PAINT}, + std::pair{"use_paint_grease_pencil", OB_MODE_PAINT_GPENCIL_LEGACY}, + std::pair{"use_vertex_grease_pencil", OB_MODE_VERTEX_GPENCIL_LEGACY}, + std::pair{"use_paint_sculpt_curves", OB_MODE_SCULPT_CURVES}, }; for (const auto &mode_mapping : mode_map) { @@ -550,7 +550,7 @@ Brush *BKE_brush_asset_runtime_ensure(Main *bmain, const AssetWeakReference *bru return local_brush_asset; } - LibraryLink_Params lapp_parameters = {nullptr}; + LibraryLink_Params lapp_parameters{}; lapp_parameters.bmain = bmain; BlendfileLinkAppendContext *lapp_context = BKE_blendfile_link_append_context_new( &lapp_parameters); -- 2.30.2 From 3bbcfeebf0474baeeb63dd58081ff36e4e99632f Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 23 Jan 2024 15:50:39 +0100 Subject: [PATCH 069/244] Fix merge mistakes. --- source/blender/blenkernel/intern/blendfile.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/blendfile.cc b/source/blender/blenkernel/intern/blendfile.cc index e9486b05543..729ca528719 100644 --- a/source/blender/blenkernel/intern/blendfile.cc +++ b/source/blender/blenkernel/intern/blendfile.cc @@ -468,7 +468,7 @@ static void reuse_old_bmain_data_for_blendfile(ReuseOldBMainData *reuse_data, co 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_uuid_renew(old_id_iter); + 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); -- 2.30.2 From 8be8f103057caa16b89017b3a1a476bb7e4bc2a7 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 24 Jan 2024 18:18:28 +0100 Subject: [PATCH 070/244] Add asset shelves for all paint modes --- scripts/startup/bl_ui/space_view3d.py | 95 ++++++++++++++++------- source/blender/blenkernel/intern/brush.cc | 8 +- 2 files changed, 75 insertions(+), 28 deletions(-) diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index 3223fe9ae78..1c7a8215238 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -1081,19 +1081,6 @@ class VIEW3D_HT_header(Header): sub.popover(panel="VIEW3D_PT_shading", text="") -class VIEW3D_AST_brush_asset_shelf(bpy.types.AssetShelf): - bl_space_type = "VIEW_3D" - bl_options = {'NO_ASSET_DRAG'} - - @classmethod - def poll(cls, context): - return context.object and context.object.mode == 'SCULPT_CURVES' - - @classmethod - def asset_poll(cls, asset): - return asset.id_type == 'BRUSH' and asset.metadata.get("use_paint_sculpt_curves", False) - - class VIEW3D_MT_editor_menus(Menu): bl_label = "" @@ -8769,28 +8756,73 @@ class VIEW3D_PT_viewport_debug(Panel): layout.prop(overlay, "use_debug_freeze_view_culling") -class VIEW3D_AST_sculpt_brushes(bpy.types.AssetShelf): - # Experimental: Asset shelf for sculpt brushes, only shows up if both the - # "Asset Shelf" and the "Extended Asset Browser" experimental features are - # enabled. - - bl_space_type = 'VIEW_3D' +class BrushAssetShelf: + bl_space_type = "VIEW_3D" + bl_options = {'NO_ASSET_DRAG'} @classmethod def poll(cls, context): - prefs = context.preferences - if not prefs.experimental.use_extended_asset_browser: - return False - - return context.mode == 'SCULPT' + return context.object and context.object.mode == cls.mode @classmethod def asset_poll(cls, asset): - return asset.id_type == 'BRUSH' + if asset.id_type != 'BRUSH': + return False + + return asset.metadata.get(cls.mode_prop, False) + + +class VIEW3D_AST_brush_sculpt(BrushAssetShelf, bpy.types.AssetShelf): + mode = 'SCULPT' + mode_prop = "use_paint_sculpt" + + +class VIEW3D_AST_brush_sculpt_curves(BrushAssetShelf, bpy.types.AssetShelf): + mode = 'SCULPT_CURVES' + mode_prop = "use_paint_sculpt_curves" + + +class VIEW3D_AST_brush_vertex_paint(BrushAssetShelf, bpy.types.AssetShelf): + mode = 'VERTEX_PAINT' + mode_prop = "use_paint_vertex" + + +class VIEW3D_AST_brush_weight_paint(BrushAssetShelf, bpy.types.AssetShelf): + mode = 'WEIGHT_PAINT' + mode_prop = "use_paint_weight" + + +class VIEW3D_AST_brush_texture_paint(BrushAssetShelf, bpy.types.AssetShelf): + mode = 'TEXTURE_PAINT' + mode_prop = "use_paint_image" + + +class VIEW3D_AST_brush_gpencil_paint(BrushAssetShelf, bpy.types.AssetShelf): + mode = 'PAINT_GPENCIL' + mode_prop = "use_paint_grease_pencil" + + +class VIEW3D_AST_brush_grease_pencil_paint(BrushAssetShelf, bpy.types.AssetShelf): + mode = 'PAINT_GREASE_PENCIL' + mode_prop = "use_paint_grease_pencil" + + +class VIEW3D_AST_brush_gpencil_sculpt(BrushAssetShelf, bpy.types.AssetShelf): + mode = 'SCULPT_GPENCIL' + mode_prop = "use_sculpt_grease_pencil" + + +class VIEW3D_AST_brush_gpencil_vertex(BrushAssetShelf, bpy.types.AssetShelf): + mode = 'VERTEX_GPENCIL' + mode_prop = "use_vertex_grease_pencil" + + +class VIEW3D_AST_brush_gpencil_weight(BrushAssetShelf, bpy.types.AssetShelf): + mode = 'WEIGHT_GPENCIL' + mode_prop = "use_weight_grease_pencil" classes = ( - VIEW3D_AST_brush_asset_shelf, VIEW3D_HT_header, VIEW3D_HT_tool_header, VIEW3D_MT_editor_menus, @@ -9048,7 +9080,16 @@ classes = ( VIEW3D_PT_curves_sculpt_parameter_falloff, VIEW3D_PT_curves_sculpt_grow_shrink_scaling, VIEW3D_PT_viewport_debug, - VIEW3D_AST_sculpt_brushes, + VIEW3D_AST_brush_sculpt, + VIEW3D_AST_brush_sculpt_curves, + VIEW3D_AST_brush_vertex_paint, + VIEW3D_AST_brush_weight_paint, + VIEW3D_AST_brush_texture_paint, + VIEW3D_AST_brush_gpencil_paint, + VIEW3D_AST_brush_grease_pencil_paint, + VIEW3D_AST_brush_gpencil_sculpt, + VIEW3D_AST_brush_gpencil_vertex, + VIEW3D_AST_brush_gpencil_weight, ) diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index b35533b5cd0..b122b001ae3 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -394,14 +394,20 @@ static void brush_asset_metadata_ensure(void *asset_ptr, AssetMetaData *asset_da Brush *brush = reinterpret_cast(asset_ptr); BLI_assert(GS(brush->id.name) == ID_BR); + /* Most names copied from brush RNA (not all are available there though). */ constexpr std::array mode_map{ std::pair{"use_paint_sculpt", OB_MODE_SCULPT}, - std::pair{"use_paint_uv_sculpt", OB_MODE_EDIT}, std::pair{"use_paint_vertex", OB_MODE_VERTEX_PAINT}, std::pair{"use_paint_weight", OB_MODE_WEIGHT_PAINT}, std::pair{"use_paint_image", OB_MODE_TEXTURE_PAINT}, + /* Sculpt UVs in the image editor while in edit mode. */ + std::pair{"use_paint_uv_sculpt", OB_MODE_EDIT}, std::pair{"use_paint_grease_pencil", OB_MODE_PAINT_GPENCIL_LEGACY}, + std::pair{"use_paint_grease_pencil", OB_MODE_PAINT_GREASE_PENCIL}, + /* Note: Not defined in brush RNA, own name. */ + std::pair{"use_sculpt_grease_pencil", OB_MODE_SCULPT_GPENCIL_LEGACY}, std::pair{"use_vertex_grease_pencil", OB_MODE_VERTEX_GPENCIL_LEGACY}, + std::pair{"use_weight_grease_pencil", OB_MODE_WEIGHT_GPENCIL_LEGACY}, std::pair{"use_paint_sculpt_curves", OB_MODE_SCULPT_CURVES}, }; -- 2.30.2 From 6f84c5da8f7a944e95f75b84b5a0c63098197212 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 24 Jan 2024 14:02:25 -0500 Subject: [PATCH 071/244] Cleanup: Remove redundant comment This fact is mentioned in the docs of #BKE_paint_brush_asset_set, no need to duplicate the information. --- source/blender/editors/sculpt_paint/curves_sculpt_ops.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc index f597dd89014..b6bd09c7b79 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc @@ -1181,7 +1181,6 @@ static int brush_asset_select_exec(bContext *C, wmOperator *op) ToolSettings *tool_settings = CTX_data_tool_settings(C); - /* Either takes ownership of the brush_asset_reference, or frees it. */ if (!BKE_paint_brush_asset_set( &tool_settings->curves_sculpt->paint, brush, brush_asset_reference)) { -- 2.30.2 From dcc1894b89fedd012beffb1482fd5c1bfea6ed1f Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 25 Jan 2024 17:10:29 +0100 Subject: [PATCH 072/244] Report/confirm overwrite of 'asset library' blendfiles. This commit extends the reporting and overwrite confirmation system created for blendfiles saved by later Blender version, to also detect and handle 'asset library' blendfiles. Asset library blendfiles are defined by: * Not having a defined current Screen or Scene in their global data. * Having a filename ending by `.asset.blend`. This is required to avoid/warn users about editing a file that would have been generated in the 'default' asset library, e.g. when saving a new Brush asset (see also Part of #116337 and #117239). Pull Request: https://projects.blender.org/blender/blender/pulls/117346 --- source/blender/blenkernel/BKE_blendfile.hh | 5 + source/blender/blenkernel/BKE_main.hh | 19 ++ source/blender/blenkernel/intern/blendfile.cc | 3 + source/blender/blenkernel/intern/main.cc | 10 ++ .../editors/interface/interface_templates.cc | 132 ++++++++------ .../blender/windowmanager/intern/wm_files.cc | 162 +++++++++++------- source/blender/windowmanager/wm_files.hh | 2 +- 7 files changed, 226 insertions(+), 107 deletions(-) diff --git a/source/blender/blenkernel/BKE_blendfile.hh b/source/blender/blenkernel/BKE_blendfile.hh index cdc26d7776c..f55eb005817 100644 --- a/source/blender/blenkernel/BKE_blendfile.hh +++ b/source/blender/blenkernel/BKE_blendfile.hh @@ -19,6 +19,11 @@ struct ReportList; struct UserDef; struct WorkspaceConfigFileData; +/** + * The suffix used for blendfiles managed by the asset system. + */ +#define BLENDER_ASSET_FILE_SUFFIX ".asset.blend" + /** * Check whether given path ends with a blend file compatible extension * (`.blend`, `.ble` or `.blend.gz`). diff --git a/source/blender/blenkernel/BKE_main.hh b/source/blender/blenkernel/BKE_main.hh index f7a25b9e7c5..89efb43e9d4 100644 --- a/source/blender/blenkernel/BKE_main.hh +++ b/source/blender/blenkernel/BKE_main.hh @@ -142,6 +142,14 @@ struct Main { * could try to use more refined detection on load. */ bool has_forward_compatibility_issues; + /** + * The currently opened .blend file was created as an asset library storage. + * + * This is used to warn the user when they try to save it from Blender UI, since this will likely + * break the automatic management from the asset library system. + */ + bool is_asset_repository; + /** Commit timestamp from `buildinfo`. */ uint64_t build_commit_timestamp; /** Commit Hash from `buildinfo`. */ @@ -299,6 +307,17 @@ void BKE_main_merge(Main *bmain_dst, Main **r_bmain_src, MainMergeReport &report */ bool BKE_main_is_empty(Main *bmain); +/** + * Check whether the bmain has issues, e.g. for reporting in the status bar. + */ +bool BKE_main_has_issues(const Main *bmain); + +/** + * Check whether user confirmation should be required when overwriting this `bmain` into its source + * blendfile. + */ +bool BKE_main_needs_overwrite_confirm(const Main *bmain); + void BKE_main_lock(Main *bmain); void BKE_main_unlock(Main *bmain); diff --git a/source/blender/blenkernel/intern/blendfile.cc b/source/blender/blenkernel/intern/blendfile.cc index 3c0efaea219..0156bc7f33f 100644 --- a/source/blender/blenkernel/intern/blendfile.cc +++ b/source/blender/blenkernel/intern/blendfile.cc @@ -853,6 +853,9 @@ static void setup_app_data(bContext *C, * nullptr curscreen)... */ else if (ELEM(nullptr, bfd->curscreen, bfd->curscene)) { BKE_report(reports->reports, RPT_WARNING, "Library file, loading empty scene"); + if (blender::StringRefNull(bfd->main->filepath).endswith(BLENDER_ASSET_FILE_SUFFIX)) { + bfd->main->is_asset_repository = true; + } mode = LOAD_UI_OFF; } else if (G.fileflags & G_FILE_NO_UI) { diff --git a/source/blender/blenkernel/intern/main.cc b/source/blender/blenkernel/intern/main.cc index ae4d61fe5bb..373059a3c47 100644 --- a/source/blender/blenkernel/intern/main.cc +++ b/source/blender/blenkernel/intern/main.cc @@ -444,6 +444,16 @@ bool BKE_main_is_empty(Main *bmain) return result; } +bool BKE_main_has_issues(const Main *bmain) +{ + return bmain->has_forward_compatibility_issues || bmain->is_asset_repository; +} + +bool BKE_main_needs_overwrite_confirm(const Main *bmain) +{ + return bmain->has_forward_compatibility_issues || bmain->is_asset_repository; +} + void BKE_main_lock(Main *bmain) { BLI_spin_lock((SpinLock *)bmain->lock); diff --git a/source/blender/editors/interface/interface_templates.cc b/source/blender/editors/interface/interface_templates.cc index cd126c59271..baf25003bcd 100644 --- a/source/blender/editors/interface/interface_templates.cc +++ b/source/blender/editors/interface/interface_templates.cc @@ -6558,13 +6558,47 @@ void uiTemplateInputStatus(uiLayout *layout, bContext *C) } } +static void ui_template_status_info_warnings_messages(Main *bmain, + Scene *scene, + ViewLayer *view_layer, + std::string &warning_message, + std::string ®ular_message, + std::string &tooltip_message) +{ + tooltip_message = ""; + char statusbar_info_flag = U.statusbar_flag; + + if (bmain->has_forward_compatibility_issues) { + warning_message = ED_info_statusbar_string_ex( + bmain, scene, view_layer, STATUSBAR_SHOW_VERSION); + statusbar_info_flag &= ~STATUSBAR_SHOW_VERSION; + + char writer_ver_str[12]; + BKE_blender_version_blendfile_string_from_values( + writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, -1); + tooltip_message += fmt::format(RPT_("File saved by newer Blender\n({}), expect loss of data"), + writer_ver_str); + } + if (bmain->is_asset_repository) { + if (!tooltip_message.empty()) { + tooltip_message += "\n\n"; + } + tooltip_message += RPT_( + "This file is managed by the Blender asset system\n" + "By editing it as a regular blend file, it will no longer\n" + "be possible to update its assets through the asset browser"); + } + + regular_message = ED_info_statusbar_string_ex(bmain, scene, view_layer, statusbar_info_flag); +} + void uiTemplateStatusInfo(uiLayout *layout, bContext *C) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); - if (!bmain->has_forward_compatibility_issues) { + if (!BKE_main_has_issues(bmain)) { const char *status_info_txt = ED_info_statusbar_string(bmain, scene, view_layer); uiItemL(layout, status_info_txt, ICON_NONE); return; @@ -6573,13 +6607,13 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C) /* Blender version part is shown as warning area when there are forward compatibility issues with * currently loaded .blend file. */ - const char *status_info_txt = ED_info_statusbar_string_ex( - bmain, scene, view_layer, (U.statusbar_flag & ~STATUSBAR_SHOW_VERSION)); - uiItemL(layout, status_info_txt, ICON_NONE); + std::string warning_message; + std::string regular_message; + std::string tooltip_message; + ui_template_status_info_warnings_messages( + bmain, scene, view_layer, warning_message, regular_message, tooltip_message); - status_info_txt = ED_info_statusbar_string_ex(bmain, scene, view_layer, STATUSBAR_SHOW_VERSION); - - uiBut *but; + uiItemL(layout, regular_message.c_str(), ICON_NONE); const uiStyle *style = UI_style_get(); uiLayout *ui_abs = uiLayoutAbsolute(layout, false); @@ -6587,27 +6621,28 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C) eUIEmbossType previous_emboss = UI_block_emboss_get(block); UI_fontstyle_set(&style->widgetlabel); - int width = int( - BLF_width(style->widgetlabel.uifont_id, status_info_txt, strlen(status_info_txt))); - width = max_ii(width, int(10 * UI_SCALE_FAC)); + const int width = max_ii(int(BLF_width(style->widgetlabel.uifont_id, + warning_message.c_str(), + warning_message.length())), + int(10 * UI_SCALE_FAC)); UI_block_align_begin(block); /* Background for icon. */ - but = uiDefBut(block, - UI_BTYPE_ROUNDBOX, - 0, - "", - 0, - 0, - UI_UNIT_X + (6 * UI_SCALE_FAC), - UI_UNIT_Y, - nullptr, - 0.0f, - 0.0f, - 0, - 0, - ""); + uiBut *but = uiDefBut(block, + UI_BTYPE_ROUNDBOX, + 0, + "", + 0, + 0, + UI_UNIT_X + (6 * UI_SCALE_FAC), + UI_UNIT_Y, + nullptr, + 0.0f, + 0.0f, + 0, + 0, + ""); /* UI_BTYPE_ROUNDBOX's bg color is set in but->col. */ UI_GetThemeColorType4ubv(TH_INFO_WARNING, SPACE_INFO, but->col); @@ -6634,14 +6669,13 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C) UI_block_align_end(block); UI_block_emboss_set(block, UI_EMBOSS_NONE); - /* The report icon itself. */ - static char compat_error_msg[256]; - char writer_ver_str[12]; - BKE_blender_version_blendfile_string_from_values( - writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, -1); - SNPRINTF(compat_error_msg, - RPT_("File saved by newer Blender\n(%s), expect loss of data"), - writer_ver_str); + /* Tool tips have to be static currently. + * FIXME This is a horrible requirement from uiBut, should probably just store an std::string for + * the tooltip as well? */ + static char tooltip_static_storage[256]; + BLI_strncpy(tooltip_static_storage, tooltip_message.c_str(), sizeof(tooltip_static_storage)); + + /* The warning icon itself. */ but = uiDefIconBut(block, UI_BTYPE_BUT, 0, @@ -6655,25 +6689,27 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C) 0.0f, 0.0f, 0.0f, - compat_error_msg); + tooltip_static_storage); UI_GetThemeColorType4ubv(TH_INFO_WARNING_TEXT, SPACE_INFO, but->col); but->col[3] = 255; /* This theme color is RBG only, so have to set alpha here. */ - /* The report message. */ - but = uiDefBut(block, - UI_BTYPE_BUT, - 0, - status_info_txt, - UI_UNIT_X, - 0, - short(width + UI_UNIT_X), - UI_UNIT_Y, - nullptr, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - compat_error_msg); + /* The warning message, if any. */ + if (!warning_message.empty()) { + but = uiDefBut(block, + UI_BTYPE_BUT, + 0, + warning_message.c_str(), + UI_UNIT_X, + 0, + short(width + UI_UNIT_X), + UI_UNIT_Y, + nullptr, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + tooltip_static_storage); + } UI_block_emboss_set(block, previous_emboss); } diff --git a/source/blender/windowmanager/intern/wm_files.cc b/source/blender/windowmanager/intern/wm_files.cc index 40ff93f63be..3a8b3756a5f 100644 --- a/source/blender/windowmanager/intern/wm_files.cc +++ b/source/blender/windowmanager/intern/wm_files.cc @@ -3552,8 +3552,8 @@ static int wm_save_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent * } if (blendfile_path[0] != '\0') { - if (CTX_data_main(C)->has_forward_compatibility_issues) { - wm_save_file_forwardcompat_dialog(C, op); + if (BKE_main_needs_overwrite_confirm(CTX_data_main(C))) { + wm_save_file_overwrite_dialog(C, op); ret = OPERATOR_INTERFACE; } else { @@ -3907,49 +3907,62 @@ static void wm_free_operator_properties_callback(void *user_data) IDP_FreeProperty(properties); } -static const char *save_file_forwardcompat_dialog_name = "save_file_forwardcompat_popup"; +static const char *save_file_overwrite_dialog_name = "save_file_overwrite_popup"; -static void file_forwardcompat_detailed_info_show(uiLayout *parent_layout, Main *bmain) +static void file_overwrite_detailed_info_show(uiLayout *parent_layout, Main *bmain) { uiLayout *layout = uiLayoutColumn(parent_layout, true); /* Trick to make both lines of text below close enough to look like they are part of a same * block. */ uiLayoutSetScaleY(layout, 0.70f); - char writer_ver_str[16]; - char current_ver_str[16]; - if (bmain->versionfile == BLENDER_VERSION) { - BKE_blender_version_blendfile_string_from_values( - writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, bmain->subversionfile); - BKE_blender_version_blendfile_string_from_values( - current_ver_str, sizeof(current_ver_str), BLENDER_FILE_VERSION, BLENDER_FILE_SUBVERSION); - } - else { - BKE_blender_version_blendfile_string_from_values( - writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, -1); - BKE_blender_version_blendfile_string_from_values( - current_ver_str, sizeof(current_ver_str), BLENDER_VERSION, -1); + if (bmain->has_forward_compatibility_issues) { + char writer_ver_str[16]; + char current_ver_str[16]; + if (bmain->versionfile == BLENDER_VERSION) { + BKE_blender_version_blendfile_string_from_values( + writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, bmain->subversionfile); + BKE_blender_version_blendfile_string_from_values( + current_ver_str, sizeof(current_ver_str), BLENDER_FILE_VERSION, BLENDER_FILE_SUBVERSION); + } + else { + BKE_blender_version_blendfile_string_from_values( + writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, -1); + BKE_blender_version_blendfile_string_from_values( + current_ver_str, sizeof(current_ver_str), BLENDER_VERSION, -1); + } + + char message_line1[256]; + char message_line2[256]; + SNPRINTF(message_line1, + RPT_("This file was saved by a newer version of Blender (%s)"), + writer_ver_str); + SNPRINTF(message_line2, + RPT_("Saving it with this Blender (%s) may cause loss of data"), + current_ver_str); + uiItemL(layout, message_line1, ICON_NONE); + uiItemL(layout, message_line2, ICON_NONE); } - char message_line1[256]; - char message_line2[256]; - SNPRINTF(message_line1, - RPT_("This file was saved by a newer version of Blender (%s)"), - writer_ver_str); - SNPRINTF(message_line2, - RPT_("Saving it with this Blender (%s) may cause loss of data"), - current_ver_str); - uiItemL(layout, message_line1, ICON_NONE); - uiItemL(layout, message_line2, ICON_NONE); + if (bmain->is_asset_repository) { + if (bmain->has_forward_compatibility_issues) { + uiItemS_ex(layout, 1.4f); + } + + uiItemL(layout, RPT_("This file is managed by the Blender asset system"), ICON_NONE); + uiItemL( + layout, RPT_("By overwriting it as a regular blend file, it will no longer "), ICON_NONE); + uiItemL(layout, RPT_("be possible to update its assets through the asset browser"), ICON_NONE); + } } -static void save_file_forwardcompat_cancel(bContext *C, void *arg_block, void * /*arg_data*/) +static void save_file_overwrite_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 save_file_forwardcompat_cancel_button(uiBlock *block, wmGenericCallback *post_action) +static void save_file_overwrite_cancel_button(uiBlock *block, wmGenericCallback *post_action) { uiBut *but = uiDefIconTextBut(block, UI_BTYPE_BUT, @@ -3966,11 +3979,11 @@ static void save_file_forwardcompat_cancel_button(uiBlock *block, wmGenericCallb 0, 0, ""); - UI_but_func_set(but, save_file_forwardcompat_cancel, block, post_action); + UI_but_func_set(but, save_file_overwrite_cancel, block, post_action); UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT); } -static void save_file_forwardcompat_overwrite(bContext *C, void *arg_block, void *arg_data) +static void save_file_overwrite_confirm(bContext *C, void *arg_block, void *arg_data) { wmWindow *win = CTX_wm_window(C); @@ -3993,8 +4006,7 @@ static void save_file_forwardcompat_overwrite(bContext *C, void *arg_block, void WM_generic_callback_free(callback); } -static void save_file_forwardcompat_overwrite_button(uiBlock *block, - wmGenericCallback *post_action) +static void save_file_overwrite_confirm_button(uiBlock *block, wmGenericCallback *post_action) { uiBut *but = uiDefIconTextBut(block, UI_BTYPE_BUT, @@ -4011,20 +4023,37 @@ static void save_file_forwardcompat_overwrite_button(uiBlock *block, 0, 0, ""); - UI_but_func_set(but, save_file_forwardcompat_overwrite, block, post_action); + UI_but_func_set(but, save_file_overwrite_confirm, block, post_action); UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT); UI_but_flag_enable(but, UI_BUT_REDALERT); } -static void save_file_forwardcompat_saveas(bContext *C, void *arg_block, void * /*arg_data*/) +static void save_file_overwrite_saveas(bContext *C, void *arg_block, void * /*arg_data*/) { + Main *bmain = CTX_data_main(C); wmWindow *win = CTX_wm_window(C); UI_popup_block_close(C, win, static_cast(arg_block)); - WM_operator_name_call(C, "WM_OT_save_as_mainfile", WM_OP_INVOKE_DEFAULT, nullptr, nullptr); + PointerRNA props_ptr; + wmOperatorType *ot = WM_operatortype_find("WM_OT_save_as_mainfile", false); + WM_operator_properties_create_ptr(&props_ptr, ot); + + if (bmain->is_asset_repository) { + /* If needed, substitute the 'proposed' Save As filepath by replacing the `.asset.blend` part + * of it by just `.blend`. */ + std::string filepath = BKE_main_blendfile_path(bmain); + if (blender::StringRef(filepath).endswith(BLENDER_ASSET_FILE_SUFFIX)) { + filepath.replace( + filepath.rfind(BLENDER_ASSET_FILE_SUFFIX), strlen(BLENDER_ASSET_FILE_SUFFIX), ".blend"); + RNA_string_set(&props_ptr, "filepath", filepath.c_str()); + } + } + + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr, nullptr); + WM_operator_properties_free(&props_ptr); } -static void save_file_forwardcompat_saveas_button(uiBlock *block, wmGenericCallback *post_action) +static void save_file_overwrite_saveas_button(uiBlock *block, wmGenericCallback *post_action) { uiBut *but = uiDefIconTextBut(block, UI_BTYPE_BUT, @@ -4041,19 +4070,17 @@ static void save_file_forwardcompat_saveas_button(uiBlock *block, wmGenericCallb 0, 0, ""); - UI_but_func_set(but, save_file_forwardcompat_saveas, block, post_action); + UI_but_func_set(but, save_file_overwrite_saveas, block, post_action); UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT); UI_but_flag_enable(but, UI_BUT_ACTIVE_DEFAULT); } -static uiBlock *block_create_save_file_forwardcompat_dialog(bContext *C, - ARegion *region, - void *arg1) +static uiBlock *block_create_save_file_overwrite_dialog(bContext *C, ARegion *region, void *arg1) { wmGenericCallback *post_action = static_cast(arg1); Main *bmain = CTX_data_main(C); - uiBlock *block = UI_block_begin(C, region, save_file_forwardcompat_dialog_name, UI_EMBOSS); + uiBlock *block = UI_block_begin(C, region, save_file_overwrite_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); @@ -4061,8 +4088,27 @@ static uiBlock *block_create_save_file_forwardcompat_dialog(bContext *C, uiLayout *layout = uiItemsAlertBox(block, 34, ALERT_ICON_WARNING); /* Title. */ - uiItemL_ex( - layout, RPT_("Overwrite file with an older Blender version?"), ICON_NONE, true, false); + if (bmain->has_forward_compatibility_issues) { + if (bmain->is_asset_repository) { + uiItemL_ex( + layout, + RPT_("Convert asset blend file to regular blend file with an older Blender version?"), + ICON_NONE, + true, + false); + } + else { + uiItemL_ex( + layout, RPT_("Overwrite file with an older Blender version?"), ICON_NONE, true, false); + } + } + else if (bmain->is_asset_repository) { + uiItemL_ex( + layout, RPT_("Convert asset blend file to regular blend file?"), ICON_NONE, true, false); + } + else { + BLI_assert_unreachable(); + } /* Filename. */ const char *blendfile_path = BKE_main_blendfile_path(CTX_data_main(C)); @@ -4079,7 +4125,7 @@ static uiBlock *block_create_save_file_forwardcompat_dialog(bContext *C, uiItemL(layout, filename, ICON_NONE); /* Detailed message info. */ - file_forwardcompat_detailed_info_show(layout, bmain); + file_overwrite_detailed_info_show(layout, bmain); uiItemS_ex(layout, 4.0f); @@ -4089,7 +4135,7 @@ static uiBlock *block_create_save_file_forwardcompat_dialog(bContext *C, uiLayoutSetScaleY(split, 1.2f); uiLayoutColumn(split, false); - save_file_forwardcompat_overwrite_button(block, post_action); + save_file_overwrite_confirm_button(block, post_action); uiLayout *split_right = uiLayoutSplit(split, 0.1f, true); @@ -4097,25 +4143,25 @@ static uiBlock *block_create_save_file_forwardcompat_dialog(bContext *C, /* Empty space. */ uiLayoutColumn(split_right, false); - save_file_forwardcompat_cancel_button(block, post_action); + save_file_overwrite_cancel_button(block, post_action); uiLayoutColumn(split_right, false); - save_file_forwardcompat_saveas_button(block, post_action); + save_file_overwrite_saveas_button(block, post_action); UI_block_bounds_set_centered(block, 14 * UI_SCALE_FAC); return block; } -void wm_save_file_forwardcompat_dialog(bContext *C, wmOperator *op) +void wm_save_file_overwrite_dialog(bContext *C, wmOperator *op) { - if (!UI_popup_block_name_exists(CTX_wm_screen(C), save_file_forwardcompat_dialog_name)) { + if (!UI_popup_block_name_exists(CTX_wm_screen(C), save_file_overwrite_dialog_name)) { wmGenericCallback *callback = MEM_cnew(__func__); callback->exec = nullptr; callback->user_data = IDP_CopyProperty(op->properties); callback->free_user_data = wm_free_operator_properties_callback; UI_popup_block_invoke( - C, block_create_save_file_forwardcompat_dialog, callback, free_post_file_close_action); + C, block_create_save_file_overwrite_dialog, callback, free_post_file_close_action); } } @@ -4245,7 +4291,7 @@ static void wm_block_file_close_discard_button(uiBlock *block, wmGenericCallback static void wm_block_file_close_save_button(uiBlock *block, wmGenericCallback *post_action, - const bool has_forwardcompat_issues) + const bool needs_overwrite_confirm) { uiBut *but = uiDefIconTextBut( block, @@ -4253,7 +4299,7 @@ static void wm_block_file_close_save_button(uiBlock *block, 0, ICON_NONE, /* Forward compatibility issues force using 'save as' operator instead of 'save' one. */ - has_forwardcompat_issues ? IFACE_("Save As...") : IFACE_("Save"), + needs_overwrite_confirm ? IFACE_("Save As...") : IFACE_("Save"), 0, 0, 0, @@ -4289,7 +4335,7 @@ static uiBlock *block_create__close_file_dialog(bContext *C, ARegion *region, vo uiLayout *layout = uiItemsAlertBox(block, 34, ALERT_ICON_QUESTION); - const bool has_forwardcompat_issues = bmain->has_forward_compatibility_issues; + const bool needs_overwrite_confirm = BKE_main_needs_overwrite_confirm(bmain); /* Title. */ uiItemL_ex(layout, RPT_("Save changes before closing?"), ICON_NONE, true, false); @@ -4306,8 +4352,8 @@ static uiBlock *block_create__close_file_dialog(bContext *C, ARegion *region, vo uiItemL(layout, filename, ICON_NONE); /* Potential forward compatibility issues message. */ - if (has_forwardcompat_issues) { - file_forwardcompat_detailed_info_show(layout, bmain); + if (needs_overwrite_confirm) { + file_overwrite_detailed_info_show(layout, bmain); } /* Image Saving Warnings. */ @@ -4416,7 +4462,7 @@ static uiBlock *block_create__close_file_dialog(bContext *C, ARegion *region, vo uiLayoutSetScaleY(split, 1.2f); uiLayoutColumn(split, false); - wm_block_file_close_save_button(block, post_action, has_forwardcompat_issues); + wm_block_file_close_save_button(block, post_action, needs_overwrite_confirm); uiLayoutColumn(split, false); wm_block_file_close_discard_button(block, post_action); @@ -4442,7 +4488,7 @@ static uiBlock *block_create__close_file_dialog(bContext *C, ARegion *region, vo wm_block_file_close_cancel_button(block, post_action); uiLayoutColumn(split_right, false); - wm_block_file_close_save_button(block, post_action, has_forwardcompat_issues); + wm_block_file_close_save_button(block, post_action, needs_overwrite_confirm); } UI_block_bounds_set_centered(block, 14 * UI_SCALE_FAC); diff --git a/source/blender/windowmanager/wm_files.hh b/source/blender/windowmanager/wm_files.hh index d62ef68dc27..26a63fcc65a 100644 --- a/source/blender/windowmanager/wm_files.hh +++ b/source/blender/windowmanager/wm_files.hh @@ -97,7 +97,7 @@ bool wm_file_or_session_data_has_unsaved_changes(const Main *bmain, const wmWind * * Important to ask confirmation, as this is a very common scenario of data loss. */ -void wm_save_file_forwardcompat_dialog(bContext *C, wmOperator *op); +void wm_save_file_overwrite_dialog(bContext *C, wmOperator *op); void WM_OT_save_homefile(wmOperatorType *ot); void WM_OT_save_userpref(wmOperatorType *ot); -- 2.30.2 From 65fce64213364b7ab46bc52610524394e53027fe Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 26 Jan 2024 21:08:13 +0100 Subject: [PATCH 073/244] Brush Assets: Switch to a single "Brush" active tool Instead of showing an active tool for every brush type, just show a single active tool and rely on the asset shelf to select different brushes. A single generic "Brush" icon is used instead of the tool icons. That avoids raising many questions in the future as we get more brushes. Pull Request: https://projects.blender.org/blender/blender/pulls/117491 --- .../startup/bl_ui/space_toolsystem_toolbar.py | 65 ++++++++++++------- .../editors/sculpt_paint/curves_sculpt_ops.cc | 1 + .../blender/editors/sculpt_paint/paint_ops.cc | 59 +---------------- .../windowmanager/intern/wm_toolsystem.cc | 3 +- 4 files changed, 44 insertions(+), 84 deletions(-) diff --git a/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 4a97fa13400..c7ab5bbbe9a 100644 --- a/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -1383,17 +1383,23 @@ class _defs_particle: class _defs_sculpt: @staticmethod - def generate_from_brushes(context): - return generate_from_enum_ex( - context, - idname_prefix="builtin_brush.", - icon_prefix="brush.sculpt.", - type=bpy.types.Brush, - attr="sculpt_tool", - # TODO(@ideasman42): we may want to enable this, - # it causes awkward grouping with 2x column button layout. - use_separators=False, - ) + def generate_brush_tool(context): + if not context: + return () + brush = context.tool_settings.sculpt.brush + if not brush: + return () + tool = brush.sculpt_tool + return [ + ToolDef.from_dict( + dict( + idname="builtin.brush", + label=brush.name, + icon="brush.sculpt.paint", + data_block=tool + ) + ) + ] @ToolDef.from_fn def hide_border(): @@ -2442,18 +2448,23 @@ class _defs_gpencil_weight: class _defs_curves_sculpt: @staticmethod - def generate_from_brushes(context): - return generate_from_enum_ex( - context, - idname_prefix="builtin_brush.", - icon_prefix="ops.curves.sculpt_", - type=bpy.types.Brush, - attr="curves_sculpt_tool", - icon_map={ - # Use the generic icon for selection painting. - "ops.curves.sculpt_selection_paint": "ops.generic.select_paint", - }, - ) + def generate_brush_tool(context): + if not context: + return () + brush = context.tool_settings.curves_sculpt.brush + if not brush: + return () + tool = brush.curves_sculpt_tool + return [ + ToolDef.from_dict( + dict( + idname="builtin.brush", + label=brush.name, + icon="brush.sculpt.paint", + data_block=tool + ) + ) + ] class _defs_gpencil_vertex: @@ -3089,7 +3100,9 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): _defs_particle.generate_from_brushes, ], 'SCULPT': [ - _defs_sculpt.generate_from_brushes, + lambda context: ( + _defs_sculpt.generate_brush_tool(context) + ), None, ( _defs_sculpt.mask_border, @@ -3234,7 +3247,9 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): ), ], 'SCULPT_CURVES': [ - _defs_curves_sculpt.generate_from_brushes, + lambda context: ( + _defs_curves_sculpt.generate_brush_tool(context) + ), None, *_tools_annotate, ], diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc index 6b96a5cb757..6b3e979b144 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc @@ -1188,6 +1188,7 @@ static int brush_asset_select_exec(bContext *C, wmOperator *op) } WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, nullptr); + WM_toolsystem_ref_set_by_id(C, "builtin.brush"); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 755649d4881..98f0dd0907c 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -849,36 +849,6 @@ static Brush *brush_tool_toggle(Main *bmain, Paint *paint, Brush *brush_orig, co return nullptr; } -/** The name of the active tool is "builtin_brush." concatenated with the returned string. */ -static blender::StringRefNull curves_active_tool_name_get(const eBrushCurvesSculptTool tool) -{ - switch (tool) { - case CURVES_SCULPT_TOOL_COMB: - return "comb"; - case CURVES_SCULPT_TOOL_DELETE: - return "delete"; - case CURVES_SCULPT_TOOL_SNAKE_HOOK: - return "snake_hook"; - case CURVES_SCULPT_TOOL_ADD: - return "add"; - case CURVES_SCULPT_TOOL_GROW_SHRINK: - return "grow_shrink"; - case CURVES_SCULPT_TOOL_SELECTION_PAINT: - return "selection_paint"; - case CURVES_SCULPT_TOOL_PINCH: - return "pinch"; - case CURVES_SCULPT_TOOL_SMOOTH: - return "smooth"; - case CURVES_SCULPT_TOOL_PUFF: - return "puff"; - case CURVES_SCULPT_TOOL_DENSITY: - return "density"; - case CURVES_SCULPT_TOOL_SLIDE: - return "slide"; - } - return ""; -} - static bool brush_generic_tool_set(bContext *C, Main *bmain, Paint *paint, @@ -910,26 +880,7 @@ static bool brush_generic_tool_set(bContext *C, BKE_paint_invalidate_overlay_all(); WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush); - - /* Tool System - * This is needed for when there is a non-sculpt tool active (transform for e.g.). - * In case we are toggling (and the brush changed to the toggle_brush), we need to get the - * tool_name again. */ - int tool_result = brush_tool(brush, paint->runtime.tool_offset); - ePaintMode paint_mode = BKE_paintmode_get_active_from_context(C); - - if (paint_mode == PAINT_MODE_SCULPT_CURVES) { - tool_name = curves_active_tool_name_get(eBrushCurvesSculptTool(tool)).c_str(); - } - else { - const EnumPropertyItem *items = BKE_paint_get_tool_enum_from_paintmode(paint_mode); - RNA_enum_name_from_value(items, tool_result, &tool_name); - } - - char tool_id[MAX_NAME]; - SNPRINTF(tool_id, "builtin_brush.%s", tool_name); - WM_toolsystem_ref_set_by_id(C, tool_id); - + WM_toolsystem_ref_set_by_id(C, "builtin.brush"); return true; } return false; @@ -976,14 +927,6 @@ static int brush_select_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - if (paint_mode == PAINT_MODE_SCULPT_CURVES) { - tool_name = curves_active_tool_name_get(eBrushCurvesSculptTool(tool)).c_str(); - } - else { - const EnumPropertyItem *items = BKE_paint_get_tool_enum_from_paintmode(paint_mode); - RNA_enum_name_from_value(items, tool, &tool_name); - } - if (brush_generic_tool_set(C, bmain, paint, tool, tool_name, create_missing, toggle)) { return OPERATOR_FINISHED; } diff --git a/source/blender/windowmanager/intern/wm_toolsystem.cc b/source/blender/windowmanager/intern/wm_toolsystem.cc index 6d796d2a923..03804cc9a3f 100644 --- a/source/blender/windowmanager/intern/wm_toolsystem.cc +++ b/source/blender/windowmanager/intern/wm_toolsystem.cc @@ -713,6 +713,7 @@ static const char *toolsystem_default_tool(const bToolKey *tkey) switch (tkey->mode) { /* Use the names of the enums for each brush tool. */ case CTX_MODE_SCULPT: + return "builtin.brush"; case CTX_MODE_PAINT_VERTEX: case CTX_MODE_PAINT_WEIGHT: case CTX_MODE_PAINT_TEXTURE: @@ -726,7 +727,7 @@ static const char *toolsystem_default_tool(const bToolKey *tkey) case CTX_MODE_VERTEX_GPENCIL_LEGACY: return "builtin_brush.Draw"; case CTX_MODE_SCULPT_CURVES: - return "builtin_brush.Density"; + return "builtin.brush"; /* end temporary hack. */ case CTX_MODE_PARTICLE: -- 2.30.2 From 39e40f6371857c401de76778e13c69dec6925e97 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 26 Jan 2024 17:14:19 +0100 Subject: [PATCH 074/244] Brush Assets: make asset select operator work with all paint modes --- .../keyconfig/keymap_data/blender_default.py | 2 +- .../editors/sculpt_paint/curves_sculpt_ops.cc | 52 ------------------- .../blender/editors/sculpt_paint/paint_ops.cc | 50 ++++++++++++++++++ 3 files changed, 51 insertions(+), 53 deletions(-) diff --git a/scripts/presets/keyconfig/keymap_data/blender_default.py b/scripts/presets/keyconfig/keymap_data/blender_default.py index 5675e12ac12..78c107f4dff 100644 --- a/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -6792,7 +6792,7 @@ def km_asset_shelf_brushes(_params): ) items.extend([ - ("sculpt_curves.brush_asset_select", {"type": 'LEFTMOUSE', "value": 'CLICK'}, None), + ("brush.asset_select", {"type": 'LEFTMOUSE', "value": 'CLICK'}, None), ]) return keymap diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc index 6b3e979b144..017c28cc0c8 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc @@ -8,9 +8,7 @@ #include "BLI_utildefines.h" #include "BLI_vector_set.hh" -#include "BKE_asset.hh" #include "BKE_attribute.hh" -#include "BKE_blendfile.hh" #include "BKE_brush.hh" #include "BKE_bvhutils.hh" #include "BKE_context.hh" @@ -18,13 +16,11 @@ #include "BKE_modifier.hh" #include "BKE_object.hh" #include "BKE_paint.hh" -#include "BKE_report.h" #include "WM_api.hh" #include "WM_message.hh" #include "WM_toolsystem.hh" -#include "ED_asset_handle.h" #include "ED_curves.hh" #include "ED_curves_sculpt.hh" #include "ED_image.hh" @@ -33,8 +29,6 @@ #include "ED_space_api.hh" #include "ED_view3d.hh" -#include "AS_asset_representation.hh" - #include "DEG_depsgraph.hh" #include "DEG_depsgraph_query.hh" @@ -1162,50 +1156,6 @@ static void SCULPT_CURVES_OT_min_distance_edit(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_DEPENDS_ON_CURSOR; } -/* -------------------------------------------------------------------- */ - -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. */ - asset_system::AssetRepresentation *asset = CTX_wm_asset(C); - if (!asset) { - return OPERATOR_CANCELLED; - } - - AssetWeakReference *brush_asset_reference = asset->make_weak_reference(); - Brush *brush = BKE_brush_asset_runtime_ensure(CTX_data_main(C), brush_asset_reference); - - ToolSettings *tool_settings = CTX_data_tool_settings(C); - - if (!BKE_paint_brush_asset_set( - &tool_settings->curves_sculpt->paint, brush, brush_asset_reference)) - { - /* Note brush datablock was still added, so was not a no-op. */ - BKE_report(op->reports, RPT_WARNING, "Unable to select brush, wrong object mode"); - return OPERATOR_FINISHED; - } - - WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, nullptr); - WM_toolsystem_ref_set_by_id(C, "builtin.brush"); - - return OPERATOR_FINISHED; -} - -static void SCULPT_CURVES_OT_brush_asset_select(wmOperatorType *ot) -{ - ot->name = "Select Brush Asset"; - ot->description = "Select a brush asset as currently sculpt/paint tool - TESTING PURPOSE ONLY"; - ot->idname = "SCULPT_CURVES_OT_brush_asset_select"; - - ot->exec = brush_asset_select_exec; - ot->poll = curves_sculpt_poll; - - ot->prop = RNA_def_string( - ot->srna, "name", nullptr, MAX_NAME, "Brush Name", "Name of the brush asset to select"); -} - } // namespace blender::ed::sculpt_paint /* -------------------------------------------------------------------- */ @@ -1220,8 +1170,6 @@ void ED_operatortypes_sculpt_curves() WM_operatortype_append(SCULPT_CURVES_OT_select_random); WM_operatortype_append(SCULPT_CURVES_OT_select_grow); WM_operatortype_append(SCULPT_CURVES_OT_min_distance_edit); - - WM_operatortype_append(SCULPT_CURVES_OT_brush_asset_select); } /** \} */ diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 98f0dd0907c..3ef678b1b66 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -45,6 +45,8 @@ #include "RNA_access.hh" #include "RNA_define.hh" +#include "AS_asset_representation.hh" + #include "curves_sculpt_intern.hh" #include "paint_intern.hh" #include "sculpt_intern.hh" @@ -971,6 +973,53 @@ static void PAINT_OT_brush_select(wmOperatorType *ot) RNA_def_property_flag(prop, PropertyFlag(PROP_HIDDEN | PROP_SKIP_SAVE)); } +/**************************** Brush Assets **********************************/ + +static bool brush_asset_poll(bContext *C) +{ + return BKE_paint_get_active_from_context(C) != nullptr; +} + +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. */ + blender::asset_system::AssetRepresentation *asset = CTX_wm_asset(C); + if (!asset) { + return OPERATOR_CANCELLED; + } + + AssetWeakReference *brush_asset_reference = asset->make_weak_reference(); + Brush *brush = BKE_brush_asset_runtime_ensure(CTX_data_main(C), brush_asset_reference); + + Paint *paint = BKE_paint_get_active_from_context(C); + + if (!BKE_paint_brush_asset_set(paint, brush, brush_asset_reference)) { + /* Note brush datablock was still added, so was not a no-op. */ + BKE_report(op->reports, RPT_WARNING, "Unable to select brush, wrong object mode"); + return OPERATOR_FINISHED; + } + + WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, nullptr); + WM_toolsystem_ref_set_by_id(C, "builtin.brush"); + + return OPERATOR_FINISHED; +} + +static void BRUSH_OT_asset_select(wmOperatorType *ot) +{ + ot->name = "Select Brush Asset"; + ot->description = "Select a brush asset as current sculpt and paint tool"; + ot->idname = "BRUSH_OT_asset_select"; + + ot->exec = brush_asset_select_exec; + ot->poll = brush_asset_poll; + + ot->prop = RNA_def_string( + ot->srna, "name", nullptr, MAX_NAME, "Brush Name", "Name of the brush asset to select"); +} + /***** Stencil Control *****/ enum StencilControlMode { @@ -1432,6 +1481,7 @@ void ED_operatortypes_paint() WM_operatortype_append(BRUSH_OT_stencil_control); WM_operatortype_append(BRUSH_OT_stencil_fit_image_aspect); WM_operatortype_append(BRUSH_OT_stencil_reset_transform); + WM_operatortype_append(BRUSH_OT_asset_select); /* NOTE: particle uses a different system, can be added with existing operators in `wm.py`. */ WM_operatortype_append(PAINT_OT_brush_select); -- 2.30.2 From e1a32ba7148374f25769eeecf6461f1d9c2e77cb Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 26 Jan 2024 19:00:34 -0500 Subject: [PATCH 075/244] Fix build errors --- source/blender/blenkernel/BKE_brush.hh | 2 ++ source/blender/blenkernel/intern/brush.cc | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/BKE_brush.hh b/source/blender/blenkernel/BKE_brush.hh index 449abd81a54..64216047234 100644 --- a/source/blender/blenkernel/BKE_brush.hh +++ b/source/blender/blenkernel/BKE_brush.hh @@ -14,6 +14,7 @@ #include "DNA_object_enums.h" enum class PaintMode : int8_t; +struct AssetWeakReference; struct Brush; struct ImBuf; struct ImagePool; @@ -30,6 +31,7 @@ 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); diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index 4a89d1fd824..92a6a4063b2 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -44,7 +44,7 @@ #include "BKE_preview_image.hh" #include "BKE_texture.h" -#include "AS_asset_library.h" +#include "AS_asset_library.hh" #include "IMB_colormanagement.hh" #include "IMB_imbuf.hh" -- 2.30.2 From a0ebef3a931c39644d2c2ca5ce1e26364f39fc92 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 26 Jan 2024 19:18:27 -0500 Subject: [PATCH 076/244] Cleanup: Remove unused property --- source/blender/editors/sculpt_paint/paint_ops.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 3a5d5e6d7b5..63f14d1ec13 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -1015,9 +1015,6 @@ static void BRUSH_OT_asset_select(wmOperatorType *ot) ot->exec = brush_asset_select_exec; ot->poll = brush_asset_poll; - - ot->prop = RNA_def_string( - ot->srna, "name", nullptr, MAX_NAME, "Brush Name", "Name of the brush asset to select"); } /***** Stencil Control *****/ -- 2.30.2 From 8e6dbddc5a62a6c148b9983aa8dbedff5ee7535d Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Sun, 28 Jan 2024 00:01:05 +0100 Subject: [PATCH 077/244] Brush Assets: Save As, Delete, Revert and Update operators This is quickly hacked code to get basic features and user interface testable. A lot more work will be required to make this good. * Add Save As Asset and Delete Asset operators. These create and delete brushes assets in asset blend files in a user asset library. * Update Asset save the current settings to the brush to the asset blend. * Revert to Asset reverts settings to the ones from the asset blend. * Regular datablock management buttons are replaced by this mechanism and hidden from the UI. * Custom icon, modes and tool have been put into a subpanel. These will need to find a better place, but should be out of the way for now. There are many TODO comments in the code, but some main things: * Delete Asset should become undoable, perhaps with a Trash mechanism. * Asset shelf refresh performance is poor and looks flickery. * The brush selector menu lists local, linked and override brushes which is confusing. It should be made to match the asset shelf. * Brush dependencies like textures are not handled yet. * The partial write mechanism and various other parts of this code will need refactoring to be less hacky. ![brush_asset.png](/attachments/100f59cb-4ddf-429c-83d3-39b071c0cb08) Co-authored-by: Brecht Van Lommel Pull Request: https://projects.blender.org/blender/blender/pulls/117513 --- .../startup/bl_ui/properties_paint_common.py | 49 +- scripts/startup/bl_ui/space_view3d.py | 18 - scripts/startup/bl_ui/space_view3d_toolbar.py | 35 +- .../blender/asset_system/AS_asset_library.hh | 5 +- source/blender/blenkernel/BKE_paint.hh | 15 + source/blender/blenkernel/intern/paint.cc | 23 + source/blender/editors/asset/ED_asset_list.hh | 2 +- .../editors/asset/intern/asset_list.cc | 7 +- .../editors/interface/interface_templates.cc | 122 ++--- .../blender/editors/sculpt_paint/paint_ops.cc | 483 +++++++++++++++++- 10 files changed, 645 insertions(+), 114 deletions(-) diff --git a/scripts/startup/bl_ui/properties_paint_common.py b/scripts/startup/bl_ui/properties_paint_common.py index 077fa936cfa..db033038835 100644 --- a/scripts/startup/bl_ui/properties_paint_common.py +++ b/scripts/startup/bl_ui/properties_paint_common.py @@ -147,19 +147,50 @@ class BrushSelectPanel(BrushPanel): brush = settings.brush row = layout.row() - large_preview = True - if large_preview: - row.column().template_ID_preview(settings, "brush", new="brush.add", rows=3, cols=8, hide_buttons=False) - else: - row.column().template_ID(settings, "brush", new="brush.add") + # 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) + + if brush is None: + return + col = row.column() col.menu("VIEW3D_MT_brush_context_menu", icon='DOWNARROW_HLT', text="") - if brush is not None: - col.prop(brush, "use_custom_icon", toggle=True, icon='FILE_IMAGE', text="") + header, panel = layout.panel("customize", default_closed=True) + header.label(text="Customize") + if panel: + panel.use_property_split = True + panel.use_property_decorate = False - if brush.use_custom_icon: - layout.prop(brush, "icon_filepath", text="") + # icon + col = panel.column(heading="Custom Icon", align=True) + row = col.row() + row.prop(brush, "use_custom_icon", text="") + sub = row.row() + sub.active = brush.use_custom_icon + sub.prop(brush, "icon_filepath", text="") + + # brush tool + if context.image_paint_object: + panel.prop(brush, "image_tool") + elif context.vertex_paint_object: + panel.prop(brush, "vertex_tool") + elif context.weight_paint_object: + panel.prop(brush, "weight_tool") + elif context.sculpt_object: + panel.prop(brush, "sculpt_tool") + elif context.tool_settings.curves_sculpt: + panel.prop(brush, "curves_sculpt_tool") + + # brush paint modes + col = panel.column(heading="Modes", align=True) + col.prop(brush, "use_paint_sculpt", text="Sculpt") + col.prop(brush, "use_paint_uv_sculpt", text="UV Sculpt") + col.prop(brush, "use_paint_vertex", text="Vertex Paint") + col.prop(brush, "use_paint_weight", text="Weight Paint") + col.prop(brush, "use_paint_image", text="Texture Paint") + col.prop(brush, "use_paint_sculpt_curves", text="Sculpt Curves") class ColorPalettePanel(BrushPanel): diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index 3fa8ea2c48f..8a72834a5ec 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -3331,23 +3331,6 @@ class VIEW3D_MT_make_links(Menu): layout.operator("object.datalayout_transfer") -class VIEW3D_MT_brush_paint_modes(Menu): - bl_label = "Enabled Modes" - - def draw(self, context): - layout = self.layout - - settings = UnifiedPaintPanel.paint_settings(context) - brush = settings.brush - - layout.prop(brush, "use_paint_sculpt", text="Sculpt") - layout.prop(brush, "use_paint_uv_sculpt", text="UV Sculpt") - layout.prop(brush, "use_paint_vertex", text="Vertex Paint") - layout.prop(brush, "use_paint_weight", text="Weight Paint") - layout.prop(brush, "use_paint_image", text="Texture Paint") - layout.prop(brush, "use_paint_sculpt_curves", text="Sculpt Curves") - - class VIEW3D_MT_paint_vertex(Menu): bl_label = "Paint" @@ -8900,7 +8883,6 @@ classes = ( VIEW3D_MT_object_cleanup, VIEW3D_MT_make_single_user, VIEW3D_MT_make_links, - VIEW3D_MT_brush_paint_modes, VIEW3D_MT_paint_vertex, VIEW3D_MT_hook, VIEW3D_MT_vertex_group, diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index e25bb7d60cb..a42d1cb6b85 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -43,25 +43,30 @@ class VIEW3D_MT_brush_context_menu(Menu): # skip if no active brush if not brush: - layout.label(text="No Brushes currently available", icon='INFO') + layout.label(text="No brush selected", icon='INFO') return - # brush paint modes - layout.menu("VIEW3D_MT_brush_paint_modes") - # brush tool + # 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 context.image_paint_object: - layout.prop_menu_enum(brush, "image_tool") - elif context.vertex_paint_object: - layout.prop_menu_enum(brush, "vertex_tool") - elif context.weight_paint_object: - layout.prop_menu_enum(brush, "weight_tool") - elif context.sculpt_object: - layout.prop_menu_enum(brush, "sculpt_tool") - layout.operator("brush.reset") - elif context.tool_settings.curves_sculpt: - layout.prop_menu_enum(brush, "curves_sculpt_tool") + if is_asset_brush: + layout.operator("brush.asset_save_as", text="Duplicate Asset...", icon='DUPLICATE') + layout.operator("brush.asset_delete", text="Delete Asset") + + layout.separator() + + layout.operator("brush.asset_update", text="Update Asset") + layout.operator("brush.asset_revert", text="Revert to Asset") + + if context.sculpt_object: + layout.operator("brush.reset", text="Reset to Defaults") + else: + layout.operator("brush.asset_save_as", text="Save As Asset...", icon='FILE_TICK') + layout.operator("brush.asset_delete", text="Delete") class VIEW3D_MT_brush_gpencil_context_menu(Menu): diff --git a/source/blender/asset_system/AS_asset_library.hh b/source/blender/asset_system/AS_asset_library.hh index 513fe29d94b..0150af112f2 100644 --- a/source/blender/asset_system/AS_asset_library.hh +++ b/source/blender/asset_system/AS_asset_library.hh @@ -107,6 +107,9 @@ class AssetLibrary { */ static void foreach_loaded(FunctionRef fn, bool include_all_library); + static std::string resolve_asset_weak_reference_to_full_path( + const AssetWeakReference &asset_reference); + void load_catalogs(); /** Load catalogs that have changed on disk. */ @@ -166,8 +169,6 @@ class AssetLibrary { */ AssetIdentifier asset_identifier_from_library(StringRef relative_asset_path); - std::string resolve_asset_weak_reference_to_full_path(const AssetWeakReference &asset_reference); - eAssetLibraryType library_type() const; StringRefNull name() const; StringRefNull root_path() const; diff --git a/source/blender/blenkernel/BKE_paint.hh b/source/blender/blenkernel/BKE_paint.hh index a0842d85966..ea2a9f12daf 100644 --- a/source/blender/blenkernel/BKE_paint.hh +++ b/source/blender/blenkernel/BKE_paint.hh @@ -8,6 +8,8 @@ * \ingroup bke */ +#include + #include "BLI_array.hh" #include "BLI_bit_vector.hh" #include "BLI_math_matrix_types.hh" @@ -203,6 +205,13 @@ Brush *BKE_paint_brush(Paint *paint); const Brush *BKE_paint_brush_for_read(const Paint *p); void BKE_paint_brush_set(Paint *paint, Brush *br); +/** + * Check if the given brush is a valid Brush Asset. + * + * A valid brush Asset is either an actual asset, or a local liboverride of a linked brush asset. + */ +bool BKE_paint_brush_is_valid_asset(const Brush *brush); + /** * Set the active brush of given paint struct, and store the weak asset reference to it. * \note Takes ownership of the given `weak_asset_reference`. @@ -211,6 +220,12 @@ bool BKE_paint_brush_asset_set(Paint *paint, Brush *brush, AssetWeakReference *weak_asset_reference); +/** + * Get the active brush of given paint struct, together with its weak asset reference. + * \note Returns unset optional if the active brush is not a valid Brush Asset data.. + */ +std::optional BKE_paint_brush_asset_get(Paint *paint, Brush **r_brush); + /** * Attempt to restore a valid active brush in `paint` from brush asset information stored in * `paint`. diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 387af177da6..2226f51bf97 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -8,6 +8,7 @@ #include #include +#include #include "MEM_guardedalloc.h" @@ -673,6 +674,13 @@ 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))); +} + static void paint_brush_asset_update(Paint &paint, Brush *brush, AssetWeakReference *brush_asset_reference) @@ -707,6 +715,21 @@ bool BKE_paint_brush_asset_set(Paint *paint, return true; } +std::optional BKE_paint_brush_asset_get(Paint *paint, Brush **r_brush) +{ + Brush *brush = *r_brush = BKE_paint_brush(paint); + + if (!BKE_paint_brush_is_valid_asset(brush)) { + return {}; + } + + if (paint->brush_asset_reference) { + return paint->brush_asset_reference; + } + + return {}; +} + void BKE_paint_brush_asset_restore(Main *bmain, Paint *paint) { if (paint->brush != nullptr) { diff --git a/source/blender/editors/asset/ED_asset_list.hh b/source/blender/editors/asset/ED_asset_list.hh index 0cf919ad419..c57261b754f 100644 --- a/source/blender/editors/asset/ED_asset_list.hh +++ b/source/blender/editors/asset/ED_asset_list.hh @@ -61,7 +61,7 @@ void iterate(const AssetLibraryReference &library_reference, AssetListIterFn fn) void storage_fetch(const AssetLibraryReference *library_reference, const bContext *C); bool is_loaded(const AssetLibraryReference *library_reference); void ensure_previews_job(const AssetLibraryReference *library_reference, const bContext *C); -void clear(const AssetLibraryReference *library_reference, bContext *C); +void clear(const AssetLibraryReference *library_reference, const bContext *C); bool storage_has_list_for_library(const AssetLibraryReference *library_reference); /** * Tag all asset lists in the storage that show main data as needing an update (re-fetch). diff --git a/source/blender/editors/asset/intern/asset_list.cc b/source/blender/editors/asset/intern/asset_list.cc index e5bc5030799..2a346e60811 100644 --- a/source/blender/editors/asset/intern/asset_list.cc +++ b/source/blender/editors/asset/intern/asset_list.cc @@ -116,7 +116,7 @@ class AssetList : NonCopyable { void setup(); void fetch(const bContext &C); void ensurePreviewsJob(const bContext *C); - void clear(bContext *C); + void clear(const bContext *C); AssetHandle asset_get_by_index(int index) const; @@ -257,7 +257,7 @@ void AssetList::ensurePreviewsJob(const bContext *C) } } -void AssetList::clear(bContext *C) +void AssetList::clear(const bContext *C) { /* Based on #ED_fileselect_clear() */ @@ -265,6 +265,7 @@ void AssetList::clear(bContext *C) filelist_readjob_stop(files, CTX_wm_manager(C)); filelist_freelib(files); filelist_clear(files); + filelist_tag_force_reset(files); WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST, nullptr); } @@ -477,7 +478,7 @@ void ensure_previews_job(const AssetLibraryReference *library_reference, const b } } -void clear(const AssetLibraryReference *library_reference, bContext *C) +void clear(const AssetLibraryReference *library_reference, const bContext *C) { AssetList *list = AssetListStorage::lookup_list(*library_reference); if (list) { diff --git a/source/blender/editors/interface/interface_templates.cc b/source/blender/editors/interface/interface_templates.cc index 5c6ebc5d0fc..483d773553b 100644 --- a/source/blender/editors/interface/interface_templates.cc +++ b/source/blender/editors/interface/interface_templates.cc @@ -1374,70 +1374,72 @@ static void template_ID(const bContext *C, template_id_workspace_pin_extra_icon(template_ui, but); - if (ID_IS_LINKED(id)) { - const bool disabled = !BKE_idtype_idcode_is_localizable(GS(id->name)); - if (id->tag & LIB_TAG_INDIRECT) { - but = uiDefIconBut(block, - UI_BTYPE_BUT, - 0, - ICON_LIBRARY_DATA_INDIRECT, - 0, - 0, - UI_UNIT_X, - UI_UNIT_Y, - nullptr, - 0, - 0, - 0, - 0, - TIP_("Indirect library data-block, cannot be made local, " - "Shift + Click to create a library override hierarchy")); + if (!hide_buttons) { + if (ID_IS_LINKED(id)) { + const bool disabled = !BKE_idtype_idcode_is_localizable(GS(id->name)); + if (id->tag & LIB_TAG_INDIRECT) { + but = uiDefIconBut(block, + UI_BTYPE_BUT, + 0, + ICON_LIBRARY_DATA_INDIRECT, + 0, + 0, + UI_UNIT_X, + UI_UNIT_Y, + nullptr, + 0, + 0, + 0, + 0, + TIP_("Indirect library data-block, cannot be made local, " + "Shift + Click to create a library override hierarchy")); + } + else { + but = uiDefIconBut(block, + UI_BTYPE_BUT, + 0, + ICON_LIBRARY_DATA_DIRECT, + 0, + 0, + UI_UNIT_X, + UI_UNIT_Y, + nullptr, + 0, + 0, + 0, + 0, + TIP_("Direct linked library data-block, click to make local, " + "Shift + Click to create a library override")); + } + if (disabled) { + UI_but_flag_enable(but, UI_BUT_DISABLED); + } + else { + UI_but_funcN_set( + but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_LOCAL)); + } } - else { - but = uiDefIconBut(block, - UI_BTYPE_BUT, - 0, - ICON_LIBRARY_DATA_DIRECT, - 0, - 0, - UI_UNIT_X, - UI_UNIT_Y, - nullptr, - 0, - 0, - 0, - 0, - TIP_("Direct linked library data-block, click to make local, " - "Shift + Click to create a library override")); - } - if (disabled) { - UI_but_flag_enable(but, UI_BUT_DISABLED); - } - else { + else if (ID_IS_OVERRIDE_LIBRARY(id)) { + but = uiDefIconBut( + block, + UI_BTYPE_BUT, + 0, + ICON_LIBRARY_DATA_OVERRIDE, + 0, + 0, + UI_UNIT_X, + UI_UNIT_Y, + nullptr, + 0, + 0, + 0, + 0, + TIP_("Library override of linked data-block, click to make fully local, " + "Shift + Click to clear the library override and toggle if it can be edited")); UI_but_funcN_set( - but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_LOCAL)); + but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_OVERRIDE)); } } - else if (ID_IS_OVERRIDE_LIBRARY(id)) { - but = uiDefIconBut( - block, - UI_BTYPE_BUT, - 0, - ICON_LIBRARY_DATA_OVERRIDE, - 0, - 0, - UI_UNIT_X, - UI_UNIT_Y, - nullptr, - 0, - 0, - 0, - 0, - TIP_("Library override of linked data-block, click to make fully local, " - "Shift + Click to clear the library override and toggle if it can be edited")); - UI_but_funcN_set( - but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_OVERRIDE)); - } if ((ID_REAL_USERS(id) > 1) && (hide_buttons == false)) { char numstr[32]; diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 63f14d1ec13..26eb54e5cf7 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -12,8 +12,10 @@ #include "MEM_guardedalloc.h" +#include "BLI_fileops.h" #include "BLI_listbase.h" #include "BLI_math_vector.h" +#include "BLI_path_util.h" #include "BLI_string.h" #include "BLI_utildefines.h" @@ -26,14 +28,23 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "BLO_writefile.hh" + +#include "BKE_asset.hh" +#include "BKE_blendfile.hh" #include "BKE_brush.hh" #include "BKE_context.hh" #include "BKE_image.h" #include "BKE_lib_id.hh" +#include "BKE_lib_override.hh" #include "BKE_main.hh" #include "BKE_paint.hh" +#include "BKE_preferences.h" #include "BKE_report.h" +#include "ED_asset_handle.hh" +#include "ED_asset_list.hh" +#include "ED_asset_mark_clear.hh" #include "ED_image.hh" #include "ED_paint.hh" #include "ED_screen.hh" @@ -45,6 +56,7 @@ #include "RNA_access.hh" #include "RNA_define.hh" +#include "AS_asset_library.hh" #include "AS_asset_representation.hh" #include "curves_sculpt_intern.hh" @@ -975,9 +987,13 @@ static void PAINT_OT_brush_select(wmOperatorType *ot) /**************************** Brush Assets **********************************/ -static bool brush_asset_poll(bContext *C) +static bool brush_asset_select_poll(bContext *C) { - return BKE_paint_get_active_from_context(C) != nullptr; + if (BKE_paint_get_active_from_context(C) == nullptr) { + return false; + } + + return CTX_wm_asset(C) != nullptr; } static int brush_asset_select_exec(bContext *C, wmOperator *op) @@ -986,9 +1002,6 @@ static int brush_asset_select_exec(bContext *C, wmOperator *op) * 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. */ blender::asset_system::AssetRepresentation *asset = CTX_wm_asset(C); - if (!asset) { - return OPERATOR_CANCELLED; - } AssetWeakReference *brush_asset_reference = asset->make_weak_reference(); Brush *brush = BKE_brush_asset_runtime_ensure(CTX_data_main(C), brush_asset_reference); @@ -1014,7 +1027,461 @@ static void BRUSH_OT_asset_select(wmOperatorType *ot) ot->idname = "BRUSH_OT_asset_select"; ot->exec = brush_asset_select_exec; - ot->poll = brush_asset_poll; + ot->poll = brush_asset_select_poll; +} + +/* FIXME Quick dirty hack to generate a weak ref from 'raw' paths. + * This needs to be properly implemented in assetlib code. + */ +static AssetWeakReference *brush_asset_create_weakref_hack(const bUserAssetLibrary *user_asset_lib, + std::string &file_path) +{ + AssetWeakReference *asset_weak_ref = MEM_new(__func__); + + blender::StringRefNull asset_root_path = user_asset_lib->dirpath; + BLI_assert(file_path.find(asset_root_path) == 0); + std::string relative_asset_path = file_path.substr(size_t(asset_root_path.size()) + 1); + + asset_weak_ref->asset_library_type = ASSET_LIBRARY_CUSTOM; + asset_weak_ref->asset_library_identifier = BLI_strdup(user_asset_lib->name); + asset_weak_ref->relative_asset_identifier = BLI_strdup(relative_asset_path.c_str()); + + return asset_weak_ref; +} + +static const bUserAssetLibrary *brush_asset_get_editable_library() +{ + /* TODO: take into account which one is marked as default. */ + LISTBASE_FOREACH (const bUserAssetLibrary *, asset_library, &U.asset_libraries) { + return asset_library; + } + return nullptr; +} + +static void brush_asset_refresh_editable_library(const bContext *C) +{ + const bUserAssetLibrary *user_library = brush_asset_get_editable_library(); + + /* TODO: Should the all library reference be automatically cleared? */ + AssetLibraryReference all_lib_ref = blender::asset_system::all_library_reference(); + blender::ed::asset::list::clear(&all_lib_ref, C); + + /* TODO: this is convoluted, can we create a reference from pointer? */ + for (const AssetLibraryReference &lib_ref : + blender::asset_system::all_valid_asset_library_refs()) + { + if (lib_ref.type == ASSET_LIBRARY_CUSTOM) { + const bUserAssetLibrary *ref_user_library = BKE_preferences_asset_library_find_index( + &U, lib_ref.custom_library_index); + if (ref_user_library == user_library) { + blender::ed::asset::list::clear(&lib_ref, C); + return; + } + } + } +} + +static std::string brush_asset_root_path_for_save() +{ + const bUserAssetLibrary *user_library = brush_asset_get_editable_library(); + if (user_library == nullptr || user_library->dirpath[0] == '\0') { + return ""; + } + + char libpath[FILE_MAX]; + BLI_strncpy(libpath, user_library->dirpath, sizeof(libpath)); + BLI_path_slash_native(libpath); + BLI_path_normalize(libpath); + + return std::string(libpath) + SEP + "Saved" + SEP + "Brushes"; +} + +static std::string brush_asset_blendfile_path_for_save(ReportList *reports, + const blender::StringRefNull &base_name) +{ + std::string root_path = brush_asset_root_path_for_save(); + BLI_assert(!root_path.empty()); + + if (!BLI_dir_create_recursive(root_path.c_str())) { + BKE_report(reports, RPT_ERROR, "Failed to create asset library directory to save brush"); + return ""; + } + + char base_name_filesafe[FILE_MAXFILE]; + BLI_strncpy(base_name_filesafe, base_name.c_str(), sizeof(base_name_filesafe)); + BLI_path_make_safe_filename(base_name_filesafe); + + if (!BLI_is_file((root_path + SEP + base_name_filesafe + BLENDER_ASSET_FILE_SUFFIX).c_str())) { + return root_path + SEP + base_name_filesafe + BLENDER_ASSET_FILE_SUFFIX; + } + int i = 1; + while (BLI_is_file((root_path + SEP + base_name_filesafe + "_" + std::to_string(i++) + + BLENDER_ASSET_FILE_SUFFIX) + .c_str())) + ; + return root_path + SEP + base_name_filesafe + "_" + std::to_string(i - 1) + + BLENDER_ASSET_FILE_SUFFIX; +} + +static bool brush_asset_write_in_library(Main *bmain, + Brush *brush, + const char *name, + const blender::StringRefNull &filepath, + std::string &final_full_file_path, + ReportList *reports) +{ + /* XXX + * FIXME + * + * This code is _pure evil_. It does in-place manipulation on IDs in global Main database, + * temporarilly remove them and add them back... + * + * Use it as-is for now (in a similar way as python API or copy-to-buffer works). Nut the whole + * 'BKE_blendfile_write_partial' code needs to be completely refactored. + * + * Ideas: + * - Have `BKE_blendfile_write_partial_begin` return a new temp Main. + * - Replace `BKE_blendfile_write_partial_tag_ID` by API to add IDs to this temp Main. + * + This should _duplicate_ the ID, not remove the original one from the source Main! + * - Have API to automatically also duplicate dependencies into temp Main. + * + Have options to e.g. make all duplicated IDs 'local' (i.e. remove their library data). + * - `BKE_blendfile_write_partial` then simply write the given temp main. + * - `BKE_blendfile_write_partial_end` frees the temp Main. + */ + + const short brush_flag = brush->id.flag; + const int brush_tag = brush->id.tag; + const int brush_us = brush->id.us; + const std::string brush_name = brush->id.name + 2; + IDOverrideLibrary *brush_liboverride = brush->id.override_library; + AssetMetaData *brush_asset_data = brush->id.asset_data; + const int write_flags = 0; /* Could use #G_FILE_COMPRESS ? */ + const eBLO_WritePathRemap remap_mode = BLO_WRITE_PATH_REMAP_RELATIVE; + + BKE_blendfile_write_partial_begin(bmain); + + brush->id.flag |= LIB_FAKEUSER; + brush->id.tag &= ~LIB_TAG_RUNTIME; + brush->id.us = 1; + BLI_strncpy(brush->id.name + 2, name, sizeof(brush->id.name) - 2); + if (!ID_IS_ASSET(&brush->id)) { + brush->id.asset_data = brush->id.override_library->reference->asset_data; + } + brush->id.override_library = nullptr; + + BKE_blendfile_write_partial_tag_ID(&brush->id, true); + + /* TODO: check overwriting existing file. */ + /* TODO: ensure filepath contains only valid characters for file system. */ + const bool sucess = BKE_blendfile_write_partial( + bmain, filepath.c_str(), write_flags, remap_mode, reports); + + if (sucess) { + final_full_file_path = std::string(filepath) + SEP + "Brush" + SEP + name; + } + + BKE_blendfile_write_partial_end(bmain); + + BKE_blendfile_write_partial_tag_ID(&brush->id, false); + brush->id.flag = brush_flag; + brush->id.tag = brush_tag; + brush->id.us = brush_us; + BLI_strncpy(brush->id.name + 2, brush_name.c_str(), sizeof(brush->id.name) - 2); + brush->id.override_library = brush_liboverride; + brush->id.asset_data = brush_asset_data; + + return sucess; +} + +static bool brush_asset_save_as_poll(bContext *C) +{ + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr; + if (paint == nullptr || brush == nullptr) { + return false; + } + + const bUserAssetLibrary *user_library = brush_asset_get_editable_library(); + if (user_library == nullptr || user_library->dirpath[0] == '\0') { + CTX_wm_operator_poll_msg_set(C, "No default asset library available to save to"); + return false; + } + + return true; +} + +static int brush_asset_save_as_exec(bContext *C, wmOperator *op) +{ + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr; + if (paint == nullptr || brush == nullptr) { + return OPERATOR_CANCELLED; + } + + /* Determine file path to save to. */ + PropertyRNA *name_prop = RNA_struct_find_property(op->ptr, "name"); + char name[MAX_NAME] = ""; + if (RNA_property_is_set(op->ptr, name_prop)) { + RNA_property_string_get(op->ptr, name_prop, name); + } + if (name[0] == '\0') { + STRNCPY(name, brush->id.name + 2); + } + + const std::string filepath = brush_asset_blendfile_path_for_save(op->reports, name); + if (filepath.empty()) { + return OPERATOR_CANCELLED; + } + + /* Turn brush into asset if it isn't yet. */ + if (!BKE_paint_brush_is_valid_asset(brush)) { + blender::ed::asset::mark_id(&brush->id); + blender::ed::asset::generate_preview(C, &brush->id); + } + BLI_assert(BKE_paint_brush_is_valid_asset(brush)); + + /* Save to asset library. */ + 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); + + if (!sucess) { + BKE_report(op->reports, RPT_ERROR, "Failed to write to asset library"); + return OPERATOR_CANCELLED; + } + + /* Create weak reference to new datablock. */ + const bUserAssetLibrary *asset_lib = brush_asset_get_editable_library(); + AssetWeakReference *new_brush_weak_ref = brush_asset_create_weakref_hack( + asset_lib, final_full_asset_filepath); + + /* 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); + + 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. */ + BKE_report(op->reports, RPT_WARNING, "Unable to activate just-saved brush asset"); + } + + brush_asset_refresh_editable_library(C); + WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_ADDED, nullptr); + + return OPERATOR_FINISHED; +} + +static int brush_asset_save_as_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) +{ + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *brush = BKE_paint_brush(paint); + + RNA_string_set(op->ptr, "name", brush->id.name + 2); + + /* TODO: add information about the asset library this will be saved to? */ + /* TODO: autofocus name? */ + return WM_operator_props_dialog_popup(C, op, 400); +} + +static void BRUSH_OT_asset_save_as(wmOperatorType *ot) +{ + ot->name = "Save As Brush Asset"; + ot->description = + "Save a copy of the active brush asset into the default asset library, and make it the " + "active brush"; + ot->idname = "BRUSH_OT_asset_save_as"; + + ot->exec = brush_asset_save_as_exec; + ot->invoke = brush_asset_save_as_invoke; + ot->poll = brush_asset_save_as_poll; + + RNA_def_string(ot->srna, "name", nullptr, MAX_NAME, "Name", "Name used to save the brush asset"); +} + +static bool brush_asset_is_editable(const AssetWeakReference &brush_weak_ref) +{ + /* Fairly simple checks, based on filepath only: + * - The blendlib filepath ends up with the `.asset.blend` extension. + * - The blendlib is located in the expected sub-directory of the editable asset library. + * + * TODO: Right now no check is done on file content, e.g. to ensure that the blendlib file has + * not been manually edited by the user (that it does not have any UI IDs e.g.). */ + + char path_buffer[FILE_MAX_LIBEXTRA]; + char *dir, *group, *name; + AS_asset_full_path_explode_from_weak_ref(&brush_weak_ref, path_buffer, &dir, &group, &name); + + if (!blender::StringRef(dir).endswith(BLENDER_ASSET_FILE_SUFFIX)) { + return false; + } + + std::string root_path_for_save = brush_asset_root_path_for_save(); + if (root_path_for_save.empty() || !blender::StringRef(dir).startswith(root_path_for_save)) { + return false; + } + + /* TODO: Do we want more checks here? E.g. check actual content of the file? */ + return true; +} + +static bool brush_asset_delete_poll(bContext *C) +{ + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr; + if (paint == nullptr || brush == nullptr) { + return false; + } + + /* Asset brush, check if belongs to an editable blend file. */ + if (paint->brush_asset_reference && BKE_paint_brush_is_valid_asset(brush)) { + if (!brush_asset_is_editable(*paint->brush_asset_reference)) { + CTX_wm_operator_poll_msg_set(C, "Asset blend file is not editable"); + return false; + } + } + + return true; +} + +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); + + if (paint->brush_asset_reference && BKE_paint_brush_is_valid_asset(brush)) { + /* Delete from asset library on disk. */ + char path_buffer[FILE_MAX_LIBEXTRA]; + char *filepath; + AS_asset_full_path_explode_from_weak_ref( + paint->brush_asset_reference, path_buffer, &filepath, nullptr, nullptr); + + if (BLI_delete(filepath, false, false) != 0) { + BKE_report(op->reports, RPT_ERROR, "Failed to delete asset library file"); + } + } + + /* 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); + } + + brush_asset_refresh_editable_library(C); + WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_REMOVED, nullptr); + + /* TODO: activate default brush. */ + + return OPERATOR_FINISHED; +} + +static void BRUSH_OT_asset_delete(wmOperatorType *ot) +{ + ot->name = "Delete Brush Asset"; + ot->description = "Delete the active brush asset both from the local session and asset library"; + ot->idname = "BRUSH_OT_asset_delete"; + + ot->exec = brush_asset_delete_exec; + ot->invoke = WM_operator_confirm; + ot->poll = brush_asset_delete_poll; +} + +static bool brush_asset_update_poll(bContext *C) +{ + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr; + if (paint == nullptr || brush == nullptr) { + return false; + } + + if (!(paint->brush_asset_reference && BKE_paint_brush_is_valid_asset(brush))) { + return false; + } + + if (!brush_asset_is_editable(*paint->brush_asset_reference)) { + CTX_wm_operator_poll_msg_set(C, "Asset blend file is not editable"); + return false; + } + + return true; +} + +static int brush_asset_update_exec(bContext *C, wmOperator *op) +{ + 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); + + char path_buffer[FILE_MAX_LIBEXTRA]; + char *filepath; + AS_asset_full_path_explode_from_weak_ref( + brush_weak_ref, path_buffer, &filepath, nullptr, nullptr); + + BLI_assert(BKE_paint_brush_is_valid_asset(brush)); + + 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); + + return OPERATOR_FINISHED; +} + +static void BRUSH_OT_asset_update(wmOperatorType *ot) +{ + ot->name = "Update Brush Asset"; + ot->description = "Update the active brush asset in the asset library with current settings"; + ot->idname = "BRUSH_OT_asset_update"; + + ot->exec = brush_asset_update_exec; + ot->poll = brush_asset_update_poll; +} + +static bool brush_asset_revert_poll(bContext *C) +{ + /* TODO: check if there is anything to revert? */ + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr; + if (paint == nullptr || brush == nullptr) { + return false; + } + + return paint->brush_asset_reference && BKE_paint_brush_is_valid_asset(brush); +} + +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); + + /* 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); + + WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush); + + return OPERATOR_FINISHED; +} + +static void BRUSH_OT_asset_revert(wmOperatorType *ot) +{ + ot->name = "Revert Brush Asset"; + ot->description = + "Revert the active brush settings to the default values from the asset library"; + ot->idname = "BRUSH_OT_asset_revert"; + + ot->exec = brush_asset_revert_exec; + ot->poll = brush_asset_revert_poll; } /***** Stencil Control *****/ @@ -1479,6 +1946,10 @@ void ED_operatortypes_paint() WM_operatortype_append(BRUSH_OT_stencil_fit_image_aspect); WM_operatortype_append(BRUSH_OT_stencil_reset_transform); WM_operatortype_append(BRUSH_OT_asset_select); + WM_operatortype_append(BRUSH_OT_asset_save_as); + WM_operatortype_append(BRUSH_OT_asset_delete); + 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); -- 2.30.2 From 63484e0e969ee57a6c724a4ff69514b7cfeb3b5a Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 29 Jan 2024 12:32:56 +0100 Subject: [PATCH 078/244] Brush Assets: switch to dedicated testing/ folder for bundled brushes --- source/creator/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 46d78580ff7..08fb99857d1 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -1678,7 +1678,7 @@ if(EXISTS "${ASSET_BUNDLE_DIR}") ) # TODO temporary change for development only. Remove before merging. - set(ASSET_BRUSH_BUNDLE_DIR "${ASSET_BUNDLE_DIR}/../working/brushes/") + set(ASSET_BRUSH_BUNDLE_DIR "${ASSET_BUNDLE_DIR}/../testing/brushes/") if(EXISTS "${ASSET_BRUSH_BUNDLE_DIR}") install( FILES ${ASSET_BRUSH_BUNDLE_DIR}/essentials_brushes.blend -- 2.30.2 From 64db6bd4beeac666a728aedc13ba7f72405d8d51 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 29 Jan 2024 12:42:05 +0100 Subject: [PATCH 079/244] Fix: crash when selected asset brush file goes missing --- source/blender/blenkernel/intern/paint.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 2226f51bf97..3a8df42858f 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -705,7 +705,7 @@ bool BKE_paint_brush_asset_set(Paint *paint, { /* Should not happen for users if brush assets are properly filtered by mode, but still protect * against it in case of invalid API usage. */ - if (paint->runtime.ob_mode != brush->ob_mode) { + if (brush && paint->runtime.ob_mode != brush->ob_mode) { BKE_asset_weak_reference_free(&weak_asset_reference); return false; } -- 2.30.2 From e26ebf6324a92611a1c77d4dabe1e9c3d7fcdfee Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 29 Jan 2024 13:31:33 +0100 Subject: [PATCH 080/244] Fix: missing catalogs, replace bundled assets entirely with testing/ Not ideal to not have the hair node group assets, but we can have only one catalog file so better to have brushes working at least. --- source/creator/CMakeLists.txt | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 08fb99857d1..d18d871dbb1 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -1669,27 +1669,18 @@ if(NOT EXISTS "${ASSET_BUNDLE_DIR}") set(ASSET_BUNDLE_DIR ${CMAKE_SOURCE_DIR}/../lib/assets/publish/) endif() -set(ASSET_TARGETDIR ${TARGETDIR_VER}/datafiles/assets) +# TODO temporary change for development only. Remove before merging. +set(ASSET_BUNDLE_TESTING_DIR "${ASSET_BUNDLE_DIR}/../testing/") +if(EXISTS "${ASSET_BUNDLE_TESTING_DIR}") + set(ASSET_BUNDLE_DIR "${ASSET_BUNDLE_TESTING_DIR}") +endif() + if(EXISTS "${ASSET_BUNDLE_DIR}") install( DIRECTORY ${ASSET_BUNDLE_DIR} - DESTINATION ${ASSET_TARGETDIR} + DESTINATION ${TARGETDIR_VER}/datafiles/assets PATTERN ".svn" EXCLUDE ) - - # TODO temporary change for development only. Remove before merging. - set(ASSET_BRUSH_BUNDLE_DIR "${ASSET_BUNDLE_DIR}/../testing/brushes/") - if(EXISTS "${ASSET_BRUSH_BUNDLE_DIR}") - install( - FILES ${ASSET_BRUSH_BUNDLE_DIR}/essentials_brushes.blend - DESTINATION ${ASSET_TARGETDIR}/brushes - ) - - install( - FILES ${ASSET_BRUSH_BUNDLE_DIR}/blender_assets.cats.txt - DESTINATION ${ASSET_TARGETDIR} - ) - endif() endif() -- 2.30.2 From 2f0c95b16d372e56f179dc86fa488ebe25e4e71d Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 29 Jan 2024 17:46:55 -0500 Subject: [PATCH 081/244] Change name of single brush tool to "Brush" --- scripts/startup/bl_ui/space_toolsystem_toolbar.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/scripts/startup/bl_ui/space_toolsystem_toolbar.py index c7ab5bbbe9a..038d2761d11 100644 --- a/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -1394,7 +1394,7 @@ class _defs_sculpt: ToolDef.from_dict( dict( idname="builtin.brush", - label=brush.name, + label="Brush", icon="brush.sculpt.paint", data_block=tool ) @@ -2459,7 +2459,7 @@ class _defs_curves_sculpt: ToolDef.from_dict( dict( idname="builtin.brush", - label=brush.name, + label="Brush", icon="brush.sculpt.paint", data_block=tool ) -- 2.30.2 From 8e2f9df9d6d2b74fa7d345f1ee3985f816d4bc0e Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 31 Jan 2024 14:33:44 -0500 Subject: [PATCH 082/244] Cleanup: Use continue to reduce indentation --- .../blender/blenkernel/intern/lib_override.cc | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/source/blender/blenkernel/intern/lib_override.cc b/source/blender/blenkernel/intern/lib_override.cc index 3590f5ec56e..96da6b8fa64 100644 --- a/source/blender/blenkernel/intern/lib_override.cc +++ b/source/blender/blenkernel/intern/lib_override.cc @@ -359,15 +359,16 @@ int BKE_lib_override_user_edited_from_library_count(Main *bmain, if (id_iter->override_library->reference->lib != library) { continue; } + if (!BKE_lib_override_library_is_user_edited(id_iter)) { + continue; + } - if (BKE_lib_override_library_is_user_edited(id_iter)) { - /* 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); - } + /* 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); } } -- 2.30.2 From 2120714afe0f27de9e47f5eecb1cc531805f5b8c Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 31 Jan 2024 14:35:53 -0500 Subject: [PATCH 083/244] Cleanup: Use proper enum type --- source/blender/blenkernel/BKE_lib_override.hh | 4 +++- source/blender/blenkernel/intern/lib_override.cc | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/BKE_lib_override.hh b/source/blender/blenkernel/BKE_lib_override.hh index 479a8128e91..02716511e0b 100644 --- a/source/blender/blenkernel/BKE_lib_override.hh +++ b/source/blender/blenkernel/BKE_lib_override.hh @@ -23,6 +23,8 @@ * of IDs in a given Main data-base. */ +#include "DNA_ID_enums.h" + #include struct BlendFileReadReport; @@ -89,7 +91,7 @@ bool BKE_lib_override_library_is_system_defined(const Main *bmain, const ID *id) * \param r_reports: If not NULL, add one report for each relevant ID. */ int BKE_lib_override_user_edited_from_library_count(Main *bmain, - const short id_type, + ID_Type id_type, Library *library, ReportList *r_reports); diff --git a/source/blender/blenkernel/intern/lib_override.cc b/source/blender/blenkernel/intern/lib_override.cc index 96da6b8fa64..e62ec05ba0b 100644 --- a/source/blender/blenkernel/intern/lib_override.cc +++ b/source/blender/blenkernel/intern/lib_override.cc @@ -340,7 +340,7 @@ bool BKE_lib_override_library_is_system_defined(const Main *bmain, const ID *id) } int BKE_lib_override_user_edited_from_library_count(Main *bmain, - const short id_type, + const ID_Type id_type, Library *library, ReportList *r_reports) { -- 2.30.2 From 287500e99363080595b690d658f3bf038799fc9f Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 29 Jan 2024 13:37:50 +0100 Subject: [PATCH 084/244] Cleanup: make format --- scripts/startup/bl_ui/space_view3d_toolbar.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index a42d1cb6b85..cdd8085fc28 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -46,7 +46,6 @@ 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 -- 2.30.2 From dc1a45f996e0633d94b250e8b3f125300fb60d14 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 31 Jan 2024 18:15:41 +0100 Subject: [PATCH 085/244] Brush Assets: move brush tool to Brush Settings > Advanced --- .../startup/bl_ui/properties_paint_common.py | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/scripts/startup/bl_ui/properties_paint_common.py b/scripts/startup/bl_ui/properties_paint_common.py index 7bc33c59cd6..1655671e4de 100644 --- a/scripts/startup/bl_ui/properties_paint_common.py +++ b/scripts/startup/bl_ui/properties_paint_common.py @@ -171,18 +171,6 @@ class BrushSelectPanel(BrushPanel): sub.active = brush.use_custom_icon sub.prop(brush, "icon_filepath", text="") - # brush tool - if context.image_paint_object: - panel.prop(brush, "image_tool") - elif context.vertex_paint_object: - panel.prop(brush, "vertex_tool") - elif context.weight_paint_object: - panel.prop(brush, "weight_tool") - elif context.sculpt_object: - panel.prop(brush, "sculpt_tool") - elif context.tool_settings.curves_sculpt: - panel.prop(brush, "curves_sculpt_tool") - # brush paint modes col = panel.column(heading="Modes", align=True) col.prop(brush, "use_paint_sculpt", text="Sculpt") @@ -981,6 +969,9 @@ def brush_settings_advanced(layout, context, brush, popover=False): use_frontface = False if mode == 'SCULPT': + layout.prop(brush, "sculpt_tool") + layout.separator() + capabilities = brush.sculpt_capabilities use_accumulate = capabilities.has_accumulate use_frontface = True @@ -1060,6 +1051,9 @@ def brush_settings_advanced(layout, context, brush, popover=False): # 3D and 2D Texture Paint. elif mode in {'PAINT_TEXTURE', 'PAINT_2D'}: + layout.prop(brush, "image_tool") + layout.separator() + capabilities = brush.image_paint_capabilities use_accumulate = capabilities.has_accumulate @@ -1087,6 +1081,9 @@ def brush_settings_advanced(layout, context, brush, popover=False): # Vertex Paint # elif mode == 'PAINT_VERTEX': + layout.prop(brush, "vertex_tool") + layout.separator() + layout.prop(brush, "use_alpha") if brush.vertex_tool != 'SMEAR': use_accumulate = True @@ -1094,10 +1091,17 @@ def brush_settings_advanced(layout, context, brush, popover=False): # Weight Paint elif mode == 'PAINT_WEIGHT': + layout.prop(brush, "weight_tool") + layout.separator() + if brush.weight_tool != 'SMEAR': use_accumulate = True use_frontface = True + # Sculpt Curves + elif mode == 'SCULPT_CURVES': + layout.prop(brush, "curves_sculpt_tool") + # Draw shared settings. if use_accumulate: layout.prop(brush, "use_accumulate") -- 2.30.2 From 6b4e9c8217b2a46e5464c93da0ad4df6ba573338 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 31 Jan 2024 18:27:44 +0100 Subject: [PATCH 086/244] Brush Assets: move brush modes to Advanced, hide custom icon for assets --- .../startup/bl_ui/properties_paint_common.py | 42 ++++++++++++------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/scripts/startup/bl_ui/properties_paint_common.py b/scripts/startup/bl_ui/properties_paint_common.py index 1655671e4de..7b779860e3f 100644 --- a/scripts/startup/bl_ui/properties_paint_common.py +++ b/scripts/startup/bl_ui/properties_paint_common.py @@ -157,29 +157,24 @@ class BrushSelectPanel(BrushPanel): col = row.column() col.menu("VIEW3D_MT_brush_context_menu", icon='DOWNARROW_HLT', text="") - header, panel = layout.panel("customize", default_closed=True) - header.label(text="Customize") - if panel: - panel.use_property_split = True - panel.use_property_decorate = False + # 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 - # icon - col = panel.column(heading="Custom Icon", align=True) + if not is_asset_brush: + # Legacy custom icon, mostly replaced by asset preview. + layout.use_property_split = True + layout.use_property_decorate = False + + col = layout.column(heading="Custom Icon", align=True) row = col.row() row.prop(brush, "use_custom_icon", text="") sub = row.row() sub.active = brush.use_custom_icon sub.prop(brush, "icon_filepath", text="") - # brush paint modes - col = panel.column(heading="Modes", align=True) - col.prop(brush, "use_paint_sculpt", text="Sculpt") - col.prop(brush, "use_paint_uv_sculpt", text="UV Sculpt") - col.prop(brush, "use_paint_vertex", text="Vertex Paint") - col.prop(brush, "use_paint_weight", text="Weight Paint") - col.prop(brush, "use_paint_image", text="Texture Paint") - col.prop(brush, "use_paint_sculpt_curves", text="Sculpt Curves") - class ColorPalettePanel(BrushPanel): bl_label = "Color Palette" @@ -1109,6 +1104,21 @@ def brush_settings_advanced(layout, context, brush, popover=False): if use_frontface: layout.prop(brush, "use_frontface", text="Front Faces Only") + # Brush modes + header, panel = layout.panel("modes", default_closed=True) + header.label(text="Modes") + if panel: + panel.use_property_split = True + panel.use_property_decorate = False + + col = panel.column(align=True) + col.prop(brush, "use_paint_sculpt", text="Sculpt") + col.prop(brush, "use_paint_uv_sculpt", text="UV Sculpt") + col.prop(brush, "use_paint_vertex", text="Vertex Paint") + col.prop(brush, "use_paint_weight", text="Weight Paint") + col.prop(brush, "use_paint_image", text="Texture Paint") + col.prop(brush, "use_paint_sculpt_curves", text="Sculpt Curves") + def draw_color_settings(context, layout, brush, color_type=False): """Draw color wheel and gradient settings.""" -- 2.30.2 From 53b8fed09394cc81efb0efbca80cd633a31a9359 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 31 Jan 2024 21:35:59 +0100 Subject: [PATCH 087/244] Fix: missing properties editor redraws on brush duplicate --- source/blender/editors/sculpt_paint/paint_ops.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 26eb54e5cf7..c1340c19810 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -1268,6 +1268,7 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) brush_asset_refresh_editable_library(C); WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_ADDED, nullptr); + WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush); return OPERATOR_FINISHED; } @@ -1374,6 +1375,7 @@ static int brush_asset_delete_exec(bContext *C, wmOperator *op) brush_asset_refresh_editable_library(C); WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_REMOVED, nullptr); + WM_main_add_notifier(NC_BRUSH | NA_EDITED, nullptr); /* TODO: activate default brush. */ -- 2.30.2 From 458716b675bb9b2bc4efd4f2827302daa99aadc9 Mon Sep 17 00:00:00 2001 From: Sean Kim Date: Wed, 31 Jan 2024 21:43:02 +0100 Subject: [PATCH 088/244] Cleanup: Perform cleanup on sculpt_automasking.cc * Add documentation / comments * Apply const where possible to cache_init function codepath * Minor cleanup of conditionals and initialization Contains changes that were deferred from #117316 Pull Request: https://projects.blender.org/blender/blender/pulls/117651 --- .../sculpt_paint/sculpt_automasking.cc | 41 ++++++++++--------- .../editors/sculpt_paint/sculpt_cloth.cc | 2 +- .../sculpt_paint/sculpt_filter_color.cc | 2 +- .../sculpt_paint/sculpt_filter_mesh.cc | 2 +- .../editors/sculpt_paint/sculpt_intern.hh | 10 ++++- 5 files changed, 32 insertions(+), 25 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.cc b/source/blender/editors/sculpt_paint/sculpt_automasking.cc index 49dd1afd0c5..6ac743f0dbc 100644 --- a/source/blender/editors/sculpt_paint/sculpt_automasking.cc +++ b/source/blender/editors/sculpt_paint/sculpt_automasking.cc @@ -632,10 +632,10 @@ static bool floodfill_cb( SCULPT_vertex_co_get(ss, to_v), data->location, data->radius, data->symm)); } -static void topology_automasking_init(Sculpt *sd, Object *ob) +static void topology_automasking_init(const Sculpt *sd, Object *ob) { SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); + const Brush *brush = BKE_paint_brush_for_read(&sd->paint); const int totvert = SCULPT_vertex_count_get(ss); for (int i : IndexRange(totvert)) { @@ -661,10 +661,10 @@ static void topology_automasking_init(Sculpt *sd, Object *ob) flood_fill::execute(ss, &flood, floodfill_cb, &fdata); } -static void init_face_sets_masking(Sculpt *sd, Object *ob) +static void init_face_sets_masking(const Sculpt *sd, Object *ob) { SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); + const Brush *brush = BKE_paint_brush_for_read(&sd->paint); if (!is_enabled(sd, ss, brush)) { return; @@ -740,7 +740,10 @@ static void init_boundary_masking(Object *ob, eBoundaryAutomaskMode mode, int pr } /* Updates the cached values, preferring brush settings over tool-level settings. */ -static void cache_settings_update(Cache &automasking, SculptSession *ss, Sculpt *sd, Brush *brush) +static void cache_settings_update(Cache &automasking, + SculptSession *ss, + const Sculpt *sd, + const Brush *brush) { automasking.settings.flags = calc_effective_bits(sd, brush); automasking.settings.initial_face_set = face_set::active_face_set_get(ss); @@ -816,10 +819,14 @@ bool tool_can_reuse_automask(int sculpt_tool) SCULPT_TOOL_DRAW_FACE_SETS); } -std::unique_ptr cache_init(Sculpt *sd, Brush *brush, Object *ob) +std::unique_ptr cache_init(const Sculpt *sd, Object *ob) +{ + return cache_init(sd, nullptr, ob); +} + +std::unique_ptr cache_init(const Sculpt *sd, const Brush *brush, Object *ob) { SculptSession *ss = ob->sculpt; - const int totvert = SCULPT_vertex_count_get(ss); if (!is_enabled(sd, ss, brush)) { return nullptr; @@ -831,7 +838,6 @@ std::unique_ptr cache_init(Sculpt *sd, Brush *brush, Object *ob) automasking->current_stroke_id = ss->stroke_id; - bool use_stroke_id = false; int mode = calc_effective_bits(sd, brush); if (mode & BRUSH_AUTOMASKING_TOPOLOGY && ss->active_vertex.i != PBVH_REF_NONE) { @@ -839,6 +845,7 @@ std::unique_ptr cache_init(Sculpt *sd, Brush *brush, Object *ob) automasking->settings.initial_island_nr = SCULPT_vertex_island_get(ss, ss->active_vertex); } + bool use_stroke_id = false; if ((mode & BRUSH_AUTOMASKING_VIEW_OCCLUSION) && (mode & BRUSH_AUTOMASKING_VIEW_NORMAL)) { use_stroke_id = true; @@ -895,6 +902,8 @@ std::unique_ptr cache_init(Sculpt *sd, Brush *brush, Object *ob) } } + /* Avoid precomputing data on the vertex level if the current auto-masking modes do not require + * it to function. */ if (!needs_factors_cache(sd, brush)) { if (ss->attrs.automasking_factor) { BKE_sculpt_attribute_destroy(ob, ss->attrs.automasking_factor); @@ -912,19 +921,11 @@ std::unique_ptr cache_init(Sculpt *sd, Brush *brush, Object *ob) SCULPT_ATTRIBUTE_NAME(automasking_factor), ¶ms); - float initial_value; - - /* Topology, boundary and boundary face sets build up the mask - * from zero which other modes can subtract from. If none of them are - * enabled initialize to 1. - */ - if (!(mode & BRUSH_AUTOMASKING_TOPOLOGY)) { - initial_value = 1.0f; - } - else { - initial_value = 0.0f; - } + /* Topology builds up the mask from zero which other modes can subtract from. + * If it isn't enabled, initialize to 1. */ + float initial_value = !(mode & BRUSH_AUTOMASKING_TOPOLOGY) ? 1.0f : 0.0f; + const int totvert = SCULPT_vertex_count_get(ss); for (int i : IndexRange(totvert)) { PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.cc b/source/blender/editors/sculpt_paint/sculpt_cloth.cc index 54ae74c7d85..c4397e63f1f 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.cc @@ -1556,7 +1556,7 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent RNA_float_get(op->ptr, "area_normal_radius"), RNA_float_get(op->ptr, "strength")); - ss->filter_cache->automasking = auto_mask::cache_init(sd, nullptr, ob); + ss->filter_cache->automasking = auto_mask::cache_init(sd, ob); const float cloth_mass = RNA_float_get(op->ptr, "cloth_mass"); const float cloth_damping = RNA_float_get(op->ptr, "cloth_damping"); diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.cc b/source/blender/editors/sculpt_paint/sculpt_filter_color.cc index 6f8151b2ebc..44319a12fa0 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.cc +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.cc @@ -371,7 +371,7 @@ static int sculpt_color_filter_init(bContext *C, wmOperator *op) RNA_float_get(op->ptr, "strength")); filter::Cache *filter_cache = ss->filter_cache; filter_cache->active_face_set = SCULPT_FACE_SET_NONE; - filter_cache->automasking = auto_mask::cache_init(sd, nullptr, ob); + filter_cache->automasking = auto_mask::cache_init(sd, ob); return OPERATOR_PASS_THROUGH; } diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc index c9b0a5bf738..4b6701e75d5 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc @@ -1010,7 +1010,7 @@ static int sculpt_mesh_filter_start(bContext *C, wmOperator *op) filter::Cache *filter_cache = ss->filter_cache; filter_cache->active_face_set = SCULPT_FACE_SET_NONE; - filter_cache->automasking = auto_mask::cache_init(sd, nullptr, ob); + filter_cache->automasking = auto_mask::cache_init(sd, ob); sculpt_filter_specific_init(filter_type, op, ss); diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index 0c6dcede029..f4b3a5c6b23 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -1285,8 +1285,14 @@ float factor_get(Cache *automasking, * brushes and filter. */ Cache *active_cache_get(SculptSession *ss); -/* Brush can be null. */ -std::unique_ptr cache_init(Sculpt *sd, Brush *brush, Object *ob); +/** + * Creates and initializes an automasking cache. + * + * For automasking modes that cannot be calculated in real time, + * data is also stored at the vertex level prior to the stroke starting. + */ +std::unique_ptr cache_init(const Sculpt *sd, Object *ob); +std::unique_ptr cache_init(const Sculpt *sd, const Brush *brush, Object *ob); void cache_free(Cache *automasking); bool mode_enabled(const Sculpt *sd, const Brush *br, eAutomasking_flag mode); -- 2.30.2 From 2e9763c25ebc69413a90b7d86d6d620f6deecb69 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 31 Jan 2024 15:41:55 -0500 Subject: [PATCH 089/244] Fix: Memory leak in gizmo tooltip function --- source/blender/windowmanager/intern/wm_operator_type.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/windowmanager/intern/wm_operator_type.cc b/source/blender/windowmanager/intern/wm_operator_type.cc index b6e9356dc58..7eb647d0a92 100644 --- a/source/blender/windowmanager/intern/wm_operator_type.cc +++ b/source/blender/windowmanager/intern/wm_operator_type.cc @@ -606,9 +606,9 @@ std::string WM_operatortype_description_or_name(bContext *C, { std::string text = WM_operatortype_description(C, ot, properties); if (text.empty()) { - const std::string text_orig = WM_operatortype_name(ot, properties); + std::string text_orig = WM_operatortype_name(ot, properties); if (!text_orig.empty()) { - text = BLI_strdupn(text_orig.c_str(), text_orig.size()); + return text_orig; } } return text; -- 2.30.2 From 684d837c305f30aff9b05871f025ae18d13addec Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 31 Jan 2024 16:32:10 -0500 Subject: [PATCH 090/244] Cleanup: Move remaining spreadsheet editor code to C++ namespace --- source/blender/editors/include/ED_space_api.hh | 7 +++++-- source/blender/editors/include/ED_spreadsheet.hh | 6 +++++- source/blender/editors/space_api/spacetypes.cc | 2 +- .../editors/space_spreadsheet/space_spreadsheet.cc | 13 +++++++------ .../spreadsheet_data_source_geometry.cc | 6 +++--- .../editors/space_spreadsheet/spreadsheet_intern.hh | 12 ++++++------ .../editors/space_spreadsheet/spreadsheet_ops.cc | 8 ++++---- .../space_spreadsheet/spreadsheet_row_filter_ui.cc | 5 +++-- .../space_spreadsheet/spreadsheet_row_filter_ui.hh | 4 ++++ source/blender/makesdna/DNA_space_types.h | 9 ++++++--- source/blender/makesrna/intern/rna_space.cc | 2 +- 11 files changed, 45 insertions(+), 29 deletions(-) diff --git a/source/blender/editors/include/ED_space_api.hh b/source/blender/editors/include/ED_space_api.hh index 325f5b91226..31261b330b7 100644 --- a/source/blender/editors/include/ED_space_api.hh +++ b/source/blender/editors/include/ED_space_api.hh @@ -36,13 +36,16 @@ void ED_spacetype_nla(); void ED_spacetype_script(); void ED_spacetype_text(); void ED_spacetype_sequencer(); -void ED_spacetype_logic(); void ED_spacetype_console(); void ED_spacetype_userpref(); void ED_spacetype_clip(); void ED_spacetype_statusbar(); void ED_spacetype_topbar(); -void ED_spacetype_spreadsheet(); +namespace blender::ed { +namespace spreadsheet { +void register_spacetype(); +} +} // namespace blender::ed /** \} */ diff --git a/source/blender/editors/include/ED_spreadsheet.hh b/source/blender/editors/include/ED_spreadsheet.hh index 05340182f94..1019571765e 100644 --- a/source/blender/editors/include/ED_spreadsheet.hh +++ b/source/blender/editors/include/ED_spreadsheet.hh @@ -7,4 +7,8 @@ struct ID; struct SpaceSpreadsheet; -ID *ED_spreadsheet_get_current_id(const SpaceSpreadsheet *sspreadsheet); +namespace blender::ed::spreadsheet { + +ID *get_current_id(const SpaceSpreadsheet *sspreadsheet); + +} diff --git a/source/blender/editors/space_api/spacetypes.cc b/source/blender/editors/space_api/spacetypes.cc index 8dc4b257881..80720eeee81 100644 --- a/source/blender/editors/space_api/spacetypes.cc +++ b/source/blender/editors/space_api/spacetypes.cc @@ -84,7 +84,7 @@ void ED_spacetypes_init() ED_spacetype_clip(); ED_spacetype_statusbar(); ED_spacetype_topbar(); - ED_spacetype_spreadsheet(); + spreadsheet::register_spacetype(); /* Register operator types for screen and all spaces. */ ED_operatortypes_userpref(); diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc index f86eecf6431..98b743dd3cf 100644 --- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc +++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc @@ -48,8 +48,7 @@ #include -using namespace blender; -using namespace blender::ed::spreadsheet; +namespace blender::ed::spreadsheet { static SpaceLink *spreadsheet_create(const ScrArea * /*area*/, const Scene * /*scene*/) { @@ -193,7 +192,7 @@ static void spreadsheet_main_region_init(wmWindowManager *wm, ARegion *region) } } -ID *ED_spreadsheet_get_current_id(const SpaceSpreadsheet *sspreadsheet) +ID *get_current_id(const SpaceSpreadsheet *sspreadsheet) { if (BLI_listbase_is_empty(&sspreadsheet->viewer_path.path)) { return nullptr; @@ -296,7 +295,7 @@ static void spreadsheet_update_context(const bContext *C) Object *spreadsheet_get_object_eval(const SpaceSpreadsheet *sspreadsheet, const Depsgraph *depsgraph) { - ID *used_id = ED_spreadsheet_get_current_id(sspreadsheet); + ID *used_id = get_current_id(sspreadsheet); if (used_id == nullptr) { return nullptr; } @@ -717,7 +716,7 @@ static void spreadsheet_blend_write(BlendWriter *writer, SpaceLink *sl) BKE_viewer_path_blend_write(writer, &sspreadsheet->viewer_path); } -void ED_spacetype_spreadsheet() +void register_spacetype() { SpaceType *st = MEM_cnew("spacetype spreadsheet"); ARegionType *art; @@ -801,8 +800,10 @@ void ED_spacetype_spreadsheet() art->init = ED_region_panels_init; art->draw = spreadsheet_dataset_region_draw; art->listener = spreadsheet_dataset_region_listener; - blender::ed::spreadsheet::spreadsheet_data_set_region_panels_register(*art); + spreadsheet_data_set_region_panels_register(*art); BLI_addhead(&st->regiontypes, art); BKE_spacetype_register(st); } + +} // namespace blender::ed::spreadsheet diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index 2bdc7796af0..b8d29a00d62 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -487,14 +487,14 @@ std::unique_ptr VolumeDataSource::get_column_values( if (STREQ(column_id.name, "Grid Name")) { return std::make_unique( IFACE_("Grid Name"), VArray::ForFunc(size, [volume](int64_t index) { - const blender::bke::VolumeGridData *volume_grid = BKE_volume_grid_get(volume, index); + const bke::VolumeGridData *volume_grid = BKE_volume_grid_get(volume, index); return volume_grid->name(); })); } if (STREQ(column_id.name, "Data Type")) { return std::make_unique( IFACE_("Data Type"), VArray::ForFunc(size, [volume](int64_t index) { - const blender::bke::VolumeGridData *volume_grid = BKE_volume_grid_get(volume, index); + const bke::VolumeGridData *volume_grid = BKE_volume_grid_get(volume, index); const VolumeGridType type = volume_grid->grid_type(); const char *name = nullptr; RNA_enum_name_from_value(rna_enum_volume_grid_data_type_items, type, &name); @@ -504,7 +504,7 @@ std::unique_ptr VolumeDataSource::get_column_values( if (STREQ(column_id.name, "Class")) { return std::make_unique( IFACE_("Class"), VArray::ForFunc(size, [volume](int64_t index) { - const blender::bke::VolumeGridData *volume_grid = BKE_volume_grid_get(volume, index); + const bke::VolumeGridData *volume_grid = BKE_volume_grid_get(volume, index); openvdb::GridClass grid_class = volume_grid->grid_class(); if (grid_class == openvdb::GridClass::GRID_FOG_VOLUME) { return IFACE_("Fog Volume"); diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh b/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh index e4412b38dfb..96c0257ba00 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh @@ -7,13 +7,18 @@ #include "BKE_geometry_set.hh" #include "spreadsheet_cache.hh" +struct ARegionType; +struct bContext; + +namespace blender::ed::spreadsheet { + struct SpaceSpreadsheet_Runtime { public: int visible_rows = 0; int tot_rows = 0; int tot_columns = 0; - blender::ed::spreadsheet::SpreadsheetCache cache; + SpreadsheetCache cache; SpaceSpreadsheet_Runtime() = default; @@ -24,15 +29,10 @@ struct SpaceSpreadsheet_Runtime { } }; -struct ARegionType; -struct bContext; - void spreadsheet_operatortypes(); -void spreadsheet_update_context_path(const bContext *C); Object *spreadsheet_get_object_eval(const SpaceSpreadsheet *sspreadsheet, const Depsgraph *depsgraph); -namespace blender::ed::spreadsheet { bke::GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *sspreadsheet, Object *object_eval); diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_ops.cc b/source/blender/editors/space_spreadsheet/spreadsheet_ops.cc index 021719194cc..412e90a9e88 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_ops.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_ops.cc @@ -22,7 +22,7 @@ #include "spreadsheet_intern.hh" #include "spreadsheet_row_filter.hh" -using namespace blender::ed::spreadsheet; +namespace blender::ed::spreadsheet { static int row_filter_add_exec(bContext *C, wmOperator * /*op*/) { @@ -82,9 +82,7 @@ static void SPREADSHEET_OT_remove_row_filter_rule(wmOperatorType *ot) static int select_component_domain_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) { - using namespace blender; - const auto component_type = blender::bke::GeometryComponent::Type( - RNA_int_get(op->ptr, "component_type")); + const auto component_type = bke::GeometryComponent::Type(RNA_int_get(op->ptr, "component_type")); bke::AttrDomain domain = bke::AttrDomain(RNA_int_get(op->ptr, "attribute_domain_type")); SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); @@ -126,3 +124,5 @@ void spreadsheet_operatortypes() WM_operatortype_append(SPREADSHEET_OT_remove_row_filter_rule); WM_operatortype_append(SPREADSHEET_OT_change_spreadsheet_data_source); } + +} // namespace blender::ed::spreadsheet diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc index 115cbc01f05..2df1091dd3e 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc @@ -31,8 +31,7 @@ #include -using namespace blender; -using namespace blender::ed::spreadsheet; +namespace blender::ed::spreadsheet { static void filter_panel_id_fn(void * /*row_filter_v*/, char *r_name) { @@ -367,3 +366,5 @@ void register_row_filter_panels(ARegionType ®ion_type) BLI_addtail(®ion_type.paneltypes, panel_type); } } + +} // namespace blender::ed::spreadsheet diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.hh b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.hh index 6737df64652..e490344be7d 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.hh @@ -6,4 +6,8 @@ struct ARegionType; +namespace blender::ed::spreadsheet { + void register_row_filter_panels(ARegionType ®ion_type); + +} diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 727766faf39..2f4ea48cea1 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -76,19 +76,22 @@ namespace blender::ed::text { struct SpaceText_Runtime; } // namespace blender::ed::text using SpaceText_Runtime = blender::ed::text::SpaceText_Runtime; + +namespace blender::ed::spreadsheet { +struct SpaceSpreadsheet_Runtime; +} // namespace blender::ed::spreadsheet +using SpaceSpreadsheet_Runtime = blender::ed::spreadsheet::SpaceSpreadsheet_Runtime; #else typedef struct SpaceNode_Runtime SpaceNode_Runtime; typedef struct SpaceOutliner_Runtime SpaceOutliner_Runtime; typedef struct SpaceSeq_Runtime SpaceSeq_Runtime; typedef struct SpaceText_Runtime SpaceText_Runtime; +typedef struct SpaceSpreadsheet_Runtime SpaceSpreadsheet_Runtime; #endif /** Defined in `file_intern.hh`. */ typedef struct SpaceFile_Runtime SpaceFile_Runtime; -/** Defined in `spreadsheet_intern.hh`. */ -typedef struct SpaceSpreadsheet_Runtime SpaceSpreadsheet_Runtime; - /* -------------------------------------------------------------------- */ /** \name SpaceLink (Base) * \{ */ diff --git a/source/blender/makesrna/intern/rna_space.cc b/source/blender/makesrna/intern/rna_space.cc index 0f89dc0692b..cf7f76e5386 100644 --- a/source/blender/makesrna/intern/rna_space.cc +++ b/source/blender/makesrna/intern/rna_space.cc @@ -3297,7 +3297,7 @@ const EnumPropertyItem *rna_SpaceSpreadsheet_attribute_domain_itemf(bContext * / SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)ptr->data; auto component_type = bke::GeometryComponent::Type(sspreadsheet->geometry_component_type); if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL) { - ID *used_id = ED_spreadsheet_get_current_id(sspreadsheet); + ID *used_id = ed::spreadsheet::get_current_id(sspreadsheet); if (used_id != nullptr) { if (GS(used_id->name) == ID_OB) { Object *used_object = (Object *)used_id; -- 2.30.2 From b3cea962282c62057fbe1a5ae38c83d8475e0f7a Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 31 Jan 2024 17:16:25 -0500 Subject: [PATCH 091/244] Cleanup: Use StringRef arguments for some UI button functions The benefits are O(1) access to the string size, clearer ownership, and easily accessible utility functions. For now, only change functions where the string is clearly non-null. It's not clear in what cases some other functions recieve null strings. --- .../blender/editors/include/UI_interface_c.hh | 30 +++++++-------- source/blender/editors/interface/interface.cc | 37 +++++++++---------- 2 files changed, 33 insertions(+), 34 deletions(-) diff --git a/source/blender/editors/include/UI_interface_c.hh b/source/blender/editors/include/UI_interface_c.hh index 413900a7e81..feb73d810a1 100644 --- a/source/blender/editors/include/UI_interface_c.hh +++ b/source/blender/editors/include/UI_interface_c.hh @@ -988,7 +988,7 @@ bool UI_but_is_userdef(const uiBut *but); uiBut *uiDefBut(uiBlock *block, int type, int retval, - const char *str, + blender::StringRef str, int x, int y, short width, @@ -1002,7 +1002,7 @@ uiBut *uiDefBut(uiBlock *block, uiBut *uiDefButF(uiBlock *block, int type, int retval, - const char *str, + blender::StringRef str, int x, int y, short width, @@ -1016,7 +1016,7 @@ uiBut *uiDefButF(uiBlock *block, uiBut *uiDefButI(uiBlock *block, int type, int retval, - const char *str, + blender::StringRef str, int x, int y, short width, @@ -1031,7 +1031,7 @@ uiBut *uiDefButBitI(uiBlock *block, int type, int bit, int retval, - const char *str, + blender::StringRef str, int x, int y, short width, @@ -1045,7 +1045,7 @@ uiBut *uiDefButBitI(uiBlock *block, uiBut *uiDefButS(uiBlock *block, int type, int retval, - const char *str, + blender::StringRef str, int x, int y, short width, @@ -1060,7 +1060,7 @@ uiBut *uiDefButBitS(uiBlock *block, int type, int bit, int retval, - const char *str, + blender::StringRef str, int x, int y, short width, @@ -1074,7 +1074,7 @@ uiBut *uiDefButBitS(uiBlock *block, uiBut *uiDefButC(uiBlock *block, int type, int retval, - const char *str, + blender::StringRef str, int x, int y, short width, @@ -1089,7 +1089,7 @@ uiBut *uiDefButBitC(uiBlock *block, int type, int bit, int retval, - const char *str, + blender::StringRef str, int x, int y, short width, @@ -1303,7 +1303,7 @@ uiBut *uiDefIconTextBut(uiBlock *block, int type, int retval, int icon, - const char *str, + blender::StringRef str, int x, int y, short width, @@ -1318,7 +1318,7 @@ uiBut *uiDefIconTextButF(uiBlock *block, int type, int retval, int icon, - const char *str, + blender::StringRef str, int x, int y, short width, @@ -1333,7 +1333,7 @@ uiBut *uiDefIconTextButI(uiBlock *block, int type, int retval, int icon, - const char *str, + blender::StringRef str, int x, int y, short width, @@ -1499,7 +1499,7 @@ int UI_icon_from_keymap_item(const wmKeyMapItem *kmi, int r_icon_mod[4]); uiBut *uiDefMenuBut(uiBlock *block, uiMenuCreateFunc func, void *arg, - const char *str, + blender::StringRef str, int x, int y, short width, @@ -1509,7 +1509,7 @@ uiBut *uiDefIconTextMenuBut(uiBlock *block, uiMenuCreateFunc func, void *arg, int icon, - const char *str, + blender::StringRef str, int x, int y, short width, @@ -1528,7 +1528,7 @@ uiBut *uiDefIconMenuBut(uiBlock *block, uiBut *uiDefBlockBut(uiBlock *block, uiBlockCreateFunc func, void *arg, - const char *str, + blender::StringRef str, int x, int y, short width, @@ -1537,7 +1537,7 @@ uiBut *uiDefBlockBut(uiBlock *block, uiBut *uiDefBlockButN(uiBlock *block, uiBlockCreateFunc func, void *argN, - const char *str, + blender::StringRef str, int x, int y, short width, diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc index c91f43cd8be..8b96529f9f4 100644 --- a/source/blender/editors/interface/interface.cc +++ b/source/blender/editors/interface/interface.cc @@ -78,6 +78,7 @@ #include "interface_intern.hh" +using blender::StringRef; using blender::Vector; /* prototypes. */ @@ -4050,8 +4051,6 @@ uiBut *ui_but_change_type(uiBut *but, eButType new_type) } /** - * \brief ui_def_but is the function that draws many button types - * * \param x, y: The lower left hand corner of the button (X axis) * \param width, height: The size of the button. * @@ -4063,7 +4062,7 @@ uiBut *ui_but_change_type(uiBut *but, eButType new_type) static uiBut *ui_def_but(uiBlock *block, int type, int retval, - const char *str, + const StringRef str, int x, int y, short width, @@ -4805,7 +4804,7 @@ static uiBut *ui_def_but_operator_ptr(uiBlock *block, uiBut *uiDefBut(uiBlock *block, int type, int retval, - const char *str, + const StringRef str, int x, int y, short width, @@ -5008,7 +5007,7 @@ static uiBut *uiDefButBit(uiBlock *block, int type, int bit, int retval, - const char *str, + const StringRef str, int x, int y, short width, @@ -5042,7 +5041,7 @@ static uiBut *uiDefButBit(uiBlock *block, uiBut *uiDefButF(uiBlock *block, int type, int retval, - const char *str, + const StringRef str, int x, int y, short width, @@ -5072,7 +5071,7 @@ uiBut *uiDefButF(uiBlock *block, uiBut *uiDefButI(uiBlock *block, int type, int retval, - const char *str, + const StringRef str, int x, int y, short width, @@ -5103,7 +5102,7 @@ uiBut *uiDefButBitI(uiBlock *block, int type, int bit, int retval, - const char *str, + const StringRef str, int x, int y, short width, @@ -5134,7 +5133,7 @@ uiBut *uiDefButBitI(uiBlock *block, uiBut *uiDefButS(uiBlock *block, int type, int retval, - const char *str, + const StringRef str, int x, int y, short width, @@ -5165,7 +5164,7 @@ uiBut *uiDefButBitS(uiBlock *block, int type, int bit, int retval, - const char *str, + const StringRef str, int x, int y, short width, @@ -5196,7 +5195,7 @@ uiBut *uiDefButBitS(uiBlock *block, uiBut *uiDefButC(uiBlock *block, int type, int retval, - const char *str, + const StringRef str, int x, int y, short width, @@ -5227,7 +5226,7 @@ uiBut *uiDefButBitC(uiBlock *block, int type, int bit, int retval, - const char *str, + const StringRef str, int x, int y, short width, @@ -5624,7 +5623,7 @@ uiBut *uiDefIconTextBut(uiBlock *block, int type, int retval, int icon, - const char *str, + const StringRef str, int x, int y, short width, @@ -5646,7 +5645,7 @@ uiBut *uiDefIconTextButF(uiBlock *block, int type, int retval, int icon, - const char *str, + const StringRef str, int x, int y, short width, @@ -5678,7 +5677,7 @@ uiBut *uiDefIconTextButI(uiBlock *block, int type, int retval, int icon, - const char *str, + const StringRef str, int x, int y, short width, @@ -6085,7 +6084,7 @@ void UI_but_func_pushed_state_set(uiBut *but, std::function uiBut *uiDefBlockBut(uiBlock *block, uiBlockCreateFunc func, void *arg, - const char *str, + const StringRef str, int x, int y, short width, @@ -6102,7 +6101,7 @@ uiBut *uiDefBlockBut(uiBlock *block, uiBut *uiDefBlockButN(uiBlock *block, uiBlockCreateFunc func, void *argN, - const char *str, + const StringRef str, int x, int y, short width, @@ -6123,7 +6122,7 @@ uiBut *uiDefBlockButN(uiBlock *block, uiBut *uiDefMenuBut(uiBlock *block, uiMenuCreateFunc func, void *arg, - const char *str, + const StringRef str, int x, int y, short width, @@ -6141,7 +6140,7 @@ uiBut *uiDefIconTextMenuBut(uiBlock *block, uiMenuCreateFunc func, void *arg, int icon, - const char *str, + const StringRef str, int x, int y, short width, -- 2.30.2 From 91bc69f069b2efcec727efa1189b06fb9c9f2097 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 1 Feb 2024 15:12:29 +0100 Subject: [PATCH 092/244] Sort assets in asset shelf by catalogs (and withing catalogs by name) --- .../editors/asset/intern/asset_list.cc | 2 +- source/blender/editors/space_file/filelist.cc | 50 +++++++++++++++++++ source/blender/makesdna/DNA_space_types.h | 2 + 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/asset/intern/asset_list.cc b/source/blender/editors/asset/intern/asset_list.cc index 2a346e60811..ff0dd856a98 100644 --- a/source/blender/editors/asset/intern/asset_list.cc +++ b/source/blender/editors/asset/intern/asset_list.cc @@ -144,7 +144,7 @@ void AssetList::setup() /* Relevant bits from file_refresh(). */ /* TODO pass options properly. */ filelist_setrecursion(files, FILE_SELECT_MAX_RECURSIONS); - filelist_setsorting(files, FILE_SORT_ALPHA, false); + filelist_setsorting(files, FILE_SORT_ASSET_CATALOG, false); filelist_setlibrary(files, &library_ref_); filelist_setfilter_options( files, diff --git a/source/blender/editors/space_file/filelist.cc b/source/blender/editors/space_file/filelist.cc index 04fcd9217c6..bbdb300a6f4 100644 --- a/source/blender/editors/space_file/filelist.cc +++ b/source/blender/editors/space_file/filelist.cc @@ -571,6 +571,53 @@ static int compare_extension(void *user_data, const void *a1, const void *a2) return compare_apply_inverted(compare_tiebreaker(entry1, entry2), sort_data); } +static int compare_asset_catalog(void *user_data, const void *a1, const void *a2) +{ + const FileListInternEntry *entry1 = static_cast(a1); + const FileListInternEntry *entry2 = static_cast(a2); + const FileSortData *sort_data = static_cast(user_data); + + if (entry1->asset && !entry2->asset) { + return 1; + } + if (!entry1->asset && entry2->asset) { + return -1; + } + if (!entry1->asset && !entry2->asset) { + if (int ret = compare_direntry_generic(entry1, entry2); ret) { + return ret; + } + + return compare_apply_inverted(compare_tiebreaker(entry1, entry2), sort_data); + } + + const asset_system::AssetLibrary &asset_library1 = entry1->asset->owner_asset_library(); + const asset_system::AssetLibrary &asset_library2 = entry2->asset->owner_asset_library(); + + const asset_system::AssetCatalog *catalog1 = asset_library1.catalog_service->find_catalog( + entry1->asset->get_metadata().catalog_id); + const asset_system::AssetCatalog *catalog2 = asset_library2.catalog_service->find_catalog( + entry2->asset->get_metadata().catalog_id); + + /* Always keep assets without catalog last. */ + if (catalog1 && !catalog2) { + return 1; + } + if (!catalog1 && catalog2) { + return -1; + } + + if (catalog1 && catalog2) { + const int order = BLI_strcasecmp_natural(catalog1->path.name().c_str(), + catalog2->path.name().c_str()); + if (order) { + return compare_apply_inverted(order, sort_data); + } + } + + return compare_apply_inverted(compare_tiebreaker(entry1, entry2), sort_data); +} + void filelist_sort(FileList *filelist) { if (filelist->flags & FL_NEED_SORTING) { @@ -589,6 +636,9 @@ void filelist_sort(FileList *filelist) case FILE_SORT_EXTENSION: sort_cb = compare_extension; break; + case FILE_SORT_ASSET_CATALOG: + sort_cb = compare_asset_catalog; + break; case FILE_SORT_DEFAULT: default: BLI_assert(0); diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 2f4ea48cea1..325129a2eeb 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -983,6 +983,8 @@ enum eFileSortType { FILE_SORT_EXTENSION = 2, FILE_SORT_TIME = 3, FILE_SORT_SIZE = 4, + /* Assets: Sort by catalog. Within each catalog, assets will be sorted by name. */ + FILE_SORT_ASSET_CATALOG = 5, }; /** #SpaceFile.tags */ -- 2.30.2 From 128d9e142ed0d2527f2dc169aab4f1a9dc444b34 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 5 Feb 2024 16:05:46 +0100 Subject: [PATCH 093/244] Bind active brush two asset shelf active item Adds a new `bpy.types.AssetShelf.get_active_asset()` method to return an active asset reference. --- scripts/startup/bl_ui/space_view3d.py | 5 ++ .../tests/asset_representation_test.cc | 69 +++++++++++++++++++ source/blender/blenkernel/BKE_asset.hh | 2 +- source/blender/blenkernel/BKE_screen.hh | 2 + .../blenkernel/intern/asset_weak_reference.cc | 37 +++++++++- .../editors/asset/intern/asset_shelf.cc | 5 +- .../asset/intern/asset_shelf_asset_view.cc | 26 +++++++ .../blender/editors/include/UI_interface_c.hh | 2 +- .../editors/interface/views/interface_view.cc | 5 +- .../blender/editors/sculpt_paint/paint_ops.cc | 1 + source/blender/makesdna/DNA_asset_types.h | 4 ++ source/blender/makesrna/intern/rna_ui.cc | 41 ++++++++++- 12 files changed, 189 insertions(+), 10 deletions(-) diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index d3c69726261..a0ac1d54bf9 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -8779,6 +8779,11 @@ class BrushAssetShelf: return asset.metadata.get(cls.mode_prop, False) + @classmethod + def get_active_asset(cls): + paint_settings = UnifiedPaintPanel.paint_settings(bpy.context) + return paint_settings.brush_asset_reference if paint_settings else None + class VIEW3D_AST_brush_sculpt(BrushAssetShelf, bpy.types.AssetShelf): mode = 'SCULPT' diff --git a/source/blender/asset_system/tests/asset_representation_test.cc b/source/blender/asset_system/tests/asset_representation_test.cc index 93934dc4927..43c5f2b6d03 100644 --- a/source/blender/asset_system/tests/asset_representation_test.cc +++ b/source/blender/asset_system/tests/asset_representation_test.cc @@ -73,6 +73,75 @@ TEST_F(AssetRepresentationTest, weak_reference__custom_library) } } +TEST_F(AssetRepresentationTest, weak_reference__compare) +{ + { + AssetWeakReference a; + AssetWeakReference b; + EXPECT_EQ(a, b); + + /* Arbitrary individual member changes to test how it affects the comparison. */ + b.asset_library_identifier = "My lib"; + EXPECT_NE(a, b); + a.asset_library_identifier = "My lib"; + EXPECT_EQ(a, b); + a.asset_library_type = ASSET_LIBRARY_ESSENTIALS; + EXPECT_NE(a, b); + b.asset_library_type = ASSET_LIBRARY_LOCAL; + EXPECT_NE(a, b); + b.asset_library_type = ASSET_LIBRARY_ESSENTIALS; + EXPECT_EQ(a, b); + a.relative_asset_identifier = "Foo"; + EXPECT_NE(a, b); + b.relative_asset_identifier = "Bar"; + EXPECT_NE(a, b); + a.relative_asset_identifier = "Bar"; + EXPECT_EQ(a, b); + + /* Make the destructor work. */ + a.asset_library_identifier = b.asset_library_identifier = nullptr; + a.relative_asset_identifier = b.relative_asset_identifier = nullptr; + } + + { + AssetWeakReference a; + a.asset_library_type = ASSET_LIBRARY_LOCAL; + a.asset_library_identifier = "My custom lib"; + a.relative_asset_identifier = "path/to/an/asset"; + + AssetWeakReference b; + EXPECT_NE(a, b); + + b.asset_library_type = ASSET_LIBRARY_LOCAL; + b.asset_library_identifier = "My custom lib"; + b.relative_asset_identifier = "path/to/an/asset"; + EXPECT_EQ(a, b); + + /* Make the destructor work. */ + a.asset_library_identifier = b.asset_library_identifier = nullptr; + a.relative_asset_identifier = b.relative_asset_identifier = nullptr; + } + + { + AssetLibraryService *service = AssetLibraryService::get(); + AssetLibrary *const library = service->get_asset_library_on_disk_custom("My custom lib", + asset_library_root_); + AssetRepresentation &asset = add_dummy_asset(*library, "path/to/an/asset"); + + AssetWeakReference *weak_ref = asset.make_weak_reference(); + AssetWeakReference other; + other.asset_library_type = ASSET_LIBRARY_CUSTOM; + other.asset_library_identifier = "My custom lib"; + other.relative_asset_identifier = "path/to/an/asset"; + EXPECT_EQ(*weak_ref, other); + BKE_asset_weak_reference_free(&weak_ref); + + /* Make the destructor work. */ + other.asset_library_identifier = nullptr; + other.relative_asset_identifier = nullptr; + } +} + TEST_F(AssetRepresentationTest, weak_reference__resolve_to_full_path__current_file) { AssetLibraryService *service = AssetLibraryService::get(); diff --git a/source/blender/blenkernel/BKE_asset.hh b/source/blender/blenkernel/BKE_asset.hh index 110c0af7469..7b70d8bbe93 100644 --- a/source/blender/blenkernel/BKE_asset.hh +++ b/source/blender/blenkernel/BKE_asset.hh @@ -78,6 +78,6 @@ void BKE_asset_metadata_read(BlendDataReader *reader, AssetMetaData *asset_data) /** Frees the weak reference and its data, and nulls the given pointer. */ void BKE_asset_weak_reference_free(AssetWeakReference **weak_ref); -AssetWeakReference *BKE_asset_weak_reference_copy(AssetWeakReference *weak_ref); +AssetWeakReference *BKE_asset_weak_reference_copy(const 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); diff --git a/source/blender/blenkernel/BKE_screen.hh b/source/blender/blenkernel/BKE_screen.hh index 3665ffdae24..530ce8a947e 100644 --- a/source/blender/blenkernel/BKE_screen.hh +++ b/source/blender/blenkernel/BKE_screen.hh @@ -540,6 +540,8 @@ struct AssetShelfType { const blender::asset_system::AssetRepresentation *asset, uiLayout *layout); + const AssetWeakReference *(*get_active_asset)(const AssetShelfType *shelf_type); + /* RNA integration */ ExtensionRNA rna_ext; }; diff --git a/source/blender/blenkernel/intern/asset_weak_reference.cc b/source/blender/blenkernel/intern/asset_weak_reference.cc index f1d0d06f72a..1b314972127 100644 --- a/source/blender/blenkernel/intern/asset_weak_reference.cc +++ b/source/blender/blenkernel/intern/asset_weak_reference.cc @@ -46,13 +46,44 @@ AssetWeakReference::~AssetWeakReference() MEM_delete(relative_asset_identifier); } +AssetWeakReference &AssetWeakReference::operator=(AssetWeakReference &&other) +{ + if (&other == this) { + return *this; + } + asset_library_type = other.asset_library_type; + asset_library_identifier = other.asset_library_identifier; + relative_asset_identifier = other.relative_asset_identifier; + other.asset_library_type = 0; /* Not a valid type. */ + other.asset_library_identifier = nullptr; + other.relative_asset_identifier = nullptr; + return *this; +} + +bool AssetWeakReference::operator==(const AssetWeakReference &other) const +{ + if (asset_library_type != other.asset_library_type) { + return false; + } + if (StringRef(asset_library_identifier) != StringRef(other.asset_library_identifier)) { + return false; + } + + return StringRef(relative_asset_identifier) == StringRef(other.relative_asset_identifier); +} + +bool AssetWeakReference::operator!=(const AssetWeakReference &other) const +{ + return !(*this == other); +} + void BKE_asset_weak_reference_free(AssetWeakReference **weak_ref) { MEM_delete(*weak_ref); *weak_ref = nullptr; } -AssetWeakReference *BKE_asset_weak_reference_copy(AssetWeakReference *weak_ref) +AssetWeakReference *BKE_asset_weak_reference_copy(const AssetWeakReference *weak_ref) { if (weak_ref == nullptr) { return nullptr; @@ -60,8 +91,8 @@ AssetWeakReference *BKE_asset_weak_reference_copy(AssetWeakReference *weak_ref) AssetWeakReference *weak_ref_copy = MEM_new(__func__); weak_ref_copy->asset_library_type = weak_ref->asset_library_type; - weak_ref_copy->asset_library_identifier = BLI_strdup(weak_ref->asset_library_identifier); - weak_ref_copy->relative_asset_identifier = BLI_strdup(weak_ref->relative_asset_identifier); + weak_ref_copy->asset_library_identifier = BLI_strdup_null(weak_ref->asset_library_identifier); + weak_ref_copy->relative_asset_identifier = BLI_strdup_null(weak_ref->relative_asset_identifier); return weak_ref_copy; } diff --git a/source/blender/editors/asset/intern/asset_shelf.cc b/source/blender/editors/asset/intern/asset_shelf.cc index 80e16bb1209..80e542b48ed 100644 --- a/source/blender/editors/asset/intern/asset_shelf.cc +++ b/source/blender/editors/asset/intern/asset_shelf.cc @@ -243,6 +243,9 @@ static void asset_shelf_region_listen(const wmRegionListenerParams *params) ED_region_tag_redraw(region); } break; + case NC_ASSET: + ED_region_tag_redraw(region); + break; } } @@ -597,7 +600,7 @@ int context(const bContext *C, const char *member, bContextDataResult *result) /* XXX hack. Get the asset from the active item, but needs to be the file... */ if (CTX_data_equals(member, "active_file")) { const ARegion *region = CTX_wm_region(C); - const uiBut *but = UI_region_views_find_active_item_but(region); + const uiBut *but = UI_region_views_find_mouse_over_but(CTX_wm_window(C), region); if (!but) { return CTX_RESULT_NO_DATA; } diff --git a/source/blender/editors/asset/intern/asset_shelf_asset_view.cc b/source/blender/editors/asset/intern/asset_shelf_asset_view.cc index 9d958d62896..1d3542c4cdb 100644 --- a/source/blender/editors/asset/intern/asset_shelf_asset_view.cc +++ b/source/blender/editors/asset/intern/asset_shelf_asset_view.cc @@ -11,6 +11,7 @@ #include "AS_asset_library.hh" #include "AS_asset_representation.hh" +#include "BKE_asset.hh" #include "BKE_screen.hh" #include "BLI_fnmatch.h" @@ -40,6 +41,7 @@ namespace blender::ed::asset::shelf { class AssetView : public ui::AbstractGridView { const AssetLibraryReference library_ref_; const AssetShelf &shelf_; + AssetWeakReference *active_asset_ = nullptr; /** Copy of the filter string from #AssetShelfSettings, with extra '*' added to the beginning and * end of the string, for `fnmatch()` to work. */ char search_string[sizeof(AssetShelfSettings::search_string) + 2] = ""; @@ -50,6 +52,7 @@ class AssetView : public ui::AbstractGridView { public: AssetView(const AssetLibraryReference &library_ref, const AssetShelf &shelf); + ~AssetView(); void build_items() override; bool begin_filtering(const bContext &C) const override; @@ -70,6 +73,7 @@ class AssetViewItem : public ui::PreviewGridItem { void disable_asset_drag(); void build_grid_tile(uiLayout &layout) const override; void build_context_menu(bContext &C, uiLayout &column) const override; + std::optional should_be_active() const override; bool is_filtered_visible() const override; std::unique_ptr create_drag_controller() const override; @@ -92,6 +96,14 @@ AssetView::AssetView(const AssetLibraryReference &library_ref, const AssetShelf BLI_strncpy_ensure_pad( search_string, shelf.settings.search_string, '*', sizeof(search_string)); } + if (shelf.type->get_active_asset) { + active_asset_ = BKE_asset_weak_reference_copy(shelf.type->get_active_asset(shelf.type)); + } +} + +AssetView::~AssetView() +{ + BKE_asset_weak_reference_free(&active_asset_); } void AssetView::build_items() @@ -216,6 +228,20 @@ void AssetViewItem::build_context_menu(bContext &C, uiLayout &column) const } } +std::optional AssetViewItem::should_be_active() const +{ + const AssetView &asset_view = dynamic_cast(get_view()); + if (!asset_view.active_asset_) { + return false; + } + const asset_system::AssetRepresentation *asset = handle_get_representation(&asset_); + AssetWeakReference *weak_ref = asset->make_weak_reference(); + const bool matches = *asset_view.active_asset_ == *weak_ref; + + BKE_asset_weak_reference_free(&weak_ref); + return matches; +} + bool AssetViewItem::is_filtered_visible() const { const AssetView &asset_view = dynamic_cast(get_view()); diff --git a/source/blender/editors/include/UI_interface_c.hh b/source/blender/editors/include/UI_interface_c.hh index 44c5758282b..21160039969 100644 --- a/source/blender/editors/include/UI_interface_c.hh +++ b/source/blender/editors/include/UI_interface_c.hh @@ -3418,4 +3418,4 @@ uiViewHandle *UI_region_view_find_at(const ARegion *region, const int xy[2], int uiViewItemHandle *UI_region_views_find_item_at(const ARegion *region, const int xy[2]) ATTR_NONNULL(); uiViewItemHandle *UI_region_views_find_active_item(const ARegion *region); -uiBut *UI_region_views_find_active_item_but(const ARegion *region); +uiBut *UI_region_views_find_mouse_over_but(const wmWindow *win, const ARegion *region); diff --git a/source/blender/editors/interface/views/interface_view.cc b/source/blender/editors/interface/views/interface_view.cc index c25560a9e14..5fc9672c20b 100644 --- a/source/blender/editors/interface/views/interface_view.cc +++ b/source/blender/editors/interface/views/interface_view.cc @@ -22,6 +22,7 @@ #include #include "DNA_screen_types.h" +#include "DNA_windowmanager_types.h" #include "BKE_screen.hh" @@ -199,9 +200,9 @@ uiViewItemHandle *UI_region_views_find_active_item(const ARegion *region) return item_but->view_item; } -uiBut *UI_region_views_find_active_item_but(const ARegion *region) +uiBut *UI_region_views_find_mouse_over_but(const wmWindow *win, const ARegion *region) { - return ui_view_item_find_active(region); + return ui_view_item_find_mouse_over(region, win->eventstate->xy); } namespace blender::ui { diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index c1340c19810..ed2bb15cd9b 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -1014,6 +1014,7 @@ static int brush_asset_select_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } + WM_main_add_notifier(NC_ASSET | NA_ACTIVATED, nullptr); WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, nullptr); WM_toolsystem_ref_set_by_id(C, "builtin.brush"); diff --git a/source/blender/makesdna/DNA_asset_types.h b/source/blender/makesdna/DNA_asset_types.h index 0c6ba9fdcd9..d3f7022cd3a 100644 --- a/source/blender/makesdna/DNA_asset_types.h +++ b/source/blender/makesdna/DNA_asset_types.h @@ -180,6 +180,10 @@ typedef struct AssetWeakReference { AssetWeakReference(AssetWeakReference &&); AssetWeakReference(const AssetWeakReference &) = delete; ~AssetWeakReference(); + AssetWeakReference &operator=(AssetWeakReference &&); + + bool operator==(const AssetWeakReference &other) const; + bool operator!=(const AssetWeakReference &other) const; /** * See AssetRepresentation::make_weak_reference(). Must be freed using diff --git a/source/blender/makesrna/intern/rna_ui.cc b/source/blender/makesrna/intern/rna_ui.cc index 0ae5605a8b4..95c6992947e 100644 --- a/source/blender/makesrna/intern/rna_ui.cc +++ b/source/blender/makesrna/intern/rna_ui.cc @@ -1145,6 +1145,29 @@ static bool asset_shelf_poll(const bContext *C, const AssetShelfType *shelf_type return is_visible; } +static const AssetWeakReference *asset_shelf_get_active_asset(const AssetShelfType *shelf_type) +{ + extern FunctionRNA rna_AssetShelf_get_active_asset_func; + + PointerRNA ptr = RNA_pointer_create(nullptr, shelf_type->rna_ext.srna, nullptr); /* dummy */ + + FunctionRNA *func = &rna_AssetShelf_get_active_asset_func; + // RNA_struct_find_function(&ptr, "get_active_asset"); + + ParameterList list; + RNA_parameter_list_create(&list, &ptr, func); + shelf_type->rna_ext.call(nullptr, &ptr, func, &list); + + void *ret; + RNA_parameter_get_lookup(&list, "asset_reference", &ret); + /* Get the value before freeing. */ + AssetWeakReference *active_asset = *(AssetWeakReference **)ret; + + RNA_parameter_list_free(&list); + + return active_asset; +} + static void asset_shelf_draw_context_menu(const bContext *C, const AssetShelfType *shelf_type, const AssetRepresentationHandle *asset, @@ -1208,7 +1231,7 @@ static StructRNA *rna_AssetShelf_register(Main *bmain, dummy_shelf.type = &dummy_shelf_type; PointerRNA dummy_shelf_ptr = RNA_pointer_create(nullptr, &RNA_AssetShelf, &dummy_shelf); - bool have_function[3]; + bool have_function[4]; /* validate the python class */ if (validate(&dummy_shelf_ptr, data, have_function) != 0) { @@ -1265,7 +1288,8 @@ static StructRNA *rna_AssetShelf_register(Main *bmain, shelf_type->poll = have_function[0] ? asset_shelf_poll : nullptr; shelf_type->asset_poll = have_function[1] ? asset_shelf_asset_poll : nullptr; - shelf_type->draw_context_menu = have_function[2] ? asset_shelf_draw_context_menu : nullptr; + shelf_type->get_active_asset = have_function[2] ? asset_shelf_get_active_asset : nullptr; + shelf_type->draw_context_menu = have_function[3] ? asset_shelf_draw_context_menu : nullptr; BLI_addtail(&space_type->asset_shelf_types, shelf_type); @@ -2314,6 +2338,19 @@ static void rna_def_asset_shelf(BlenderRNA *brna) parm = RNA_def_pointer(func, "asset", "AssetRepresentation", "", ""); RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); + func = RNA_def_function(srna, "get_active_asset", nullptr); + RNA_def_function_ui_description( + func, + "Return a reference to the asset that should be highlighted as active in the asset shelf"); + RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_REGISTER_OPTIONAL); + /* return type */ + parm = RNA_def_pointer(func, + "asset_reference", + "AssetWeakReference", + "", + "The weak reference to the asset to be hightlighted as active, or None"); + RNA_def_function_return(func, parm); + func = RNA_def_function(srna, "draw_context_menu", nullptr); RNA_def_function_ui_description( func, "Draw UI elements into the context menu UI layout displayed on right click"); -- 2.30.2 From 4e17d78a9b4702ddc47a5cd6157ddd3714276777 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 1 Feb 2024 21:23:17 +0100 Subject: [PATCH 094/244] Default preview size of 40 for brush asset shelves --- scripts/startup/bl_ui/space_view3d.py | 1 + source/blender/blenkernel/BKE_screen.hh | 2 ++ source/blender/editors/asset/intern/asset_shelf.cc | 3 ++- source/blender/makesrna/intern/rna_ui.cc | 7 +++++++ 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index 2c2cd89638e..8dcc24a1a4c 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -8768,6 +8768,7 @@ class VIEW3D_PT_viewport_debug(Panel): class BrushAssetShelf: bl_space_type = "VIEW_3D" bl_options = {'NO_ASSET_DRAG'} + bl_default_preview_size = 40 @classmethod def poll(cls, context): diff --git a/source/blender/blenkernel/BKE_screen.hh b/source/blender/blenkernel/BKE_screen.hh index 547d324cfa3..1d80b939c78 100644 --- a/source/blender/blenkernel/BKE_screen.hh +++ b/source/blender/blenkernel/BKE_screen.hh @@ -525,6 +525,8 @@ struct AssetShelfType { AssetShelfTypeFlag flag; + short default_preview_size; + /** Determine if asset shelves of this type should be available in current context or not. */ bool (*poll)(const bContext *C, const AssetShelfType *shelf_type); diff --git a/source/blender/editors/asset/intern/asset_shelf.cc b/source/blender/editors/asset/intern/asset_shelf.cc index 15ea19db484..94cb7a3dba2 100644 --- a/source/blender/editors/asset/intern/asset_shelf.cc +++ b/source/blender/editors/asset/intern/asset_shelf.cc @@ -90,7 +90,8 @@ static AssetShelf *create_shelf_from_type(AssetShelfType &type) { AssetShelf *shelf = MEM_new(__func__); *shelf = dna::shallow_zero_initialize(); - shelf->settings.preview_size = DEFAULT_TILE_SIZE; + shelf->settings.preview_size = type.default_preview_size ? type.default_preview_size : + DEFAULT_TILE_SIZE; shelf->settings.asset_library_reference = asset_system::all_library_reference(); shelf->type = &type; shelf->preferred_row_count = 1; diff --git a/source/blender/makesrna/intern/rna_ui.cc b/source/blender/makesrna/intern/rna_ui.cc index 007a8a37934..6ef63e87639 100644 --- a/source/blender/makesrna/intern/rna_ui.cc +++ b/source/blender/makesrna/intern/rna_ui.cc @@ -2321,6 +2321,13 @@ static void rna_def_asset_shelf(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL | PROP_ENUM_FLAG); RNA_def_property_ui_text(prop, "Options", "Options for this asset shelf type"); + prop = RNA_def_property(srna, "bl_default_preview_size", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_sdna(prop, nullptr, "type->default_preview_size"); + RNA_def_property_range(prop, 32, 256); + RNA_def_property_flag(prop, PROP_REGISTER); + RNA_def_property_ui_text( + prop, "Default Preview Size", "Default size of the asset preview thumbnails in pixels"); + PropertyRNA *parm; FunctionRNA *func; -- 2.30.2 From f8a7a61d1fe98ca4d87dd5772a9877907bf8c73b Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 7 Feb 2024 16:32:47 +0100 Subject: [PATCH 095/244] Fix recently added asset shelf preview-size option not optional --- source/blender/makesrna/intern/rna_ui.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/makesrna/intern/rna_ui.cc b/source/blender/makesrna/intern/rna_ui.cc index 6ef63e87639..be6f3981d74 100644 --- a/source/blender/makesrna/intern/rna_ui.cc +++ b/source/blender/makesrna/intern/rna_ui.cc @@ -2324,7 +2324,7 @@ static void rna_def_asset_shelf(BlenderRNA *brna) prop = RNA_def_property(srna, "bl_default_preview_size", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, nullptr, "type->default_preview_size"); RNA_def_property_range(prop, 32, 256); - RNA_def_property_flag(prop, PROP_REGISTER); + RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); RNA_def_property_ui_text( prop, "Default Preview Size", "Default size of the asset preview thumbnails in pixels"); -- 2.30.2 From 6c74295f54145e0aec823f00d903b754d436b282 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 7 Feb 2024 12:48:30 -0500 Subject: [PATCH 096/244] Fix build error --- source/blender/editors/sculpt_paint/paint_weight.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/sculpt_paint/paint_weight.cc b/source/blender/editors/sculpt_paint/paint_weight.cc index 6e6b9e0504f..87a6d76b15c 100644 --- a/source/blender/editors/sculpt_paint/paint_weight.cc +++ b/source/blender/editors/sculpt_paint/paint_weight.cc @@ -1538,11 +1538,11 @@ static void wpaint_paint_leaves(bContext *C, /** \name Enter Weight Paint Mode * \{ */ -static void grease_pencil_wpaintmode_enter(Scene *scene, Object *ob) +static void grease_pencil_wpaintmode_enter(Main *bmain, Scene *scene, Object *ob) { const PaintMode paint_mode = PaintMode::Weight; Paint *weight_paint = BKE_paint_get_active_from_paintmode(scene, paint_mode); - BKE_paint_ensure(scene->toolsettings, &weight_paint); + BKE_paint_ensure(bmain, scene->toolsettings, &weight_paint); ob->mode |= OB_MODE_WEIGHT_PAINT; @@ -1557,7 +1557,7 @@ void ED_object_wpaintmode_enter_ex(Main *bmain, Depsgraph *depsgraph, Scene *sce vwpaint::mode_enter_generic(bmain, depsgraph, scene, ob, OB_MODE_WEIGHT_PAINT); break; case OB_GREASE_PENCIL: - grease_pencil_wpaintmode_enter(scene, ob); + grease_pencil_wpaintmode_enter(bmain, scene, ob); break; default: BLI_assert_unreachable(); -- 2.30.2 From 44b80df241b684d738d0593feab485578ca65d87 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 7 Feb 2024 19:02:38 +0100 Subject: [PATCH 097/244] Brush Assets: Support adding shortcut to asset shelf items Don't use a keymap for the asset shelf to activate brushes; there's no need to recreate basic UI interaction at this level. Instead use an operator button so the ability to add a shortcut is automaticaly added by other UI code. Also change the operator to use an explicit asset reference instead of the "context asset". This was already done for other operators that add data-blocks from assets. Pull Request: https://projects.blender.org/blender/blender/pulls/117861 --- .../keyconfig/keymap_data/blender_default.py | 18 -------- scripts/startup/bl_ui/space_view3d.py | 1 + source/blender/blenkernel/BKE_screen.hh | 3 ++ .../asset/intern/asset_shelf_asset_view.cc | 20 +++++---- .../blender/editors/include/UI_grid_view.hh | 8 ++++ .../editors/interface/interface_handlers.cc | 1 + .../editors/interface/views/grid_view.cc | 42 +++++++++++++++---- .../blender/editors/sculpt_paint/paint_ops.cc | 22 +++++----- source/blender/makesrna/intern/rna_ui.cc | 29 +++++++++++++ .../windowmanager/intern/wm_keymap_utils.cc | 7 ++-- 10 files changed, 102 insertions(+), 49 deletions(-) diff --git a/scripts/presets/keyconfig/keymap_data/blender_default.py b/scripts/presets/keyconfig/keymap_data/blender_default.py index ade8736e0d3..a400035d414 100644 --- a/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -6802,21 +6802,6 @@ def km_node_link_modal_map(_params): return keymap -def km_asset_shelf_brushes(_params): - items = [] - keymap = ( - "Asset Shelf", - {"space_type": 'EMPTY', "region_type": 'WINDOW'}, - {"items": items}, - ) - - items.extend([ - ("brush.asset_select", {"type": 'LEFTMOUSE', "value": 'CLICK'}, None), - ]) - - return keymap - - # Fallback for gizmos that don't have custom a custom key-map. def km_generic_gizmo(_params): keymap = ( @@ -8652,9 +8637,6 @@ def generate_keymaps(params=None): km_curve_pen_modal_map(params), km_node_link_modal_map(params), - # Asset Shelf Keymaps. - km_asset_shelf_brushes(params), - # Gizmos. km_generic_gizmo(params), km_generic_gizmo_drag(params), diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index 1d5e3c1c090..36a89eb371c 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -8785,6 +8785,7 @@ class VIEW3D_PT_viewport_debug(Panel): class BrushAssetShelf: bl_space_type = "VIEW_3D" bl_options = {'NO_ASSET_DRAG'} + bl_activate_operator = "BRUSH_OT_asset_select" bl_default_preview_size = 40 @classmethod diff --git a/source/blender/blenkernel/BKE_screen.hh b/source/blender/blenkernel/BKE_screen.hh index 1d80b939c78..7b97dfa271d 100644 --- a/source/blender/blenkernel/BKE_screen.hh +++ b/source/blender/blenkernel/BKE_screen.hh @@ -523,6 +523,9 @@ struct AssetShelfType { int space_type; + /** Operator to call when activating a grid view item. */ + std::string activate_operator; + AssetShelfTypeFlag flag; short default_preview_size; diff --git a/source/blender/editors/asset/intern/asset_shelf_asset_view.cc b/source/blender/editors/asset/intern/asset_shelf_asset_view.cc index 1d3542c4cdb..ab4340e4096 100644 --- a/source/blender/editors/asset/intern/asset_shelf_asset_view.cc +++ b/source/blender/editors/asset/intern/asset_shelf_asset_view.cc @@ -23,6 +23,7 @@ #include "ED_asset_handle.hh" #include "ED_asset_list.hh" +#include "ED_asset_menu_utils.hh" #include "ED_asset_shelf.hh" #include "UI_grid_view.hh" @@ -206,16 +207,17 @@ void AssetViewItem::disable_asset_drag() void AssetViewItem::build_grid_tile(uiLayout &layout) const { - PointerRNA file_ptr = RNA_pointer_create( - nullptr, - &RNA_FileSelectEntry, - /* XXX passing file pointer here, should be asset handle or asset representation. */ - const_cast(asset_.file_data)); + const AssetView &asset_view = reinterpret_cast(this->get_view()); + const AssetShelfType &shelf_type = *asset_view.shelf_.type; - uiBlock *block = uiLayoutGetBlock(&layout); - UI_but_context_ptr_set( - block, reinterpret_cast(view_item_but_), "active_file", &file_ptr); - ui::PreviewGridItem::build_grid_tile(layout); + wmOperatorType *ot = WM_operatortype_find(shelf_type.activate_operator.c_str(), true); + PointerRNA op_props = PointerRNA_NULL; + if (ot) { + WM_operator_properties_create_ptr(&op_props, ot); + asset::operator_asset_reference_props_set(*handle_get_representation(&asset_), op_props); + } + + ui::PreviewGridItem::build_grid_tile_button(layout, ot, &op_props); } void AssetViewItem::build_context_menu(bContext &C, uiLayout &column) const diff --git a/source/blender/editors/include/UI_grid_view.hh b/source/blender/editors/include/UI_grid_view.hh index 0f7c957d2cc..05ea6c06d4b 100644 --- a/source/blender/editors/include/UI_grid_view.hh +++ b/source/blender/editors/include/UI_grid_view.hh @@ -19,6 +19,7 @@ #include "UI_resources.hh" struct bContext; +struct PointerRNA; struct uiBlock; struct uiButViewItem; struct uiLayout; @@ -204,6 +205,13 @@ class PreviewGridItem : public AbstractGridViewItem { void build_grid_tile(uiLayout &layout) const override; + /** + * \note: Takes ownership of the operator properies defined in \a op_props. + */ + void build_grid_tile_button(uiLayout &layout, + const wmOperatorType *ot = nullptr, + const PointerRNA *op_props = nullptr) const; + /** * Set a custom callback to execute when activating this view item. This way users don't have to * sub-class #PreviewGridItem, just to implement custom activation behavior (a common thing to diff --git a/source/blender/editors/interface/interface_handlers.cc b/source/blender/editors/interface/interface_handlers.cc index 493c73479c1..73c62b47ee7 100644 --- a/source/blender/editors/interface/interface_handlers.cc +++ b/source/blender/editors/interface/interface_handlers.cc @@ -2335,6 +2335,7 @@ static void ui_apply_but( switch (but_type) { case UI_BTYPE_BUT: case UI_BTYPE_DECORATOR: + case UI_BTYPE_PREVIEW_TILE: ui_apply_but_BUT(C, but, data); break; case UI_BTYPE_TEXT: diff --git a/source/blender/editors/interface/views/grid_view.cc b/source/blender/editors/interface/views/grid_view.cc index 5ce1739139c..d4ae95485db 100644 --- a/source/blender/editors/interface/views/grid_view.cc +++ b/source/blender/editors/interface/views/grid_view.cc @@ -17,6 +17,8 @@ #include "WM_types.hh" +#include "RNA_access.hh" + #include "UI_interface.hh" #include "interface_intern.hh" @@ -409,25 +411,44 @@ PreviewGridItem::PreviewGridItem(StringRef identifier, StringRef label, int prev { } -void PreviewGridItem::build_grid_tile(uiLayout &layout) const +void PreviewGridItem::build_grid_tile_button(uiLayout &layout, + const wmOperatorType *ot, + const PointerRNA *op_props) const { const GridViewStyle &style = this->get_view().get_style(); uiBlock *block = uiLayoutGetBlock(&layout); - uiBut *but = uiDefBut(block, + uiBut *but; + if (ot) { + but = uiDefButO_ptr(block, UI_BTYPE_PREVIEW_TILE, - 0, + const_cast(ot), + WM_OP_INVOKE_REGION_WIN, hide_label_ ? "" : label, 0, 0, style.tile_width, style.tile_height, - nullptr, - 0, - 0, - 0, - 0, ""); + but->opptr = MEM_new(__func__, *op_props); + } + else { + but = uiDefBut(block, + UI_BTYPE_PREVIEW_TILE, + 0, + hide_label_ ? "" : label, + 0, + 0, + style.tile_width, + style.tile_height, + nullptr, + 0, + 0, + 0, + 0, + ""); + } + /* Draw icons that are not previews or images as normal icons with a fixed icon size. Otherwise * they will be upscaled to the button size. Should probably be done by the widget code. */ const int is_preview_flag = (BKE_icon_is_preview(preview_icon_id) || @@ -442,6 +463,11 @@ void PreviewGridItem::build_grid_tile(uiLayout &layout) const but->emboss = UI_EMBOSS_NONE; } +void PreviewGridItem::build_grid_tile(uiLayout &layout) const +{ + this->build_grid_tile_button(layout); +} + void PreviewGridItem::set_on_activate_fn(ActivateFn fn) { activate_fn_ = fn; diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index ed2bb15cd9b..04603e86959 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -45,6 +45,7 @@ #include "ED_asset_handle.hh" #include "ED_asset_list.hh" #include "ED_asset_mark_clear.hh" +#include "ED_asset_menu_utils.hh" #include "ED_image.hh" #include "ED_paint.hh" #include "ED_screen.hh" @@ -987,21 +988,18 @@ static void PAINT_OT_brush_select(wmOperatorType *ot) /**************************** Brush Assets **********************************/ -static bool brush_asset_select_poll(bContext *C) -{ - if (BKE_paint_get_active_from_context(C) == nullptr) { - return false; - } - - return CTX_wm_asset(C) != nullptr; -} - static int brush_asset_select_exec(bContext *C, wmOperator *op) { + using namespace blender; + using namespace blender::ed; /* 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. */ - blender::asset_system::AssetRepresentation *asset = CTX_wm_asset(C); + const asset_system::AssetRepresentation *asset = + asset::operator_asset_reference_props_get_asset_from_all_library(*C, *op->ptr, op->reports); + if (!asset) { + return OPERATOR_CANCELLED; + } AssetWeakReference *brush_asset_reference = asset->make_weak_reference(); Brush *brush = BKE_brush_asset_runtime_ensure(CTX_data_main(C), brush_asset_reference); @@ -1023,12 +1021,14 @@ static int brush_asset_select_exec(bContext *C, wmOperator *op) static void BRUSH_OT_asset_select(wmOperatorType *ot) { + using namespace blender::ed; ot->name = "Select Brush Asset"; ot->description = "Select a brush asset as current sculpt and paint tool"; ot->idname = "BRUSH_OT_asset_select"; ot->exec = brush_asset_select_exec; - ot->poll = brush_asset_select_poll; + + asset::operator_asset_reference_props_register(*ot->srna); } /* FIXME Quick dirty hack to generate a weak ref from 'raw' paths. diff --git a/source/blender/makesrna/intern/rna_ui.cc b/source/blender/makesrna/intern/rna_ui.cc index be6f3981d74..e8726679e95 100644 --- a/source/blender/makesrna/intern/rna_ui.cc +++ b/source/blender/makesrna/intern/rna_ui.cc @@ -1303,6 +1303,24 @@ static StructRNA *rna_AssetShelf_register(Main *bmain, return srna; } +static void rna_AssetShelf_activate_operator_get(PointerRNA *ptr, char *value) +{ + AssetShelf *shelf = static_cast(ptr->data); + strcpy(value, shelf->type->activate_operator.c_str()); +} + +static int rna_AssetShelf_activate_operator_length(PointerRNA *ptr) +{ + AssetShelf *shelf = static_cast(ptr->data); + return shelf->type->activate_operator.size(); +} + +static void rna_AssetShelf_activate_operator_set(PointerRNA *ptr, const char *value) +{ + AssetShelf *shelf = static_cast(ptr->data); + shelf->type->activate_operator = value; +} + static StructRNA *rna_AssetShelf_refine(PointerRNA *shelf_ptr) { AssetShelf *shelf = (AssetShelf *)shelf_ptr->data; @@ -2321,6 +2339,17 @@ static void rna_def_asset_shelf(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL | PROP_ENUM_FLAG); RNA_def_property_ui_text(prop, "Options", "Options for this asset shelf type"); + prop = RNA_def_property(srna, "bl_activate_operator", PROP_STRING, PROP_NONE); + RNA_def_property_string_funcs(prop, + "rna_AssetShelf_activate_operator_get", + "rna_AssetShelf_activate_operator_length", + "rna_AssetShelf_activate_operator_set"); + RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); + RNA_def_property_ui_text( + prop, + "Activate Operator", + "Operator to call when activating an item with asset reference properties"); + prop = RNA_def_property(srna, "bl_default_preview_size", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, nullptr, "type->default_preview_size"); RNA_def_property_range(prop, 32, 256); diff --git a/source/blender/windowmanager/intern/wm_keymap_utils.cc b/source/blender/windowmanager/intern/wm_keymap_utils.cc index d02a2eb21b1..7c440f22dbf 100644 --- a/source/blender/windowmanager/intern/wm_keymap_utils.cc +++ b/source/blender/windowmanager/intern/wm_keymap_utils.cc @@ -204,11 +204,9 @@ wmKeyMap *WM_keymap_guess_from_context(const bContext *C) wmKeyMap *WM_keymap_guess_opname(const bContext *C, const char *opname) { /* Op types purposely skipped for now: - * BRUSH_OT * BOID_OT * BUTTONS_OT * CONSTRAINT_OT - * PAINT_OT * ED_OT * FLUID_OT * TEXTURE_OT @@ -331,7 +329,7 @@ wmKeyMap *WM_keymap_guess_opname(const bContext *C, const char *opname) km = WM_keymap_find_all( wm, "Paint Face Mask (Weight, Vertex, Texture)", SPACE_EMPTY, RGN_TYPE_WINDOW); } - else if (STRPREFIX(opname, "PAINT_OT")) { + else if (STRPREFIX(opname, "PAINT_OT") || STRPREFIX(opname, "BRUSH_OT")) { /* check for relevant mode */ switch (CTX_data_mode_enum(C)) { case CTX_MODE_PAINT_WEIGHT: @@ -346,6 +344,9 @@ wmKeyMap *WM_keymap_guess_opname(const bContext *C, const char *opname) case CTX_MODE_SCULPT: km = WM_keymap_find_all(wm, "Sculpt", SPACE_EMPTY, RGN_TYPE_WINDOW); break; + case CTX_MODE_SCULPT_CURVES: + km = WM_keymap_find_all(wm, "Sculpt Curves", SPACE_EMPTY, RGN_TYPE_WINDOW); + break; default: break; } -- 2.30.2 From f6898f9bcb01594897bd639ee47fa56bc69270a4 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 7 Feb 2024 13:18:08 -0500 Subject: [PATCH 098/244] Fix brush tool dissapears with no brush active I'm not sure what the "data_block" part of the tool dictionary actually does anymore but I won't get rid of it for now --- .../startup/bl_ui/space_toolsystem_toolbar.py | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/scripts/startup/bl_ui/space_toolsystem_toolbar.py index c8ebbd3a5bf..b8e5a4af808 100644 --- a/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -1384,12 +1384,11 @@ class _defs_sculpt: @staticmethod def generate_brush_tool(context): - if not context: - return () - brush = context.tool_settings.sculpt.brush - if not brush: - return () - tool = brush.sculpt_tool + tool = None + if context: + brush = context.tool_settings.sculpt.brush + if brush: + tool = brush.sculpt_tool return [ ToolDef.from_dict( dict( @@ -2449,12 +2448,11 @@ class _defs_curves_sculpt: @staticmethod def generate_brush_tool(context): - if not context: - return () - brush = context.tool_settings.curves_sculpt.brush - if not brush: - return () - tool = brush.curves_sculpt_tool + tool = None + if context: + brush = context.tool_settings.sculpt.brush + if brush: + tool = brush.curves_sculpt_tool return [ ToolDef.from_dict( dict( -- 2.30.2 From 2d7c804ebcf7dec68326e91d489cc1c83e89c0f2 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 7 Feb 2024 20:38:30 +0100 Subject: [PATCH 099/244] Store enabled catalogs per asset shelf (= more or less per mode) in preferences --- source/blender/blenkernel/BKE_preferences.h | 27 ++++++ source/blender/blenkernel/intern/blender.cc | 7 ++ .../blender/blenkernel/intern/preferences.cc | 93 +++++++++++++++++++ source/blender/blenloader/intern/readfile.cc | 8 ++ source/blender/blenloader/intern/writefile.cc | 9 ++ .../editors/asset/intern/asset_shelf.cc | 10 +- .../editors/asset/intern/asset_shelf.hh | 8 +- .../intern/asset_shelf_catalog_selector.cc | 10 +- .../asset/intern/asset_shelf_settings.cc | 66 ++++++------- source/blender/makesdna/DNA_screen_types.h | 1 - .../blender/makesdna/DNA_userdef_defaults.h | 12 +++ source/blender/makesdna/DNA_userdef_types.h | 15 +++ source/blender/makesdna/intern/dna_defaults.c | 2 + 13 files changed, 213 insertions(+), 55 deletions(-) diff --git a/source/blender/blenkernel/BKE_preferences.h b/source/blender/blenkernel/BKE_preferences.h index fec295c8d13..e587ea12837 100644 --- a/source/blender/blenkernel/BKE_preferences.h +++ b/source/blender/blenkernel/BKE_preferences.h @@ -17,6 +17,7 @@ extern "C" { struct UserDef; struct bUserExtensionRepo; struct bUserAssetLibrary; +struct bUserAssetShelfSettings; /* -------------------------------------------------------------------- */ /** \name Assert Libraries @@ -105,6 +106,32 @@ int BKE_preferences_extension_repo_get_index(const UserDef *userdef, /** \} */ +/* -------------------------------------------------------------------- */ +/** \name #bUserAssetShelvesSettings + * \{ */ + +bUserAssetShelfSettings *BKE_preferences_asset_shelf_settings_get(const UserDef *userdef, + const char *shelf_idname); +bool BKE_preferences_asset_shelf_settings_is_catalog_path_enabled(const UserDef *userdef, + const char *shelf_idname, + const char *catalog_path); +/** + * Enable a catalog path for a asset shelf identified by \a shelf_idname. Will create the shelf + * settings in the Preferences if necessary. + * \return Return true if the catalog was newly enabled. The Preferences should be tagged as dirty + * then. + */ +bool BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(UserDef *userdef, + const char *shelf_idname, + const char *catalog_path); + +/** \} */ + #ifdef __cplusplus } #endif + +void BKE_preferences_asset_shelf_settings_clear_enabled_catalog_paths(const UserDef *userdef, + const char *shelf_idname); +void BKE_preferences_asset_shelf_settings_clear_enabled_catalog_paths( + bUserAssetShelfSettings *settings); diff --git a/source/blender/blenkernel/intern/blender.cc b/source/blender/blenkernel/intern/blender.cc index a94244eee6c..8b7f66c763a 100644 --- a/source/blender/blenkernel/intern/blender.cc +++ b/source/blender/blenkernel/intern/blender.cc @@ -35,6 +35,7 @@ #include "BKE_layer.hh" #include "BKE_main.hh" #include "BKE_node.h" +#include "BKE_preferences.h" #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_screen.hh" @@ -343,6 +344,12 @@ void BKE_blender_userdef_data_free(UserDef *userdef, bool clear_fonts) BLI_freelistN(&userdef->script_directories); BLI_freelistN(&userdef->asset_libraries); BLI_freelistN(&userdef->extension_repos); + LISTBASE_FOREACH_MUTABLE (bUserAssetShelfSettings *, settings, &userdef->asset_shelves_settings) + { + BKE_preferences_asset_shelf_settings_clear_enabled_catalog_paths(settings); + MEM_freeN(settings); + } + BLI_listbase_clear(&userdef->asset_shelves_settings); BLI_freelistN(&userdef->uistyles); BLI_freelistN(&userdef->uifonts); diff --git a/source/blender/blenkernel/intern/preferences.cc b/source/blender/blenkernel/intern/preferences.cc index 155b92eb2ca..449ad17b241 100644 --- a/source/blender/blenkernel/intern/preferences.cc +++ b/source/blender/blenkernel/intern/preferences.cc @@ -269,5 +269,98 @@ int BKE_preferences_extension_repo_get_index(const UserDef *userdef, { return BLI_findindex(&userdef->extension_repos, repo); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #bUserAssetShelfSettings + * \{ */ + +bUserAssetShelfSettings *asset_shelf_settings_new(UserDef *userdef, const char *shelf_idname) +{ + bUserAssetShelfSettings *settings = DNA_struct_default_alloc(bUserAssetShelfSettings); + BLI_addtail(&userdef->asset_shelves_settings, settings); + STRNCPY(settings->shelf_idname, shelf_idname); + BLI_assert(BLI_listbase_is_empty(&settings->enabled_catalog_paths)); + return settings; +} + +bUserAssetShelfSettings *BKE_preferences_asset_shelf_settings_ensure(UserDef *userdef, + const char *shelf_idname) +{ + if (bUserAssetShelfSettings *settings = BKE_preferences_asset_shelf_settings_get(userdef, + shelf_idname)) + { + return settings; + } + + return asset_shelf_settings_new(userdef, shelf_idname); +} + +bUserAssetShelfSettings *BKE_preferences_asset_shelf_settings_get(const UserDef *userdef, + const char *shelf_idname) +{ + return static_cast( + BLI_findstring(&userdef->asset_shelves_settings, + shelf_idname, + offsetof(bUserAssetShelfSettings, shelf_idname))); +} + +bool asset_shelf_settings_is_catalog_path_enabled(const bUserAssetShelfSettings *settings, + const char *catalog_path) +{ + return BLI_findstring_ptr( + &settings->enabled_catalog_paths, catalog_path, offsetof(LinkData, data)) != nullptr; +} + +bool BKE_preferences_asset_shelf_settings_is_catalog_path_enabled(const UserDef *userdef, + const char *shelf_idname, + const char *catalog_path) +{ + const bUserAssetShelfSettings *settings = BKE_preferences_asset_shelf_settings_get(userdef, + shelf_idname); + if (!settings) { + return false; + } + return asset_shelf_settings_is_catalog_path_enabled(settings, catalog_path); +} + +bool BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(UserDef *userdef, + const char *shelf_idname, + const char *catalog_path) +{ + if (BKE_preferences_asset_shelf_settings_is_catalog_path_enabled( + userdef, shelf_idname, catalog_path)) + { + return false; + } + + bUserAssetShelfSettings *settings = BKE_preferences_asset_shelf_settings_ensure(userdef, + shelf_idname); + + char *path_copy = BLI_strdup(catalog_path); + BLI_addtail(&settings->enabled_catalog_paths, BLI_genericNodeN(path_copy)); + return true; +} + +void BKE_preferences_asset_shelf_settings_clear_enabled_catalog_paths( + bUserAssetShelfSettings *settings) +{ + LISTBASE_FOREACH_MUTABLE (LinkData *, path_link, &settings->enabled_catalog_paths) { + MEM_freeN(path_link->data); + BLI_freelinkN(&settings->enabled_catalog_paths, path_link); + } + BLI_assert(BLI_listbase_is_empty(&settings->enabled_catalog_paths)); +} + +void BKE_preferences_asset_shelf_settings_clear_enabled_catalog_paths(const UserDef *userdef, + const char *shelf_idname) +{ + bUserAssetShelfSettings *settings = BKE_preferences_asset_shelf_settings_get(userdef, + shelf_idname); + if (!settings) { + return; + } + BKE_preferences_asset_shelf_settings_clear_enabled_catalog_paths(settings); +} /** \} */ diff --git a/source/blender/blenloader/intern/readfile.cc b/source/blender/blenloader/intern/readfile.cc index d2a8b87540b..38745040d28 100644 --- a/source/blender/blenloader/intern/readfile.cc +++ b/source/blender/blenloader/intern/readfile.cc @@ -3439,6 +3439,7 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead) BLO_read_list(reader, &user->script_directories); BLO_read_list(reader, &user->asset_libraries); BLO_read_list(reader, &user->extension_repos); + BLO_read_list(reader, &user->asset_shelves_settings); LISTBASE_FOREACH (wmKeyMap *, keymap, &user->user_keymaps) { keymap->modal_items = nullptr; @@ -3486,6 +3487,13 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead) IDP_BlendDataRead(reader, &addon->prop); } + LISTBASE_FOREACH (bUserAssetShelfSettings *, shelf_settings, &user->asset_shelves_settings) { + BLO_read_list(reader, &shelf_settings->enabled_catalog_paths); + LISTBASE_FOREACH (LinkData *, path_link, &shelf_settings->enabled_catalog_paths) { + BLO_read_data_address(reader, &path_link->data); + } + } + /* XXX */ user->uifonts.first = user->uifonts.last = nullptr; diff --git a/source/blender/blenloader/intern/writefile.cc b/source/blender/blenloader/intern/writefile.cc index e1640a18f69..cf8ba408aab 100644 --- a/source/blender/blenloader/intern/writefile.cc +++ b/source/blender/blenloader/intern/writefile.cc @@ -933,6 +933,15 @@ static void write_userdef(BlendWriter *writer, const UserDef *userdef) LISTBASE_FOREACH (const bUserExtensionRepo *, repo_ref, &userdef->extension_repos) { BLO_write_struct(writer, bUserExtensionRepo, repo_ref); } + LISTBASE_FOREACH ( + const bUserAssetShelfSettings *, shelf_settings, &userdef->asset_shelves_settings) + { + BLO_write_struct(writer, bUserAssetShelfSettings, shelf_settings); + LISTBASE_FOREACH (const LinkData *, path_link, &shelf_settings->enabled_catalog_paths) { + BLO_write_struct(writer, LinkData, path_link); + BLO_write_string(writer, (const char *)path_link->data); + } + } LISTBASE_FOREACH (const uiStyle *, style, &userdef->uistyles) { BLO_write_struct(writer, uiStyle, style); diff --git a/source/blender/editors/asset/intern/asset_shelf.cc b/source/blender/editors/asset/intern/asset_shelf.cc index 94cb7a3dba2..52666bb2e3f 100644 --- a/source/blender/editors/asset/intern/asset_shelf.cc +++ b/source/blender/editors/asset/intern/asset_shelf.cc @@ -674,9 +674,10 @@ static uiBut *add_tab_button(uiBlock &block, StringRefNull name) return but; } -static void add_catalog_tabs(AssetShelfSettings &shelf_settings, uiLayout &layout) +static void add_catalog_tabs(AssetShelf &shelf, uiLayout &layout) { uiBlock *block = uiLayoutGetBlock(&layout); + AssetShelfSettings &shelf_settings = shelf.settings; /* "All" tab. */ { @@ -694,7 +695,7 @@ static void add_catalog_tabs(AssetShelfSettings &shelf_settings, uiLayout &layou /* Regular catalog tabs. */ settings_foreach_enabled_catalog_path( - shelf_settings, [&shelf_settings, block](const asset_system::AssetCatalogPath &path) { + shelf, [&shelf_settings, block](const asset_system::AssetCatalogPath &path) { uiBut *but = add_tab_button(*block, path.name()); UI_but_func_set(but, [&shelf_settings, path](bContext &C) { @@ -730,9 +731,8 @@ static void asset_shelf_header_draw(const bContext *C, Header *header) uiItemS(layout); PointerRNA shelf_ptr = active_shelf_ptr_from_context(C); - AssetShelf *shelf = static_cast(shelf_ptr.data); - if (shelf) { - add_catalog_tabs(shelf->settings, *layout); + if (AssetShelf *shelf = static_cast(shelf_ptr.data)) { + add_catalog_tabs(*shelf, *layout); } uiItemSpacer(layout); diff --git a/source/blender/editors/asset/intern/asset_shelf.hh b/source/blender/editors/asset/intern/asset_shelf.hh index 713c4a47e85..42dc320ee82 100644 --- a/source/blender/editors/asset/intern/asset_shelf.hh +++ b/source/blender/editors/asset/intern/asset_shelf.hh @@ -52,20 +52,20 @@ void regiondata_blend_read_data(BlendDataReader *reader, RegionAssetShelf **shel void settings_blend_write(BlendWriter *writer, const AssetShelfSettings &settings); void settings_blend_read_data(BlendDataReader *reader, AssetShelfSettings &settings); -void settings_clear_enabled_catalogs(AssetShelfSettings &settings); void settings_set_active_catalog(AssetShelfSettings &settings, const asset_system::AssetCatalogPath &path); void settings_set_all_catalog_active(AssetShelfSettings &settings); bool settings_is_active_catalog(const AssetShelfSettings &settings, const asset_system::AssetCatalogPath &path); bool settings_is_all_catalog_active(const AssetShelfSettings &settings); -bool settings_is_catalog_path_enabled(const AssetShelfSettings &settings, +void settings_clear_enabled_catalogs(const AssetShelf &shelf); +bool settings_is_catalog_path_enabled(const AssetShelf &shelf, const asset_system::AssetCatalogPath &path); -void settings_set_catalog_path_enabled(AssetShelfSettings &settings, +void settings_set_catalog_path_enabled(const AssetShelf &shelf, const asset_system::AssetCatalogPath &path); void settings_foreach_enabled_catalog_path( - const AssetShelfSettings &settings, + const AssetShelf &shelf, FunctionRef fn); } // namespace blender::ed::asset::shelf diff --git a/source/blender/editors/asset/intern/asset_shelf_catalog_selector.cc b/source/blender/editors/asset/intern/asset_shelf_catalog_selector.cc index 4011de16e54..f8fedd15095 100644 --- a/source/blender/editors/asset/intern/asset_shelf_catalog_selector.cc +++ b/source/blender/editors/asset/intern/asset_shelf_catalog_selector.cc @@ -74,7 +74,7 @@ class AssetCatalogSelectorTree : public ui::AbstractTreeView { Item &build_catalog_items_recursive(ui::TreeViewOrItem &parent_view_item, asset_system::AssetCatalogTreeItem &catalog_item) const { - Item &view_item = parent_view_item.add_tree_item(catalog_item, shelf_settings_); + Item &view_item = parent_view_item.add_tree_item(catalog_item, shelf_); catalog_item.foreach_child([&view_item, this](asset_system::AssetCatalogTreeItem &child) { build_catalog_items_recursive(view_item, child); @@ -92,11 +92,11 @@ class AssetCatalogSelectorTree : public ui::AbstractTreeView { char catalog_path_enabled_ = false; public: - Item(asset_system::AssetCatalogTreeItem &catalog_item, AssetShelfSettings &shelf_settings) + Item(asset_system::AssetCatalogTreeItem &catalog_item, AssetShelf &shelf) : ui::BasicTreeViewItem(catalog_item.get_name()), catalog_item_(catalog_item), catalog_path_enabled_( - settings_is_catalog_path_enabled(shelf_settings, catalog_item.catalog_path())) + settings_is_catalog_path_enabled(shelf, catalog_item.catalog_path())) { disable_activatable(); } @@ -169,11 +169,11 @@ class AssetCatalogSelectorTree : public ui::AbstractTreeView { void AssetCatalogSelectorTree::update_shelf_settings_from_enabled_catalogs() { - settings_clear_enabled_catalogs(shelf_settings_); + settings_clear_enabled_catalogs(shelf_); foreach_item([this](ui::AbstractTreeViewItem &view_item) { const auto &selector_tree_item = dynamic_cast(view_item); if (selector_tree_item.is_catalog_path_enabled()) { - settings_set_catalog_path_enabled(shelf_settings_, selector_tree_item.catalog_path()); + settings_set_catalog_path_enabled(shelf_, selector_tree_item.catalog_path()); } }); } diff --git a/source/blender/editors/asset/intern/asset_shelf_settings.cc b/source/blender/editors/asset/intern/asset_shelf_settings.cc index 987385be374..63e2e200199 100644 --- a/source/blender/editors/asset/intern/asset_shelf_settings.cc +++ b/source/blender/editors/asset/intern/asset_shelf_settings.cc @@ -20,6 +20,8 @@ #include "BLI_string.h" #include "BLI_string_ref.hh" +#include "BKE_preferences.h" + #include "asset_shelf.hh" using namespace blender; @@ -45,18 +47,11 @@ AssetShelfSettings &AssetShelfSettings::operator=(const AssetShelfSettings &othe if (active_catalog_path) { active_catalog_path = BLI_strdup(other.active_catalog_path); } - BLI_listbase_clear(&enabled_catalog_paths); - - LISTBASE_FOREACH (LinkData *, catalog_path_item, &other.enabled_catalog_paths) { - LinkData *new_path_item = BLI_genericNodeN(BLI_strdup((char *)catalog_path_item->data)); - BLI_addtail(&enabled_catalog_paths, new_path_item); - } return *this; } AssetShelfSettings::~AssetShelfSettings() { - shelf::settings_clear_enabled_catalogs(*this); MEM_delete(active_catalog_path); } @@ -65,33 +60,14 @@ namespace blender::ed::asset::shelf { void settings_blend_write(BlendWriter *writer, const AssetShelfSettings &settings) { BLO_write_struct(writer, AssetShelfSettings, &settings); - - LISTBASE_FOREACH (LinkData *, catalog_path_item, &settings.enabled_catalog_paths) { - BLO_write_struct(writer, LinkData, catalog_path_item); - BLO_write_string(writer, (const char *)catalog_path_item->data); - } - BLO_write_string(writer, settings.active_catalog_path); } void settings_blend_read_data(BlendDataReader *reader, AssetShelfSettings &settings) { - BLO_read_list(reader, &settings.enabled_catalog_paths); - LISTBASE_FOREACH (LinkData *, catalog_path_item, &settings.enabled_catalog_paths) { - BLO_read_data_address(reader, &catalog_path_item->data); - } BLO_read_data_address(reader, &settings.active_catalog_path); } -void settings_clear_enabled_catalogs(AssetShelfSettings &settings) -{ - LISTBASE_FOREACH_MUTABLE (LinkData *, catalog_path_item, &settings.enabled_catalog_paths) { - MEM_freeN(catalog_path_item->data); - BLI_freelinkN(&settings.enabled_catalog_paths, catalog_path_item); - } - BLI_assert(BLI_listbase_is_empty(&settings.enabled_catalog_paths)); -} - void settings_set_active_catalog(AssetShelfSettings &settings, const asset_system::AssetCatalogPath &path) { @@ -116,30 +92,40 @@ bool settings_is_all_catalog_active(const AssetShelfSettings &settings) return !settings.active_catalog_path || !settings.active_catalog_path[0]; } -bool settings_is_catalog_path_enabled(const AssetShelfSettings &settings, - const asset_system::AssetCatalogPath &path) +void settings_clear_enabled_catalogs(const AssetShelf &shelf) { - LISTBASE_FOREACH (LinkData *, catalog_path_item, &settings.enabled_catalog_paths) { - if (StringRef((const char *)catalog_path_item->data) == path.str()) { - return true; - } - } - return false; + BKE_preferences_asset_shelf_settings_clear_enabled_catalog_paths(&U, shelf.idname); } -void settings_set_catalog_path_enabled(AssetShelfSettings &settings, +bool settings_is_catalog_path_enabled(const AssetShelf &shelf, + const asset_system::AssetCatalogPath &path) +{ + return BKE_preferences_asset_shelf_settings_is_catalog_path_enabled( + &U, shelf.idname, path.c_str()); +} + +void settings_set_catalog_path_enabled(const AssetShelf &shelf, const asset_system::AssetCatalogPath &path) { - char *path_copy = BLI_strdupn(path.c_str(), path.length()); - BLI_addtail(&settings.enabled_catalog_paths, BLI_genericNodeN(path_copy)); + if (BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled( + &U, shelf.idname, path.c_str())) + { + U.runtime.is_dirty = true; + } } void settings_foreach_enabled_catalog_path( - const AssetShelfSettings &settings, + const AssetShelf &shelf, FunctionRef fn) { - LISTBASE_FOREACH (LinkData *, catalog_path_item, &settings.enabled_catalog_paths) { - fn(asset_system::AssetCatalogPath((char *)catalog_path_item->data)); + const bUserAssetShelfSettings *pref_settings = BKE_preferences_asset_shelf_settings_get( + &U, shelf.idname); + if (!pref_settings) { + return; + } + + LISTBASE_FOREACH (LinkData *, path_link, &pref_settings->enabled_catalog_paths) { + fn(asset_system::AssetCatalogPath((char *)path_link->data)); } } diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index 7bd59a3c1fd..0c0c1e88829 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -786,7 +786,6 @@ typedef struct AssetShelfSettings { AssetLibraryReference asset_library_reference; - ListBase enabled_catalog_paths; /* #LinkData */ /** If not set (null or empty string), all assets will be displayed ("All" catalog behavior). */ const char *active_catalog_path; diff --git a/source/blender/makesdna/DNA_userdef_defaults.h b/source/blender/makesdna/DNA_userdef_defaults.h index 6bd35d28ce0..8ee7f2211e3 100644 --- a/source/blender/makesdna/DNA_userdef_defaults.h +++ b/source/blender/makesdna/DNA_userdef_defaults.h @@ -40,4 +40,16 @@ /** \} */ +/* -------------------------------------------------------------------- */ +/** \name bUserExtensionRepo Struct + * \{ */ + +#define _DNA_DEFAULT_bUserAssetShelfSettings \ + { \ + .shelf_idname = {'\0'}, \ + .enabled_catalog_paths = {NULL, NULL}, \ + } + +/** \} */ + /* clang-format on */ diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 96977c5cd11..034d492cb10 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -739,6 +739,20 @@ typedef struct bUserScriptDirectory { char dir_path[768]; /* FILE_MAXDIR */ } bUserScriptDirectory; +/** + * Settings for an asset shelf, stored in the Preferences. Most settings are still stored in the + * asset shelf instance in #AssetShelfSettings. This is just for the options that should be shared + * as Preferences. + */ +typedef struct bUserAssetShelfSettings { + struct bUserAssetShelfSettings *next, *prev; + + /** Identifier that matches the #AssetShelfType.idname of the shelf these settings apply to. */ + char shelf_idname[64]; /* MAX_NAME */ + + ListBase enabled_catalog_paths; /* #LinkData */ +} bUserAssetShelfSettings; + typedef struct UserDef { DNA_DEFINE_CXX_METHODS(UserDef) @@ -861,6 +875,7 @@ typedef struct UserDef { struct ListBase asset_libraries; /** #bUserExtensionRepo */ struct ListBase extension_repos; + struct ListBase asset_shelves_settings; /* #bUserAssetShelfSettings */ char keyconfigstr[64]; diff --git a/source/blender/makesdna/intern/dna_defaults.c b/source/blender/makesdna/intern/dna_defaults.c index 4e363153c0e..2774059d617 100644 --- a/source/blender/makesdna/intern/dna_defaults.c +++ b/source/blender/makesdna/intern/dna_defaults.c @@ -224,6 +224,7 @@ SDNA_DEFAULT_DECL_STRUCT(Tex); /* DNA_userdef_types.h */ SDNA_DEFAULT_DECL_STRUCT(bUserAssetLibrary); SDNA_DEFAULT_DECL_STRUCT(bUserExtensionRepo); +SDNA_DEFAULT_DECL_STRUCT(bUserAssetShelfSettings); /* DNA_view3d_defaults.h */ SDNA_DEFAULT_DECL_STRUCT(View3D); @@ -476,6 +477,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = { SDNA_DEFAULT_DECL_EX(WalkNavigation, UserDef.walk_navigation), SDNA_DEFAULT_DECL(bUserAssetLibrary), SDNA_DEFAULT_DECL(bUserExtensionRepo), + SDNA_DEFAULT_DECL(bUserAssetShelfSettings), /* DNA_view3d_defaults.h */ SDNA_DEFAULT_DECL(View3D), -- 2.30.2 From 9817b56c9b6805e8e28c3e8463a9ba19137ed651 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 8 Feb 2024 12:38:52 -0500 Subject: [PATCH 100/244] Cleanup: Move new code to proper namespace Also use StringRef and pass the string refs by value --- .../blender/editors/sculpt_paint/paint_ops.cc | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 04603e86959..b43b117af47 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -986,12 +986,12 @@ static void PAINT_OT_brush_select(wmOperatorType *ot) RNA_def_property_flag(prop, PropertyFlag(PROP_HIDDEN | PROP_SKIP_SAVE)); } +namespace blender::ed::sculpt_paint { + /**************************** Brush Assets **********************************/ static int brush_asset_select_exec(bContext *C, wmOperator *op) { - using namespace blender; - using namespace blender::ed; /* 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. */ @@ -1021,7 +1021,6 @@ static int brush_asset_select_exec(bContext *C, wmOperator *op) static void BRUSH_OT_asset_select(wmOperatorType *ot) { - using namespace blender::ed; ot->name = "Select Brush Asset"; ot->description = "Select a brush asset as current sculpt and paint tool"; ot->idname = "BRUSH_OT_asset_select"; @@ -1039,7 +1038,7 @@ static AssetWeakReference *brush_asset_create_weakref_hack(const bUserAssetLibra { AssetWeakReference *asset_weak_ref = MEM_new(__func__); - blender::StringRefNull asset_root_path = user_asset_lib->dirpath; + StringRef asset_root_path = user_asset_lib->dirpath; BLI_assert(file_path.find(asset_root_path) == 0); std::string relative_asset_path = file_path.substr(size_t(asset_root_path.size()) + 1); @@ -1064,18 +1063,16 @@ static void brush_asset_refresh_editable_library(const bContext *C) const bUserAssetLibrary *user_library = brush_asset_get_editable_library(); /* TODO: Should the all library reference be automatically cleared? */ - AssetLibraryReference all_lib_ref = blender::asset_system::all_library_reference(); - blender::ed::asset::list::clear(&all_lib_ref, C); + AssetLibraryReference all_lib_ref = asset_system::all_library_reference(); + asset::list::clear(&all_lib_ref, C); /* TODO: this is convoluted, can we create a reference from pointer? */ - for (const AssetLibraryReference &lib_ref : - blender::asset_system::all_valid_asset_library_refs()) - { + for (const AssetLibraryReference &lib_ref : asset_system::all_valid_asset_library_refs()) { if (lib_ref.type == ASSET_LIBRARY_CUSTOM) { const bUserAssetLibrary *ref_user_library = BKE_preferences_asset_library_find_index( &U, lib_ref.custom_library_index); if (ref_user_library == user_library) { - blender::ed::asset::list::clear(&lib_ref, C); + asset::list::clear(&lib_ref, C); return; } } @@ -1098,7 +1095,7 @@ static std::string brush_asset_root_path_for_save() } static std::string brush_asset_blendfile_path_for_save(ReportList *reports, - const blender::StringRefNull &base_name) + const StringRefNull base_name) { std::string root_path = brush_asset_root_path_for_save(); BLI_assert(!root_path.empty()); @@ -1127,7 +1124,7 @@ static std::string brush_asset_blendfile_path_for_save(ReportList *reports, static bool brush_asset_write_in_library(Main *bmain, Brush *brush, const char *name, - const blender::StringRefNull &filepath, + const StringRefNull filepath, std::string &final_full_file_path, ReportList *reports) { @@ -1236,8 +1233,8 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) /* Turn brush into asset if it isn't yet. */ if (!BKE_paint_brush_is_valid_asset(brush)) { - blender::ed::asset::mark_id(&brush->id); - blender::ed::asset::generate_preview(C, &brush->id); + asset::mark_id(&brush->id); + asset::generate_preview(C, &brush->id); } BLI_assert(BKE_paint_brush_is_valid_asset(brush)); @@ -1314,12 +1311,12 @@ static bool brush_asset_is_editable(const AssetWeakReference &brush_weak_ref) char *dir, *group, *name; AS_asset_full_path_explode_from_weak_ref(&brush_weak_ref, path_buffer, &dir, &group, &name); - if (!blender::StringRef(dir).endswith(BLENDER_ASSET_FILE_SUFFIX)) { + if (!StringRef(dir).endswith(BLENDER_ASSET_FILE_SUFFIX)) { return false; } std::string root_path_for_save = brush_asset_root_path_for_save(); - if (root_path_for_save.empty() || !blender::StringRef(dir).startswith(root_path_for_save)) { + if (root_path_for_save.empty() || !StringRef(dir).startswith(root_path_for_save)) { return false; } @@ -1487,6 +1484,8 @@ static void BRUSH_OT_asset_revert(wmOperatorType *ot) ot->poll = brush_asset_revert_poll; } +} // namespace blender::ed::sculpt_paint + /***** Stencil Control *****/ enum StencilControlMode { -- 2.30.2 From 6d9f313ba1d9da2d57bbd13dba7819432f0638d5 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 8 Feb 2024 12:47:08 -0500 Subject: [PATCH 101/244] Fix compile warnings in asset shelf settings code --- source/blender/blenkernel/intern/preferences.cc | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/source/blender/blenkernel/intern/preferences.cc b/source/blender/blenkernel/intern/preferences.cc index 449ad17b241..37c610fe3b4 100644 --- a/source/blender/blenkernel/intern/preferences.cc +++ b/source/blender/blenkernel/intern/preferences.cc @@ -275,7 +275,8 @@ int BKE_preferences_extension_repo_get_index(const UserDef *userdef, /** \name #bUserAssetShelfSettings * \{ */ -bUserAssetShelfSettings *asset_shelf_settings_new(UserDef *userdef, const char *shelf_idname) +static bUserAssetShelfSettings *asset_shelf_settings_new(UserDef *userdef, + const char *shelf_idname) { bUserAssetShelfSettings *settings = DNA_struct_default_alloc(bUserAssetShelfSettings); BLI_addtail(&userdef->asset_shelves_settings, settings); @@ -284,15 +285,14 @@ bUserAssetShelfSettings *asset_shelf_settings_new(UserDef *userdef, const char * return settings; } -bUserAssetShelfSettings *BKE_preferences_asset_shelf_settings_ensure(UserDef *userdef, - const char *shelf_idname) +static bUserAssetShelfSettings *asset_shelf_settings_ensure(UserDef *userdef, + const char *shelf_idname) { if (bUserAssetShelfSettings *settings = BKE_preferences_asset_shelf_settings_get(userdef, shelf_idname)) { return settings; } - return asset_shelf_settings_new(userdef, shelf_idname); } @@ -305,8 +305,8 @@ bUserAssetShelfSettings *BKE_preferences_asset_shelf_settings_get(const UserDef offsetof(bUserAssetShelfSettings, shelf_idname))); } -bool asset_shelf_settings_is_catalog_path_enabled(const bUserAssetShelfSettings *settings, - const char *catalog_path) +static bool asset_shelf_settings_is_catalog_path_enabled(const bUserAssetShelfSettings *settings, + const char *catalog_path) { return BLI_findstring_ptr( &settings->enabled_catalog_paths, catalog_path, offsetof(LinkData, data)) != nullptr; @@ -334,8 +334,7 @@ bool BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(UserDef *u return false; } - bUserAssetShelfSettings *settings = BKE_preferences_asset_shelf_settings_ensure(userdef, - shelf_idname); + bUserAssetShelfSettings *settings = asset_shelf_settings_ensure(userdef, shelf_idname); char *path_copy = BLI_strdup(catalog_path); BLI_addtail(&settings->enabled_catalog_paths, BLI_genericNodeN(path_copy)); -- 2.30.2 From ec898027a8e8f3c70bdadd5673cf6c21a79f3125 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 8 Feb 2024 23:47:42 +0100 Subject: [PATCH 102/244] Brush Assets: Add asset operations to shelf context menu Pull Request: https://projects.blender.org/blender/blender/pulls/118017 --- scripts/startup/bl_ui/space_view3d.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index 987e6fe44dc..fd9be9524e5 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -8807,6 +8807,13 @@ class BrushAssetShelf: paint_settings = UnifiedPaintPanel.paint_settings(bpy.context) return paint_settings.brush_asset_reference if paint_settings else None + @classmethod + def draw_context_menu(self, context, asset, layout): + # Currently this menu adds operators that deal with the affected brush and don't take the + # asset into account. Luckily that is okay for now, since right clicking in the grid view + # also activates the item. + layout.menu_contents("VIEW3D_MT_brush_context_menu") + class VIEW3D_AST_brush_sculpt(BrushAssetShelf, bpy.types.AssetShelf): mode = 'SCULPT' -- 2.30.2 From 4812d45c21f1b2d25616e1af3a554290ecb53f41 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 9 Feb 2024 00:02:30 +0100 Subject: [PATCH 103/244] Brush Assets: Add "Default" option for asset library for new brushes A new option controls where duplicated brushes are stored. Maybe eventually the duplicate operator will allow choosing the library, but it also seems reasonable to store a default so users don't have to make that choice every time. Pull Request: https://projects.blender.org/blender/blender/pulls/117963 --- scripts/startup/bl_ui/space_userpref.py | 1 + .../blender/editors/sculpt_paint/paint_ops.cc | 12 ++++++++---- source/blender/makesdna/DNA_asset_types.h | 1 + source/blender/makesrna/intern/rna_userdef.cc | 17 +++++++++++++++++ 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/scripts/startup/bl_ui/space_userpref.py b/scripts/startup/bl_ui/space_userpref.py index 80a6cb27441..ea427e3818e 100644 --- a/scripts/startup/bl_ui/space_userpref.py +++ b/scripts/startup/bl_ui/space_userpref.py @@ -1581,6 +1581,7 @@ class USERPREF_PT_file_paths_asset_libraries(FilePathsPanel, Panel): layout.prop(active_library, "path") layout.prop(active_library, "import_method", text="Import Method") layout.prop(active_library, "use_relative_path") + layout.prop(active_library, "is_default") class USERPREF_UL_asset_libraries(UIList): diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index b43b117af47..500b2bfba2c 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -1051,11 +1051,15 @@ static AssetWeakReference *brush_asset_create_weakref_hack(const bUserAssetLibra static const bUserAssetLibrary *brush_asset_get_editable_library() { - /* TODO: take into account which one is marked as default. */ - LISTBASE_FOREACH (const bUserAssetLibrary *, asset_library, &U.asset_libraries) { - return asset_library; + if (BLI_listbase_is_empty(&U.asset_libraries)) { + return nullptr; } - return nullptr; + LISTBASE_FOREACH (const bUserAssetLibrary *, asset_library, &U.asset_libraries) { + if (asset_library->flag & ASSET_LIBRARY_DEFAULT) { + return asset_library; + } + } + return static_cast(U.asset_libraries.first); } static void brush_asset_refresh_editable_library(const bContext *C) diff --git a/source/blender/makesdna/DNA_asset_types.h b/source/blender/makesdna/DNA_asset_types.h index d3f7022cd3a..7245f635bce 100644 --- a/source/blender/makesdna/DNA_asset_types.h +++ b/source/blender/makesdna/DNA_asset_types.h @@ -126,6 +126,7 @@ typedef enum eAssetImportMethod { typedef enum eAssetLibrary_Flag { ASSET_LIBRARY_RELATIVE_PATH = (1 << 0), + ASSET_LIBRARY_DEFAULT = (1 << 1), } eAssetLibrary_Flag; /** diff --git a/source/blender/makesrna/intern/rna_userdef.cc b/source/blender/makesrna/intern/rna_userdef.cc index db0c1889f3e..c0bc96c474e 100644 --- a/source/blender/makesrna/intern/rna_userdef.cc +++ b/source/blender/makesrna/intern/rna_userdef.cc @@ -358,6 +358,18 @@ static void rna_userdef_asset_library_path_set(PointerRNA *ptr, const char *valu BKE_preferences_asset_library_path_set(library, value); } +static void rna_UserAssetLibrary_is_default_set(PointerRNA *ptr, bool value) +{ + bUserAssetLibrary *library = static_cast(ptr->data); + if (!value) { + return; + } + LISTBASE_FOREACH (bUserAssetLibrary *, library_iter, &U.asset_libraries) { + library_iter->flag &= ~ASSET_LIBRARY_DEFAULT; + } + library->flag |= ASSET_LIBRARY_DEFAULT; +} + static void rna_userdef_extension_repo_name_set(PointerRNA *ptr, const char *value) { bUserExtensionRepo *repo = (bUserExtensionRepo *)ptr->data; @@ -6576,6 +6588,11 @@ static void rna_def_userdef_filepaths_asset_library(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, nullptr, "flag", ASSET_LIBRARY_RELATIVE_PATH); RNA_def_property_ui_text( prop, "Relative Path", "Use relative path when linking assets from this asset library"); + + prop = RNA_def_property(srna, "is_default", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", ASSET_LIBRARY_DEFAULT); + RNA_def_property_boolean_funcs(prop, nullptr, "rna_UserAssetLibrary_is_default_set"); + RNA_def_property_ui_text(prop, "Default", "Use this library for saving new assets"); } static void rna_def_userdef_filepaths_extension_repo(BlenderRNA *brna) -- 2.30.2 From 4bedf2f2a0bdddb8c3c20c47d2dafb13b3211820 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 8 Feb 2024 18:06:47 -0500 Subject: [PATCH 104/244] Cleanup: Rename functions that aren't brush specific --- .../blender/editors/sculpt_paint/paint_ops.cc | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 500b2bfba2c..9eff982a373 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -1049,7 +1049,7 @@ static AssetWeakReference *brush_asset_create_weakref_hack(const bUserAssetLibra return asset_weak_ref; } -static const bUserAssetLibrary *brush_asset_get_editable_library() +static const bUserAssetLibrary *brush_asset_get_default_library() { if (BLI_listbase_is_empty(&U.asset_libraries)) { return nullptr; @@ -1062,9 +1062,9 @@ static const bUserAssetLibrary *brush_asset_get_editable_library() return static_cast(U.asset_libraries.first); } -static void brush_asset_refresh_editable_library(const bContext *C) +static void refresh_asset_library(const bContext *C) { - const bUserAssetLibrary *user_library = brush_asset_get_editable_library(); + const bUserAssetLibrary *user_library = brush_asset_get_default_library(); /* TODO: Should the all library reference be automatically cleared? */ AssetLibraryReference all_lib_ref = asset_system::all_library_reference(); @@ -1085,7 +1085,7 @@ static void brush_asset_refresh_editable_library(const bContext *C) static std::string brush_asset_root_path_for_save() { - const bUserAssetLibrary *user_library = brush_asset_get_editable_library(); + const bUserAssetLibrary *user_library = brush_asset_get_default_library(); if (user_library == nullptr || user_library->dirpath[0] == '\0') { return ""; } @@ -1203,7 +1203,7 @@ static bool brush_asset_save_as_poll(bContext *C) return false; } - const bUserAssetLibrary *user_library = brush_asset_get_editable_library(); + const bUserAssetLibrary *user_library = brush_asset_get_default_library(); if (user_library == nullptr || user_library->dirpath[0] == '\0') { CTX_wm_operator_poll_msg_set(C, "No default asset library available to save to"); return false; @@ -1253,7 +1253,7 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) } /* Create weak reference to new datablock. */ - const bUserAssetLibrary *asset_lib = brush_asset_get_editable_library(); + const bUserAssetLibrary *asset_lib = brush_asset_get_default_library(); AssetWeakReference *new_brush_weak_ref = brush_asset_create_weakref_hack( asset_lib, final_full_asset_filepath); @@ -1268,7 +1268,7 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) BKE_report(op->reports, RPT_WARNING, "Unable to activate just-saved brush asset"); } - brush_asset_refresh_editable_library(C); + refresh_asset_library(C); WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_ADDED, nullptr); WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush); @@ -1302,7 +1302,7 @@ static void BRUSH_OT_asset_save_as(wmOperatorType *ot) RNA_def_string(ot->srna, "name", nullptr, MAX_NAME, "Name", "Name used to save the brush asset"); } -static bool brush_asset_is_editable(const AssetWeakReference &brush_weak_ref) +static bool asset_is_editable(const AssetWeakReference &brush_weak_ref) { /* Fairly simple checks, based on filepath only: * - The blendlib filepath ends up with the `.asset.blend` extension. @@ -1338,7 +1338,7 @@ static bool brush_asset_delete_poll(bContext *C) /* Asset brush, check if belongs to an editable blend file. */ if (paint->brush_asset_reference && BKE_paint_brush_is_valid_asset(brush)) { - if (!brush_asset_is_editable(*paint->brush_asset_reference)) { + if (!asset_is_editable(*paint->brush_asset_reference)) { CTX_wm_operator_poll_msg_set(C, "Asset blend file is not editable"); return false; } @@ -1375,7 +1375,7 @@ static int brush_asset_delete_exec(bContext *C, wmOperator *op) BKE_id_delete(bmain, original_brush); } - brush_asset_refresh_editable_library(C); + refresh_asset_library(C); WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_REMOVED, nullptr); WM_main_add_notifier(NC_BRUSH | NA_EDITED, nullptr); @@ -1407,7 +1407,7 @@ static bool brush_asset_update_poll(bContext *C) return false; } - if (!brush_asset_is_editable(*paint->brush_asset_reference)) { + if (!asset_is_editable(*paint->brush_asset_reference)) { CTX_wm_operator_poll_msg_set(C, "Asset blend file is not editable"); return false; } -- 2.30.2 From 2dec98b69fd9aae0008642b74789a6f161adfbc3 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 8 Feb 2024 18:11:32 -0500 Subject: [PATCH 105/244] Cleanup: Rename more variables that aren't brush specific The idea is to make this code more general in the future so it doesn't just deal with brush assets --- source/blender/editors/sculpt_paint/paint_ops.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 9eff982a373..fffa5d08bb1 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -1302,7 +1302,7 @@ static void BRUSH_OT_asset_save_as(wmOperatorType *ot) RNA_def_string(ot->srna, "name", nullptr, MAX_NAME, "Name", "Name used to save the brush asset"); } -static bool asset_is_editable(const AssetWeakReference &brush_weak_ref) +static bool asset_is_editable(const AssetWeakReference &asset_weak_ref) { /* Fairly simple checks, based on filepath only: * - The blendlib filepath ends up with the `.asset.blend` extension. @@ -1313,7 +1313,7 @@ static bool asset_is_editable(const AssetWeakReference &brush_weak_ref) char path_buffer[FILE_MAX_LIBEXTRA]; char *dir, *group, *name; - AS_asset_full_path_explode_from_weak_ref(&brush_weak_ref, path_buffer, &dir, &group, &name); + AS_asset_full_path_explode_from_weak_ref(&asset_weak_ref, path_buffer, &dir, &group, &name); if (!StringRef(dir).endswith(BLENDER_ASSET_FILE_SUFFIX)) { return false; @@ -1419,13 +1419,13 @@ static int brush_asset_update_exec(bContext *C, wmOperator *op) { Paint *paint = BKE_paint_get_active_from_context(C); Brush *brush = nullptr; - const AssetWeakReference *brush_weak_ref = + const AssetWeakReference *asset_weak_ref = BKE_paint_brush_asset_get(paint, &brush).value_or(nullptr); char path_buffer[FILE_MAX_LIBEXTRA]; char *filepath; AS_asset_full_path_explode_from_weak_ref( - brush_weak_ref, path_buffer, &filepath, nullptr, nullptr); + asset_weak_ref, path_buffer, &filepath, nullptr, nullptr); BLI_assert(BKE_paint_brush_is_valid_asset(brush)); -- 2.30.2 From 9c1b8375782200d5239e65fbbe07c26325b1fce6 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 8 Feb 2024 18:20:11 -0500 Subject: [PATCH 106/244] Fix: Refresh proper library after deleting asset Pass the asset library to some functions that always just retrieved the default before. That makes them more flexible and fixes some potential issues where the wrong asset library was updated or used in a poll function, etc. --- .../blender/editors/sculpt_paint/paint_ops.cc | 45 ++++++++++++------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index fffa5d08bb1..45ef4a1b677 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -1062,10 +1062,8 @@ static const bUserAssetLibrary *brush_asset_get_default_library() return static_cast(U.asset_libraries.first); } -static void refresh_asset_library(const bContext *C) +static void refresh_asset_library(const bContext *C, const bUserAssetLibrary &user_library) { - const bUserAssetLibrary *user_library = brush_asset_get_default_library(); - /* TODO: Should the all library reference be automatically cleared? */ AssetLibraryReference all_lib_ref = asset_system::all_library_reference(); asset::list::clear(&all_lib_ref, C); @@ -1075,7 +1073,7 @@ static void refresh_asset_library(const bContext *C) if (lib_ref.type == ASSET_LIBRARY_CUSTOM) { const bUserAssetLibrary *ref_user_library = BKE_preferences_asset_library_find_index( &U, lib_ref.custom_library_index); - if (ref_user_library == user_library) { + if (ref_user_library == &user_library) { asset::list::clear(&lib_ref, C); return; } @@ -1083,15 +1081,14 @@ static void refresh_asset_library(const bContext *C) } } -static std::string brush_asset_root_path_for_save() +static std::string brush_asset_root_path_for_save(const bUserAssetLibrary &user_library) { - const bUserAssetLibrary *user_library = brush_asset_get_default_library(); - if (user_library == nullptr || user_library->dirpath[0] == '\0') { + if (user_library.dirpath[0] == '\0') { return ""; } char libpath[FILE_MAX]; - BLI_strncpy(libpath, user_library->dirpath, sizeof(libpath)); + BLI_strncpy(libpath, user_library.dirpath, sizeof(libpath)); BLI_path_slash_native(libpath); BLI_path_normalize(libpath); @@ -1099,9 +1096,10 @@ static std::string brush_asset_root_path_for_save() } static std::string brush_asset_blendfile_path_for_save(ReportList *reports, + const bUserAssetLibrary &user_library, const StringRefNull base_name) { - std::string root_path = brush_asset_root_path_for_save(); + std::string root_path = brush_asset_root_path_for_save(user_library); BLI_assert(!root_path.empty()); if (!BLI_dir_create_recursive(root_path.c_str())) { @@ -1230,7 +1228,11 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) STRNCPY(name, brush->id.name + 2); } - const std::string filepath = brush_asset_blendfile_path_for_save(op->reports, name); + const bUserAssetLibrary *library = brush_asset_get_default_library(); + if (!library) { + return OPERATOR_CANCELLED; + } + const std::string filepath = brush_asset_blendfile_path_for_save(op->reports, *library, name); if (filepath.empty()) { return OPERATOR_CANCELLED; } @@ -1252,10 +1254,9 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - /* Create weak reference to new datablock. */ - const bUserAssetLibrary *asset_lib = brush_asset_get_default_library(); + const bUserAssetLibrary *library = brush_asset_get_default_library(); AssetWeakReference *new_brush_weak_ref = brush_asset_create_weakref_hack( - asset_lib, final_full_asset_filepath); + library, final_full_asset_filepath); /* 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()); @@ -1268,7 +1269,7 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) BKE_report(op->reports, RPT_WARNING, "Unable to activate just-saved brush asset"); } - refresh_asset_library(C); + refresh_asset_library(C, *library); WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_ADDED, nullptr); WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush); @@ -1319,7 +1320,13 @@ static bool asset_is_editable(const AssetWeakReference &asset_weak_ref) return false; } - std::string root_path_for_save = brush_asset_root_path_for_save(); + const bUserAssetLibrary *library = BKE_preferences_asset_library_find_by_name( + &U, asset_weak_ref.asset_library_identifier); + if (!library) { + return false; + } + + std::string root_path_for_save = brush_asset_root_path_for_save(*library); if (root_path_for_save.empty() || !StringRef(dir).startswith(root_path_for_save)) { return false; } @@ -1353,6 +1360,12 @@ static int brush_asset_delete_exec(bContext *C, wmOperator *op) Paint *paint = BKE_paint_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); + bUserAssetLibrary *library = BKE_preferences_asset_library_find_by_name( + &U, paint->brush_asset_reference->asset_library_identifier); + if (!library) { + return OPERATOR_CANCELLED; + } + if (paint->brush_asset_reference && BKE_paint_brush_is_valid_asset(brush)) { /* Delete from asset library on disk. */ char path_buffer[FILE_MAX_LIBEXTRA]; @@ -1375,7 +1388,7 @@ static int brush_asset_delete_exec(bContext *C, wmOperator *op) BKE_id_delete(bmain, original_brush); } - refresh_asset_library(C); + refresh_asset_library(C, *library); WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_REMOVED, nullptr); WM_main_add_notifier(NC_BRUSH | NA_EDITED, nullptr); -- 2.30.2 From 24d4ee1ab648cad97f835f7f08ffcbbe4e5a4653 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 8 Feb 2024 18:21:09 -0500 Subject: [PATCH 107/244] Cleanup: Reorder static function Just to make a diff in a future PR cleaner --- .../blender/editors/sculpt_paint/paint_ops.cc | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 45ef4a1b677..097d544d0da 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -1210,6 +1210,38 @@ static bool brush_asset_save_as_poll(bContext *C) return true; } +static bool asset_is_editable(const AssetWeakReference &asset_weak_ref) +{ + /* Fairly simple checks, based on filepath only: + * - The blendlib filepath ends up with the `.asset.blend` extension. + * - The blendlib is located in the expected sub-directory of the editable asset library. + * + * TODO: Right now no check is done on file content, e.g. to ensure that the blendlib file has + * not been manually edited by the user (that it does not have any UI IDs e.g.). */ + + char path_buffer[FILE_MAX_LIBEXTRA]; + char *dir, *group, *name; + AS_asset_full_path_explode_from_weak_ref(&asset_weak_ref, path_buffer, &dir, &group, &name); + + if (!StringRef(dir).endswith(BLENDER_ASSET_FILE_SUFFIX)) { + return false; + } + + const bUserAssetLibrary *library = BKE_preferences_asset_library_find_by_name( + &U, asset_weak_ref.asset_library_identifier); + if (!library) { + return false; + } + + std::string root_path_for_save = brush_asset_root_path_for_save(*library); + if (root_path_for_save.empty() || !StringRef(dir).startswith(root_path_for_save)) { + return false; + } + + /* TODO: Do we want more checks here? E.g. check actual content of the file? */ + return true; +} + static int brush_asset_save_as_exec(bContext *C, wmOperator *op) { Paint *paint = BKE_paint_get_active_from_context(C); @@ -1303,38 +1335,6 @@ static void BRUSH_OT_asset_save_as(wmOperatorType *ot) RNA_def_string(ot->srna, "name", nullptr, MAX_NAME, "Name", "Name used to save the brush asset"); } -static bool asset_is_editable(const AssetWeakReference &asset_weak_ref) -{ - /* Fairly simple checks, based on filepath only: - * - The blendlib filepath ends up with the `.asset.blend` extension. - * - The blendlib is located in the expected sub-directory of the editable asset library. - * - * TODO: Right now no check is done on file content, e.g. to ensure that the blendlib file has - * not been manually edited by the user (that it does not have any UI IDs e.g.). */ - - char path_buffer[FILE_MAX_LIBEXTRA]; - char *dir, *group, *name; - AS_asset_full_path_explode_from_weak_ref(&asset_weak_ref, path_buffer, &dir, &group, &name); - - if (!StringRef(dir).endswith(BLENDER_ASSET_FILE_SUFFIX)) { - return false; - } - - const bUserAssetLibrary *library = BKE_preferences_asset_library_find_by_name( - &U, asset_weak_ref.asset_library_identifier); - if (!library) { - return false; - } - - std::string root_path_for_save = brush_asset_root_path_for_save(*library); - if (root_path_for_save.empty() || !StringRef(dir).startswith(root_path_for_save)) { - return false; - } - - /* TODO: Do we want more checks here? E.g. check actual content of the file? */ - return true; -} - static bool brush_asset_delete_poll(bContext *C) { Paint *paint = BKE_paint_get_active_from_context(C); -- 2.30.2 From bb1ba7c8e6e6f497e528afe5b9fe05f0ee23ad5e Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 8 Feb 2024 18:22:09 -0500 Subject: [PATCH 108/244] Fix build error --- source/blender/editors/sculpt_paint/paint_ops.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 097d544d0da..db1a4486989 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -1286,7 +1286,6 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - const bUserAssetLibrary *library = brush_asset_get_default_library(); AssetWeakReference *new_brush_weak_ref = brush_asset_create_weakref_hack( library, final_full_asset_filepath); -- 2.30.2 From 868af4a5c6f18f3214cc08e8fb068151a387ea4f Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 8 Feb 2024 18:25:43 -0500 Subject: [PATCH 109/244] Brush Assets: Focus name in Save as Brush Asset operator popup --- source/blender/editors/sculpt_paint/paint_ops.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index db1a4486989..4749796f96c 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -1315,7 +1315,6 @@ static int brush_asset_save_as_invoke(bContext *C, wmOperator *op, const wmEvent RNA_string_set(op->ptr, "name", brush->id.name + 2); /* TODO: add information about the asset library this will be saved to? */ - /* TODO: autofocus name? */ return WM_operator_props_dialog_popup(C, op, 400); } @@ -1331,7 +1330,8 @@ static void BRUSH_OT_asset_save_as(wmOperatorType *ot) ot->invoke = brush_asset_save_as_invoke; ot->poll = brush_asset_save_as_poll; - RNA_def_string(ot->srna, "name", nullptr, MAX_NAME, "Name", "Name used to save the brush asset"); + ot->prop = RNA_def_string( + ot->srna, "name", nullptr, MAX_NAME, "Name", "Name for the new brush asset"); } static bool brush_asset_delete_poll(bContext *C) -- 2.30.2 From 58d2699f63396d441cfeacf0473ee0cf944eadf6 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 8 Feb 2024 18:26:03 -0500 Subject: [PATCH 110/244] UI: Use proper title case in operator name --- source/blender/editors/sculpt_paint/paint_ops.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 4749796f96c..2f9f8cc7ec3 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -1320,7 +1320,7 @@ static int brush_asset_save_as_invoke(bContext *C, wmOperator *op, const wmEvent static void BRUSH_OT_asset_save_as(wmOperatorType *ot) { - ot->name = "Save As Brush Asset"; + ot->name = "Save as Brush Asset"; ot->description = "Save a copy of the active brush asset into the default asset library, and make it the " "active brush"; -- 2.30.2 From 25f6ad437c7990b3ccaef579e653c0705d4761a9 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 8 Feb 2024 18:29:33 -0500 Subject: [PATCH 111/244] UI: Customize confirm text in save as brush asset popup --- source/blender/editors/sculpt_paint/paint_ops.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 2f9f8cc7ec3..c126f828671 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -1315,7 +1315,7 @@ static int brush_asset_save_as_invoke(bContext *C, wmOperator *op, const wmEvent RNA_string_set(op->ptr, "name", brush->id.name + 2); /* TODO: add information about the asset library this will be saved to? */ - return WM_operator_props_dialog_popup(C, op, 400); + return WM_operator_props_dialog_popup(C, op, 400, std::nullopt, IFACE_("Save")); } static void BRUSH_OT_asset_save_as(wmOperatorType *ot) -- 2.30.2 From d35df02001af82a54eb5bfaf2729b2a04e264ba0 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 14 Feb 2024 11:33:55 -0500 Subject: [PATCH 112/244] Fix build errors after merging main --- source/blender/blenkernel/intern/blendfile.cc | 18 +++++++++--------- .../blender/editors/sculpt_paint/paint_ops.cc | 2 ++ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/source/blender/blenkernel/intern/blendfile.cc b/source/blender/blenkernel/intern/blendfile.cc index ddbb06fb99a..3b7235890db 100644 --- a/source/blender/blenkernel/intern/blendfile.cc +++ b/source/blender/blenkernel/intern/blendfile.cc @@ -309,10 +309,10 @@ static Library *reuse_bmain_data_dependencies_new_library_get(ReuseOldBMainData return nullptr; } - IDRemapper *remapper = reuse_data->remapper; + id::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); + IDRemapperApplyResult result = remapper->apply(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. */ @@ -336,7 +336,7 @@ static int reuse_bmain_data_dependencies_process_cb(LibraryIDLinkCallbackData *c Main *old_bmain = reuse_data->old_bmain; /* First check if it has already been remapped. */ - IDRemapper *remapper = reuse_bmain_data_remapper_ensure(reuse_data); + id::IDRemapper &remapper = reuse_bmain_data_remapper_ensure(reuse_data); if (reuse_bmain_data_remapper_is_id_remapped(remapper, id)) { return IDWALK_RET_STOP_RECURSION; } @@ -375,7 +375,7 @@ static int reuse_bmain_data_dependencies_process_cb(LibraryIDLinkCallbackData *c * * 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); + remapper.add(id, id_iter); return IDWALK_RET_STOP_RECURSION; } } @@ -387,7 +387,7 @@ static int reuse_bmain_data_dependencies_process_cb(LibraryIDLinkCallbackData *c 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); + remapper.add(id, id); return IDWALK_RET_NOP; } @@ -413,7 +413,7 @@ static void reuse_old_bmain_data_for_blendfile(ReuseOldBMainData *reuse_data, co 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); + id::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. */ @@ -452,7 +452,7 @@ static void reuse_old_bmain_data_for_blendfile(ReuseOldBMainData *reuse_data, co * * 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); + remapper.add(old_id_iter, new_id_iter); } can_use_old_id = false; break; @@ -468,7 +468,7 @@ static void reuse_old_bmain_data_for_blendfile(ReuseOldBMainData *reuse_data, co 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); + remapper.add(old_id_iter, old_id_iter); /* Port over dependencies of re-used ID, unless matching already existing ones in * new_bmain can be found. diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 38be11f1734..94a434a1900 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -55,6 +55,8 @@ #include "RNA_access.hh" #include "RNA_define.hh" +#include "BLT_translation.hh" + #include "AS_asset_library.hh" #include "AS_asset_representation.hh" -- 2.30.2 From a2768e3066f06289e5a821fad044743343fed150 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 14 Feb 2024 17:36:48 +0100 Subject: [PATCH 113/244] Brush Assets: Add library choice to "Save As" operator Previously the new brush would always be saved to the default library. Now the user is presented with a choice that defaults to the library marked as "Default" in the preferences. Because the library is now chosen by an operator property, we can't check its editable status in the poll function. Instead we just check it when the operator executes. Pull Request: https://projects.blender.org/blender/blender/pulls/118021 --- .../blender/editors/sculpt_paint/paint_ops.cc | 51 ++++++++++++++----- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 94a434a1900..929acf977f1 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -41,6 +41,7 @@ #include "BKE_report.hh" #include "ED_asset_handle.hh" +#include "ED_asset_library.hh" #include "ED_asset_list.hh" #include "ED_asset_mark_clear.hh" #include "ED_asset_menu_utils.hh" @@ -1201,9 +1202,8 @@ static bool brush_asset_save_as_poll(bContext *C) return false; } - const bUserAssetLibrary *user_library = brush_asset_get_default_library(); - if (user_library == nullptr || user_library->dirpath[0] == '\0') { - CTX_wm_operator_poll_msg_set(C, "No default asset library available to save to"); + if (BLI_listbase_is_empty(&U.asset_libraries)) { + CTX_wm_operator_poll_msg_set(C, "No asset library available to save to"); return false; } @@ -1242,6 +1242,13 @@ static bool asset_is_editable(const AssetWeakReference &asset_weak_ref) return true; } +static const bUserAssetLibrary *get_asset_library_from_prop(PointerRNA &ptr) +{ + const int enum_value = RNA_enum_get(&ptr, "asset_library_reference"); + const AssetLibraryReference lib_ref = asset::library_reference_from_enum_value(enum_value); + return BKE_preferences_asset_library_find_index(&U, lib_ref.custom_library_index); +} + static int brush_asset_save_as_exec(bContext *C, wmOperator *op) { Paint *paint = BKE_paint_get_active_from_context(C); @@ -1260,7 +1267,7 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) STRNCPY(name, brush->id.name + 2); } - const bUserAssetLibrary *library = brush_asset_get_default_library(); + const bUserAssetLibrary *library = get_asset_library_from_prop(*op->ptr); if (!library) { return OPERATOR_CANCELLED; } @@ -1314,10 +1321,32 @@ static int brush_asset_save_as_invoke(bContext *C, wmOperator *op, const wmEvent RNA_string_set(op->ptr, "name", brush->id.name + 2); - /* TODO: add information about the asset library this will be saved to? */ + if (const bUserAssetLibrary *library = brush_asset_get_default_library()) { + AssetLibraryReference library_ref{}; + library_ref.custom_library_index = BLI_findindex(&U.asset_libraries, library); + library_ref.type = ASSET_LIBRARY_CUSTOM; + RNA_enum_set( + op->ptr, "asset_library_reference", asset::library_reference_to_enum_value(&library_ref)); + } + return WM_operator_props_dialog_popup(C, op, 400, std::nullopt, IFACE_("Save")); } +static const EnumPropertyItem *rna_asset_library_reference_itemf(bContext * /*C*/, + PointerRNA * /*ptr*/, + PropertyRNA * /*prop*/, + bool *r_free) +{ + const EnumPropertyItem *items = asset::library_reference_to_rna_enum_itemf(false); + if (!items) { + *r_free = false; + return nullptr; + } + + *r_free = true; + return items; +} + static void BRUSH_OT_asset_save_as(wmOperatorType *ot) { ot->name = "Save as Brush Asset"; @@ -1332,6 +1361,10 @@ static void BRUSH_OT_asset_save_as(wmOperatorType *ot) ot->prop = RNA_def_string( ot->srna, "name", nullptr, MAX_NAME, "Name", "Name for the new brush asset"); + + PropertyRNA *prop = RNA_def_property(ot->srna, "asset_library_reference", PROP_ENUM, PROP_NONE); + RNA_def_enum_funcs(prop, rna_asset_library_reference_itemf); + RNA_def_property_ui_text(prop, "Library", "Asset library used to store the new brush"); } static bool brush_asset_delete_poll(bContext *C) @@ -1342,14 +1375,6 @@ static bool brush_asset_delete_poll(bContext *C) return false; } - /* Asset brush, check if belongs to an editable blend file. */ - if (paint->brush_asset_reference && BKE_paint_brush_is_valid_asset(brush)) { - if (!asset_is_editable(*paint->brush_asset_reference)) { - CTX_wm_operator_poll_msg_set(C, "Asset blend file is not editable"); - return false; - } - } - return true; } -- 2.30.2 From f4a5bcabbd2beb58e0835349b128866035f51b44 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 14 Feb 2024 13:34:17 -0500 Subject: [PATCH 114/244] Cleanup: Extract function to convert user library to library reference --- source/blender/editors/sculpt_paint/paint_ops.cc | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 929acf977f1..ef8b1cfe4dc 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -1249,6 +1249,14 @@ static const bUserAssetLibrary *get_asset_library_from_prop(PointerRNA &ptr) return BKE_preferences_asset_library_find_index(&U, lib_ref.custom_library_index); } +static AssetLibraryReference user_library_to_library_ref(const bUserAssetLibrary &user_library) +{ + AssetLibraryReference library_ref{}; + library_ref.custom_library_index = BLI_findindex(&U.asset_libraries, &user_library); + library_ref.type = ASSET_LIBRARY_CUSTOM; + return library_ref; +} + static int brush_asset_save_as_exec(bContext *C, wmOperator *op) { Paint *paint = BKE_paint_get_active_from_context(C); @@ -1322,11 +1330,9 @@ static int brush_asset_save_as_invoke(bContext *C, wmOperator *op, const wmEvent RNA_string_set(op->ptr, "name", brush->id.name + 2); if (const bUserAssetLibrary *library = brush_asset_get_default_library()) { - AssetLibraryReference library_ref{}; - library_ref.custom_library_index = BLI_findindex(&U.asset_libraries, library); - library_ref.type = ASSET_LIBRARY_CUSTOM; - RNA_enum_set( - op->ptr, "asset_library_reference", asset::library_reference_to_enum_value(&library_ref)); + const AssetLibraryReference library_ref = user_library_to_library_ref(*library); + const int enum_value = asset::library_reference_to_enum_value(&library_ref); + RNA_enum_set(op->ptr, "asset_library_reference", enum_value); } return WM_operator_props_dialog_popup(C, op, 400, std::nullopt, IFACE_("Save")); -- 2.30.2 From 71f2c80a777734f4d71bd2435d7541ce1d597f68 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 14 Feb 2024 13:34:46 -0500 Subject: [PATCH 115/244] Cleanup: Rename variable Soon there will be another asset library variable with a different type, this is to distinguish the two --- source/blender/editors/sculpt_paint/paint_ops.cc | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index ef8b1cfe4dc..e6c42f3dd0e 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -1275,11 +1275,12 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) STRNCPY(name, brush->id.name + 2); } - const bUserAssetLibrary *library = get_asset_library_from_prop(*op->ptr); - if (!library) { + const bUserAssetLibrary *user_library = get_asset_library_from_prop(*op->ptr); + if (!user_library) { return OPERATOR_CANCELLED; } - const std::string filepath = brush_asset_blendfile_path_for_save(op->reports, *library, name); + const std::string filepath = brush_asset_blendfile_path_for_save( + op->reports, *user_library, name); if (filepath.empty()) { return OPERATOR_CANCELLED; } @@ -1302,7 +1303,7 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) } AssetWeakReference *new_brush_weak_ref = brush_asset_create_weakref_hack( - library, final_full_asset_filepath); + user_library, final_full_asset_filepath); /* 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()); @@ -1315,7 +1316,7 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) BKE_report(op->reports, RPT_WARNING, "Unable to activate just-saved brush asset"); } - refresh_asset_library(C, *library); + refresh_asset_library(C, *user_library); WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_ADDED, nullptr); WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush); -- 2.30.2 From 126636854bc5c5d6aa4d353b7d07262f3133b76d Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 14 Feb 2024 20:25:19 +0100 Subject: [PATCH 116/244] Brush Assets: Versioning to activate new brush tool and asset Activate the new brush tool when opening old files, and activate the default brush asset for the mode. I'm guessing we may end up storing the default brush asset for each mode somewhere else in the long term, but doing it in versioning is simple now, and has the benefit of decoupling the one time change from the defaults in the future. Also replace the workspace's tool reference that could still point to the old separate tools for each brush. This makes everything work by default when opening a new factory startup file. Pull Request: https://projects.blender.org/blender/blender/pulls/118289 --- .../blender/blenkernel/BKE_blender_version.h | 2 +- .../blenloader/intern/versioning_400.cc | 55 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index f760a0febb9..d9ba05dce6c 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -29,7 +29,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 3 +#define BLENDER_FILE_SUBVERSION 4 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and cancel loading the file, showing a warning to diff --git a/source/blender/blenloader/intern/versioning_400.cc b/source/blender/blenloader/intern/versioning_400.cc index c8b1e093736..147a49126a1 100644 --- a/source/blender/blenloader/intern/versioning_400.cc +++ b/source/blender/blenloader/intern/versioning_400.cc @@ -26,6 +26,7 @@ #include "DNA_movieclip_types.h" #include "DNA_scene_types.h" #include "DNA_sequence_types.h" +#include "DNA_workspace_types.h" #include "DNA_world_types.h" #include "DNA_defaults.h" @@ -47,6 +48,7 @@ #include "BKE_animsys.h" #include "BKE_armature.hh" #include "BKE_attribute.hh" +#include "BKE_context.hh" #include "BKE_curve.hh" #include "BKE_effect.h" #include "BKE_grease_pencil.hh" @@ -1939,6 +1941,55 @@ static bool seq_filter_bilinear_to_auto(Sequence *seq, void * /*user_data*/) return true; } +static void update_paint_modes_for_brush_assets(Main &bmain) +{ + /* Replace paint brushes with a reference to the default brush asset for that mode. */ + LISTBASE_FOREACH (Scene *, scene, &bmain.scenes) { + auto set_paint_asset_ref = [&](Paint &paint, const blender::StringRef asset) { + AssetWeakReference *weak_ref = MEM_new(__func__); + weak_ref->asset_library_type = eAssetLibraryType::ASSET_LIBRARY_ESSENTIALS; + const std::string path = "brushes/essentials_brushes.blend/Brush/" + asset; + weak_ref->relative_asset_identifier = BLI_strdupn(path.data(), path.size()); + paint.brush_asset_reference = weak_ref; + paint.brush = nullptr; + }; + + ToolSettings *ts = scene->toolsettings; + if (ts->sculpt) { + set_paint_asset_ref(ts->sculpt->paint, "Draw"); + } + if (ts->curves_sculpt) { + set_paint_asset_ref(ts->curves_sculpt->paint, "Comb Curves"); + } + if (ts->wpaint) { + set_paint_asset_ref(ts->wpaint->paint, "Paint Weight"); + } + if (ts->vpaint) { + set_paint_asset_ref(ts->vpaint->paint, "Paint Vertex"); + } + set_paint_asset_ref(ts->imapaint.paint, "Paint Texture"); + } + + /* Replace persistent tool references with the new single builtin brush tool. */ + LISTBASE_FOREACH (WorkSpace *, workspace, &bmain.workspaces) { + LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) { + if (tref->space_type != SPACE_VIEW3D) { + continue; + } + if (!ELEM(tref->mode, + CTX_MODE_SCULPT, + CTX_MODE_SCULPT_CURVES, + CTX_MODE_PAINT_TEXTURE, + CTX_MODE_PAINT_VERTEX, + CTX_MODE_PAINT_WEIGHT)) + { + continue; + } + STRNCPY(tref->idname, "builtin.brush"); + } + } +} + void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) { if (!MAIN_VERSION_FILE_ATLEAST(bmain, 400, 1)) { @@ -2938,6 +2989,10 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) FOREACH_NODETREE_END; } + if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 4)) { + update_paint_modes_for_brush_assets(*bmain); + } + /** * Always bump subversion in BKE_blender_version.h when adding versioning * code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check. -- 2.30.2 From b5d44d8f5da3502b47045b47ed253f420c695f3c Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 15 Feb 2024 17:54:33 +0100 Subject: [PATCH 117/244] Make asset shelf visible by default in sculpt/paint modes Adds a new `DEFAULT_VISIBLE` option for the `bl_options` of an asset shelf. When this asset shelf will be created for the first time, the asset shelf will be made visible, otherwise it will be hidden. We don't mess with the visibility further, meaning, when re-opening a previously active asset shelf, we keep the current asset shelf visibility. So only the first load is affected. Alternatively asset shelves could remember their last visibility state, and restore that as context (e.g. the mode) changes. To be evaluated. Had to add some trickery to make this overriding of visibility possible. --- scripts/startup/bl_ui/space_view3d.py | 2 +- source/blender/blenkernel/BKE_screen.hh | 7 ++ .../blender/editors/asset/ED_asset_shelf.hh | 1 + .../editors/asset/intern/asset_shelf.cc | 70 ++++++++++++------- source/blender/editors/screen/area.cc | 3 + source/blender/editors/screen/screen_edit.cc | 3 + .../editors/space_view3d/space_view3d.cc | 1 + source/blender/makesrna/intern/rna_ui.cc | 6 ++ 8 files changed, 67 insertions(+), 26 deletions(-) diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index d2d68e1e48b..a7308d856bd 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -8814,7 +8814,7 @@ class VIEW3D_PT_viewport_debug(Panel): class BrushAssetShelf: bl_space_type = "VIEW_3D" - bl_options = {'NO_ASSET_DRAG'} + bl_options = {'DEFAULT_VISIBLE', 'NO_ASSET_DRAG'} bl_activate_operator = "BRUSH_OT_asset_select" bl_default_preview_size = 40 diff --git a/source/blender/blenkernel/BKE_screen.hh b/source/blender/blenkernel/BKE_screen.hh index 99267581393..b3fbb5fab9e 100644 --- a/source/blender/blenkernel/BKE_screen.hh +++ b/source/blender/blenkernel/BKE_screen.hh @@ -233,6 +233,12 @@ struct ARegionType { /* return context data */ bContextDataCallback context; + /** + * Called on every frame in which the region's poll succeeds, regardless of visibility, before + * drawing, visibility evaluation and initialization. Allows the region to override visibility. + */ + void (*on_poll_success)(const bContext *C, ARegion *region); + /** * Called whenever the user changes the region's size. Not called when the size is changed * through other means, like to adjust for a scaled down window. @@ -516,6 +522,7 @@ enum AssetShelfTypeFlag { /** Do not trigger asset dragging on drag events. Drag events can be overridden with custom * keymap items then. */ ASSET_SHELF_TYPE_FLAG_NO_ASSET_DRAG = (1 << 0), + ASSET_SHELF_TYPE_FLAG_DEFAULT_VISIBLE = (1 << 1), ASSET_SHELF_TYPE_FLAG_MAX }; diff --git a/source/blender/editors/asset/ED_asset_shelf.hh b/source/blender/editors/asset/ED_asset_shelf.hh index 4287614b7cb..36222442574 100644 --- a/source/blender/editors/asset/ED_asset_shelf.hh +++ b/source/blender/editors/asset/ED_asset_shelf.hh @@ -45,6 +45,7 @@ void region_on_user_resize(const ARegion *region); void region_listen(const wmRegionListenerParams *params); void region_layout(const bContext *C, ARegion *region); void region_draw(const bContext *C, ARegion *region); +void region_on_poll_success(const bContext *C, ARegion *region); void region_blend_read_data(BlendDataReader *reader, ARegion *region); void region_blend_write(BlendWriter *writer, ARegion *region); int region_prefsizey(void); diff --git a/source/blender/editors/asset/intern/asset_shelf.cc b/source/blender/editors/asset/intern/asset_shelf.cc index dde68d627b4..7a75ea94958 100644 --- a/source/blender/editors/asset/intern/asset_shelf.cc +++ b/source/blender/editors/asset/intern/asset_shelf.cc @@ -13,6 +13,7 @@ #include "AS_asset_catalog_path.hh" #include "AS_asset_library.hh" +#include "BLI_function_ref.hh" #include "BLI_string.h" #include "BKE_context.hh" @@ -134,12 +135,15 @@ static void activate_shelf(RegionAssetShelf &shelf_regiondata, AssetShelf &shelf * * The returned shelf is guaranteed to have its #AssetShelf.type pointer set. * + * \param on_create: Function called when a new asset shelf is created (case 3). + * * \return A non-owning pointer to the now active shelf. Might be null if no shelf is valid in * current context (all polls failed). */ static AssetShelf *update_active_shelf(const bContext &C, SpaceType &space_type, - RegionAssetShelf &shelf_regiondata) + RegionAssetShelf &shelf_regiondata, + FunctionRef on_create) { /* Note: Don't access #AssetShelf.type directly, use #asset_shelf_type_ensure(). */ @@ -174,6 +178,9 @@ static AssetShelf *update_active_shelf(const bContext &C, BLI_addhead(&shelf_regiondata.shelves, new_shelf); /* Moves ownership to the regiondata. */ activate_shelf(shelf_regiondata, *new_shelf); + if (on_create) { + on_create(*new_shelf); + } return new_shelf; } } @@ -267,13 +274,13 @@ void region_listen(const wmRegionListenerParams *params) void region_init(wmWindowManager *wm, ARegion *region) { - if (!region->regiondata) { - region->regiondata = MEM_cnew("RegionAssetShelf"); - } - RegionAssetShelf &shelf_regiondata = *RegionAssetShelf::get_from_asset_shelf_region(*region); + /* Region-data should've been created by a previously called #region_before_redraw(). */ + RegionAssetShelf *shelf_regiondata = RegionAssetShelf::get_from_asset_shelf_region(*region); + BLI_assert_msg( + shelf_regiondata, + "Region-data should've been created by a previously called `region_before_redraw()`."); - /* Active shelf is only set on draw, so this may be null! */ - AssetShelf *active_shelf = shelf_regiondata.active_shelf; + AssetShelf *active_shelf = shelf_regiondata->active_shelf; UI_view2d_region_reinit(®ion->v2d, V2D_COMMONVIEW_PANELS_UI, region->winx, region->winy); @@ -417,17 +424,12 @@ int region_prefsizey() void region_layout(const bContext *C, ARegion *region) { - const SpaceLink *space = CTX_wm_space_data(C); - SpaceType *space_type = BKE_spacetype_from_id(space->spacetype); - RegionAssetShelf *shelf_regiondata = RegionAssetShelf::get_from_asset_shelf_region(*region); - if (!shelf_regiondata) { - /* Region-data should've been created by a previously called #region_init(). */ - BLI_assert_unreachable(); - return; - } + BLI_assert_msg( + shelf_regiondata, + "Region-data should've been created by a previously called `region_before_redraw()`."); - AssetShelf *active_shelf = update_active_shelf(*C, *space_type, *shelf_regiondata); + const AssetShelf *active_shelf = shelf_regiondata->active_shelf; if (!active_shelf) { return; } @@ -479,6 +481,33 @@ void region_draw(const bContext *C, ARegion *region) UI_view2d_scrollers_draw(®ion->v2d, nullptr); } +void region_on_poll_success(const bContext *C, ARegion *region) +{ + ScrArea *area = CTX_wm_area(C); + + BLI_assert(region->regiontype == RGN_TYPE_ASSET_SHELF); + if (!region->regiondata) { + region->regiondata = MEM_cnew("RegionAssetShelf"); + } + + RegionAssetShelf *shelf_regiondata = RegionAssetShelf::get_from_asset_shelf_region(*region); + if (!shelf_regiondata) { + BLI_assert_unreachable(); + return; + } + + update_active_shelf(*C, *area->type, *shelf_regiondata, [&](AssetShelf &new_shelf) { + /* Update region visibility (`'DEFAULT_VISIBLE'` option). */ + const int old_flag = region->flag; + SET_FLAG_FROM_TEST(region->flag, + (new_shelf.type->flag & ASSET_SHELF_TYPE_FLAG_DEFAULT_VISIBLE) == 0, + RGN_FLAG_HIDDEN); + if (old_flag != region->flag) { + ED_region_visibility_change_update(const_cast(C), area, region); + } + }); +} + void header_region_listen(const wmRegionListenerParams *params) { asset_shelf_region_listen(params); @@ -493,15 +522,6 @@ void header_region_init(wmWindowManager * /*wm*/, ARegion *region) void header_region(const bContext *C, ARegion *region) { - const SpaceLink *space = CTX_wm_space_data(C); - SpaceType *space_type = BKE_spacetype_from_id(space->spacetype); - const ARegion *main_shelf_region = BKE_area_find_region_type(CTX_wm_area(C), - RGN_TYPE_ASSET_SHELF); - - RegionAssetShelf *shelf_regiondata = RegionAssetShelf::get_from_asset_shelf_region( - *main_shelf_region); - update_active_shelf(*C, *space_type, *shelf_regiondata); - ED_region_header_with_button_sections(C, region, uiButtonSectionsAlign::Bottom); } diff --git a/source/blender/editors/screen/area.cc b/source/blender/editors/screen/area.cc index 2da7b766c16..0ed9beb51e6 100644 --- a/source/blender/editors/screen/area.cc +++ b/source/blender/editors/screen/area.cc @@ -2019,6 +2019,9 @@ void ED_area_update_region_sizes(wmWindowManager *wm, wmWindow *win, ScrArea *ar area_azone_init(win, screen, area); LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { + if (region->flag & RGN_FLAG_POLL_FAILED) { + continue; + } region_evaulate_visibility(region); /* region size may have changed, init does necessary adjustments */ diff --git a/source/blender/editors/screen/screen_edit.cc b/source/blender/editors/screen/screen_edit.cc index 9a426038add..d95ad44ed99 100644 --- a/source/blender/editors/screen/screen_edit.cc +++ b/source/blender/editors/screen/screen_edit.cc @@ -755,6 +755,9 @@ static void screen_regions_poll(bContext *C, const wmWindow *win, bScreen *scree if (region_poll(C, screen, area, region) == false) { region->flag |= RGN_FLAG_POLL_FAILED; } + else if (region->type && region->type->on_poll_success) { + region->type->on_poll_success(C, region); + } if (old_region_flag != region->flag) { any_changed = true; diff --git a/source/blender/editors/space_view3d/space_view3d.cc b/source/blender/editors/space_view3d/space_view3d.cc index 138fe99c718..d179e6f9c67 100644 --- a/source/blender/editors/space_view3d/space_view3d.cc +++ b/source/blender/editors/space_view3d/space_view3d.cc @@ -2196,6 +2196,7 @@ void ED_spacetype_view3d() art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_ASSET_SHELF | ED_KEYMAP_FRAMES; art->duplicate = asset::shelf::region_duplicate; art->free = asset::shelf::region_free; + art->on_poll_success = asset::shelf::region_on_poll_success; art->listener = asset::shelf::region_listen; art->poll = asset::shelf::regions_poll; art->snap_size = asset::shelf::region_snap; diff --git a/source/blender/makesrna/intern/rna_ui.cc b/source/blender/makesrna/intern/rna_ui.cc index f541861672d..d2ef60c7b11 100644 --- a/source/blender/makesrna/intern/rna_ui.cc +++ b/source/blender/makesrna/intern/rna_ui.cc @@ -2303,6 +2303,12 @@ static void rna_def_asset_shelf(BlenderRNA *brna) "No Asset Dragging", "Disable the default asset dragging on drag events. Useful for implementing custom " "dragging via custom key-map items"}, + {ASSET_SHELF_TYPE_FLAG_DEFAULT_VISIBLE, + "DEFAULT_VISIBLE", + 0, + "Visible by Default", + "Unhide the asset shelf when it's available for the first time, otherwise it will be " + "hidden"}, {0, nullptr, 0, nullptr, nullptr}, }; -- 2.30.2 From dc687427fcfbcd31a32fe653caa3b4f221707336 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 15 Feb 2024 18:05:00 +0100 Subject: [PATCH 118/244] Cleanup: clarify lambda purpose in comment --- .../editors/asset/intern/asset_shelf.cc | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/source/blender/editors/asset/intern/asset_shelf.cc b/source/blender/editors/asset/intern/asset_shelf.cc index 7a75ea94958..188df9f6cea 100644 --- a/source/blender/editors/asset/intern/asset_shelf.cc +++ b/source/blender/editors/asset/intern/asset_shelf.cc @@ -496,16 +496,17 @@ void region_on_poll_success(const bContext *C, ARegion *region) return; } - update_active_shelf(*C, *area->type, *shelf_regiondata, [&](AssetShelf &new_shelf) { - /* Update region visibility (`'DEFAULT_VISIBLE'` option). */ - const int old_flag = region->flag; - SET_FLAG_FROM_TEST(region->flag, - (new_shelf.type->flag & ASSET_SHELF_TYPE_FLAG_DEFAULT_VISIBLE) == 0, - RGN_FLAG_HIDDEN); - if (old_flag != region->flag) { - ED_region_visibility_change_update(const_cast(C), area, region); - } - }); + update_active_shelf( + *C, *area->type, *shelf_regiondata, /*on_create=*/[&](AssetShelf &new_shelf) { + /* Update region visibility (`'DEFAULT_VISIBLE'` option). */ + const int old_flag = region->flag; + SET_FLAG_FROM_TEST(region->flag, + (new_shelf.type->flag & ASSET_SHELF_TYPE_FLAG_DEFAULT_VISIBLE) == 0, + RGN_FLAG_HIDDEN); + if (old_flag != region->flag) { + ED_region_visibility_change_update(const_cast(C), area, region); + } + }); } void header_region_listen(const wmRegionListenerParams *params) -- 2.30.2 From 9a7e824632d4654bbc9ed12acf3eb8468a2f00cf Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 15 Feb 2024 20:53:02 +0100 Subject: [PATCH 119/244] Asset Shelf: slight increase default brush size from 40 to 48 To make them slightly larger than toolbar buttons still. --- scripts/startup/bl_ui/space_view3d.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index a7308d856bd..3b470e507ca 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -8816,7 +8816,7 @@ class BrushAssetShelf: bl_space_type = "VIEW_3D" bl_options = {'DEFAULT_VISIBLE', 'NO_ASSET_DRAG'} bl_activate_operator = "BRUSH_OT_asset_select" - bl_default_preview_size = 40 + bl_default_preview_size = 48 @classmethod def poll(cls, context): -- 2.30.2 From a49e8d050910b4da37088ee52efe283b0e3691eb Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 15 Feb 2024 20:54:02 +0100 Subject: [PATCH 120/244] Fix: Essentials brushses show delete asset when not supported --- source/blender/editors/sculpt_paint/paint_ops.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index e6c42f3dd0e..fb1933f3605 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -1382,6 +1382,14 @@ static bool brush_asset_delete_poll(bContext *C) return false; } + /* Asset brush, check if belongs to an editable blend file. */ + if (paint->brush_asset_reference && BKE_paint_brush_is_valid_asset(brush)) { + if (!asset_is_editable(*paint->brush_asset_reference)) { + CTX_wm_operator_poll_msg_set(C, "Asset blend file is not editable"); + return false; + } + } + return true; } -- 2.30.2 From 42f1fce5675bcc1b7603e1dcfeee24f22c0ca25d Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 15 Feb 2024 20:55:56 +0100 Subject: [PATCH 121/244] Brush Assets: Remove Reset to Defaults from menu, it works too poorly --- scripts/startup/bl_ui/space_view3d_toolbar.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index cdd8085fc28..7b13ae6dc99 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -61,8 +61,9 @@ class VIEW3D_MT_brush_context_menu(Menu): layout.operator("brush.asset_update", text="Update Asset") layout.operator("brush.asset_revert", text="Revert to Asset") - if context.sculpt_object: - layout.operator("brush.reset", text="Reset to Defaults") + # TODO: does not behave in a useful way now, eventually improve or remove entirely. + # if context.sculpt_object: + # layout.operator("brush.reset", text="Reset to Defaults") else: layout.operator("brush.asset_save_as", text="Save As Asset...", icon='FILE_TICK') layout.operator("brush.asset_delete", text="Delete") -- 2.30.2 From 2c4f83d901426b8ff19060e5e6029494b16eb740 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 16 Feb 2024 18:38:15 +0100 Subject: [PATCH 122/244] Enable sculpt brush catalogs by default After feedback from Julien, the "Cloth", "General" and "Painting" catalogs are the ones we want to enable by default in mesh sculpt mode. Other modes are fine with "All" only. --- source/blender/blenloader/intern/versioning_userdef.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/source/blender/blenloader/intern/versioning_userdef.cc b/source/blender/blenloader/intern/versioning_userdef.cc index 2e4206c546f..959f83aeffe 100644 --- a/source/blender/blenloader/intern/versioning_userdef.cc +++ b/source/blender/blenloader/intern/versioning_userdef.cc @@ -923,6 +923,15 @@ void blo_do_versions_userdef(UserDef *userdef) } } + { + BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled( + userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh/Sculpt/Cloth"); + BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled( + userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh/Sculpt/General"); + BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled( + userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh/Sculpt/Painting"); + } + /** * Always bump subversion in BKE_blender_version.h when adding versioning * code here, and wrap it inside a USER_VERSION_ATLEAST check. -- 2.30.2 From d616f2a3eaf4776886aecad05bffde46a325de65 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 20 Feb 2024 15:07:27 -0500 Subject: [PATCH 123/244] Fix build errors after merge --- source/blender/blenkernel/BKE_brush.hh | 2 +- source/blender/blenkernel/BKE_paint.hh | 2 +- source/blender/blenkernel/intern/brush.cc | 6 ++--- source/blender/blenkernel/intern/paint.cc | 24 +++++++------------ .../asset/intern/asset_shelf_asset_view.cc | 7 +++--- .../blender/editors/sculpt_paint/paint_ops.cc | 17 ++++++------- 6 files changed, 25 insertions(+), 33 deletions(-) diff --git a/source/blender/blenkernel/BKE_brush.hh b/source/blender/blenkernel/BKE_brush.hh index 5d5d66a7e13..5b959003906 100644 --- a/source/blender/blenkernel/BKE_brush.hh +++ b/source/blender/blenkernel/BKE_brush.hh @@ -33,7 +33,7 @@ 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); + const AssetWeakReference &brush_asset_reference); /* Data-block functions. */ diff --git a/source/blender/blenkernel/BKE_paint.hh b/source/blender/blenkernel/BKE_paint.hh index ea2a9f12daf..e179694978c 100644 --- a/source/blender/blenkernel/BKE_paint.hh +++ b/source/blender/blenkernel/BKE_paint.hh @@ -218,7 +218,7 @@ bool BKE_paint_brush_is_valid_asset(const Brush *brush); */ bool BKE_paint_brush_asset_set(Paint *paint, Brush *brush, - AssetWeakReference *weak_asset_reference); + const AssetWeakReference &weak_asset_reference); /** * Get the active brush of given paint struct, together with its weak asset reference. diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index 13a9b1e3859..89012c781a9 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -526,15 +526,13 @@ static void brush_defaults(Brush *brush) #undef FROM_DEFAULT_PTR } -Brush *BKE_brush_asset_runtime_ensure(Main *bmain, const AssetWeakReference *brush_asset_reference) +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); + &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; diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index f455eeb9f8e..310d3d5ecec 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -685,30 +685,26 @@ bool BKE_paint_brush_is_valid_asset(const Brush *brush) static void paint_brush_asset_update(Paint &paint, Brush *brush, - AssetWeakReference *brush_asset_reference) + const AssetWeakReference &brush_asset_reference) { - if (paint.brush_asset_reference != nullptr) { - BKE_asset_weak_reference_free(&paint.brush_asset_reference); - } + MEM_delete(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))) { - BKE_asset_weak_reference_free(&brush_asset_reference); return; } - paint.brush_asset_reference = brush_asset_reference; + paint.brush_asset_reference = MEM_new(__func__, brush_asset_reference); } bool BKE_paint_brush_asset_set(Paint *paint, Brush *brush, - AssetWeakReference *weak_asset_reference) + const AssetWeakReference &weak_asset_reference) { /* Should not happen for users if brush assets are properly filtered by mode, but still protect * against it in case of invalid API usage. */ if (brush && paint->runtime.ob_mode != brush->ob_mode) { - BKE_asset_weak_reference_free(&weak_asset_reference); return false; } @@ -742,14 +738,14 @@ void BKE_paint_brush_asset_restore(Main *bmain, Paint *paint) return; } - AssetWeakReference *brush_asset_reference = paint->brush_asset_reference; - paint->brush_asset_reference = nullptr; + AssetWeakReference weak_ref = std::move(*paint->brush_asset_reference); + MEM_delete(paint->brush_asset_reference); - Brush *brush_asset = BKE_brush_asset_runtime_ensure(bmain, brush_asset_reference); + Brush *brush_asset = BKE_brush_asset_runtime_ensure(bmain, weak_ref); /* Will either re-assign the brush_asset_reference to `paint`, or free it if loading a brush ID * from it failed. */ - BKE_paint_brush_asset_set(paint, brush_asset, brush_asset_reference); + BKE_paint_brush_asset_set(paint, brush_asset, weak_ref); } void BKE_paint_runtime_init(const ToolSettings *ts, Paint *paint) @@ -1290,9 +1286,7 @@ void BKE_paint_free(Paint *paint) { BKE_curvemapping_free(paint->cavity_curve); MEM_SAFE_FREE(paint->tool_slots); - if (paint->brush_asset_reference != nullptr) { - BKE_asset_weak_reference_free(&paint->brush_asset_reference); - } + MEM_delete(paint->brush_asset_reference); } void BKE_paint_copy(const Paint *src, Paint *tar, const int flag) diff --git a/source/blender/editors/asset/intern/asset_shelf_asset_view.cc b/source/blender/editors/asset/intern/asset_shelf_asset_view.cc index a4ced448857..bfdeb7dca5e 100644 --- a/source/blender/editors/asset/intern/asset_shelf_asset_view.cc +++ b/source/blender/editors/asset/intern/asset_shelf_asset_view.cc @@ -102,7 +102,7 @@ AssetView::AssetView(const AssetLibraryReference &library_ref, const AssetShelf AssetView::~AssetView() { - BKE_asset_weak_reference_free(&active_asset_); + MEM_delete(active_asset_); } void AssetView::build_items() @@ -235,10 +235,9 @@ std::optional AssetViewItem::should_be_active() const return false; } const asset_system::AssetRepresentation *asset = handle_get_representation(&asset_); - AssetWeakReference *weak_ref = asset->make_weak_reference(); - const bool matches = *asset_view.active_asset_ == *weak_ref; + AssetWeakReference weak_ref = asset->make_weak_reference(); + const bool matches = *asset_view.active_asset_ == weak_ref; - BKE_asset_weak_reference_free(&weak_ref); return matches; } diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index fb1933f3605..313188fd4a1 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -1002,7 +1002,7 @@ static int brush_asset_select_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - AssetWeakReference *brush_asset_reference = asset->make_weak_reference(); + AssetWeakReference brush_asset_reference = asset->make_weak_reference(); Brush *brush = BKE_brush_asset_runtime_ensure(CTX_data_main(C), brush_asset_reference); Paint *paint = BKE_paint_get_active_from_context(C); @@ -1034,18 +1034,19 @@ static void BRUSH_OT_asset_select(wmOperatorType *ot) /* FIXME Quick dirty hack to generate a weak ref from 'raw' paths. * This needs to be properly implemented in assetlib code. */ -static AssetWeakReference *brush_asset_create_weakref_hack(const bUserAssetLibrary *user_asset_lib, - std::string &file_path) +static AssetWeakReference brush_asset_create_weakref_hack(const bUserAssetLibrary *user_asset_lib, + std::string &file_path) { - AssetWeakReference *asset_weak_ref = MEM_new(__func__); + AssetWeakReference asset_weak_ref{}; StringRef asset_root_path = user_asset_lib->dirpath; BLI_assert(file_path.find(asset_root_path) == 0); std::string relative_asset_path = file_path.substr(size_t(asset_root_path.size()) + 1); - asset_weak_ref->asset_library_type = ASSET_LIBRARY_CUSTOM; - asset_weak_ref->asset_library_identifier = BLI_strdup(user_asset_lib->name); - asset_weak_ref->relative_asset_identifier = BLI_strdup(relative_asset_path.c_str()); + asset_weak_ref.asset_library_type = ASSET_LIBRARY_CUSTOM; + asset_weak_ref.asset_library_identifier = BLI_strdup(user_asset_lib->name); + asset_weak_ref.relative_asset_identifier = BLI_strdupn(relative_asset_path.c_str(), + relative_asset_path.size()); return asset_weak_ref; } @@ -1302,7 +1303,7 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - AssetWeakReference *new_brush_weak_ref = brush_asset_create_weakref_hack( + AssetWeakReference new_brush_weak_ref = brush_asset_create_weakref_hack( user_library, final_full_asset_filepath); /* TODO: maybe not needed, even less so if there is more visual confirmation of change. */ -- 2.30.2 From c0eacdb5746066f2803917dbd7c3177973918185 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 20 Feb 2024 15:35:01 -0500 Subject: [PATCH 124/244] Fix more errors after merge --- .../asset_system/tests/asset_representation_test.cc | 5 ++--- source/blender/blenkernel/intern/asset_weak_reference.cc | 7 +++++++ source/blender/blenkernel/intern/paint.cc | 1 + source/blender/makesdna/DNA_asset_types.h | 4 ++-- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/source/blender/asset_system/tests/asset_representation_test.cc b/source/blender/asset_system/tests/asset_representation_test.cc index 0d8dd0b37ad..d2ed2c2a5f1 100644 --- a/source/blender/asset_system/tests/asset_representation_test.cc +++ b/source/blender/asset_system/tests/asset_representation_test.cc @@ -125,13 +125,12 @@ TEST_F(AssetRepresentationTest, weak_reference__compare) asset_library_root_); AssetRepresentation &asset = add_dummy_asset(*library, "path/to/an/asset"); - AssetWeakReference *weak_ref = asset.make_weak_reference(); + AssetWeakReference weak_ref = asset.make_weak_reference(); AssetWeakReference other; other.asset_library_type = ASSET_LIBRARY_CUSTOM; other.asset_library_identifier = "My custom lib"; other.relative_asset_identifier = "path/to/an/asset"; - EXPECT_EQ(*weak_ref, other); - BKE_asset_weak_reference_free(&weak_ref); + EXPECT_EQ(weak_ref, other); /* Make the destructor work. */ other.asset_library_identifier = nullptr; diff --git a/source/blender/blenkernel/intern/asset_weak_reference.cc b/source/blender/blenkernel/intern/asset_weak_reference.cc index d354415d722..ce779ba4596 100644 --- a/source/blender/blenkernel/intern/asset_weak_reference.cc +++ b/source/blender/blenkernel/intern/asset_weak_reference.cc @@ -30,6 +30,13 @@ AssetWeakReference::AssetWeakReference() { } +AssetWeakReference::AssetWeakReference(const AssetWeakReference &other) + : asset_library_type(other.asset_library_type), + asset_library_identifier(BLI_strdup_null(other.asset_library_identifier)), + relative_asset_identifier(BLI_strdup_null(other.relative_asset_identifier)) +{ +} + AssetWeakReference::AssetWeakReference(AssetWeakReference &&other) : asset_library_type(other.asset_library_type), asset_library_identifier(other.asset_library_identifier), diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 310d3d5ecec..6ee29a3dc08 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -740,6 +740,7 @@ void BKE_paint_brush_asset_restore(Main *bmain, Paint *paint) AssetWeakReference weak_ref = std::move(*paint->brush_asset_reference); MEM_delete(paint->brush_asset_reference); + paint->brush_asset_reference = nullptr; Brush *brush_asset = BKE_brush_asset_runtime_ensure(bmain, weak_ref); diff --git a/source/blender/makesdna/DNA_asset_types.h b/source/blender/makesdna/DNA_asset_types.h index d474ff590f3..e5262cb4e78 100644 --- a/source/blender/makesdna/DNA_asset_types.h +++ b/source/blender/makesdna/DNA_asset_types.h @@ -178,10 +178,10 @@ typedef struct AssetWeakReference { #ifdef __cplusplus AssetWeakReference(); + AssetWeakReference(const AssetWeakReference &); AssetWeakReference(AssetWeakReference &&); - AssetWeakReference(const AssetWeakReference &) = delete; - ~AssetWeakReference(); AssetWeakReference &operator=(AssetWeakReference &&); + ~AssetWeakReference(); bool operator==(const AssetWeakReference &other) const; bool operator!=(const AssetWeakReference &other) const; -- 2.30.2 From 633e0a6b3ac6ab4d87f4410dca53164d0918f36e Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 21 Feb 2024 14:03:30 +0100 Subject: [PATCH 125/244] Brush Assets: Add catalog option to asset pushing operator Adds the ability to choose which of the libraries catalogs to add the new asset to, or to create a new catalog. The catalogs are presented in a dropdown list, and every hierarchy level is available. Eventually we might want some form of tree view here. But for now this approach is very simple. Pull Request: https://projects.blender.org/blender/blender/pulls/118382 --- .../blender/editors/sculpt_paint/paint_ops.cc | 90 ++++++++++++++++++- 1 file changed, 88 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 313188fd4a1..817c41d9f4a 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -58,6 +58,7 @@ #include "BLT_translation.hh" +#include "AS_asset_catalog_tree.hh" #include "AS_asset_library.hh" #include "AS_asset_representation.hh" @@ -1129,6 +1130,8 @@ static bool brush_asset_write_in_library(Main *bmain, Brush *brush, const char *name, const StringRefNull filepath, + const std::optional catalog, + const std::optional catalog_simple_name, std::string &final_full_file_path, ReportList *reports) { @@ -1171,6 +1174,13 @@ static bool brush_asset_write_in_library(Main *bmain, } brush->id.override_library = nullptr; + if (catalog) { + brush->id.asset_data->catalog_id = *catalog; + } + if (catalog_simple_name) { + STRNCPY(brush->id.asset_data->catalog_simple_name, catalog_simple_name->c_str()); + } + BKE_blendfile_write_partial_tag_ID(&brush->id, true); /* TODO: check overwriting existing file. */ @@ -1250,6 +1260,28 @@ static const bUserAssetLibrary *get_asset_library_from_prop(PointerRNA &ptr) return BKE_preferences_asset_library_find_index(&U, lib_ref.custom_library_index); } +static asset_system::AssetCatalog &asset_library_ensure_catalog( + asset_system::AssetLibrary &library, const asset_system::AssetCatalogPath &path) +{ + if (asset_system::AssetCatalog *catalog = library.catalog_service->find_catalog_by_path(path)) { + return *catalog; + } + return *library.catalog_service->create_catalog(path); +} + +static asset_system::AssetCatalog &asset_library_ensure_catalogs_in_path( + asset_system::AssetLibrary &library, const asset_system::AssetCatalogPath &path) +{ + /* Adding multiple catalogs in a path at a time with #AssetCatalogService::create_catalog() + * doesn't work; add each potentially new catalog in the hierarchy manually here. */ + asset_system::AssetCatalogPath parent = ""; + path.iterate_components([&](StringRef component_name, bool /*is_last_component*/) { + asset_library_ensure_catalog(library, parent / component_name); + parent = parent / component_name; + }); + return *library.catalog_service->find_catalog_by_path(path); +} + static AssetLibraryReference user_library_to_library_ref(const bUserAssetLibrary &user_library) { AssetLibraryReference library_ref{}; @@ -1293,10 +1325,33 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) } BLI_assert(BKE_paint_brush_is_valid_asset(brush)); + asset_system::AssetLibrary *library = AS_asset_library_load( + CTX_data_main(C), user_library_to_library_ref(*user_library)); + if (!library) { + BKE_report(op->reports, RPT_ERROR, "Failed to load asset library"); + return OPERATOR_CANCELLED; + } + + /* Add asset to catalog. */ + char catalog_path[MAX_NAME]; + RNA_string_get(op->ptr, "catalog_path", catalog_path); + const asset_system::AssetCatalog &catalog = asset_library_ensure_catalogs_in_path(*library, + catalog_path); + const asset_system::CatalogID catalog_id = catalog.catalog_id; + const std::string catalog_simple_name = catalog.simple_name; + + library->catalog_service->write_to_disk(filepath); + /* Save to asset library. */ 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); + const bool sucess = brush_asset_write_in_library(CTX_data_main(C), + brush, + name, + filepath, + catalog_id, + catalog_simple_name, + final_full_asset_filepath, + op->reports); if (!sucess) { BKE_report(op->reports, RPT_ERROR, "Failed to write to asset library"); @@ -1355,6 +1410,30 @@ static const EnumPropertyItem *rna_asset_library_reference_itemf(bContext * /*C* return items; } +static void visit_asset_catalog_for_search_fn( + const bContext *C, + PointerRNA *ptr, + PropertyRNA * /*prop*/, + const char * /*edit_text*/, + FunctionRef visit_fn) +{ + const bUserAssetLibrary *user_library = get_asset_library_from_prop(*ptr); + if (!user_library) { + return; + } + + asset_system::AssetLibrary *library = AS_asset_library_load( + CTX_data_main(C), user_library_to_library_ref(*user_library)); + if (!library) { + return; + } + + asset_system::AssetCatalogTree &full_tree = *library->catalog_service->get_catalog_tree(); + full_tree.foreach_item([&](const asset_system::AssetCatalogTreeItem &item) { + visit_fn(StringPropertySearchVisitParams{item.catalog_path().str(), std::nullopt}); + }); +} + static void BRUSH_OT_asset_save_as(wmOperatorType *ot) { ot->name = "Save as Brush Asset"; @@ -1373,6 +1452,11 @@ static void BRUSH_OT_asset_save_as(wmOperatorType *ot) PropertyRNA *prop = RNA_def_property(ot->srna, "asset_library_reference", PROP_ENUM, PROP_NONE); RNA_def_enum_funcs(prop, rna_asset_library_reference_itemf); RNA_def_property_ui_text(prop, "Library", "Asset library used to store the new brush"); + + prop = RNA_def_string( + ot->srna, "catalog_path", nullptr, MAX_NAME, "Catalog", "Catalog to use for the new asset"); + RNA_def_property_string_search_func_runtime( + prop, visit_asset_catalog_for_search_fn, PROP_STRING_SEARCH_SUGGESTION); } static bool brush_asset_delete_poll(bContext *C) @@ -1487,6 +1571,8 @@ static int brush_asset_update_exec(bContext *C, wmOperator *op) brush, brush->id.name + 2, filepath, + std::nullopt, + std::nullopt, final_full_asset_filepath, op->reports); -- 2.30.2 From 525e85bd30c49c2fb402e38a1d969b34eaf69cf5 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 21 Feb 2024 10:08:45 -0500 Subject: [PATCH 126/244] Cleanup: Use asset weak reference copy constructor instead of function --- source/blender/blenkernel/BKE_asset.hh | 1 - .../blenkernel/intern/asset_weak_reference.cc | 14 ------------- source/blender/blenkernel/intern/paint.cc | 21 +++++++++++-------- .../asset/intern/asset_shelf_asset_view.cc | 14 +++++++------ 4 files changed, 20 insertions(+), 30 deletions(-) diff --git a/source/blender/blenkernel/BKE_asset.hh b/source/blender/blenkernel/BKE_asset.hh index c9596c48cd6..a98a157384d 100644 --- a/source/blender/blenkernel/BKE_asset.hh +++ b/source/blender/blenkernel/BKE_asset.hh @@ -76,6 +76,5 @@ PreviewImage *BKE_asset_metadata_preview_get_from_id(const AssetMetaData *asset_ void BKE_asset_metadata_write(BlendWriter *writer, AssetMetaData *asset_data); void BKE_asset_metadata_read(BlendDataReader *reader, AssetMetaData *asset_data); -AssetWeakReference *BKE_asset_weak_reference_copy(const 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); diff --git a/source/blender/blenkernel/intern/asset_weak_reference.cc b/source/blender/blenkernel/intern/asset_weak_reference.cc index ce779ba4596..4aa281b0c7e 100644 --- a/source/blender/blenkernel/intern/asset_weak_reference.cc +++ b/source/blender/blenkernel/intern/asset_weak_reference.cc @@ -84,20 +84,6 @@ bool AssetWeakReference::operator!=(const AssetWeakReference &other) const return !(*this == other); } -AssetWeakReference *BKE_asset_weak_reference_copy(const AssetWeakReference *weak_ref) -{ - if (weak_ref == nullptr) { - return nullptr; - } - - AssetWeakReference *weak_ref_copy = MEM_new(__func__); - weak_ref_copy->asset_library_type = weak_ref->asset_library_type; - weak_ref_copy->asset_library_identifier = BLI_strdup_null(weak_ref->asset_library_identifier); - weak_ref_copy->relative_asset_identifier = BLI_strdup_null(weak_ref->relative_asset_identifier); - - return weak_ref_copy; -} - AssetWeakReference AssetWeakReference::make_reference( const asset_system::AssetLibrary &library, const asset_system::AssetIdentifier &asset_identifier) diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 6ee29a3dc08..7e832558269 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1290,20 +1290,23 @@ void BKE_paint_free(Paint *paint) MEM_delete(paint->brush_asset_reference); } -void BKE_paint_copy(const Paint *src, Paint *tar, const int flag) +void BKE_paint_copy(const Paint *src, Paint *dst, const int flag) { - tar->brush = src->brush; - tar->cavity_curve = BKE_curvemapping_copy(src->cavity_curve); - tar->tool_slots = static_cast(MEM_dupallocN(src->tool_slots)); + dst->brush = src->brush; + dst->cavity_curve = BKE_curvemapping_copy(src->cavity_curve); + dst->tool_slots = static_cast(MEM_dupallocN(src->tool_slots)); - tar->brush_asset_reference = BKE_asset_weak_reference_copy(src->brush_asset_reference); + if (src->brush_asset_reference) { + dst->brush_asset_reference = MEM_new(__func__, + *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); + id_us_plus((ID *)dst->brush); + id_us_plus((ID *)dst->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); + for (int i = 0; i < dst->tool_slots_len; i++) { + id_us_plus((ID *)dst->tool_slots[i].brush); } } } diff --git a/source/blender/editors/asset/intern/asset_shelf_asset_view.cc b/source/blender/editors/asset/intern/asset_shelf_asset_view.cc index bfdeb7dca5e..785cacb5fa8 100644 --- a/source/blender/editors/asset/intern/asset_shelf_asset_view.cc +++ b/source/blender/editors/asset/intern/asset_shelf_asset_view.cc @@ -40,7 +40,7 @@ namespace blender::ed::asset::shelf { class AssetView : public ui::AbstractGridView { const AssetLibraryReference library_ref_; const AssetShelf &shelf_; - AssetWeakReference *active_asset_ = nullptr; + std::optional active_asset_; /** Copy of the filter string from #AssetShelfSettings, with extra '*' added to the beginning and * end of the string, for `fnmatch()` to work. */ char search_string[sizeof(AssetShelfSettings::search_string) + 2] = ""; @@ -96,14 +96,16 @@ AssetView::AssetView(const AssetLibraryReference &library_ref, const AssetShelf search_string, shelf.settings.search_string, '*', sizeof(search_string)); } if (shelf.type->get_active_asset) { - active_asset_ = BKE_asset_weak_reference_copy(shelf.type->get_active_asset(shelf.type)); + if (const AssetWeakReference *weak_ref = shelf.type->get_active_asset(shelf.type)) { + active_asset_ = *weak_ref; + } + else { + active_asset_.reset(); + } } } -AssetView::~AssetView() -{ - MEM_delete(active_asset_); -} +AssetView::~AssetView() {} void AssetView::build_items() { -- 2.30.2 From 84997955b4551d305ffebe8ea968595bc455f304 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 21 Feb 2024 10:25:19 -0500 Subject: [PATCH 127/244] Change AssetWeakReference equality functions to free functions Just a more typical pattern. Also use STREQ and make all three comparisons look the same. --- .../blenkernel/intern/asset_weak_reference.cc | 17 +++++++---------- source/blender/makesdna/DNA_asset_types.h | 7 +++++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/source/blender/blenkernel/intern/asset_weak_reference.cc b/source/blender/blenkernel/intern/asset_weak_reference.cc index 4aa281b0c7e..2e16a450404 100644 --- a/source/blender/blenkernel/intern/asset_weak_reference.cc +++ b/source/blender/blenkernel/intern/asset_weak_reference.cc @@ -67,21 +67,18 @@ AssetWeakReference &AssetWeakReference::operator=(AssetWeakReference &&other) return *this; } -bool AssetWeakReference::operator==(const AssetWeakReference &other) const +bool operator==(const AssetWeakReference &a, const AssetWeakReference &b) { - if (asset_library_type != other.asset_library_type) { + if (a.asset_library_type != b.asset_library_type) { return false; } - if (StringRef(asset_library_identifier) != StringRef(other.asset_library_identifier)) { + if (!STREQ(a.asset_library_identifier, b.asset_library_identifier)) { return false; } - - return StringRef(relative_asset_identifier) == StringRef(other.relative_asset_identifier); -} - -bool AssetWeakReference::operator!=(const AssetWeakReference &other) const -{ - return !(*this == other); + if (!STREQ(a.relative_asset_identifier, b.relative_asset_identifier)) { + return false; + } + return true; } AssetWeakReference AssetWeakReference::make_reference( diff --git a/source/blender/makesdna/DNA_asset_types.h b/source/blender/makesdna/DNA_asset_types.h index 4f40b1903cd..d48e55b1823 100644 --- a/source/blender/makesdna/DNA_asset_types.h +++ b/source/blender/makesdna/DNA_asset_types.h @@ -174,8 +174,11 @@ typedef struct AssetWeakReference { AssetWeakReference &operator=(AssetWeakReference &&); ~AssetWeakReference(); - bool operator==(const AssetWeakReference &other) const; - bool operator!=(const AssetWeakReference &other) const; + friend bool operator==(const AssetWeakReference &a, const AssetWeakReference &b); + friend bool operator!=(const AssetWeakReference &a, const AssetWeakReference &b) + { + return !(a == b); + } /** * See AssetRepresentation::make_weak_reference(). -- 2.30.2 From 914ad994f2988c45162c60d05b331a1b286dc5cb Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 21 Feb 2024 11:07:59 -0500 Subject: [PATCH 128/244] Fix: Handle null strings in asset weak reference comparison Mistake in earlier cleanup --- source/blender/blenkernel/intern/asset_weak_reference.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/asset_weak_reference.cc b/source/blender/blenkernel/intern/asset_weak_reference.cc index f2f1a91d1a8..cb3b9807b45 100644 --- a/source/blender/blenkernel/intern/asset_weak_reference.cc +++ b/source/blender/blenkernel/intern/asset_weak_reference.cc @@ -78,10 +78,10 @@ bool operator==(const AssetWeakReference &a, const AssetWeakReference &b) if (a.asset_library_type != b.asset_library_type) { return false; } - if (!STREQ(a.asset_library_identifier, b.asset_library_identifier)) { + if (StringRef(a.asset_library_identifier) != StringRef(b.asset_library_identifier)) { return false; } - if (!STREQ(a.relative_asset_identifier, b.relative_asset_identifier)) { + if (StringRef(a.relative_asset_identifier) != StringRef(b.relative_asset_identifier)) { return false; } return true; -- 2.30.2 From b030911aa9d70055714f8a81f2c56107fd5167d7 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 27 Feb 2024 14:49:24 +0100 Subject: [PATCH 129/244] Brush Assets: Store assets outside Main This is an experiment, but we hope this can become stable and in an acceptable state in the brush-assets-project branch. * 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. Highlighting property changes is gone because of this. This change does not necessarily need to be made along with the rest, but it does simplify making renaming and editing textures work. * 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. * Basic texture editing works and gets saved and loaded. But will need more work for all operators, RNA, editors to deal with different mains. Includes other changes for brush/tool selection. * Remove "brush select" operator * Remove creating a new brush on Paint init when none is found * Remove unnecessary tool -> brush connection * Remove BRUSH_OT_add operator Pull Request: https://projects.blender.org/blender/blender/pulls/117730 --- .../startup/bl_ui/properties_paint_common.py | 8 +- scripts/startup/bl_ui/space_view3d_toolbar.py | 8 +- source/blender/blenkernel/BKE_asset.hh | 8 + source/blender/blenkernel/BKE_brush.hh | 4 - source/blender/blenkernel/BKE_lib_override.hh | 11 - .../blenkernel/intern/asset_weak_reference.cc | 138 +++++++++ source/blender/blenkernel/intern/blendfile.cc | 193 +------------ source/blender/blenkernel/intern/brush.cc | 55 ---- .../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 | 34 ++- .../blender/editors/render/render_shading.cc | 17 +- .../blender/editors/sculpt_paint/paint_ops.cc | 59 ++-- .../blender/editors/space_image/image_ops.cc | 18 +- source/blender/makesdna/DNA_ID.h | 9 + source/blender/makesdna/DNA_scene_types.h | 5 + source/blender/makesrna/intern/makesrna.cc | 2 + source/blender/makesrna/intern/rna_ID.cc | 31 ++- .../blender/makesrna/intern/rna_main_api.cc | 13 + .../blender/windowmanager/intern/wm_files.cc | 261 +----------------- .../windowmanager/intern/wm_init_exit.cc | 4 + 23 files changed, 297 insertions(+), 646 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 7b13ae6dc99..c7ae171663d 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 a98a157384d..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; @@ -20,6 +21,7 @@ struct BlendDataReader; struct BlendWriter; struct ID; struct IDProperty; +struct Main; struct PreviewImage; using PreSaveFn = void (*)(void *asset_ptr, AssetMetaData *asset_data); @@ -78,3 +80,9 @@ void BKE_asset_metadata_read(BlendDataReader *reader, AssetMetaData *asset_data) 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, + ID_Type id_type, + const AssetWeakReference &weak_ref); diff --git a/source/blender/blenkernel/BKE_brush.hh b/source/blender/blenkernel/BKE_brush.hh index 5b959003906..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 cb3b9807b45..fe844e13d34 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.hh" #include "DNA_asset_types.h" @@ -118,3 +128,131 @@ 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 filepath; + Main *main; + + 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)) + { + } + + ~AssetWeakReferenceMain() + { + if (main) { + BKE_main_free(main); + } + } +}; + +static Vector &get_weak_reference_mains() +{ + static Vector mains; + return 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 : get_weak_reference_mains()) { + /* 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; + } + } + } + + BLI_assert_unreachable(); + return nullptr; +} + +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; + } + } + + get_weak_reference_mains().append_as(filepath); + return *get_weak_reference_mains().last().main; +} + +void BKE_asset_weak_reference_main_free() +{ + get_weak_reference_mains().clear_and_shrink(); +} + +ID *BKE_asset_weak_reference_ensure(Main &global_main, + const ID_Type id_type, + const AssetWeakReference &weak_ref) +{ + 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; + } + + BLI_assert(asset_name != nullptr); + + /* If weak reference resolves to a null library path, assume we are in local asset case. */ + 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 = 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_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); + + BlendfileLinkAppendContextItem *lapp_item = BKE_blendfile_link_append_context_item_add( + 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); + 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(&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)); + + return local_asset; +} diff --git a/source/blender/blenkernel/intern/blendfile.cc b/source/blender/blenkernel/intern/blendfile.cc index b678f17bedc..d32daa7c259 100644 --- a/source/blender/blenkernel/intern/blendfile.cc +++ b/source/blender/blenkernel/intern/blendfile.cc @@ -302,188 +302,6 @@ static bool reuse_bmain_data_remapper_is_id_remapped(id::IDRemapper &remapper, I return false; } -static Library *reuse_bmain_data_dependencies_new_library_get(ReuseOldBMainData *reuse_data, - Library *old_lib) -{ - if (old_lib == nullptr) { - return nullptr; - } - - id::IDRemapper *remapper = reuse_data->remapper; - Library *new_lib = old_lib; - IDRemapperApplyResult result = remapper->apply(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. */ - id::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. */ - remapper.add(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. */ - remapper.add(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); - - id::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)) { - remapper.add(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. */ - remapper.add(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. @@ -491,9 +309,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) { @@ -906,12 +723,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 89012c781a9..2f7f46df019 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -25,7 +25,6 @@ #include "BLT_translation.hh" #include "BKE_asset.hh" -#include "BKE_blendfile_link_append.hh" #include "BKE_bpath.hh" #include "BKE_brush.hh" #include "BKE_colortools.hh" @@ -526,60 +525,6 @@ static void brush_defaults(Brush *brush) #undef FROM_DEFAULT_PTR } -Brush *BKE_brush_asset_runtime_ensure(Main *bmain, const AssetWeakReference &brush_asset_reference) -{ - 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 ac3910d58d1..b176c6db072 100644 --- a/source/blender/blenkernel/intern/lib_override.cc +++ b/source/blender/blenkernel/intern/lib_override.cc @@ -341,42 +341,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 7e832558269..9d64f7e053b 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -668,8 +668,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); @@ -678,9 +676,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, { MEM_delete(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)) { return; } @@ -742,7 +736,8 @@ void BKE_paint_brush_asset_restore(Main *bmain, Paint *paint) MEM_delete(paint->brush_asset_reference); paint->brush_asset_reference = nullptr; - Brush *brush_asset = BKE_brush_asset_runtime_ensure(bmain, weak_ref); + Brush *brush_asset = reinterpret_cast( + 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. */ @@ -1302,13 +1297,7 @@ void BKE_paint_copy(const Paint *src, Paint *dst, const int flag) } if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) { - id_us_plus((ID *)dst->brush); id_us_plus((ID *)dst->palette); - if (src->tool_slots != nullptr) { - for (int i = 0; i < dst->tool_slots_len; i++) { - id_us_plus((ID *)dst->tool_slots[i].brush); - } - } } } diff --git a/source/blender/blenkernel/intern/paint_toolslots.cc b/source/blender/blenkernel/intern/paint_toolslots.cc index 4ab9259de8f..fc03248b527 100644 --- a/source/blender/blenkernel/intern/paint_toolslots.cc +++ b/source/blender/blenkernel/intern/paint_toolslots.cc @@ -53,7 +53,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); } } } @@ -120,10 +119,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; } @@ -147,7 +142,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 99187c7e878..d8e7697e73b 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -591,7 +591,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. @@ -610,7 +610,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 332c74d3a75..94a4a2eff5d 100644 --- a/source/blender/editors/interface/interface_templates.cc +++ b/source/blender/editors/interface/interface_templates.cc @@ -43,6 +43,8 @@ #include "BLF_api.hh" #include "BLT_translation.hh" +#include "BKE_action.h" +#include "BKE_asset.hh" #include "BKE_blender_version.h" #include "BKE_blendfile.hh" #include "BKE_colorband.hh" @@ -943,6 +945,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); @@ -996,12 +1000,13 @@ 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(CTX_data_main(C), 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. */ @@ -1017,12 +1022,13 @@ 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(CTX_data_main(C), 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); @@ -1037,15 +1043,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); } @@ -1336,7 +1343,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")); @@ -1755,10 +1762,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/render/render_shading.cc b/source/blender/editors/render/render_shading.cc index 6ff7b11b11c..f319c069b83 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/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 817c41d9f4a..60736242078 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -35,6 +35,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" @@ -72,7 +73,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) { @@ -194,7 +195,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); @@ -944,6 +945,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; } @@ -997,6 +999,7 @@ 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); const asset_system::AssetRepresentation *asset = asset::operator_asset_reference_props_get_asset_from_all_library(*C, *op->ptr, op->reports); if (!asset) { @@ -1004,7 +1007,8 @@ static int brush_asset_select_exec(bContext *C, wmOperator *op) } 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, ID_BR, brush_asset_reference)); Paint *paint = BKE_paint_get_active_from_context(C); @@ -1292,6 +1296,7 @@ static AssetLibraryReference user_library_to_library_ref(const bUserAssetLibrary 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) { @@ -1343,8 +1348,10 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) library->catalog_service->write_to_disk(filepath); /* 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), + const bool sucess = brush_asset_write_in_library(asset_main, brush, name, filepath, @@ -1364,8 +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()); - 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, 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. */ @@ -1480,9 +1487,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); bUserAssetLibrary *library = BKE_preferences_asset_library_find_by_name( &U, paint->brush_asset_reference->asset_library_identifier); @@ -1502,15 +1510,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? refresh_asset_library(C, *library); WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_REMOVED, nullptr); @@ -1554,11 +1559,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 *asset_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( @@ -1566,8 +1575,9 @@ 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_asset_write_in_library(asset_main, brush, brush->id.name + 2, filepath, @@ -1606,12 +1616,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, ID_BR, *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; } diff --git a/source/blender/editors/space_image/image_ops.cc b/source/blender/editors/space_image/image_ops.cc index 5f278f15fd0..7441cd24ed2 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.hh" @@ -2532,7 +2533,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; @@ -2540,9 +2541,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); @@ -2566,7 +2572,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, @@ -2584,8 +2590,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 */ @@ -2596,7 +2600,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 @@ -2604,7 +2608,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); diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 44e45b2f26e..68ae6fc6f08 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/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; /** diff --git a/source/blender/makesrna/intern/makesrna.cc b/source/blender/makesrna/intern/makesrna.cc index c9360b5689f..41f9f85f509 100644 --- a/source/blender/makesrna/intern/makesrna.cc +++ b/source/blender/makesrna/intern/makesrna.cc @@ -1270,6 +1270,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 343d6995907..e67c900ca7c 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" @@ -285,12 +286,13 @@ int rna_ID_name_length(PointerRNA *ptr) void rna_ID_name_set(PointerRNA *ptr, const char *value) { ID *id = (ID *)ptr->data; - BLI_assert(BKE_id_is_in_global_main(id)); - BLI_assert(!ID_IS_LINKED(id)); - - 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); - 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; @@ -323,7 +325,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))) { if (r_info) { *r_info = N_("Datablocks not in global Main data-base cannot be renamed"); } @@ -1155,9 +1158,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 { @@ -1213,8 +1218,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 ***** */ @@ -2302,6 +2309,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 acb1d52ff92..584657ba133 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.hh" @@ -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 53b223f8fff..26848046c0f 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}, }; @@ -4529,217 +4484,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 d52c6e7d905..7bccf885e3f 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.cc +++ b/source/blender/windowmanager/intern/wm_init_exit.cc @@ -33,8 +33,11 @@ #include "BLO_undofile.hh" #include "BLO_writefile.hh" +#include "BKE_asset.hh" #include "BKE_blender.hh" #include "BKE_blendfile.hh" +#include "BKE_brush.hh" +#include "BKE_callbacks.hh" #include "BKE_context.hh" #include "BKE_global.hh" #include "BKE_icons.h" @@ -576,6 +579,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 a4e5d54a7688d52c154d5bf5bb62eb33989ee336 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 21 Feb 2024 14:02:47 -0500 Subject: [PATCH 130/244] 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 88602a74daa94fc73e44e15130c619bb4fcc940d Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 21 Feb 2024 14:13:44 -0500 Subject: [PATCH 131/244] 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 951eedb2ae01cab88d941bfa4a34f7cb35508311 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 21 Feb 2024 14:14:34 -0500 Subject: [PATCH 132/244] 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 5cc18152c06415776b965482af5d95b1420e5d7a Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 21 Feb 2024 14:25:24 -0500 Subject: [PATCH 133/244] 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 fc40e662a56e3973204e9941eccd53033558328b Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 27 Feb 2024 19:18:16 +0100 Subject: [PATCH 134/244] Refactor: Remove now unnecessary brush library override support --- source/blender/makesrna/intern/rna_brush.cc | 27 +-------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/source/blender/makesrna/intern/rna_brush.cc b/source/blender/makesrna/intern/rna_brush.cc index 9ae82fb2441..96ae8064129 100644 --- a/source/blender/makesrna/intern/rna_brush.cc +++ b/source/blender/makesrna/intern/rna_brush.cc @@ -770,12 +770,7 @@ static void rna_Brush_reset_icon(Brush *br) static void rna_Brush_update(Main * /*bmain*/, Scene * /*scene*/, PointerRNA *ptr) { - Brush *br = (Brush *)ptr->owner_id; - - if (ID_IS_OVERRIDE_LIBRARY_REAL(&br->id)) { - br->id.tag |= LIB_TAG_LIBOVERRIDE_AUTOREFRESH; - } - + Brush *br = (Brush *)ptr->data; WM_main_add_notifier(NC_BRUSH | NA_EDITED, br); // WM_main_add_notifier(NC_SPACE | ND_SPACE_VIEW3D, nullptr); } @@ -2151,18 +2146,14 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) RNA_def_struct_sdna(srna, "BrushCurvesSculptSettings"); RNA_def_struct_ui_text(srna, "Curves Sculpt Brush Settings", ""); - RNA_define_lib_overridable(true); - prop = RNA_def_property(srna, "add_amount", PROP_INT, PROP_NONE); RNA_def_property_range(prop, 1, INT32_MAX); RNA_def_property_ui_text(prop, "Count", "Number of curves added by the Add brush"); - RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "points_per_curve", PROP_INT, PROP_NONE); RNA_def_property_range(prop, 2, INT32_MAX); RNA_def_property_ui_text( prop, "Points per Curve", "Number of control points in a newly added curve"); - RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "use_uniform_scale", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, nullptr, "flag", BRUSH_CURVES_SCULPT_FLAG_SCALE_UNIFORM); @@ -2170,20 +2161,17 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) "Scale Uniform", "Grow or shrink curves by changing their size uniformly instead of " "using trimming or extrapolation"); - RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "minimum_length", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_range(prop, 0.0f, FLT_MAX); RNA_def_property_ui_text( prop, "Minimum Length", "Avoid shrinking curves shorter than this length"); - RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "use_length_interpolate", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna( prop, nullptr, "flag", BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH); RNA_def_property_ui_text( prop, "Interpolate Length", "Use length of the curves in close proximity"); - RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "use_radius_interpolate", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna( @@ -2198,13 +2186,11 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Interpolate Point Count", "Use the number of points from the curves in close proximity"); - RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "use_shape_interpolate", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, nullptr, "flag", BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE); RNA_def_property_ui_text( prop, "Interpolate Shape", "Use shape of the curves in close proximity"); - RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "curve_length", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_range(prop, 0.0, FLT_MAX); @@ -2212,14 +2198,12 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) prop, "Curve Length", "Length of newly added curves when it is not interpolated from other curves"); - RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "minimum_distance", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_range(prop, 0.0f, FLT_MAX); RNA_def_property_ui_range(prop, 0.0, 1000.0f, 0.001, 2); RNA_def_property_ui_text( prop, "Minimum Distance", "Goal distance between curve roots for the Density brush"); - RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "curve_radius", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_range(prop, 0.0, FLT_MAX); @@ -2234,22 +2218,17 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) RNA_def_property_range(prop, 0, INT32_MAX); RNA_def_property_ui_text( prop, "Density Add Attempts", "How many times the Density brush tries to add a new curve"); - RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "density_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, density_mode_items); RNA_def_property_ui_text( prop, "Density Mode", "Determines whether the brush adds or removes curves"); - RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "curve_parameter_falloff", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "CurveMapping"); RNA_def_property_ui_text(prop, "Curve Parameter Falloff", "Falloff that is applied from the tip to the root of each curve"); - RNA_def_property_update(prop, 0, "rna_Brush_update"); - - RNA_define_lib_overridable(false); } static void rna_def_brush(BlenderRNA *brna) @@ -2597,8 +2576,6 @@ static void rna_def_brush(BlenderRNA *brna) srna, "Brush", "Brush data-block for storing brush settings for painting and sculpting"); RNA_def_struct_ui_icon(srna, ICON_BRUSH_DATA); - RNA_define_lib_overridable(true); - /* enums */ prop = RNA_def_property(srna, "blend", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, prop_blend_items); @@ -3942,8 +3919,6 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_struct_type(prop, "BrushCurvesSculptSettings"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Curves Sculpt Settings", ""); - - RNA_define_lib_overridable(false); } /** -- 2.30.2 From a78f52f5d0b449779574def4c432ab6319d17d2c Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 27 Feb 2024 20:35:37 +0100 Subject: [PATCH 135/244] Fix: Tag new datablocks in asset main, wrong tag value --- source/blender/blenkernel/BKE_main.hh | 5 +++++ source/blender/blenkernel/intern/asset_weak_reference.cc | 1 + source/blender/blenkernel/intern/lib_id.cc | 7 +++++++ source/blender/makesdna/DNA_ID.h | 2 +- 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/BKE_main.hh b/source/blender/blenkernel/BKE_main.hh index f004e061235..366c442ee47 100644 --- a/source/blender/blenkernel/BKE_main.hh +++ b/source/blender/blenkernel/BKE_main.hh @@ -191,6 +191,11 @@ struct Main { */ bool is_global_main; + /** + * True if main used to store weakly referenced assets. + */ + bool is_asset_weak_reference_main; + BlendThumbnail *blen_thumb; Library *curlib; diff --git a/source/blender/blenkernel/intern/asset_weak_reference.cc b/source/blender/blenkernel/intern/asset_weak_reference.cc index fe844e13d34..4de494e2f32 100644 --- a/source/blender/blenkernel/intern/asset_weak_reference.cc +++ b/source/blender/blenkernel/intern/asset_weak_reference.cc @@ -144,6 +144,7 @@ struct AssetWeakReferenceMain { AssetWeakReferenceMain(std::string filepath) : filepath(std::move(filepath)), main(BKE_main_new()) { + main->is_asset_weak_reference_main = true; } AssetWeakReferenceMain(const AssetWeakReferenceMain &) = delete; AssetWeakReferenceMain(AssetWeakReferenceMain &&other) diff --git a/source/blender/blenkernel/intern/lib_id.cc b/source/blender/blenkernel/intern/lib_id.cc index 6b906db4af2..a497187d10d 100644 --- a/source/blender/blenkernel/intern/lib_id.cc +++ b/source/blender/blenkernel/intern/lib_id.cc @@ -1269,6 +1269,9 @@ void *BKE_libblock_alloc(Main *bmain, short type, const char *name, const int fl if ((flag & LIB_ID_CREATE_NO_DEG_TAG) == 0) { DEG_id_type_tag(bmain, type); } + if (bmain->is_asset_weak_reference_main) { + id->tag |= LIB_TAG_ASSET_MAIN; + } } else { BLI_strncpy(id->name + 2, name, sizeof(id->name) - 2); @@ -1462,6 +1465,10 @@ void BKE_libblock_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int ori DEG_id_type_tag(bmain, GS(new_id->name)); } + if (bmain && bmain->is_asset_weak_reference_main) { + new_id->tag |= LIB_TAG_ASSET_MAIN; + } + *r_newid = new_id; } diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 68ae6fc6f08..3363b13d0b2 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -1006,7 +1006,7 @@ enum { * 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, + LIB_TAG_ASSET_MAIN = 1 << 27, /* ------------------------------------------------------------------------------------------- */ -- 2.30.2 From f29e118487c30c9ff63132343ce59bbd74f7e76e Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 27 Feb 2024 18:37:49 +0100 Subject: [PATCH 136/244] Refactor: Add BKE_main_from_id for main lookups Instead of specifically naming this function after asset weak references. --- source/blender/blenkernel/BKE_asset.hh | 6 +++++- source/blender/blenkernel/BKE_brush.hh | 1 - source/blender/blenkernel/BKE_lib_override.hh | 2 -- source/blender/blenkernel/BKE_main.hh | 9 +++++++++ source/blender/blenkernel/intern/asset_weak_reference.cc | 8 +++----- source/blender/blenkernel/intern/main.cc | 6 ++++++ source/blender/editors/interface/interface_templates.cc | 8 ++++---- source/blender/editors/render/render_shading.cc | 2 +- source/blender/editors/sculpt_paint/paint_ops.cc | 8 ++++---- source/blender/editors/space_image/image_ops.cc | 2 +- source/blender/makesrna/intern/rna_ID.cc | 6 +++--- source/blender/makesrna/intern/rna_main_api.cc | 2 +- 12 files changed, 37 insertions(+), 23 deletions(-) diff --git a/source/blender/blenkernel/BKE_asset.hh b/source/blender/blenkernel/BKE_asset.hh index 3920d9e0d08..6e663a4e459 100644 --- a/source/blender/blenkernel/BKE_asset.hh +++ b/source/blender/blenkernel/BKE_asset.hh @@ -81,7 +81,11 @@ void BKE_asset_metadata_read(BlendDataReader *reader, AssetMetaData *asset_data) 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); +/** + * Database of assets that are weakly reference by scene data, + * currently used for brush assets and their dependencies. + */ +Main *BKE_asset_weak_reference_main(const ID *id); void BKE_asset_weak_reference_main_free(); ID *BKE_asset_weak_reference_ensure(Main &global_main, ID_Type id_type, diff --git a/source/blender/blenkernel/BKE_brush.hh b/source/blender/blenkernel/BKE_brush.hh index 67244f2b6e6..ec7d758057d 100644 --- a/source/blender/blenkernel/BKE_brush.hh +++ b/source/blender/blenkernel/BKE_brush.hh @@ -14,7 +14,6 @@ #include "DNA_object_enums.h" enum class PaintMode : int8_t; -struct AssetWeakReference; struct Brush; struct ImBuf; struct ImagePool; diff --git a/source/blender/blenkernel/BKE_lib_override.hh b/source/blender/blenkernel/BKE_lib_override.hh index 54243dd2722..39481b808d2 100644 --- a/source/blender/blenkernel/BKE_lib_override.hh +++ b/source/blender/blenkernel/BKE_lib_override.hh @@ -23,8 +23,6 @@ * of IDs in a given Main data-base. */ -#include "DNA_ID_enums.h" - #include struct BlendFileReadReport; diff --git a/source/blender/blenkernel/BKE_main.hh b/source/blender/blenkernel/BKE_main.hh index 366c442ee47..51212547760 100644 --- a/source/blender/blenkernel/BKE_main.hh +++ b/source/blender/blenkernel/BKE_main.hh @@ -512,6 +512,15 @@ ListBase *which_libbase(Main *bmain, short type); */ int set_listbasepointers(Main *main, ListBase *lb[]); +/** + * Return main database this ID is a member of. + * + * Use this in operator and draw code instead of assuming the main + * in the context owns datablocks. Some datablock can be part of + * main datablocks from asset libraries instead. + */ +Main *BKE_main_from_id(Main *global_main, const ID *id); + #define MAIN_VERSION_FILE_ATLEAST(main, ver, subver) \ ((main)->versionfile > (ver) || \ ((main)->versionfile == (ver) && (main)->subversionfile >= (subver))) diff --git a/source/blender/blenkernel/intern/asset_weak_reference.cc b/source/blender/blenkernel/intern/asset_weak_reference.cc index 4de494e2f32..2accc9429d8 100644 --- a/source/blender/blenkernel/intern/asset_weak_reference.cc +++ b/source/blender/blenkernel/intern/asset_weak_reference.cc @@ -129,7 +129,7 @@ void BKE_asset_weak_reference_read(BlendDataReader *reader, AssetWeakReference * BLO_read_data_address(reader, &weak_ref->relative_asset_identifier); } -/* Main database for each brush asset blend file. +/* Main database for storing assets that are weak referenced. * * This avoids mixing asset datablocks in the regular main, which leads to naming conflicts and * confusing user interface. @@ -166,11 +166,9 @@ static Vector &get_weak_reference_mains() return mains; } -Main *BKE_asset_weak_reference_main(Main *global_main, const ID *id) +Main *BKE_asset_weak_reference_main(const ID *id) { - if (!(id->tag & LIB_TAG_ASSET_MAIN)) { - return global_main; - } + BLI_assert(id->tag & LIB_TAG_ASSET_MAIN); for (const AssetWeakReferenceMain &weak_ref_main : get_weak_reference_mains()) { /* TODO: Look into make this whole thing more efficient. */ diff --git a/source/blender/blenkernel/intern/main.cc b/source/blender/blenkernel/intern/main.cc index 13881f03ce9..13d173d7bc0 100644 --- a/source/blender/blenkernel/intern/main.cc +++ b/source/blender/blenkernel/intern/main.cc @@ -24,6 +24,7 @@ #include "DNA_ID.h" +#include "BKE_asset_weak_reference.hh" #include "BKE_bpath.hh" #include "BKE_global.hh" #include "BKE_idtype.hh" @@ -961,3 +962,8 @@ int set_listbasepointers(Main *bmain, ListBase *lb[/*INDEX_ID_MAX*/]) return (INDEX_ID_MAX - 1); } + +Main *BKE_main_from_id(Main *global_main, const ID *id) +{ + return (id->tag & LIB_TAG_ASSET_MAIN) ? BKE_asset_weak_reference_main(id) : global_main; +} diff --git a/source/blender/editors/interface/interface_templates.cc b/source/blender/editors/interface/interface_templates.cc index 4f1c7655a38..7d8453731f2 100644 --- a/source/blender/editors/interface/interface_templates.cc +++ b/source/blender/editors/interface/interface_templates.cc @@ -1028,7 +1028,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) break; case UI_ID_LOCAL: if (id) { - Main *id_main = BKE_asset_weak_reference_main(CTX_data_main(C), id); + Main *id_main = BKE_main_from_id(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); @@ -1050,7 +1050,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 *id_main = BKE_asset_weak_reference_main(CTX_data_main(C), id); + Main *id_main = BKE_main_from_id(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); @@ -1072,7 +1072,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) /* make copy */ Main *bmain = CTX_data_main(C); - Main *id_main = BKE_asset_weak_reference_main(bmain, id); + Main *id_main = BKE_main_from_id(bmain, id); if (do_scene_obj) { Scene *scene = CTX_data_scene(C); @@ -1792,7 +1792,7 @@ static void ui_template_id(uiLayout *layout, Main *id_main = CTX_data_main(C); if (ptr->owner_id) { - id_main = BKE_asset_weak_reference_main(id_main, ptr->owner_id); + id_main = BKE_main_from_id(id_main, ptr->owner_id); } StructRNA *type = RNA_property_pointer_type(ptr, prop); diff --git a/source/blender/editors/render/render_shading.cc b/source/blender/editors/render/render_shading.cc index 7d25e44b1c0..1ff534415c3 100644 --- a/source/blender/editors/render/render_shading.cc +++ b/source/blender/editors/render/render_shading.cc @@ -829,7 +829,7 @@ static int new_texture_exec(bContext *C, wmOperator * /*op*/) Main *id_main = CTX_data_main(C); if (ptr.owner_id) { - id_main = BKE_asset_weak_reference_main(id_main, ptr.owner_id); + id_main = BKE_main_from_id(id_main, ptr.owner_id); } /* add or copy texture */ diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 7a6377fe2a3..9027e36bb9b 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -1112,7 +1112,7 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) library->catalog_service->write_to_disk(filepath); /* Save to asset library. */ - Main *asset_main = BKE_asset_weak_reference_main(bmain, &brush->id); + Main *asset_main = BKE_main_from_id(bmain, &brush->id); std::string final_full_asset_filepath; const bool sucess = brush_asset_write_in_library(asset_main, @@ -1254,7 +1254,7 @@ static int brush_asset_delete_exec(bContext *C, wmOperator *op) 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); + Main *asset_main = BKE_main_from_id(bmain, &brush->id); bUserAssetLibrary *library = BKE_preferences_asset_library_find_by_name( &U, paint->brush_asset_reference->asset_library_identifier); @@ -1339,7 +1339,7 @@ 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); + Main *asset_main = BKE_main_from_id(bmain, &brush->id); std::string final_full_asset_filepath; brush_asset_write_in_library(asset_main, brush, @@ -1380,7 +1380,7 @@ 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); + Main *asset_main = BKE_main_from_id(bmain, &brush->id); // TODO: delete and reload dependencies too? // TODO: hack to make remapping work, should not be needed diff --git a/source/blender/editors/space_image/image_ops.cc b/source/blender/editors/space_image/image_ops.cc index 6cd8f649961..9b631afc03f 100644 --- a/source/blender/editors/space_image/image_ops.cc +++ b/source/blender/editors/space_image/image_ops.cc @@ -2547,7 +2547,7 @@ static int image_new_exec(bContext *C, wmOperator *op) sima = CTX_wm_space_image(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); + id_main = BKE_main_from_id(id_main, data->pprop.ptr.owner_id); } prop = RNA_struct_find_property(op->ptr, "name"); diff --git a/source/blender/makesrna/intern/rna_ID.cc b/source/blender/makesrna/intern/rna_ID.cc index 098f1bc5a33..7406c39ae84 100644 --- a/source/blender/makesrna/intern/rna_ID.cc +++ b/source/blender/makesrna/intern/rna_ID.cc @@ -286,7 +286,7 @@ int rna_ID_name_length(PointerRNA *ptr) void rna_ID_name_set(PointerRNA *ptr, const char *value) { ID *id = (ID *)ptr->data; - Main *id_main = BKE_asset_weak_reference_main(G_MAIN, id); + Main *id_main = BKE_main_from_id(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); /* TODO: add BKE_id_is_in_editable_main? */ @@ -1158,7 +1158,7 @@ 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); + Main *id_main = BKE_main_from_id(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)); @@ -1218,7 +1218,7 @@ 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); + Main *id_main = BKE_main_from_id(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(id_main, lib, value); diff --git a/source/blender/makesrna/intern/rna_main_api.cc b/source/blender/makesrna/intern/rna_main_api.cc index 584657ba133..abafc5acff8 100644 --- a/source/blender/makesrna/intern/rna_main_api.cc +++ b/source/blender/makesrna/intern/rna_main_api.cc @@ -127,7 +127,7 @@ static void rna_Main_ID_remove(Main *bmain, return; } /* TODO: also check reverse case somehow? */ - if (bmain != BKE_asset_weak_reference_main(bmain, id)) { + if (bmain != BKE_main_from_id(bmain, id)) { BKE_reportf(reports, RPT_ERROR, "%s '%s' is part of a difference main database and should be removed from there", -- 2.30.2 From 2ac6f8d455d7114d263bfe48e219bf379286b5fe Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 27 Feb 2024 19:15:35 +0100 Subject: [PATCH 137/244] RNA: Better validation of matching main database Including no main database cases that were not checked previously. Replaces BKE_id_is_in_global_main assets by moving them in BKE_main_from_id which needs to be called anyway. --- source/blender/blenkernel/BKE_lib_id.hh | 6 -- source/blender/blenkernel/BKE_main.hh | 4 +- source/blender/blenkernel/intern/lib_id.cc | 7 --- source/blender/blenkernel/intern/main.cc | 24 +++++++- source/blender/makesrna/intern/makesrna.cc | 30 +++++++--- source/blender/makesrna/intern/rna_ID.cc | 32 +++++------ source/blender/makesrna/intern/rna_access.cc | 9 ++- .../blender/makesrna/intern/rna_armature.cc | 20 +++++-- source/blender/makesrna/intern/rna_image.cc | 27 +++++---- .../blender/makesrna/intern/rna_main_api.cc | 4 +- source/blender/makesrna/intern/rna_object.cc | 56 ++++++++++++------- source/blender/makesrna/intern/rna_pose.cc | 9 ++- source/blender/makesrna/intern/rna_scene.cc | 7 ++- source/blender/makesrna/intern/rna_space.cc | 8 ++- source/blender/python/bmesh/bmesh_py_types.cc | 4 +- source/blender/python/gpu/gpu_py_offscreen.cc | 7 +-- source/blender/python/intern/bpy_rna_anim.cc | 5 +- 17 files changed, 160 insertions(+), 99 deletions(-) diff --git a/source/blender/blenkernel/BKE_lib_id.hh b/source/blender/blenkernel/BKE_lib_id.hh index 78e1172f15c..d09c96eb79f 100644 --- a/source/blender/blenkernel/BKE_lib_id.hh +++ b/source/blender/blenkernel/BKE_lib_id.hh @@ -633,12 +633,6 @@ void BKE_library_make_local( void BKE_id_tag_set_atomic(ID *id, int tag); void BKE_id_tag_clear_atomic(ID *id, int tag); -/** - * Check that given ID pointer actually is in G_MAIN. - * Main intended use is for debug asserts in places we cannot easily get rid of #G_Main. - */ -bool BKE_id_is_in_global_main(ID *id); - bool BKE_id_can_be_asset(const ID *id); /** diff --git a/source/blender/blenkernel/BKE_main.hh b/source/blender/blenkernel/BKE_main.hh index 51212547760..163b65c4258 100644 --- a/source/blender/blenkernel/BKE_main.hh +++ b/source/blender/blenkernel/BKE_main.hh @@ -518,8 +518,10 @@ int set_listbasepointers(Main *main, ListBase *lb[]); * Use this in operator and draw code instead of assuming the main * in the context owns datablocks. Some datablock can be part of * main datablocks from asset libraries instead. + * + * Optionally can verify membership of global_main, but this is expensive. */ -Main *BKE_main_from_id(Main *global_main, const ID *id); +Main *BKE_main_from_id(Main *global_main, const ID *id, bool verify = false); #define MAIN_VERSION_FILE_ATLEAST(main, ver, subver) \ ((main)->versionfile > (ver) || \ diff --git a/source/blender/blenkernel/intern/lib_id.cc b/source/blender/blenkernel/intern/lib_id.cc index a497187d10d..1c75f0efcb8 100644 --- a/source/blender/blenkernel/intern/lib_id.cc +++ b/source/blender/blenkernel/intern/lib_id.cc @@ -2131,13 +2131,6 @@ void BKE_id_tag_clear_atomic(ID *id, int tag) atomic_fetch_and_and_int32(&id->tag, ~tag); } -bool BKE_id_is_in_global_main(ID *id) -{ - /* We do not want to fail when id is nullptr here, even though this is a bit strange behavior... - */ - return (id == nullptr || BLI_findindex(which_libbase(G_MAIN, GS(id->name)), id) != -1); -} - bool BKE_id_can_be_asset(const ID *id) { return !ID_IS_LINKED(id) && !ID_IS_OVERRIDE_LIBRARY(id) && diff --git a/source/blender/blenkernel/intern/main.cc b/source/blender/blenkernel/intern/main.cc index 13d173d7bc0..8fdbf7fb967 100644 --- a/source/blender/blenkernel/intern/main.cc +++ b/source/blender/blenkernel/intern/main.cc @@ -24,7 +24,7 @@ #include "DNA_ID.h" -#include "BKE_asset_weak_reference.hh" +#include "BKE_asset.hh" #include "BKE_bpath.hh" #include "BKE_global.hh" #include "BKE_idtype.hh" @@ -963,7 +963,25 @@ int set_listbasepointers(Main *bmain, ListBase *lb[/*INDEX_ID_MAX*/]) return (INDEX_ID_MAX - 1); } -Main *BKE_main_from_id(Main *global_main, const ID *id) +Main *BKE_main_from_id(Main *global_main, const ID *id, const bool verify) { - return (id->tag & LIB_TAG_ASSET_MAIN) ? BKE_asset_weak_reference_main(id) : global_main; + if (id == nullptr || (id->tag & LIB_TAG_NO_MAIN)) { + return nullptr; + } + if (id->tag & LIB_TAG_ASSET_MAIN) { + return BKE_asset_weak_reference_main(id); + } + + if (verify) { + /* This is rather expensive, so don't do by default and assume valid input. */ + if (BLI_findindex(which_libbase(global_main, GS(id->name)), id) == -1) { + return nullptr; + } + } + else { + /* Debug assert, especially for places that pass in G_MAIN. */ + BLI_assert(BLI_findindex(which_libbase(global_main, GS(id->name)), id) != -1); + } + + return global_main; } diff --git a/source/blender/makesrna/intern/makesrna.cc b/source/blender/makesrna/intern/makesrna.cc index 41f9f85f509..ad05a89c094 100644 --- a/source/blender/makesrna/intern/makesrna.cc +++ b/source/blender/makesrna/intern/makesrna.cc @@ -1263,16 +1263,30 @@ static char *rna_def_property_set_func( else { rna_print_data_get(f, dp); + PointerPropertyRNA *pprop = (PointerPropertyRNA *)dp->prop; + StructRNA *type = (pprop->type) ? rna_find_struct((const char *)pprop->type) : nullptr; + if (prop->flag & PROP_ID_SELF_CHECK) { + /* No pointers to self allowed. */ rna_print_id_get(f, dp); fprintf(f, " if (id == value.data) {\n"); fprintf(f, " return;\n"); fprintf(f, " }\n"); } - /* TODO: check here for wrong linking between regular and asset main? */ + if (type && (type->flag & STRUCT_ID)) { + /* Can only assign pointers between datablocks in the same main database. */ + fprintf(f, " if (value.data) {\n"); + fprintf(f, " Main *owner_main = BKE_main_from_id(G_MAIN, ptr->owner_id);\n"); + fprintf(f, " Main *value_main = BKE_main_from_id(G_MAIN, (ID *)value.data);\n"); + fprintf(f, " if (owner_main && owner_main != value_main) {\n"); + fprintf(f, " return;\n"); + fprintf(f, " }\n"); + fprintf(f, " }\n"); + } if (prop->flag & PROP_ID_REFCOUNT) { + /* Perform reference counting. */ fprintf(f, "\n if (data->%s) {\n", dp->dnaname); fprintf(f, " id_us_min((ID *)data->%s);\n", dp->dnaname); fprintf(f, " }\n"); @@ -1280,14 +1294,11 @@ static char *rna_def_property_set_func( fprintf(f, " id_us_plus((ID *)value.data);\n"); fprintf(f, " }\n"); } - else { - PointerPropertyRNA *pprop = (PointerPropertyRNA *)dp->prop; - StructRNA *type = (pprop->type) ? rna_find_struct((const char *)pprop->type) : nullptr; - if (type && (type->flag & STRUCT_ID)) { - fprintf(f, " if (value.data) {\n"); - fprintf(f, " id_lib_extern((ID *)value.data);\n"); - fprintf(f, " }\n"); - } + else if (type && (type->flag & STRUCT_ID)) { + /* Still mark linked data as used if not reference counting. */ + fprintf(f, " if (value.data) {\n"); + fprintf(f, " id_lib_extern((ID *)value.data);\n"); + fprintf(f, " }\n"); } fprintf(f, " *(void **)&data->%s = value.data;\n", dp->dnaname); @@ -4861,6 +4872,7 @@ static void rna_generate(BlenderRNA *brna, FILE *f, const char *filename, const fprintf(f, "#include \"BLI_utildefines.h\"\n\n"); fprintf(f, "#include \"BKE_context.hh\"\n"); + fprintf(f, "#include \"BKE_global.hh\"\n"); fprintf(f, "#include \"BKE_lib_id.hh\"\n"); fprintf(f, "#include \"BKE_main.hh\"\n"); fprintf(f, "#include \"BKE_report.hh\"\n"); diff --git a/source/blender/makesrna/intern/rna_ID.cc b/source/blender/makesrna/intern/rna_ID.cc index 7406c39ae84..b2079385a34 100644 --- a/source/blender/makesrna/intern/rna_ID.cc +++ b/source/blender/makesrna/intern/rna_ID.cc @@ -289,9 +289,6 @@ void rna_ID_name_set(PointerRNA *ptr, const char *value) Main *id_main = BKE_main_from_id(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); - /* 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) { @@ -300,6 +297,8 @@ void rna_ID_name_set(PointerRNA *ptr, const char *value) DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); } } + + /* TODO: this does not update immediately in the asset shelf. */ } static int rna_ID_name_editable(const PointerRNA *ptr, const char **r_info) @@ -325,8 +324,7 @@ static int rna_ID_name_editable(const PointerRNA *ptr, const char **r_info) return 0; } } - /* TODO: add BKE_id_is_in_editable_main? */ - else if (!(BKE_id_is_in_global_main(id) || (id->tag & LIB_TAG_ASSET_MAIN))) { + else if (BKE_main_from_id(G_MAIN, id, true) == nullptr) { if (r_info) { *r_info = N_("Datablocks not in global Main data-base cannot be renamed"); } @@ -1156,18 +1154,20 @@ int rna_IDMaterials_assign_int(PointerRNA *ptr, int key, const PointerRNA *assig { ID *id = ptr->owner_id; 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_main_from_id(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(id_main, id, mat_id, key + 1); - return 1; - } - else { + Material *mat = (Material *)assign_ptr->owner_id; + if (!(totcol && (key >= 0 && key < *totcol))) { return 0; } + + Main *id_main = BKE_main_from_id(G_MAIN, id); + if (mat) { + if (id_main != BKE_main_from_id(G_MAIN, &mat->id)) { + return 0; + } + } + + BKE_id_material_assign(id_main, id, mat, key + 1); + return 1; } static void rna_IDMaterials_append_id(ID *id, Main *bmain, Material *ma) @@ -1219,8 +1219,6 @@ static void rna_Library_filepath_set(PointerRNA *ptr, const char *value) { Library *lib = (Library *)ptr->data; Main *id_main = BKE_main_from_id(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(id_main, lib, value); } diff --git a/source/blender/makesrna/intern/rna_access.cc b/source/blender/makesrna/intern/rna_access.cc index 800ed49f6ac..f73056c1e46 100644 --- a/source/blender/makesrna/intern/rna_access.cc +++ b/source/blender/makesrna/intern/rna_access.cc @@ -2143,7 +2143,8 @@ static bool rna_property_editable_do(const PointerRNA *ptr, return false; } if (ID_IS_OVERRIDE_LIBRARY(id)) { - const bool is_liboverride_system = BKE_lib_override_library_is_system_defined(G_MAIN, id); + Main *main = BKE_main_from_id(G_MAIN, id); + const bool is_liboverride_system = BKE_lib_override_library_is_system_defined(main, id); if (!RNA_property_overridable_get(ptr, prop_orig)) { if (r_info != nullptr && (*r_info)[0] == '\0') { *r_info = N_("Can't edit this property from an override data-block"); @@ -2341,7 +2342,11 @@ bool RNA_property_update_check(PropertyRNA *prop) void RNA_property_update(bContext *C, PointerRNA *ptr, PropertyRNA *prop) { - rna_property_update(C, CTX_data_main(C), CTX_data_scene(C), ptr, prop); + Main *main = CTX_data_main(C); + if (ptr->owner_id) { + main = BKE_main_from_id(main, ptr->owner_id); + } + rna_property_update(C, main, CTX_data_scene(C), ptr, prop); } void RNA_property_update_main(Main *bmain, Scene *scene, PointerRNA *ptr, PropertyRNA *prop) diff --git a/source/blender/makesrna/intern/rna_armature.cc b/source/blender/makesrna/intern/rna_armature.cc index 163a9dd3f2b..f92f89cdc5b 100644 --- a/source/blender/makesrna/intern/rna_armature.cc +++ b/source/blender/makesrna/intern/rna_armature.cc @@ -791,28 +791,36 @@ static void rna_EditBone_name_set(PointerRNA *ptr, const char *value) { bArmature *arm = (bArmature *)ptr->owner_id; EditBone *ebone = (EditBone *)ptr->data; - char oldname[sizeof(ebone->name)], newname[sizeof(ebone->name)]; + Main *main = BKE_main_from_id(G_MAIN, &arm->id); + if (main == nullptr) { + return; + } + + char oldname[sizeof(ebone->name)], newname[sizeof(ebone->name)]; /* need to be on the stack */ STRNCPY_UTF8(newname, value); STRNCPY(oldname, ebone->name); - BLI_assert(BKE_id_is_in_global_main(&arm->id)); - ED_armature_bone_rename(G_MAIN, arm, oldname, newname); + ED_armature_bone_rename(main, arm, oldname, newname); } static void rna_Bone_name_set(PointerRNA *ptr, const char *value) { bArmature *arm = (bArmature *)ptr->owner_id; Bone *bone = (Bone *)ptr->data; - char oldname[sizeof(bone->name)], newname[sizeof(bone->name)]; + Main *main = BKE_main_from_id(G_MAIN, &arm->id); + if (main == nullptr) { + return; + } + + char oldname[sizeof(bone->name)], newname[sizeof(bone->name)]; /* need to be on the stack */ STRNCPY_UTF8(newname, value); STRNCPY(oldname, bone->name); - BLI_assert(BKE_id_is_in_global_main(&arm->id)); - ED_armature_bone_rename(G_MAIN, arm, oldname, newname); + ED_armature_bone_rename(main, arm, oldname, newname); } static void rna_EditBone_connected_check(EditBone *ebone) diff --git a/source/blender/makesrna/intern/rna_image.cc b/source/blender/makesrna/intern/rna_image.cc index f93e234de7a..cacf2e5c11e 100644 --- a/source/blender/makesrna/intern/rna_image.cc +++ b/source/blender/makesrna/intern/rna_image.cc @@ -86,18 +86,23 @@ static void rna_Image_source_set(PointerRNA *ptr, int value) { Image *ima = (Image *)ptr->owner_id; - if (value != ima->source) { - ima->source = value; - BLI_assert(BKE_id_is_in_global_main(&ima->id)); - BKE_image_signal(G_MAIN, ima, nullptr, IMA_SIGNAL_SRC_CHANGE); - if (ima->source == IMA_SRC_TILED) { - BKE_image_signal(G_MAIN, ima, nullptr, IMA_SIGNAL_RELOAD); - } - - DEG_id_tag_update(&ima->id, 0); - DEG_id_tag_update(&ima->id, ID_RECALC_EDITORS | ID_RECALC_SOURCE); - DEG_relations_tag_update(G_MAIN); + if (value == ima->source) { + return; } + ima->source = value; + + Main *main = BKE_main_from_id(G_MAIN, &ima->id); + if (main == nullptr) { + return; + } + BKE_image_signal(main, ima, nullptr, IMA_SIGNAL_SRC_CHANGE); + if (ima->source == IMA_SRC_TILED) { + BKE_image_signal(main, ima, nullptr, IMA_SIGNAL_RELOAD); + } + + DEG_id_tag_update(&ima->id, 0); + DEG_id_tag_update(&ima->id, ID_RECALC_EDITORS | ID_RECALC_SOURCE); + DEG_relations_tag_update(main); } static void rna_Image_reload_update(Main *bmain, Scene * /*scene*/, PointerRNA *ptr) diff --git a/source/blender/makesrna/intern/rna_main_api.cc b/source/blender/makesrna/intern/rna_main_api.cc index abafc5acff8..df3a8568403 100644 --- a/source/blender/makesrna/intern/rna_main_api.cc +++ b/source/blender/makesrna/intern/rna_main_api.cc @@ -27,7 +27,6 @@ # include "BKE_action.h" # include "BKE_armature.hh" -# include "BKE_asset.hh" # include "BKE_brush.hh" # include "BKE_camera.h" # include "BKE_collection.hh" @@ -126,11 +125,10 @@ static void rna_Main_ID_remove(Main *bmain, id->name + 2); return; } - /* TODO: also check reverse case somehow? */ if (bmain != BKE_main_from_id(bmain, id)) { BKE_reportf(reports, RPT_ERROR, - "%s '%s' is part of a difference main database and should be removed from there", + "%s '%s' is part of a different main database and should be removed from there", BKE_idtype_idcode_to_name(GS(id->name)), id->name + 2); return; diff --git a/source/blender/makesrna/intern/rna_object.cc b/source/blender/makesrna/intern/rna_object.cc index 0cef6833e58..b83a3f78b5d 100644 --- a/source/blender/makesrna/intern/rna_object.cc +++ b/source/blender/makesrna/intern/rna_object.cc @@ -556,6 +556,14 @@ static void rna_Object_data_set(PointerRNA *ptr, PointerRNA value, ReportList *r return; } + Main *ob_main = BKE_main_from_id(G_MAIN, &ob->id); + if (id) { + if (ob_main != BKE_main_from_id(G_MAIN, id)) { + BKE_report(reports, RPT_ERROR, "Can't assign object data from different main database"); + return; + } + } + if (ob->type == OB_EMPTY) { if (ob->data) { id_us_min(static_cast(ob->data)); @@ -568,7 +576,7 @@ static void rna_Object_data_set(PointerRNA *ptr, PointerRNA value, ReportList *r } } else if (ob->type == OB_MESH) { - BKE_mesh_assign_object(G_MAIN, ob, reinterpret_cast(id)); + BKE_mesh_assign_object(ob_main, ob, reinterpret_cast(id)); } else { if (ob->data) { @@ -580,13 +588,13 @@ static void rna_Object_data_set(PointerRNA *ptr, PointerRNA value, ReportList *r id_us_plus(id); ob->data = id; - BKE_object_materials_test(G_MAIN, ob, id); + BKE_object_materials_test(ob_main, ob, id); if (GS(id->name) == ID_CU_LEGACY) { BKE_curve_type_test(ob); } else if (ob->type == OB_ARMATURE) { - BKE_pose_rebuild(G_MAIN, ob, static_cast(ob->data), true); + BKE_pose_rebuild(ob_main, ob, static_cast(ob->data), true); } } } @@ -1147,17 +1155,23 @@ static PointerRNA rna_Object_active_material_get(PointerRNA *ptr) return rna_pointer_inherit_refine(ptr, &RNA_Material, ma); } -static void rna_Object_active_material_set(PointerRNA *ptr, - PointerRNA value, - ReportList * /*reports*/) +static void rna_Object_active_material_set(PointerRNA *ptr, PointerRNA value, ReportList *reports) { Object *ob = reinterpret_cast(ptr->owner_id); + Material *mat = reinterpret_cast(value.data); - DEG_id_tag_update(static_cast(value.data), 0); - BLI_assert(BKE_id_is_in_global_main(&ob->id)); - BLI_assert(BKE_id_is_in_global_main(static_cast(value.data))); - BKE_object_material_assign( - G_MAIN, ob, static_cast(value.data), ob->actcol, BKE_MAT_ASSIGN_EXISTING); + Main *ob_main = BKE_main_from_id(G_MAIN, &ob->id); + if (mat) { + Main *mat_main = BKE_main_from_id(G_MAIN, &mat->id); + if (ob_main != mat_main) { + BKE_report(reports, RPT_ERROR, "Can't assign material from other main database"); + return; + } + } + + DEG_id_tag_update(&mat->id, 0); + + BKE_object_material_assign(ob_main, ob, mat, ob->actcol, BKE_MAT_ASSIGN_EXISTING); if (ob->type == OB_GPENCIL_LEGACY) { /* Notifying material property in top-bar. */ @@ -1381,17 +1395,21 @@ static PointerRNA rna_MaterialSlot_material_get(PointerRNA *ptr) return rna_pointer_inherit_refine(ptr, &RNA_Material, ma); } -static void rna_MaterialSlot_material_set(PointerRNA *ptr, - PointerRNA value, - ReportList * /*reports*/) +static void rna_MaterialSlot_material_set(PointerRNA *ptr, PointerRNA value, ReportList *reports) { Object *ob = reinterpret_cast(ptr->owner_id); - int index = rna_MaterialSlot_index(ptr); + Material *mat = reinterpret_cast(value.data); - BLI_assert(BKE_id_is_in_global_main(&ob->id)); - BLI_assert(BKE_id_is_in_global_main(static_cast(value.data))); - BKE_object_material_assign( - G_MAIN, ob, static_cast(value.data), index + 1, BKE_MAT_ASSIGN_EXISTING); + Main *ob_main = BKE_main_from_id(G_MAIN, &ob->id); + if (mat) { + if (ob_main != BKE_main_from_id(G_MAIN, &mat->id)) { + BKE_report(reports, RPT_ERROR, "Can't assign material from other main database"); + return; + } + } + + int index = rna_MaterialSlot_index(ptr); + BKE_object_material_assign(ob_main, ob, mat, index + 1, BKE_MAT_ASSIGN_EXISTING); } static bool rna_MaterialSlot_material_poll(PointerRNA *ptr, PointerRNA value) diff --git a/source/blender/makesrna/intern/rna_pose.cc b/source/blender/makesrna/intern/rna_pose.cc index 8f2db2b5058..618b7d00b9e 100644 --- a/source/blender/makesrna/intern/rna_pose.cc +++ b/source/blender/makesrna/intern/rna_pose.cc @@ -245,6 +245,11 @@ static float rna_PoseChannel_length_get(PointerRNA *ptr) static void rna_PoseChannel_name_set(PointerRNA *ptr, const char *value) { Object *ob = (Object *)ptr->owner_id; + Main *main = BKE_main_from_id(G_MAIN, &ob->id); + if (main == nullptr) { + return; + } + bPoseChannel *pchan = (bPoseChannel *)ptr->data; char oldname[sizeof(pchan->name)], newname[sizeof(pchan->name)]; @@ -252,9 +257,7 @@ static void rna_PoseChannel_name_set(PointerRNA *ptr, const char *value) STRNCPY_UTF8(newname, value); STRNCPY(oldname, pchan->name); - BLI_assert(BKE_id_is_in_global_main(&ob->id)); - BLI_assert(BKE_id_is_in_global_main(static_cast(ob->data))); - ED_armature_bone_rename(G_MAIN, static_cast(ob->data), oldname, newname); + ED_armature_bone_rename(main, static_cast(ob->data), oldname, newname); } /* See rna_Bone_update_renamed() */ diff --git a/source/blender/makesrna/intern/rna_scene.cc b/source/blender/makesrna/intern/rna_scene.cc index 36f182341df..f4925f1cf67 100644 --- a/source/blender/makesrna/intern/rna_scene.cc +++ b/source/blender/makesrna/intern/rna_scene.cc @@ -1843,8 +1843,11 @@ void rna_ViewLayer_name_set(PointerRNA *ptr, const char *value) { Scene *scene = (Scene *)ptr->owner_id; ViewLayer *view_layer = (ViewLayer *)ptr->data; - BLI_assert(BKE_id_is_in_global_main(&scene->id)); - BKE_view_layer_rename(G_MAIN, scene, view_layer, value); + Main *main = BKE_main_from_id(G_MAIN, &scene->id); + if (main == nullptr) { + return; + } + BKE_view_layer_rename(main, scene, view_layer, value); } static void rna_SceneRenderView_name_set(PointerRNA *ptr, const char *value) diff --git a/source/blender/makesrna/intern/rna_space.cc b/source/blender/makesrna/intern/rna_space.cc index ee7bb2541c8..2219aa0a826 100644 --- a/source/blender/makesrna/intern/rna_space.cc +++ b/source/blender/makesrna/intern/rna_space.cc @@ -1720,9 +1720,13 @@ static void rna_SpaceImageEditor_image_set(PointerRNA *ptr, PointerRNA value, ReportList * /*reports*/) { - BLI_assert(BKE_id_is_in_global_main(static_cast(value.data))); SpaceImage *sima = static_cast(ptr->data); - ED_space_image_set(G_MAIN, sima, (Image *)value.data, false); + Image *image = reinterpret_cast(value.data); + Main *main = BKE_main_from_id(G_MAIN, &image->id); + if (main == nullptr) { + return; + } + ED_space_image_set(main, sima, image, false); } static void rna_SpaceImageEditor_mask_set(PointerRNA *ptr, diff --git a/source/blender/python/bmesh/bmesh_py_types.cc b/source/blender/python/bmesh/bmesh_py_types.cc index fd9669efc24..527a9326f9d 100644 --- a/source/blender/python/bmesh/bmesh_py_types.cc +++ b/source/blender/python/bmesh/bmesh_py_types.cc @@ -20,6 +20,7 @@ #include "BKE_customdata.hh" #include "BKE_global.hh" #include "BKE_lib_id.hh" +#include "BKE_main.hh" #include "BKE_mesh.hh" #include "BKE_mesh_runtime.hh" #include "BKE_object.hh" @@ -1226,8 +1227,7 @@ static PyObject *bpy_bmesh_to_mesh(BPy_BMesh *self, PyObject *args) * anything in this case. */ } else { - BLI_assert(BKE_id_is_in_global_main(&mesh->id)); - bmain = G_MAIN; /* XXX UGLY! */ + bmain = BKE_main_from_id(G_MAIN, &mesh->id); /* XXX UGLY! */ params.calc_object_remap = true; } diff --git a/source/blender/python/gpu/gpu_py_offscreen.cc b/source/blender/python/gpu/gpu_py_offscreen.cc index b2614a9a072..908ca04a65d 100644 --- a/source/blender/python/gpu/gpu_py_offscreen.cc +++ b/source/blender/python/gpu/gpu_py_offscreen.cc @@ -18,7 +18,7 @@ #include "BLI_utildefines.h" #include "BKE_global.hh" -#include "BKE_lib_id.hh" /* For #BKE_id_is_in_global_main. */ +#include "BKE_main.hh" #include "BKE_scene.hh" #include "DNA_view3d_types.h" @@ -456,9 +456,8 @@ static PyObject *pygpu_offscreen_draw_view3d(BPyGPUOffScreen *self, PyObject *ar return nullptr; } - BLI_assert(BKE_id_is_in_global_main(&scene->id)); - - depsgraph = BKE_scene_ensure_depsgraph(G_MAIN, scene, view_layer); + Main *main = BKE_main_from_id(G_MAIN, &scene->id); + depsgraph = BKE_scene_ensure_depsgraph(main, scene, view_layer); /* Disable 'bgl' state since it interfere with off-screen drawing, see: #84402. */ const bool is_bgl = GPU_bgl_get(); diff --git a/source/blender/python/intern/bpy_rna_anim.cc b/source/blender/python/intern/bpy_rna_anim.cc index d6ded7162db..8511b591065 100644 --- a/source/blender/python/intern/bpy_rna_anim.cc +++ b/source/blender/python/intern/bpy_rna_anim.cc @@ -31,6 +31,7 @@ #include "BKE_global.hh" #include "BKE_idtype.hh" #include "BKE_lib_id.hh" +#include "BKE_main.hh" #include "BKE_report.hh" #include "RNA_access.hh" @@ -381,9 +382,9 @@ PyObject *pyrna_struct_keyframe_insert(BPy_StructRNA *self, PyObject *args, PyOb } else { ID *id = self->ptr.owner_id; + Main *main = BKE_main_from_id(G_MAIN, id); - BLI_assert(BKE_id_is_in_global_main(id)); - result = (blender::animrig::insert_keyframe(G_MAIN, + result = (blender::animrig::insert_keyframe(main, &reports, id, nullptr, -- 2.30.2 From 32696f6a3036d59f241060be4d3472b42084cb56 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 27 Feb 2024 20:07:01 +0100 Subject: [PATCH 138/244] Refactor: Improve datablock selector main checking --- .../editors/interface/interface_templates.cc | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/source/blender/editors/interface/interface_templates.cc b/source/blender/editors/interface/interface_templates.cc index 7d8453731f2..2f61a2b539e 100644 --- a/source/blender/editors/interface/interface_templates.cc +++ b/source/blender/editors/interface/interface_templates.cc @@ -973,11 +973,10 @@ 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); + Main *id_main = BKE_main_from_id(CTX_data_main(C), id); const int event = POINTER_AS_INT(arg_event); const char *undo_push_label = nullptr; @@ -1028,7 +1027,6 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) break; case UI_ID_LOCAL: if (id) { - Main *id_main = BKE_main_from_id(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); @@ -1050,7 +1048,6 @@ 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 *id_main = BKE_main_from_id(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); @@ -1071,18 +1068,15 @@ 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_main_from_id(bmain, id); - if (do_scene_obj) { Scene *scene = CTX_data_scene(C); ED_object_single_user(id_main, scene, (Object *)id); WM_event_add_notifier(C, NC_WINDOW, nullptr); - DEG_relations_tag_update(bmain); + DEG_relations_tag_update(id_main); } else { id_single_user(C, id, &template_ui->ptr, template_ui->prop); - DEG_relations_tag_update(bmain); + DEG_relations_tag_update(id_main); } undo_push_label = "Make Single User"; } @@ -1371,7 +1365,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) && !(id->tag & LIB_TAG_ASSET_MAIN); + const bool user_alert = (id->us <= 0); const int width = template_search_textbut_width(&idptr, RNA_struct_find_property(&idptr, "name")); -- 2.30.2 From c5b25d954b99cf4c2c4c1abe46ef97554bc31831 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 28 Feb 2024 12:58:47 -0500 Subject: [PATCH 139/244] Fix: Brushes not activated properly in curves sculpt mode This code that set a different tool as active depending on the brush isn't relevant anymore. It's possible that the deletion could go further into Python code, but the separation isn't so clear there. --- .../windowmanager/intern/wm_toolsystem.cc | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/source/blender/windowmanager/intern/wm_toolsystem.cc b/source/blender/windowmanager/intern/wm_toolsystem.cc index 91c585f02db..49df525b4b8 100644 --- a/source/blender/windowmanager/intern/wm_toolsystem.cc +++ b/source/blender/windowmanager/intern/wm_toolsystem.cc @@ -346,25 +346,6 @@ void WM_toolsystem_ref_sync_from_context(Main *bmain, WorkSpace *workspace, bToo } } } - else { - const PaintMode paint_mode = BKE_paintmode_get_from_tool(tref); - Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode); - const EnumPropertyItem *items = BKE_paint_get_tool_enum_from_paintmode(paint_mode); - if (paint && paint->brush && items) { - const ID *brush = (ID *)paint->brush; - const char tool_type = BKE_brush_tool_get((Brush *)brush, paint); - const int i = RNA_enum_from_value(items, tool_type); - /* Possible when loading files from the future. */ - if (i != -1) { - const char *name = items[i].name; - const char *identifier = items[i].identifier; - if (!STREQ(tref_rt->data_block, identifier)) { - STRNCPY(tref_rt->data_block, identifier); - SNPRINTF(tref->idname, "builtin_brush.%s", name); - } - } - } - } } } -- 2.30.2 From 7677c036c0750a3c2382be937fafdc0b0da168f7 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 28 Feb 2024 19:53:42 +0100 Subject: [PATCH 140/244] Fix factory settings not using default catalogs Apparently userdef versioning doesn't run for factory settings. --- source/blender/blenkernel/intern/blendfile.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/source/blender/blenkernel/intern/blendfile.cc b/source/blender/blenkernel/intern/blendfile.cc index 51e00c174d0..3bbf1308b5f 100644 --- a/source/blender/blenkernel/intern/blendfile.cc +++ b/source/blender/blenkernel/intern/blendfile.cc @@ -1256,6 +1256,15 @@ UserDef *BKE_blendfile_userdef_from_defaults() BKE_preferences_extension_repo_add_default(userdef); BKE_preferences_extension_repo_add_default_user(userdef); + { + BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled( + userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh/Sculpt/Cloth"); + BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled( + userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh/Sculpt/General"); + BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled( + userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh/Sculpt/Painting"); + } + return userdef; } -- 2.30.2 From 0c75cdf62697b1f507b034d798a97ebd179b1fd1 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 28 Feb 2024 14:18:20 -0500 Subject: [PATCH 141/244] Fix: Use after free on exit I found two cases where `BKE_blender_free` frees pointers that are owned by the asset main, resulting in use-after-frees: - Image pool for sculpt brush evaluation - Freeing the "moviecache" system which isn't stored per-Main To fix these, free the asset main data-bases after the global main. --- source/blender/blenkernel/intern/blender.cc | 6 ++++++ source/blender/windowmanager/intern/wm_init_exit.cc | 2 -- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/blender.cc b/source/blender/blenkernel/intern/blender.cc index 3517caa8b0e..5982589d583 100644 --- a/source/blender/blenkernel/intern/blender.cc +++ b/source/blender/blenkernel/intern/blender.cc @@ -22,6 +22,7 @@ #include "IMB_moviecache.hh" #include "BKE_addon.h" +#include "BKE_asset.hh" #include "BKE_blender.hh" /* own include */ #include "BKE_blender_user_menu.hh" /* own include */ #include "BKE_blender_version.h" /* own include */ @@ -60,6 +61,11 @@ void BKE_blender_free() BKE_blender_globals_clear(); + /* Free separate data-bases after #BKE_blender_globals_clear in case any data-blocks in the + * global main use pointers to asset main data-blocks when they're freed. That generally + * shouldn't happen but it's better to be safe. */ + BKE_asset_weak_reference_main_free(); + if (G.log.file != nullptr) { fclose(static_cast(G.log.file)); } diff --git a/source/blender/windowmanager/intern/wm_init_exit.cc b/source/blender/windowmanager/intern/wm_init_exit.cc index 7bccf885e3f..545bb0aca61 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.cc +++ b/source/blender/windowmanager/intern/wm_init_exit.cc @@ -33,7 +33,6 @@ #include "BLO_undofile.hh" #include "BLO_writefile.hh" -#include "BKE_asset.hh" #include "BKE_blender.hh" #include "BKE_blendfile.hh" #include "BKE_brush.hh" @@ -579,7 +578,6 @@ 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 038b29caa5f98fce82161ba681c3d3037a3e011c Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 28 Feb 2024 14:19:39 -0500 Subject: [PATCH 142/244] Fix: Support separate asset main in image open operator Put the new image data-block in the asset main if the owner of the pointer property is also in that Main. --- .../blender/editors/space_image/image_ops.cc | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/source/blender/editors/space_image/image_ops.cc b/source/blender/editors/space_image/image_ops.cc index 9b631afc03f..3f7992c6220 100644 --- a/source/blender/editors/space_image/image_ops.cc +++ b/source/blender/editors/space_image/image_ops.cc @@ -1326,7 +1326,6 @@ static Image *image_open_single(Main *bmain, static int image_open_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); ScrArea *area = CTX_wm_area(C); Scene *scene = CTX_data_scene(C); ImageUser *iuser = nullptr; @@ -1341,9 +1340,16 @@ static int image_open_exec(bContext *C, wmOperator *op) image_open_init(C, op); } - ListBase ranges = ED_image_filesel_detect_sequences(bmain, op, use_udim); + ImageOpenData *iod = static_cast(op->customdata); + + Main *id_main = CTX_data_main(C); + if (iod->pprop.ptr.owner_id) { + id_main = BKE_main_from_id(id_main, iod->pprop.ptr.owner_id); + } + + ListBase ranges = ED_image_filesel_detect_sequences(id_main, op, use_udim); LISTBASE_FOREACH (ImageFrameRange *, range, &ranges) { - Image *ima_range = image_open_single(bmain, op, range, use_multiview); + Image *ima_range = image_open_single(id_main, op, range, use_multiview); /* take the first image */ if ((ima == nullptr) && ima_range) { @@ -1360,9 +1366,6 @@ static int image_open_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - /* hook into UI */ - ImageOpenData *iod = static_cast(op->customdata); - if (iod->pprop.prop) { /* when creating new ID blocks, use is already 1, but RNA * pointer use also increases user, so this compensates it */ @@ -1378,7 +1381,7 @@ static int image_open_exec(bContext *C, wmOperator *op) } else if (area && area->spacetype == SPACE_IMAGE) { SpaceImage *sima = static_cast(area->spacedata.first); - ED_space_image_set(bmain, sima, ima, false); + ED_space_image_set(id_main, sima, ima, false); iuser = &sima->iuser; } else { @@ -1418,9 +1421,9 @@ static int image_open_exec(bContext *C, wmOperator *op) } /* XXX BKE_packedfile_unpack_image frees image buffers */ - ED_preview_kill_jobs(CTX_wm_manager(C), bmain); + ED_preview_kill_jobs(CTX_wm_manager(C), id_main); - BKE_image_signal(bmain, ima, iuser, IMA_SIGNAL_RELOAD); + BKE_image_signal(id_main, ima, iuser, IMA_SIGNAL_RELOAD); WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima); MEM_freeN(op->customdata); -- 2.30.2 From e7c03c16440bd407b2a9fd14f07cc4049ef0127e Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 16 Jan 2024 18:49:39 +0100 Subject: [PATCH 143/244] Fix: Restore bundling of testing/ assets that got lost in merge --- source/creator/CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 1f023425552..b8ac7e78dab 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -1714,6 +1714,12 @@ if(EXISTS "${ASSET_BUNDLE_TESTING_DIR}") set(ASSET_BUNDLE_DIR "${ASSET_BUNDLE_TESTING_DIR}") endif() +# TODO temporary change for development only. Remove before merging. +set(ASSET_BUNDLE_TESTING_DIR "${ASSET_BUNDLE_DIR}/../testing/") +if(EXISTS "${ASSET_BUNDLE_TESTING_DIR}") + set(ASSET_BUNDLE_DIR "${ASSET_BUNDLE_TESTING_DIR}") +endif() + if(EXISTS "${ASSET_BUNDLE_DIR}") install( DIRECTORY ${ASSET_BUNDLE_DIR} -- 2.30.2 From ea833527f649b1ea897e770c749752f4701cb504 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 28 Feb 2024 16:13:57 -0500 Subject: [PATCH 144/244] Change toolbars in other "brush" modes Affect all modes except for grease pencil and particle edit --- .../startup/bl_ui/space_toolsystem_toolbar.py | 105 ++++++++++++------ 1 file changed, 74 insertions(+), 31 deletions(-) diff --git a/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 75c72188f91..ed86646c36a 100644 --- a/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -1368,6 +1368,8 @@ class _defs_sculpt: @staticmethod def generate_brush_tool(context): + # Though `data_block` is conceptually unnecessary with a single brush tool, + # it's still used because many areas assume that brush tools have it set #bToolRef. tool = None if context: brush = context.tool_settings.sculpt.brush @@ -1634,13 +1636,23 @@ class _defs_vertex_paint: @staticmethod def generate_from_brushes(context): - return generate_from_enum_ex( - context, - idname_prefix="builtin_brush.", - icon_prefix="brush.paint_vertex.", - type=bpy.types.Brush, - attr="vertex_tool", - ) + # Though `data_block` is conceptually unnecessary with a single brush tool, + # it's still used because many areas assume that brush tools have it set #bToolRef. + tool = None + if context: + brush = context.tool_settings.vertex_paint.brush + if brush: + tool = brush.vertex_tool + return [ + ToolDef.from_dict( + dict( + idname="builtin.brush", + label="Brush", + icon="brush.sculpt.paint", + data_block=tool + ) + ) + ] class _defs_texture_paint: @@ -1655,14 +1667,24 @@ class _defs_texture_paint: @staticmethod def generate_from_brushes(context): - return generate_from_enum_ex( - context, - idname_prefix="builtin_brush.", - icon_prefix="brush.paint_texture.", - type=bpy.types.Brush, - attr="image_tool", - cursor='PAINT_CROSS', - ) + # Though `data_block` is conceptually unnecessary with a single brush tool, + # it's still used because many areas assume that brush tools have it set #bToolRef. + tool = None + if context: + brush = context.tool_settings.image_paint.brush + if brush: + tool = brush.image_tool + return [ + ToolDef.from_dict( + dict( + idname="builtin.brush", + label="Brush", + icon="brush.sculpt.paint", + cursor='PAINT_CROSS', + data_block=tool + ) + ) + ] class _defs_weight_paint: @@ -1682,13 +1704,23 @@ class _defs_weight_paint: @staticmethod def generate_from_brushes(context): - return generate_from_enum_ex( - context, - idname_prefix="builtin_brush.", - icon_prefix="brush.paint_weight.", - type=bpy.types.Brush, - attr="weight_tool", - ) + # Though `data_block` is conceptually unnecessary with a single brush tool, + # it's still used because many areas assume that brush tools have it set #bToolRef. + tool = None + if context: + brush = context.tool_settings.weight_paint.brush + if brush: + tool = brush.weight_tool + return [ + ToolDef.from_dict( + dict( + idname="builtin.brush", + label="Brush", + icon="brush.sculpt.paint", + data_block=tool + ) + ) + ] @ToolDef.from_fn def sample_weight(): @@ -1765,14 +1797,23 @@ class _defs_paint_grease_pencil: # # @staticmethod # def generate_from_brushes(context): - # return generate_from_enum_ex( - # context, - # idname_prefix="builtin_brush.", - # icon_prefix="brush.gpencil_draw.", - # type=bpy.types.Brush, - # attr="gpencil_tool", - # cursor='DOT', - # ) + # # Though `data_block` is conceptually unnecessary with a single brush tool, + # # it's still used because many areas assume that brush tools have it set #bToolRef. + # tool = None + # if context: + # brush = context.tool_settings.gpencil_paint.brush + # if brush: + # tool = brush.gpencil_tool + # return [ + # ToolDef.from_dict( + # dict( + # idname="builtin.brush", + # label="Brush", + # icon="brush.sculpt.paint", + # data_block=tool + # ) + # ) + # ] @ToolDef.from_fn def draw(): @@ -2432,9 +2473,11 @@ class _defs_curves_sculpt: @staticmethod def generate_brush_tool(context): + # Though `data_block` is conceptually unnecessary with a single brush tool, + # it's still used because many areas assume that brush tools have it set #bToolRef. tool = None if context: - brush = context.tool_settings.sculpt.brush + brush = context.tool_settings.curves_sculpt.brush if brush: tool = brush.curves_sculpt_tool return [ -- 2.30.2 From e9588e5175973302003b46ae24efbcf9827d5f00 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 28 Feb 2024 16:14:17 -0500 Subject: [PATCH 145/244] Fix: Correct assert in BKE_main_from_id for embedded IDs --- source/blender/blenkernel/intern/main.cc | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/main.cc b/source/blender/blenkernel/intern/main.cc index 8fdbf7fb967..4345e4892cd 100644 --- a/source/blender/blenkernel/intern/main.cc +++ b/source/blender/blenkernel/intern/main.cc @@ -980,7 +980,15 @@ Main *BKE_main_from_id(Main *global_main, const ID *id, const bool verify) } else { /* Debug assert, especially for places that pass in G_MAIN. */ - BLI_assert(BLI_findindex(which_libbase(global_main, GS(id->name)), id) != -1); + #ifndef NDEBUG + if (id->flag & LIB_EMBEDDED_DATA) { + const ID *id_owner = BKE_id_owner_get(const_cast(id)); + BLI_assert(BLI_findindex(which_libbase(global_main, GS(id_owner->name)), id_owner) != -1); + } + else { + BLI_assert(BLI_findindex(which_libbase(global_main, GS(id->name)), id) != -1); + } + #endif } return global_main; -- 2.30.2 From 554dcdd4299083bfab5631cf7aef098a59803982 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 29 Feb 2024 12:46:10 +0100 Subject: [PATCH 146/244] Take brush assets out of experimental This makes brush assets be fully supported (non-experimental) asset types. --- source/blender/editors/asset/ED_asset_type.hh | 5 +++-- source/blender/editors/asset/intern/asset_type.cc | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/asset/ED_asset_type.hh b/source/blender/editors/asset/ED_asset_type.hh index 6e13ffe7e2f..1f5795bcd91 100644 --- a/source/blender/editors/asset/ED_asset_type.hh +++ b/source/blender/editors/asset/ED_asset_type.hh @@ -16,7 +16,8 @@ namespace blender::ed::asset { bool id_type_is_non_experimental(const ID *id); #define ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_FLAGS \ - (FILTER_ID_MA | FILTER_ID_GR | FILTER_ID_OB | FILTER_ID_AC | FILTER_ID_WO | FILTER_ID_NT) + (FILTER_ID_BR | FILTER_ID_MA | FILTER_ID_GR | FILTER_ID_OB | FILTER_ID_AC | FILTER_ID_WO | \ + FILTER_ID_NT) /** * Check if the asset type for \a id (which doesn't need to be an asset right now) can be an asset, @@ -39,6 +40,6 @@ int64_t types_supported_as_filter_flags(); * Should start with a consonant, so usages can prefix it with "a" (not "an"). */ #define ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING \ - "Material, Collection, Object, Pose Action, Node Group or World" + "Material, Collection, Object, Brush, Pose Action, Node Group or World" } // namespace blender::ed::asset diff --git a/source/blender/editors/asset/intern/asset_type.cc b/source/blender/editors/asset/intern/asset_type.cc index bca873e615b..2fb73c9e646 100644 --- a/source/blender/editors/asset/intern/asset_type.cc +++ b/source/blender/editors/asset/intern/asset_type.cc @@ -20,7 +20,7 @@ bool id_type_is_non_experimental(const ID *id) { /* Remember to update #ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING and * #ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_FLAGS() with this! */ - return ELEM(GS(id->name), ID_MA, ID_GR, ID_OB, ID_AC, ID_WO, ID_NT); + return ELEM(GS(id->name), ID_BR, ID_MA, ID_GR, ID_OB, ID_AC, ID_WO, ID_NT); } bool id_type_is_supported(const ID *id) -- 2.30.2 From 890d3b40454c5efb695de8e0dcf2fc59f3a504d8 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 29 Feb 2024 14:39:33 +0100 Subject: [PATCH 147/244] Fix crash when showing toolbar tooltips Crashed since 5cc18152c0. --- scripts/modules/bl_keymap_utils/keymap_from_toolbar.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/modules/bl_keymap_utils/keymap_from_toolbar.py b/scripts/modules/bl_keymap_utils/keymap_from_toolbar.py index b0a75563892..9fa63e73591 100644 --- a/scripts/modules/bl_keymap_utils/keymap_from_toolbar.py +++ b/scripts/modules/bl_keymap_utils/keymap_from_toolbar.py @@ -357,7 +357,6 @@ def generate(context, space_type, *, use_fallback_keys=True, use_reset=True): if use_hack_properties: keymap.keymap_items.remove(kmi_hack) - keymap.keymap_items.remove(kmi_hack_brush_select) # Keep last so we can try add a key without any modifiers # in the case this toolbar was activated with modifiers. -- 2.30.2 From 9f8fd30f03bb3055f2492f24ae70e5b539cf8a28 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 29 Feb 2024 11:43:25 -0500 Subject: [PATCH 148/244] Fix build errors after merging main --- source/blender/editors/sculpt_paint/paint_ops.cc | 10 +++++----- source/blender/editors/space_file/filelist.cc | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 9027e36bb9b..7cef7a099ca 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -1031,10 +1031,10 @@ static const bUserAssetLibrary *get_asset_library_from_prop(PointerRNA &ptr) static asset_system::AssetCatalog &asset_library_ensure_catalog( asset_system::AssetLibrary &library, const asset_system::AssetCatalogPath &path) { - if (asset_system::AssetCatalog *catalog = library.catalog_service->find_catalog_by_path(path)) { + if (asset_system::AssetCatalog *catalog = library.catalog_service().find_catalog_by_path(path)) { return *catalog; } - return *library.catalog_service->create_catalog(path); + return *library.catalog_service().create_catalog(path); } static asset_system::AssetCatalog &asset_library_ensure_catalogs_in_path( @@ -1047,7 +1047,7 @@ static asset_system::AssetCatalog &asset_library_ensure_catalogs_in_path( asset_library_ensure_catalog(library, parent / component_name); parent = parent / component_name; }); - return *library.catalog_service->find_catalog_by_path(path); + return *library.catalog_service().find_catalog_by_path(path); } static AssetLibraryReference user_library_to_library_ref(const bUserAssetLibrary &user_library) @@ -1109,7 +1109,7 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) const asset_system::CatalogID catalog_id = catalog.catalog_id; const std::string catalog_simple_name = catalog.simple_name; - library->catalog_service->write_to_disk(filepath); + library->catalog_service().write_to_disk(filepath); /* Save to asset library. */ Main *asset_main = BKE_main_from_id(bmain, &brush->id); @@ -1199,7 +1199,7 @@ static void visit_asset_catalog_for_search_fn( return; } - const asset_system::AssetCatalogTree &full_tree = library->catalog_service->catalog_tree(); + const asset_system::AssetCatalogTree &full_tree = library->catalog_service().catalog_tree(); full_tree.foreach_item([&](const asset_system::AssetCatalogTreeItem &item) { visit_fn(StringPropertySearchVisitParams{item.catalog_path().str(), std::nullopt}); }); diff --git a/source/blender/editors/space_file/filelist.cc b/source/blender/editors/space_file/filelist.cc index bba009239f8..379a318c111 100644 --- a/source/blender/editors/space_file/filelist.cc +++ b/source/blender/editors/space_file/filelist.cc @@ -590,9 +590,9 @@ static int compare_asset_catalog(void *user_data, const void *a1, const void *a2 const asset_system::AssetLibrary &asset_library1 = entry1->asset->owner_asset_library(); const asset_system::AssetLibrary &asset_library2 = entry2->asset->owner_asset_library(); - const asset_system::AssetCatalog *catalog1 = asset_library1.catalog_service->find_catalog( + const asset_system::AssetCatalog *catalog1 = asset_library1.catalog_service().find_catalog( entry1->asset->get_metadata().catalog_id); - const asset_system::AssetCatalog *catalog2 = asset_library2.catalog_service->find_catalog( + const asset_system::AssetCatalog *catalog2 = asset_library2.catalog_service().find_catalog( entry2->asset->get_metadata().catalog_id); /* Always keep assets without catalog last. */ -- 2.30.2 From 4ab5e38eca014a076d785f35e09fbd42ff59f213 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 29 Feb 2024 11:54:01 -0500 Subject: [PATCH 149/244] Fix: Use after free when selecting asset in some cases --- source/blender/blenkernel/intern/paint.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 8afb6735391..ba866038d2b 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -683,7 +683,9 @@ static void paint_brush_asset_update(Paint &paint, Brush *brush, const AssetWeakReference &brush_asset_reference) { + BLI_assert(&brush_asset_reference != paint.brush_asset_reference); MEM_delete(paint.brush_asset_reference); + paint.brush_asset_reference = nullptr; if (brush == nullptr || brush != paint.brush || !(brush->id.tag & LIB_TAG_ASSET_MAIN)) { return; -- 2.30.2 From 5204c7dd43a362fedc2015ae41cd0f82b212c235 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 29 Feb 2024 13:55:20 -0500 Subject: [PATCH 150/244] Fix: Refresh asset libraries after updating asset Otherwise a name change doesn't update the asset shelf. Though there are still other issues with renaming- duplicate brushes will be created when renaming a local copy, switching brushes, then switching back. Other local changes will also be lost in that case. --- source/blender/editors/sculpt_paint/paint_ops.cc | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 7cef7a099ca..4b8b7fce047 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -1328,6 +1328,15 @@ static int brush_asset_update_exec(bContext *C, wmOperator *op) Brush *brush = nullptr; const AssetWeakReference *asset_weak_ref = BKE_paint_brush_asset_get(paint, &brush).value_or(nullptr); + if (!asset_weak_ref) { + return OPERATOR_CANCELLED; + } + + const bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_by_name( + &U, asset_weak_ref->asset_library_identifier); + if (!user_library) { + return OPERATOR_CANCELLED; + } // TODO: maybe can check directly in poll BLI_assert((brush->id.tag & LIB_TAG_ASSET_MAIN) != 0); @@ -1350,6 +1359,10 @@ static int brush_asset_update_exec(bContext *C, wmOperator *op) final_full_asset_filepath, op->reports); + refresh_asset_library(C, *user_library); + WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_EDITED, nullptr); + WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush); + return OPERATOR_FINISHED; } -- 2.30.2 From 605110fbf07f808c338aea6dc002b2a9440d5374 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 29 Feb 2024 14:43:08 -0500 Subject: [PATCH 151/244] UI: Show plus icon to hint that save as operator can create new catalogs --- source/blender/editors/interface/interface_utils.cc | 2 +- source/blender/editors/sculpt_paint/paint_ops.cc | 12 +++++++++++- source/blender/makesrna/RNA_types.hh | 1 + 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/interface/interface_utils.cc b/source/blender/editors/interface/interface_utils.cc index 60a3275f1c5..8e59c534785 100644 --- a/source/blender/editors/interface/interface_utils.cc +++ b/source/blender/editors/interface/interface_utils.cc @@ -505,7 +505,7 @@ void ui_rna_collection_search_update_fn( } cis->index = items_list.size(); - cis->iconid = ICON_NONE; + cis->iconid = visit_params.icon_id.value_or(ICON_NONE); cis->is_id = false; cis->name_prefix_offset = 0; cis->has_sep_char = visit_params.info.has_value(); diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 4b8b7fce047..7e314d23dee 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -59,10 +59,13 @@ #include "BLT_translation.hh" +#include "AS_asset_catalog_path.hh" #include "AS_asset_catalog_tree.hh" #include "AS_asset_library.hh" #include "AS_asset_representation.hh" +#include "UI_resources.hh" + #include "curves_sculpt_intern.hh" #include "paint_intern.hh" #include "sculpt_intern.hh" @@ -1185,7 +1188,7 @@ static void visit_asset_catalog_for_search_fn( const bContext *C, PointerRNA *ptr, PropertyRNA * /*prop*/, - const char * /*edit_text*/, + const char *edit_text, FunctionRef visit_fn) { const bUserAssetLibrary *user_library = get_asset_library_from_prop(*ptr); @@ -1199,6 +1202,13 @@ static void visit_asset_catalog_for_search_fn( return; } + if (edit_text && edit_text[0] != '\0') { + asset_system::AssetCatalogPath edit_path = edit_text; + if (!library->catalog_service().find_catalog_by_path(edit_path)) { + visit_fn(StringPropertySearchVisitParams{edit_path.str(), std::nullopt, ICON_ADD}); + } + } + const asset_system::AssetCatalogTree &full_tree = library->catalog_service().catalog_tree(); full_tree.foreach_item([&](const asset_system::AssetCatalogTreeItem &item) { visit_fn(StringPropertySearchVisitParams{item.catalog_path().str(), std::nullopt}); diff --git a/source/blender/makesrna/RNA_types.hh b/source/blender/makesrna/RNA_types.hh index 6d2945778cd..86458b4e81d 100644 --- a/source/blender/makesrna/RNA_types.hh +++ b/source/blender/makesrna/RNA_types.hh @@ -546,6 +546,7 @@ struct StringPropertySearchVisitParams { std::string text; /** Additional information to display. */ std::optional info; + std::optional icon_id; }; enum eStringPropertySearchFlag { -- 2.30.2 From 151e5f4fe2496c58168c568ad3c3708a5b62c22b Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 29 Feb 2024 15:10:27 -0500 Subject: [PATCH 152/244] UI: Use "label tooltip" as a fallback for quick favorites menu text Useful when the button has no display text but has been set up to have a quickly accessible tooltip in its place. --- source/blender/editors/interface/interface_context_menu.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/editors/interface/interface_context_menu.cc b/source/blender/editors/interface/interface_context_menu.cc index 43c1c6e1dbb..e900155a991 100644 --- a/source/blender/editors/interface/interface_context_menu.cc +++ b/source/blender/editors/interface/interface_context_menu.cc @@ -416,6 +416,10 @@ static void ui_but_user_menu_add(bContext *C, uiBut *but, bUserMenu *um) STRNCPY(drawstr, idname); #endif } + else if (but->tip_label_func) { + /* The "quick tooltip" often contains a short string that can be used as a fallback. */ + drawstr = but->tip_label_func(but); + } } ED_screen_user_menu_item_add_operator( &um->items, -- 2.30.2 From 068c2e0ddaeec217353ee8753e8112f76f052f44 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 6 Mar 2024 18:12:54 +0100 Subject: [PATCH 153/244] Fix: Duplicated code to install assets --- source/creator/CMakeLists.txt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index b8ac7e78dab..1f023425552 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -1714,12 +1714,6 @@ if(EXISTS "${ASSET_BUNDLE_TESTING_DIR}") set(ASSET_BUNDLE_DIR "${ASSET_BUNDLE_TESTING_DIR}") endif() -# TODO temporary change for development only. Remove before merging. -set(ASSET_BUNDLE_TESTING_DIR "${ASSET_BUNDLE_DIR}/../testing/") -if(EXISTS "${ASSET_BUNDLE_TESTING_DIR}") - set(ASSET_BUNDLE_DIR "${ASSET_BUNDLE_TESTING_DIR}") -endif() - if(EXISTS "${ASSET_BUNDLE_DIR}") install( DIRECTORY ${ASSET_BUNDLE_DIR} -- 2.30.2 From 43f3142156126f04f999178db1f6d01834c3c692 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 7 Mar 2024 17:18:30 +0100 Subject: [PATCH 154/244] Fix build error after previous merge --- source/blender/editors/interface/views/grid_view.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/blender/editors/interface/views/grid_view.cc b/source/blender/editors/interface/views/grid_view.cc index a7b650817e0..6601705d925 100644 --- a/source/blender/editors/interface/views/grid_view.cc +++ b/source/blender/editors/interface/views/grid_view.cc @@ -440,8 +440,6 @@ void PreviewGridItem::build_grid_tile_button(uiLayout &layout, nullptr, 0, 0, - 0, - 0, ""); } -- 2.30.2 From b96d3d1f2e6e9656d15d34f4b51538c939d30136 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 13 Mar 2024 16:29:26 +0100 Subject: [PATCH 155/244] Update subversion after merge --- source/blender/blenkernel/BKE_blender_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index dd074b2c94d..63f37f185e5 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -29,7 +29,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 9 +#define BLENDER_FILE_SUBVERSION 10 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and cancel loading the file, showing a warning to -- 2.30.2 From 521583f8e55291fd5c22f9e65628ef458006c90e Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 13 Mar 2024 17:47:25 +0100 Subject: [PATCH 156/244] Cleanup: Apply clang format --- source/blender/blenkernel/intern/main.cc | 8 +++---- .../editors/asset/intern/asset_shelf.cc | 21 +++++++++---------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/source/blender/blenkernel/intern/main.cc b/source/blender/blenkernel/intern/main.cc index 19b217d2e85..38d660b4ff7 100644 --- a/source/blender/blenkernel/intern/main.cc +++ b/source/blender/blenkernel/intern/main.cc @@ -982,16 +982,16 @@ Main *BKE_main_from_id(Main *global_main, const ID *id, const bool verify) } } else { - /* Debug assert, especially for places that pass in G_MAIN. */ - #ifndef NDEBUG +/* Debug assert, especially for places that pass in G_MAIN. */ +#ifndef NDEBUG if (id->flag & LIB_EMBEDDED_DATA) { - const ID *id_owner = BKE_id_owner_get(const_cast(id)); + const ID *id_owner = BKE_id_owner_get(const_cast(id)); BLI_assert(BLI_findindex(which_libbase(global_main, GS(id_owner->name)), id_owner) != -1); } else { BLI_assert(BLI_findindex(which_libbase(global_main, GS(id->name)), id) != -1); } - #endif +#endif } return global_main; diff --git a/source/blender/editors/asset/intern/asset_shelf.cc b/source/blender/editors/asset/intern/asset_shelf.cc index 3c990ff3a3d..e73db502df7 100644 --- a/source/blender/editors/asset/intern/asset_shelf.cc +++ b/source/blender/editors/asset/intern/asset_shelf.cc @@ -713,18 +713,17 @@ static void add_catalog_tabs(AssetShelf &shelf, uiLayout &layout) uiItemS(&layout); /* Regular catalog tabs. */ - settings_foreach_enabled_catalog_path( - shelf, [&](const asset_system::AssetCatalogPath &path) { - uiBut *but = add_tab_button(*block, path.name()); + settings_foreach_enabled_catalog_path(shelf, [&](const asset_system::AssetCatalogPath &path) { + uiBut *but = add_tab_button(*block, path.name()); - UI_but_func_set(but, [&shelf_settings, path](bContext &C) { - settings_set_active_catalog(shelf_settings, path); - send_redraw_notifier(C); - }); - UI_but_func_pushed_state_set(but, [&shelf_settings, path](const uiBut &) -> bool { - return settings_is_active_catalog(shelf_settings, path); - }); - }); + UI_but_func_set(but, [&shelf_settings, path](bContext &C) { + settings_set_active_catalog(shelf_settings, path); + send_redraw_notifier(C); + }); + UI_but_func_pushed_state_set(but, [&shelf_settings, path](const uiBut &) -> bool { + return settings_is_active_catalog(shelf_settings, path); + }); + }); } /** \} */ -- 2.30.2 From 3bb99d0a25694f0736ba16a85375c78e7b2f4591 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 20 Mar 2024 18:37:25 +0100 Subject: [PATCH 157/244] Bump subversion after previous merge --- source/blender/blenkernel/BKE_blender_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 63f37f185e5..2100ed68660 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -29,7 +29,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 10 +#define BLENDER_FILE_SUBVERSION 12 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and cancel loading the file, showing a warning to -- 2.30.2 From 05ab2baacc2a577007e6240da30d363a842de0c5 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 21 Mar 2024 21:02:33 +0100 Subject: [PATCH 158/244] Refactor: Access paint brush through accessor function --- source/blender/blenkernel/intern/brush.cc | 8 ++--- .../blenkernel/intern/gpencil_legacy.cc | 4 +-- source/blender/blenkernel/intern/paint.cc | 4 +-- source/blender/draw/intern/draw_view_c.cc | 3 +- .../editors/gpencil_legacy/gpencil_data.cc | 15 ++++----- .../editors/gpencil_legacy/gpencil_edit.cc | 9 ++--- .../editors/gpencil_legacy/gpencil_merge.cc | 6 ++-- .../editors/gpencil_legacy/gpencil_paint.cc | 33 ++++++++++--------- .../gpencil_legacy/gpencil_primitive.cc | 3 +- .../gpencil_legacy/gpencil_sculpt_paint.cc | 3 +- .../editors/gpencil_legacy/gpencil_utils.cc | 15 +++++---- .../gpencil_legacy/gpencil_vertex_ops.cc | 2 +- .../gpencil_legacy/gpencil_vertex_paint.cc | 3 +- .../gpencil_legacy/gpencil_weight_paint.cc | 11 ++++--- .../intern/grease_pencil_frames.cc | 3 +- .../editors/sculpt_paint/paint_curve.cc | 19 ++++++----- .../editors/sculpt_paint/paint_curve_undo.cc | 6 ++-- .../blender/editors/sculpt_paint/paint_ops.cc | 4 +-- .../editors/sculpt_paint/paint_vertex.cc | 19 ++++++----- .../editors/sculpt_paint/paint_weight.cc | 10 +++--- .../blender/editors/sculpt_paint/sculpt_uv.cc | 5 +-- .../blender/editors/space_image/image_edit.cc | 3 +- .../transform/transform_convert_paintcurve.cc | 5 ++- .../editors/transform/transform_generics.cc | 8 +++-- source/blender/makesrna/intern/rna_brush.cc | 4 +-- 25 files changed, 115 insertions(+), 90 deletions(-) diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index cf1df39dc9a..cc0508c11da 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -1392,7 +1392,7 @@ void BKE_brush_gpencil_paint_presets(Main *bmain, ToolSettings *ts, const bool r bool is_new = false; Paint *paint = &ts->gp_paint->paint; - Brush *brush_prev = paint->brush; + Brush *brush_prev = BKE_paint_brush(paint); Brush *brush, *deft_draw; /* Airbrush brush. */ brush = gpencil_brush_ensure(bmain, ts, "Airbrush", OB_MODE_PAINT_GPENCIL_LEGACY, &is_new); @@ -1493,7 +1493,7 @@ void BKE_brush_gpencil_vertex_presets(Main *bmain, ToolSettings *ts, const bool bool is_new = false; Paint *vertexpaint = &ts->gp_vertexpaint->paint; - Brush *brush_prev = vertexpaint->brush; + Brush *brush_prev = BKE_paint_brush(vertexpaint); Brush *brush, *deft_vertex; /* Vertex Draw brush. */ brush = gpencil_brush_ensure(bmain, ts, "Vertex Draw", OB_MODE_VERTEX_GPENCIL_LEGACY, &is_new); @@ -1541,7 +1541,7 @@ void BKE_brush_gpencil_sculpt_presets(Main *bmain, ToolSettings *ts, const bool bool is_new = false; Paint *sculptpaint = &ts->gp_sculptpaint->paint; - Brush *brush_prev = sculptpaint->brush; + Brush *brush_prev = BKE_paint_brush(sculptpaint); Brush *brush, *deft_sculpt; /* Smooth brush. */ @@ -1618,7 +1618,7 @@ void BKE_brush_gpencil_weight_presets(Main *bmain, ToolSettings *ts, const bool bool is_new = false; Paint *weightpaint = &ts->gp_weightpaint->paint; - Brush *brush_prev = weightpaint->brush; + Brush *brush_prev = BKE_paint_brush(weightpaint); Brush *brush, *deft_weight; /* Weight Draw brush. */ diff --git a/source/blender/blenkernel/intern/gpencil_legacy.cc b/source/blender/blenkernel/intern/gpencil_legacy.cc index 3628aa1c7ac..9912608de34 100644 --- a/source/blender/blenkernel/intern/gpencil_legacy.cc +++ b/source/blender/blenkernel/intern/gpencil_legacy.cc @@ -1749,9 +1749,9 @@ Material *BKE_gpencil_object_material_ensure_from_active_input_toolsettings(Main Object *ob, ToolSettings *ts) { - if (ts && ts->gp_paint && ts->gp_paint->paint.brush) { + if (ts && ts->gp_paint && BKE_paint_brush(&ts->gp_paint->paint)) { return BKE_gpencil_object_material_ensure_from_active_input_brush( - bmain, ob, ts->gp_paint->paint.brush); + bmain, ob, BKE_paint_brush(&ts->gp_paint->paint)); } return BKE_gpencil_object_material_ensure_from_active_input_brush(bmain, ob, nullptr); diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 31dfb800df0..a3f4b9a0fe0 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -262,7 +262,7 @@ void BKE_paint_invalidate_overlay_tex(Scene *scene, ViewLayer *view_layer, const return; } - Brush *br = p->brush; + Brush *br = BKE_paint_brush(p); if (!br) { return; } @@ -282,7 +282,7 @@ void BKE_paint_invalidate_cursor_overlay(Scene *scene, ViewLayer *view_layer, Cu return; } - Brush *br = p->brush; + Brush *br = BKE_paint_brush(p); if (br && br->curve == curve) { overlay_flags |= PAINT_OVERLAY_INVALID_CURVE; } diff --git a/source/blender/draw/intern/draw_view_c.cc b/source/blender/draw/intern/draw_view_c.cc index a982282c5d4..4e0882d4fca 100644 --- a/source/blender/draw/intern/draw_view_c.cc +++ b/source/blender/draw/intern/draw_view_c.cc @@ -75,8 +75,9 @@ static bool is_cursor_visible(const DRWContextState *draw_ctx, Scene *scene, Vie /* exception: object in texture paint mode, clone brush, use_clone_layer disabled */ else if (draw_ctx->object_mode & OB_MODE_TEXTURE_PAINT) { const Paint *p = BKE_paint_get_active(scene, view_layer); + const Brush *brush = (p) ? BKE_paint_brush_for_read(p) : nullptr; - if (p && p->brush && p->brush->imagepaint_tool == PAINT_TOOL_CLONE) { + if (brush && brush->imagepaint_tool == PAINT_TOOL_CLONE) { if ((scene->toolsettings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_CLONE) == 0) { return true; } diff --git a/source/blender/editors/gpencil_legacy/gpencil_data.cc b/source/blender/editors/gpencil_legacy/gpencil_data.cc index aee22c46ee2..d5c0781b26c 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_data.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_data.cc @@ -1961,12 +1961,11 @@ static int gpencil_brush_reset_exec(bContext *C, wmOperator * /*op*/) Main *bmain = CTX_data_main(C); ToolSettings *ts = CTX_data_tool_settings(C); const enum eContextObjectMode mode = CTX_data_mode_enum(C); - Brush *brush = nullptr; switch (mode) { case CTX_MODE_PAINT_GPENCIL_LEGACY: { Paint *paint = &ts->gp_paint->paint; - brush = paint->brush; + Brush *brush = BKE_paint_brush(paint); if (brush && brush->gpencil_settings) { BKE_gpencil_brush_preset_set(bmain, brush, brush->gpencil_settings->preset_type); } @@ -1974,7 +1973,7 @@ static int gpencil_brush_reset_exec(bContext *C, wmOperator * /*op*/) } case CTX_MODE_SCULPT_GPENCIL_LEGACY: { Paint *paint = &ts->gp_sculptpaint->paint; - brush = paint->brush; + Brush *brush = BKE_paint_brush(paint); if (brush && brush->gpencil_settings) { BKE_gpencil_brush_preset_set(bmain, brush, brush->gpencil_settings->preset_type); } @@ -1982,7 +1981,7 @@ static int gpencil_brush_reset_exec(bContext *C, wmOperator * /*op*/) } case CTX_MODE_WEIGHT_GPENCIL_LEGACY: { Paint *paint = &ts->gp_weightpaint->paint; - brush = paint->brush; + Brush *brush = BKE_paint_brush(paint); if (brush && brush->gpencil_settings) { BKE_gpencil_brush_preset_set(bmain, brush, brush->gpencil_settings->preset_type); } @@ -1990,7 +1989,7 @@ static int gpencil_brush_reset_exec(bContext *C, wmOperator * /*op*/) } case CTX_MODE_VERTEX_GPENCIL_LEGACY: { Paint *paint = &ts->gp_vertexpaint->paint; - brush = paint->brush; + Brush *brush = BKE_paint_brush(paint); if (brush && brush->gpencil_settings) { BKE_gpencil_brush_preset_set(bmain, brush, brush->gpencil_settings->preset_type); } @@ -2057,7 +2056,7 @@ static void gpencil_brush_delete_mode_brushes(Main *bmain, Paint *paint, const enum eContextObjectMode mode) { - Brush *brush_active = paint->brush; + Brush *brush_active = BKE_paint_brush(paint); Brush *brush_next = nullptr; for (Brush *brush = static_cast(bmain->brushes.first); brush; brush = brush_next) { brush_next = static_cast(brush->id.next); @@ -2151,8 +2150,8 @@ static int gpencil_brush_reset_all_exec(bContext *C, wmOperator * /*op*/) char tool = '0'; if (paint) { - if (paint->brush) { - Brush *brush_active = paint->brush; + Brush *brush_active = BKE_paint_brush(paint); + if (brush_active) { switch (mode) { case CTX_MODE_PAINT_GPENCIL_LEGACY: { tool = brush_active->gpencil_tool; diff --git a/source/blender/editors/gpencil_legacy/gpencil_edit.cc b/source/blender/editors/gpencil_legacy/gpencil_edit.cc index 5e53b50efb2..6aca1bb806e 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_edit.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_edit.cc @@ -395,8 +395,9 @@ static int gpencil_paintmode_toggle_exec(bContext *C, wmOperator *op) BKE_gpencil_palette_ensure(bmain, CTX_data_scene(C)); Paint *paint = &ts->gp_paint->paint; + Brush *brush = BKE_paint_brush(paint); /* if not exist, create a new one */ - if ((paint->brush == nullptr) || (paint->brush->gpencil_settings == nullptr)) { + if ((brush == nullptr) || (brush->gpencil_settings == nullptr)) { BKE_brush_gpencil_paint_presets(bmain, ts, true); } BKE_paint_toolslots_brush_validate(bmain, &ts->gp_paint->paint); @@ -503,7 +504,7 @@ static int gpencil_sculptmode_toggle_exec(bContext *C, wmOperator *op) /* Be sure we have brushes. */ BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_sculptpaint); - const bool reset_mode = (ts->gp_sculptpaint->paint.brush == nullptr); + const bool reset_mode = (BKE_paint_brush(&ts->gp_sculptpaint->paint) == nullptr); BKE_brush_gpencil_sculpt_presets(bmain, ts, reset_mode); BKE_paint_toolslots_brush_validate(bmain, &ts->gp_sculptpaint->paint); @@ -617,7 +618,7 @@ static int gpencil_weightmode_toggle_exec(bContext *C, wmOperator *op) /* Be sure we have brushes. */ BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_weightpaint); - const bool reset_mode = (ts->gp_weightpaint->paint.brush == nullptr); + const bool reset_mode = (BKE_paint_brush(&ts->gp_weightpaint->paint) == nullptr); BKE_brush_gpencil_weight_presets(bmain, ts, reset_mode); BKE_paint_toolslots_brush_validate(bmain, &ts->gp_weightpaint->paint); @@ -725,7 +726,7 @@ static int gpencil_vertexmode_toggle_exec(bContext *C, wmOperator *op) BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_paint); BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_vertexpaint); - const bool reset_mode = (ts->gp_vertexpaint->paint.brush == nullptr); + const bool reset_mode = (BKE_paint_brush(&ts->gp_vertexpaint->paint) == nullptr); BKE_brush_gpencil_vertex_presets(bmain, ts, reset_mode); BKE_paint_toolslots_brush_validate(bmain, &ts->gp_vertexpaint->paint); diff --git a/source/blender/editors/gpencil_legacy/gpencil_merge.cc b/source/blender/editors/gpencil_legacy/gpencil_merge.cc index be5403af4aa..238bdf49395 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_merge.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_merge.cc @@ -22,6 +22,7 @@ #include "BKE_gpencil_geom_legacy.h" #include "BKE_gpencil_legacy.h" #include "BKE_material.h" +#include "BKE_paint.hh" #include "BKE_report.hh" #include "WM_api.hh" @@ -97,12 +98,13 @@ static bGPDstroke *gpencil_prepare_stroke(bContext *C, wmOperator *op, int totpo const bool cyclic = RNA_boolean_get(op->ptr, "cyclic"); Paint *paint = &ts->gp_paint->paint; + Brush *brush = BKE_paint_brush(paint); /* if not exist, create a new one */ - if ((paint->brush == nullptr) || (paint->brush->gpencil_settings == nullptr)) { + if ((brush == nullptr) || (brush->gpencil_settings == nullptr)) { /* create new brushes */ BKE_brush_gpencil_paint_presets(bmain, ts, false); } - Brush *brush = paint->brush; + brush = BKE_paint_brush(paint); /* frame */ short add_frame_mode; diff --git a/source/blender/editors/gpencil_legacy/gpencil_paint.cc b/source/blender/editors/gpencil_legacy/gpencil_paint.cc index 65470565146..7ac52ddec39 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_paint.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_paint.cc @@ -302,7 +302,7 @@ static bool gpencil_draw_poll(bContext *C) } ToolSettings *ts = CTX_data_scene(C)->toolsettings; - if (!ts->gp_paint->paint.brush) { + if (!BKE_paint_brush(&ts->gp_paint->paint)) { CTX_wm_operator_poll_msg_set(C, "Grease Pencil has no active paint tool"); return false; } @@ -1919,7 +1919,7 @@ static Brush *gpencil_get_default_eraser(Main *bmain, ToolSettings *ts) { Brush *brush_dft = nullptr; Paint *paint = &ts->gp_paint->paint; - Brush *brush_prev = paint->brush; + Brush *brush_prev = BKE_paint_brush(paint); for (Brush *brush = static_cast(bmain->brushes.first); brush; brush = static_cast(brush->id.next)) { @@ -1989,30 +1989,33 @@ static void gpencil_init_drawing_brush(bContext *C, tGPsdata *p) Paint *paint = &ts->gp_paint->paint; bool changed = false; + Brush *brush = BKE_paint_brush(paint); + /* if not exist, create a new one */ - if ((paint->brush == nullptr) || (paint->brush->gpencil_settings == nullptr)) { + if ((brush == nullptr) || (brush->gpencil_settings == nullptr)) { /* create new brushes */ BKE_brush_gpencil_paint_presets(bmain, ts, true); changed = true; + brush = BKE_paint_brush(paint); } /* Be sure curves are initialized. */ - BKE_curvemapping_init(paint->brush->gpencil_settings->curve_sensitivity); - BKE_curvemapping_init(paint->brush->gpencil_settings->curve_strength); - BKE_curvemapping_init(paint->brush->gpencil_settings->curve_jitter); - BKE_curvemapping_init(paint->brush->gpencil_settings->curve_rand_pressure); - BKE_curvemapping_init(paint->brush->gpencil_settings->curve_rand_strength); - BKE_curvemapping_init(paint->brush->gpencil_settings->curve_rand_uv); - BKE_curvemapping_init(paint->brush->gpencil_settings->curve_rand_hue); - BKE_curvemapping_init(paint->brush->gpencil_settings->curve_rand_saturation); - BKE_curvemapping_init(paint->brush->gpencil_settings->curve_rand_value); + BKE_curvemapping_init(brush->gpencil_settings->curve_sensitivity); + BKE_curvemapping_init(brush->gpencil_settings->curve_strength); + BKE_curvemapping_init(brush->gpencil_settings->curve_jitter); + BKE_curvemapping_init(brush->gpencil_settings->curve_rand_pressure); + BKE_curvemapping_init(brush->gpencil_settings->curve_rand_strength); + BKE_curvemapping_init(brush->gpencil_settings->curve_rand_uv); + BKE_curvemapping_init(brush->gpencil_settings->curve_rand_hue); + BKE_curvemapping_init(brush->gpencil_settings->curve_rand_saturation); + BKE_curvemapping_init(brush->gpencil_settings->curve_rand_value); /* Assign to temp #tGPsdata */ - p->brush = paint->brush; - if (paint->brush->gpencil_tool != GPAINT_TOOL_ERASE) { + p->brush = BKE_paint_brush(paint); + if (p->brush->gpencil_tool != GPAINT_TOOL_ERASE) { p->eraser = gpencil_get_default_eraser(p->bmain, ts); } else { - p->eraser = paint->brush; + p->eraser = p->brush; } /* set new eraser as default */ gpencil_set_default_eraser(p->bmain, p->eraser); diff --git a/source/blender/editors/gpencil_legacy/gpencil_primitive.cc b/source/blender/editors/gpencil_legacy/gpencil_primitive.cc index 77bf6e6a4dd..02aa6de2451 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_primitive.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_primitive.cc @@ -1211,7 +1211,8 @@ static void gpencil_primitive_init(bContext *C, wmOperator *op) gpencil_point_conversion_init(C, &tgpi->gsc); /* if brush doesn't exist, create a new set (fix damaged files from old versions) */ - if ((paint->brush == nullptr) || (paint->brush->gpencil_settings == nullptr)) { + Brush *brush = BKE_paint_brush(paint); + if ((brush == nullptr) || (brush->gpencil_settings == nullptr)) { BKE_brush_gpencil_paint_presets(bmain, ts, true); } diff --git a/source/blender/editors/gpencil_legacy/gpencil_sculpt_paint.cc b/source/blender/editors/gpencil_legacy/gpencil_sculpt_paint.cc index 72de7515477..bb564c834c0 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_sculpt_paint.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_sculpt_paint.cc @@ -43,6 +43,7 @@ #include "BKE_gpencil_modifier_legacy.h" #include "BKE_main.hh" #include "BKE_material.h" +#include "BKE_paint.hh" #include "BKE_report.hh" #include "UI_interface.hh" @@ -1202,7 +1203,7 @@ static bool gpencil_sculpt_brush_init(bContext *C, wmOperator *op) gso->region = CTX_wm_region(C); Paint *paint = &ts->gp_sculptpaint->paint; - Brush *brush = paint->brush; + Brush *brush = BKE_paint_brush(paint); gso->brush = brush; BKE_curvemapping_init(gso->brush->curve); diff --git a/source/blender/editors/gpencil_legacy/gpencil_utils.cc b/source/blender/editors/gpencil_legacy/gpencil_utils.cc index fb4ee91c2a4..afb7ffdf312 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_utils.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_utils.cc @@ -328,7 +328,7 @@ bool gpencil_active_brush_poll(bContext *C) ToolSettings *ts = CTX_data_tool_settings(C); Paint *paint = &ts->gp_paint->paint; if (paint) { - return (paint->brush != nullptr); + return (BKE_paint_brush(paint) != nullptr); } return false; } @@ -1431,8 +1431,9 @@ void ED_gpencil_add_defaults(bContext *C, Object *ob) BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_paint); Paint *paint = &ts->gp_paint->paint; + Brush *brush = BKE_paint_brush(paint); /* if not exist, create a new one */ - if ((paint->brush == nullptr) || (paint->brush->gpencil_settings == nullptr)) { + if ((brush == nullptr) || (brush->gpencil_settings == nullptr)) { /* create new brushes */ BKE_brush_gpencil_paint_presets(bmain, ts, true); } @@ -1739,7 +1740,7 @@ float ED_gpencil_cursor_radius(bContext *C, int x, int y) Scene *scene = CTX_data_scene(C); Object *ob = CTX_data_active_object(C); ARegion *region = CTX_wm_region(C); - Brush *brush = scene->toolsettings->gp_paint->paint.brush; + Brush *brush = BKE_paint_brush(&scene->toolsettings->gp_paint->paint); bGPdata *gpd = ED_gpencil_data_get_active(C); /* Show brush size. */ @@ -1830,7 +1831,7 @@ static void gpencil_brush_cursor_draw(bContext *C, int x, int y, void *customdat /* for paint use paint brush size and color */ if (gpd->flag & GP_DATA_STROKE_PAINTMODE) { - brush = scene->toolsettings->gp_paint->paint.brush; + brush = BKE_paint_brush(&scene->toolsettings->gp_paint->paint); if ((brush == nullptr) || (brush->gpencil_settings == nullptr)) { return; } @@ -1902,7 +1903,7 @@ static void gpencil_brush_cursor_draw(bContext *C, int x, int y, void *customdat /* Sculpt use sculpt brush size */ if (GPENCIL_SCULPT_MODE(gpd)) { - brush = scene->toolsettings->gp_sculptpaint->paint.brush; + brush = BKE_paint_brush(&scene->toolsettings->gp_sculptpaint->paint); if ((brush == nullptr) || (brush->gpencil_settings == nullptr)) { return; } @@ -1922,7 +1923,7 @@ static void gpencil_brush_cursor_draw(bContext *C, int x, int y, void *customdat /* Weight Paint */ if (GPENCIL_WEIGHT_MODE(gpd)) { - brush = scene->toolsettings->gp_weightpaint->paint.brush; + brush = BKE_paint_brush(&scene->toolsettings->gp_weightpaint->paint); if ((brush == nullptr) || (brush->gpencil_settings == nullptr)) { return; } @@ -1942,7 +1943,7 @@ static void gpencil_brush_cursor_draw(bContext *C, int x, int y, void *customdat /* For Vertex Paint use brush size. */ if (GPENCIL_VERTEX_MODE(gpd)) { - brush = scene->toolsettings->gp_vertexpaint->paint.brush; + brush = BKE_paint_brush(&scene->toolsettings->gp_vertexpaint->paint); if ((brush == nullptr) || (brush->gpencil_settings == nullptr)) { return; } diff --git a/source/blender/editors/gpencil_legacy/gpencil_vertex_ops.cc b/source/blender/editors/gpencil_legacy/gpencil_vertex_ops.cc index b74b0b33acf..6d039564a05 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_vertex_ops.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_vertex_ops.cc @@ -553,7 +553,7 @@ static int gpencil_vertexpaint_set_exec(bContext *C, wmOperator *op) Object *ob = CTX_data_active_object(C); bGPdata *gpd = (bGPdata *)ob->data; Paint *paint = &ts->gp_vertexpaint->paint; - Brush *brush = paint->brush; + Brush *brush = BKE_paint_brush(paint); const bool is_multiedit = bool(GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)); const eGp_Vertex_Mode mode = eGp_Vertex_Mode(RNA_enum_get(op->ptr, "mode")); diff --git a/source/blender/editors/gpencil_legacy/gpencil_vertex_paint.cc b/source/blender/editors/gpencil_legacy/gpencil_vertex_paint.cc index 69e4433a2ff..96bace404d0 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_vertex_paint.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_vertex_paint.cc @@ -24,6 +24,7 @@ #include "BKE_context.hh" #include "BKE_gpencil_legacy.h" #include "BKE_material.h" +#include "BKE_paint.hh" #include "BKE_report.hh" #include "WM_api.hh" @@ -714,7 +715,7 @@ static bool gpencil_vertexpaint_brush_init(bContext *C, wmOperator *op) MEM_callocN(sizeof(tGP_BrushVertexpaintData), "tGP_BrushVertexpaintData")); op->customdata = gso; - gso->brush = paint->brush; + gso->brush = BKE_paint_brush(paint); srgb_to_linearrgb_v3_v3(gso->linear_color, gso->brush->rgb); BKE_curvemapping_init(gso->brush->curve); diff --git a/source/blender/editors/gpencil_legacy/gpencil_weight_paint.cc b/source/blender/editors/gpencil_legacy/gpencil_weight_paint.cc index 88557c221b0..1325d9066b7 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_weight_paint.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_weight_paint.cc @@ -30,6 +30,7 @@ #include "BKE_gpencil_legacy.h" #include "BKE_modifier.hh" #include "BKE_object_deform.h" +#include "BKE_paint.hh" #include "BKE_report.hh" #include "DNA_meshdata_types.h" @@ -752,7 +753,7 @@ static bool gpencil_weightpaint_brush_init(bContext *C, wmOperator *op) gso->bmain = CTX_data_main(C); - gso->brush = paint->brush; + gso->brush = BKE_paint_brush(paint); BKE_curvemapping_init(gso->brush->curve); gso->is_painting = false; @@ -866,7 +867,8 @@ static bool gpencil_weightpaint_brush_poll(bContext *C) } ToolSettings *ts = CTX_data_scene(C)->toolsettings; - if (!ts->gp_weightpaint->paint.brush) { + Brush *brush = BKE_paint_brush(&ts->gp_weightpaint->paint); + if (brush == nullptr) { CTX_wm_operator_poll_msg_set(C, "Grease Pencil has no active paint tool"); return false; } @@ -1519,9 +1521,10 @@ static int gpencil_weight_toggle_direction_invoke(bContext *C, { ToolSettings *ts = CTX_data_tool_settings(C); Paint *paint = &ts->gp_weightpaint->paint; + Brush *brush = BKE_paint_brush(paint); /* Toggle Add/Subtract flag. */ - paint->brush->flag ^= BRUSH_DIR_IN; + brush->flag ^= BRUSH_DIR_IN; /* Update tool settings. */ WM_main_add_notifier(NC_BRUSH | NA_EDITED, nullptr); @@ -1582,7 +1585,7 @@ static int gpencil_weight_sample_invoke(bContext *C, wmOperator * /*op*/, const /* Get brush radius. */ ToolSettings *ts = CTX_data_tool_settings(C); - Brush *brush = ts->gp_weightpaint->paint.brush; + Brush *brush = BKE_paint_brush(&ts->gp_weightpaint->paint); const int radius = brush->size; /* Init closest points. */ diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_frames.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_frames.cc index 48288d7fc71..502f3cdd40b 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_frames.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_frames.cc @@ -12,6 +12,7 @@ #include "BKE_context.hh" #include "BKE_grease_pencil.hh" +#include "BKE_paint.hh" #include "BKE_report.hh" #include "DEG_depsgraph.hh" @@ -350,7 +351,7 @@ bool ensure_active_keyframe(const Scene &scene, GreasePencil &grease_pencil) (*active_layer.frame_key_at(current_frame) < current_frame); if (blender::animrig::is_autokey_on(&scene) && needs_new_drawing) { - const Brush *brush = scene.toolsettings->gp_paint->paint.brush; + const Brush *brush = BKE_paint_brush_for_read(&scene.toolsettings->gp_paint->paint); if (((scene.toolsettings->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST) != 0) || (brush->gpencil_tool == GPAINT_TOOL_ERASE)) { diff --git a/source/blender/editors/sculpt_paint/paint_curve.cc b/source/blender/editors/sculpt_paint/paint_curve.cc index 107ded2d8f0..35e91d17465 100644 --- a/source/blender/editors/sculpt_paint/paint_curve.cc +++ b/source/blender/editors/sculpt_paint/paint_curve.cc @@ -43,7 +43,6 @@ bool paint_curve_poll(bContext *C) { Object *ob = CTX_data_active_object(C); - Paint *p; RegionView3D *rv3d = CTX_wm_region_view3d(C); SpaceImage *sima; @@ -57,9 +56,10 @@ bool paint_curve_poll(bContext *C) return false; } - p = BKE_paint_get_active_from_context(C); + Paint *p = BKE_paint_get_active_from_context(C); + Brush *brush = (p) ? BKE_paint_brush(p) : nullptr; - if (p && p->brush && (p->brush->flag & BRUSH_CURVE)) { + if (brush && (brush->flag & BRUSH_CURVE)) { return true; } @@ -147,10 +147,11 @@ static char paintcurve_point_side_index(const BezTriple *bezt, static int paintcurve_new_exec(bContext *C, wmOperator * /*op*/) { Paint *p = BKE_paint_get_active_from_context(C); + Brush *brush = (p) ? BKE_paint_brush(p) : nullptr; Main *bmain = CTX_data_main(C); - if (p && p->brush) { - p->brush->paint_curve = BKE_paint_curve_add(bmain, DATA_("PaintCurve")); + if (brush) { + brush->paint_curve = BKE_paint_curve_add(bmain, DATA_("PaintCurve")); } WM_event_add_notifier(C, NC_PAINTCURVE | NA_ADDED, nullptr); @@ -176,7 +177,7 @@ void PAINTCURVE_OT_new(wmOperatorType *ot) static void paintcurve_point_add(bContext *C, wmOperator *op, const int loc[2]) { Paint *p = BKE_paint_get_active_from_context(C); - Brush *br = p->brush; + Brush *br = BKE_paint_brush(p); Main *bmain = CTX_data_main(C); wmWindow *window = CTX_wm_window(C); ARegion *region = CTX_wm_region(C); @@ -287,7 +288,7 @@ void PAINTCURVE_OT_add_point(wmOperatorType *ot) static int paintcurve_delete_point_exec(bContext *C, wmOperator *op) { Paint *p = BKE_paint_get_active_from_context(C); - Brush *br = p->brush; + Brush *br = BKE_paint_brush(p); PaintCurve *pc; PaintCurvePoint *pcp; wmWindow *window = CTX_wm_window(C); @@ -370,7 +371,7 @@ static bool paintcurve_point_select( wmWindow *window = CTX_wm_window(C); ARegion *region = CTX_wm_region(C); Paint *p = BKE_paint_get_active_from_context(C); - Brush *br = p->brush; + Brush *br = BKE_paint_brush(p); PaintCurve *pc; int i; const float loc_fl[2] = {float(loc[0]), float(loc[1])}; @@ -544,7 +545,7 @@ static int paintcurve_slide_invoke(bContext *C, wmOperator *op, const wmEvent *e int i; bool do_select = RNA_boolean_get(op->ptr, "select"); bool align = RNA_boolean_get(op->ptr, "align"); - Brush *br = p->brush; + Brush *br = BKE_paint_brush(p); PaintCurve *pc = br->paint_curve; PaintCurvePoint *pcp; diff --git a/source/blender/editors/sculpt_paint/paint_curve_undo.cc b/source/blender/editors/sculpt_paint/paint_curve_undo.cc index cf4fa3649d3..f1cc8992ad0 100644 --- a/source/blender/editors/sculpt_paint/paint_curve_undo.cc +++ b/source/blender/editors/sculpt_paint/paint_curve_undo.cc @@ -77,7 +77,8 @@ static bool paintcurve_undosys_poll(bContext *C) return false; } Paint *p = BKE_paint_get_active_from_context(C); - return (p->brush && p->brush->paint_curve); + Brush *brush = BKE_paint_brush(p); + return (brush && brush->paint_curve); } static void paintcurve_undosys_step_encode_init(bContext *C, UndoStep *us_p) @@ -95,7 +96,8 @@ static bool paintcurve_undosys_step_encode(bContext *C, Main * /*bmain*/, UndoSt } Paint *p = BKE_paint_get_active_from_context(C); - PaintCurve *pc = p ? (p->brush ? p->brush->paint_curve : nullptr) : nullptr; + Brush *brush = BKE_paint_brush(p); + PaintCurve *pc = p ? (brush ? brush->paint_curve : nullptr) : nullptr; if (pc == nullptr) { return false; } diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 5606cca7d6c..45ec7afa459 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -369,8 +369,8 @@ static int palette_color_add_exec(bContext *C, wmOperator * /*op*/) color = BKE_palette_color_add(palette); palette->active_color = BLI_listbase_count(&palette->colors) - 1; - if (paint->brush) { - const Brush *brush = paint->brush; + const Brush *brush = BKE_paint_brush_for_read(paint); + if (brush) { if (ELEM(mode, PaintMode::Texture3D, PaintMode::Texture2D, diff --git a/source/blender/editors/sculpt_paint/paint_vertex.cc b/source/blender/editors/sculpt_paint/paint_vertex.cc index a24a3e9aa39..410faf41184 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.cc +++ b/source/blender/editors/sculpt_paint/paint_vertex.cc @@ -181,8 +181,8 @@ bool test_brush_angle_falloff(const Brush &brush, bool use_normal(const VPaint *vp) { - return ((vp->paint.brush->flag & BRUSH_FRONTFACE) != 0) || - ((vp->paint.brush->flag & BRUSH_FRONTFACE_FALLOFF) != 0); + const Brush *brush = BKE_paint_brush_for_read(&vp->paint); + return ((brush->flag & BRUSH_FRONTFACE) != 0) || ((brush->flag & BRUSH_FRONTFACE_FALLOFF) != 0); } bool brush_use_accumulate_ex(const Brush *brush, const int ob_mode) @@ -194,7 +194,8 @@ bool brush_use_accumulate_ex(const Brush *brush, const int ob_mode) bool brush_use_accumulate(const VPaint *vp) { - return brush_use_accumulate_ex(vp->paint.brush, vp->paint.runtime.ob_mode); + const Brush *brush = BKE_paint_brush_for_read(&vp->paint); + return brush_use_accumulate_ex(brush, vp->paint.runtime.ob_mode); } void init_stroke(Depsgraph *depsgraph, Object *ob) @@ -479,7 +480,7 @@ void update_cache_invariants( } copy_v2_v2(cache->mouse, cache->initial_mouse); - const Brush *brush = vp->paint.brush; + const Brush *brush = BKE_paint_brush(&vp->paint); /* Truly temporary data that isn't stored in properties */ cache->vc = vc; cache->brush = brush; @@ -686,7 +687,7 @@ static Color vpaint_blend(const VPaint *vp, { using Value = typename Traits::ValueType; - const Brush *brush = vp->paint.brush; + const Brush *brush = BKE_paint_brush_for_read(&vp->paint); const IMB_BlendMode blend = (IMB_BlendMode)brush->blend; const Color color_blend = BLI_mix_colors(blend, color_curr, color_paint, alpha); @@ -963,9 +964,10 @@ static VPaintData *vpaint_init_vpaint(bContext *C, vpd->domain = domain; vpd->vc = ED_view3d_viewcontext_init(C, depsgraph); + vwpaint::view_angle_limits_init(&vpd->normal_angle_precalc, - vp->paint.brush->falloff_angle, - (vp->paint.brush->flag & BRUSH_FRONTFACE_FALLOFF) != 0); + brush->falloff_angle, + (brush->flag & BRUSH_FRONTFACE_FALLOFF) != 0); vpd->paintcol = vpaint_get_current_col( scene, vp, (RNA_enum_get(op->ptr, "mode") == BRUSH_STROKE_INVERT)); @@ -1950,7 +1952,8 @@ static void vpaint_stroke_update_step(bContext *C, BKE_mesh_batch_cache_dirty_tag((Mesh *)ob->data, BKE_MESH_BATCH_DIRTY_ALL); - if (vp->paint.brush->vertexpaint_tool == VPAINT_TOOL_SMEAR) { + Brush *brush = BKE_paint_brush(&vp->paint); + if (brush->vertexpaint_tool == VPAINT_TOOL_SMEAR) { vpd->smear.color_prev = vpd->smear.color_curr; } diff --git a/source/blender/editors/sculpt_paint/paint_weight.cc b/source/blender/editors/sculpt_paint/paint_weight.cc index 71049e4e8f5..9076da67339 100644 --- a/source/blender/editors/sculpt_paint/paint_weight.cc +++ b/source/blender/editors/sculpt_paint/paint_weight.cc @@ -184,7 +184,7 @@ static float wpaint_blend(const VPaint *wp, const float /*brush_alpha_value*/, const bool do_flip) { - const Brush *brush = wp->paint.brush; + const Brush *brush = BKE_paint_brush_for_read(&wp->paint); IMB_BlendMode blend = (IMB_BlendMode)brush->blend; if (do_flip) { @@ -932,9 +932,11 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo wpd = (WPaintData *)MEM_callocN(sizeof(WPaintData), "WPaintData"); paint_stroke_set_mode_data(stroke, wpd); wpd->vc = ED_view3d_viewcontext_init(C, depsgraph); + + const Brush *brush = BKE_paint_brush(&vp->paint); vwpaint::view_angle_limits_init(&wpd->normal_angle_precalc, - vp->paint.brush->falloff_angle, - (vp->paint.brush->flag & BRUSH_FRONTFACE_FALLOFF) != 0); + brush->falloff_angle, + (brush->flag & BRUSH_FRONTFACE_FALLOFF) != 0); wpd->active.index = vgroup_index.active; wpd->mirror.index = vgroup_index.mirror; @@ -1007,7 +1009,7 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo vwpaint::update_cache_invariants(C, vp, ss, op, mouse); vwpaint::init_session_data(ts, ob); - if (ELEM(vp->paint.brush->weightpaint_tool, WPAINT_TOOL_SMEAR, WPAINT_TOOL_BLUR)) { + if (ELEM(brush->weightpaint_tool, WPAINT_TOOL_SMEAR, WPAINT_TOOL_BLUR)) { wpd->precomputed_weight = (float *)MEM_mallocN(sizeof(float) * mesh->verts_num, __func__); } diff --git a/source/blender/editors/sculpt_paint/sculpt_uv.cc b/source/blender/editors/sculpt_paint/sculpt_uv.cc index 3476bb2d4c2..541289a6be1 100644 --- a/source/blender/editors/sculpt_paint/sculpt_uv.cc +++ b/source/blender/editors/sculpt_paint/sculpt_uv.cc @@ -647,10 +647,11 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm UvSculptData *data = MEM_cnew(__func__); BMEditMesh *em = BKE_editmesh_from_object(obedit); BMesh *bm = em->bm; + Brush *brush = BKE_paint_brush(&ts->uvsculpt->paint); op->customdata = data; - BKE_curvemapping_init(ts->uvsculpt->paint.brush->curve); + BKE_curvemapping_init(brush->curve); if (data) { ARegion *region = CTX_wm_region(C); @@ -668,7 +669,7 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm int island_index = 0; data->tool = (RNA_enum_get(op->ptr, "mode") == BRUSH_STROKE_SMOOTH) ? UV_SCULPT_TOOL_RELAX : - eBrushUVSculptTool(ts->uvsculpt->paint.brush->uv_sculpt_tool); + eBrushUVSculptTool(brush->uv_sculpt_tool); data->invert = (RNA_enum_get(op->ptr, "mode") == BRUSH_STROKE_INVERT) ? 1 : 0; data->uvsculpt = &ts->uvsculpt->paint; diff --git a/source/blender/editors/space_image/image_edit.cc b/source/blender/editors/space_image/image_edit.cc index 35b734510c2..a7f1cdabc34 100644 --- a/source/blender/editors/space_image/image_edit.cc +++ b/source/blender/editors/space_image/image_edit.cc @@ -22,6 +22,7 @@ #include "BKE_layer.hh" #include "BKE_lib_id.hh" #include "BKE_main.hh" +#include "BKE_paint.hh" #include "BKE_scene.hh" #include "IMB_imbuf_types.hh" @@ -520,7 +521,7 @@ bool ED_space_image_paint_curve(const bContext *C) SpaceImage *sima = CTX_wm_space_image(C); if (sima && sima->mode == SI_MODE_PAINT) { - Brush *br = CTX_data_tool_settings(C)->imapaint.paint.brush; + Brush *br = BKE_paint_brush(&CTX_data_tool_settings(C)->imapaint.paint); if (br && (br->flag & BRUSH_CURVE)) { return true; diff --git a/source/blender/editors/transform/transform_convert_paintcurve.cc b/source/blender/editors/transform/transform_convert_paintcurve.cc index d35d4d7958c..ea4f4df20ec 100644 --- a/source/blender/editors/transform/transform_convert_paintcurve.cc +++ b/source/blender/editors/transform/transform_convert_paintcurve.cc @@ -112,9 +112,9 @@ static void PaintCurvePointToTransData(PaintCurvePoint *pcp, static void createTransPaintCurveVerts(bContext *C, TransInfo *t) { Paint *paint = BKE_paint_get_active_from_context(C); + Brush *br = (paint) ? BKE_paint_brush(paint) : nullptr; PaintCurve *pc; PaintCurvePoint *pcp; - Brush *br; TransData *td = nullptr; TransData2D *td2d = nullptr; TransDataPaintCurve *tdpc = nullptr; @@ -125,11 +125,10 @@ static void createTransPaintCurveVerts(bContext *C, TransInfo *t) tc->data_len = 0; - if (!paint || !paint->brush || !paint->brush->paint_curve) { + if (!paint || !br) { return; } - br = paint->brush; pc = br->paint_curve; for (pcp = pc->points, i = 0; i < pc->tot_points; i++, pcp++) { diff --git a/source/blender/editors/transform/transform_generics.cc b/source/blender/editors/transform/transform_generics.cc index 6e12ff6f980..ecc56021066 100644 --- a/source/blender/editors/transform/transform_generics.cc +++ b/source/blender/editors/transform/transform_generics.cc @@ -271,7 +271,8 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve if ((object_mode & OB_MODE_ALL_PAINT) || (object_mode & OB_MODE_SCULPT_CURVES)) { Paint *p = BKE_paint_get_active_from_context(C); - if (p && p->brush && (p->brush->flag & BRUSH_CURVE)) { + Brush *brush = (p) ? BKE_paint_brush(p) : nullptr; + if (brush && (brush->flag & BRUSH_CURVE)) { t->options |= CTX_PAINT_CURVE; } } @@ -303,7 +304,8 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve } else if (sima->mode == SI_MODE_PAINT) { Paint *p = &sce->toolsettings->imapaint.paint; - if (p->brush && (p->brush->flag & BRUSH_CURVE)) { + Brush *brush = (p) ? BKE_paint_brush(p) : nullptr; + if (brush && (brush->flag & BRUSH_CURVE)) { t->options |= CTX_PAINT_CURVE; } } @@ -1110,7 +1112,7 @@ bool calculateCenterActive(TransInfo *t, bool select_only, float r_center[3]) } else if (t->options & CTX_PAINT_CURVE) { Paint *p = BKE_paint_get_active(t->scene, t->view_layer); - Brush *br = p->brush; + Brush *br = BKE_paint_brush(p); PaintCurve *pc = br->paint_curve; copy_v3_v3(r_center, pc->points[pc->add_index - 1].bez.vec[1]); r_center[2] = 0.0f; diff --git a/source/blender/makesrna/intern/rna_brush.cc b/source/blender/makesrna/intern/rna_brush.cc index 9e47b08d27b..b721de0f2a6 100644 --- a/source/blender/makesrna/intern/rna_brush.cc +++ b/source/blender/makesrna/intern/rna_brush.cc @@ -1087,7 +1087,7 @@ static void rna_BrushGpencilSettings_default_eraser_update(Main *bmain, { ToolSettings *ts = scene->toolsettings; Paint *paint = &ts->gp_paint->paint; - Brush *brush_cur = paint->brush; + Brush *brush_cur = BKE_paint_brush(paint); /* disable default eraser in all brushes */ for (Brush *brush = static_cast(bmain->brushes.first); brush; @@ -1127,7 +1127,7 @@ static void rna_BrushGpencilSettings_eraser_mode_update(Main * /*bmain*/, { ToolSettings *ts = scene->toolsettings; Paint *paint = &ts->gp_paint->paint; - Brush *brush = paint->brush; + Brush *brush = BKE_paint_brush(paint); /* set eraser icon */ if ((brush) && (brush->gpencil_tool == GPAINT_TOOL_ERASE)) { -- 2.30.2 From c6541d36e6fa5beb52b48429cef8149d35a8aa08 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 21 Mar 2024 17:35:04 +0100 Subject: [PATCH 159/244] Brush: Remove brush.reset operator, replaced by reverting to essentials asset --- scripts/startup/bl_ui/space_view3d_toolbar.py | 4 -- .../blender/editors/sculpt_paint/paint_ops.cc | 37 ------------------- 2 files changed, 41 deletions(-) diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index 85afe397d59..01e0238819b 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -54,10 +54,6 @@ class VIEW3D_MT_brush_context_menu(Menu): layout.operator("brush.asset_update", text="Update Asset") layout.operator("brush.asset_revert", text="Revert to Asset") - - # TODO: does not behave in a useful way now, eventually improve or remove entirely. - # if context.sculpt_object: - # layout.operator("brush.reset", text="Reset to Defaults") else: layout.operator("brush.asset_save_as", text="Save As Asset...", icon='FILE_TICK') layout.operator("brush.asset_delete", text="Delete") diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 45ec7afa459..4ca8c735d39 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -721,42 +721,6 @@ static void PALETTE_OT_join(wmOperatorType *ot) RNA_def_string(ot->srna, "palette", nullptr, MAX_ID_NAME - 2, "Palette", "Name of the Palette"); } -static int brush_reset_exec(bContext *C, wmOperator * /*op*/) -{ - Paint *paint = BKE_paint_get_active_from_context(C); - Brush *brush = BKE_paint_brush(paint); - Object *ob = CTX_data_active_object(C); - - if (!ob || !brush) { - return OPERATOR_CANCELLED; - } - - /* TODO: other modes */ - if (ob->mode & OB_MODE_SCULPT) { - BKE_brush_sculpt_reset(brush); - } - else { - return OPERATOR_CANCELLED; - } - WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush); - - return OPERATOR_FINISHED; -} - -static void BRUSH_OT_reset(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Reset Brush"; - ot->description = "Return brush to defaults based on current tool"; - ot->idname = "BRUSH_OT_reset"; - - /* api callbacks */ - ot->exec = brush_reset_exec; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - namespace blender::ed::sculpt_paint { /**************************** Brush Assets **********************************/ @@ -1891,7 +1855,6 @@ void ED_operatortypes_paint() WM_operatortype_append(BRUSH_OT_scale_size); WM_operatortype_append(BRUSH_OT_curve_preset); WM_operatortype_append(BRUSH_OT_sculpt_curves_falloff_preset); - WM_operatortype_append(BRUSH_OT_reset); WM_operatortype_append(BRUSH_OT_stencil_control); WM_operatortype_append(BRUSH_OT_stencil_fit_image_aspect); WM_operatortype_append(BRUSH_OT_stencil_reset_transform); -- 2.30.2 From e2fce46588d888562dccdff6a52cd81eaed3334f Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 22 Mar 2024 15:19:43 +0100 Subject: [PATCH 160/244] Brush: Add functions to set default and essentials brushes, use where needed --- source/blender/blenkernel/BKE_paint.hh | 18 ++- source/blender/blenkernel/intern/paint.cc | 144 +++++++++++++++--- .../blenloader/intern/versioning_400.cc | 25 +-- .../gpencil_legacy/gpencil_primitive.cc | 2 +- .../blender/editors/sculpt_paint/paint_ops.cc | 5 +- .../editors/sculpt_paint/paint_vertex.cc | 14 +- source/blender/editors/sculpt_paint/sculpt.cc | 9 +- 7 files changed, 158 insertions(+), 59 deletions(-) diff --git a/source/blender/blenkernel/BKE_paint.hh b/source/blender/blenkernel/BKE_paint.hh index 4c0dba30558..ef04cc236ab 100644 --- a/source/blender/blenkernel/BKE_paint.hh +++ b/source/blender/blenkernel/BKE_paint.hh @@ -199,9 +199,17 @@ Paint *BKE_paint_get_active(Scene *sce, ViewLayer *view_layer); Paint *BKE_paint_get_active_from_context(const bContext *C); PaintMode BKE_paintmode_get_active_from_context(const bContext *C); PaintMode BKE_paintmode_get_from_tool(const bToolRef *tref); + +/* Paint brush retrieval and assignment. */ + Brush *BKE_paint_brush(Paint *paint); -const Brush *BKE_paint_brush_for_read(const Paint *p); -void BKE_paint_brush_set(Paint *paint, Brush *br); +const Brush *BKE_paint_brush_for_read(const Paint *paint); + +void BKE_paint_brush_set(Paint *paint, Brush *brush); +bool BKE_paint_brush_set_default(Main *bmain, Paint *paint); +bool BKE_paint_brush_set_essentials(Main *bmain, Paint *paint, const char *name); + +void BKE_paint_brush_set_default_references(ToolSettings *ts); /** * Check if the given brush is a valid Brush Asset. @@ -224,11 +232,7 @@ bool BKE_paint_brush_asset_set(Paint *paint, */ std::optional BKE_paint_brush_asset_get(Paint *paint, Brush **r_brush); -/** - * Attempt to restore a valid active brush in `paint` from brush asset information stored in - * `paint`. - */ -void BKE_paint_brush_asset_restore(Main *bmain, Paint *p); +/* Paint palette. */ Palette *BKE_paint_palette(Paint *paint); void BKE_paint_palette_set(Paint *p, Palette *palette); diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index a3f4b9a0fe0..004c8a172ed 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -33,6 +33,7 @@ #include "BLI_math_matrix.h" #include "BLI_math_matrix.hh" #include "BLI_math_vector.h" +#include "BLI_string.h" #include "BLI_string_utf8.h" #include "BLI_utildefines.h" #include "BLI_vector.hh" @@ -664,6 +665,32 @@ PaintMode BKE_paintmode_get_from_tool(const bToolRef *tref) return PaintMode::Invalid; } +static bool paint_brush_set_from_asset_reference(Main *bmain, Paint *paint) +{ + /* Attempt to restore a valid active brush from brush asset information. */ + if (paint->brush != nullptr) { + return false; + } + if (paint->brush_asset_reference == nullptr) { + return false; + } + + Brush *brush = reinterpret_cast( + BKE_asset_weak_reference_ensure(*bmain, ID_BR, *paint->brush_asset_reference)); + BLI_assert(brush == nullptr || (brush->id.tag & LIB_TAG_ASSET_MAIN)); + + /* Ensure we have a brush with appropriate mode to assign. + * Could happen if contents of asset blend was manually changed. */ + if (brush == nullptr || (paint->runtime.ob_mode & brush->ob_mode) == 0) { + MEM_delete(paint->brush_asset_reference); + paint->brush_asset_reference = nullptr; + return false; + } + + paint->brush = brush; + return true; +} + Brush *BKE_paint_brush(Paint *p) { return (Brush *)BKE_paint_brush_for_read((const Paint *)p); @@ -733,26 +760,107 @@ std::optional BKE_paint_brush_asset_get(Paint *paint, Brus return {}; } -void BKE_paint_brush_asset_restore(Main *bmain, Paint *paint) +static void paint_brush_set_essentials_reference(Paint *paint, const char *name) { - if (paint->brush != nullptr) { - return; - } - - if (paint->brush_asset_reference == nullptr) { - return; - } - - AssetWeakReference weak_ref = std::move(*paint->brush_asset_reference); + /* Set brush asset reference to a named brush in the essentials asset library. */ MEM_delete(paint->brush_asset_reference); - paint->brush_asset_reference = nullptr; - Brush *brush_asset = reinterpret_cast( - BKE_asset_weak_reference_ensure(*bmain, ID_BR, weak_ref)); + AssetWeakReference *weak_ref = MEM_new(__func__); + weak_ref->asset_library_type = eAssetLibraryType::ASSET_LIBRARY_ESSENTIALS; + weak_ref->relative_asset_identifier = BLI_sprintfN("brushes/essentials_brushes.blend/Brush/%s", + name); + paint->brush_asset_reference = weak_ref; + paint->brush = nullptr; +} - /* Will either re-assign the brush_asset_reference to `paint`, or free it if loading a brush ID - * from it failed. */ - BKE_paint_brush_asset_set(paint, brush_asset, weak_ref); +static void paint_brush_set_default_reference(Paint *paint) +{ + const char *name = nullptr; + + switch (paint->runtime.ob_mode) { + case OB_MODE_SCULPT: + name = "Draw"; + break; + case OB_MODE_VERTEX_PAINT: + name = "Paint Vertex"; + break; + case OB_MODE_WEIGHT_PAINT: + name = "Paint Weight"; + break; + case OB_MODE_TEXTURE_PAINT: + name = "Paint Texture"; + break; + case OB_MODE_SCULPT_CURVES: + name = "Comb Curves"; + break; + case OB_MODE_EDIT: + /* TODO: UV sculpt. */ + break; + case OB_MODE_PAINT_GREASE_PENCIL: + case OB_MODE_PAINT_GPENCIL_LEGACY: + name = "Pencil"; + break; + case OB_MODE_VERTEX_GPENCIL_LEGACY: + name = "Paint Point Color"; + break; + case OB_MODE_SCULPT_GPENCIL_LEGACY: + name = "Smooth Stroke"; + break; + case OB_MODE_WEIGHT_GPENCIL_LEGACY: + name = "Paint Point Weight"; + break; + default: + BLI_assert_unreachable(); + return; + } + + if (name) { + paint_brush_set_essentials_reference(paint, name); + } +} + +void BKE_paint_brush_set_default_references(ToolSettings *ts) +{ + if (ts->sculpt) { + paint_brush_set_default_reference(&ts->sculpt->paint); + } + if (ts->curves_sculpt) { + paint_brush_set_default_reference(&ts->curves_sculpt->paint); + } + if (ts->wpaint) { + paint_brush_set_default_reference(&ts->wpaint->paint); + } + if (ts->vpaint) { + paint_brush_set_default_reference(&ts->vpaint->paint); + } + if (ts->uvsculpt) { + paint_brush_set_default_reference(&ts->uvsculpt->paint); + } + if (ts->gp_paint) { + paint_brush_set_default_reference(&ts->gp_paint->paint); + } + if (ts->gp_vertexpaint) { + paint_brush_set_default_reference(&ts->gp_vertexpaint->paint); + } + if (ts->gp_sculptpaint) { + paint_brush_set_default_reference(&ts->gp_sculptpaint->paint); + } + if (ts->gp_weightpaint) { + paint_brush_set_default_reference(&ts->gp_weightpaint->paint); + } + paint_brush_set_default_reference(&ts->imapaint.paint); +} + +bool BKE_paint_brush_set_default(Main *bmain, Paint *paint) +{ + paint_brush_set_default_reference(paint); + return paint_brush_set_from_asset_reference(bmain, paint); +} + +bool BKE_paint_brush_set_essentials(Main *bmain, Paint *paint, const char *name) +{ + paint_brush_set_essentials_reference(paint, name); + return paint_brush_set_from_asset_reference(bmain, paint); } void BKE_paint_runtime_init(const ToolSettings *ts, Paint *paint) @@ -1205,7 +1313,7 @@ bool BKE_paint_ensure(Main *bmain, ToolSettings *ts, Paint **r_paint) BLI_assert(paint_test.runtime.tool_offset == (*r_paint)->runtime.tool_offset); #endif } - BKE_paint_brush_asset_restore(bmain, *r_paint); + paint_brush_set_from_asset_reference(bmain, *r_paint); return true; } @@ -1253,7 +1361,7 @@ bool BKE_paint_ensure(Main *bmain, ToolSettings *ts, Paint **r_paint) *r_paint = paint; BKE_paint_runtime_init(ts, paint); - BKE_paint_brush_asset_restore(bmain, paint); + BKE_paint_brush_set_default(bmain, paint); return false; } diff --git a/source/blender/blenloader/intern/versioning_400.cc b/source/blender/blenloader/intern/versioning_400.cc index 14a7ee49227..972dcb7fdcd 100644 --- a/source/blender/blenloader/intern/versioning_400.cc +++ b/source/blender/blenloader/intern/versioning_400.cc @@ -60,6 +60,7 @@ #include "BKE_mesh_legacy_convert.hh" #include "BKE_nla.h" #include "BKE_node_runtime.hh" +#include "BKE_paint.hh" #include "BKE_scene.hh" #include "BKE_tracking.h" @@ -1997,29 +1998,7 @@ static void update_paint_modes_for_brush_assets(Main &bmain) { /* Replace paint brushes with a reference to the default brush asset for that mode. */ LISTBASE_FOREACH (Scene *, scene, &bmain.scenes) { - auto set_paint_asset_ref = [&](Paint &paint, const blender::StringRef asset) { - AssetWeakReference *weak_ref = MEM_new(__func__); - weak_ref->asset_library_type = eAssetLibraryType::ASSET_LIBRARY_ESSENTIALS; - const std::string path = "brushes/essentials_brushes.blend/Brush/" + asset; - weak_ref->relative_asset_identifier = BLI_strdupn(path.data(), path.size()); - paint.brush_asset_reference = weak_ref; - paint.brush = nullptr; - }; - - ToolSettings *ts = scene->toolsettings; - if (ts->sculpt) { - set_paint_asset_ref(ts->sculpt->paint, "Draw"); - } - if (ts->curves_sculpt) { - set_paint_asset_ref(ts->curves_sculpt->paint, "Comb Curves"); - } - if (ts->wpaint) { - set_paint_asset_ref(ts->wpaint->paint, "Paint Weight"); - } - if (ts->vpaint) { - set_paint_asset_ref(ts->vpaint->paint, "Paint Vertex"); - } - set_paint_asset_ref(ts->imapaint.paint, "Paint Texture"); + BKE_paint_brush_set_default_references(scene->toolsettings); } /* Replace persistent tool references with the new single builtin brush tool. */ diff --git a/source/blender/editors/gpencil_legacy/gpencil_primitive.cc b/source/blender/editors/gpencil_legacy/gpencil_primitive.cc index 02aa6de2451..f7171a02310 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_primitive.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_primitive.cc @@ -1217,7 +1217,7 @@ static void gpencil_primitive_init(bContext *C, wmOperator *op) } /* Set Draw brush. */ - Brush *brush = BKE_paint_toolslots_brush_get(paint, 0); + brush = BKE_paint_toolslots_brush_get(paint, 0); BKE_brush_tool_set(brush, paint, 0); BKE_paint_brush_set(paint, brush); diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 4ca8c735d39..e8e84c2b47a 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -1256,11 +1256,12 @@ static int brush_asset_delete_exec(bContext *C, wmOperator *op) // TODO: delete whole asset main if empty? refresh_asset_library(C, *library); + + BKE_paint_brush_set_default(bmain, paint); + WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_REMOVED, nullptr); WM_main_add_notifier(NC_BRUSH | NA_EDITED, nullptr); - /* TODO: activate default brush. */ - return OPERATOR_FINISHED; } diff --git a/source/blender/editors/sculpt_paint/paint_vertex.cc b/source/blender/editors/sculpt_paint/paint_vertex.cc index 410faf41184..6eeb9a9503c 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.cc +++ b/source/blender/editors/sculpt_paint/paint_vertex.cc @@ -575,24 +575,28 @@ void last_stroke_update(Scene *scene, const float location[3]) /* -------------------------------------------------------------------- */ void smooth_brush_toggle_on(const bContext *C, Paint *paint, StrokeCache *cache) { + Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); + Brush *cur_brush = BKE_paint_brush(paint); /* Switch to the blur (smooth) brush if possible. */ - /* Note: used for both vertexpaint and weightpaint, VPAINT_TOOL_BLUR & WPAINT_TOOL_BLUR are the - * same, see comments for eBrushVertexPaintTool & eBrushWeightPaintTool. */ - Brush *smooth_brush = BKE_paint_toolslots_brush_get(paint, WPAINT_TOOL_BLUR); + BKE_paint_brush_set_essentials(bmain, + paint, + (paint->runtime.ob_mode = OB_MODE_WEIGHT_PAINT) ? "Blur Weight" : + "Blur Vertex"); + Brush *smooth_brush = BKE_paint_brush(paint); + if (!smooth_brush) { + BKE_paint_brush_set(paint, cur_brush); CLOG_WARN(&LOG, "Switching to the blur (smooth) brush not possible, corresponding brush not"); cache->saved_active_brush_name[0] = '\0'; return; } - Brush *cur_brush = paint->brush; int cur_brush_size = BKE_brush_size_get(scene, cur_brush); STRNCPY(cache->saved_active_brush_name, cur_brush->id.name + 2); - BKE_paint_brush_set(paint, smooth_brush); cache->saved_smooth_size = BKE_brush_size_get(scene, smooth_brush); BKE_brush_size_set(scene, smooth_brush, cur_brush_size); BKE_curvemapping_init(smooth_brush->curve); diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index a617aafe7c6..d145a23673b 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -4125,8 +4125,9 @@ static void smooth_brush_toggle_on(const bContext *C, Paint *paint, blender::ed::sculpt_paint::StrokeCache *cache) { + Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); - Brush *cur_brush = paint->brush; + Brush *cur_brush = BKE_paint_brush(paint); if (cur_brush->sculpt_tool == SCULPT_TOOL_MASK) { cache->saved_mask_brush_tool = cur_brush->mask_tool; @@ -4145,8 +4146,11 @@ static void smooth_brush_toggle_on(const bContext *C, } /* Switch to the smooth brush if possible. */ - Brush *smooth_brush = BKE_paint_toolslots_brush_get(paint, SCULPT_TOOL_SMOOTH); + BKE_paint_brush_set_essentials(bmain, paint, "Smooth"); + Brush *smooth_brush = BKE_paint_brush(paint); + if (!smooth_brush) { + BKE_paint_brush_set(paint, cur_brush); CLOG_WARN(&LOG, "Switching to the smooth brush not possible, corresponding brush not"); cache->saved_active_brush_name[0] = '\0'; return; @@ -4156,7 +4160,6 @@ static void smooth_brush_toggle_on(const bContext *C, STRNCPY(cache->saved_active_brush_name, cur_brush->id.name + 2); - BKE_paint_brush_set(paint, smooth_brush); cache->saved_smooth_size = BKE_brush_size_get(scene, smooth_brush); BKE_brush_size_set(scene, smooth_brush, cur_brush_size); BKE_curvemapping_init(smooth_brush->curve); -- 2.30.2 From 626d6e76e09861fdaef1336a8c2bda6a901f79bd Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 22 Mar 2024 15:04:13 +0100 Subject: [PATCH 161/244] Brushes: Remove unused paint tool slots These were used for associating brushes with specific tools. --- source/blender/blenkernel/BKE_brush.hh | 11 -- source/blender/blenkernel/BKE_paint.hh | 19 +- source/blender/blenkernel/CMakeLists.txt | 1 - source/blender/blenkernel/intern/paint.cc | 107 ++--------- .../blenkernel/intern/paint_toolslots.cc | 164 ----------------- source/blender/blenkernel/intern/scene.cc | 20 --- .../blenloader/intern/versioning_280.cc | 1 - .../editors/gpencil_legacy/gpencil_data.cc | 2 +- .../editors/gpencil_legacy/gpencil_edit.cc | 8 +- .../gpencil_legacy/gpencil_primitive.cc | 8 +- .../editors/sculpt_paint/paint_image.cc | 2 +- .../editors/sculpt_paint/paint_vertex.cc | 2 +- .../editors/sculpt_paint/paint_weight.cc | 2 +- .../editors/sculpt_paint/sculpt_ops.cc | 2 +- source/blender/makesdna/DNA_scene_types.h | 15 +- .../makesrna/intern/rna_sculpt_paint.cc | 170 +++--------------- 16 files changed, 55 insertions(+), 479 deletions(-) delete mode 100644 source/blender/blenkernel/intern/paint_toolslots.cc diff --git a/source/blender/blenkernel/BKE_brush.hh b/source/blender/blenkernel/BKE_brush.hh index ec7d758057d..fc214b1ea97 100644 --- a/source/blender/blenkernel/BKE_brush.hh +++ b/source/blender/blenkernel/BKE_brush.hh @@ -180,16 +180,5 @@ void BKE_brush_scale_size(int *r_brush_size, */ bool BKE_brush_has_cube_tip(const Brush *brush, PaintMode paint_mode); -/* Accessors */ -#define BKE_brush_tool_get(brush, p) \ - (CHECK_TYPE_ANY(brush, Brush *, const Brush *), \ - *(const char *)POINTER_OFFSET(brush, (p)->runtime.tool_offset)) -#define BKE_brush_tool_set(brush, p, tool) \ - { \ - CHECK_TYPE_ANY(brush, Brush *); \ - *(char *)POINTER_OFFSET(brush, (p)->runtime.tool_offset) = tool; \ - } \ - ((void)0) - /* debugging only */ void BKE_brush_debug_print_state(Brush *br); diff --git a/source/blender/blenkernel/BKE_paint.hh b/source/blender/blenkernel/BKE_paint.hh index ef04cc236ab..369af90c6a8 100644 --- a/source/blender/blenkernel/BKE_paint.hh +++ b/source/blender/blenkernel/BKE_paint.hh @@ -184,16 +184,12 @@ void BKE_paint_free(Paint *p); */ void BKE_paint_copy(const Paint *src, Paint *tar, int flag); -void BKE_paint_runtime_init(const ToolSettings *ts, Paint *paint); - void BKE_paint_cavity_curve_preset(Paint *p, int preset); eObjectMode BKE_paint_object_mode_from_paintmode(PaintMode mode); bool BKE_paint_ensure_from_paintmode(Main *bmain, Scene *sce, PaintMode mode); Paint *BKE_paint_get_active_from_paintmode(Scene *sce, PaintMode mode); const EnumPropertyItem *BKE_paint_get_tool_enum_from_paintmode(PaintMode mode); -const char *BKE_paint_get_tool_enum_translation_context_from_paintmode(PaintMode mode); -const char *BKE_paint_get_tool_prop_id_from_paintmode(PaintMode mode); uint BKE_paint_get_brush_tool_offset_from_paintmode(PaintMode mode); Paint *BKE_paint_get_active(Scene *sce, ViewLayer *view_layer); Paint *BKE_paint_get_active_from_context(const bContext *C); @@ -211,6 +207,8 @@ bool BKE_paint_brush_set_essentials(Main *bmain, Paint *paint, const char *name) void BKE_paint_brush_set_default_references(ToolSettings *ts); +void BKE_paint_brush_validate(Main *bmain, Paint *paint); + /** * Check if the given brush is a valid Brush Asset. * @@ -286,19 +284,6 @@ void paint_update_brush_rake_rotation(UnifiedPaintSettings *ups, Brush *brush, f void BKE_paint_stroke_get_average(const Scene *scene, const Object *ob, float stroke[3]); -/* Tool slot API. */ - -void BKE_paint_toolslots_init_from_main(Main *bmain); -void BKE_paint_toolslots_len_ensure(Paint *paint, int len); -void BKE_paint_toolslots_brush_update_ex(Paint *paint, Brush *brush); -void BKE_paint_toolslots_brush_update(Paint *paint); -/** - * Run this to ensure brush types are set for each slot on entering modes - * (for new scenes for example). - */ -void BKE_paint_toolslots_brush_validate(Main *bmain, Paint *paint); -Brush *BKE_paint_toolslots_brush_get(Paint *paint, int slot_index); - /* .blend I/O */ void BKE_paint_blend_write(BlendWriter *writer, Paint *paint); diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 4a5827e644a..4c1a89db95e 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -253,7 +253,6 @@ set(SRC intern/packedFile.cc intern/paint.cc intern/paint_canvas.cc - intern/paint_toolslots.cc intern/particle.cc intern/particle_child.cc intern/particle_distribute.cc diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 004c8a172ed..8945b1cb09e 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -441,61 +441,6 @@ const EnumPropertyItem *BKE_paint_get_tool_enum_from_paintmode(const PaintMode m return nullptr; } -const char *BKE_paint_get_tool_prop_id_from_paintmode(const PaintMode mode) -{ - switch (mode) { - case PaintMode::Sculpt: - return "sculpt_tool"; - case PaintMode::Vertex: - return "vertex_tool"; - case PaintMode::Weight: - return "weight_tool"; - case PaintMode::Texture2D: - case PaintMode::Texture3D: - return "image_tool"; - case PaintMode::SculptUV: - return "uv_sculpt_tool"; - case PaintMode::GPencil: - return "gpencil_tool"; - case PaintMode::VertexGPencil: - return "gpencil_vertex_tool"; - case PaintMode::SculptGPencil: - return "gpencil_sculpt_tool"; - case PaintMode::WeightGPencil: - return "gpencil_weight_tool"; - case PaintMode::SculptCurves: - return "curves_sculpt_tool"; - case PaintMode::Invalid: - break; - } - - /* Invalid paint mode. */ - return nullptr; -} - -const char *BKE_paint_get_tool_enum_translation_context_from_paintmode(const PaintMode mode) -{ - switch (mode) { - case PaintMode::Sculpt: - case PaintMode::GPencil: - case PaintMode::Texture2D: - case PaintMode::Texture3D: - return BLT_I18NCONTEXT_ID_BRUSH; - case PaintMode::Vertex: - case PaintMode::Weight: - case PaintMode::SculptUV: - case PaintMode::VertexGPencil: - case PaintMode::SculptGPencil: - case PaintMode::WeightGPencil: - case PaintMode::SculptCurves: - case PaintMode::Invalid: - break; - } - - /* Invalid paint mode. */ - return BLT_I18NCONTEXT_DEFAULT; -} - Paint *BKE_paint_get_active(Scene *sce, ViewLayer *view_layer) { if (sce && view_layer) { @@ -705,8 +650,6 @@ void BKE_paint_brush_set(Paint *p, Brush *br) { if (p) { p->brush = br; - - BKE_paint_toolslots_brush_update(p); } } @@ -863,30 +806,35 @@ bool BKE_paint_brush_set_essentials(Main *bmain, Paint *paint, const char *name) return paint_brush_set_from_asset_reference(bmain, paint); } -void BKE_paint_runtime_init(const ToolSettings *ts, Paint *paint) +void BKE_paint_brush_validate(Main *bmain, Paint *paint) +{ + /* Clear brush with invalid mode. Unclear if this can still happen, + * but kept from old paint toolslots code. */ + Brush *brush = BKE_paint_brush(paint); + if (brush && (paint->runtime.ob_mode & brush->ob_mode) == 0) { + BKE_paint_brush_set(paint, nullptr); + BKE_paint_brush_set_default(bmain, paint); + } +} + +static void paint_runtime_init(const ToolSettings *ts, Paint *paint) { if (paint == &ts->imapaint.paint) { - paint->runtime.tool_offset = offsetof(Brush, imagepaint_tool); paint->runtime.ob_mode = OB_MODE_TEXTURE_PAINT; } else if (ts->sculpt && paint == &ts->sculpt->paint) { - paint->runtime.tool_offset = offsetof(Brush, sculpt_tool); paint->runtime.ob_mode = OB_MODE_SCULPT; } else if (ts->vpaint && paint == &ts->vpaint->paint) { - paint->runtime.tool_offset = offsetof(Brush, vertexpaint_tool); paint->runtime.ob_mode = OB_MODE_VERTEX_PAINT; } else if (ts->wpaint && paint == &ts->wpaint->paint) { - paint->runtime.tool_offset = offsetof(Brush, weightpaint_tool); paint->runtime.ob_mode = OB_MODE_WEIGHT_PAINT; } else if (ts->uvsculpt && paint == &ts->uvsculpt->paint) { - paint->runtime.tool_offset = offsetof(Brush, uv_sculpt_tool); paint->runtime.ob_mode = OB_MODE_EDIT; } else if (ts->gp_paint && paint == &ts->gp_paint->paint) { - paint->runtime.tool_offset = offsetof(Brush, gpencil_tool); if (U.experimental.use_grease_pencil_version3) { paint->runtime.ob_mode = OB_MODE_PAINT_GREASE_PENCIL; } @@ -895,24 +843,22 @@ void BKE_paint_runtime_init(const ToolSettings *ts, Paint *paint) } } else if (ts->gp_vertexpaint && paint == &ts->gp_vertexpaint->paint) { - paint->runtime.tool_offset = offsetof(Brush, gpencil_vertex_tool); paint->runtime.ob_mode = OB_MODE_VERTEX_GPENCIL_LEGACY; } else if (ts->gp_sculptpaint && paint == &ts->gp_sculptpaint->paint) { - paint->runtime.tool_offset = offsetof(Brush, gpencil_sculpt_tool); paint->runtime.ob_mode = OB_MODE_SCULPT_GPENCIL_LEGACY; } else if (ts->gp_weightpaint && paint == &ts->gp_weightpaint->paint) { - paint->runtime.tool_offset = offsetof(Brush, gpencil_weight_tool); paint->runtime.ob_mode = OB_MODE_WEIGHT_GPENCIL_LEGACY; } else if (ts->curves_sculpt && paint == &ts->curves_sculpt->paint) { - paint->runtime.tool_offset = offsetof(Brush, curves_sculpt_tool); paint->runtime.ob_mode = OB_MODE_SCULPT_CURVES; } else { BLI_assert_unreachable(); } + + paint->runtime.initialized = true; } uint BKE_paint_get_brush_tool_offset_from_paintmode(const PaintMode mode) @@ -1283,13 +1229,11 @@ bool BKE_paint_ensure(Main *bmain, ToolSettings *ts, Paint **r_paint) { Paint *paint = nullptr; if (*r_paint) { - /* Tool offset should never be 0 for initialized paint settings, so it's a reliable way to - * check if already initialized. */ - if ((*r_paint)->runtime.tool_offset == 0) { + if (!(*r_paint)->runtime.initialized) { /* Currently only image painting is initialized this way, others have to be allocated. */ BLI_assert(ELEM(*r_paint, (Paint *)&ts->imapaint)); - BKE_paint_runtime_init(ts, *r_paint); + paint_runtime_init(ts, *r_paint); } else { BLI_assert(ELEM(*r_paint, @@ -1306,11 +1250,10 @@ bool BKE_paint_ensure(Main *bmain, ToolSettings *ts, Paint **r_paint) (Paint *)&ts->imapaint)); #ifndef NDEBUG Paint paint_test = **r_paint; - BKE_paint_runtime_init(ts, *r_paint); + paint_runtime_init(ts, *r_paint); /* Swap so debug doesn't hide errors when release fails. */ std::swap(**r_paint, paint_test); BLI_assert(paint_test.runtime.ob_mode == (*r_paint)->runtime.ob_mode); - BLI_assert(paint_test.runtime.tool_offset == (*r_paint)->runtime.tool_offset); #endif } paint_brush_set_from_asset_reference(bmain, *r_paint); @@ -1360,7 +1303,7 @@ bool BKE_paint_ensure(Main *bmain, ToolSettings *ts, Paint **r_paint) *r_paint = paint; - BKE_paint_runtime_init(ts, paint); + paint_runtime_init(ts, paint); BKE_paint_brush_set_default(bmain, paint); return false; @@ -1386,7 +1329,6 @@ void BKE_paint_init(Main *bmain, Scene *sce, PaintMode mode, const uchar col[3]) void BKE_paint_free(Paint *paint) { BKE_curvemapping_free(paint->cavity_curve); - MEM_SAFE_FREE(paint->tool_slots); MEM_delete(paint->brush_asset_reference); } @@ -1394,7 +1336,6 @@ void BKE_paint_copy(const Paint *src, Paint *dst, const int flag) { dst->brush = src->brush; dst->cavity_curve = BKE_curvemapping_copy(src->cavity_curve); - dst->tool_slots = static_cast(MEM_dupallocN(src->tool_slots)); if (src->brush_asset_reference) { dst->brush_asset_reference = MEM_new(__func__, @@ -1426,7 +1367,6 @@ void BKE_paint_blend_write(BlendWriter *writer, Paint *p) if (p->brush_asset_reference) { BKE_asset_weak_reference_write(writer, p->brush_asset_reference); } - BLO_write_struct_array(writer, PaintToolSlot, p->tool_slots_len, p->tool_slots); } void BKE_paint_blend_read_data(BlendDataReader *reader, const Scene *scene, Paint *p) @@ -1444,17 +1384,8 @@ void BKE_paint_blend_read_data(BlendDataReader *reader, const Scene *scene, Pain BKE_asset_weak_reference_read(reader, p->brush_asset_reference); } - BLO_read_data_address(reader, &p->tool_slots); - - /* Workaround for invalid data written in older versions. */ - const size_t expected_size = sizeof(PaintToolSlot) * p->tool_slots_len; - if (p->tool_slots && MEM_allocN_len(p->tool_slots) < expected_size) { - MEM_freeN(p->tool_slots); - p->tool_slots = static_cast(MEM_callocN(expected_size, "PaintToolSlot")); - } - p->paint_cursor = nullptr; - BKE_paint_runtime_init(scene->toolsettings, p); + paint_runtime_init(scene->toolsettings, p); } bool paint_is_grid_face_hidden(const blender::BoundedBitSpan grid_hidden, diff --git a/source/blender/blenkernel/intern/paint_toolslots.cc b/source/blender/blenkernel/intern/paint_toolslots.cc deleted file mode 100644 index fc03248b527..00000000000 --- a/source/blender/blenkernel/intern/paint_toolslots.cc +++ /dev/null @@ -1,164 +0,0 @@ -/* SPDX-FileCopyrightText: 2023 Blender Authors - * - * SPDX-License-Identifier: GPL-2.0-or-later */ - -/** \file - * \ingroup bke - */ - -#include - -#include "MEM_guardedalloc.h" - -#include "DNA_brush_types.h" -#include "DNA_scene_types.h" - -#include "BLI_utildefines.h" - -#include "BKE_brush.hh" -#include "BKE_lib_id.hh" -#include "BKE_main.hh" -#include "BKE_paint.hh" - -/* -------------------------------------------------------------------- */ -/** \name Tool Slot Initialization / Versioning - * - * These functions run to update old files (while versioning), - * take care only to perform low-level functions here. - * \{ */ - -void BKE_paint_toolslots_len_ensure(Paint *paint, int len) -{ - /* Tool slots are 'uchar'. */ - BLI_assert(len <= UCHAR_MAX); - if (paint->tool_slots_len < len) { - paint->tool_slots = static_cast( - MEM_recallocN(paint->tool_slots, sizeof(*paint->tool_slots) * len)); - paint->tool_slots_len = len; - } -} - -static void paint_toolslots_init(Main *bmain, Paint *paint) -{ - if (paint == nullptr) { - return; - } - const eObjectMode ob_mode = eObjectMode(paint->runtime.ob_mode); - BLI_assert(paint->runtime.tool_offset && ob_mode); - for (Brush *brush = static_cast(bmain->brushes.first); brush; - brush = static_cast(brush->id.next)) - { - if (brush->ob_mode & ob_mode) { - const int slot_index = BKE_brush_tool_get(brush, 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; - } - } - } -} - -/** - * Initialize runtime since this is called from versioning code. - */ -static void paint_toolslots_init_with_runtime(Main *bmain, ToolSettings *ts, Paint *paint) -{ - if (paint == nullptr) { - return; - } - - /* Needed so #Paint_Runtime is updated when versioning. */ - BKE_paint_runtime_init(ts, paint); - paint_toolslots_init(bmain, paint); -} - -void BKE_paint_toolslots_init_from_main(Main *bmain) -{ - for (Scene *scene = static_cast(bmain->scenes.first); scene; - scene = static_cast(scene->id.next)) - { - ToolSettings *ts = scene->toolsettings; - paint_toolslots_init_with_runtime(bmain, ts, &ts->imapaint.paint); - if (ts->sculpt) { - paint_toolslots_init_with_runtime(bmain, ts, &ts->sculpt->paint); - } - if (ts->vpaint) { - paint_toolslots_init_with_runtime(bmain, ts, &ts->vpaint->paint); - } - if (ts->wpaint) { - paint_toolslots_init_with_runtime(bmain, ts, &ts->wpaint->paint); - } - if (ts->uvsculpt) { - paint_toolslots_init_with_runtime(bmain, ts, &ts->uvsculpt->paint); - } - if (ts->gp_paint) { - paint_toolslots_init_with_runtime(bmain, ts, &ts->gp_paint->paint); - } - if (ts->gp_vertexpaint) { - paint_toolslots_init_with_runtime(bmain, ts, &ts->gp_vertexpaint->paint); - } - if (ts->gp_sculptpaint) { - paint_toolslots_init_with_runtime(bmain, ts, &ts->gp_sculptpaint->paint); - } - if (ts->gp_weightpaint) { - paint_toolslots_init_with_runtime(bmain, ts, &ts->gp_weightpaint->paint); - } - if (ts->curves_sculpt) { - paint_toolslots_init_with_runtime(bmain, ts, &ts->curves_sculpt->paint); - } - } -} - -/** \} */ - -void BKE_paint_toolslots_brush_update_ex(Paint *paint, Brush *brush) -{ - const uint tool_offset = paint->runtime.tool_offset; - UNUSED_VARS_NDEBUG(tool_offset); - BLI_assert(tool_offset != 0); - 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]; - tslot->brush = brush; -} - -void BKE_paint_toolslots_brush_update(Paint *paint) -{ - if (paint->brush == nullptr) { - return; - } - BKE_paint_toolslots_brush_update_ex(paint, paint->brush); -} - -void BKE_paint_toolslots_brush_validate(Main *bmain, Paint *paint) -{ - /* Clear slots with invalid slots or mode (unlikely but possible). */ - const uint tool_offset = paint->runtime.tool_offset; - UNUSED_VARS_NDEBUG(tool_offset); - const eObjectMode ob_mode = eObjectMode(paint->runtime.ob_mode); - BLI_assert(tool_offset && ob_mode); - for (int i = 0; i < paint->tool_slots_len; i++) { - PaintToolSlot *tslot = &paint->tool_slots[i]; - if (tslot->brush) { - if ((i != BKE_brush_tool_get(tslot->brush, paint)) || (tslot->brush->ob_mode & ob_mode) == 0) - { - tslot->brush = nullptr; - } - } - } - - /* Unlikely but possible the active brush is not currently using a slot. */ - BKE_paint_toolslots_brush_update(paint); - - /* Fill slots from brushes. */ - paint_toolslots_init(bmain, paint); -} - -Brush *BKE_paint_toolslots_brush_get(Paint *paint, int slot_index) -{ - if (slot_index < paint->tool_slots_len) { - PaintToolSlot *tslot = &paint->tool_slots[slot_index]; - return tslot->brush; - } - return nullptr; -} diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc index 3ac76a1e97a..c545489a03f 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -602,26 +602,6 @@ static void scene_foreach_paint(LibraryForeachIDData *data, &paint_old->brush, IDWALK_CB_NOP); - for (int i = 0; i < paint_old->tool_slots_len; i++) { - /* This is a bit tricky. - * - In case we do not do `undo_restore`, `paint` and `paint_old` pointers are the same, so - * this is equivalent to simply looping over slots from `paint`. - * - In case we do `undo_restore`, we only want to consider the slots from the old one, since - * those are the one we keep in the end. - * + In case the new data has less valid slots, we feed in a dummy null pointer. - * + In case the new data has more valid slots, the extra ones are ignored. - */ - brush_tmp = nullptr; - brush_p = (paint && i < paint->tool_slots_len) ? &paint->tool_slots[i].brush : &brush_tmp; - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER_P(data, - brush_p, - do_undo_restore, - SCENE_FOREACH_UNDO_RESTORE, - reader, - &paint_old->tool_slots[i].brush, - IDWALK_CB_NOP); - } - Palette *palette_tmp = nullptr; Palette **palette_p = paint ? &paint->palette : &palette_tmp; BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER_P(data, diff --git a/source/blender/blenloader/intern/versioning_280.cc b/source/blender/blenloader/intern/versioning_280.cc index 236b3741b11..d907f28e2c3 100644 --- a/source/blender/blenloader/intern/versioning_280.cc +++ b/source/blender/blenloader/intern/versioning_280.cc @@ -2557,7 +2557,6 @@ void do_versions_after_linking_280(FileData *fd, Main *bmain) brush->gpencil_tool = brush->gpencil_settings->brush_type; } } - BKE_paint_toolslots_init_from_main(bmain); } if (!MAIN_VERSION_FILE_ATLEAST(bmain, 280, 38)) { diff --git a/source/blender/editors/gpencil_legacy/gpencil_data.cc b/source/blender/editors/gpencil_legacy/gpencil_data.cc index d5c0781b26c..2d50e4fe140 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_data.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_data.cc @@ -2200,7 +2200,7 @@ static int gpencil_brush_reset_all_exec(bContext *C, wmOperator * /*op*/) } } - BKE_paint_toolslots_brush_validate(bmain, paint); + BKE_paint_brush_validate(bmain, paint); /* Set Again the first brush of the mode. */ Brush *deft_brush = gpencil_brush_get_first_by_mode(bmain, paint, mode, tool); diff --git a/source/blender/editors/gpencil_legacy/gpencil_edit.cc b/source/blender/editors/gpencil_legacy/gpencil_edit.cc index 6aca1bb806e..6cbffc4440c 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_edit.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_edit.cc @@ -400,7 +400,7 @@ static int gpencil_paintmode_toggle_exec(bContext *C, wmOperator *op) if ((brush == nullptr) || (brush->gpencil_settings == nullptr)) { BKE_brush_gpencil_paint_presets(bmain, ts, true); } - BKE_paint_toolslots_brush_validate(bmain, &ts->gp_paint->paint); + BKE_paint_brush_validate(bmain, &ts->gp_paint->paint); } /* setup other modes */ @@ -507,7 +507,7 @@ static int gpencil_sculptmode_toggle_exec(bContext *C, wmOperator *op) const bool reset_mode = (BKE_paint_brush(&ts->gp_sculptpaint->paint) == nullptr); BKE_brush_gpencil_sculpt_presets(bmain, ts, reset_mode); - BKE_paint_toolslots_brush_validate(bmain, &ts->gp_sculptpaint->paint); + BKE_paint_brush_validate(bmain, &ts->gp_sculptpaint->paint); } /* setup other modes */ @@ -621,7 +621,7 @@ static int gpencil_weightmode_toggle_exec(bContext *C, wmOperator *op) const bool reset_mode = (BKE_paint_brush(&ts->gp_weightpaint->paint) == nullptr); BKE_brush_gpencil_weight_presets(bmain, ts, reset_mode); - BKE_paint_toolslots_brush_validate(bmain, &ts->gp_weightpaint->paint); + BKE_paint_brush_validate(bmain, &ts->gp_weightpaint->paint); } /* setup other modes */ @@ -729,7 +729,7 @@ static int gpencil_vertexmode_toggle_exec(bContext *C, wmOperator *op) const bool reset_mode = (BKE_paint_brush(&ts->gp_vertexpaint->paint) == nullptr); BKE_brush_gpencil_vertex_presets(bmain, ts, reset_mode); - BKE_paint_toolslots_brush_validate(bmain, &ts->gp_vertexpaint->paint); + BKE_paint_brush_validate(bmain, &ts->gp_vertexpaint->paint); /* Ensure Palette by default. */ BKE_gpencil_palette_ensure(bmain, CTX_data_scene(C)); diff --git a/source/blender/editors/gpencil_legacy/gpencil_primitive.cc b/source/blender/editors/gpencil_legacy/gpencil_primitive.cc index f7171a02310..0c8a58dab80 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_primitive.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_primitive.cc @@ -1216,12 +1216,8 @@ static void gpencil_primitive_init(bContext *C, wmOperator *op) BKE_brush_gpencil_paint_presets(bmain, ts, true); } - /* Set Draw brush. */ - brush = BKE_paint_toolslots_brush_get(paint, 0); - - BKE_brush_tool_set(brush, paint, 0); - BKE_paint_brush_set(paint, brush); - tgpi->brush = brush; + /* Set brush. */ + tgpi->brush = BKE_paint_brush(paint); /* control points */ tgpi->gpd->runtime.cp_points = static_cast( diff --git a/source/blender/editors/sculpt_paint/paint_image.cc b/source/blender/editors/sculpt_paint/paint_image.cc index 5c0cfe12099..1abb6a90a45 100644 --- a/source/blender/editors/sculpt_paint/paint_image.cc +++ b/source/blender/editors/sculpt_paint/paint_image.cc @@ -901,7 +901,7 @@ void ED_object_texture_paint_mode_enter_ex(Main *bmain, BKE_paint_init(bmain, scene, PaintMode::Texture3D, PAINT_CURSOR_TEXTURE_PAINT); - BKE_paint_toolslots_brush_validate(bmain, &imapaint->paint); + BKE_paint_brush_validate(bmain, &imapaint->paint); if (U.glreslimit != 0) { BKE_image_free_all_gputextures(bmain); diff --git a/source/blender/editors/sculpt_paint/paint_vertex.cc b/source/blender/editors/sculpt_paint/paint_vertex.cc index 6eeb9a9503c..49a17e53a37 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.cc +++ b/source/blender/editors/sculpt_paint/paint_vertex.cc @@ -860,7 +860,7 @@ static int vpaint_mode_toggle_exec(bContext *C, wmOperator *op) depsgraph = CTX_data_ensure_evaluated_depsgraph(C); } ED_object_vpaintmode_enter_ex(bmain, depsgraph, scene, ob); - BKE_paint_toolslots_brush_validate(bmain, &ts->vpaint->paint); + BKE_paint_brush_validate(bmain, &ts->vpaint->paint); } BKE_mesh_batch_cache_dirty_tag((Mesh *)ob->data, BKE_MESH_BATCH_DIRTY_ALL); diff --git a/source/blender/editors/sculpt_paint/paint_weight.cc b/source/blender/editors/sculpt_paint/paint_weight.cc index 9076da67339..4ac5efd1077 100644 --- a/source/blender/editors/sculpt_paint/paint_weight.cc +++ b/source/blender/editors/sculpt_paint/paint_weight.cc @@ -1674,7 +1674,7 @@ static int wpaint_mode_toggle_exec(bContext *C, wmOperator *op) depsgraph = CTX_data_ensure_evaluated_depsgraph(C); } ED_object_wpaintmode_enter_ex(bmain, depsgraph, scene, ob); - BKE_paint_toolslots_brush_validate(bmain, &ts->wpaint->paint); + BKE_paint_brush_validate(bmain, &ts->wpaint->paint); } /* Prepare armature posemode. */ diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.cc b/source/blender/editors/sculpt_paint/sculpt_ops.cc index eac4f1d8723..5be7ad95922 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/sculpt_ops.cc @@ -527,7 +527,7 @@ static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op) depsgraph = CTX_data_ensure_evaluated_depsgraph(C); } ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, false, op->reports); - BKE_paint_toolslots_brush_validate(bmain, &ts->sculpt->paint); + BKE_paint_brush_validate(bmain, &ts->sculpt->paint); if (ob->mode & mode_flag) { Mesh *mesh = static_cast(ob->data); diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index d60c5147de2..cc6727cbb7a 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -950,16 +950,11 @@ typedef struct TimeMarker { typedef struct Paint_Runtime { /** Avoid having to compare with scene pointer everywhere. */ - unsigned int tool_offset; + unsigned int initialized; unsigned short ob_mode; char _pad[2]; } Paint_Runtime; -/** We might want to store other things here. */ -typedef struct PaintToolSlot { - struct Brush *brush; -} PaintToolSlot; - /** Paint Tool Base. */ typedef struct Paint { /** @@ -976,14 +971,6 @@ typedef struct Paint { */ struct AssetWeakReference *brush_asset_reference; - /** - * Each tool has its own active brush, - * The currently active tool is defined by the current 'brush'. - */ - struct PaintToolSlot *tool_slots; - int tool_slots_len; - char _pad1[4]; - struct Palette *palette; /** Cavity curve. */ struct CurveMapping *cavity_curve; diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.cc b/source/blender/makesrna/intern/rna_sculpt_paint.cc index bc0ad62d2c0..bffbaaef197 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.cc +++ b/source/blender/makesrna/intern/rna_sculpt_paint.cc @@ -270,114 +270,27 @@ static std::optional rna_ParticleEdit_path(const PointerRNA * /*ptr return "tool_settings.particle_edit"; } -static bool rna_Brush_mode_poll(PointerRNA *ptr, PointerRNA value) +static PointerRNA rna_Paint_brush_get(PointerRNA *ptr) +{ + Paint *paint = static_cast(ptr->data); + Brush *brush = BKE_paint_brush(paint); + return RNA_id_pointer_create(&brush->id); +} + +static void rna_Paint_brush_set(PointerRNA *ptr, PointerRNA value, ReportList * /*reports*/) +{ + Paint *paint = static_cast(ptr->data); + Brush *brush = static_cast(value.data); + BKE_paint_brush_set(paint, brush); + BKE_paint_invalidate_overlay_all(); +} + +static bool rna_Paint_brush_poll(PointerRNA *ptr, PointerRNA value) { const Paint *paint = static_cast(ptr->data); - Brush *brush = (Brush *)value.owner_id; - const uint tool_offset = paint->runtime.tool_offset; - const eObjectMode ob_mode = eObjectMode(paint->runtime.ob_mode); - UNUSED_VARS_NDEBUG(tool_offset); - BLI_assert(tool_offset && ob_mode); + const Brush *brush = static_cast(value.data); - if (brush->ob_mode & ob_mode) { - if (paint->brush) { - if (BKE_brush_tool_get(paint->brush, paint) == BKE_brush_tool_get(brush, paint)) { - return true; - } - } - else { - return true; - } - } - - return false; -} - -static bool paint_contains_brush_slot(const Paint *paint, const PaintToolSlot *tslot, int *r_index) -{ - if ((tslot >= paint->tool_slots) && (tslot < (paint->tool_slots + paint->tool_slots_len))) { - *r_index = int(tslot - paint->tool_slots); - return true; - } - return false; -} - -static bool rna_Brush_mode_with_tool_poll(PointerRNA *ptr, PointerRNA value) -{ - Scene *scene = (Scene *)ptr->owner_id; - const PaintToolSlot *tslot = static_cast(ptr->data); - ToolSettings *ts = scene->toolsettings; - Brush *brush = (Brush *)value.owner_id; - int mode = 0; - int slot_index = 0; - - if (paint_contains_brush_slot(&ts->imapaint.paint, tslot, &slot_index)) { - if (slot_index != brush->imagepaint_tool) { - return false; - } - mode = OB_MODE_TEXTURE_PAINT; - } - else if (paint_contains_brush_slot(&ts->sculpt->paint, tslot, &slot_index)) { - if (slot_index != brush->sculpt_tool) { - return false; - } - mode = OB_MODE_SCULPT; - } - else if (paint_contains_brush_slot(&ts->uvsculpt->paint, tslot, &slot_index)) { - if (slot_index != brush->uv_sculpt_tool) { - return false; - } - mode = OB_MODE_EDIT; - } - else if (paint_contains_brush_slot(&ts->vpaint->paint, tslot, &slot_index)) { - if (slot_index != brush->vertexpaint_tool) { - return false; - } - mode = OB_MODE_VERTEX_PAINT; - } - else if (paint_contains_brush_slot(&ts->wpaint->paint, tslot, &slot_index)) { - if (slot_index != brush->weightpaint_tool) { - return false; - } - mode = OB_MODE_WEIGHT_PAINT; - } - else if (paint_contains_brush_slot(&ts->gp_paint->paint, tslot, &slot_index)) { - if (slot_index != brush->gpencil_tool) { - return false; - } - if (U.experimental.use_grease_pencil_version3) { - mode = OB_MODE_PAINT_GREASE_PENCIL; - } - else { - mode = OB_MODE_PAINT_GPENCIL_LEGACY; - } - } - else if (paint_contains_brush_slot(&ts->gp_vertexpaint->paint, tslot, &slot_index)) { - if (slot_index != brush->gpencil_vertex_tool) { - return false; - } - mode = OB_MODE_VERTEX_GPENCIL_LEGACY; - } - else if (paint_contains_brush_slot(&ts->gp_sculptpaint->paint, tslot, &slot_index)) { - if (slot_index != brush->gpencil_sculpt_tool) { - return false; - } - mode = OB_MODE_SCULPT_GPENCIL_LEGACY; - } - else if (paint_contains_brush_slot(&ts->gp_weightpaint->paint, tslot, &slot_index)) { - if (slot_index != brush->gpencil_weight_tool) { - return false; - } - mode = OB_MODE_WEIGHT_GPENCIL_LEGACY; - } - else if (paint_contains_brush_slot(&ts->curves_sculpt->paint, tslot, &slot_index)) { - if (slot_index != brush->curves_sculpt_tool) { - return false; - } - mode = OB_MODE_SCULPT_CURVES; - } - - return brush->ob_mode & mode; + return (brush == nullptr) || (paint->runtime.ob_mode & brush->ob_mode) != 0; } static void rna_Sculpt_update(bContext *C, PointerRNA * /*ptr*/) @@ -453,16 +366,6 @@ static std::optional rna_ParticleBrush_path(const PointerRNA * /*pt return "tool_settings.particle_edit.brush"; } -static void rna_Paint_brush_update(Main * /*bmain*/, Scene * /*scene*/, PointerRNA *ptr) -{ - Paint *paint = static_cast(ptr->data); - Brush *br = paint->brush; - BKE_paint_invalidate_overlay_all(); - /* Needed because we're not calling 'BKE_paint_brush_set' which handles this. */ - BKE_paint_toolslots_brush_update(paint); - WM_main_add_notifier(NC_BRUSH | NA_SELECTED, br); -} - static void rna_ImaPaint_viewport_update(Main * /*bmain*/, Scene * /*scene*/, PointerRNA * /*ptr*/) { /* not the best solution maybe, but will refresh the 3D viewport */ @@ -598,20 +501,6 @@ static void rna_def_paint_curve(BlenderRNA *brna) RNA_def_struct_ui_icon(srna, ICON_CURVE_BEZCURVE); } -static void rna_def_paint_tool_slot(BlenderRNA *brna) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, "PaintToolSlot", nullptr); - RNA_def_struct_ui_text(srna, "Paint Tool Slot", ""); - - prop = RNA_def_property(srna, "brush", PROP_POINTER, PROP_NONE); - RNA_def_property_flag(prop, PROP_EDITABLE); - RNA_def_property_pointer_funcs(prop, nullptr, nullptr, nullptr, "rna_Brush_mode_with_tool_poll"); - RNA_def_property_ui_text(prop, "Brush", ""); -} - static void rna_def_paint(BlenderRNA *brna) { StructRNA *srna; @@ -623,9 +512,11 @@ static void rna_def_paint(BlenderRNA *brna) /* Global Settings */ prop = RNA_def_property(srna, "brush", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_EDITABLE); - RNA_def_property_pointer_funcs(prop, nullptr, nullptr, nullptr, "rna_Brush_mode_poll"); + RNA_def_property_struct_type(prop, "Brush"); + RNA_def_property_pointer_funcs( + prop, "rna_Paint_brush_get", "rna_Paint_brush_set", nullptr, "rna_Paint_brush_poll"); RNA_def_property_ui_text(prop, "Brush", "Active Brush"); - RNA_def_property_update(prop, 0, "rna_Paint_brush_update"); + RNA_def_property_update(prop, NC_BRUSH | NA_SELECTED, nullptr); prop = RNA_def_property(srna, "brush_asset_reference", PROP_POINTER, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); @@ -634,22 +525,6 @@ static void rna_def_paint(BlenderRNA *brna) "A weak reference to the matching brush asset, used e.g. to restore " "the last used brush on file load"); - /* paint_tool_slots */ - prop = RNA_def_property(srna, "tool_slots", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, nullptr, "tool_slots", "tool_slots_len"); - RNA_def_property_struct_type(prop, "PaintToolSlot"); - /* don't dereference pointer! */ - RNA_def_property_collection_funcs(prop, - nullptr, - nullptr, - nullptr, - "rna_iterator_array_get", - nullptr, - nullptr, - nullptr, - nullptr); - RNA_def_property_ui_text(prop, "Paint Tool Slots", ""); - prop = RNA_def_property(srna, "palette", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_pointer_funcs(prop, nullptr, nullptr, nullptr, nullptr); @@ -1687,7 +1562,6 @@ void RNA_def_sculpt_paint(BlenderRNA *brna) /* *** Non-Animated *** */ RNA_define_animate_sdna(false); rna_def_paint_curve(brna); - rna_def_paint_tool_slot(brna); rna_def_paint(brna); rna_def_sculpt(brna); rna_def_uv_sculpt(brna); -- 2.30.2 From 3a7ebb7dc794a592ad24037cc85aaa25636221bf Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 22 Mar 2024 15:45:12 +0100 Subject: [PATCH 162/244] Refactor: Remove some abstraction brush assets API, not helpful anymore Pull Request: https://projects.blender.org/blender/blender/pulls/119801 --- source/blender/blenkernel/BKE_paint.hh | 15 +----- source/blender/blenkernel/intern/paint.cc | 38 +++++---------- .../blender/editors/sculpt_paint/paint_ops.cc | 48 +++++++++---------- 3 files changed, 35 insertions(+), 66 deletions(-) diff --git a/source/blender/blenkernel/BKE_paint.hh b/source/blender/blenkernel/BKE_paint.hh index 369af90c6a8..5818c5f1095 100644 --- a/source/blender/blenkernel/BKE_paint.hh +++ b/source/blender/blenkernel/BKE_paint.hh @@ -201,7 +201,7 @@ PaintMode BKE_paintmode_get_from_tool(const bToolRef *tref); Brush *BKE_paint_brush(Paint *paint); const Brush *BKE_paint_brush_for_read(const Paint *paint); -void BKE_paint_brush_set(Paint *paint, Brush *brush); +bool BKE_paint_brush_set(Paint *paint, Brush *brush); bool BKE_paint_brush_set_default(Main *bmain, Paint *paint); bool BKE_paint_brush_set_essentials(Main *bmain, Paint *paint, const char *name); @@ -209,13 +209,6 @@ void BKE_paint_brush_set_default_references(ToolSettings *ts); void BKE_paint_brush_validate(Main *bmain, Paint *paint); -/** - * Check if the given brush is a valid Brush Asset. - * - * A valid brush Asset is either an actual asset, or a local liboverride of a linked brush asset. - */ -bool BKE_paint_brush_is_valid_asset(const Brush *brush); - /** * Set the active brush of given paint struct, and store the weak asset reference to it. * \note Takes ownership of the given `weak_asset_reference`. @@ -224,12 +217,6 @@ bool BKE_paint_brush_asset_set(Paint *paint, Brush *brush, const AssetWeakReference &weak_asset_reference); -/** - * Get the active brush of given paint struct, together with its weak asset reference. - * \note Returns unset optional if the active brush is not a valid Brush Asset data.. - */ -std::optional BKE_paint_brush_asset_get(Paint *paint, Brush **r_brush); - /* Paint palette. */ Palette *BKE_paint_palette(Paint *paint); diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 8945b1cb09e..cc30bbee9dc 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -636,26 +636,27 @@ static bool paint_brush_set_from_asset_reference(Main *bmain, Paint *paint) return true; } -Brush *BKE_paint_brush(Paint *p) +Brush *BKE_paint_brush(Paint *paint) { - return (Brush *)BKE_paint_brush_for_read((const Paint *)p); + return (Brush *)BKE_paint_brush_for_read((const Paint *)paint); } -const Brush *BKE_paint_brush_for_read(const Paint *p) +const Brush *BKE_paint_brush_for_read(const Paint *paint) { - return p ? p->brush : nullptr; + return paint ? paint->brush : nullptr; } -void BKE_paint_brush_set(Paint *p, Brush *br) +bool BKE_paint_brush_set(Paint *paint, Brush *brush) { - if (p) { - p->brush = br; + if (paint == nullptr || paint->brush == brush) { + return false; + } + if (brush && (paint->runtime.ob_mode & brush->ob_mode) == 0) { + return false; } -} -bool BKE_paint_brush_is_valid_asset(const Brush *brush) -{ - return brush && ID_IS_ASSET(&brush->id); + paint->brush = brush; + return true; } static void paint_brush_asset_update(Paint &paint, @@ -688,21 +689,6 @@ bool BKE_paint_brush_asset_set(Paint *paint, return true; } -std::optional BKE_paint_brush_asset_get(Paint *paint, Brush **r_brush) -{ - Brush *brush = *r_brush = BKE_paint_brush(paint); - - if (!BKE_paint_brush_is_valid_asset(brush)) { - return {}; - } - - if (paint->brush_asset_reference) { - return paint->brush_asset_reference; - } - - return {}; -} - static void paint_brush_set_essentials_reference(Paint *paint, const char *name) { /* Set brush asset reference to a named brush in the essentials asset library. */ diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index e8e84c2b47a..eba648d2c30 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -1055,11 +1055,11 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) } /* Turn brush into asset if it isn't yet. */ - if (!BKE_paint_brush_is_valid_asset(brush)) { + if (!ID_IS_ASSET(brush)) { asset::mark_id(&brush->id); asset::generate_preview(C, &brush->id); } - BLI_assert(BKE_paint_brush_is_valid_asset(brush)); + BLI_assert(ID_IS_ASSET(brush)); asset_system::AssetLibrary *library = AS_asset_library_load( CTX_data_main(C), user_library_to_library_ref(*user_library)); @@ -1082,16 +1082,16 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) Main *asset_main = BKE_main_from_id(bmain, &brush->id); std::string final_full_asset_filepath; - const bool sucess = brush_asset_write_in_library(asset_main, - brush, - name, - filepath, - catalog_id, - catalog_simple_name, - final_full_asset_filepath, - op->reports); + const bool success = brush_asset_write_in_library(asset_main, + brush, + name, + filepath, + catalog_id, + catalog_simple_name, + final_full_asset_filepath, + op->reports); - if (!sucess) { + if (!success) { BKE_report(op->reports, RPT_ERROR, "Failed to write to asset library"); return OPERATOR_CANCELLED; } @@ -1099,7 +1099,6 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) AssetWeakReference new_brush_weak_ref = brush_asset_create_weakref_hack( user_library, final_full_asset_filepath); - /* 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( @@ -1213,7 +1212,7 @@ static bool brush_asset_delete_poll(bContext *C) } /* Asset brush, check if belongs to an editable blend file. */ - if (paint->brush_asset_reference && BKE_paint_brush_is_valid_asset(brush)) { + if (paint->brush_asset_reference && ID_IS_ASSET(brush)) { if (!asset_is_editable(*paint->brush_asset_reference)) { CTX_wm_operator_poll_msg_set(C, "Asset blend file is not editable"); return false; @@ -1236,7 +1235,7 @@ static int brush_asset_delete_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - if (paint->brush_asset_reference && BKE_paint_brush_is_valid_asset(brush)) { + if (paint->brush_asset_reference && ID_IS_ASSET(brush)) { /* Delete from asset library on disk. */ char path_buffer[FILE_MAX_LIBEXTRA]; char *filepath; @@ -1284,7 +1283,11 @@ static bool brush_asset_update_poll(bContext *C) return false; } - if (!(paint->brush_asset_reference && BKE_paint_brush_is_valid_asset(brush))) { + if ((brush->id.tag & LIB_TAG_ASSET_MAIN) == 0) { + return false; + } + + if (!(paint->brush_asset_reference && ID_IS_ASSET(brush))) { return false; } @@ -1300,12 +1303,8 @@ 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 *asset_weak_ref = - BKE_paint_brush_asset_get(paint, &brush).value_or(nullptr); - if (!asset_weak_ref) { - return OPERATOR_CANCELLED; - } + Brush *brush = BKE_paint_brush(paint); + const AssetWeakReference *asset_weak_ref = paint->brush_asset_reference; const bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_by_name( &U, asset_weak_ref->asset_library_identifier); @@ -1313,15 +1312,12 @@ static int brush_asset_update_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - // 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( asset_weak_ref, path_buffer, &filepath, nullptr, nullptr); - BLI_assert(BKE_paint_brush_is_valid_asset(brush)); + BLI_assert(ID_IS_ASSET(brush)); Main *asset_main = BKE_main_from_id(bmain, &brush->id); std::string final_full_asset_filepath; @@ -1360,7 +1356,7 @@ static bool brush_asset_revert_poll(bContext *C) return false; } - return paint->brush_asset_reference && BKE_paint_brush_is_valid_asset(brush); + return paint->brush_asset_reference && ID_IS_ASSET(brush); } static int brush_asset_revert_exec(bContext *C, wmOperator * /*op*/) -- 2.30.2 From 5ce0e4ca17da147582fbdf51d94c39421dcbf8ac Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 22 Mar 2024 20:17:54 +0100 Subject: [PATCH 163/244] Brush: Add more detailed delete confirmation for brush asset delete Until we have a trash mechanism. --- source/blender/editors/sculpt_paint/paint_ops.cc | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index eba648d2c30..8d18d92924d 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -64,6 +64,7 @@ #include "AS_asset_library.hh" #include "AS_asset_representation.hh" +#include "UI_interface_icons.hh" #include "UI_resources.hh" #include "curves_sculpt_intern.hh" @@ -1264,6 +1265,18 @@ static int brush_asset_delete_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static int brush_asset_delete_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) +{ + return WM_operator_confirm_ex( + C, + op, + IFACE_("Delete Brush Asset"), + IFACE_("Permanently delete brush asset blend file. This can't be undone."), + IFACE_("Delete"), + ALERT_ICON_WARNING, + false); +} + static void BRUSH_OT_asset_delete(wmOperatorType *ot) { ot->name = "Delete Brush Asset"; @@ -1271,7 +1284,7 @@ static void BRUSH_OT_asset_delete(wmOperatorType *ot) ot->idname = "BRUSH_OT_asset_delete"; ot->exec = brush_asset_delete_exec; - ot->invoke = WM_operator_confirm; + ot->invoke = brush_asset_delete_invoke; ot->poll = brush_asset_delete_poll; } -- 2.30.2 From b716ea68494043a0c20c1a1977b6d7555d259db1 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 22 Mar 2024 20:39:39 +0100 Subject: [PATCH 164/244] Fix: Crash saving brush asset with empty catalog --- source/blender/editors/sculpt_paint/paint_ops.cc | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 8d18d92924d..5b34087d119 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -1072,10 +1072,16 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) /* Add asset to catalog. */ char catalog_path[MAX_NAME]; RNA_string_get(op->ptr, "catalog_path", catalog_path); - const asset_system::AssetCatalog &catalog = asset_library_ensure_catalogs_in_path(*library, - catalog_path); - const asset_system::CatalogID catalog_id = catalog.catalog_id; - const std::string catalog_simple_name = catalog.simple_name; + + std::optional catalog_id; + std::optional catalog_simple_name; + + if (catalog_path[0]) { + const asset_system::AssetCatalog &catalog = asset_library_ensure_catalogs_in_path( + *library, catalog_path); + catalog_id = catalog.catalog_id; + catalog_simple_name = catalog.simple_name; + } library->catalog_service().write_to_disk(filepath); -- 2.30.2 From 99b9650f87cca5377035134dad736b16f6a36c37 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 25 Mar 2024 14:34:44 +0100 Subject: [PATCH 165/244] Brush Assets: Make revert and delete operators affect dependencies This is done by reloading or freeing the entire main of the asset, and remapping pointers for all datablocks contained in it. This remapping is primarily needed for the user interface to point to the same datablocks. Pull Request: https://projects.blender.org/blender/blender/pulls/119806 --- source/blender/blenkernel/BKE_asset.hh | 4 +- .../blenkernel/intern/asset_weak_reference.cc | 112 ++++++++++++++++-- source/blender/blenkernel/intern/blender.cc | 2 +- .../blender/editors/sculpt_paint/paint_ops.cc | 26 ++-- 4 files changed, 115 insertions(+), 29 deletions(-) diff --git a/source/blender/blenkernel/BKE_asset.hh b/source/blender/blenkernel/BKE_asset.hh index 6e663a4e459..537fe00eca4 100644 --- a/source/blender/blenkernel/BKE_asset.hh +++ b/source/blender/blenkernel/BKE_asset.hh @@ -86,7 +86,9 @@ void BKE_asset_weak_reference_read(BlendDataReader *reader, AssetWeakReference * * currently used for brush assets and their dependencies. */ Main *BKE_asset_weak_reference_main(const ID *id); -void BKE_asset_weak_reference_main_free(); ID *BKE_asset_weak_reference_ensure(Main &global_main, ID_Type id_type, const AssetWeakReference &weak_ref); +void BKE_asset_weak_reference_main_reload(Main &global_main, Main &main); +void BKE_asset_weak_reference_main_free(Main &global_main, Main &asset_main); +void BKE_asset_weak_reference_main_free_all(); diff --git a/source/blender/blenkernel/intern/asset_weak_reference.cc b/source/blender/blenkernel/intern/asset_weak_reference.cc index 2accc9429d8..a8ecb5eac86 100644 --- a/source/blender/blenkernel/intern/asset_weak_reference.cc +++ b/source/blender/blenkernel/intern/asset_weak_reference.cc @@ -9,7 +9,9 @@ #include #include +#include "BLI_path_util.h" #include "BLI_string.h" +#include "BLI_vector.hh" #include "DNA_space_types.h" @@ -20,10 +22,9 @@ #include "BKE_blendfile_link_append.hh" #include "BKE_idtype.hh" #include "BKE_lib_id.hh" +#include "BKE_lib_remap.hh" #include "BKE_main.hh" -#include "BLI_vector.hh" - #include "BLO_read_write.hh" #include "BLO_readfile.hh" @@ -132,12 +133,9 @@ void BKE_asset_weak_reference_read(BlendDataReader *reader, AssetWeakReference * /* Main database for storing assets that are weak referenced. * * This avoids mixing asset datablocks in the regular main, which leads to naming conflicts and - * confusing user interface. - * - * TODO: Heavily WIP code. */ + * confusing user interface. */ struct AssetWeakReferenceMain { - /* TODO: not sure if this is the best unique identifier. */ std::string filepath; Main *main; @@ -145,12 +143,22 @@ struct AssetWeakReferenceMain { : filepath(std::move(filepath)), main(BKE_main_new()) { main->is_asset_weak_reference_main = true; + BLI_assert(!BLI_path_is_rel(filepath.c_str())); } AssetWeakReferenceMain(const AssetWeakReferenceMain &) = delete; AssetWeakReferenceMain(AssetWeakReferenceMain &&other) : filepath(std::exchange(other.filepath, "")), main(std::exchange(other.main, nullptr)) { } + AssetWeakReferenceMain &operator=(AssetWeakReferenceMain &&other) + { + if (this == &other) { + return *this; + } + this->filepath = std::exchange(other.filepath, ""); + this->main = std::exchange(other.main, nullptr); + return *this; + } ~AssetWeakReferenceMain() { @@ -158,8 +166,70 @@ struct AssetWeakReferenceMain { BKE_main_free(main); } } + + void reload(Main &global_main); + void clear_users(Main &global_main); }; +void AssetWeakReferenceMain::reload(Main &global_main) +{ + Main *old_main = this->main; + this->main = BKE_main_new(); + this->main->is_asset_weak_reference_main = true; + + /* Fill fresh main database with same datablock as before. */ + LibraryLink_Params lapp_params{}; + lapp_params.bmain = this->main; + 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, this->filepath.c_str(), nullptr); + + /* Requests all existing datablocks to be appended again. */ + ID *old_id; + FOREACH_MAIN_ID_BEGIN (old_main, old_id) { + ID_Type old_id_code = GS(old_id->name); + if (BKE_idtype_idcode_is_linkable(old_id_code)) { + BlendfileLinkAppendContextItem *lapp_item = BKE_blendfile_link_append_context_item_add( + lapp_context, old_id->name + 2, old_id_code, nullptr); + BKE_blendfile_link_append_context_item_library_index_enable(lapp_context, lapp_item, 0); + } + } + FOREACH_MAIN_ID_END; + + BKE_blendfile_link(lapp_context, nullptr); + BKE_blendfile_append(lapp_context, nullptr); + + BKE_blendfile_link_append_context_free(lapp_context); + + BKE_main_id_tag_all(this->main, LIB_TAG_ASSET_MAIN, true); + + /* Remap old to new. */ + bke::id::IDRemapper mappings; + FOREACH_MAIN_ID_BEGIN (old_main, old_id) { + ID *new_id = BKE_libblock_find_name(this->main, GS(old_id->name), old_id->name + 2); + mappings.add(old_id, new_id); + } + FOREACH_MAIN_ID_END; + BKE_libblock_remap_multiple(&global_main, mappings, 0); + + /* Free old database. */ + BKE_main_free(old_main); +} + +void AssetWeakReferenceMain::clear_users(Main &global_main) +{ + /* Remap old to null pointer. */ + bke::id::IDRemapper mappings; + ID *old_id; + FOREACH_MAIN_ID_BEGIN (this->main, old_id) { + mappings.add(old_id, nullptr); + } + FOREACH_MAIN_ID_END; + BKE_libblock_remap_multiple(&global_main, mappings, 0); +} + static Vector &get_weak_reference_mains() { static Vector mains; @@ -196,7 +266,34 @@ static Main &asset_weak_reference_main_ensure(const StringRef filepath) return *get_weak_reference_mains().last().main; } -void BKE_asset_weak_reference_main_free() +void BKE_asset_weak_reference_main_reload(Main &global_main, Main &asset_main) +{ + for (AssetWeakReferenceMain &weak_ref_main : get_weak_reference_mains()) { + if (weak_ref_main.main == &asset_main) { + weak_ref_main.reload(global_main); + return; + } + } + + BLI_assert_unreachable(); +} + +void BKE_asset_weak_reference_main_free(Main &global_main, Main &asset_main) +{ + int index = 0; + for (AssetWeakReferenceMain &weak_ref_main : get_weak_reference_mains()) { + if (weak_ref_main.main == &asset_main) { + weak_ref_main.clear_users(global_main); + get_weak_reference_mains().remove(index); + return; + } + index++; + } + + BLI_assert_unreachable(); +} + +void BKE_asset_weak_reference_main_free_all() { get_weak_reference_mains().clear_and_shrink(); } @@ -247,7 +344,6 @@ ID *BKE_asset_weak_reference_ensure(Main &global_main, BKE_blendfile_link_append_context_free(lapp_context); - /* TODO: only do for new ones? */ 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. */ diff --git a/source/blender/blenkernel/intern/blender.cc b/source/blender/blenkernel/intern/blender.cc index 5982589d583..4a79b0c390f 100644 --- a/source/blender/blenkernel/intern/blender.cc +++ b/source/blender/blenkernel/intern/blender.cc @@ -64,7 +64,7 @@ void BKE_blender_free() /* Free separate data-bases after #BKE_blender_globals_clear in case any data-blocks in the * global main use pointers to asset main data-blocks when they're freed. That generally * shouldn't happen but it's better to be safe. */ - BKE_asset_weak_reference_main_free(); + BKE_asset_weak_reference_main_free_all(); if (G.log.file != nullptr) { fclose(static_cast(G.log.file)); diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 5b34087d119..18e7b2020ac 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -34,7 +34,6 @@ #include "BKE_context.hh" #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" @@ -1255,11 +1254,8 @@ static int brush_asset_delete_exec(bContext *C, wmOperator *op) } if (asset_main != bmain) { - // TODO: hack: no pointer should exist, should do runtime lookup - BKE_libblock_remap(bmain, brush, nullptr, 0); + BKE_asset_weak_reference_main_free(*bmain, *asset_main); } - BKE_id_delete(asset_main, brush); - // TODO: delete whole asset main if empty? refresh_asset_library(C, *library); @@ -1368,14 +1364,13 @@ static void BRUSH_OT_asset_update(wmOperatorType *ot) static bool brush_asset_revert_poll(bContext *C) { - /* TODO: check if there is anything to revert? */ Paint *paint = BKE_paint_get_active_from_context(C); Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr; if (paint == nullptr || brush == nullptr) { return false; } - return paint->brush_asset_reference && ID_IS_ASSET(brush); + return paint->brush_asset_reference && (brush->id.tag & LIB_TAG_ASSET_MAIN); } static int brush_asset_revert_exec(bContext *C, wmOperator * /*op*/) @@ -1385,19 +1380,12 @@ static int brush_asset_revert_exec(bContext *C, wmOperator * /*op*/) Brush *brush = BKE_paint_brush(paint); Main *asset_main = BKE_main_from_id(bmain, &brush->id); - // 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); + /* Reload entire main, including texture dependencies. This relies on there + * being only a single brush asset per blend file. */ + BKE_asset_weak_reference_main_reload(*bmain, *asset_main); - Brush *new_brush = reinterpret_cast( - 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); - BKE_id_delete(asset_main, brush); - - WM_main_add_notifier(NC_BRUSH | NA_EDITED, new_brush); + WM_main_add_notifier(NC_BRUSH | NA_EDITED, nullptr); + WM_main_add_notifier(NC_TEXTURE | ND_NODES, nullptr); return OPERATOR_FINISHED; } -- 2.30.2 From 3e53bcfb997e63f19cfa39bb60623e8b9783faa3 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 27 Mar 2024 09:36:13 -0400 Subject: [PATCH 166/244] Fix: Build error from mistake in versioning code merge --- .../blenloader/intern/versioning_400.cc | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/source/blender/blenloader/intern/versioning_400.cc b/source/blender/blenloader/intern/versioning_400.cc index 8aa1f285a80..2319a1fdf3c 100644 --- a/source/blender/blenloader/intern/versioning_400.cc +++ b/source/blender/blenloader/intern/versioning_400.cc @@ -3148,19 +3148,18 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 13)) { update_paint_modes_for_brush_assets(*bmain); } -} -/** - * Always bump subversion in BKE_blender_version.h when adding versioning - * code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check. - * - * \note Keep this message at the bottom of the function. - */ + /** + * Always bump subversion in BKE_blender_version.h when adding versioning + * code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check. + * + * \note Keep this message at the bottom of the function. + */ -/* Always run this versioning; meshes are written with the legacy format which always needs to - * be converted to the new format on file load. Can be moved to a subversion check in a larger - * breaking release. */ -LISTBASE_FOREACH (Mesh *, mesh, &bmain->meshes) { - blender::bke::mesh_sculpt_mask_to_generic(*mesh); -} + /* Always run this versioning; meshes are written with the legacy format which always needs to + * be converted to the new format on file load. Can be moved to a subversion check in a larger + * breaking release. */ + LISTBASE_FOREACH (Mesh *, mesh, &bmain->meshes) { + blender::bke::mesh_sculpt_mask_to_generic(*mesh); + } } -- 2.30.2 From 2d454111083fc956270c05797cb58694c3b12cd6 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 27 Mar 2024 09:43:51 -0400 Subject: [PATCH 167/244] Format --- .../startup/bl_ui/space_toolsystem_toolbar.py | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/scripts/startup/bl_ui/space_toolsystem_toolbar.py index a8d1fe87fe3..515165949b4 100644 --- a/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -1818,23 +1818,23 @@ class _defs_paint_grease_pencil: # # @staticmethod # def generate_from_brushes(context): - # # Though `data_block` is conceptually unnecessary with a single brush tool, - # # it's still used because many areas assume that brush tools have it set #bToolRef. - # tool = None - # if context: - # brush = context.tool_settings.gpencil_paint.brush - # if brush: - # tool = brush.gpencil_tool - # return [ - # ToolDef.from_dict( - # dict( - # idname="builtin.brush", - # label="Brush", - # icon="brush.sculpt.paint", - # data_block=tool - # ) - # ) - # ] + # # Though `data_block` is conceptually unnecessary with a single brush tool, + # # it's still used because many areas assume that brush tools have it set #bToolRef. + # tool = None + # if context: + # brush = context.tool_settings.gpencil_paint.brush + # if brush: + # tool = brush.gpencil_tool + # return [ + # ToolDef.from_dict( + # dict( + # idname="builtin.brush", + # label="Brush", + # icon="brush.sculpt.paint", + # data_block=tool + # ) + # ) + # ] @ToolDef.from_fn def draw(): -- 2.30.2 From 15a886ddab852cd780fb557b1fd2d96b7f502eb1 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 27 Mar 2024 09:45:34 -0400 Subject: [PATCH 168/244] Brush Assets: Add back brush selection shortcuts --- .../keyconfig/keymap_data/blender_default.py | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/scripts/presets/keyconfig/keymap_data/blender_default.py b/scripts/presets/keyconfig/keymap_data/blender_default.py index e36d4467db0..cc8f992b1cb 100644 --- a/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -5518,6 +5518,37 @@ 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), + # Brushes + ("brush.asset_select", {"type": 'V', "value": 'PRESS'}, + {"properties": [("asset_library_type", 'ESSENTIALS'), + ("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Draw")]}), + ("brush.asset_select", {"type": 'S', "value": 'PRESS'}, + {"properties": [("asset_library_type", 'ESSENTIALS'), + ("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Smooth")]}), + ("brush.asset_select", {"type": 'P', "value": 'PRESS'}, + {"properties": [("asset_library_type", 'ESSENTIALS'), + ("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Pinch/Magnify")]}), + ("brush.asset_select", {"type": 'I', "value": 'PRESS'}, + {"properties": [("asset_library_type", 'ESSENTIALS'), + ("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Inflate/Deflate")]}), + ("brush.asset_select", {"type": 'G', "value": 'PRESS'}, + {"properties": [("asset_library_type", 'ESSENTIALS'), + ("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Grab")]}), + ("brush.asset_select", {"type": 'T', "value": 'PRESS', "shift": True}, + {"properties": [("asset_library_type", 'ESSENTIALS'), + ("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Scrape/Fill")]}), + ("brush.asset_select", {"type": 'C', "value": 'PRESS'}, + {"properties": [("asset_library_type", 'ESSENTIALS'), + ("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Clay Strips")]}), + ("brush.asset_select", {"type": 'C', "value": 'PRESS', "shift": True}, + {"properties": [("asset_library_type", 'ESSENTIALS'), + ("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Crease")]}), + ("brush.asset_select", {"type": 'K', "value": 'PRESS'}, + {"properties": [("asset_library_type", 'ESSENTIALS'), + ("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Snake Hook")]}), + ("brush.asset_select", {"type": 'M', "value": 'PRESS'}, + {"properties": [("asset_library_type", 'ESSENTIALS'), + ("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Mask")]}), ]) # Lasso Masking. -- 2.30.2 From 7142ec1695d153477fcb996d18f281feff899bae Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 29 Mar 2024 17:01:16 +0100 Subject: [PATCH 169/244] Brush Assets: Remove local brushes from default startup file There was a catch I didn't expect with the outliner treestore, but solving that this way seems fine anyway. Pull Request: https://projects.blender.org/blender/blender/pulls/120039 --- .../blenloader/intern/versioning_defaults.cc | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/source/blender/blenloader/intern/versioning_defaults.cc b/source/blender/blenloader/intern/versioning_defaults.cc index 7df8b08fd12..1783d56abc8 100644 --- a/source/blender/blenloader/intern/versioning_defaults.cc +++ b/source/blender/blenloader/intern/versioning_defaults.cc @@ -21,6 +21,7 @@ #include "BLI_math_rotation.h" #include "BLI_math_vector.h" #include "BLI_math_vector_types.hh" +#include "BLI_mempool.h" #include "BLI_string.h" #include "BLI_utildefines.h" @@ -904,4 +905,26 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) } } } + + { + /* Remove default brushes replaced by assets. Also remove outliner `treestore` that may point + * to brushes. Normally the treestore is updated properly but it doesn't seem to update during + * versioning code. It's not helpful anyway. */ + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, space_link, &area->spacedata) { + if (space_link->spacetype == SPACE_OUTLINER) { + SpaceOutliner *space_outliner = reinterpret_cast(space_link); + if (space_outliner->treestore) { + BLI_mempool_destroy(space_outliner->treestore); + space_outliner->treestore = nullptr; + } + } + } + } + } + LISTBASE_FOREACH_MUTABLE (Brush *, brush, &bmain->brushes) { + BKE_id_delete(bmain, brush); + } + } } -- 2.30.2 From ba71fa3909a7f4d02d545251a4ce53ca755764a3 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 29 Mar 2024 17:23:28 +0100 Subject: [PATCH 170/244] Refactor: Move asset editing code into own file and refactor * Move from paint_ops.cc and asset_weak_reference.cc into asset_edit.cc * Rename AssetWeakReferenceMain to AssetEditBlend * Change some functions to be based on ID instead of main pointer * Make most of save as and update code no longer brush specific Pull Request: https://projects.blender.org/blender/blender/pulls/119888 --- source/blender/blenkernel/BKE_asset.hh | 12 - source/blender/blenkernel/BKE_asset_edit.hh | 69 +++ source/blender/blenkernel/CMakeLists.txt | 2 + .../blender/blenkernel/intern/asset_edit.cc | 518 ++++++++++++++++++ .../blenkernel/intern/asset_weak_reference.cc | 222 -------- source/blender/blenkernel/intern/blender.cc | 4 +- source/blender/blenkernel/intern/main.cc | 4 +- source/blender/blenkernel/intern/paint.cc | 5 +- .../blender/editors/sculpt_paint/paint_ops.cc | 248 +-------- 9 files changed, 619 insertions(+), 465 deletions(-) create mode 100644 source/blender/blenkernel/BKE_asset_edit.hh create mode 100644 source/blender/blenkernel/intern/asset_edit.cc diff --git a/source/blender/blenkernel/BKE_asset.hh b/source/blender/blenkernel/BKE_asset.hh index 537fe00eca4..bc8fd7a249f 100644 --- a/source/blender/blenkernel/BKE_asset.hh +++ b/source/blender/blenkernel/BKE_asset.hh @@ -80,15 +80,3 @@ void BKE_asset_metadata_read(BlendDataReader *reader, AssetMetaData *asset_data) void BKE_asset_weak_reference_write(BlendWriter *writer, const AssetWeakReference *weak_ref); void BKE_asset_weak_reference_read(BlendDataReader *reader, AssetWeakReference *weak_ref); - -/** - * Database of assets that are weakly reference by scene data, - * currently used for brush assets and their dependencies. - */ -Main *BKE_asset_weak_reference_main(const ID *id); -ID *BKE_asset_weak_reference_ensure(Main &global_main, - ID_Type id_type, - const AssetWeakReference &weak_ref); -void BKE_asset_weak_reference_main_reload(Main &global_main, Main &main); -void BKE_asset_weak_reference_main_free(Main &global_main, Main &asset_main); -void BKE_asset_weak_reference_main_free_all(); diff --git a/source/blender/blenkernel/BKE_asset_edit.hh b/source/blender/blenkernel/BKE_asset_edit.hh new file mode 100644 index 00000000000..e78805b8dfb --- /dev/null +++ b/source/blender/blenkernel/BKE_asset_edit.hh @@ -0,0 +1,69 @@ +/* SPDX-FileCopyrightText: 2024 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup bke + */ + +#pragma once + +/** + * Editing of datablocks from asset libraries, separate from the current open + * blend file. + * + * Each asset blend file is loaded into a separate main database, including the + * asset datablocks and their dependencies. These datablocks are all tagged with + * LIB_TAG_ASSET_MAIN. These can not be linked with other datablocks in the + * current blend file. + * + * For editable assets in user asset libraries, each asset is stored in its own + * blend file. This way the blend file can be easily saved, reloaded and deleted. + * + * This mechanism is currently only used for brush assets. + */ + +#include +#include + +#include "AS_asset_catalog.hh" + +#include "DNA_ID_enums.h" + +struct bUserAssetLibrary; +struct AssetWeakReference; +struct ID; +struct Main; +struct ReportList; + +namespace blender::bke { + +/** Get datablock from weak reference, loading the blend file as needed. */ +ID *asset_edit_id_from_weak_reference(Main &global_main, + ID_Type id_type, + const AssetWeakReference &weak_ref); + +/** Get main database that a given asset datablock corresponds to. */ +Main *asset_edit_main(const ID *id); + +/** Asset editing operations. */ + +bool asset_edit_id_is_editable(const ID *id); + +std::optional asset_edit_id_save_as( + Main &global_main, + const ID *id, + const char *name, + std::optional catalog_id, + std::optional catalog_simple_name, + const bUserAssetLibrary *user_library, + ReportList *reports); + +bool asset_edit_id_save(Main &global_main, const ID *id, ReportList *reports); +bool asset_edit_id_revert(Main &global_main, const ID *id, ReportList *reports); +bool asset_edit_id_delete(Main &global_main, const ID *id, ReportList *reports); + +/** Clean up on exit. */ +void asset_edit_main_free_all(); + +} // namespace blender::bke diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 70f946eb6be..c30974496ab 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -65,6 +65,7 @@ set(SRC intern/armature_update.cc intern/asset.cc intern/asset_weak_reference.cc + intern/asset_edit.cc intern/attribute.cc intern/attribute_access.cc intern/attribute_math.cc @@ -333,6 +334,7 @@ set(SRC BKE_appdir.hh BKE_armature.hh BKE_asset.hh + BKE_asset_edit.hh BKE_attribute.h BKE_attribute.hh BKE_attribute_math.hh diff --git a/source/blender/blenkernel/intern/asset_edit.cc b/source/blender/blenkernel/intern/asset_edit.cc new file mode 100644 index 00000000000..c23d07fcb71 --- /dev/null +++ b/source/blender/blenkernel/intern/asset_edit.cc @@ -0,0 +1,518 @@ +/* SPDX-FileCopyrightText: 2024 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup bke + */ + +#include +#include + +#include "BLI_fileops.h" +#include "BLI_path_util.h" +#include "BLI_string.h" +#include "BLI_vector.hh" + +#include "DNA_space_types.h" + +#include "AS_asset_identifier.hh" +#include "AS_asset_library.hh" + +#include "BKE_asset.hh" +#include "BKE_asset_edit.hh" +#include "BKE_blendfile.hh" +#include "BKE_blendfile_link_append.hh" +#include "BKE_idtype.hh" +#include "BKE_lib_id.hh" +#include "BKE_lib_remap.hh" +#include "BKE_main.hh" +#include "BKE_preferences.h" +#include "BKE_report.hh" + +#include "BLO_read_write.hh" +#include "BLO_readfile.hh" +#include "BLO_writefile.hh" + +#include "DNA_asset_types.h" + +#include "MEM_guardedalloc.h" + +namespace blender::bke { + +/** + * Asset library blend file, with editable contents. + */ +struct AssetEditBlend { + std::string filepath; + Main *main; + bool is_editable = false; + + AssetEditBlend(const std::string &filepath); + ~AssetEditBlend(); + + AssetEditBlend(const AssetEditBlend &) = delete; + AssetEditBlend(AssetEditBlend &&other); + AssetEditBlend &operator=(AssetEditBlend &&other); + + ID *ensure_id(ID_Type id_type, const char *asset_name); + void reload(Main &global_main); + void clear_users(Main &global_main); +}; + +AssetEditBlend::AssetEditBlend(const std::string &filepath) + : filepath(std::move(filepath)), main(BKE_main_new()) +{ + this->main->is_asset_weak_reference_main = true; + BLI_assert(!BLI_path_is_rel(filepath.c_str())); + + /* Fairly simple check based on filepath only. + * - Ends with `.asset.bend` extensions. + * - Is located in user asset library. + * + * TODO? + * - Check file contents. + * - Check file is writable. + */ + this->is_editable = StringRef(filepath).endswith(BLENDER_ASSET_FILE_SUFFIX) && + BKE_preferences_asset_library_containing_path(&U, filepath.c_str()); +} + +AssetEditBlend::~AssetEditBlend() +{ + if (main) { + BKE_main_free(main); + } +} + +AssetEditBlend::AssetEditBlend(AssetEditBlend &&other) + : filepath(std::exchange(other.filepath, "")), main(std::exchange(other.main, nullptr)) +{ +} +AssetEditBlend &AssetEditBlend::operator=(AssetEditBlend &&other) +{ + if (this == &other) { + return *this; + } + this->filepath = std::exchange(other.filepath, ""); + this->main = std::exchange(other.main, nullptr); + return *this; +} + +ID *AssetEditBlend::ensure_id(const ID_Type id_type, const char *asset_name) +{ + /* Check if we have the asset already. */ + ID *local_asset = BKE_libblock_find_name(this->main, id_type, asset_name); + if (local_asset) { + BLI_assert(ID_IS_ASSET(local_asset)); + return local_asset; + } + + /* Load asset from asset library. */ + LibraryLink_Params lapp_params{}; + lapp_params.bmain = this->main; + 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, filepath.c_str(), nullptr); + + BlendfileLinkAppendContextItem *lapp_item = BKE_blendfile_link_append_context_item_add( + 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); + 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); + + BKE_main_id_tag_all(this->main, 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)); + + return local_asset; +} + +static std::string asset_root_path_for_save(const bUserAssetLibrary &user_library, + const ID_Type id_type) +{ + BLI_assert(user_library.dirpath[0] != '\0'); + + char libpath[FILE_MAX]; + BLI_strncpy(libpath, user_library.dirpath, sizeof(libpath)); + BLI_path_slash_native(libpath); + BLI_path_normalize(libpath); + + /* Capitalize folder name. Ideally this would already available in + * the type info to work correctly with multiple words. */ + const IDTypeInfo *id_type_info = BKE_idtype_get_info_from_idcode(id_type); + std::string name = id_type_info->name_plural; + name[0] = BLI_toupper_ascii(name[0]); + + return std::string(libpath) + SEP + "Saved" + SEP + name; +} + +static std::string asset_blendfile_path_for_save(ReportList *reports, + const bUserAssetLibrary &user_library, + const StringRefNull base_name, + const ID_Type id_type) +{ + std::string root_path = asset_root_path_for_save(user_library, id_type); + BLI_assert(!root_path.empty()); + + if (!BLI_dir_create_recursive(root_path.c_str())) { + BKE_report(reports, RPT_ERROR, "Failed to create asset library directory to save brush"); + return ""; + } + + char base_name_filesafe[FILE_MAXFILE]; + BLI_strncpy(base_name_filesafe, base_name.c_str(), sizeof(base_name_filesafe)); + BLI_path_make_safe_filename(base_name_filesafe); + + const std::string filepath = root_path + SEP + base_name_filesafe + BLENDER_ASSET_FILE_SUFFIX; + if (!BLI_is_file(filepath.c_str())) { + return filepath; + } + + for (int i = 1;; i++) { + const std::string filepath = root_path + SEP + base_name_filesafe + "_" + std::to_string(i++) + + BLENDER_ASSET_FILE_SUFFIX; + if (!BLI_is_file((filepath.c_str()))) { + return filepath; + } + } + + return ""; +} + +static bool asset_write_in_library(Main *bmain, + const ID *id_const, + const char *name, + const StringRefNull filepath, + const std::optional catalog, + const std::optional catalog_simple_name, + std::string &final_full_file_path, + ReportList *reports) +{ + /* XXX + * FIXME + * + * This code is _pure evil_. It does in-place manipulation on IDs in global Main database, + * temporarilly remove them and add them back... + * + * Use it as-is for now (in a similar way as python API or copy-to-buffer works). Nut the whole + * 'BKE_blendfile_write_partial' code needs to be completely refactored. + * + * Ideas: + * - Have `BKE_blendfile_write_partial_begin` return a new temp Main. + * - Replace `BKE_blendfile_write_partial_tag_ID` by API to add IDs to this temp Main. + * + This should _duplicate_ the ID, not remove the original one from the source Main! + * - Have API to automatically also duplicate dependencies into temp Main. + * + Have options to e.g. make all duplicated IDs 'local' (i.e. remove their library data). + * - `BKE_blendfile_write_partial` then simply write the given temp main. + * - `BKE_blendfile_write_partial_end` frees the temp Main. + */ + + ID *id = const_cast(id_const); + + const short prev_flag = id->flag; + const int prev_tag = id->tag; + const int prev_us = id->us; + const std::string prev_name = id->name + 2; + IDOverrideLibrary *prev_liboverride = id->override_library; + AssetMetaData *asset_data = id->asset_data; + const int write_flags = 0; /* Could use #G_FILE_COMPRESS ? */ + const eBLO_WritePathRemap remap_mode = BLO_WRITE_PATH_REMAP_RELATIVE; + + BKE_blendfile_write_partial_begin(bmain); + + id->flag |= LIB_FAKEUSER; + id->tag &= ~LIB_TAG_RUNTIME; + id->us = 1; + BLI_strncpy(id->name + 2, name, sizeof(id->name) - 2); + if (!ID_IS_ASSET(id)) { + id->asset_data = id->override_library->reference->asset_data; + } + id->override_library = nullptr; + + if (catalog) { + id->asset_data->catalog_id = *catalog; + } + if (catalog_simple_name) { + STRNCPY(id->asset_data->catalog_simple_name, catalog_simple_name->c_str()); + } + + BKE_blendfile_write_partial_tag_ID(id, true); + + /* TODO: check overwriting existing file. */ + /* TODO: ensure filepath contains only valid characters for file system. */ + const bool sucess = BKE_blendfile_write_partial( + bmain, filepath.c_str(), write_flags, remap_mode, reports); + + if (sucess) { + final_full_file_path = std::string(filepath) + SEP + "Brush" + SEP + name; + } + + BKE_blendfile_write_partial_end(bmain); + + BKE_blendfile_write_partial_tag_ID(id, false); + id->flag = prev_flag; + id->tag = prev_tag; + id->us = prev_us; + BLI_strncpy(id->name + 2, prev_name.c_str(), sizeof(id->name) - 2); + id->override_library = prev_liboverride; + id->asset_data = asset_data; + + return sucess; +} + +void AssetEditBlend::reload(Main &global_main) +{ + Main *old_main = this->main; + this->main = BKE_main_new(); + this->main->is_asset_weak_reference_main = true; + + /* Fill fresh main database with same datablock as before. */ + LibraryLink_Params lapp_params{}; + lapp_params.bmain = this->main; + 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, this->filepath.c_str(), nullptr); + + /* Requests all existing datablocks to be appended again. */ + ID *old_id; + FOREACH_MAIN_ID_BEGIN (old_main, old_id) { + ID_Type old_id_code = GS(old_id->name); + if (BKE_idtype_idcode_is_linkable(old_id_code)) { + BlendfileLinkAppendContextItem *lapp_item = BKE_blendfile_link_append_context_item_add( + lapp_context, old_id->name + 2, old_id_code, nullptr); + BKE_blendfile_link_append_context_item_library_index_enable(lapp_context, lapp_item, 0); + } + } + FOREACH_MAIN_ID_END; + + BKE_blendfile_link(lapp_context, nullptr); + BKE_blendfile_append(lapp_context, nullptr); + + BKE_blendfile_link_append_context_free(lapp_context); + + BKE_main_id_tag_all(this->main, LIB_TAG_ASSET_MAIN, true); + + /* Remap old to new. */ + bke::id::IDRemapper mappings; + FOREACH_MAIN_ID_BEGIN (old_main, old_id) { + ID *new_id = BKE_libblock_find_name(this->main, GS(old_id->name), old_id->name + 2); + mappings.add(old_id, new_id); + } + FOREACH_MAIN_ID_END; + BKE_libblock_remap_multiple(&global_main, mappings, 0); + + /* Free old database. */ + BKE_main_free(old_main); +} + +void AssetEditBlend::clear_users(Main &global_main) +{ + /* Remap old to null pointer. */ + bke::id::IDRemapper mappings; + ID *old_id; + FOREACH_MAIN_ID_BEGIN (this->main, old_id) { + mappings.add(old_id, nullptr); + } + FOREACH_MAIN_ID_END; + BKE_libblock_remap_multiple(&global_main, mappings, 0); +} + +/** + * Public API + */ + +static Vector &asset_edit_blend_get_all() +{ + static Vector mains; + return mains; +} + +static AssetEditBlend *asset_edit_blend_from_id(const ID *id) +{ + BLI_assert(id->tag & LIB_TAG_ASSET_MAIN); + + for (AssetEditBlend &asset_blend : asset_edit_blend_get_all()) { + /* TODO: Look into make this whole thing more efficient. */ + ListBase *lb = which_libbase(asset_blend.main, GS(id->name)); + LISTBASE_FOREACH (ID *, other_id, lb) { + if (id == other_id) { + return &asset_blend; + } + } + } + + BLI_assert_unreachable(); + return nullptr; +} + +Main *asset_edit_main(const ID *id) +{ + const AssetEditBlend *asset_blend = asset_edit_blend_from_id(id); + return (asset_blend) ? asset_blend->main : nullptr; +} + +static AssetEditBlend &asset_edit_blend_file_ensure(const StringRef filepath) +{ + for (AssetEditBlend &asset_blend : asset_edit_blend_get_all()) { + if (asset_blend.filepath == filepath) { + return asset_blend; + } + } + + asset_edit_blend_get_all().append_as(filepath); + return asset_edit_blend_get_all().last(); +} + +std::optional asset_edit_id_save_as(Main &global_main, + const ID *id, + const char *name, + std::optional catalog_id, + std::optional catalog_simple_name, + const bUserAssetLibrary *user_library, + ReportList *reports) +{ + const std::string filepath = asset_blendfile_path_for_save( + reports, *user_library, name, GS(id->name)); + + /* Save to asset library. */ + Main *asset_main = BKE_main_from_id(&global_main, id); + + std::string final_full_asset_filepath; + const bool success = asset_write_in_library(asset_main, + id, + name, + filepath, + catalog_id, + catalog_simple_name, + final_full_asset_filepath, + reports); + + if (!success) { + BKE_report(reports, RPT_ERROR, "Failed to write to asset library"); + return std::nullopt; + } + + BKE_reportf(reports, RPT_INFO, "Saved \"%s\"", filepath.c_str()); + + return final_full_asset_filepath; +} + +bool asset_edit_id_save(Main & /*global_main*/, const ID *id, ReportList *reports) +{ + AssetEditBlend *asset_blend = asset_edit_blend_from_id(id); + if (asset_blend == nullptr) { + return false; + } + + std::string final_full_asset_filepath; + const bool success = asset_write_in_library(asset_blend->main, + id, + id->name + 2, + asset_blend->filepath.c_str(), + std::nullopt, + std::nullopt, + final_full_asset_filepath, + reports); + + if (!success) { + BKE_report(reports, RPT_ERROR, "Failed to write to asset library"); + return false; + } + + return true; +} + +bool asset_edit_id_revert(Main &global_main, const ID *id, ReportList * /*reports*/) +{ + AssetEditBlend *asset_blend = asset_edit_blend_from_id(id); + if (asset_blend == nullptr) { + return false; + } + + /* Reload entire main, including texture dependencies. This relies on there + * being only a single brush asset per blend file. */ + asset_blend->reload(global_main); + + return true; +} + +bool asset_edit_id_delete(Main &global_main, const ID *id, ReportList *reports) +{ + AssetEditBlend *asset_blend = asset_edit_blend_from_id(id); + if (asset_blend == nullptr) { + return false; + } + + if (BLI_delete(asset_blend->filepath.c_str(), false, false) != 0) { + BKE_report(reports, RPT_ERROR, "Failed to delete asset library file"); + return false; + } + + asset_blend->clear_users(global_main); + + int index = 0; + for (AssetEditBlend &asset_blend_iter : asset_edit_blend_get_all()) { + if (&asset_blend_iter == asset_blend) { + asset_edit_blend_get_all().remove(index); + break; + } + index++; + } + + return true; +} + +ID *asset_edit_id_from_weak_reference(Main &global_main, + const ID_Type id_type, + const AssetWeakReference &weak_ref) +{ + 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; + } + + BLI_assert(asset_name != nullptr); + + /* Find asset in current blend file. */ + if (asset_lib_path == nullptr) { + ID *local_asset = BKE_libblock_find_name(&global_main, id_type, asset_name); + BLI_assert(local_asset == nullptr || ID_IS_ASSET(local_asset)); + return local_asset; + } + + /* If weak reference resolves to a null library path, assume we are in local asset case. */ + AssetEditBlend &asset_blend = asset_edit_blend_file_ensure(asset_lib_path); + return asset_blend.ensure_id(id_type, asset_name); +} + +bool asset_edit_id_is_editable(const ID *id) +{ + if (!(id->tag & LIB_TAG_ASSET_MAIN)) { + return false; + } + + const AssetEditBlend *asset_blend = asset_edit_blend_from_id(id); + return (asset_blend) ? asset_blend->is_editable : false; +} + +void asset_edit_main_free_all() +{ + asset_edit_blend_get_all().clear_and_shrink(); +} + +} // namespace blender::bke diff --git a/source/blender/blenkernel/intern/asset_weak_reference.cc b/source/blender/blenkernel/intern/asset_weak_reference.cc index a8ecb5eac86..3a08698acad 100644 --- a/source/blender/blenkernel/intern/asset_weak_reference.cc +++ b/source/blender/blenkernel/intern/asset_weak_reference.cc @@ -129,225 +129,3 @@ 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 storing assets that are weak referenced. - * - * This avoids mixing asset datablocks in the regular main, which leads to naming conflicts and - * confusing user interface. */ - -struct AssetWeakReferenceMain { - std::string filepath; - Main *main; - - AssetWeakReferenceMain(std::string filepath) - : filepath(std::move(filepath)), main(BKE_main_new()) - { - main->is_asset_weak_reference_main = true; - BLI_assert(!BLI_path_is_rel(filepath.c_str())); - } - AssetWeakReferenceMain(const AssetWeakReferenceMain &) = delete; - AssetWeakReferenceMain(AssetWeakReferenceMain &&other) - : filepath(std::exchange(other.filepath, "")), main(std::exchange(other.main, nullptr)) - { - } - AssetWeakReferenceMain &operator=(AssetWeakReferenceMain &&other) - { - if (this == &other) { - return *this; - } - this->filepath = std::exchange(other.filepath, ""); - this->main = std::exchange(other.main, nullptr); - return *this; - } - - ~AssetWeakReferenceMain() - { - if (main) { - BKE_main_free(main); - } - } - - void reload(Main &global_main); - void clear_users(Main &global_main); -}; - -void AssetWeakReferenceMain::reload(Main &global_main) -{ - Main *old_main = this->main; - this->main = BKE_main_new(); - this->main->is_asset_weak_reference_main = true; - - /* Fill fresh main database with same datablock as before. */ - LibraryLink_Params lapp_params{}; - lapp_params.bmain = this->main; - 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, this->filepath.c_str(), nullptr); - - /* Requests all existing datablocks to be appended again. */ - ID *old_id; - FOREACH_MAIN_ID_BEGIN (old_main, old_id) { - ID_Type old_id_code = GS(old_id->name); - if (BKE_idtype_idcode_is_linkable(old_id_code)) { - BlendfileLinkAppendContextItem *lapp_item = BKE_blendfile_link_append_context_item_add( - lapp_context, old_id->name + 2, old_id_code, nullptr); - BKE_blendfile_link_append_context_item_library_index_enable(lapp_context, lapp_item, 0); - } - } - FOREACH_MAIN_ID_END; - - BKE_blendfile_link(lapp_context, nullptr); - BKE_blendfile_append(lapp_context, nullptr); - - BKE_blendfile_link_append_context_free(lapp_context); - - BKE_main_id_tag_all(this->main, LIB_TAG_ASSET_MAIN, true); - - /* Remap old to new. */ - bke::id::IDRemapper mappings; - FOREACH_MAIN_ID_BEGIN (old_main, old_id) { - ID *new_id = BKE_libblock_find_name(this->main, GS(old_id->name), old_id->name + 2); - mappings.add(old_id, new_id); - } - FOREACH_MAIN_ID_END; - BKE_libblock_remap_multiple(&global_main, mappings, 0); - - /* Free old database. */ - BKE_main_free(old_main); -} - -void AssetWeakReferenceMain::clear_users(Main &global_main) -{ - /* Remap old to null pointer. */ - bke::id::IDRemapper mappings; - ID *old_id; - FOREACH_MAIN_ID_BEGIN (this->main, old_id) { - mappings.add(old_id, nullptr); - } - FOREACH_MAIN_ID_END; - BKE_libblock_remap_multiple(&global_main, mappings, 0); -} - -static Vector &get_weak_reference_mains() -{ - static Vector mains; - return mains; -} - -Main *BKE_asset_weak_reference_main(const ID *id) -{ - BLI_assert(id->tag & LIB_TAG_ASSET_MAIN); - - for (const AssetWeakReferenceMain &weak_ref_main : get_weak_reference_mains()) { - /* 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; - } - } - } - - BLI_assert_unreachable(); - return nullptr; -} - -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; - } - } - - get_weak_reference_mains().append_as(filepath); - return *get_weak_reference_mains().last().main; -} - -void BKE_asset_weak_reference_main_reload(Main &global_main, Main &asset_main) -{ - for (AssetWeakReferenceMain &weak_ref_main : get_weak_reference_mains()) { - if (weak_ref_main.main == &asset_main) { - weak_ref_main.reload(global_main); - return; - } - } - - BLI_assert_unreachable(); -} - -void BKE_asset_weak_reference_main_free(Main &global_main, Main &asset_main) -{ - int index = 0; - for (AssetWeakReferenceMain &weak_ref_main : get_weak_reference_mains()) { - if (weak_ref_main.main == &asset_main) { - weak_ref_main.clear_users(global_main); - get_weak_reference_mains().remove(index); - return; - } - index++; - } - - BLI_assert_unreachable(); -} - -void BKE_asset_weak_reference_main_free_all() -{ - get_weak_reference_mains().clear_and_shrink(); -} - -ID *BKE_asset_weak_reference_ensure(Main &global_main, - const ID_Type id_type, - const AssetWeakReference &weak_ref) -{ - 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; - } - - BLI_assert(asset_name != nullptr); - - /* If weak reference resolves to a null library path, assume we are in local asset case. */ - 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 = 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_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); - - BlendfileLinkAppendContextItem *lapp_item = BKE_blendfile_link_append_context_item_add( - 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); - 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); - - 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)); - - return local_asset; -} diff --git a/source/blender/blenkernel/intern/blender.cc b/source/blender/blenkernel/intern/blender.cc index 2a3f2cfc58e..07c4a9dd714 100644 --- a/source/blender/blenkernel/intern/blender.cc +++ b/source/blender/blenkernel/intern/blender.cc @@ -22,7 +22,7 @@ #include "IMB_moviecache.hh" #include "BKE_addon.h" -#include "BKE_asset.hh" +#include "BKE_asset_edit.hh" #include "BKE_blender.hh" /* own include */ #include "BKE_blender_user_menu.hh" /* own include */ #include "BKE_blender_version.h" /* own include */ @@ -64,7 +64,7 @@ void BKE_blender_free() /* Free separate data-bases after #BKE_blender_globals_clear in case any data-blocks in the * global main use pointers to asset main data-blocks when they're freed. That generally * shouldn't happen but it's better to be safe. */ - BKE_asset_weak_reference_main_free_all(); + blender::bke::asset_edit_main_free_all(); if (G.log.file != nullptr) { fclose(static_cast(G.log.file)); diff --git a/source/blender/blenkernel/intern/main.cc b/source/blender/blenkernel/intern/main.cc index 38d660b4ff7..a52d923914e 100644 --- a/source/blender/blenkernel/intern/main.cc +++ b/source/blender/blenkernel/intern/main.cc @@ -24,7 +24,7 @@ #include "DNA_ID.h" -#include "BKE_asset.hh" +#include "BKE_asset_edit.hh" #include "BKE_bpath.hh" #include "BKE_global.hh" #include "BKE_idtype.hh" @@ -972,7 +972,7 @@ Main *BKE_main_from_id(Main *global_main, const ID *id, const bool verify) return nullptr; } if (id->tag & LIB_TAG_ASSET_MAIN) { - return BKE_asset_weak_reference_main(id); + return blender::bke::asset_edit_main(id); } if (verify) { diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index e68eeed5659..aeee9fb02e1 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -41,6 +41,7 @@ #include "BLT_translation.hh" #include "BKE_asset.hh" +#include "BKE_asset_edit.hh" #include "BKE_attribute.hh" #include "BKE_brush.hh" #include "BKE_ccg.h" @@ -620,8 +621,8 @@ static bool paint_brush_set_from_asset_reference(Main *bmain, Paint *paint) return false; } - Brush *brush = reinterpret_cast( - BKE_asset_weak_reference_ensure(*bmain, ID_BR, *paint->brush_asset_reference)); + Brush *brush = reinterpret_cast(blender::bke::asset_edit_id_from_weak_reference( + *bmain, ID_BR, *paint->brush_asset_reference)); BLI_assert(brush == nullptr || (brush->id.tag & LIB_TAG_ASSET_MAIN)); /* Ensure we have a brush with appropriate mode to assign. diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 48fdc66304a..cea01e9a21b 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -29,6 +29,7 @@ #include "BLO_writefile.hh" #include "BKE_asset.hh" +#include "BKE_asset_edit.hh" #include "BKE_blendfile.hh" #include "BKE_brush.hh" #include "BKE_context.hh" @@ -739,7 +740,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, ID_BR, brush_asset_reference)); + blender::bke::asset_edit_id_from_weak_reference(*bmain, ID_BR, brush_asset_reference)); Paint *paint = BKE_paint_get_active_from_context(C); @@ -771,7 +772,7 @@ static void BRUSH_OT_asset_select(wmOperatorType *ot) * This needs to be properly implemented in assetlib code. */ static AssetWeakReference brush_asset_create_weakref_hack(const bUserAssetLibrary *user_asset_lib, - std::string &file_path) + const std::string &file_path) { AssetWeakReference asset_weak_ref{}; @@ -819,127 +820,6 @@ static void refresh_asset_library(const bContext *C, const bUserAssetLibrary &us } } -static std::string brush_asset_root_path_for_save(const bUserAssetLibrary &user_library) -{ - if (user_library.dirpath[0] == '\0') { - return ""; - } - - char libpath[FILE_MAX]; - BLI_strncpy(libpath, user_library.dirpath, sizeof(libpath)); - BLI_path_slash_native(libpath); - BLI_path_normalize(libpath); - - return std::string(libpath) + SEP + "Saved" + SEP + "Brushes"; -} - -static std::string brush_asset_blendfile_path_for_save(ReportList *reports, - const bUserAssetLibrary &user_library, - const StringRefNull base_name) -{ - std::string root_path = brush_asset_root_path_for_save(user_library); - BLI_assert(!root_path.empty()); - - if (!BLI_dir_create_recursive(root_path.c_str())) { - BKE_report(reports, RPT_ERROR, "Failed to create asset library directory to save brush"); - return ""; - } - - char base_name_filesafe[FILE_MAXFILE]; - BLI_strncpy(base_name_filesafe, base_name.c_str(), sizeof(base_name_filesafe)); - BLI_path_make_safe_filename(base_name_filesafe); - - if (!BLI_is_file((root_path + SEP + base_name_filesafe + BLENDER_ASSET_FILE_SUFFIX).c_str())) { - return root_path + SEP + base_name_filesafe + BLENDER_ASSET_FILE_SUFFIX; - } - int i = 1; - while (BLI_is_file((root_path + SEP + base_name_filesafe + "_" + std::to_string(i++) + - BLENDER_ASSET_FILE_SUFFIX) - .c_str())) - ; - return root_path + SEP + base_name_filesafe + "_" + std::to_string(i - 1) + - BLENDER_ASSET_FILE_SUFFIX; -} - -static bool brush_asset_write_in_library(Main *bmain, - Brush *brush, - const char *name, - const StringRefNull filepath, - const std::optional catalog, - const std::optional catalog_simple_name, - std::string &final_full_file_path, - ReportList *reports) -{ - /* XXX - * FIXME - * - * This code is _pure evil_. It does in-place manipulation on IDs in global Main database, - * temporarilly remove them and add them back... - * - * Use it as-is for now (in a similar way as python API or copy-to-buffer works). Nut the whole - * 'BKE_blendfile_write_partial' code needs to be completely refactored. - * - * Ideas: - * - Have `BKE_blendfile_write_partial_begin` return a new temp Main. - * - Replace `BKE_blendfile_write_partial_tag_ID` by API to add IDs to this temp Main. - * + This should _duplicate_ the ID, not remove the original one from the source Main! - * - Have API to automatically also duplicate dependencies into temp Main. - * + Have options to e.g. make all duplicated IDs 'local' (i.e. remove their library data). - * - `BKE_blendfile_write_partial` then simply write the given temp main. - * - `BKE_blendfile_write_partial_end` frees the temp Main. - */ - - const short brush_flag = brush->id.flag; - const int brush_tag = brush->id.tag; - const int brush_us = brush->id.us; - const std::string brush_name = brush->id.name + 2; - IDOverrideLibrary *brush_liboverride = brush->id.override_library; - AssetMetaData *brush_asset_data = brush->id.asset_data; - const int write_flags = 0; /* Could use #G_FILE_COMPRESS ? */ - const eBLO_WritePathRemap remap_mode = BLO_WRITE_PATH_REMAP_RELATIVE; - - BKE_blendfile_write_partial_begin(bmain); - - brush->id.flag |= LIB_FAKEUSER; - brush->id.tag &= ~LIB_TAG_RUNTIME; - brush->id.us = 1; - BLI_strncpy(brush->id.name + 2, name, sizeof(brush->id.name) - 2); - if (!ID_IS_ASSET(&brush->id)) { - brush->id.asset_data = brush->id.override_library->reference->asset_data; - } - brush->id.override_library = nullptr; - - if (catalog) { - brush->id.asset_data->catalog_id = *catalog; - } - if (catalog_simple_name) { - STRNCPY(brush->id.asset_data->catalog_simple_name, catalog_simple_name->c_str()); - } - - BKE_blendfile_write_partial_tag_ID(&brush->id, true); - - /* TODO: check overwriting existing file. */ - /* TODO: ensure filepath contains only valid characters for file system. */ - const bool sucess = BKE_blendfile_write_partial( - bmain, filepath.c_str(), write_flags, remap_mode, reports); - - if (sucess) { - final_full_file_path = std::string(filepath) + SEP + "Brush" + SEP + name; - } - - BKE_blendfile_write_partial_end(bmain); - - BKE_blendfile_write_partial_tag_ID(&brush->id, false); - brush->id.flag = brush_flag; - brush->id.tag = brush_tag; - brush->id.us = brush_us; - BLI_strncpy(brush->id.name + 2, brush_name.c_str(), sizeof(brush->id.name) - 2); - brush->id.override_library = brush_liboverride; - brush->id.asset_data = brush_asset_data; - - return sucess; -} - static bool brush_asset_save_as_poll(bContext *C) { Paint *paint = BKE_paint_get_active_from_context(C); @@ -956,38 +836,6 @@ static bool brush_asset_save_as_poll(bContext *C) return true; } -static bool asset_is_editable(const AssetWeakReference &asset_weak_ref) -{ - /* Fairly simple checks, based on filepath only: - * - The blendlib filepath ends up with the `.asset.blend` extension. - * - The blendlib is located in the expected sub-directory of the editable asset library. - * - * TODO: Right now no check is done on file content, e.g. to ensure that the blendlib file has - * not been manually edited by the user (that it does not have any UI IDs e.g.). */ - - char path_buffer[FILE_MAX_LIBEXTRA]; - char *dir, *group, *name; - AS_asset_full_path_explode_from_weak_ref(&asset_weak_ref, path_buffer, &dir, &group, &name); - - if (!StringRef(dir).endswith(BLENDER_ASSET_FILE_SUFFIX)) { - return false; - } - - const bUserAssetLibrary *library = BKE_preferences_asset_library_find_by_name( - &U, asset_weak_ref.asset_library_identifier); - if (!library) { - return false; - } - - std::string root_path_for_save = brush_asset_root_path_for_save(*library); - if (root_path_for_save.empty() || !StringRef(dir).startswith(root_path_for_save)) { - return false; - } - - /* TODO: Do we want more checks here? E.g. check actual content of the file? */ - return true; -} - static const bUserAssetLibrary *get_asset_library_from_prop(PointerRNA &ptr) { const int enum_value = RNA_enum_get(&ptr, "asset_library_reference"); @@ -1048,18 +896,6 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) if (!user_library) { return OPERATOR_CANCELLED; } - const std::string filepath = brush_asset_blendfile_path_for_save( - op->reports, *user_library, name); - if (filepath.empty()) { - return OPERATOR_CANCELLED; - } - - /* Turn brush into asset if it isn't yet. */ - if (!ID_IS_ASSET(brush)) { - asset::mark_id(&brush->id); - asset::generate_preview(C, &brush->id); - } - BLI_assert(ID_IS_ASSET(brush)); asset_system::AssetLibrary *library = AS_asset_library_load( CTX_data_main(C), user_library_to_library_ref(*user_library)); @@ -1068,6 +904,13 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + /* Turn brush into asset if it isn't yet. */ + if (!ID_IS_ASSET(&brush->id)) { + asset::mark_id(&brush->id); + asset::generate_preview(C, &brush->id); + } + BLI_assert(ID_IS_ASSET(&brush->id)); + /* Add asset to catalog. */ char catalog_path[MAX_NAME]; RNA_string_get(op->ptr, "catalog_path", catalog_path); @@ -1082,33 +925,20 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) catalog_simple_name = catalog.simple_name; } - library->catalog_service().write_to_disk(filepath); + const std::optional final_full_asset_filepath = blender::bke::asset_edit_id_save_as( + *bmain, &brush->id, name, catalog_id, catalog_simple_name, user_library, op->reports); - /* Save to asset library. */ - Main *asset_main = BKE_main_from_id(bmain, &brush->id); - - std::string final_full_asset_filepath; - const bool success = brush_asset_write_in_library(asset_main, - brush, - name, - filepath, - catalog_id, - catalog_simple_name, - final_full_asset_filepath, - op->reports); - - if (!success) { - BKE_report(op->reports, RPT_ERROR, "Failed to write to asset library"); + if (!final_full_asset_filepath) { return OPERATOR_CANCELLED; } - AssetWeakReference new_brush_weak_ref = brush_asset_create_weakref_hack( - user_library, final_full_asset_filepath); + library->catalog_service().write_to_disk(*final_full_asset_filepath); - BKE_reportf(op->reports, RPT_INFO, "Saved \"%s\"", filepath.c_str()); + AssetWeakReference new_brush_weak_ref = brush_asset_create_weakref_hack( + user_library, *final_full_asset_filepath); brush = reinterpret_cast( - BKE_asset_weak_reference_ensure(*bmain, ID_BR, new_brush_weak_ref)); + blender::bke::asset_edit_id_from_weak_reference(*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. */ @@ -1219,7 +1049,7 @@ static bool brush_asset_delete_poll(bContext *C) /* Asset brush, check if belongs to an editable blend file. */ if (paint->brush_asset_reference && ID_IS_ASSET(brush)) { - if (!asset_is_editable(*paint->brush_asset_reference)) { + if (!blender::bke::asset_edit_id_is_editable(&brush->id)) { CTX_wm_operator_poll_msg_set(C, "Asset blend file is not editable"); return false; } @@ -1233,7 +1063,6 @@ static int brush_asset_delete_exec(bContext *C, wmOperator *op) 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_main_from_id(bmain, &brush->id); bUserAssetLibrary *library = BKE_preferences_asset_library_find_by_name( &U, paint->brush_asset_reference->asset_library_identifier); @@ -1241,21 +1070,7 @@ static int brush_asset_delete_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - if (paint->brush_asset_reference && ID_IS_ASSET(brush)) { - /* Delete from asset library on disk. */ - char path_buffer[FILE_MAX_LIBEXTRA]; - char *filepath; - AS_asset_full_path_explode_from_weak_ref( - paint->brush_asset_reference, path_buffer, &filepath, nullptr, nullptr); - - if (BLI_delete(filepath, false, false) != 0) { - BKE_report(op->reports, RPT_ERROR, "Failed to delete asset library file"); - } - } - - if (asset_main != bmain) { - BKE_asset_weak_reference_main_free(*bmain, *asset_main); - } + blender::bke::asset_edit_id_delete(*bmain, &brush->id, op->reports); refresh_asset_library(C, *library); @@ -1306,7 +1121,7 @@ static bool brush_asset_update_poll(bContext *C) return false; } - if (!asset_is_editable(*paint->brush_asset_reference)) { + if (!blender::bke::asset_edit_id_is_editable(&brush->id)) { CTX_wm_operator_poll_msg_set(C, "Asset blend file is not editable"); return false; } @@ -1327,23 +1142,9 @@ static int brush_asset_update_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - char path_buffer[FILE_MAX_LIBEXTRA]; - char *filepath; - AS_asset_full_path_explode_from_weak_ref( - asset_weak_ref, path_buffer, &filepath, nullptr, nullptr); - BLI_assert(ID_IS_ASSET(brush)); - Main *asset_main = BKE_main_from_id(bmain, &brush->id); - std::string final_full_asset_filepath; - brush_asset_write_in_library(asset_main, - brush, - brush->id.name + 2, - filepath, - std::nullopt, - std::nullopt, - final_full_asset_filepath, - op->reports); + blender::bke::asset_edit_id_save(*bmain, &brush->id, op->reports); refresh_asset_library(C, *user_library); WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_EDITED, nullptr); @@ -1373,16 +1174,13 @@ static bool brush_asset_revert_poll(bContext *C) return paint->brush_asset_reference && (brush->id.tag & LIB_TAG_ASSET_MAIN); } -static int brush_asset_revert_exec(bContext *C, wmOperator * /*op*/) +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_main_from_id(bmain, &brush->id); - /* Reload entire main, including texture dependencies. This relies on there - * being only a single brush asset per blend file. */ - BKE_asset_weak_reference_main_reload(*bmain, *asset_main); + blender::bke::asset_edit_id_revert(*bmain, &brush->id, op->reports); WM_main_add_notifier(NC_BRUSH | NA_EDITED, nullptr); WM_main_add_notifier(NC_TEXTURE | ND_NODES, nullptr); -- 2.30.2 From d6363a5ce577d906e187e3c12c35d5fd620a9e7a Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 29 Mar 2024 12:57:05 -0400 Subject: [PATCH 171/244] Cleanup: Remove unnecessary namespace specification --- source/blender/editors/sculpt_paint/paint_ops.cc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index cea01e9a21b..4965afe9094 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -740,7 +740,7 @@ static int brush_asset_select_exec(bContext *C, wmOperator *op) AssetWeakReference brush_asset_reference = asset->make_weak_reference(); Brush *brush = reinterpret_cast( - blender::bke::asset_edit_id_from_weak_reference(*bmain, ID_BR, brush_asset_reference)); + bke::asset_edit_id_from_weak_reference(*bmain, ID_BR, brush_asset_reference)); Paint *paint = BKE_paint_get_active_from_context(C); @@ -925,7 +925,7 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) catalog_simple_name = catalog.simple_name; } - const std::optional final_full_asset_filepath = blender::bke::asset_edit_id_save_as( + const std::optional final_full_asset_filepath = bke::asset_edit_id_save_as( *bmain, &brush->id, name, catalog_id, catalog_simple_name, user_library, op->reports); if (!final_full_asset_filepath) { @@ -938,7 +938,7 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) user_library, *final_full_asset_filepath); brush = reinterpret_cast( - blender::bke::asset_edit_id_from_weak_reference(*bmain, ID_BR, new_brush_weak_ref)); + bke::asset_edit_id_from_weak_reference(*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. */ @@ -1049,7 +1049,7 @@ static bool brush_asset_delete_poll(bContext *C) /* Asset brush, check if belongs to an editable blend file. */ if (paint->brush_asset_reference && ID_IS_ASSET(brush)) { - if (!blender::bke::asset_edit_id_is_editable(&brush->id)) { + if (!bke::asset_edit_id_is_editable(&brush->id)) { CTX_wm_operator_poll_msg_set(C, "Asset blend file is not editable"); return false; } @@ -1070,7 +1070,7 @@ static int brush_asset_delete_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - blender::bke::asset_edit_id_delete(*bmain, &brush->id, op->reports); + bke::asset_edit_id_delete(*bmain, &brush->id, op->reports); refresh_asset_library(C, *library); @@ -1121,7 +1121,7 @@ static bool brush_asset_update_poll(bContext *C) return false; } - if (!blender::bke::asset_edit_id_is_editable(&brush->id)) { + if (!bke::asset_edit_id_is_editable(&brush->id)) { CTX_wm_operator_poll_msg_set(C, "Asset blend file is not editable"); return false; } @@ -1144,7 +1144,7 @@ static int brush_asset_update_exec(bContext *C, wmOperator *op) BLI_assert(ID_IS_ASSET(brush)); - blender::bke::asset_edit_id_save(*bmain, &brush->id, op->reports); + bke::asset_edit_id_save(*bmain, &brush->id, op->reports); refresh_asset_library(C, *user_library); WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_EDITED, nullptr); @@ -1180,7 +1180,7 @@ static int brush_asset_revert_exec(bContext *C, wmOperator *op) Paint *paint = BKE_paint_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); - blender::bke::asset_edit_id_revert(*bmain, &brush->id, op->reports); + bke::asset_edit_id_revert(*bmain, &brush->id, op->reports); WM_main_add_notifier(NC_BRUSH | NA_EDITED, nullptr); WM_main_add_notifier(NC_TEXTURE | ND_NODES, nullptr); -- 2.30.2 From c0653b1fff34529e4db2d207bd9af45bb47784c7 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 29 Mar 2024 18:00:41 +0100 Subject: [PATCH 172/244] Brush Assets: Improve defaults for "Duplicate Asset" operator Use the library from the operator's last execution instead of storing a "default" option in the user preferences. Or if the last library isn't available, use the current library as a default, or the first library if the current library isn't editable. Also use the asset's current catalog path for the initial asset path. Pull Request: https://projects.blender.org/blender/blender/pulls/120028 --- scripts/startup/bl_ui/space_userpref.py | 1 - .../editors/asset/intern/asset_menu_utils.cc | 2 +- .../editors/include/ED_asset_menu_utils.hh | 3 + .../blender/editors/sculpt_paint/paint_ops.cc | 85 +++++++++++++++---- source/blender/makesdna/DNA_asset_types.h | 1 - source/blender/makesrna/intern/rna_userdef.cc | 17 ---- 6 files changed, 72 insertions(+), 37 deletions(-) diff --git a/scripts/startup/bl_ui/space_userpref.py b/scripts/startup/bl_ui/space_userpref.py index b45a4a68392..145ea4e9e29 100644 --- a/scripts/startup/bl_ui/space_userpref.py +++ b/scripts/startup/bl_ui/space_userpref.py @@ -1581,7 +1581,6 @@ class USERPREF_PT_file_paths_asset_libraries(FilePathsPanel, Panel): layout.prop(active_library, "path") layout.prop(active_library, "import_method", text="Import Method") layout.prop(active_library, "use_relative_path") - layout.prop(active_library, "is_default") class USERPREF_UL_asset_libraries(UIList): diff --git a/source/blender/editors/asset/intern/asset_menu_utils.cc b/source/blender/editors/asset/intern/asset_menu_utils.cc index f995c68e0e0..8f4ad604d17 100644 --- a/source/blender/editors/asset/intern/asset_menu_utils.cc +++ b/source/blender/editors/asset/intern/asset_menu_utils.cc @@ -89,7 +89,7 @@ static const asset_system::AssetRepresentation *get_local_asset_from_relative_id return matching_asset; } -static const asset_system::AssetRepresentation *find_asset_from_weak_ref( +const asset_system::AssetRepresentation *find_asset_from_weak_ref( const bContext &C, const AssetWeakReference &weak_ref, ReportList *reports) { if (weak_ref.asset_library_type == ASSET_LIBRARY_LOCAL) { diff --git a/source/blender/editors/include/ED_asset_menu_utils.hh b/source/blender/editors/include/ED_asset_menu_utils.hh index 9ca3daa12e7..0021aca8a47 100644 --- a/source/blender/editors/include/ED_asset_menu_utils.hh +++ b/source/blender/editors/include/ED_asset_menu_utils.hh @@ -44,6 +44,9 @@ void operator_asset_reference_props_set(const asset_system::AssetRepresentation PointerRNA &ptr); void operator_asset_reference_props_register(StructRNA &srna); +const asset_system::AssetRepresentation *find_asset_from_weak_ref( + const bContext &C, const AssetWeakReference &weak_ref, ReportList *reports); + /** * Load all asset libraries to find an asset from the #operator_asset_reference_props_register * properties. The loading happens in the background, so there may be no result immediately. In diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 4965afe9094..708d373a0a1 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -788,19 +788,6 @@ static AssetWeakReference brush_asset_create_weakref_hack(const bUserAssetLibrar return asset_weak_ref; } -static const bUserAssetLibrary *brush_asset_get_default_library() -{ - if (BLI_listbase_is_empty(&U.asset_libraries)) { - return nullptr; - } - LISTBASE_FOREACH (const bUserAssetLibrary *, asset_library, &U.asset_libraries) { - if (asset_library->flag & ASSET_LIBRARY_DEFAULT) { - return asset_library; - } - } - return static_cast(U.asset_libraries.first); -} - static void refresh_asset_library(const bContext *C, const bUserAssetLibrary &user_library) { /* TODO: Should the all library reference be automatically cleared? */ @@ -827,6 +814,12 @@ static bool brush_asset_save_as_poll(bContext *C) if (paint == nullptr || brush == nullptr) { return false; } + if (!paint->brush_asset_reference) { + /* The brush should always be an imported asset. We use this asset reference to find + * which library and catalog the brush came from, as defaults for the popup. */ + BLI_assert_unreachable(); + return false; + } if (BLI_listbase_is_empty(&U.asset_libraries)) { CTX_wm_operator_poll_msg_set(C, "No asset library available to save to"); @@ -952,17 +945,74 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static std::optional library_to_library_ref( + const asset_system::AssetLibrary &library) +{ + for (const AssetLibraryReference &ref : asset_system::all_valid_asset_library_refs()) { + const std::string root_path = AS_asset_library_root_path_from_library_ref(ref); + /* Use #BLI_path_cmp_normalized because `library.root_path()` ends with a slash while + * `root_path` doesn't. */ + if (BLI_path_cmp_normalized(root_path.c_str(), library.root_path().c_str()) == 0) { + return ref; + } + } + return std::nullopt; +} + +static bool library_is_editable(const AssetLibraryReference &library) +{ + if (library.type == ASSET_LIBRARY_ESSENTIALS) { + return false; + } + return true; +} + static int brush_asset_save_as_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) { Paint *paint = BKE_paint_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); + const AssetWeakReference &brush_weak_ref = *paint->brush_asset_reference; + const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref( + *C, brush_weak_ref, op->reports); + if (!asset) { + BLI_assert_unreachable(); + return OPERATOR_CANCELLED; + } + const asset_system::AssetLibrary &library = asset->owner_asset_library(); + const std::optional library_ref = library_to_library_ref(library); + if (!library_ref) { + BLI_assert_unreachable(); + return OPERATOR_CANCELLED; + } RNA_string_set(op->ptr, "name", brush->id.name + 2); - if (const bUserAssetLibrary *library = brush_asset_get_default_library()) { - const AssetLibraryReference library_ref = user_library_to_library_ref(*library); - const int enum_value = asset::library_reference_to_enum_value(&library_ref); - RNA_enum_set(op->ptr, "asset_library_reference", enum_value); + /* If the library isn't saved from the operator's last execution, find the current library or the + * first library if the current library isn't editable. */ + if (!RNA_struct_property_is_set_ex(op->ptr, "asset_library_reference", false)) { + if (library_is_editable(*library_ref)) { + RNA_enum_set(op->ptr, + "asset_library_reference", + asset::library_reference_to_enum_value(&*library_ref)); + } + else { + const AssetLibraryReference first_library = user_library_to_library_ref( + *static_cast(U.asset_libraries.first)); + RNA_enum_set(op->ptr, + "asset_library_reference", + asset::library_reference_to_enum_value(&first_library)); + } + } + + /* By default, put the new asset in the same catalog as the existing asset. */ + if (!RNA_struct_property_is_set(op->ptr, "catalog_path")) { + const asset_system::CatalogID catalog_id = asset->get_metadata().catalog_id; + const asset_system::AssetCatalog *catalog = library.catalog_service().find_catalog(catalog_id); + if (catalog == nullptr) { + BLI_assert_unreachable(); + return OPERATOR_CANCELLED; + } + RNA_string_set(op->ptr, "catalog_path", catalog->path.c_str()); } return WM_operator_props_dialog_popup(C, op, 400, std::nullopt, IFACE_("Save")); @@ -990,6 +1040,7 @@ static void visit_asset_catalog_for_search_fn( const char *edit_text, FunctionRef visit_fn) { + /* NOTE: Using the all library would also be a valid choice. */ const bUserAssetLibrary *user_library = get_asset_library_from_prop(*ptr); if (!user_library) { return; diff --git a/source/blender/makesdna/DNA_asset_types.h b/source/blender/makesdna/DNA_asset_types.h index 7d23ad76475..5ae7c074772 100644 --- a/source/blender/makesdna/DNA_asset_types.h +++ b/source/blender/makesdna/DNA_asset_types.h @@ -117,7 +117,6 @@ typedef enum eAssetImportMethod { typedef enum eAssetLibrary_Flag { ASSET_LIBRARY_RELATIVE_PATH = (1 << 0), - ASSET_LIBRARY_DEFAULT = (1 << 1), } eAssetLibrary_Flag; /** diff --git a/source/blender/makesrna/intern/rna_userdef.cc b/source/blender/makesrna/intern/rna_userdef.cc index 3b47c079bd6..ebea4b45dab 100644 --- a/source/blender/makesrna/intern/rna_userdef.cc +++ b/source/blender/makesrna/intern/rna_userdef.cc @@ -361,18 +361,6 @@ static void rna_userdef_asset_library_path_set(PointerRNA *ptr, const char *valu BKE_preferences_asset_library_path_set(library, value); } -static void rna_UserAssetLibrary_is_default_set(PointerRNA *ptr, bool value) -{ - bUserAssetLibrary *library = static_cast(ptr->data); - if (!value) { - return; - } - LISTBASE_FOREACH (bUserAssetLibrary *, library_iter, &U.asset_libraries) { - library_iter->flag &= ~ASSET_LIBRARY_DEFAULT; - } - library->flag |= ASSET_LIBRARY_DEFAULT; -} - static void rna_userdef_extension_repo_name_set(PointerRNA *ptr, const char *value) { bUserExtensionRepo *repo = (bUserExtensionRepo *)ptr->data; @@ -6618,11 +6606,6 @@ static void rna_def_userdef_filepaths_asset_library(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, nullptr, "flag", ASSET_LIBRARY_RELATIVE_PATH); RNA_def_property_ui_text( prop, "Relative Path", "Use relative path when linking assets from this asset library"); - - prop = RNA_def_property(srna, "is_default", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "flag", ASSET_LIBRARY_DEFAULT); - RNA_def_property_boolean_funcs(prop, nullptr, "rna_UserAssetLibrary_is_default_set"); - RNA_def_property_ui_text(prop, "Default", "Use this library for saving new assets"); } static void rna_def_userdef_filepaths_extension_repo(BlenderRNA *brna) -- 2.30.2 From 6a545523e59e965b2ba1e7ef252066fa14e761af Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 29 Mar 2024 13:04:58 -0400 Subject: [PATCH 173/244] Fix: Poll function for brush asset delete operator The operator requires the paint->brush_asset_reference. That should always be set anyway, but better to be safe. --- source/blender/editors/sculpt_paint/paint_ops.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 708d373a0a1..56c2ad99630 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -1097,9 +1097,12 @@ static bool brush_asset_delete_poll(bContext *C) if (paint == nullptr || brush == nullptr) { return false; } + if (!paint->brush_asset_reference) { + return false; + } /* Asset brush, check if belongs to an editable blend file. */ - if (paint->brush_asset_reference && ID_IS_ASSET(brush)) { + if (ID_IS_ASSET(brush)) { if (!bke::asset_edit_id_is_editable(&brush->id)) { CTX_wm_operator_poll_msg_set(C, "Asset blend file is not editable"); return false; -- 2.30.2 From 37a94b6e12c898a2744e30fb8aab975c0641566a Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 29 Mar 2024 13:10:58 -0400 Subject: [PATCH 174/244] Cleanup: Remove redundant null check in save as operator Null-ness of paint and brush is checked in the operator's poll function --- source/blender/editors/sculpt_paint/paint_ops.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 56c2ad99630..d67504e8221 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -871,9 +871,6 @@ 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) { - return OPERATOR_CANCELLED; - } /* Determine file path to save to. */ PropertyRNA *name_prop = RNA_struct_find_property(op->ptr, "name"); -- 2.30.2 From d558bfcdadd736bde302ad08c849b61e8afefc27 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 29 Mar 2024 13:12:06 -0400 Subject: [PATCH 175/244] Cleanup: Avoid unnecessary context lookup --- source/blender/editors/sculpt_paint/paint_ops.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index d67504e8221..d68349beb20 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -888,7 +888,7 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) } asset_system::AssetLibrary *library = AS_asset_library_load( - CTX_data_main(C), user_library_to_library_ref(*user_library)); + bmain, user_library_to_library_ref(*user_library)); if (!library) { BKE_report(op->reports, RPT_ERROR, "Failed to load asset library"); return OPERATOR_CANCELLED; -- 2.30.2 From a800bdc4733f58b3f8ae78fc789834c80f33b6e7 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 29 Mar 2024 14:23:34 -0400 Subject: [PATCH 176/244] Cleanup: Use references, StringRef for asset edit API functions --- source/blender/blenkernel/BKE_asset_edit.hh | 20 ++-- .../blender/blenkernel/intern/asset_edit.cc | 113 +++++++++--------- source/blender/blenkernel/intern/main.cc | 2 +- .../blender/editors/sculpt_paint/paint_ops.cc | 12 +- 4 files changed, 75 insertions(+), 72 deletions(-) diff --git a/source/blender/blenkernel/BKE_asset_edit.hh b/source/blender/blenkernel/BKE_asset_edit.hh index e78805b8dfb..c7ead3f2c4d 100644 --- a/source/blender/blenkernel/BKE_asset_edit.hh +++ b/source/blender/blenkernel/BKE_asset_edit.hh @@ -26,6 +26,8 @@ #include #include +#include "BLI_string_ref.hh" + #include "AS_asset_catalog.hh" #include "DNA_ID_enums.h" @@ -44,24 +46,24 @@ ID *asset_edit_id_from_weak_reference(Main &global_main, const AssetWeakReference &weak_ref); /** Get main database that a given asset datablock corresponds to. */ -Main *asset_edit_main(const ID *id); +Main *asset_edit_main(const ID &id); /** Asset editing operations. */ -bool asset_edit_id_is_editable(const ID *id); +bool asset_edit_id_is_editable(const ID &id); std::optional asset_edit_id_save_as( Main &global_main, - const ID *id, - const char *name, + const ID &id, + StringRef name, std::optional catalog_id, std::optional catalog_simple_name, - const bUserAssetLibrary *user_library, - ReportList *reports); + const bUserAssetLibrary &user_library, + ReportList &reports); -bool asset_edit_id_save(Main &global_main, const ID *id, ReportList *reports); -bool asset_edit_id_revert(Main &global_main, const ID *id, ReportList *reports); -bool asset_edit_id_delete(Main &global_main, const ID *id, ReportList *reports); +bool asset_edit_id_save(Main &global_main, const ID &id, ReportList &reports); +bool asset_edit_id_revert(Main &global_main, const ID &id, ReportList &reports); +bool asset_edit_id_delete(Main &global_main, const ID &id, ReportList &reports); /** Clean up on exit. */ void asset_edit_main_free_all(); diff --git a/source/blender/blenkernel/intern/asset_edit.cc b/source/blender/blenkernel/intern/asset_edit.cc index c23d07fcb71..a084efc0619 100644 --- a/source/blender/blenkernel/intern/asset_edit.cc +++ b/source/blender/blenkernel/intern/asset_edit.cc @@ -155,21 +155,23 @@ static std::string asset_root_path_for_save(const bUserAssetLibrary &user_librar return std::string(libpath) + SEP + "Saved" + SEP + name; } -static std::string asset_blendfile_path_for_save(ReportList *reports, - const bUserAssetLibrary &user_library, - const StringRefNull base_name, - const ID_Type id_type) +static std::string asset_blendfile_path_for_save(const bUserAssetLibrary &user_library, + const StringRef base_name, + const ID_Type id_type, + ReportList &reports) { std::string root_path = asset_root_path_for_save(user_library, id_type); BLI_assert(!root_path.empty()); if (!BLI_dir_create_recursive(root_path.c_str())) { - BKE_report(reports, RPT_ERROR, "Failed to create asset library directory to save brush"); + BKE_report(&reports, RPT_ERROR, "Failed to create asset library directory to save brush"); return ""; } char base_name_filesafe[FILE_MAXFILE]; - BLI_strncpy(base_name_filesafe, base_name.c_str(), sizeof(base_name_filesafe)); + BLI_strncpy(base_name_filesafe, + base_name.data(), + std::min(sizeof(base_name_filesafe), size_t(base_name.size()))); BLI_path_make_safe_filename(base_name_filesafe); const std::string filepath = root_path + SEP + base_name_filesafe + BLENDER_ASSET_FILE_SUFFIX; @@ -189,13 +191,13 @@ static std::string asset_blendfile_path_for_save(ReportList *reports, } static bool asset_write_in_library(Main *bmain, - const ID *id_const, - const char *name, + const ID &id_const, + const StringRef name, const StringRefNull filepath, const std::optional catalog, const std::optional catalog_simple_name, std::string &final_full_file_path, - ReportList *reports) + ReportList &reports) { /* XXX * FIXME @@ -216,41 +218,41 @@ static bool asset_write_in_library(Main *bmain, * - `BKE_blendfile_write_partial_end` frees the temp Main. */ - ID *id = const_cast(id_const); + ID &id = const_cast(id_const); - const short prev_flag = id->flag; - const int prev_tag = id->tag; - const int prev_us = id->us; - const std::string prev_name = id->name + 2; - IDOverrideLibrary *prev_liboverride = id->override_library; - AssetMetaData *asset_data = id->asset_data; + const short prev_flag = id.flag; + const int prev_tag = id.tag; + const int prev_us = id.us; + const std::string prev_name = id.name + 2; + IDOverrideLibrary *prev_liboverride = id.override_library; + AssetMetaData *asset_data = id.asset_data; const int write_flags = 0; /* Could use #G_FILE_COMPRESS ? */ const eBLO_WritePathRemap remap_mode = BLO_WRITE_PATH_REMAP_RELATIVE; BKE_blendfile_write_partial_begin(bmain); - id->flag |= LIB_FAKEUSER; - id->tag &= ~LIB_TAG_RUNTIME; - id->us = 1; - BLI_strncpy(id->name + 2, name, sizeof(id->name) - 2); - if (!ID_IS_ASSET(id)) { - id->asset_data = id->override_library->reference->asset_data; + id.flag |= LIB_FAKEUSER; + id.tag &= ~LIB_TAG_RUNTIME; + id.us = 1; + BLI_strncpy(id.name + 2, name.data(), std::min(sizeof(id.name) - 2, size_t(name.size()))); + if (!ID_IS_ASSET(&id)) { + id.asset_data = id.override_library->reference->asset_data; } - id->override_library = nullptr; + id.override_library = nullptr; if (catalog) { - id->asset_data->catalog_id = *catalog; + id.asset_data->catalog_id = *catalog; } if (catalog_simple_name) { - STRNCPY(id->asset_data->catalog_simple_name, catalog_simple_name->c_str()); + STRNCPY(id.asset_data->catalog_simple_name, catalog_simple_name->c_str()); } - BKE_blendfile_write_partial_tag_ID(id, true); + BKE_blendfile_write_partial_tag_ID(&id, true); /* TODO: check overwriting existing file. */ /* TODO: ensure filepath contains only valid characters for file system. */ const bool sucess = BKE_blendfile_write_partial( - bmain, filepath.c_str(), write_flags, remap_mode, reports); + bmain, filepath.c_str(), write_flags, remap_mode, &reports); if (sucess) { final_full_file_path = std::string(filepath) + SEP + "Brush" + SEP + name; @@ -258,13 +260,13 @@ static bool asset_write_in_library(Main *bmain, BKE_blendfile_write_partial_end(bmain); - BKE_blendfile_write_partial_tag_ID(id, false); - id->flag = prev_flag; - id->tag = prev_tag; - id->us = prev_us; - BLI_strncpy(id->name + 2, prev_name.c_str(), sizeof(id->name) - 2); - id->override_library = prev_liboverride; - id->asset_data = asset_data; + BKE_blendfile_write_partial_tag_ID(&id, false); + id.flag = prev_flag; + id.tag = prev_tag; + id.us = prev_us; + BLI_strncpy(id.name + 2, prev_name.c_str(), sizeof(id.name) - 2); + id.override_library = prev_liboverride; + id.asset_data = asset_data; return sucess; } @@ -338,15 +340,15 @@ static Vector &asset_edit_blend_get_all() return mains; } -static AssetEditBlend *asset_edit_blend_from_id(const ID *id) +static AssetEditBlend *asset_edit_blend_from_id(const ID &id) { - BLI_assert(id->tag & LIB_TAG_ASSET_MAIN); + BLI_assert(id.tag & LIB_TAG_ASSET_MAIN); for (AssetEditBlend &asset_blend : asset_edit_blend_get_all()) { /* TODO: Look into make this whole thing more efficient. */ - ListBase *lb = which_libbase(asset_blend.main, GS(id->name)); + ListBase *lb = which_libbase(asset_blend.main, GS(id.name)); LISTBASE_FOREACH (ID *, other_id, lb) { - if (id == other_id) { + if (&id == other_id) { return &asset_blend; } } @@ -356,7 +358,7 @@ static AssetEditBlend *asset_edit_blend_from_id(const ID *id) return nullptr; } -Main *asset_edit_main(const ID *id) +Main *asset_edit_main(const ID &id) { const AssetEditBlend *asset_blend = asset_edit_blend_from_id(id); return (asset_blend) ? asset_blend->main : nullptr; @@ -375,18 +377,18 @@ static AssetEditBlend &asset_edit_blend_file_ensure(const StringRef filepath) } std::optional asset_edit_id_save_as(Main &global_main, - const ID *id, - const char *name, + const ID &id, + const StringRef name, std::optional catalog_id, std::optional catalog_simple_name, - const bUserAssetLibrary *user_library, - ReportList *reports) + const bUserAssetLibrary &user_library, + ReportList &reports) { const std::string filepath = asset_blendfile_path_for_save( - reports, *user_library, name, GS(id->name)); + user_library, name, GS(id.name), reports); /* Save to asset library. */ - Main *asset_main = BKE_main_from_id(&global_main, id); + Main *asset_main = BKE_main_from_id(&global_main, &id); std::string final_full_asset_filepath; const bool success = asset_write_in_library(asset_main, @@ -397,18 +399,17 @@ std::optional asset_edit_id_save_as(Main &global_main, catalog_simple_name, final_full_asset_filepath, reports); - if (!success) { - BKE_report(reports, RPT_ERROR, "Failed to write to asset library"); + BKE_report(&reports, RPT_ERROR, "Failed to write to asset library"); return std::nullopt; } - BKE_reportf(reports, RPT_INFO, "Saved \"%s\"", filepath.c_str()); + BKE_reportf(&reports, RPT_INFO, "Saved \"%s\"", filepath.c_str()); return final_full_asset_filepath; } -bool asset_edit_id_save(Main & /*global_main*/, const ID *id, ReportList *reports) +bool asset_edit_id_save(Main & /*global_main*/, const ID &id, ReportList &reports) { AssetEditBlend *asset_blend = asset_edit_blend_from_id(id); if (asset_blend == nullptr) { @@ -418,7 +419,7 @@ bool asset_edit_id_save(Main & /*global_main*/, const ID *id, ReportList *report std::string final_full_asset_filepath; const bool success = asset_write_in_library(asset_blend->main, id, - id->name + 2, + id.name + 2, asset_blend->filepath.c_str(), std::nullopt, std::nullopt, @@ -426,14 +427,14 @@ bool asset_edit_id_save(Main & /*global_main*/, const ID *id, ReportList *report reports); if (!success) { - BKE_report(reports, RPT_ERROR, "Failed to write to asset library"); + BKE_report(&reports, RPT_ERROR, "Failed to write to asset library"); return false; } return true; } -bool asset_edit_id_revert(Main &global_main, const ID *id, ReportList * /*reports*/) +bool asset_edit_id_revert(Main &global_main, const ID &id, ReportList & /*reports*/) { AssetEditBlend *asset_blend = asset_edit_blend_from_id(id); if (asset_blend == nullptr) { @@ -447,7 +448,7 @@ bool asset_edit_id_revert(Main &global_main, const ID *id, ReportList * /*report return true; } -bool asset_edit_id_delete(Main &global_main, const ID *id, ReportList *reports) +bool asset_edit_id_delete(Main &global_main, const ID &id, ReportList &reports) { AssetEditBlend *asset_blend = asset_edit_blend_from_id(id); if (asset_blend == nullptr) { @@ -455,7 +456,7 @@ bool asset_edit_id_delete(Main &global_main, const ID *id, ReportList *reports) } if (BLI_delete(asset_blend->filepath.c_str(), false, false) != 0) { - BKE_report(reports, RPT_ERROR, "Failed to delete asset library file"); + BKE_report(&reports, RPT_ERROR, "Failed to delete asset library file"); return false; } @@ -500,9 +501,9 @@ ID *asset_edit_id_from_weak_reference(Main &global_main, return asset_blend.ensure_id(id_type, asset_name); } -bool asset_edit_id_is_editable(const ID *id) +bool asset_edit_id_is_editable(const ID &id) { - if (!(id->tag & LIB_TAG_ASSET_MAIN)) { + if (!(id.tag & LIB_TAG_ASSET_MAIN)) { return false; } diff --git a/source/blender/blenkernel/intern/main.cc b/source/blender/blenkernel/intern/main.cc index a52d923914e..4f17d92f878 100644 --- a/source/blender/blenkernel/intern/main.cc +++ b/source/blender/blenkernel/intern/main.cc @@ -972,7 +972,7 @@ Main *BKE_main_from_id(Main *global_main, const ID *id, const bool verify) return nullptr; } if (id->tag & LIB_TAG_ASSET_MAIN) { - return blender::bke::asset_edit_main(id); + return blender::bke::asset_edit_main(*id); } if (verify) { diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index d68349beb20..c70b096182f 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -916,7 +916,7 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) } const std::optional final_full_asset_filepath = bke::asset_edit_id_save_as( - *bmain, &brush->id, name, catalog_id, catalog_simple_name, user_library, op->reports); + *bmain, brush->id, name, catalog_id, catalog_simple_name, *user_library, *op->reports); if (!final_full_asset_filepath) { return OPERATOR_CANCELLED; @@ -1100,7 +1100,7 @@ static bool brush_asset_delete_poll(bContext *C) /* Asset brush, check if belongs to an editable blend file. */ if (ID_IS_ASSET(brush)) { - if (!bke::asset_edit_id_is_editable(&brush->id)) { + if (!bke::asset_edit_id_is_editable(brush->id)) { CTX_wm_operator_poll_msg_set(C, "Asset blend file is not editable"); return false; } @@ -1121,7 +1121,7 @@ static int brush_asset_delete_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - bke::asset_edit_id_delete(*bmain, &brush->id, op->reports); + bke::asset_edit_id_delete(*bmain, brush->id, *op->reports); refresh_asset_library(C, *library); @@ -1172,7 +1172,7 @@ static bool brush_asset_update_poll(bContext *C) return false; } - if (!bke::asset_edit_id_is_editable(&brush->id)) { + if (!bke::asset_edit_id_is_editable(brush->id)) { CTX_wm_operator_poll_msg_set(C, "Asset blend file is not editable"); return false; } @@ -1195,7 +1195,7 @@ static int brush_asset_update_exec(bContext *C, wmOperator *op) BLI_assert(ID_IS_ASSET(brush)); - bke::asset_edit_id_save(*bmain, &brush->id, op->reports); + bke::asset_edit_id_save(*bmain, brush->id, *op->reports); refresh_asset_library(C, *user_library); WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_EDITED, nullptr); @@ -1231,7 +1231,7 @@ static int brush_asset_revert_exec(bContext *C, wmOperator *op) Paint *paint = BKE_paint_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); - bke::asset_edit_id_revert(*bmain, &brush->id, op->reports); + bke::asset_edit_id_revert(*bmain, brush->id, *op->reports); WM_main_add_notifier(NC_BRUSH | NA_EDITED, nullptr); WM_main_add_notifier(NC_TEXTURE | ND_NODES, nullptr); -- 2.30.2 From 5044b5a3752ab53d9a874ab9c7f55bd681a77359 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 11 Apr 2024 12:07:07 -0400 Subject: [PATCH 177/244] Fix: null pointer access in brush RNA function --- source/blender/makesrna/intern/rna_sculpt_paint.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.cc b/source/blender/makesrna/intern/rna_sculpt_paint.cc index bffbaaef197..212b1f20b7e 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.cc +++ b/source/blender/makesrna/intern/rna_sculpt_paint.cc @@ -274,6 +274,9 @@ static PointerRNA rna_Paint_brush_get(PointerRNA *ptr) { Paint *paint = static_cast(ptr->data); Brush *brush = BKE_paint_brush(paint); + if (!brush) { + return PointerRNA_NULL; + } return RNA_id_pointer_create(&brush->id); } -- 2.30.2 From f245bc2b166e5e7c2fd2c8175e9c00b97c0ac900 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 11 Apr 2024 12:08:46 -0400 Subject: [PATCH 178/244] Fix: Make catalog path fetching in save as operator forgiving The catalog wasn't found for the "All" catalog, for example. Not sure why, but there's no reason to be so strict here. --- source/blender/editors/sculpt_paint/paint_ops.cc | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index c70b096182f..92ae5e18852 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -1003,13 +1003,10 @@ static int brush_asset_save_as_invoke(bContext *C, wmOperator *op, const wmEvent /* By default, put the new asset in the same catalog as the existing asset. */ if (!RNA_struct_property_is_set(op->ptr, "catalog_path")) { - const asset_system::CatalogID catalog_id = asset->get_metadata().catalog_id; - const asset_system::AssetCatalog *catalog = library.catalog_service().find_catalog(catalog_id); - if (catalog == nullptr) { - BLI_assert_unreachable(); - return OPERATOR_CANCELLED; + const asset_system::CatalogID &id = asset->get_metadata().catalog_id; + if (const asset_system::AssetCatalog *catalog = library.catalog_service().find_catalog(id)) { + RNA_string_set(op->ptr, "catalog_path", catalog->path.c_str()); } - RNA_string_set(op->ptr, "catalog_path", catalog->path.c_str()); } return WM_operator_props_dialog_popup(C, op, 400, std::nullopt, IFACE_("Save")); -- 2.30.2 From 759cc436df89685cd41a686887c1ed07e3a75fe2 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 11 Apr 2024 14:44:33 -0400 Subject: [PATCH 179/244] Fix GPv2 brush tools, consistency in toolbar file --- .../startup/bl_ui/space_toolsystem_toolbar.py | 152 +++++++++++------- 1 file changed, 90 insertions(+), 62 deletions(-) diff --git a/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 289852b01d8..64c7dccc328 100644 --- a/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -1374,7 +1374,7 @@ class _defs_particle: class _defs_sculpt: @staticmethod - def generate_brush_tool(context): + def generate_from_brushes(context): # Though `data_block` is conceptually unnecessary with a single brush tool, # it's still used because many areas assume that brush tools have it set #bToolRef. tool = None @@ -2128,17 +2128,23 @@ class _defs_gpencil_paint: @staticmethod def generate_from_brushes(context): - return generate_from_enum_ex( - context, - idname_prefix="builtin_brush.", - icon_prefix="brush.gpencil_draw.", - type=bpy.types.Brush, - attr="gpencil_tool", - cursor='DOT', - tooldef_keywords=dict( - operator="gpencil.draw", - ), - ) + # Though `data_block` is conceptually unnecessary with a single brush tool, + # it's still used because many areas assume that brush tools have it set #bToolRef. + tool = None + if context: + brush = context.tool_settings.gpencil_paint.brush + if brush: + tool = brush.gpencil_tool + return [ + ToolDef.from_dict( + dict( + idname="builtin.brush", + label="Brush", + icon="brush.sculpt.paint", + data_block=tool + ) + ) + ] @ToolDef.from_fn def cutter(): @@ -2494,16 +2500,24 @@ class _defs_gpencil_sculpt: @staticmethod def generate_from_brushes(context): - return generate_from_enum_ex( - context, - idname_prefix="builtin_brush.", - icon_prefix="ops.gpencil.sculpt_", - type=bpy.types.Brush, - attr="gpencil_sculpt_tool", - tooldef_keywords=dict( - operator="gpencil.sculpt_paint", - ), - ) + # Though `data_block` is conceptually unnecessary with a single brush tool, + # it's still used because many areas assume that brush tools have it set #bToolRef. + tool = None + if context: + brush = context.tool_settings.gpencil_sculpt_paint.brush + if brush: + tool = brush.gpencil_sculpt_tool + return [ + ToolDef.from_dict( + dict( + idname="builtin.brush", + label="Brush", + icon="brush.sculpt.paint", + data_block=tool + ) + ) + ] + class _defs_grease_pencil_sculpt: @@ -2524,39 +2538,52 @@ class _defs_grease_pencil_sculpt: @staticmethod def generate_from_brushes(context): - return generate_from_enum_ex( - context, - idname_prefix="builtin_brush.", - icon_prefix="ops.gpencil.sculpt_", - type=bpy.types.Brush, - # Uses GPv2 tool settings - attr="gpencil_sculpt_tool", - tooldef_keywords=dict( - operator="grease_pencil.sculpt_paint", - ), - ) + # Though `data_block` is conceptually unnecessary with a single brush tool, + # it's still used because many areas assume that brush tools have it set #bToolRef. + tool = None + if context: + brush = context.tool_settings.gpencil_sculpt.brush + if brush: + tool = brush.gpencil_sculpt_tool + return [ + ToolDef.from_dict( + dict( + idname="builtin.brush", + label="Brush", + icon="brush.sculpt.paint", + data_block=tool + ) + ) + ] class _defs_gpencil_weight: @staticmethod def generate_from_brushes(context): - return generate_from_enum_ex( - context, - idname_prefix="builtin_brush.", - icon_prefix="ops.gpencil.sculpt_", - type=bpy.types.Brush, - attr="gpencil_weight_tool", - tooldef_keywords=dict( - operator="gpencil.weight_paint", - ), - ) + # Though `data_block` is conceptually unnecessary with a single brush tool, + # it's still used because many areas assume that brush tools have it set #bToolRef. + tool = None + if context: + brush = context.tool_settings.gpencil_weight_paint.brush + if brush: + tool = brush.gpencil_weight_tool + return [ + ToolDef.from_dict( + dict( + idname="builtin.brush", + label="Brush", + icon="brush.sculpt.paint", + data_block=tool + ) + ) + ] class _defs_curves_sculpt: @staticmethod - def generate_brush_tool(context): + def generate_from_brushes(context): # Though `data_block` is conceptually unnecessary with a single brush tool, # it's still used because many areas assume that brush tools have it set #bToolRef. tool = None @@ -2595,18 +2622,23 @@ class _defs_gpencil_vertex: @staticmethod def generate_from_brushes(context): - return generate_from_enum_ex( - context, - idname_prefix="builtin_brush.", - icon_prefix="brush.paint_vertex.", - type=bpy.types.Brush, - attr="gpencil_vertex_tool", - cursor='DOT', - tooldef_keywords=dict( - operator="gpencil.vertex_paint", - ), - ) - + # Though `data_block` is conceptually unnecessary with a single brush tool, + # it's still used because many areas assume that brush tools have it set #bToolRef. + tool = None + if context: + brush = context.tool_settings.gpencil_vertex_paint.brush + if brush: + tool = brush.gpencil_vertex_tool + return [ + ToolDef.from_dict( + dict( + idname="builtin.brush", + label="Brush", + icon="brush.sculpt.paint", + data_block=tool + ) + ) + ] class _defs_node_select: @@ -3206,9 +3238,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): _defs_particle.generate_from_brushes, ], 'SCULPT': [ - lambda context: ( - _defs_sculpt.generate_brush_tool(context) - ), + _defs_sculpt.generate_from_brushes, None, ( _defs_sculpt.mask_border, @@ -3368,9 +3398,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): ), ], 'SCULPT_CURVES': [ - lambda context: ( - _defs_curves_sculpt.generate_brush_tool(context) - ), + _defs_curves_sculpt.generate_from_brushes, None, *_tools_annotate, ], -- 2.30.2 From 6546c957fbe00dbfa47f8709c876afcccb9f5cd8 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 11 Apr 2024 14:47:20 -0400 Subject: [PATCH 180/244] Fix: GPv3 sculpt mode --- scripts/startup/bl_ui/space_toolsystem_toolbar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 64c7dccc328..2e7d137586d 100644 --- a/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -2542,7 +2542,7 @@ class _defs_grease_pencil_sculpt: # it's still used because many areas assume that brush tools have it set #bToolRef. tool = None if context: - brush = context.tool_settings.gpencil_sculpt.brush + brush = context.tool_settings.gpencil_sculpt_paint.brush if brush: tool = brush.gpencil_sculpt_tool return [ -- 2.30.2 From ec3e4fc7d9a7afa316c734a36c481b6ab5cdbdc3 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 11 Apr 2024 14:53:47 -0400 Subject: [PATCH 181/244] Change GPv3 to use new single tool for all brushes Note this currently doesn't work because the brush assets use the old GPv2 object mode. --- .../startup/bl_ui/space_toolsystem_toolbar.py | 72 ++++++------------- 1 file changed, 20 insertions(+), 52 deletions(-) diff --git a/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 2e7d137586d..9fa11979897 100644 --- a/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -1837,55 +1837,25 @@ class _defs_weight_paint: class _defs_paint_grease_pencil: - # FIXME: Replace brush tools with code below once they are all implemented: - # - # @staticmethod - # def generate_from_brushes(context): - # # Though `data_block` is conceptually unnecessary with a single brush tool, - # # it's still used because many areas assume that brush tools have it set #bToolRef. - # tool = None - # if context: - # brush = context.tool_settings.gpencil_paint.brush - # if brush: - # tool = brush.gpencil_tool - # return [ - # ToolDef.from_dict( - # dict( - # idname="builtin.brush", - # label="Brush", - # icon="brush.sculpt.paint", - # data_block=tool - # ) - # ) - # ] - - @ToolDef.from_fn - def draw(): - return dict( - idname="builtin_brush.Draw", - label="Draw", - icon="brush.gpencil_draw.draw", - data_block='DRAW', - ) - - @ToolDef.from_fn - def erase(): - return dict( - idname="builtin_brush.Erase", - label="Erase", - icon="brush.gpencil_draw.erase", - data_block='ERASE', - ) - - @ToolDef.from_fn - def tint(): - return dict( - idname="builtin_brush.Tint", - label="Tint", - icon="brush.gpencil_draw.tint", - data_block='TINT', - ) - + @staticmethod + def generate_from_brushes(context): + # Though `data_block` is conceptually unnecessary with a single brush tool, + # it's still used because many areas assume that brush tools have it set #bToolRef. + tool = None + if context: + brush = context.tool_settings.gpencil_paint.brush + if brush: + tool = brush.gpencil_tool + return [ + ToolDef.from_dict( + dict( + idname="builtin.brush", + label="Brush", + icon="brush.sculpt.paint", + data_block=tool + ) + ) + ] class _defs_image_generic: @@ -3329,9 +3299,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): 'PAINT_GREASE_PENCIL': [ _defs_view3d_generic.cursor, None, - _defs_paint_grease_pencil.draw, - _defs_paint_grease_pencil.erase, - _defs_paint_grease_pencil.tint, + _defs_paint_grease_pencil.generate_from_brushes, ], 'PAINT_GPENCIL': [ _defs_view3d_generic.cursor, -- 2.30.2 From c5f3dd13fa32e5bdc6aaf1e48c556debf3edfac9 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 17 Apr 2024 10:59:36 -0400 Subject: [PATCH 182/244] UI: Grammar: Add missing periods in asset save text --- .../editors/interface/templates/interface_templates.cc | 4 ++-- source/blender/windowmanager/intern/wm_files.cc | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/source/blender/editors/interface/templates/interface_templates.cc b/source/blender/editors/interface/templates/interface_templates.cc index 5da5a2ba73c..bcb6fbdb500 100644 --- a/source/blender/editors/interface/templates/interface_templates.cc +++ b/source/blender/editors/interface/templates/interface_templates.cc @@ -6381,9 +6381,9 @@ static void ui_template_status_info_warnings_messages(Main *bmain, tooltip_message += "\n\n"; } tooltip_message += RPT_( - "This file is managed by the Blender asset system\n" + "This file is managed by the Blender asset system.\n" "By editing it as a regular blend file, it will no longer\n" - "be possible to update its assets through the asset browser"); + "be possible to update its assets through the asset browser."); } regular_message = ED_info_statusbar_string_ex(bmain, scene, view_layer, statusbar_info_flag); diff --git a/source/blender/windowmanager/intern/wm_files.cc b/source/blender/windowmanager/intern/wm_files.cc index da531b8f47a..90b18c4b740 100644 --- a/source/blender/windowmanager/intern/wm_files.cc +++ b/source/blender/windowmanager/intern/wm_files.cc @@ -4021,10 +4021,11 @@ static void file_overwrite_detailed_info_show(uiLayout *parent_layout, Main *bma uiItemS_ex(layout, 1.4f); } - uiItemL(layout, RPT_("This file is managed by the Blender asset system"), ICON_NONE); + uiItemL(layout, RPT_("This file is managed by the Blender asset system."), ICON_NONE); uiItemL( - layout, RPT_("By overwriting it as a regular blend file, it will no longer "), ICON_NONE); - uiItemL(layout, RPT_("be possible to update its assets through the asset browser"), ICON_NONE); + layout, RPT_("By overwriting it as a regular blend file, it will no longer"), ICON_NONE); + uiItemL( + layout, RPT_("be possible to update its assets through the asset browser."), ICON_NONE); } } -- 2.30.2 From b8bc1c56f7154b6fba3741f70549be9108c10ba1 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 17 Apr 2024 17:28:48 +0200 Subject: [PATCH 183/244] Brush Assets: Operator to edit asset metadata Add an operator to edit the catalog, author, and description of brush assets. This doesn't edit the preview image yet, I prefer to add that separately. ![image](/attachments/50ab300d-f130-44be-9134-df7a3a15b233) Pull Request: https://projects.blender.org/blender/blender/pulls/120081 --- scripts/startup/bl_ui/space_view3d_toolbar.py | 1 + source/blender/blenkernel/BKE_asset_edit.hh | 14 +- .../blender/blenkernel/intern/asset_edit.cc | 32 +- .../blender/editors/sculpt_paint/paint_ops.cc | 289 ++++++++++++++---- 4 files changed, 240 insertions(+), 96 deletions(-) diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index 59b48288829..cc9163a5351 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -52,6 +52,7 @@ class VIEW3D_MT_brush_context_menu(Menu): layout.separator() + layout.operator("brush.asset_edit_metadata", text="Edit Metadata") layout.operator("brush.asset_update", text="Update Asset") layout.operator("brush.asset_revert", text="Revert to Asset") else: diff --git a/source/blender/blenkernel/BKE_asset_edit.hh b/source/blender/blenkernel/BKE_asset_edit.hh index c7ead3f2c4d..d64d989c8a9 100644 --- a/source/blender/blenkernel/BKE_asset_edit.hh +++ b/source/blender/blenkernel/BKE_asset_edit.hh @@ -33,6 +33,7 @@ #include "DNA_ID_enums.h" struct bUserAssetLibrary; +struct AssetMetaData; struct AssetWeakReference; struct ID; struct Main; @@ -52,14 +53,11 @@ Main *asset_edit_main(const ID &id); bool asset_edit_id_is_editable(const ID &id); -std::optional asset_edit_id_save_as( - Main &global_main, - const ID &id, - StringRef name, - std::optional catalog_id, - std::optional catalog_simple_name, - const bUserAssetLibrary &user_library, - ReportList &reports); +std::optional asset_edit_id_save_as(Main &global_main, + const ID &id, + StringRef name, + const bUserAssetLibrary &user_library, + ReportList &reports); bool asset_edit_id_save(Main &global_main, const ID &id, ReportList &reports); bool asset_edit_id_revert(Main &global_main, const ID &id, ReportList &reports); diff --git a/source/blender/blenkernel/intern/asset_edit.cc b/source/blender/blenkernel/intern/asset_edit.cc index a084efc0619..36883795433 100644 --- a/source/blender/blenkernel/intern/asset_edit.cc +++ b/source/blender/blenkernel/intern/asset_edit.cc @@ -194,12 +194,11 @@ static bool asset_write_in_library(Main *bmain, const ID &id_const, const StringRef name, const StringRefNull filepath, - const std::optional catalog, - const std::optional catalog_simple_name, std::string &final_full_file_path, ReportList &reports) { - /* XXX + /* TODO: Comment seems to be resolved by separate #Main storage? + * XXX * FIXME * * This code is _pure evil_. It does in-place manipulation on IDs in global Main database, @@ -224,8 +223,8 @@ static bool asset_write_in_library(Main *bmain, const int prev_tag = id.tag; const int prev_us = id.us; const std::string prev_name = id.name + 2; + /* TODO: Remove library overrides stuff now that they are not used for brush assets. */ IDOverrideLibrary *prev_liboverride = id.override_library; - AssetMetaData *asset_data = id.asset_data; const int write_flags = 0; /* Could use #G_FILE_COMPRESS ? */ const eBLO_WritePathRemap remap_mode = BLO_WRITE_PATH_REMAP_RELATIVE; @@ -235,18 +234,8 @@ static bool asset_write_in_library(Main *bmain, id.tag &= ~LIB_TAG_RUNTIME; id.us = 1; BLI_strncpy(id.name + 2, name.data(), std::min(sizeof(id.name) - 2, size_t(name.size()))); - if (!ID_IS_ASSET(&id)) { - id.asset_data = id.override_library->reference->asset_data; - } id.override_library = nullptr; - if (catalog) { - id.asset_data->catalog_id = *catalog; - } - if (catalog_simple_name) { - STRNCPY(id.asset_data->catalog_simple_name, catalog_simple_name->c_str()); - } - BKE_blendfile_write_partial_tag_ID(&id, true); /* TODO: check overwriting existing file. */ @@ -266,7 +255,6 @@ static bool asset_write_in_library(Main *bmain, id.us = prev_us; BLI_strncpy(id.name + 2, prev_name.c_str(), sizeof(id.name) - 2); id.override_library = prev_liboverride; - id.asset_data = asset_data; return sucess; } @@ -379,8 +367,6 @@ static AssetEditBlend &asset_edit_blend_file_ensure(const StringRef filepath) std::optional asset_edit_id_save_as(Main &global_main, const ID &id, const StringRef name, - std::optional catalog_id, - std::optional catalog_simple_name, const bUserAssetLibrary &user_library, ReportList &reports) { @@ -391,14 +377,8 @@ std::optional asset_edit_id_save_as(Main &global_main, Main *asset_main = BKE_main_from_id(&global_main, &id); std::string final_full_asset_filepath; - const bool success = asset_write_in_library(asset_main, - id, - name, - filepath, - catalog_id, - catalog_simple_name, - final_full_asset_filepath, - reports); + const bool success = asset_write_in_library( + asset_main, id, name, filepath, final_full_asset_filepath, reports); if (!success) { BKE_report(&reports, RPT_ERROR, "Failed to write to asset library"); return std::nullopt; @@ -421,8 +401,6 @@ bool asset_edit_id_save(Main & /*global_main*/, const ID &id, ReportList &report id, id.name + 2, asset_blend->filepath.c_str(), - std::nullopt, - std::nullopt, final_full_asset_filepath, reports); diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 92ae5e18852..89cea863d6c 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -788,23 +788,49 @@ static AssetWeakReference brush_asset_create_weakref_hack(const bUserAssetLibrar return asset_weak_ref; } -static void refresh_asset_library(const bContext *C, const bUserAssetLibrary &user_library) +static std::optional library_to_library_ref( + const asset_system::AssetLibrary &library) { + for (const AssetLibraryReference &ref : asset_system::all_valid_asset_library_refs()) { + const std::string root_path = AS_asset_library_root_path_from_library_ref(ref); + /* Use #BLI_path_cmp_normalized because `library.root_path()` ends with a slash while + * `root_path` doesn't. */ + if (BLI_path_cmp_normalized(root_path.c_str(), library.root_path().c_str()) == 0) { + return ref; + } + } + return std::nullopt; +} + +static AssetLibraryReference user_library_to_library_ref(const bUserAssetLibrary &user_library) +{ + AssetLibraryReference library_ref{}; + library_ref.custom_library_index = BLI_findindex(&U.asset_libraries, &user_library); + library_ref.type = ASSET_LIBRARY_CUSTOM; + return library_ref; +} + +static const bUserAssetLibrary *library_ref_to_user_library( + const AssetLibraryReference &library_ref) +{ + if (library_ref.type != ASSET_LIBRARY_CUSTOM) { + return nullptr; + } + return static_cast( + BLI_findlink(&U.asset_libraries, library_ref.custom_library_index)); +} + +static void refresh_asset_library(const bContext *C, const AssetLibraryReference &library_ref) +{ + asset::list::clear(&library_ref, C); /* TODO: Should the all library reference be automatically cleared? */ AssetLibraryReference all_lib_ref = asset_system::all_library_reference(); asset::list::clear(&all_lib_ref, C); +} - /* TODO: this is convoluted, can we create a reference from pointer? */ - for (const AssetLibraryReference &lib_ref : asset_system::all_valid_asset_library_refs()) { - if (lib_ref.type == ASSET_LIBRARY_CUSTOM) { - const bUserAssetLibrary *ref_user_library = BKE_preferences_asset_library_find_index( - &U, lib_ref.custom_library_index); - if (ref_user_library == &user_library) { - asset::list::clear(&lib_ref, C); - return; - } - } - } +static void refresh_asset_library(const bContext *C, const bUserAssetLibrary &user_library) +{ + refresh_asset_library(C, user_library_to_library_ref(user_library)); } static bool brush_asset_save_as_poll(bContext *C) @@ -858,14 +884,6 @@ static asset_system::AssetCatalog &asset_library_ensure_catalogs_in_path( return *library.catalog_service().find_catalog_by_path(path); } -static AssetLibraryReference user_library_to_library_ref(const bUserAssetLibrary &user_library) -{ - AssetLibraryReference library_ref{}; - library_ref.custom_library_index = BLI_findindex(&U.asset_libraries, &user_library); - library_ref.type = ASSET_LIBRARY_CUSTOM; - return library_ref; -} - static int brush_asset_save_as_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); @@ -905,19 +923,15 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) char catalog_path[MAX_NAME]; RNA_string_get(op->ptr, "catalog_path", catalog_path); - std::optional catalog_id; - std::optional catalog_simple_name; - + AssetMetaData &meta_data = *brush->id.asset_data; if (catalog_path[0]) { const asset_system::AssetCatalog &catalog = asset_library_ensure_catalogs_in_path( *library, catalog_path); - catalog_id = catalog.catalog_id; - catalog_simple_name = catalog.simple_name; + BKE_asset_metadata_catalog_id_set(&meta_data, catalog.catalog_id, catalog.simple_name.c_str()); } const std::optional final_full_asset_filepath = bke::asset_edit_id_save_as( - *bmain, brush->id, name, catalog_id, catalog_simple_name, *user_library, *op->reports); - + *bmain, brush->id, name, *user_library, *op->reports); if (!final_full_asset_filepath) { return OPERATOR_CANCELLED; } @@ -942,20 +956,6 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -static std::optional library_to_library_ref( - const asset_system::AssetLibrary &library) -{ - for (const AssetLibraryReference &ref : asset_system::all_valid_asset_library_refs()) { - const std::string root_path = AS_asset_library_root_path_from_library_ref(ref); - /* Use #BLI_path_cmp_normalized because `library.root_path()` ends with a slash while - * `root_path` doesn't. */ - if (BLI_path_cmp_normalized(root_path.c_str(), library.root_path().c_str()) == 0) { - return ref; - } - } - return std::nullopt; -} - static bool library_is_editable(const AssetLibraryReference &library) { if (library.type == ASSET_LIBRARY_ESSENTIALS) { @@ -967,12 +967,10 @@ static bool library_is_editable(const AssetLibraryReference &library) static int brush_asset_save_as_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) { Paint *paint = BKE_paint_get_active_from_context(C); - Brush *brush = BKE_paint_brush(paint); const AssetWeakReference &brush_weak_ref = *paint->brush_asset_reference; const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref( *C, brush_weak_ref, op->reports); if (!asset) { - BLI_assert_unreachable(); return OPERATOR_CANCELLED; } const asset_system::AssetLibrary &library = asset->owner_asset_library(); @@ -982,7 +980,7 @@ static int brush_asset_save_as_invoke(bContext *C, wmOperator *op, const wmEvent return OPERATOR_CANCELLED; } - RNA_string_set(op->ptr, "name", brush->id.name + 2); + RNA_string_set(op->ptr, "name", asset->get_name().c_str()); /* If the library isn't saved from the operator's last execution, find the current library or the * first library if the current library isn't editable. */ @@ -1027,27 +1025,20 @@ static const EnumPropertyItem *rna_asset_library_reference_itemf(bContext * /*C* return items; } -static void visit_asset_catalog_for_search_fn( - const bContext *C, - PointerRNA *ptr, - PropertyRNA * /*prop*/, - const char *edit_text, - FunctionRef visit_fn) +static void visit_library_catalogs_catalog_for_search( + const Main &bmain, + const bUserAssetLibrary &user_library, + const StringRef edit_text, + const FunctionRef visit_fn) { - /* NOTE: Using the all library would also be a valid choice. */ - const bUserAssetLibrary *user_library = get_asset_library_from_prop(*ptr); - if (!user_library) { - return; - } - - asset_system::AssetLibrary *library = AS_asset_library_load( - CTX_data_main(C), user_library_to_library_ref(*user_library)); + const asset_system::AssetLibrary *library = AS_asset_library_load( + &bmain, user_library_to_library_ref(user_library)); if (!library) { return; } - if (edit_text && edit_text[0] != '\0') { - asset_system::AssetCatalogPath edit_path = edit_text; + if (!edit_text.is_empty()) { + const asset_system::AssetCatalogPath edit_path = edit_text; if (!library->catalog_service().find_catalog_by_path(edit_path)) { visit_fn(StringPropertySearchVisitParams{edit_path.str(), std::nullopt, ICON_ADD}); } @@ -1059,6 +1050,20 @@ static void visit_asset_catalog_for_search_fn( }); } +static void visit_library_prop_catalogs_catalog_for_search_fn( + const bContext *C, + PointerRNA *ptr, + PropertyRNA * /*prop*/, + const char *edit_text, + FunctionRef visit_fn) +{ + /* NOTE: Using the all library would also be a valid choice. */ + if (const bUserAssetLibrary *user_library = get_asset_library_from_prop(*ptr)) { + visit_library_catalogs_catalog_for_search( + *CTX_data_main(C), *user_library, edit_text, visit_fn); + } +} + static void BRUSH_OT_asset_save_as(wmOperatorType *ot) { ot->name = "Save as Brush Asset"; @@ -1081,7 +1086,168 @@ static void BRUSH_OT_asset_save_as(wmOperatorType *ot) prop = RNA_def_string( ot->srna, "catalog_path", nullptr, MAX_NAME, "Catalog", "Catalog to use for the new asset"); RNA_def_property_string_search_func_runtime( - prop, visit_asset_catalog_for_search_fn, PROP_STRING_SEARCH_SUGGESTION); + prop, visit_library_prop_catalogs_catalog_for_search_fn, PROP_STRING_SEARCH_SUGGESTION); +} + +static int brush_asset_edit_metadata_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + const Paint *paint = BKE_paint_get_active_from_context(C); + const Brush *brush = (paint) ? BKE_paint_brush_for_read(paint) : nullptr; + BLI_assert(ID_IS_ASSET(&brush->id)); + const AssetWeakReference &brush_weak_ref = *paint->brush_asset_reference; + const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref( + *C, brush_weak_ref, op->reports); + if (!asset) { + return OPERATOR_CANCELLED; + } + const asset_system::AssetLibrary &library_const = asset->owner_asset_library(); + const AssetLibraryReference library_ref = *library_to_library_ref(library_const); + asset_system::AssetLibrary *library = AS_asset_library_load(bmain, library_ref); + + char catalog_path[MAX_NAME]; + RNA_string_get(op->ptr, "catalog_path", catalog_path); + + AssetMetaData &meta_data = *brush->id.asset_data; + MEM_SAFE_FREE(meta_data.author); + meta_data.author = RNA_string_get_alloc(op->ptr, "author", nullptr, 0, nullptr); + MEM_SAFE_FREE(meta_data.description); + meta_data.description = RNA_string_get_alloc(op->ptr, "description", nullptr, 0, nullptr); + + if (catalog_path[0]) { + const asset_system::AssetCatalog &catalog = asset_library_ensure_catalogs_in_path( + *library, catalog_path); + BKE_asset_metadata_catalog_id_set(&meta_data, catalog.catalog_id, catalog.simple_name.c_str()); + } + + if (!bke::asset_edit_id_save(*bmain, brush->id, *op->reports)) { + return OPERATOR_CANCELLED; + } + + char asset_full_path_buffer[FILE_MAX_LIBEXTRA]; + char *file_path = nullptr; + AS_asset_full_path_explode_from_weak_ref( + &brush_weak_ref, asset_full_path_buffer, &file_path, nullptr, nullptr); + if (!file_path) { + BLI_assert_unreachable(); + return OPERATOR_CANCELLED; + } + + library->catalog_service().write_to_disk(file_path); + + refresh_asset_library(C, library_ref); + WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_EDITED, nullptr); + + return OPERATOR_FINISHED; +} + +static int brush_asset_edit_metadata_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) +{ + const Paint *paint = BKE_paint_get_active_from_context(C); + const AssetWeakReference &brush_weak_ref = *paint->brush_asset_reference; + const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref( + *C, brush_weak_ref, op->reports); + if (!asset) { + return OPERATOR_CANCELLED; + } + const asset_system::AssetLibrary &library = asset->owner_asset_library(); + const AssetMetaData &meta_data = asset->get_metadata(); + + if (!RNA_struct_property_is_set(op->ptr, "catalog_path")) { + const asset_system::CatalogID &id = meta_data.catalog_id; + if (const asset_system::AssetCatalog *catalog = library.catalog_service().find_catalog(id)) { + RNA_string_set(op->ptr, "catalog_path", catalog->path.c_str()); + } + } + if (!RNA_struct_property_is_set(op->ptr, "author")) { + RNA_string_set(op->ptr, "author", meta_data.author ? meta_data.author : ""); + } + if (!RNA_struct_property_is_set(op->ptr, "description")) { + RNA_string_set(op->ptr, "description", meta_data.description ? meta_data.description : ""); + } + + return WM_operator_props_dialog_popup(C, op, 400, std::nullopt, IFACE_("Edit Metadata")); +} + +static void visit_active_library_catalogs_catalog_for_search_fn( + const bContext *C, + PointerRNA * /*ptr*/, + PropertyRNA * /*prop*/, + const char *edit_text, + FunctionRef visit_fn) +{ + const Paint *paint = BKE_paint_get_active_from_context(C); + const AssetWeakReference &brush_weak_ref = *paint->brush_asset_reference; + const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref( + *C, brush_weak_ref, nullptr); + if (!asset) { + return; + } + const asset_system::AssetLibrary &library = asset->owner_asset_library(); + + /* NOTE: Using the all library would also be a valid choice. */ + visit_library_catalogs_catalog_for_search( + *CTX_data_main(C), + *library_ref_to_user_library(*library_to_library_ref(library)), + edit_text, + visit_fn); +} + +static bool brush_asset_edit_metadata_poll(bContext *C) +{ + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr; + if (paint == nullptr || brush == nullptr) { + return false; + } + if (!ID_IS_ASSET(&brush->id)) { + BLI_assert_unreachable(); + return false; + } + const AssetWeakReference *brush_weak_ref = paint->brush_asset_reference; + if (!brush_weak_ref) { + BLI_assert_unreachable(); + return false; + } + const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref( + *C, *brush_weak_ref, nullptr); + if (!asset) { + BLI_assert_unreachable(); + return false; + } + const std::optional library_ref = library_to_library_ref( + asset->owner_asset_library()); + if (!library_ref) { + BLI_assert_unreachable(); + return false; + } + if (!library_is_editable(*library_ref)) { + CTX_wm_operator_poll_msg_set(C, "Asset library is not editable"); + return false; + } + if (!bke::asset_edit_id_is_editable(brush->id)) { + CTX_wm_operator_poll_msg_set(C, "Asset file is not editable"); + return false; + } + return true; +} + +static void BRUSH_OT_asset_edit_metadata(wmOperatorType *ot) +{ + ot->name = "Edit Metadata"; + ot->description = "Edit asset information like the catalog, preview image, tags, or author"; + ot->idname = "BRUSH_OT_asset_edit_metadata"; + + ot->exec = brush_asset_edit_metadata_exec; + ot->invoke = brush_asset_edit_metadata_invoke; + ot->poll = brush_asset_edit_metadata_poll; + + PropertyRNA *prop = RNA_def_string( + ot->srna, "catalog_path", nullptr, MAX_NAME, "Catalog", "The asset's catalog path"); + RNA_def_property_string_search_func_runtime( + prop, visit_active_library_catalogs_catalog_for_search_fn, PROP_STRING_SEARCH_SUGGESTION); + RNA_def_string(ot->srna, "author", nullptr, MAX_NAME, "Author", ""); + RNA_def_string(ot->srna, "description", nullptr, MAX_NAME, "Description", ""); } static bool brush_asset_delete_poll(bContext *C) @@ -1710,6 +1876,7 @@ void ED_operatortypes_paint() WM_operatortype_append(BRUSH_OT_stencil_reset_transform); WM_operatortype_append(BRUSH_OT_asset_select); WM_operatortype_append(BRUSH_OT_asset_save_as); + WM_operatortype_append(BRUSH_OT_asset_edit_metadata); WM_operatortype_append(BRUSH_OT_asset_delete); WM_operatortype_append(BRUSH_OT_asset_update); WM_operatortype_append(BRUSH_OT_asset_revert); -- 2.30.2 From c01e9a66370b16070ce6581be478c8187c0aee11 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 18 Apr 2024 14:27:49 +0200 Subject: [PATCH 184/244] Brush Assets: Allow manually saving managed asset files The asset system makes some assumptions about these `.asset.blend` files that make working with them much easier. Disallowing manual saving was a way to help keep those assumptions true. However it also gets in the way when working with these files. This PR removes the stripping of the ".asset" part of the file path and rephrases the warning message. Pull Request: https://projects.blender.org/blender/blender/pulls/120746 --- .../interface/templates/interface_templates.cc | 6 +++--- source/blender/windowmanager/intern/wm_files.cc | 17 ++--------------- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/source/blender/editors/interface/templates/interface_templates.cc b/source/blender/editors/interface/templates/interface_templates.cc index bcb6fbdb500..b7a6bb0ed94 100644 --- a/source/blender/editors/interface/templates/interface_templates.cc +++ b/source/blender/editors/interface/templates/interface_templates.cc @@ -6381,9 +6381,9 @@ static void ui_template_status_info_warnings_messages(Main *bmain, tooltip_message += "\n\n"; } tooltip_message += RPT_( - "This file is managed by the Blender asset system.\n" - "By editing it as a regular blend file, it will no longer\n" - "be possible to update its assets through the asset browser."); + "This file is managed by the Blender asset system\n" + "and is expected to contain a single asset data-block.\n" + "Take care to avoid data loss when editing assets."); } regular_message = ED_info_statusbar_string_ex(bmain, scene, view_layer, statusbar_info_flag); diff --git a/source/blender/windowmanager/intern/wm_files.cc b/source/blender/windowmanager/intern/wm_files.cc index 90b18c4b740..46c9690d30f 100644 --- a/source/blender/windowmanager/intern/wm_files.cc +++ b/source/blender/windowmanager/intern/wm_files.cc @@ -4022,10 +4022,8 @@ static void file_overwrite_detailed_info_show(uiLayout *parent_layout, Main *bma } uiItemL(layout, RPT_("This file is managed by the Blender asset system."), ICON_NONE); - uiItemL( - layout, RPT_("By overwriting it as a regular blend file, it will no longer"), ICON_NONE); - uiItemL( - layout, RPT_("be possible to update its assets through the asset browser."), ICON_NONE); + uiItemL(layout, RPT_("and is expected to contain a single asset data-block."), ICON_NONE); + uiItemL(layout, RPT_("Take care to avoid data loss when editing assets."), ICON_NONE); } } @@ -4096,17 +4094,6 @@ static void save_file_overwrite_saveas(bContext *C, void *arg_block, void * /*ar wmOperatorType *ot = WM_operatortype_find("WM_OT_save_as_mainfile", false); WM_operator_properties_create_ptr(&props_ptr, ot); - if (bmain->is_asset_repository) { - /* If needed, substitute the 'proposed' Save As filepath by replacing the `.asset.blend` part - * of it by just `.blend`. */ - std::string filepath = BKE_main_blendfile_path(bmain); - if (blender::StringRef(filepath).endswith(BLENDER_ASSET_FILE_SUFFIX)) { - filepath.replace( - filepath.rfind(BLENDER_ASSET_FILE_SUFFIX), strlen(BLENDER_ASSET_FILE_SUFFIX), ".blend"); - RNA_string_set(&props_ptr, "filepath", filepath.c_str()); - } - } - WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr, nullptr); WM_operator_properties_free(&props_ptr); } -- 2.30.2 From 7ed46913100f81547fc570e95b310a0a99fee3d6 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 19 Apr 2024 16:08:03 -0400 Subject: [PATCH 185/244] Cleanup: Unused variable --- source/blender/windowmanager/intern/wm_files.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/source/blender/windowmanager/intern/wm_files.cc b/source/blender/windowmanager/intern/wm_files.cc index 46c9690d30f..2feb901c5a1 100644 --- a/source/blender/windowmanager/intern/wm_files.cc +++ b/source/blender/windowmanager/intern/wm_files.cc @@ -4086,7 +4086,6 @@ static void save_file_overwrite_confirm_button(uiBlock *block, wmGenericCallback static void save_file_overwrite_saveas(bContext *C, void *arg_block, void * /*arg_data*/) { - Main *bmain = CTX_data_main(C); wmWindow *win = CTX_wm_window(C); UI_popup_block_close(C, win, static_cast(arg_block)); -- 2.30.2 From f2086022ee49b78362f6e63e8af48ba37f3f357e Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 24 Apr 2024 17:37:23 +0200 Subject: [PATCH 186/244] Brush Assets: Make enabled catalog storage in preferences optional Adds a new asset shelf option (`STORE_ENABLED_CATALOGS_IN_PREFERENCES` option in BPY) to use the Preferences for storing the enabled catalogs. This way asset shelf types can decide if for their use-case, they want to synchronize the enabled catalogs over Blender sessions and files, or keep the stored locally in the file. This is important because for example on one hand, it would be annoying if for brush assets you'd have to enable the visible catalog tabs for every 3D View and every file, while on the other hand you need that level of control for the pose library where the catalogs the rigger/animator cares about varies from project to project, character to character and shot to shot. Conceptually this also makes some sense: The new brush assets workflow synchronizes brush assets and their catalogs across Blender sessions and files, basically making them globally accessible independent of the current file/project, so treating the enabled catalogs the same is consistent. Pull Request: https://projects.blender.org/blender/blender/pulls/120264 --- scripts/startup/bl_ui/space_view3d.py | 2 +- source/blender/blenkernel/BKE_asset.hh | 10 +++ source/blender/blenkernel/BKE_preferences.h | 5 -- source/blender/blenkernel/BKE_screen.hh | 1 + .../blenkernel/intern/asset_weak_reference.cc | 55 ++++++++++++++ source/blender/blenkernel/intern/blender.cc | 3 +- .../blender/blenkernel/intern/preferences.cc | 36 +-------- source/blender/blenloader/intern/readfile.cc | 5 +- source/blender/blenloader/intern/writefile.cc | 6 +- .../editors/asset/intern/asset_shelf.hh | 8 +- .../asset/intern/asset_shelf_settings.cc | 73 +++++++++++++++---- source/blender/makesdna/DNA_asset_types.h | 5 ++ source/blender/makesdna/DNA_screen_types.h | 1 + source/blender/makesdna/DNA_userdef_types.h | 2 +- source/blender/makesrna/intern/rna_ui.cc | 6 ++ 15 files changed, 154 insertions(+), 64 deletions(-) diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index f42c1063f59..ea55a6b98f3 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -8934,7 +8934,7 @@ class VIEW3D_PT_viewport_debug(Panel): class BrushAssetShelf: bl_space_type = "VIEW_3D" - bl_options = {'DEFAULT_VISIBLE', 'NO_ASSET_DRAG'} + bl_options = {'DEFAULT_VISIBLE', 'NO_ASSET_DRAG', 'STORE_ENABLED_CATALOGS_IN_PREFERENCES'} bl_activate_operator = "BRUSH_OT_asset_select" bl_default_preview_size = 48 diff --git a/source/blender/blenkernel/BKE_asset.hh b/source/blender/blenkernel/BKE_asset.hh index bc8fd7a249f..4f6a56e0f26 100644 --- a/source/blender/blenkernel/BKE_asset.hh +++ b/source/blender/blenkernel/BKE_asset.hh @@ -80,3 +80,13 @@ void BKE_asset_metadata_read(BlendDataReader *reader, AssetMetaData *asset_data) void BKE_asset_weak_reference_write(BlendWriter *writer, const AssetWeakReference *weak_ref); void BKE_asset_weak_reference_read(BlendDataReader *reader, AssetWeakReference *weak_ref); + +void BKE_asset_catalog_path_list_free(ListBase &catalog_path_list); +ListBase BKE_asset_catalog_path_list_duplicate(const ListBase &catalog_path_list); +void BKE_asset_catalog_path_list_blend_write(BlendWriter *writer, + const ListBase &catalog_path_list); +void BKE_asset_catalog_path_list_blend_read_data(BlendDataReader *reader, + ListBase &catalog_path_list); +bool BKE_asset_catalog_path_list_has_path(const ListBase &catalog_path_list, + const char *catalog_path); +void BKE_asset_catalog_path_list_add_path(ListBase &catalog_path_list, const char *catalog_path); diff --git a/source/blender/blenkernel/BKE_preferences.h b/source/blender/blenkernel/BKE_preferences.h index 7f2e4e25611..7238a07fd5c 100644 --- a/source/blender/blenkernel/BKE_preferences.h +++ b/source/blender/blenkernel/BKE_preferences.h @@ -147,8 +147,3 @@ bool BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(UserDef *u #ifdef __cplusplus } #endif - -void BKE_preferences_asset_shelf_settings_clear_enabled_catalog_paths(const UserDef *userdef, - const char *shelf_idname); -void BKE_preferences_asset_shelf_settings_clear_enabled_catalog_paths( - bUserAssetShelfSettings *settings); diff --git a/source/blender/blenkernel/BKE_screen.hh b/source/blender/blenkernel/BKE_screen.hh index 674d0a7d7bd..2419a127e06 100644 --- a/source/blender/blenkernel/BKE_screen.hh +++ b/source/blender/blenkernel/BKE_screen.hh @@ -523,6 +523,7 @@ enum AssetShelfTypeFlag { * keymap items then. */ ASSET_SHELF_TYPE_FLAG_NO_ASSET_DRAG = (1 << 0), ASSET_SHELF_TYPE_FLAG_DEFAULT_VISIBLE = (1 << 1), + ASSET_SHELF_TYPE_FLAG_STORE_CATALOGS_IN_PREFS = (1 << 2), ASSET_SHELF_TYPE_FLAG_MAX }; diff --git a/source/blender/blenkernel/intern/asset_weak_reference.cc b/source/blender/blenkernel/intern/asset_weak_reference.cc index 3a08698acad..9f6ecb3152b 100644 --- a/source/blender/blenkernel/intern/asset_weak_reference.cc +++ b/source/blender/blenkernel/intern/asset_weak_reference.cc @@ -129,3 +129,58 @@ 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); } + +void BKE_asset_catalog_path_list_free(ListBase &catalog_path_list) +{ + LISTBASE_FOREACH_MUTABLE (AssetCatalogPathLink *, catalog_path, &catalog_path_list) { + MEM_delete(catalog_path->path); + BLI_freelinkN(&catalog_path_list, catalog_path); + } + BLI_assert(BLI_listbase_is_empty(&catalog_path_list)); +} + +ListBase BKE_asset_catalog_path_list_duplicate(const ListBase &catalog_path_list) +{ + ListBase duplicated_list = {nullptr}; + + LISTBASE_FOREACH (AssetCatalogPathLink *, catalog_path, &catalog_path_list) { + AssetCatalogPathLink *copied_path = MEM_cnew(__func__); + copied_path->path = BLI_strdup(catalog_path->path); + + BLI_addtail(&duplicated_list, copied_path); + } + + return duplicated_list; +} + +void BKE_asset_catalog_path_list_blend_write(BlendWriter *writer, + const ListBase &catalog_path_list) +{ + LISTBASE_FOREACH (const AssetCatalogPathLink *, catalog_path, &catalog_path_list) { + BLO_write_struct(writer, AssetCatalogPathLink, catalog_path); + BLO_write_string(writer, catalog_path->path); + } +} + +void BKE_asset_catalog_path_list_blend_read_data(BlendDataReader *reader, + ListBase &catalog_path_list) +{ + BLO_read_list(reader, &catalog_path_list); + LISTBASE_FOREACH (AssetCatalogPathLink *, catalog_path, &catalog_path_list) { + BLO_read_data_address(reader, &catalog_path->path); + } +} + +bool BKE_asset_catalog_path_list_has_path(const ListBase &catalog_path_list, + const char *catalog_path) +{ + return BLI_findstring_ptr( + &catalog_path_list, catalog_path, offsetof(AssetCatalogPathLink, path)) != nullptr; +} + +void BKE_asset_catalog_path_list_add_path(ListBase &catalog_path_list, const char *catalog_path) +{ + AssetCatalogPathLink *new_path = MEM_cnew(__func__); + new_path->path = BLI_strdup(catalog_path); + BLI_addtail(&catalog_path_list, new_path); +} diff --git a/source/blender/blenkernel/intern/blender.cc b/source/blender/blenkernel/intern/blender.cc index 07c4a9dd714..efc7ef907fa 100644 --- a/source/blender/blenkernel/intern/blender.cc +++ b/source/blender/blenkernel/intern/blender.cc @@ -22,6 +22,7 @@ #include "IMB_moviecache.hh" #include "BKE_addon.h" +#include "BKE_asset.hh" #include "BKE_asset_edit.hh" #include "BKE_blender.hh" /* own include */ #include "BKE_blender_user_menu.hh" /* own include */ @@ -349,7 +350,7 @@ void BKE_blender_userdef_data_free(UserDef *userdef, bool clear_fonts) BLI_freelistN(&userdef->extension_repos); LISTBASE_FOREACH_MUTABLE (bUserAssetShelfSettings *, settings, &userdef->asset_shelves_settings) { - BKE_preferences_asset_shelf_settings_clear_enabled_catalog_paths(settings); + BKE_asset_catalog_path_list_free(settings->enabled_catalog_paths); MEM_freeN(settings); } BLI_listbase_clear(&userdef->asset_shelves_settings); diff --git a/source/blender/blenkernel/intern/preferences.cc b/source/blender/blenkernel/intern/preferences.cc index 41b7b78fafd..f7f0e36d288 100644 --- a/source/blender/blenkernel/intern/preferences.cc +++ b/source/blender/blenkernel/intern/preferences.cc @@ -20,10 +20,12 @@ #include "BLI_string_utils.hh" #include "BKE_appdir.hh" +#include "BKE_asset.hh" #include "BKE_preferences.h" #include "BLT_translation.hh" +#include "DNA_asset_types.h" #include "DNA_defaults.h" #include "DNA_userdef_types.h" @@ -435,13 +437,6 @@ bUserAssetShelfSettings *BKE_preferences_asset_shelf_settings_get(const UserDef offsetof(bUserAssetShelfSettings, shelf_idname))); } -static bool asset_shelf_settings_is_catalog_path_enabled(const bUserAssetShelfSettings *settings, - const char *catalog_path) -{ - return BLI_findstring_ptr( - &settings->enabled_catalog_paths, catalog_path, offsetof(LinkData, data)) != nullptr; -} - bool BKE_preferences_asset_shelf_settings_is_catalog_path_enabled(const UserDef *userdef, const char *shelf_idname, const char *catalog_path) @@ -451,7 +446,7 @@ bool BKE_preferences_asset_shelf_settings_is_catalog_path_enabled(const UserDef if (!settings) { return false; } - return asset_shelf_settings_is_catalog_path_enabled(settings, catalog_path); + return BKE_asset_catalog_path_list_has_path(settings->enabled_catalog_paths, catalog_path); } bool BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(UserDef *userdef, @@ -465,31 +460,8 @@ bool BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(UserDef *u } bUserAssetShelfSettings *settings = asset_shelf_settings_ensure(userdef, shelf_idname); - - char *path_copy = BLI_strdup(catalog_path); - BLI_addtail(&settings->enabled_catalog_paths, BLI_genericNodeN(path_copy)); + BKE_asset_catalog_path_list_add_path(settings->enabled_catalog_paths, catalog_path); return true; } -void BKE_preferences_asset_shelf_settings_clear_enabled_catalog_paths( - bUserAssetShelfSettings *settings) -{ - LISTBASE_FOREACH_MUTABLE (LinkData *, path_link, &settings->enabled_catalog_paths) { - MEM_freeN(path_link->data); - BLI_freelinkN(&settings->enabled_catalog_paths, path_link); - } - BLI_assert(BLI_listbase_is_empty(&settings->enabled_catalog_paths)); -} - -void BKE_preferences_asset_shelf_settings_clear_enabled_catalog_paths(const UserDef *userdef, - const char *shelf_idname) -{ - bUserAssetShelfSettings *settings = BKE_preferences_asset_shelf_settings_get(userdef, - shelf_idname); - if (!settings) { - return; - } - BKE_preferences_asset_shelf_settings_clear_enabled_catalog_paths(settings); -} - /** \} */ diff --git a/source/blender/blenloader/intern/readfile.cc b/source/blender/blenloader/intern/readfile.cc index 475697f4174..8e3906a9294 100644 --- a/source/blender/blenloader/intern/readfile.cc +++ b/source/blender/blenloader/intern/readfile.cc @@ -3433,10 +3433,7 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead) } LISTBASE_FOREACH (bUserAssetShelfSettings *, shelf_settings, &user->asset_shelves_settings) { - BLO_read_list(reader, &shelf_settings->enabled_catalog_paths); - LISTBASE_FOREACH (LinkData *, path_link, &shelf_settings->enabled_catalog_paths) { - BLO_read_data_address(reader, &path_link->data); - } + BKE_asset_catalog_path_list_blend_read_data(reader, shelf_settings->enabled_catalog_paths); } /* XXX */ diff --git a/source/blender/blenloader/intern/writefile.cc b/source/blender/blenloader/intern/writefile.cc index c3b6551feb9..df4cd0d765d 100644 --- a/source/blender/blenloader/intern/writefile.cc +++ b/source/blender/blenloader/intern/writefile.cc @@ -100,6 +100,7 @@ #include "MEM_guardedalloc.h" /* MEM_freeN */ +#include "BKE_asset.hh" #include "BKE_blender_version.h" #include "BKE_bpath.hh" #include "BKE_global.hh" /* For #Global `G`. */ @@ -938,10 +939,7 @@ static void write_userdef(BlendWriter *writer, const UserDef *userdef) const bUserAssetShelfSettings *, shelf_settings, &userdef->asset_shelves_settings) { BLO_write_struct(writer, bUserAssetShelfSettings, shelf_settings); - LISTBASE_FOREACH (const LinkData *, path_link, &shelf_settings->enabled_catalog_paths) { - BLO_write_struct(writer, LinkData, path_link); - BLO_write_string(writer, (const char *)path_link->data); - } + BKE_asset_catalog_path_list_blend_write(writer, shelf_settings->enabled_catalog_paths); } LISTBASE_FOREACH (const uiStyle *, style, &userdef->uistyles) { diff --git a/source/blender/editors/asset/intern/asset_shelf.hh b/source/blender/editors/asset/intern/asset_shelf.hh index 42dc320ee82..b2a34fbc3c5 100644 --- a/source/blender/editors/asset/intern/asset_shelf.hh +++ b/source/blender/editors/asset/intern/asset_shelf.hh @@ -58,10 +58,14 @@ void settings_set_all_catalog_active(AssetShelfSettings &settings); bool settings_is_active_catalog(const AssetShelfSettings &settings, const asset_system::AssetCatalogPath &path); bool settings_is_all_catalog_active(const AssetShelfSettings &settings); -void settings_clear_enabled_catalogs(const AssetShelf &shelf); +/** + * Clears the list of enabled catalogs in either the Preferences (if any) or the asset shelf + * settings (if any), depending on the #ASSET_SHELF_TYPE_FLAG_STORE_CATALOGS_IN_PREFS flag. + */ +void settings_clear_enabled_catalogs(AssetShelf &shelf); bool settings_is_catalog_path_enabled(const AssetShelf &shelf, const asset_system::AssetCatalogPath &path); -void settings_set_catalog_path_enabled(const AssetShelf &shelf, +void settings_set_catalog_path_enabled(AssetShelf &shelf, const asset_system::AssetCatalogPath &path); void settings_foreach_enabled_catalog_path( diff --git a/source/blender/editors/asset/intern/asset_shelf_settings.cc b/source/blender/editors/asset/intern/asset_shelf_settings.cc index 63e2e200199..85f1a313476 100644 --- a/source/blender/editors/asset/intern/asset_shelf_settings.cc +++ b/source/blender/editors/asset/intern/asset_shelf_settings.cc @@ -20,7 +20,9 @@ #include "BLI_string.h" #include "BLI_string_ref.hh" +#include "BKE_asset.hh" #include "BKE_preferences.h" +#include "BKE_screen.hh" #include "asset_shelf.hh" @@ -47,11 +49,15 @@ AssetShelfSettings &AssetShelfSettings::operator=(const AssetShelfSettings &othe if (active_catalog_path) { active_catalog_path = BLI_strdup(other.active_catalog_path); } + BKE_asset_catalog_path_list_free(enabled_catalog_paths); + enabled_catalog_paths = BKE_asset_catalog_path_list_duplicate(other.enabled_catalog_paths); + return *this; } AssetShelfSettings::~AssetShelfSettings() { + BKE_asset_catalog_path_list_free(enabled_catalog_paths); MEM_delete(active_catalog_path); } @@ -60,11 +66,14 @@ namespace blender::ed::asset::shelf { void settings_blend_write(BlendWriter *writer, const AssetShelfSettings &settings) { BLO_write_struct(writer, AssetShelfSettings, &settings); + + BKE_asset_catalog_path_list_blend_write(writer, settings.enabled_catalog_paths); BLO_write_string(writer, settings.active_catalog_path); } void settings_blend_read_data(BlendDataReader *reader, AssetShelfSettings &settings) { + BKE_asset_catalog_path_list_blend_read_data(reader, settings.enabled_catalog_paths); BLO_read_data_address(reader, &settings.active_catalog_path); } @@ -92,25 +101,62 @@ bool settings_is_all_catalog_active(const AssetShelfSettings &settings) return !settings.active_catalog_path || !settings.active_catalog_path[0]; } -void settings_clear_enabled_catalogs(const AssetShelf &shelf) +static bool use_enabled_catalogs_from_prefs(const AssetShelf &shelf) { - BKE_preferences_asset_shelf_settings_clear_enabled_catalog_paths(&U, shelf.idname); + return shelf.type && (shelf.type->flag & ASSET_SHELF_TYPE_FLAG_STORE_CATALOGS_IN_PREFS); +} + +static const ListBase *get_enabled_catalog_path_list(const AssetShelf &shelf) +{ + if (use_enabled_catalogs_from_prefs(shelf)) { + bUserAssetShelfSettings *pref_settings = BKE_preferences_asset_shelf_settings_get( + &U, shelf.idname); + return pref_settings ? &pref_settings->enabled_catalog_paths : nullptr; + } + return &shelf.settings.enabled_catalog_paths; +} + +static ListBase *get_enabled_catalog_path_list(AssetShelf &shelf) +{ + return const_cast( + get_enabled_catalog_path_list(const_cast(shelf))); +} + +void settings_clear_enabled_catalogs(AssetShelf &shelf) +{ + ListBase *enabled_catalog_paths = get_enabled_catalog_path_list(shelf); + if (enabled_catalog_paths) { + BKE_asset_catalog_path_list_free(*enabled_catalog_paths); + BLI_assert(BLI_listbase_is_empty(enabled_catalog_paths)); + } } bool settings_is_catalog_path_enabled(const AssetShelf &shelf, const asset_system::AssetCatalogPath &path) { - return BKE_preferences_asset_shelf_settings_is_catalog_path_enabled( - &U, shelf.idname, path.c_str()); + const ListBase *enabled_catalog_paths = get_enabled_catalog_path_list(shelf); + if (!enabled_catalog_paths) { + return false; + } + + return BKE_asset_catalog_path_list_has_path(*enabled_catalog_paths, path.c_str()); } -void settings_set_catalog_path_enabled(const AssetShelf &shelf, +void settings_set_catalog_path_enabled(AssetShelf &shelf, const asset_system::AssetCatalogPath &path) { - if (BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled( - &U, shelf.idname, path.c_str())) - { - U.runtime.is_dirty = true; + if (use_enabled_catalogs_from_prefs(shelf)) { + if (BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled( + &U, shelf.idname, path.c_str())) + { + U.runtime.is_dirty = true; + } + } + else { + if (!BKE_asset_catalog_path_list_has_path(shelf.settings.enabled_catalog_paths, path.c_str())) + { + BKE_asset_catalog_path_list_add_path(shelf.settings.enabled_catalog_paths, path.c_str()); + } } } @@ -118,14 +164,13 @@ void settings_foreach_enabled_catalog_path( const AssetShelf &shelf, FunctionRef fn) { - const bUserAssetShelfSettings *pref_settings = BKE_preferences_asset_shelf_settings_get( - &U, shelf.idname); - if (!pref_settings) { + const ListBase *enabled_catalog_paths = get_enabled_catalog_path_list(shelf); + if (!enabled_catalog_paths) { return; } - LISTBASE_FOREACH (LinkData *, path_link, &pref_settings->enabled_catalog_paths) { - fn(asset_system::AssetCatalogPath((char *)path_link->data)); + LISTBASE_FOREACH (const AssetCatalogPathLink *, path_link, enabled_catalog_paths) { + fn(asset_system::AssetCatalogPath(path_link->path)); } } diff --git a/source/blender/makesdna/DNA_asset_types.h b/source/blender/makesdna/DNA_asset_types.h index 5ae7c074772..b92378393e3 100644 --- a/source/blender/makesdna/DNA_asset_types.h +++ b/source/blender/makesdna/DNA_asset_types.h @@ -205,3 +205,8 @@ typedef struct AssetWeakReference { typedef struct AssetHandle { const struct FileDirEntry *file_data; } AssetHandle; + +struct AssetCatalogPathLink { + struct AssetCatalogPathLink *next, *prev; + char *path; +}; diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index 8592a2e16ce..d3e1e728965 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -799,6 +799,7 @@ typedef struct AssetShelfSettings { AssetLibraryReference asset_library_reference; + ListBase enabled_catalog_paths; /* #AssetCatalogPathLink */ /** If not set (null or empty string), all assets will be displayed ("All" catalog behavior). */ const char *active_catalog_path; diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index da80ec6117c..126c2ed3b16 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -765,7 +765,7 @@ typedef struct bUserAssetShelfSettings { /** Identifier that matches the #AssetShelfType.idname of the shelf these settings apply to. */ char shelf_idname[64]; /* MAX_NAME */ - ListBase enabled_catalog_paths; /* #LinkData */ + ListBase enabled_catalog_paths; /* #AssetCatalogPathLink */ } bUserAssetShelfSettings; /** diff --git a/source/blender/makesrna/intern/rna_ui.cc b/source/blender/makesrna/intern/rna_ui.cc index 55adfcc1d95..a0cddbdd06b 100644 --- a/source/blender/makesrna/intern/rna_ui.cc +++ b/source/blender/makesrna/intern/rna_ui.cc @@ -2309,6 +2309,12 @@ static void rna_def_asset_shelf(BlenderRNA *brna) "Visible by Default", "Unhide the asset shelf when it's available for the first time, otherwise it will be " "hidden"}, + {ASSET_SHELF_TYPE_FLAG_STORE_CATALOGS_IN_PREFS, + "STORE_ENABLED_CATALOGS_IN_PREFERENCES", + 0, + "Store Enabled Catalogs in Preferences", + "Store the shelf's enabled catalogs in the preferences rather than the local asset shelf " + "settings"}, {0, nullptr, 0, nullptr, nullptr}, }; -- 2.30.2 From d7ae38471b4153e933d69d564ae1ab6a90186ac9 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 24 Apr 2024 15:15:24 -0400 Subject: [PATCH 187/244] Cleanup: Remove unnecessary change in versioning --- source/blender/blenloader/intern/versioning_400.cc | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/source/blender/blenloader/intern/versioning_400.cc b/source/blender/blenloader/intern/versioning_400.cc index 414583a1b8e..7356421beea 100644 --- a/source/blender/blenloader/intern/versioning_400.cc +++ b/source/blender/blenloader/intern/versioning_400.cc @@ -3127,13 +3127,7 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) FOREACH_NODETREE_END; } - if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 5)) { - LISTBASE_FOREACH (Scene *, sce, &bmain->scenes) { - image_settings_avi_to_ffmpeg(sce); - } - } - - if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 6)) { + if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 4)) { if (!DNA_struct_member_exists(fd->filesdna, "SpaceImage", "float", "stretch_opacity")) { LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { -- 2.30.2 From f60eeb9e3efd18ed826f2056d894e2942e51f347 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 24 Apr 2024 15:42:27 -0400 Subject: [PATCH 188/244] Fix: Compile error after merge --- source/blender/blenkernel/intern/asset_weak_reference.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/asset_weak_reference.cc b/source/blender/blenkernel/intern/asset_weak_reference.cc index b0d2c56f229..cd88a6ac279 100644 --- a/source/blender/blenkernel/intern/asset_weak_reference.cc +++ b/source/blender/blenkernel/intern/asset_weak_reference.cc @@ -165,7 +165,7 @@ void BKE_asset_catalog_path_list_blend_write(BlendWriter *writer, void BKE_asset_catalog_path_list_blend_read_data(BlendDataReader *reader, ListBase &catalog_path_list) { - BLO_read_list(reader, &catalog_path_list); + BLO_read_struct_list(reader, AssetCatalogPathLink, &catalog_path_list); LISTBASE_FOREACH (AssetCatalogPathLink *, catalog_path, &catalog_path_list) { BLO_read_data_address(reader, &catalog_path->path); } -- 2.30.2 From cdba752bb0ef4ba78614a5bd54647b0cfc0387ca Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 24 Apr 2024 21:44:09 +0200 Subject: [PATCH 189/244] Brush Assets: Add asset shelf to image editor Pull Request: https://projects.blender.org/blender/blender/pulls/120844 --- scripts/startup/bl_ui/space_image.py | 14 +++++++++++ scripts/startup/bl_ui/space_view3d.py | 25 +++++++++++-------- .../editors/space_image/space_image.cc | 1 + 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/scripts/startup/bl_ui/space_image.py b/scripts/startup/bl_ui/space_image.py index 5de208286a9..d8e706f2528 100644 --- a/scripts/startup/bl_ui/space_image.py +++ b/scripts/startup/bl_ui/space_image.py @@ -3,6 +3,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later from bpy.types import ( + AssetShelf, Header, Menu, Panel, @@ -30,6 +31,9 @@ from bl_ui.properties_grease_pencil_common import ( from bl_ui.space_toolsystem_common import ( ToolActivePanelHelper, ) +from bl_ui.space_view3d import ( + BrushAssetShelf, +) from bpy.app.translations import ( contexts as i18n_contexts, @@ -1723,6 +1727,15 @@ class IMAGE_PT_annotation(AnnotationDataPanel, Panel): # Grease Pencil drawing tools. +class ImageAssetShelf(BrushAssetShelf): + bl_space_type = "IMAGE_EDITOR" + + +class IMAGE_AST_brush_sculpt(ImageAssetShelf, AssetShelf): + mode = 'TEXTURE_PAINT' + mode_prop = "use_paint_image" + + classes = ( IMAGE_MT_view, IMAGE_MT_view_zoom, @@ -1794,6 +1807,7 @@ classes = ( IMAGE_PT_overlay_uv_edit_geometry, IMAGE_PT_overlay_texture_paint, IMAGE_PT_overlay_image, + IMAGE_AST_brush_sculpt, ) diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index ea55a6b98f3..3166d87b281 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -8933,7 +8933,6 @@ class VIEW3D_PT_viewport_debug(Panel): class BrushAssetShelf: - bl_space_type = "VIEW_3D" bl_options = {'DEFAULT_VISIBLE', 'NO_ASSET_DRAG', 'STORE_ENABLED_CATALOGS_IN_PREFERENCES'} bl_activate_operator = "BRUSH_OT_asset_select" bl_default_preview_size = 48 @@ -8962,52 +8961,56 @@ class BrushAssetShelf: layout.menu_contents("VIEW3D_MT_brush_context_menu") -class VIEW3D_AST_brush_sculpt(BrushAssetShelf, bpy.types.AssetShelf): +class View3DAssetShelf(BrushAssetShelf): + bl_space_type = "VIEW_3D" + + +class VIEW3D_AST_brush_sculpt(View3DAssetShelf, bpy.types.AssetShelf): mode = 'SCULPT' mode_prop = "use_paint_sculpt" -class VIEW3D_AST_brush_sculpt_curves(BrushAssetShelf, bpy.types.AssetShelf): +class VIEW3D_AST_brush_sculpt_curves(View3DAssetShelf, bpy.types.AssetShelf): mode = 'SCULPT_CURVES' mode_prop = "use_paint_sculpt_curves" -class VIEW3D_AST_brush_vertex_paint(BrushAssetShelf, bpy.types.AssetShelf): +class VIEW3D_AST_brush_vertex_paint(View3DAssetShelf, bpy.types.AssetShelf): mode = 'VERTEX_PAINT' mode_prop = "use_paint_vertex" -class VIEW3D_AST_brush_weight_paint(BrushAssetShelf, bpy.types.AssetShelf): +class VIEW3D_AST_brush_weight_paint(View3DAssetShelf, bpy.types.AssetShelf): mode = 'WEIGHT_PAINT' mode_prop = "use_paint_weight" -class VIEW3D_AST_brush_texture_paint(BrushAssetShelf, bpy.types.AssetShelf): +class VIEW3D_AST_brush_texture_paint(View3DAssetShelf, bpy.types.AssetShelf): mode = 'TEXTURE_PAINT' mode_prop = "use_paint_image" -class VIEW3D_AST_brush_gpencil_paint(BrushAssetShelf, bpy.types.AssetShelf): +class VIEW3D_AST_brush_gpencil_paint(View3DAssetShelf, bpy.types.AssetShelf): mode = 'PAINT_GPENCIL' mode_prop = "use_paint_grease_pencil" -class VIEW3D_AST_brush_grease_pencil_paint(BrushAssetShelf, bpy.types.AssetShelf): +class VIEW3D_AST_brush_grease_pencil_paint(View3DAssetShelf, bpy.types.AssetShelf): mode = 'PAINT_GREASE_PENCIL' mode_prop = "use_paint_grease_pencil" -class VIEW3D_AST_brush_gpencil_sculpt(BrushAssetShelf, bpy.types.AssetShelf): +class VIEW3D_AST_brush_gpencil_sculpt(View3DAssetShelf, bpy.types.AssetShelf): mode = 'SCULPT_GPENCIL' mode_prop = "use_sculpt_grease_pencil" -class VIEW3D_AST_brush_gpencil_vertex(BrushAssetShelf, bpy.types.AssetShelf): +class VIEW3D_AST_brush_gpencil_vertex(View3DAssetShelf, bpy.types.AssetShelf): mode = 'VERTEX_GPENCIL' mode_prop = "use_vertex_grease_pencil" -class VIEW3D_AST_brush_gpencil_weight(BrushAssetShelf, bpy.types.AssetShelf): +class VIEW3D_AST_brush_gpencil_weight(View3DAssetShelf, bpy.types.AssetShelf): mode = 'WEIGHT_GPENCIL' mode_prop = "use_weight_grease_pencil" diff --git a/source/blender/editors/space_image/space_image.cc b/source/blender/editors/space_image/space_image.cc index 008b34e28a0..752227221b4 100644 --- a/source/blender/editors/space_image/space_image.cc +++ b/source/blender/editors/space_image/space_image.cc @@ -1227,6 +1227,7 @@ void ED_spacetype_image() art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_ASSET_SHELF | ED_KEYMAP_FRAMES; art->duplicate = asset::shelf::region_duplicate; art->free = asset::shelf::region_free; + art->on_poll_success = asset::shelf::region_on_poll_success; art->listener = asset::shelf::region_listen; art->poll = asset::shelf::regions_poll; art->snap_size = asset::shelf::region_snap; -- 2.30.2 From 3be12541cdcbd92724983abb9163f2ad4503958b Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 25 Apr 2024 16:09:00 +0200 Subject: [PATCH 190/244] Fix compile error after changes in main See fe0e2907b3. --- source/blender/blenkernel/intern/brush.cc | 1 - source/blender/blenkernel/intern/paint.cc | 1 - 2 files changed, 2 deletions(-) diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index bff866a21f9..fea20e91c43 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -404,7 +404,6 @@ static void brush_asset_metadata_ensure(void *asset_ptr, AssetMetaData *asset_da /* Sculpt UVs in the image editor while in edit mode. */ std::pair{"use_paint_uv_sculpt", OB_MODE_EDIT}, std::pair{"use_paint_grease_pencil", OB_MODE_PAINT_GPENCIL_LEGACY}, - std::pair{"use_paint_grease_pencil", OB_MODE_PAINT_GREASE_PENCIL}, /* Note: Not defined in brush RNA, own name. */ std::pair{"use_sculpt_grease_pencil", OB_MODE_SCULPT_GPENCIL_LEGACY}, std::pair{"use_vertex_grease_pencil", OB_MODE_VERTEX_GPENCIL_LEGACY}, diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 2c43d4bd692..c803c25afbb 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -743,7 +743,6 @@ static void paint_brush_set_default_reference(Paint *paint) case OB_MODE_EDIT: /* TODO: UV sculpt. */ break; - case OB_MODE_PAINT_GREASE_PENCIL: case OB_MODE_PAINT_GPENCIL_LEGACY: name = "Pencil"; break; -- 2.30.2 From 1f83f277dc1bc994c374b49f962a1fc751cc8994 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 26 Apr 2024 16:03:00 +0200 Subject: [PATCH 191/244] Brush asset shelf popup Pull Request: https://projects.blender.org/blender/blender/pulls/119217 --- scripts/startup/bl_ui/space_view3d.py | 30 ++- source/blender/editors/asset/CMakeLists.txt | 1 + .../blender/editors/asset/ED_asset_shelf.hh | 23 ++ .../editors/asset/intern/asset_shelf.cc | 39 ++-- .../editors/asset/intern/asset_shelf.hh | 10 + .../asset/intern/asset_shelf_asset_view.cc | 21 +- .../intern/asset_shelf_catalog_selector.cc | 29 +-- .../editors/asset/intern/asset_shelf_popup.cc | 197 ++++++++++++++++++ .../asset/intern/asset_shelf_regiondata.cc | 13 ++ .../blender/editors/include/UI_interface_c.hh | 19 ++ .../blender/editors/interface/CMakeLists.txt | 2 + .../editors/interface/interface_handlers.cc | 2 + .../editors/interface/interface_intern.hh | 4 + .../regions/interface_region_menu_popup.cc | 10 + .../regions/interface_region_popup.cc | 4 + .../interface_template_asset_shelf_popover.cc | 61 ++++++ .../editors/interface/views/grid_view.cc | 17 +- source/blender/makesdna/DNA_screen_types.h | 3 + source/blender/makesrna/intern/rna_ui_api.cc | 33 +++ 19 files changed, 475 insertions(+), 43 deletions(-) create mode 100644 source/blender/editors/asset/intern/asset_shelf_popup.cc create mode 100644 source/blender/editors/interface/templates/interface_template_asset_shelf_popover.cc diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index ed973d4ff81..c5bd36261d6 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -235,9 +235,18 @@ class _draw_tool_settings_context_mode: return False paint = context.tool_settings.sculpt - layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True) - brush = paint.brush + + brush_name = brush.name if brush else None + preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 + fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' + layout.template_asset_shelf_popover( + BrushAssetShelf.get_shelf_name_from_mode(context.object.mode), + name=brush_name, + icon=fallback_icon, + icon_value=preview_icon_id, + ) + if brush is None: return False @@ -9023,6 +9032,23 @@ class BrushAssetShelf: # also activates the item. layout.menu_contents("VIEW3D_MT_brush_context_menu") + # Not nice, but needed unfortunately. + @staticmethod + def get_shelf_name_from_mode(obmode): + mode_map = { + 'SCULPT': "VIEW3D_AST_brush_sculpt", + 'SCULPT_CURVES': "VIEW3D_AST_brush_sculpt_curves", + 'VERTEX_PAINT': "VIEW3D_AST_brush_vertex_paint", + 'WEIGHT_PAINT': "VIEW3D_AST_brush_weight_paint", + 'TEXTURE_PAINT': "VIEW3D_AST_brush_texture_paint", + 'PAINT_GPENCIL': "VIEW3D_AST_brush_gpencil_paint", + 'PAINT_GREASE_PENCIL': "VIEW3D_AST_brush_grease_pencil_paint", + 'SCULPT_GPENCIL': "VIEW3D_AST_brush_gpencil_sculpt", + 'VERTEX_GPENCIL': "VIEW3D_AST_brush_gpencil_vertex", + 'WEIGHT_GPENCIL': "VIEW3D_AST_brush_gpencil_weight", + } + return mode_map[obmode] + class View3DAssetShelf(BrushAssetShelf): bl_space_type = "VIEW_3D" diff --git a/source/blender/editors/asset/CMakeLists.txt b/source/blender/editors/asset/CMakeLists.txt index 1ad84a1c0d2..3e387636b46 100644 --- a/source/blender/editors/asset/CMakeLists.txt +++ b/source/blender/editors/asset/CMakeLists.txt @@ -34,6 +34,7 @@ set(SRC intern/asset_shelf.cc intern/asset_shelf_asset_view.cc intern/asset_shelf_catalog_selector.cc + intern/asset_shelf_popup.cc intern/asset_shelf_regiondata.cc intern/asset_shelf_settings.cc intern/asset_temp_id_consumer.cc diff --git a/source/blender/editors/asset/ED_asset_shelf.hh b/source/blender/editors/asset/ED_asset_shelf.hh index 8371d53dd13..28be8ac5059 100644 --- a/source/blender/editors/asset/ED_asset_shelf.hh +++ b/source/blender/editors/asset/ED_asset_shelf.hh @@ -17,9 +17,16 @@ struct bContextDataResult; struct BlendDataReader; struct BlendWriter; struct Main; +struct SpaceType; +struct uiBlock; struct RegionPollParams; struct wmWindowManager; +namespace blender { +class StringRef; +class StringRefNull; +} // namespace blender + namespace blender::ed::asset::shelf { /* -------------------------------------------------------------------- */ @@ -58,6 +65,22 @@ void header_regiontype_register(ARegionType *region_type, const int space_type); /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Asset shelf type */ + +bool type_poll(const bContext &C, const SpaceType &space_type, const AssetShelfType *shelf_type); +AssetShelfType *type_find_from_idname(const SpaceType &space_type, StringRefNull idname); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Asset shelf popup */ + +uiBlock *popup_block_create(const bContext *C, ARegion *region, AssetShelfType *shelf_type); +void type_popup_unlink(const AssetShelfType &shelf_type); + +/** \} */ + /* -------------------------------------------------------------------- */ void type_unlink(const Main &bmain, const AssetShelfType &shelf_type); diff --git a/source/blender/editors/asset/intern/asset_shelf.cc b/source/blender/editors/asset/intern/asset_shelf.cc index 72bc8b70ed6..f18b925d34a 100644 --- a/source/blender/editors/asset/intern/asset_shelf.cc +++ b/source/blender/editors/asset/intern/asset_shelf.cc @@ -52,9 +52,7 @@ void send_redraw_notifier(const bContext &C) /** \name Shelf Type * \{ */ -static bool asset_shelf_type_poll(const bContext &C, - const SpaceType &space_type, - const AssetShelfType *shelf_type) +bool type_poll(const bContext &C, const SpaceType &space_type, const AssetShelfType *shelf_type) { if (!shelf_type) { return false; @@ -71,7 +69,17 @@ static bool asset_shelf_type_poll(const bContext &C, return !shelf_type->poll || shelf_type->poll(&C, shelf_type); } -static AssetShelfType *asset_shelf_type_ensure(const SpaceType &space_type, AssetShelf &shelf) +AssetShelfType *type_find_from_idname(const SpaceType &space_type, StringRefNull idname) +{ + for (const std::unique_ptr &shelf_type : space_type.asset_shelf_types) { + if (idname == shelf_type->idname) { + return shelf_type.get(); + } + } + return nullptr; +} + +AssetShelfType *type_ensure(const SpaceType &space_type, AssetShelf &shelf) { if (shelf.type) { return shelf.type; @@ -87,7 +95,7 @@ static AssetShelfType *asset_shelf_type_ensure(const SpaceType &space_type, Asse return nullptr; } -static AssetShelf *create_shelf_from_type(AssetShelfType &type) +AssetShelf *create_shelf_from_type(AssetShelfType &type) { AssetShelf *shelf = MEM_new(__func__); *shelf = dna::shallow_zero_initialize(); @@ -149,8 +157,7 @@ static AssetShelf *update_active_shelf(const bContext &C, /* Case 1: */ if (shelf_regiondata.active_shelf && - asset_shelf_type_poll( - C, space_type, asset_shelf_type_ensure(space_type, *shelf_regiondata.active_shelf))) + type_poll(C, space_type, type_ensure(space_type, *shelf_regiondata.active_shelf))) { /* Not a strong precondition, but if this is wrong something weird might be going on. */ BLI_assert(shelf_regiondata.active_shelf == shelf_regiondata.shelves.first); @@ -164,7 +171,7 @@ static AssetShelf *update_active_shelf(const bContext &C, continue; } - if (asset_shelf_type_poll(C, space_type, asset_shelf_type_ensure(space_type, *shelf))) { + if (type_poll(C, space_type, type_ensure(space_type, *shelf))) { /* Found a valid previously activated shelf, reactivate it. */ activate_shelf(shelf_regiondata, *shelf); return shelf; @@ -173,7 +180,7 @@ static AssetShelf *update_active_shelf(const bContext &C, /* Case 3: */ for (const std::unique_ptr &shelf_type : space_type.asset_shelf_types) { - if (asset_shelf_type_poll(C, space_type, shelf_type.get())) { + if (type_poll(C, space_type, shelf_type.get())) { AssetShelf *new_shelf = create_shelf_from_type(*shelf_type); BLI_addhead(&shelf_regiondata.shelves, new_shelf); /* Moves ownership to the regiondata. */ @@ -224,7 +231,7 @@ static bool asset_shelf_space_poll(const bContext *C, const SpaceLink *space_lin /* Is there any asset shelf type registered that returns true for it's poll? */ for (const std::unique_ptr &shelf_type : space_type->asset_shelf_types) { - if (asset_shelf_type_poll(*C, *space_type, shelf_type.get())) { + if (type_poll(*C, *space_type, shelf_type.get())) { return true; } } @@ -483,19 +490,13 @@ void region_draw(const bContext *C, ARegion *region) void region_on_poll_success(const bContext *C, ARegion *region) { - ScrArea *area = CTX_wm_area(C); - - BLI_assert(region->regiontype == RGN_TYPE_ASSET_SHELF); - if (!region->regiondata) { - region->regiondata = MEM_cnew("RegionAssetShelf"); - } - - RegionAssetShelf *shelf_regiondata = RegionAssetShelf::get_from_asset_shelf_region(*region); + RegionAssetShelf *shelf_regiondata = RegionAssetShelf::ensure_from_asset_shelf_region(*region); if (!shelf_regiondata) { BLI_assert_unreachable(); return; } + ScrArea *area = CTX_wm_area(C); update_active_shelf( *C, *area->type, *shelf_regiondata, /*on_create=*/[&](AssetShelf &new_shelf) { /* Update region visibility (`'DEFAULT_VISIBLE'` option). */ @@ -812,6 +813,8 @@ void type_unlink(const Main &bmain, const AssetShelfType &shelf_type) } } } + + type_popup_unlink(shelf_type); } /** \} */ diff --git a/source/blender/editors/asset/intern/asset_shelf.hh b/source/blender/editors/asset/intern/asset_shelf.hh index b98258c45d0..a014117ffce 100644 --- a/source/blender/editors/asset/intern/asset_shelf.hh +++ b/source/blender/editors/asset/intern/asset_shelf.hh @@ -9,14 +9,19 @@ #pragma once #include "BLI_function_ref.hh" +#include "BLI_string_ref.hh" struct ARegion; +struct ARegionType; struct AssetLibraryReference; struct AssetShelf; +struct AssetShelfType; struct AssetShelfSettings; struct bContext; struct BlendDataReader; struct BlendWriter; +struct RegionAssetShelf; +struct SpaceType; struct uiLayout; namespace blender::asset_system { @@ -39,6 +44,11 @@ AssetShelf *active_shelf_from_context(const bContext *C); void send_redraw_notifier(const bContext &C); +AssetShelfType *type_ensure(const SpaceType &space_type, AssetShelf &shelf); +AssetShelf *create_shelf_from_type(AssetShelfType &type); + +void library_selector_draw(const bContext *C, uiLayout *layout, AssetShelf &shelf); + /** * Deep-copies \a shelf_regiondata into newly allocated memory. Must be freed using * #regiondata_free(). diff --git a/source/blender/editors/asset/intern/asset_shelf_asset_view.cc b/source/blender/editors/asset/intern/asset_shelf_asset_view.cc index 0c816bdd343..4e6eef1b700 100644 --- a/source/blender/editors/asset/intern/asset_shelf_asset_view.cc +++ b/source/blender/editors/asset/intern/asset_shelf_asset_view.cc @@ -45,12 +45,13 @@ class AssetView : public ui::AbstractGridView { * end of the string, for `fnmatch()` to work. */ char search_string[sizeof(AssetShelfSettings::search_string) + 2] = ""; std::optional catalog_filter_ = std::nullopt; + bool is_popup_ = false; friend class AssetViewItem; friend class AssetDragController; public: - AssetView(const AssetLibraryReference &library_ref, const AssetShelf &shelf); + AssetView(const AssetLibraryReference &library_ref, const AssetShelf &shelf, bool is_popup); ~AssetView(); void build_items() override; @@ -72,6 +73,7 @@ class AssetViewItem : public ui::PreviewGridItem { void disable_asset_drag(); void build_grid_tile(uiLayout &layout) const override; void build_context_menu(bContext &C, uiLayout &column) const override; + void on_activate(bContext &C) override; std::optional should_be_active() const override; bool is_filtered_visible() const override; @@ -88,8 +90,10 @@ class AssetDragController : public ui::AbstractViewItemDragController { void *create_drag_data() const override; }; -AssetView::AssetView(const AssetLibraryReference &library_ref, const AssetShelf &shelf) - : library_ref_(library_ref), shelf_(shelf) +AssetView::AssetView(const AssetLibraryReference &library_ref, + const AssetShelf &shelf, + const bool is_popup) + : library_ref_(library_ref), shelf_(shelf), is_popup_(is_popup) { if (shelf.settings.search_string[0]) { BLI_strncpy_ensure_pad( @@ -230,6 +234,14 @@ void AssetViewItem::build_context_menu(bContext &C, uiLayout &column) const } } +void AssetViewItem::on_activate(bContext & /*C*/) +{ + const AssetView &asset_view = dynamic_cast(get_view()); + if (asset_view.is_popup_) { + UI_popup_menu_close_from_but(reinterpret_cast(view_item_button())); + } +} + std::optional AssetViewItem::should_be_active() const { const AssetView &asset_view = dynamic_cast(get_view()); @@ -284,7 +296,8 @@ void build_asset_view(uiLayout &layout, BLI_assert(tile_width != 0); BLI_assert(tile_height != 0); - std::unique_ptr asset_view = std::make_unique(library_ref, shelf); + const bool is_popup = region.regiontype == RGN_TYPE_TEMPORARY; + std::unique_ptr asset_view = std::make_unique(library_ref, shelf, is_popup); asset_view->set_catalog_filter(catalog_filter_from_shelf_settings(shelf.settings, *library)); asset_view->set_tile_size(tile_width, tile_height); diff --git a/source/blender/editors/asset/intern/asset_shelf_catalog_selector.cc b/source/blender/editors/asset/intern/asset_shelf_catalog_selector.cc index 9446f75cb2e..4a9c4c6e60a 100644 --- a/source/blender/editors/asset/intern/asset_shelf_catalog_selector.cc +++ b/source/blender/editors/asset/intern/asset_shelf_catalog_selector.cc @@ -174,32 +174,37 @@ void AssetCatalogSelectorTree::update_shelf_settings_from_enabled_catalogs() }); } +void library_selector_draw(const bContext *C, uiLayout *layout, AssetShelf &shelf) +{ + uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT); + + PointerRNA shelf_ptr = RNA_pointer_create(&CTX_wm_screen(C)->id, &RNA_AssetShelf, &shelf); + + uiLayout *row = uiLayoutRow(layout, true); + uiItemR(row, &shelf_ptr, "asset_library_reference", UI_ITEM_NONE, "", ICON_NONE); + if (shelf.settings.asset_library_reference.type != ASSET_LIBRARY_LOCAL) { + uiItemO(row, "", ICON_FILE_REFRESH, "ASSET_OT_library_refresh"); + } +} + static void catalog_selector_panel_draw(const bContext *C, Panel *panel) { - const AssetLibraryReference *library_ref = CTX_wm_asset_library_ref(C); AssetShelf *shelf = active_shelf_from_context(C); if (!shelf) { return; } uiLayout *layout = panel->layout; - uiBlock *block = uiLayoutGetBlock(layout); - uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT); + library_selector_draw(C, layout, *shelf); - PointerRNA shelf_ptr = RNA_pointer_create(&CTX_wm_screen(C)->id, &RNA_AssetShelf, shelf); - - uiLayout *row = uiLayoutRow(layout, true); - uiItemR(row, &shelf_ptr, "asset_library_reference", UI_ITEM_NONE, "", ICON_NONE); - if (library_ref->type != ASSET_LIBRARY_LOCAL) { - uiItemO(row, "", ICON_FILE_REFRESH, "ASSET_OT_library_refresh"); - } - - asset_system::AssetLibrary *library = list::library_get_once_available(*library_ref); + asset_system::AssetLibrary *library = list::library_get_once_available( + shelf->settings.asset_library_reference); if (!library) { return; } + uiBlock *block = uiLayoutGetBlock(layout); ui::AbstractTreeView *tree_view = UI_block_add_view( *block, "asset catalog tree view", diff --git a/source/blender/editors/asset/intern/asset_shelf_popup.cc b/source/blender/editors/asset/intern/asset_shelf_popup.cc new file mode 100644 index 00000000000..0ed0ed58de5 --- /dev/null +++ b/source/blender/editors/asset/intern/asset_shelf_popup.cc @@ -0,0 +1,197 @@ +/* SPDX-FileCopyrightText: 2024 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edasset + */ + +#include "asset_shelf.hh" + +#include "BKE_screen.hh" + +#include "BLT_translation.hh" + +#include "UI_interface_c.hh" +#include "UI_tree_view.hh" + +#include "ED_asset_filter.hh" +#include "ED_asset_list.hh" +#include "ED_asset_shelf.hh" + +#include "RNA_access.hh" +#include "RNA_prototypes.h" + +namespace blender::ed::asset::shelf { + +class StaticPopupShelves { + public: + Vector popup_shelves; + + ~StaticPopupShelves() + { + for (AssetShelf *shelf : popup_shelves) { + MEM_delete(shelf); + } + } + + static Vector &shelves() + { + static StaticPopupShelves storage; + return storage.popup_shelves; + } +}; + +void type_popup_unlink(const AssetShelfType &shelf_type) +{ + for (AssetShelf *shelf : StaticPopupShelves::shelves()) { + if (shelf->type == &shelf_type) { + shelf->type = nullptr; + } + } +} + +static AssetShelf *get_shelf_for_popup(const bContext *C, AssetShelfType &shelf_type) +{ + const SpaceType *space_type = BKE_spacetype_from_id(shelf_type.space_type); + + Vector &popup_shelves = StaticPopupShelves::shelves(); + + for (AssetShelf *shelf : popup_shelves) { + if (STREQ(shelf->idname, shelf_type.idname)) { + if (type_poll(*C, *space_type, type_ensure(*space_type, *shelf))) { + return shelf; + } + break; + } + } + + if (type_poll(*C, *space_type, &shelf_type)) { + AssetShelf *new_shelf = create_shelf_from_type(shelf_type); + popup_shelves.append(new_shelf); + return new_shelf; + } + + return nullptr; +} + +class AssetCatalogTreeView : public ui::AbstractTreeView { + AssetShelf &shelf_; + asset_system::AssetCatalogTree catalog_tree_; + + public: + AssetCatalogTreeView(const asset_system::AssetLibrary &library, AssetShelf &shelf) + : shelf_(shelf) + { + catalog_tree_ = build_filtered_catalog_tree( + library, + shelf_.settings.asset_library_reference, + [this](const asset_system::AssetRepresentation &asset) { + return (!shelf_.type->asset_poll || shelf_.type->asset_poll(shelf_.type, &asset)); + }); + } + + void build_tree() override + { + if (catalog_tree_.is_empty()) { + auto &item = this->add_tree_item(RPT_("No applicable assets found"), + ICON_INFO); + item.disable_interaction(); + return; + } + + auto &all_item = this->add_tree_item(IFACE_("All")); + all_item.set_on_activate_fn([this](bContext &C, ui::BasicTreeViewItem &) { + settings_set_all_catalog_active(shelf_.settings); + send_redraw_notifier(C); + }); + all_item.set_is_active_fn( + [this]() { return settings_is_all_catalog_active(shelf_.settings); }); + all_item.uncollapse_by_default(); + + catalog_tree_.foreach_root_item([&, this]( + const asset_system::AssetCatalogTreeItem &catalog_item) { + ui::BasicTreeViewItem &item = this->build_catalog_items_recursive(all_item, catalog_item); + item.uncollapse_by_default(); + }); + } + + ui::BasicTreeViewItem &build_catalog_items_recursive( + ui::TreeViewOrItem &parent_view_item, + const asset_system::AssetCatalogTreeItem &catalog_item) const + { + ui::BasicTreeViewItem &view_item = parent_view_item.add_tree_item( + catalog_item.get_name()); + + std::string catalog_path = catalog_item.catalog_path().str(); + view_item.set_on_activate_fn([this, catalog_path](bContext &C, ui::BasicTreeViewItem &) { + settings_set_active_catalog(shelf_.settings, catalog_path); + send_redraw_notifier(C); + }); + view_item.set_is_active_fn([this, catalog_path]() { + return settings_is_active_catalog(shelf_.settings, catalog_path); + }); + + catalog_item.foreach_child( + [&view_item, this](const asset_system::AssetCatalogTreeItem &child) { + build_catalog_items_recursive(view_item, child); + }); + + return view_item; + } +}; + +static void catalog_tree_draw(uiLayout &layout, AssetShelf &shelf) +{ + const asset_system::AssetLibrary *library = list::library_get_once_available( + shelf.settings.asset_library_reference); + if (!library) { + return; + } + + uiBlock *block = uiLayoutGetBlock(&layout); + ui::AbstractTreeView *tree_view = UI_block_add_view( + *block, + "asset shelf catalog tree view", + std::make_unique(*library, shelf)); + + ui::TreeViewBuilder::build_tree_view(*tree_view, layout); +} + +uiBlock *popup_block_create(const bContext *C, ARegion *region, AssetShelfType *shelf_type) +{ + uiBlock *block = UI_block_begin(C, region, "_popup", UI_EMBOSS); + UI_block_flag_enable(block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_POPOVER); + UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP); + + AssetShelf *shelf = get_shelf_for_popup(C, *shelf_type); + if (!shelf) { + BLI_assert_unreachable(); + return block; + } + + const uiStyle *style = UI_style_get_dpi(); + + const float pad = 0.2f * UI_UNIT_Y; /* UI_MENU_PADDING */ + uiLayout *layout = UI_block_layout( + block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, pad, 0, UI_UNIT_X * 40, 0, pad / 2, style); + + PointerRNA library_ref_ptr = RNA_pointer_create( + &CTX_wm_screen(C)->id, &RNA_AssetLibraryReference, &shelf->settings.asset_library_reference); + uiLayoutSetContextPointer(layout, "asset_library_reference", &library_ref_ptr); + + uiLayout *row = uiLayoutRow(layout, false); + uiLayout *sub = uiLayoutRow(row, false); + uiLayoutSetUnitsX(sub, 10); + uiLayoutSetFixedSize(sub, true); + uiLayout *catalogs_col = uiLayoutColumn(sub, false); + library_selector_draw(C, catalogs_col, *shelf); + catalog_tree_draw(*catalogs_col, *shelf); + + uiLayout *asset_view_col = uiLayoutColumn(row, false); + build_asset_view(*asset_view_col, shelf->settings.asset_library_reference, *shelf, *C, *region); + + return block; +} + +} // namespace blender::ed::asset::shelf diff --git a/source/blender/editors/asset/intern/asset_shelf_regiondata.cc b/source/blender/editors/asset/intern/asset_shelf_regiondata.cc index 5cf411e73cd..e2c3b418a49 100644 --- a/source/blender/editors/asset/intern/asset_shelf_regiondata.cc +++ b/source/blender/editors/asset/intern/asset_shelf_regiondata.cc @@ -25,6 +25,19 @@ RegionAssetShelf *RegionAssetShelf::get_from_asset_shelf_region(const ARegion &r return static_cast(region.regiondata); } +RegionAssetShelf *RegionAssetShelf::ensure_from_asset_shelf_region(ARegion ®ion) +{ + if (region.regiontype != RGN_TYPE_ASSET_SHELF) { + /* Should only be called on main asset shelf region. */ + BLI_assert_unreachable(); + return nullptr; + } + if (!region.regiondata) { + region.regiondata = MEM_cnew("RegionAssetShelf"); + } + return static_cast(region.regiondata); +} + namespace blender::ed::asset::shelf { RegionAssetShelf *regiondata_duplicate(const RegionAssetShelf *shelf_regiondata) diff --git a/source/blender/editors/include/UI_interface_c.hh b/source/blender/editors/include/UI_interface_c.hh index 7aa2f08d7e0..c8a8e89c4c5 100644 --- a/source/blender/editors/include/UI_interface_c.hh +++ b/source/blender/editors/include/UI_interface_c.hh @@ -707,6 +707,18 @@ uiLayout *UI_popup_menu_layout(uiPopupMenu *pup); void UI_popup_menu_reports(bContext *C, ReportList *reports) ATTR_NONNULL(); int UI_popup_menu_invoke(bContext *C, const char *idname, ReportList *reports) ATTR_NONNULL(1, 2); +/** + * If \a block is displayed in a popup menu, tag it for closing. + * \param is_cancel: If set to true, the popup will be closed as being cancelled (e.g. when + * pressing escape) as opposed to being handled successfully. + */ +void UI_popup_menu_close(const uiBlock *block, bool is_cancel = false); +/** + * Version of #UI_popup_menu_close() that can be called on a button contained in a popup menu + * block. Convenience since the block may not be available. + */ +void UI_popup_menu_close_from_but(const uiBut *but, bool is_cancel = false); + /** * Allow setting menu return value from externals. * E.g. WM might need to do this for exiting files correctly. @@ -1488,6 +1500,10 @@ uiBut *uiDefIconMenuBut(uiBlock *block, short height, const char *tip); +/** + * Note that \a fun can set the #UI_BLOCK_KEEP_OPEN flag to the block it creates, to allow + * refreshing the popup. That is, redrawing the layout, potentially affecting the popup size. + */ uiBut *uiDefBlockBut(uiBlock *block, uiBlockCreateFunc func, void *arg, @@ -2703,6 +2719,9 @@ void uiTemplateAssetView(uiLayout *layout, const char *drag_opname, PointerRNA *r_drag_op_properties); +void uiTemplateAssetShelfPopover( + uiLayout *layout, bContext *C, const char *asset_shelf_id, const char *name, const int icon); + void uiTemplateLightLinkingCollection(uiLayout *layout, uiLayout *context_layout, PointerRNA *ptr, diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index 5a9f75193c6..09b34cfc6e3 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -5,6 +5,7 @@ set(INC . ../include + ../asset ../../asset_system ../../blenkernel ../../blenloader @@ -64,6 +65,7 @@ set(SRC regions/interface_regions.cc interface_string_search.cc interface_style.cc + templates/interface_template_asset_shelf_popover.cc templates/interface_template_asset_view.cc templates/interface_template_attribute_search.cc templates/interface_template_bone_collection_tree.cc diff --git a/source/blender/editors/interface/interface_handlers.cc b/source/blender/editors/interface/interface_handlers.cc index cc5e115b528..35bb8d510a2 100644 --- a/source/blender/editors/interface/interface_handlers.cc +++ b/source/blender/editors/interface/interface_handlers.cc @@ -11503,6 +11503,8 @@ static int ui_handle_menus_recursive(bContext *C, if (!menu->retvalue) { ui_handle_viewlist_items_hover(event, menu->region); } + /* Handle mouse clicks on overlapping view item button. */ + ui_handle_view_item_event(C, event, but, menu->region); if (do_towards_reinit) { ui_mouse_motion_towards_reinit(menu, event->xy); diff --git a/source/blender/editors/interface/interface_intern.hh b/source/blender/editors/interface/interface_intern.hh index e2d260e7dae..ff49071c88c 100644 --- a/source/blender/editors/interface/interface_intern.hh +++ b/source/blender/editors/interface/interface_intern.hh @@ -977,6 +977,10 @@ uiBlock *ui_popup_block_refresh(bContext *C, ARegion *butregion, uiBut *but); +/** + * Note that callbacks can set the #UI_BLOCK_KEEP_OPEN flag to the block it creates, to allow + * refreshing the popup. That is, redrawing the layout, potentially affecting the popup size. + */ uiPopupBlockHandle *ui_popup_block_create(bContext *C, ARegion *butregion, uiBut *but, diff --git a/source/blender/editors/interface/regions/interface_region_menu_popup.cc b/source/blender/editors/interface/regions/interface_region_menu_popup.cc index b1ca5e76c10..31aff86f3fd 100644 --- a/source/blender/editors/interface/regions/interface_region_menu_popup.cc +++ b/source/blender/editors/interface/regions/interface_region_menu_popup.cc @@ -762,4 +762,14 @@ bool UI_popup_block_name_exists(const bScreen *screen, const blender::StringRef return false; } +void UI_popup_menu_close(const uiBlock *block, const bool is_cancel) +{ + UI_popup_menu_retval_set(block, is_cancel ? UI_RETURN_CANCEL : UI_RETURN_OK, true); +} + +void UI_popup_menu_close_from_but(const uiBut *but, const bool is_cancel) +{ + UI_popup_menu_close(but->block, is_cancel); +} + /** \} */ diff --git a/source/blender/editors/interface/regions/interface_region_popup.cc b/source/blender/editors/interface/regions/interface_region_popup.cc index 8fdd35e043f..41b035fc3f3 100644 --- a/source/blender/editors/interface/regions/interface_region_popup.cc +++ b/source/blender/editors/interface/regions/interface_region_popup.cc @@ -885,6 +885,10 @@ uiPopupBlockHandle *ui_popup_block_create(bContext *C, uiBlock *block = ui_popup_block_refresh(C, handle, butregion, but); handle = block->handle; + if (block->flag & UI_BLOCK_KEEP_OPEN) { + handle->can_refresh = true; + } + /* keep centered on window resizing */ if (block->bounds_type == UI_BLOCK_BOUNDS_POPUP_CENTER) { type.listener = ui_block_region_popup_window_listener; diff --git a/source/blender/editors/interface/templates/interface_template_asset_shelf_popover.cc b/source/blender/editors/interface/templates/interface_template_asset_shelf_popover.cc new file mode 100644 index 00000000000..d2ad32f0006 --- /dev/null +++ b/source/blender/editors/interface/templates/interface_template_asset_shelf_popover.cc @@ -0,0 +1,61 @@ +/* SPDX-FileCopyrightText: 2024 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edinterface + */ + +#include "BKE_context.hh" +#include "BKE_screen.hh" + +#include "RNA_access.hh" + +#include "UI_interface_c.hh" +#include "UI_resources.hh" +#include "interface_intern.hh" + +#include "ED_asset_shelf.hh" + +using namespace blender; + +static uiBlock *asset_shelf_block_fn(bContext *C, ARegion *region, void *arg_shelf_type) +{ + AssetShelfType *shelf_type = reinterpret_cast(arg_shelf_type); + return ed::asset::shelf::popup_block_create(C, region, shelf_type); +} + +void uiTemplateAssetShelfPopover(uiLayout *layout, + bContext *C, + const char *asset_shelf_id, + const char *name, + const BIFIconID icon) +{ + const ScrArea *area = CTX_wm_area(C); + AssetShelfType *shelf_type = ed::asset::shelf::type_find_from_idname(*area->type, + asset_shelf_id); + if (!shelf_type) { + RNA_warning("Asset shelf type not found: %s", asset_shelf_id); + return; + } + + const ARegion *region = CTX_wm_region(C); + const bool use_big_size = !RGN_TYPE_IS_HEADER_ANY(region->regiontype); + const short width = [&]() -> short { + if (use_big_size) { + return UI_UNIT_X * 6; + } + return UI_UNIT_X * (name ? 7 : 1.6f); + }(); + const short height = UI_UNIT_Y * (use_big_size ? 6 : 1); + + uiBlock *block = uiLayoutGetBlock(layout); + uiBut *but = uiDefBlockBut( + block, asset_shelf_block_fn, shelf_type, name, 0, 0, width, height, "Select an asset"); + ui_def_but_icon(but, icon, UI_HAS_ICON); + UI_but_drawflag_enable(but, UI_BUT_ICON_LEFT); + + if (ed::asset::shelf::type_poll(*C, *area->type, shelf_type) == false) { + UI_but_flag_enable(but, UI_BUT_DISABLED); + } +} diff --git a/source/blender/editors/interface/views/grid_view.cc b/source/blender/editors/interface/views/grid_view.cc index ec8f251c836..1edf92ae079 100644 --- a/source/blender/editors/interface/views/grid_view.cc +++ b/source/blender/editors/interface/views/grid_view.cc @@ -204,7 +204,6 @@ GridViewItemDropTarget::GridViewItemDropTarget(AbstractGridView &view) : view_(v * side(s) as well. */ class BuildOnlyVisibleButtonsHelper { - const View2D &v2d_; const AbstractGridView &grid_view_; const GridViewStyle &style_; const int cols_per_row_ = 0; @@ -221,30 +220,34 @@ class BuildOnlyVisibleButtonsHelper { void fill_layout_after_visible(uiBlock &block) const; private: - IndexRange get_visible_range() const; + IndexRange get_visible_range(const View2D &v2d) const; void add_spacer_button(uiBlock &block, int row_count) const; }; BuildOnlyVisibleButtonsHelper::BuildOnlyVisibleButtonsHelper(const View2D &v2d, const AbstractGridView &grid_view, const int cols_per_row) - : v2d_(v2d), grid_view_(grid_view), style_(grid_view.get_style()), cols_per_row_(cols_per_row) + : grid_view_(grid_view), style_(grid_view.get_style()), cols_per_row_(cols_per_row) { - visible_items_range_ = this->get_visible_range(); + visible_items_range_ = this->get_visible_range(v2d); } -IndexRange BuildOnlyVisibleButtonsHelper::get_visible_range() const +IndexRange BuildOnlyVisibleButtonsHelper::get_visible_range(const View2D &v2d) const { + if ((v2d.flag & V2D_IS_INIT) == 0) { + return IndexRange(0, grid_view_.get_item_count_filtered()); + } + int first_idx_in_view = 0; - const float scroll_ofs_y = std::abs(v2d_.cur.ymax - v2d_.tot.ymax); + const float scroll_ofs_y = std::abs(v2d.cur.ymax - v2d.tot.ymax); if (!IS_EQF(scroll_ofs_y, 0)) { const int scrolled_away_rows = int(scroll_ofs_y) / style_.tile_height; first_idx_in_view = scrolled_away_rows * cols_per_row_; } - const int view_height = BLI_rcti_size_y(&v2d_.mask); + const int view_height = BLI_rcti_size_y(&v2d.mask); const int count_rows_in_view = std::max(view_height / style_.tile_height, 1); const int max_items_in_view = (count_rows_in_view + 1) * cols_per_row_; diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index d3e1e728965..6d610fc1614 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -833,6 +833,7 @@ typedef struct AssetShelf { AssetShelfSettings settings; + /** Only for the permanent asset shelf regions, not asset shelves in temporary popups. */ short preferred_row_count; char _pad[6]; } AssetShelf; @@ -855,6 +856,8 @@ typedef struct RegionAssetShelf { AssetShelf *active_shelf; /* Non-owning. */ #ifdef __cplusplus static RegionAssetShelf *get_from_asset_shelf_region(const ARegion ®ion); + /** Creates the asset shelf region data if necessary, and returns it. */ + static RegionAssetShelf *ensure_from_asset_shelf_region(ARegion ®ion); #endif } RegionAssetShelf; diff --git a/source/blender/makesrna/intern/rna_ui_api.cc b/source/blender/makesrna/intern/rna_ui_api.cc index 86fffd5c54d..5a67192962e 100644 --- a/source/blender/makesrna/intern/rna_ui_api.cc +++ b/source/blender/makesrna/intern/rna_ui_api.cc @@ -963,6 +963,20 @@ static int rna_ui_get_enum_icon(bContext *C, return icon; } +void rna_uiTemplateAssetShelfPopover(uiLayout *layout, + bContext *C, + const char *asset_shelf_id, + const char *name, + BIFIconID icon, + int icon_value) +{ + if (icon_value && !icon) { + icon = icon_value; + } + + uiTemplateAssetShelfPopover(layout, C, asset_shelf_id, name, icon); +} + #else static void api_ui_item_common_heading(FunctionRNA *func) @@ -2287,6 +2301,25 @@ void RNA_api_ui_layout(StructRNA *srna) RNA_def_function_flag(func, FUNC_USE_CONTEXT); parm = RNA_def_pointer(func, "node", "Node", "Node", "Display inputs of this node"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + + func = RNA_def_function(srna, "template_asset_shelf_popover", "rna_uiTemplateAssetShelfPopover"); + RNA_def_function_ui_description(func, "Create a button to open an asset shelf in a popover"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT); + parm = RNA_def_string(func, + "asset_shelf", + nullptr, + 0, + "", + "Identifier of the asset shelf to display (`bl_idname`)"); + RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); + parm = RNA_def_string( + func, "name", nullptr, 0, "", "Optional name to indicate the active asset"); + RNA_def_property_clear_flag(parm, PROP_NEVER_NULL); + parm = RNA_def_property(func, "icon", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(parm, rna_enum_icon_items); + RNA_def_property_ui_text(parm, "Icon", "Override automatic icon of the item"); + parm = RNA_def_property(func, "icon_value", PROP_INT, PROP_UNSIGNED); + RNA_def_property_ui_text(parm, "Icon Value", "Override automatic icon of the item"); } #endif -- 2.30.2 From 2ff7ed0366cc9ee40e9702614b99dce3a51ad3d5 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 26 Apr 2024 18:36:27 +0200 Subject: [PATCH 192/244] Brush Assets: Show new catalog in asset shelf when duplicating brush When adding a new brush asset in a new catalog, make it visible in the currently active asset shelf. Pull Request: https://projects.blender.org/blender/blender/pulls/120531 --- source/blender/editors/asset/ED_asset_shelf.hh | 2 ++ .../editors/asset/intern/asset_shelf.cc | 2 +- .../blender/editors/sculpt_paint/paint_ops.cc | 18 ++++++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/asset/ED_asset_shelf.hh b/source/blender/editors/asset/ED_asset_shelf.hh index 28be8ac5059..3615bfe3e7f 100644 --- a/source/blender/editors/asset/ED_asset_shelf.hh +++ b/source/blender/editors/asset/ED_asset_shelf.hh @@ -88,6 +88,8 @@ void type_unlink(const Main &bmain, const AssetShelfType &shelf_type); int tile_width(const AssetShelfSettings &settings); int tile_height(const AssetShelfSettings &settings); +AssetShelf *active_shelf_from_area(const ScrArea *area); + int context(const bContext *C, const char *member, bContextDataResult *result); } // namespace blender::ed::asset::shelf diff --git a/source/blender/editors/asset/intern/asset_shelf.cc b/source/blender/editors/asset/intern/asset_shelf.cc index f18b925d34a..1c9db98234e 100644 --- a/source/blender/editors/asset/intern/asset_shelf.cc +++ b/source/blender/editors/asset/intern/asset_shelf.cc @@ -562,7 +562,7 @@ void region_blend_write(BlendWriter *writer, ARegion *region) /** \name Asset Shelf Context * \{ */ -static AssetShelf *active_shelf_from_area(const ScrArea *area) +AssetShelf *active_shelf_from_area(const ScrArea *area) { const ARegion *shelf_region = BKE_area_find_region_type(area, RGN_TYPE_ASSET_SHELF); if (!shelf_region) { diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index fe658429596..6b077efc1b1 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -40,12 +40,14 @@ #include "BKE_paint.hh" #include "BKE_preferences.h" #include "BKE_report.hh" +#include "BKE_screen.hh" #include "ED_asset_handle.hh" #include "ED_asset_library.hh" #include "ED_asset_list.hh" #include "ED_asset_mark_clear.hh" #include "ED_asset_menu_utils.hh" +#include "ED_asset_shelf.hh" #include "ED_image.hh" #include "ED_paint.hh" #include "ED_screen.hh" @@ -884,6 +886,21 @@ static asset_system::AssetCatalog &asset_library_ensure_catalogs_in_path( return *library.catalog_service().find_catalog_by_path(path); } +static void show_catalog_in_asset_shelf(const bContext &C, const StringRefNull catalog_path) +{ + ScrArea *area = CTX_wm_area(&C); + if (!area) { + return; + } + const AssetShelf *shelf = asset::shelf::active_shelf_from_area(area); + if (!BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled( + &U, shelf->idname, catalog_path.c_str())) + { + return; + } + U.runtime.is_dirty = true; +} + static int brush_asset_save_as_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); @@ -937,6 +954,7 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) } library->catalog_service().write_to_disk(*final_full_asset_filepath); + show_catalog_in_asset_shelf(*C, catalog_path); AssetWeakReference new_brush_weak_ref = brush_asset_create_weakref_hack( user_library, *final_full_asset_filepath); -- 2.30.2 From eb8a88ccc7a575879eb8a3fea56bc6c4ea290c17 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 30 Apr 2024 12:40:02 +0200 Subject: [PATCH 193/244] Fix compile error --- source/blender/editors/asset/ED_asset_shelf.hh | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/editors/asset/ED_asset_shelf.hh b/source/blender/editors/asset/ED_asset_shelf.hh index 3615bfe3e7f..bb85b8fb9c0 100644 --- a/source/blender/editors/asset/ED_asset_shelf.hh +++ b/source/blender/editors/asset/ED_asset_shelf.hh @@ -10,6 +10,7 @@ struct ARegion; struct ARegionType; +struct AssetShelf; struct AssetShelfSettings; struct AssetShelfType; struct bContext; -- 2.30.2 From 2373b4e2ac18e4dc13dc29fb63c7f7f0689ff531 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 30 Apr 2024 18:38:57 +0200 Subject: [PATCH 194/244] Add filter button for asset shelf popup Needs a bit of polish and should be enabled by default (while allowing other interactions with the popup, which isn't trivial), but works otherwise. --- .../blender/editors/asset/intern/asset_shelf_popup.cc | 11 +++++++++-- .../blender/editors/interface/interface_handlers.cc | 2 ++ source/blender/editors/interface/views/grid_view.cc | 6 ++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/asset/intern/asset_shelf_popup.cc b/source/blender/editors/asset/intern/asset_shelf_popup.cc index 0ed0ed58de5..27b7de6698e 100644 --- a/source/blender/editors/asset/intern/asset_shelf_popup.cc +++ b/source/blender/editors/asset/intern/asset_shelf_popup.cc @@ -160,6 +160,7 @@ static void catalog_tree_draw(uiLayout &layout, AssetShelf &shelf) uiBlock *popup_block_create(const bContext *C, ARegion *region, AssetShelfType *shelf_type) { + bScreen *screen = CTX_wm_screen(C); uiBlock *block = UI_block_begin(C, region, "_popup", UI_EMBOSS); UI_block_flag_enable(block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_POPOVER); UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP); @@ -177,7 +178,7 @@ uiBlock *popup_block_create(const bContext *C, ARegion *region, AssetShelfType * block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, pad, 0, UI_UNIT_X * 40, 0, pad / 2, style); PointerRNA library_ref_ptr = RNA_pointer_create( - &CTX_wm_screen(C)->id, &RNA_AssetLibraryReference, &shelf->settings.asset_library_reference); + &screen->id, &RNA_AssetLibraryReference, &shelf->settings.asset_library_reference); uiLayoutSetContextPointer(layout, "asset_library_reference", &library_ref_ptr); uiLayout *row = uiLayoutRow(layout, false); @@ -188,7 +189,13 @@ uiBlock *popup_block_create(const bContext *C, ARegion *region, AssetShelfType * library_selector_draw(C, catalogs_col, *shelf); catalog_tree_draw(*catalogs_col, *shelf); - uiLayout *asset_view_col = uiLayoutColumn(row, false); + uiLayout *right_col = uiLayoutColumn(row, false); + sub = uiLayoutRow(right_col, false); + /* Same as file/asset browser header. */ + PointerRNA shelf_ptr = RNA_pointer_create(&screen->id, &RNA_AssetShelf, shelf); + uiItemR(sub, &shelf_ptr, "search_filter", UI_ITEM_R_IMMEDIATE, "", ICON_VIEWZOOM); + + uiLayout *asset_view_col = uiLayoutColumn(right_col, false); build_asset_view(*asset_view_col, shelf->settings.asset_library_reference, *shelf, *C, *region); return block; diff --git a/source/blender/editors/interface/interface_handlers.cc b/source/blender/editors/interface/interface_handlers.cc index adb935e520c..3d80a97305f 100644 --- a/source/blender/editors/interface/interface_handlers.cc +++ b/source/blender/editors/interface/interface_handlers.cc @@ -4048,6 +4048,8 @@ static void ui_do_but_textedit( if (changed || (retval == WM_UI_HANDLER_BREAK)) { ED_region_tag_redraw(data->region); + /* In case of popup regions, tag for popup refreshing too (contents may have changed). */ + ED_region_tag_refresh_ui(data->region); } } diff --git a/source/blender/editors/interface/views/grid_view.cc b/source/blender/editors/interface/views/grid_view.cc index 1edf92ae079..fe340fe888d 100644 --- a/source/blender/editors/interface/views/grid_view.cc +++ b/source/blender/editors/interface/views/grid_view.cc @@ -262,6 +262,9 @@ bool BuildOnlyVisibleButtonsHelper::is_item_visible(const int item_idx) const void BuildOnlyVisibleButtonsHelper::fill_layout_before_visible(uiBlock &block) const { + if (visible_items_range_.is_empty()) { + return; + } const int first_idx_in_view = visible_items_range_.first(); if (first_idx_in_view < 1) { return; @@ -273,6 +276,9 @@ void BuildOnlyVisibleButtonsHelper::fill_layout_before_visible(uiBlock &block) c void BuildOnlyVisibleButtonsHelper::fill_layout_after_visible(uiBlock &block) const { + if (visible_items_range_.is_empty()) { + return; + } const int last_item_idx = grid_view_.get_item_count_filtered() - 1; const int last_visible_idx = visible_items_range_.last(); -- 2.30.2 From 8f835362d2e59a8b690fd023ad02b11993999ea3 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 1 May 2024 12:09:17 -0400 Subject: [PATCH 195/244] Cleanup: Remove unused includes --- source/blender/windowmanager/intern/wm_init_exit.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/blender/windowmanager/intern/wm_init_exit.cc b/source/blender/windowmanager/intern/wm_init_exit.cc index 5d9caf9ae29..d675461490b 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.cc +++ b/source/blender/windowmanager/intern/wm_init_exit.cc @@ -35,8 +35,6 @@ #include "BKE_blender.hh" #include "BKE_blendfile.hh" -#include "BKE_brush.hh" -#include "BKE_callbacks.hh" #include "BKE_context.hh" #include "BKE_global.hh" #include "BKE_icons.h" -- 2.30.2 From 051963e73f845701611875d812fc8a07f31aba7c Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 1 May 2024 12:28:13 -0400 Subject: [PATCH 196/244] Cleanup: Remove unnecessary change --- source/blender/windowmanager/intern/wm_keymap_utils.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/windowmanager/intern/wm_keymap_utils.cc b/source/blender/windowmanager/intern/wm_keymap_utils.cc index 2deb2c900ae..06311ac4108 100644 --- a/source/blender/windowmanager/intern/wm_keymap_utils.cc +++ b/source/blender/windowmanager/intern/wm_keymap_utils.cc @@ -336,7 +336,7 @@ wmKeyMap *WM_keymap_guess_opname(const bContext *C, const char *opname) wm, "Paint Face Mask (Weight, Vertex, Texture)", SPACE_EMPTY, RGN_TYPE_WINDOW); } else if (STRPREFIX(opname, "PAINT_OT") || STRPREFIX(opname, "BRUSH_OT")) { - /* check for relevant mode. */ + /* Check for relevant mode. */ switch (CTX_data_mode_enum(C)) { case CTX_MODE_PAINT_WEIGHT: km = WM_keymap_find_all(wm, "Weight Paint", SPACE_EMPTY, RGN_TYPE_WINDOW); -- 2.30.2 From a9fc2b3c9f8bde11a8b5e223d57c0a2c4efbeb24 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 1 May 2024 12:40:54 -0400 Subject: [PATCH 197/244] Cleanup: Move brush assets operators from paint_ops.cc to a new file The diff doesn't try to find similarities between deleted and added code this way --- .../editors/sculpt_paint/CMakeLists.txt | 1 + .../editors/sculpt_paint/brush_asset_ops.cc | 751 ++++++++++++++++++ .../editors/sculpt_paint/paint_intern.hh | 7 + .../blender/editors/sculpt_paint/paint_ops.cc | 735 ----------------- 4 files changed, 759 insertions(+), 735 deletions(-) create mode 100644 source/blender/editors/sculpt_paint/brush_asset_ops.cc diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index 21ffccc7625..33953dc23cb 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -30,6 +30,7 @@ set(INC_SYS ) set(SRC + brush_asset_ops.cc curves_sculpt_add.cc curves_sculpt_brush.cc curves_sculpt_comb.cc diff --git a/source/blender/editors/sculpt_paint/brush_asset_ops.cc b/source/blender/editors/sculpt_paint/brush_asset_ops.cc new file mode 100644 index 00000000000..36dbf9fd393 --- /dev/null +++ b/source/blender/editors/sculpt_paint/brush_asset_ops.cc @@ -0,0 +1,751 @@ +/* SPDX-FileCopyrightText: 2024 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_path_util.h" +#include "BLI_string.h" + +#include "DNA_brush_types.h" +#include "DNA_scene_types.h" +#include "DNA_space_types.h" + +#include "BKE_asset.hh" +#include "BKE_asset_edit.hh" +#include "BKE_blendfile.hh" +#include "BKE_brush.hh" +#include "BKE_context.hh" +#include "BKE_paint.hh" +#include "BKE_preferences.h" +#include "BKE_report.hh" + +#include "AS_asset_catalog_path.hh" +#include "AS_asset_catalog_tree.hh" +#include "AS_asset_library.hh" +#include "AS_asset_representation.hh" + +#include "RNA_access.hh" +#include "RNA_define.hh" + +#include "ED_asset_handle.hh" +#include "ED_asset_library.hh" +#include "ED_asset_list.hh" +#include "ED_asset_mark_clear.hh" +#include "ED_asset_menu_utils.hh" +#include "ED_asset_shelf.hh" + +#include "UI_interface_icons.hh" +#include "UI_resources.hh" + +#include "BLT_translation.hh" + +#include "WM_api.hh" +#include "WM_toolsystem.hh" + +#include "paint_intern.hh" + +namespace blender::ed::sculpt_paint { + +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); + const asset_system::AssetRepresentation *asset = + asset::operator_asset_reference_props_get_asset_from_all_library(*C, *op->ptr, op->reports); + if (!asset) { + return OPERATOR_CANCELLED; + } + + AssetWeakReference brush_asset_reference = asset->make_weak_reference(); + Brush *brush = reinterpret_cast( + bke::asset_edit_id_from_weak_reference(*bmain, ID_BR, brush_asset_reference)); + + Paint *paint = BKE_paint_get_active_from_context(C); + + if (!BKE_paint_brush_asset_set(paint, brush, brush_asset_reference)) { + /* Note brush datablock was still added, so was not a no-op. */ + BKE_report(op->reports, RPT_WARNING, "Unable to select brush, wrong object mode"); + return OPERATOR_FINISHED; + } + + WM_main_add_notifier(NC_ASSET | NA_ACTIVATED, nullptr); + WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, nullptr); + WM_toolsystem_ref_set_by_id(C, "builtin.brush"); + + return OPERATOR_FINISHED; +} + +void BRUSH_OT_asset_select(wmOperatorType *ot) +{ + ot->name = "Select Brush Asset"; + ot->description = "Select a brush asset as current sculpt and paint tool"; + ot->idname = "BRUSH_OT_asset_select"; + + ot->exec = brush_asset_select_exec; + + asset::operator_asset_reference_props_register(*ot->srna); +} + +/* FIXME Quick dirty hack to generate a weak ref from 'raw' paths. + * This needs to be properly implemented in assetlib code. + */ +static AssetWeakReference brush_asset_create_weakref_hack(const bUserAssetLibrary *user_asset_lib, + const std::string &file_path) +{ + AssetWeakReference asset_weak_ref{}; + + StringRef asset_root_path = user_asset_lib->dirpath; + BLI_assert(file_path.find(asset_root_path) == 0); + std::string relative_asset_path = file_path.substr(size_t(asset_root_path.size()) + 1); + + asset_weak_ref.asset_library_type = ASSET_LIBRARY_CUSTOM; + asset_weak_ref.asset_library_identifier = BLI_strdup(user_asset_lib->name); + asset_weak_ref.relative_asset_identifier = BLI_strdupn(relative_asset_path.c_str(), + relative_asset_path.size()); + + return asset_weak_ref; +} + +static std::optional library_to_library_ref( + const asset_system::AssetLibrary &library) +{ + for (const AssetLibraryReference &ref : asset_system::all_valid_asset_library_refs()) { + const std::string root_path = AS_asset_library_root_path_from_library_ref(ref); + /* Use #BLI_path_cmp_normalized because `library.root_path()` ends with a slash while + * `root_path` doesn't. */ + if (BLI_path_cmp_normalized(root_path.c_str(), library.root_path().c_str()) == 0) { + return ref; + } + } + return std::nullopt; +} + +static AssetLibraryReference user_library_to_library_ref(const bUserAssetLibrary &user_library) +{ + AssetLibraryReference library_ref{}; + library_ref.custom_library_index = BLI_findindex(&U.asset_libraries, &user_library); + library_ref.type = ASSET_LIBRARY_CUSTOM; + return library_ref; +} + +static const bUserAssetLibrary *library_ref_to_user_library( + const AssetLibraryReference &library_ref) +{ + if (library_ref.type != ASSET_LIBRARY_CUSTOM) { + return nullptr; + } + return static_cast( + BLI_findlink(&U.asset_libraries, library_ref.custom_library_index)); +} + +static void refresh_asset_library(const bContext *C, const AssetLibraryReference &library_ref) +{ + asset::list::clear(&library_ref, C); + /* TODO: Should the all library reference be automatically cleared? */ + AssetLibraryReference all_lib_ref = asset_system::all_library_reference(); + asset::list::clear(&all_lib_ref, C); +} + +static void refresh_asset_library(const bContext *C, const bUserAssetLibrary &user_library) +{ + refresh_asset_library(C, user_library_to_library_ref(user_library)); +} + +static bool brush_asset_save_as_poll(bContext *C) +{ + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr; + if (paint == nullptr || brush == nullptr) { + return false; + } + if (!paint->brush_asset_reference) { + /* The brush should always be an imported asset. We use this asset reference to find + * which library and catalog the brush came from, as defaults for the popup. */ + BLI_assert_unreachable(); + return false; + } + + if (BLI_listbase_is_empty(&U.asset_libraries)) { + CTX_wm_operator_poll_msg_set(C, "No asset library available to save to"); + return false; + } + + return true; +} + +static const bUserAssetLibrary *get_asset_library_from_prop(PointerRNA &ptr) +{ + const int enum_value = RNA_enum_get(&ptr, "asset_library_reference"); + const AssetLibraryReference lib_ref = asset::library_reference_from_enum_value(enum_value); + return BKE_preferences_asset_library_find_index(&U, lib_ref.custom_library_index); +} + +static asset_system::AssetCatalog &asset_library_ensure_catalog( + asset_system::AssetLibrary &library, const asset_system::AssetCatalogPath &path) +{ + if (asset_system::AssetCatalog *catalog = library.catalog_service().find_catalog_by_path(path)) { + return *catalog; + } + return *library.catalog_service().create_catalog(path); +} + +static asset_system::AssetCatalog &asset_library_ensure_catalogs_in_path( + asset_system::AssetLibrary &library, const asset_system::AssetCatalogPath &path) +{ + /* Adding multiple catalogs in a path at a time with #AssetCatalogService::create_catalog() + * doesn't work; add each potentially new catalog in the hierarchy manually here. */ + asset_system::AssetCatalogPath parent = ""; + path.iterate_components([&](StringRef component_name, bool /*is_last_component*/) { + asset_library_ensure_catalog(library, parent / component_name); + parent = parent / component_name; + }); + return *library.catalog_service().find_catalog_by_path(path); +} + +static void show_catalog_in_asset_shelf(const bContext &C, const StringRefNull catalog_path) +{ + ScrArea *area = CTX_wm_area(&C); + if (!area) { + return; + } + const AssetShelf *shelf = asset::shelf::active_shelf_from_area(area); + if (!BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled( + &U, shelf->idname, catalog_path.c_str())) + { + return; + } + U.runtime.is_dirty = true; +} + +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; + + /* Determine file path to save to. */ + PropertyRNA *name_prop = RNA_struct_find_property(op->ptr, "name"); + char name[MAX_NAME] = ""; + if (RNA_property_is_set(op->ptr, name_prop)) { + RNA_property_string_get(op->ptr, name_prop, name); + } + if (name[0] == '\0') { + STRNCPY(name, brush->id.name + 2); + } + + const bUserAssetLibrary *user_library = get_asset_library_from_prop(*op->ptr); + if (!user_library) { + return OPERATOR_CANCELLED; + } + + asset_system::AssetLibrary *library = AS_asset_library_load( + bmain, user_library_to_library_ref(*user_library)); + if (!library) { + BKE_report(op->reports, RPT_ERROR, "Failed to load asset library"); + return OPERATOR_CANCELLED; + } + + /* Turn brush into asset if it isn't yet. */ + if (!ID_IS_ASSET(&brush->id)) { + asset::mark_id(&brush->id); + asset::generate_preview(C, &brush->id); + } + BLI_assert(ID_IS_ASSET(&brush->id)); + + /* Add asset to catalog. */ + char catalog_path[MAX_NAME]; + RNA_string_get(op->ptr, "catalog_path", catalog_path); + + AssetMetaData &meta_data = *brush->id.asset_data; + if (catalog_path[0]) { + const asset_system::AssetCatalog &catalog = asset_library_ensure_catalogs_in_path( + *library, catalog_path); + BKE_asset_metadata_catalog_id_set(&meta_data, catalog.catalog_id, catalog.simple_name.c_str()); + } + + const std::optional final_full_asset_filepath = bke::asset_edit_id_save_as( + *bmain, brush->id, name, *user_library, *op->reports); + if (!final_full_asset_filepath) { + return OPERATOR_CANCELLED; + } + + library->catalog_service().write_to_disk(*final_full_asset_filepath); + show_catalog_in_asset_shelf(*C, catalog_path); + + AssetWeakReference new_brush_weak_ref = brush_asset_create_weakref_hack( + user_library, *final_full_asset_filepath); + + brush = reinterpret_cast( + bke::asset_edit_id_from_weak_reference(*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. */ + BKE_report(op->reports, RPT_WARNING, "Unable to activate just-saved brush asset"); + } + + refresh_asset_library(C, *user_library); + WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_ADDED, nullptr); + WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush); + + return OPERATOR_FINISHED; +} + +static bool library_is_editable(const AssetLibraryReference &library) +{ + if (library.type == ASSET_LIBRARY_ESSENTIALS) { + return false; + } + return true; +} + +static int brush_asset_save_as_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) +{ + Paint *paint = BKE_paint_get_active_from_context(C); + const AssetWeakReference &brush_weak_ref = *paint->brush_asset_reference; + const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref( + *C, brush_weak_ref, op->reports); + if (!asset) { + return OPERATOR_CANCELLED; + } + const asset_system::AssetLibrary &library = asset->owner_asset_library(); + const std::optional library_ref = library_to_library_ref(library); + if (!library_ref) { + BLI_assert_unreachable(); + return OPERATOR_CANCELLED; + } + + RNA_string_set(op->ptr, "name", asset->get_name().c_str()); + + /* If the library isn't saved from the operator's last execution, find the current library or the + * first library if the current library isn't editable. */ + if (!RNA_struct_property_is_set_ex(op->ptr, "asset_library_reference", false)) { + if (library_is_editable(*library_ref)) { + RNA_enum_set(op->ptr, + "asset_library_reference", + asset::library_reference_to_enum_value(&*library_ref)); + } + else { + const AssetLibraryReference first_library = user_library_to_library_ref( + *static_cast(U.asset_libraries.first)); + RNA_enum_set(op->ptr, + "asset_library_reference", + asset::library_reference_to_enum_value(&first_library)); + } + } + + /* By default, put the new asset in the same catalog as the existing asset. */ + if (!RNA_struct_property_is_set(op->ptr, "catalog_path")) { + const asset_system::CatalogID &id = asset->get_metadata().catalog_id; + if (const asset_system::AssetCatalog *catalog = library.catalog_service().find_catalog(id)) { + RNA_string_set(op->ptr, "catalog_path", catalog->path.c_str()); + } + } + + return WM_operator_props_dialog_popup(C, op, 400, std::nullopt, IFACE_("Save")); +} + +static const EnumPropertyItem *rna_asset_library_reference_itemf(bContext * /*C*/, + PointerRNA * /*ptr*/, + PropertyRNA * /*prop*/, + bool *r_free) +{ + const EnumPropertyItem *items = asset::library_reference_to_rna_enum_itemf(false); + if (!items) { + *r_free = false; + return nullptr; + } + + *r_free = true; + return items; +} + +static void visit_library_catalogs_catalog_for_search( + const Main &bmain, + const bUserAssetLibrary &user_library, + const StringRef edit_text, + const FunctionRef visit_fn) +{ + const asset_system::AssetLibrary *library = AS_asset_library_load( + &bmain, user_library_to_library_ref(user_library)); + if (!library) { + return; + } + + if (!edit_text.is_empty()) { + const asset_system::AssetCatalogPath edit_path = edit_text; + if (!library->catalog_service().find_catalog_by_path(edit_path)) { + visit_fn(StringPropertySearchVisitParams{edit_path.str(), std::nullopt, ICON_ADD}); + } + } + + const asset_system::AssetCatalogTree &full_tree = library->catalog_service().catalog_tree(); + full_tree.foreach_item([&](const asset_system::AssetCatalogTreeItem &item) { + visit_fn(StringPropertySearchVisitParams{item.catalog_path().str(), std::nullopt}); + }); +} + +static void visit_library_prop_catalogs_catalog_for_search_fn( + const bContext *C, + PointerRNA *ptr, + PropertyRNA * /*prop*/, + const char *edit_text, + FunctionRef visit_fn) +{ + /* NOTE: Using the all library would also be a valid choice. */ + if (const bUserAssetLibrary *user_library = get_asset_library_from_prop(*ptr)) { + visit_library_catalogs_catalog_for_search( + *CTX_data_main(C), *user_library, edit_text, visit_fn); + } +} + +void BRUSH_OT_asset_save_as(wmOperatorType *ot) +{ + ot->name = "Save as Brush Asset"; + ot->description = + "Save a copy of the active brush asset into the default asset library, and make it the " + "active brush"; + ot->idname = "BRUSH_OT_asset_save_as"; + + ot->exec = brush_asset_save_as_exec; + ot->invoke = brush_asset_save_as_invoke; + ot->poll = brush_asset_save_as_poll; + + ot->prop = RNA_def_string( + ot->srna, "name", nullptr, MAX_NAME, "Name", "Name for the new brush asset"); + + PropertyRNA *prop = RNA_def_property(ot->srna, "asset_library_reference", PROP_ENUM, PROP_NONE); + RNA_def_enum_funcs(prop, rna_asset_library_reference_itemf); + RNA_def_property_ui_text(prop, "Library", "Asset library used to store the new brush"); + + prop = RNA_def_string( + ot->srna, "catalog_path", nullptr, MAX_NAME, "Catalog", "Catalog to use for the new asset"); + RNA_def_property_string_search_func_runtime( + prop, visit_library_prop_catalogs_catalog_for_search_fn, PROP_STRING_SEARCH_SUGGESTION); +} + +static int brush_asset_edit_metadata_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + const Paint *paint = BKE_paint_get_active_from_context(C); + const Brush *brush = (paint) ? BKE_paint_brush_for_read(paint) : nullptr; + BLI_assert(ID_IS_ASSET(&brush->id)); + const AssetWeakReference &brush_weak_ref = *paint->brush_asset_reference; + const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref( + *C, brush_weak_ref, op->reports); + if (!asset) { + return OPERATOR_CANCELLED; + } + const asset_system::AssetLibrary &library_const = asset->owner_asset_library(); + const AssetLibraryReference library_ref = *library_to_library_ref(library_const); + asset_system::AssetLibrary *library = AS_asset_library_load(bmain, library_ref); + + char catalog_path[MAX_NAME]; + RNA_string_get(op->ptr, "catalog_path", catalog_path); + + AssetMetaData &meta_data = *brush->id.asset_data; + MEM_SAFE_FREE(meta_data.author); + meta_data.author = RNA_string_get_alloc(op->ptr, "author", nullptr, 0, nullptr); + MEM_SAFE_FREE(meta_data.description); + meta_data.description = RNA_string_get_alloc(op->ptr, "description", nullptr, 0, nullptr); + + if (catalog_path[0]) { + const asset_system::AssetCatalog &catalog = asset_library_ensure_catalogs_in_path( + *library, catalog_path); + BKE_asset_metadata_catalog_id_set(&meta_data, catalog.catalog_id, catalog.simple_name.c_str()); + } + + if (!bke::asset_edit_id_save(*bmain, brush->id, *op->reports)) { + return OPERATOR_CANCELLED; + } + + char asset_full_path_buffer[FILE_MAX_LIBEXTRA]; + char *file_path = nullptr; + AS_asset_full_path_explode_from_weak_ref( + &brush_weak_ref, asset_full_path_buffer, &file_path, nullptr, nullptr); + if (!file_path) { + BLI_assert_unreachable(); + return OPERATOR_CANCELLED; + } + + library->catalog_service().write_to_disk(file_path); + + refresh_asset_library(C, library_ref); + WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_EDITED, nullptr); + + return OPERATOR_FINISHED; +} + +static int brush_asset_edit_metadata_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) +{ + const Paint *paint = BKE_paint_get_active_from_context(C); + const AssetWeakReference &brush_weak_ref = *paint->brush_asset_reference; + const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref( + *C, brush_weak_ref, op->reports); + if (!asset) { + return OPERATOR_CANCELLED; + } + const asset_system::AssetLibrary &library = asset->owner_asset_library(); + const AssetMetaData &meta_data = asset->get_metadata(); + + if (!RNA_struct_property_is_set(op->ptr, "catalog_path")) { + const asset_system::CatalogID &id = meta_data.catalog_id; + if (const asset_system::AssetCatalog *catalog = library.catalog_service().find_catalog(id)) { + RNA_string_set(op->ptr, "catalog_path", catalog->path.c_str()); + } + } + if (!RNA_struct_property_is_set(op->ptr, "author")) { + RNA_string_set(op->ptr, "author", meta_data.author ? meta_data.author : ""); + } + if (!RNA_struct_property_is_set(op->ptr, "description")) { + RNA_string_set(op->ptr, "description", meta_data.description ? meta_data.description : ""); + } + + return WM_operator_props_dialog_popup(C, op, 400, std::nullopt, IFACE_("Edit Metadata")); +} + +static void visit_active_library_catalogs_catalog_for_search_fn( + const bContext *C, + PointerRNA * /*ptr*/, + PropertyRNA * /*prop*/, + const char *edit_text, + FunctionRef visit_fn) +{ + const Paint *paint = BKE_paint_get_active_from_context(C); + const AssetWeakReference &brush_weak_ref = *paint->brush_asset_reference; + const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref( + *C, brush_weak_ref, nullptr); + if (!asset) { + return; + } + const asset_system::AssetLibrary &library = asset->owner_asset_library(); + + /* NOTE: Using the all library would also be a valid choice. */ + visit_library_catalogs_catalog_for_search( + *CTX_data_main(C), + *library_ref_to_user_library(*library_to_library_ref(library)), + edit_text, + visit_fn); +} + +static bool brush_asset_edit_metadata_poll(bContext *C) +{ + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr; + if (paint == nullptr || brush == nullptr) { + return false; + } + if (!ID_IS_ASSET(&brush->id)) { + BLI_assert_unreachable(); + return false; + } + const AssetWeakReference *brush_weak_ref = paint->brush_asset_reference; + if (!brush_weak_ref) { + BLI_assert_unreachable(); + return false; + } + const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref( + *C, *brush_weak_ref, nullptr); + if (!asset) { + BLI_assert_unreachable(); + return false; + } + const std::optional library_ref = library_to_library_ref( + asset->owner_asset_library()); + if (!library_ref) { + BLI_assert_unreachable(); + return false; + } + if (!library_is_editable(*library_ref)) { + CTX_wm_operator_poll_msg_set(C, "Asset library is not editable"); + return false; + } + if (!bke::asset_edit_id_is_editable(brush->id)) { + CTX_wm_operator_poll_msg_set(C, "Asset file is not editable"); + return false; + } + return true; +} + +void BRUSH_OT_asset_edit_metadata(wmOperatorType *ot) +{ + ot->name = "Edit Metadata"; + ot->description = "Edit asset information like the catalog, preview image, tags, or author"; + ot->idname = "BRUSH_OT_asset_edit_metadata"; + + ot->exec = brush_asset_edit_metadata_exec; + ot->invoke = brush_asset_edit_metadata_invoke; + ot->poll = brush_asset_edit_metadata_poll; + + PropertyRNA *prop = RNA_def_string( + ot->srna, "catalog_path", nullptr, MAX_NAME, "Catalog", "The asset's catalog path"); + RNA_def_property_string_search_func_runtime( + prop, visit_active_library_catalogs_catalog_for_search_fn, PROP_STRING_SEARCH_SUGGESTION); + RNA_def_string(ot->srna, "author", nullptr, MAX_NAME, "Author", ""); + RNA_def_string(ot->srna, "description", nullptr, MAX_NAME, "Description", ""); +} + +static bool brush_asset_delete_poll(bContext *C) +{ + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr; + if (paint == nullptr || brush == nullptr) { + return false; + } + if (!paint->brush_asset_reference) { + return false; + } + + /* Asset brush, check if belongs to an editable blend file. */ + if (ID_IS_ASSET(brush)) { + if (!bke::asset_edit_id_is_editable(brush->id)) { + CTX_wm_operator_poll_msg_set(C, "Asset blend file is not editable"); + return false; + } + } + + return true; +} + +static int brush_asset_delete_exec(bContext *C, wmOperator *op) +{ + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *brush = BKE_paint_brush(paint); + Main *bmain = CTX_data_main(C); + + bUserAssetLibrary *library = BKE_preferences_asset_library_find_by_name( + &U, paint->brush_asset_reference->asset_library_identifier); + if (!library) { + return OPERATOR_CANCELLED; + } + + bke::asset_edit_id_delete(*bmain, brush->id, *op->reports); + + refresh_asset_library(C, *library); + + BKE_paint_brush_set_default(bmain, paint); + + WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_REMOVED, nullptr); + WM_main_add_notifier(NC_BRUSH | NA_EDITED, nullptr); + + return OPERATOR_FINISHED; +} + +static int brush_asset_delete_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) +{ + return WM_operator_confirm_ex( + C, + op, + IFACE_("Delete Brush Asset"), + IFACE_("Permanently delete brush asset blend file. This can't be undone."), + IFACE_("Delete"), + ALERT_ICON_WARNING, + false); +} + +void BRUSH_OT_asset_delete(wmOperatorType *ot) +{ + ot->name = "Delete Brush Asset"; + ot->description = "Delete the active brush asset both from the local session and asset library"; + ot->idname = "BRUSH_OT_asset_delete"; + + ot->exec = brush_asset_delete_exec; + ot->invoke = brush_asset_delete_invoke; + ot->poll = brush_asset_delete_poll; +} + +static bool brush_asset_update_poll(bContext *C) +{ + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr; + if (paint == nullptr || brush == nullptr) { + return false; + } + + if ((brush->id.tag & LIB_TAG_ASSET_MAIN) == 0) { + return false; + } + + if (!(paint->brush_asset_reference && ID_IS_ASSET(brush))) { + return false; + } + + if (!bke::asset_edit_id_is_editable(brush->id)) { + CTX_wm_operator_poll_msg_set(C, "Asset blend file is not editable"); + return false; + } + + return true; +} + +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 = BKE_paint_brush(paint); + const AssetWeakReference *asset_weak_ref = paint->brush_asset_reference; + + const bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_by_name( + &U, asset_weak_ref->asset_library_identifier); + if (!user_library) { + return OPERATOR_CANCELLED; + } + + BLI_assert(ID_IS_ASSET(brush)); + + bke::asset_edit_id_save(*bmain, brush->id, *op->reports); + + refresh_asset_library(C, *user_library); + WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_EDITED, nullptr); + WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush); + + return OPERATOR_FINISHED; +} + +void BRUSH_OT_asset_update(wmOperatorType *ot) +{ + ot->name = "Update Brush Asset"; + ot->description = "Update the active brush asset in the asset library with current settings"; + ot->idname = "BRUSH_OT_asset_update"; + + ot->exec = brush_asset_update_exec; + ot->poll = brush_asset_update_poll; +} + +static bool brush_asset_revert_poll(bContext *C) +{ + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr; + if (paint == nullptr || brush == nullptr) { + return false; + } + + return paint->brush_asset_reference && (brush->id.tag & LIB_TAG_ASSET_MAIN); +} + +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); + + bke::asset_edit_id_revert(*bmain, brush->id, *op->reports); + + WM_main_add_notifier(NC_BRUSH | NA_EDITED, nullptr); + WM_main_add_notifier(NC_TEXTURE | ND_NODES, nullptr); + + return OPERATOR_FINISHED; +} + +void BRUSH_OT_asset_revert(wmOperatorType *ot) +{ + ot->name = "Revert Brush Asset"; + ot->description = + "Revert the active brush settings to the default values from the asset library"; + ot->idname = "BRUSH_OT_asset_revert"; + + ot->exec = brush_asset_revert_exec; + ot->poll = brush_asset_revert_poll; +} + +} // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/paint_intern.hh b/source/blender/editors/sculpt_paint/paint_intern.hh index 7f3a96cbc15..823531e5fc7 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.hh +++ b/source/blender/editors/sculpt_paint/paint_intern.hh @@ -108,6 +108,13 @@ bool paint_stroke_started(PaintStroke *stroke); bool paint_brush_tool_poll(bContext *C); +void BRUSH_OT_asset_select(wmOperatorType *ot); +void BRUSH_OT_asset_save_as(wmOperatorType *ot); +void BRUSH_OT_asset_edit_metadata(wmOperatorType *ot); +void BRUSH_OT_asset_delete(wmOperatorType *ot); +void BRUSH_OT_asset_update(wmOperatorType *ot); +void BRUSH_OT_asset_revert(wmOperatorType *ot); + } // namespace blender::ed::sculpt_paint /** diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index e4743fc4945..1d17f9ae075 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -12,10 +12,8 @@ #include "MEM_guardedalloc.h" -#include "BLI_fileops.h" #include "BLI_listbase.h" #include "BLI_math_vector.h" -#include "BLI_path_util.h" #include "BLI_string.h" #include "BLI_utildefines.h" @@ -26,28 +24,14 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" -#include "BLO_writefile.hh" - -#include "BKE_asset.hh" -#include "BKE_asset_edit.hh" -#include "BKE_blendfile.hh" #include "BKE_brush.hh" #include "BKE_context.hh" #include "BKE_image.h" #include "BKE_lib_id.hh" -#include "BKE_lib_remap.hh" #include "BKE_main.hh" #include "BKE_paint.hh" -#include "BKE_preferences.h" #include "BKE_report.hh" -#include "BKE_screen.hh" -#include "ED_asset_handle.hh" -#include "ED_asset_library.hh" -#include "ED_asset_list.hh" -#include "ED_asset_mark_clear.hh" -#include "ED_asset_menu_utils.hh" -#include "ED_asset_shelf.hh" #include "ED_image.hh" #include "ED_paint.hh" #include "ED_screen.hh" @@ -59,16 +43,6 @@ #include "RNA_access.hh" #include "RNA_define.hh" -#include "BLT_translation.hh" - -#include "AS_asset_catalog_path.hh" -#include "AS_asset_catalog_tree.hh" -#include "AS_asset_library.hh" -#include "AS_asset_representation.hh" - -#include "UI_interface_icons.hh" -#include "UI_resources.hh" - #include "curves_sculpt_intern.hh" #include "paint_intern.hh" #include "sculpt_intern.hh" @@ -724,715 +698,6 @@ static void PALETTE_OT_join(wmOperatorType *ot) RNA_def_string(ot->srna, "palette", nullptr, MAX_ID_NAME - 2, "Palette", "Name of the Palette"); } -namespace blender::ed::sculpt_paint { - -/**************************** Brush Assets **********************************/ - -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); - const asset_system::AssetRepresentation *asset = - asset::operator_asset_reference_props_get_asset_from_all_library(*C, *op->ptr, op->reports); - if (!asset) { - return OPERATOR_CANCELLED; - } - - AssetWeakReference brush_asset_reference = asset->make_weak_reference(); - Brush *brush = reinterpret_cast( - bke::asset_edit_id_from_weak_reference(*bmain, ID_BR, brush_asset_reference)); - - Paint *paint = BKE_paint_get_active_from_context(C); - - if (!BKE_paint_brush_asset_set(paint, brush, brush_asset_reference)) { - /* Note brush datablock was still added, so was not a no-op. */ - BKE_report(op->reports, RPT_WARNING, "Unable to select brush, wrong object mode"); - return OPERATOR_FINISHED; - } - - WM_main_add_notifier(NC_ASSET | NA_ACTIVATED, nullptr); - WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, nullptr); - WM_toolsystem_ref_set_by_id(C, "builtin.brush"); - - return OPERATOR_FINISHED; -} - -static void BRUSH_OT_asset_select(wmOperatorType *ot) -{ - ot->name = "Select Brush Asset"; - ot->description = "Select a brush asset as current sculpt and paint tool"; - ot->idname = "BRUSH_OT_asset_select"; - - ot->exec = brush_asset_select_exec; - - asset::operator_asset_reference_props_register(*ot->srna); -} - -/* FIXME Quick dirty hack to generate a weak ref from 'raw' paths. - * This needs to be properly implemented in assetlib code. - */ -static AssetWeakReference brush_asset_create_weakref_hack(const bUserAssetLibrary *user_asset_lib, - const std::string &file_path) -{ - AssetWeakReference asset_weak_ref{}; - - StringRef asset_root_path = user_asset_lib->dirpath; - BLI_assert(file_path.find(asset_root_path) == 0); - std::string relative_asset_path = file_path.substr(size_t(asset_root_path.size()) + 1); - - asset_weak_ref.asset_library_type = ASSET_LIBRARY_CUSTOM; - asset_weak_ref.asset_library_identifier = BLI_strdup(user_asset_lib->name); - asset_weak_ref.relative_asset_identifier = BLI_strdupn(relative_asset_path.c_str(), - relative_asset_path.size()); - - return asset_weak_ref; -} - -static std::optional library_to_library_ref( - const asset_system::AssetLibrary &library) -{ - for (const AssetLibraryReference &ref : asset_system::all_valid_asset_library_refs()) { - const std::string root_path = AS_asset_library_root_path_from_library_ref(ref); - /* Use #BLI_path_cmp_normalized because `library.root_path()` ends with a slash while - * `root_path` doesn't. */ - if (BLI_path_cmp_normalized(root_path.c_str(), library.root_path().c_str()) == 0) { - return ref; - } - } - return std::nullopt; -} - -static AssetLibraryReference user_library_to_library_ref(const bUserAssetLibrary &user_library) -{ - AssetLibraryReference library_ref{}; - library_ref.custom_library_index = BLI_findindex(&U.asset_libraries, &user_library); - library_ref.type = ASSET_LIBRARY_CUSTOM; - return library_ref; -} - -static const bUserAssetLibrary *library_ref_to_user_library( - const AssetLibraryReference &library_ref) -{ - if (library_ref.type != ASSET_LIBRARY_CUSTOM) { - return nullptr; - } - return static_cast( - BLI_findlink(&U.asset_libraries, library_ref.custom_library_index)); -} - -static void refresh_asset_library(const bContext *C, const AssetLibraryReference &library_ref) -{ - asset::list::clear(&library_ref, C); - /* TODO: Should the all library reference be automatically cleared? */ - AssetLibraryReference all_lib_ref = asset_system::all_library_reference(); - asset::list::clear(&all_lib_ref, C); -} - -static void refresh_asset_library(const bContext *C, const bUserAssetLibrary &user_library) -{ - refresh_asset_library(C, user_library_to_library_ref(user_library)); -} - -static bool brush_asset_save_as_poll(bContext *C) -{ - Paint *paint = BKE_paint_get_active_from_context(C); - Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr; - if (paint == nullptr || brush == nullptr) { - return false; - } - if (!paint->brush_asset_reference) { - /* The brush should always be an imported asset. We use this asset reference to find - * which library and catalog the brush came from, as defaults for the popup. */ - BLI_assert_unreachable(); - return false; - } - - if (BLI_listbase_is_empty(&U.asset_libraries)) { - CTX_wm_operator_poll_msg_set(C, "No asset library available to save to"); - return false; - } - - return true; -} - -static const bUserAssetLibrary *get_asset_library_from_prop(PointerRNA &ptr) -{ - const int enum_value = RNA_enum_get(&ptr, "asset_library_reference"); - const AssetLibraryReference lib_ref = asset::library_reference_from_enum_value(enum_value); - return BKE_preferences_asset_library_find_index(&U, lib_ref.custom_library_index); -} - -static asset_system::AssetCatalog &asset_library_ensure_catalog( - asset_system::AssetLibrary &library, const asset_system::AssetCatalogPath &path) -{ - if (asset_system::AssetCatalog *catalog = library.catalog_service().find_catalog_by_path(path)) { - return *catalog; - } - return *library.catalog_service().create_catalog(path); -} - -static asset_system::AssetCatalog &asset_library_ensure_catalogs_in_path( - asset_system::AssetLibrary &library, const asset_system::AssetCatalogPath &path) -{ - /* Adding multiple catalogs in a path at a time with #AssetCatalogService::create_catalog() - * doesn't work; add each potentially new catalog in the hierarchy manually here. */ - asset_system::AssetCatalogPath parent = ""; - path.iterate_components([&](StringRef component_name, bool /*is_last_component*/) { - asset_library_ensure_catalog(library, parent / component_name); - parent = parent / component_name; - }); - return *library.catalog_service().find_catalog_by_path(path); -} - -static void show_catalog_in_asset_shelf(const bContext &C, const StringRefNull catalog_path) -{ - ScrArea *area = CTX_wm_area(&C); - if (!area) { - return; - } - const AssetShelf *shelf = asset::shelf::active_shelf_from_area(area); - if (!BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled( - &U, shelf->idname, catalog_path.c_str())) - { - return; - } - U.runtime.is_dirty = true; -} - -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; - - /* Determine file path to save to. */ - PropertyRNA *name_prop = RNA_struct_find_property(op->ptr, "name"); - char name[MAX_NAME] = ""; - if (RNA_property_is_set(op->ptr, name_prop)) { - RNA_property_string_get(op->ptr, name_prop, name); - } - if (name[0] == '\0') { - STRNCPY(name, brush->id.name + 2); - } - - const bUserAssetLibrary *user_library = get_asset_library_from_prop(*op->ptr); - if (!user_library) { - return OPERATOR_CANCELLED; - } - - asset_system::AssetLibrary *library = AS_asset_library_load( - bmain, user_library_to_library_ref(*user_library)); - if (!library) { - BKE_report(op->reports, RPT_ERROR, "Failed to load asset library"); - return OPERATOR_CANCELLED; - } - - /* Turn brush into asset if it isn't yet. */ - if (!ID_IS_ASSET(&brush->id)) { - asset::mark_id(&brush->id); - asset::generate_preview(C, &brush->id); - } - BLI_assert(ID_IS_ASSET(&brush->id)); - - /* Add asset to catalog. */ - char catalog_path[MAX_NAME]; - RNA_string_get(op->ptr, "catalog_path", catalog_path); - - AssetMetaData &meta_data = *brush->id.asset_data; - if (catalog_path[0]) { - const asset_system::AssetCatalog &catalog = asset_library_ensure_catalogs_in_path( - *library, catalog_path); - BKE_asset_metadata_catalog_id_set(&meta_data, catalog.catalog_id, catalog.simple_name.c_str()); - } - - const std::optional final_full_asset_filepath = bke::asset_edit_id_save_as( - *bmain, brush->id, name, *user_library, *op->reports); - if (!final_full_asset_filepath) { - return OPERATOR_CANCELLED; - } - - library->catalog_service().write_to_disk(*final_full_asset_filepath); - show_catalog_in_asset_shelf(*C, catalog_path); - - AssetWeakReference new_brush_weak_ref = brush_asset_create_weakref_hack( - user_library, *final_full_asset_filepath); - - brush = reinterpret_cast( - bke::asset_edit_id_from_weak_reference(*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. */ - BKE_report(op->reports, RPT_WARNING, "Unable to activate just-saved brush asset"); - } - - refresh_asset_library(C, *user_library); - WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_ADDED, nullptr); - WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush); - - return OPERATOR_FINISHED; -} - -static bool library_is_editable(const AssetLibraryReference &library) -{ - if (library.type == ASSET_LIBRARY_ESSENTIALS) { - return false; - } - return true; -} - -static int brush_asset_save_as_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) -{ - Paint *paint = BKE_paint_get_active_from_context(C); - const AssetWeakReference &brush_weak_ref = *paint->brush_asset_reference; - const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref( - *C, brush_weak_ref, op->reports); - if (!asset) { - return OPERATOR_CANCELLED; - } - const asset_system::AssetLibrary &library = asset->owner_asset_library(); - const std::optional library_ref = library_to_library_ref(library); - if (!library_ref) { - BLI_assert_unreachable(); - return OPERATOR_CANCELLED; - } - - RNA_string_set(op->ptr, "name", asset->get_name().c_str()); - - /* If the library isn't saved from the operator's last execution, find the current library or the - * first library if the current library isn't editable. */ - if (!RNA_struct_property_is_set_ex(op->ptr, "asset_library_reference", false)) { - if (library_is_editable(*library_ref)) { - RNA_enum_set(op->ptr, - "asset_library_reference", - asset::library_reference_to_enum_value(&*library_ref)); - } - else { - const AssetLibraryReference first_library = user_library_to_library_ref( - *static_cast(U.asset_libraries.first)); - RNA_enum_set(op->ptr, - "asset_library_reference", - asset::library_reference_to_enum_value(&first_library)); - } - } - - /* By default, put the new asset in the same catalog as the existing asset. */ - if (!RNA_struct_property_is_set(op->ptr, "catalog_path")) { - const asset_system::CatalogID &id = asset->get_metadata().catalog_id; - if (const asset_system::AssetCatalog *catalog = library.catalog_service().find_catalog(id)) { - RNA_string_set(op->ptr, "catalog_path", catalog->path.c_str()); - } - } - - return WM_operator_props_dialog_popup(C, op, 400, std::nullopt, IFACE_("Save")); -} - -static const EnumPropertyItem *rna_asset_library_reference_itemf(bContext * /*C*/, - PointerRNA * /*ptr*/, - PropertyRNA * /*prop*/, - bool *r_free) -{ - const EnumPropertyItem *items = asset::library_reference_to_rna_enum_itemf(false); - if (!items) { - *r_free = false; - return nullptr; - } - - *r_free = true; - return items; -} - -static void visit_library_catalogs_catalog_for_search( - const Main &bmain, - const bUserAssetLibrary &user_library, - const StringRef edit_text, - const FunctionRef visit_fn) -{ - const asset_system::AssetLibrary *library = AS_asset_library_load( - &bmain, user_library_to_library_ref(user_library)); - if (!library) { - return; - } - - if (!edit_text.is_empty()) { - const asset_system::AssetCatalogPath edit_path = edit_text; - if (!library->catalog_service().find_catalog_by_path(edit_path)) { - visit_fn(StringPropertySearchVisitParams{edit_path.str(), std::nullopt, ICON_ADD}); - } - } - - const asset_system::AssetCatalogTree &full_tree = library->catalog_service().catalog_tree(); - full_tree.foreach_item([&](const asset_system::AssetCatalogTreeItem &item) { - visit_fn(StringPropertySearchVisitParams{item.catalog_path().str(), std::nullopt}); - }); -} - -static void visit_library_prop_catalogs_catalog_for_search_fn( - const bContext *C, - PointerRNA *ptr, - PropertyRNA * /*prop*/, - const char *edit_text, - FunctionRef visit_fn) -{ - /* NOTE: Using the all library would also be a valid choice. */ - if (const bUserAssetLibrary *user_library = get_asset_library_from_prop(*ptr)) { - visit_library_catalogs_catalog_for_search( - *CTX_data_main(C), *user_library, edit_text, visit_fn); - } -} - -static void BRUSH_OT_asset_save_as(wmOperatorType *ot) -{ - ot->name = "Save as Brush Asset"; - ot->description = - "Save a copy of the active brush asset into the default asset library, and make it the " - "active brush"; - ot->idname = "BRUSH_OT_asset_save_as"; - - ot->exec = brush_asset_save_as_exec; - ot->invoke = brush_asset_save_as_invoke; - ot->poll = brush_asset_save_as_poll; - - ot->prop = RNA_def_string( - ot->srna, "name", nullptr, MAX_NAME, "Name", "Name for the new brush asset"); - - PropertyRNA *prop = RNA_def_property(ot->srna, "asset_library_reference", PROP_ENUM, PROP_NONE); - RNA_def_enum_funcs(prop, rna_asset_library_reference_itemf); - RNA_def_property_ui_text(prop, "Library", "Asset library used to store the new brush"); - - prop = RNA_def_string( - ot->srna, "catalog_path", nullptr, MAX_NAME, "Catalog", "Catalog to use for the new asset"); - RNA_def_property_string_search_func_runtime( - prop, visit_library_prop_catalogs_catalog_for_search_fn, PROP_STRING_SEARCH_SUGGESTION); -} - -static int brush_asset_edit_metadata_exec(bContext *C, wmOperator *op) -{ - Main *bmain = CTX_data_main(C); - const Paint *paint = BKE_paint_get_active_from_context(C); - const Brush *brush = (paint) ? BKE_paint_brush_for_read(paint) : nullptr; - BLI_assert(ID_IS_ASSET(&brush->id)); - const AssetWeakReference &brush_weak_ref = *paint->brush_asset_reference; - const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref( - *C, brush_weak_ref, op->reports); - if (!asset) { - return OPERATOR_CANCELLED; - } - const asset_system::AssetLibrary &library_const = asset->owner_asset_library(); - const AssetLibraryReference library_ref = *library_to_library_ref(library_const); - asset_system::AssetLibrary *library = AS_asset_library_load(bmain, library_ref); - - char catalog_path[MAX_NAME]; - RNA_string_get(op->ptr, "catalog_path", catalog_path); - - AssetMetaData &meta_data = *brush->id.asset_data; - MEM_SAFE_FREE(meta_data.author); - meta_data.author = RNA_string_get_alloc(op->ptr, "author", nullptr, 0, nullptr); - MEM_SAFE_FREE(meta_data.description); - meta_data.description = RNA_string_get_alloc(op->ptr, "description", nullptr, 0, nullptr); - - if (catalog_path[0]) { - const asset_system::AssetCatalog &catalog = asset_library_ensure_catalogs_in_path( - *library, catalog_path); - BKE_asset_metadata_catalog_id_set(&meta_data, catalog.catalog_id, catalog.simple_name.c_str()); - } - - if (!bke::asset_edit_id_save(*bmain, brush->id, *op->reports)) { - return OPERATOR_CANCELLED; - } - - char asset_full_path_buffer[FILE_MAX_LIBEXTRA]; - char *file_path = nullptr; - AS_asset_full_path_explode_from_weak_ref( - &brush_weak_ref, asset_full_path_buffer, &file_path, nullptr, nullptr); - if (!file_path) { - BLI_assert_unreachable(); - return OPERATOR_CANCELLED; - } - - library->catalog_service().write_to_disk(file_path); - - refresh_asset_library(C, library_ref); - WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_EDITED, nullptr); - - return OPERATOR_FINISHED; -} - -static int brush_asset_edit_metadata_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) -{ - const Paint *paint = BKE_paint_get_active_from_context(C); - const AssetWeakReference &brush_weak_ref = *paint->brush_asset_reference; - const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref( - *C, brush_weak_ref, op->reports); - if (!asset) { - return OPERATOR_CANCELLED; - } - const asset_system::AssetLibrary &library = asset->owner_asset_library(); - const AssetMetaData &meta_data = asset->get_metadata(); - - if (!RNA_struct_property_is_set(op->ptr, "catalog_path")) { - const asset_system::CatalogID &id = meta_data.catalog_id; - if (const asset_system::AssetCatalog *catalog = library.catalog_service().find_catalog(id)) { - RNA_string_set(op->ptr, "catalog_path", catalog->path.c_str()); - } - } - if (!RNA_struct_property_is_set(op->ptr, "author")) { - RNA_string_set(op->ptr, "author", meta_data.author ? meta_data.author : ""); - } - if (!RNA_struct_property_is_set(op->ptr, "description")) { - RNA_string_set(op->ptr, "description", meta_data.description ? meta_data.description : ""); - } - - return WM_operator_props_dialog_popup(C, op, 400, std::nullopt, IFACE_("Edit Metadata")); -} - -static void visit_active_library_catalogs_catalog_for_search_fn( - const bContext *C, - PointerRNA * /*ptr*/, - PropertyRNA * /*prop*/, - const char *edit_text, - FunctionRef visit_fn) -{ - const Paint *paint = BKE_paint_get_active_from_context(C); - const AssetWeakReference &brush_weak_ref = *paint->brush_asset_reference; - const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref( - *C, brush_weak_ref, nullptr); - if (!asset) { - return; - } - const asset_system::AssetLibrary &library = asset->owner_asset_library(); - - /* NOTE: Using the all library would also be a valid choice. */ - visit_library_catalogs_catalog_for_search( - *CTX_data_main(C), - *library_ref_to_user_library(*library_to_library_ref(library)), - edit_text, - visit_fn); -} - -static bool brush_asset_edit_metadata_poll(bContext *C) -{ - Paint *paint = BKE_paint_get_active_from_context(C); - Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr; - if (paint == nullptr || brush == nullptr) { - return false; - } - if (!ID_IS_ASSET(&brush->id)) { - BLI_assert_unreachable(); - return false; - } - const AssetWeakReference *brush_weak_ref = paint->brush_asset_reference; - if (!brush_weak_ref) { - BLI_assert_unreachable(); - return false; - } - const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref( - *C, *brush_weak_ref, nullptr); - if (!asset) { - BLI_assert_unreachable(); - return false; - } - const std::optional library_ref = library_to_library_ref( - asset->owner_asset_library()); - if (!library_ref) { - BLI_assert_unreachable(); - return false; - } - if (!library_is_editable(*library_ref)) { - CTX_wm_operator_poll_msg_set(C, "Asset library is not editable"); - return false; - } - if (!bke::asset_edit_id_is_editable(brush->id)) { - CTX_wm_operator_poll_msg_set(C, "Asset file is not editable"); - return false; - } - return true; -} - -static void BRUSH_OT_asset_edit_metadata(wmOperatorType *ot) -{ - ot->name = "Edit Metadata"; - ot->description = "Edit asset information like the catalog, preview image, tags, or author"; - ot->idname = "BRUSH_OT_asset_edit_metadata"; - - ot->exec = brush_asset_edit_metadata_exec; - ot->invoke = brush_asset_edit_metadata_invoke; - ot->poll = brush_asset_edit_metadata_poll; - - PropertyRNA *prop = RNA_def_string( - ot->srna, "catalog_path", nullptr, MAX_NAME, "Catalog", "The asset's catalog path"); - RNA_def_property_string_search_func_runtime( - prop, visit_active_library_catalogs_catalog_for_search_fn, PROP_STRING_SEARCH_SUGGESTION); - RNA_def_string(ot->srna, "author", nullptr, MAX_NAME, "Author", ""); - RNA_def_string(ot->srna, "description", nullptr, MAX_NAME, "Description", ""); -} - -static bool brush_asset_delete_poll(bContext *C) -{ - Paint *paint = BKE_paint_get_active_from_context(C); - Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr; - if (paint == nullptr || brush == nullptr) { - return false; - } - if (!paint->brush_asset_reference) { - return false; - } - - /* Asset brush, check if belongs to an editable blend file. */ - if (ID_IS_ASSET(brush)) { - if (!bke::asset_edit_id_is_editable(brush->id)) { - CTX_wm_operator_poll_msg_set(C, "Asset blend file is not editable"); - return false; - } - } - - return true; -} - -static int brush_asset_delete_exec(bContext *C, wmOperator *op) -{ - Paint *paint = BKE_paint_get_active_from_context(C); - Brush *brush = BKE_paint_brush(paint); - Main *bmain = CTX_data_main(C); - - bUserAssetLibrary *library = BKE_preferences_asset_library_find_by_name( - &U, paint->brush_asset_reference->asset_library_identifier); - if (!library) { - return OPERATOR_CANCELLED; - } - - bke::asset_edit_id_delete(*bmain, brush->id, *op->reports); - - refresh_asset_library(C, *library); - - BKE_paint_brush_set_default(bmain, paint); - - WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_REMOVED, nullptr); - WM_main_add_notifier(NC_BRUSH | NA_EDITED, nullptr); - - return OPERATOR_FINISHED; -} - -static int brush_asset_delete_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) -{ - return WM_operator_confirm_ex( - C, - op, - IFACE_("Delete Brush Asset"), - IFACE_("Permanently delete brush asset blend file. This can't be undone."), - IFACE_("Delete"), - ALERT_ICON_WARNING, - false); -} - -static void BRUSH_OT_asset_delete(wmOperatorType *ot) -{ - ot->name = "Delete Brush Asset"; - ot->description = "Delete the active brush asset both from the local session and asset library"; - ot->idname = "BRUSH_OT_asset_delete"; - - ot->exec = brush_asset_delete_exec; - ot->invoke = brush_asset_delete_invoke; - ot->poll = brush_asset_delete_poll; -} - -static bool brush_asset_update_poll(bContext *C) -{ - Paint *paint = BKE_paint_get_active_from_context(C); - Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr; - if (paint == nullptr || brush == nullptr) { - return false; - } - - if ((brush->id.tag & LIB_TAG_ASSET_MAIN) == 0) { - return false; - } - - if (!(paint->brush_asset_reference && ID_IS_ASSET(brush))) { - return false; - } - - if (!bke::asset_edit_id_is_editable(brush->id)) { - CTX_wm_operator_poll_msg_set(C, "Asset blend file is not editable"); - return false; - } - - return true; -} - -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 = BKE_paint_brush(paint); - const AssetWeakReference *asset_weak_ref = paint->brush_asset_reference; - - const bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_by_name( - &U, asset_weak_ref->asset_library_identifier); - if (!user_library) { - return OPERATOR_CANCELLED; - } - - BLI_assert(ID_IS_ASSET(brush)); - - bke::asset_edit_id_save(*bmain, brush->id, *op->reports); - - refresh_asset_library(C, *user_library); - WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_EDITED, nullptr); - WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush); - - return OPERATOR_FINISHED; -} - -static void BRUSH_OT_asset_update(wmOperatorType *ot) -{ - ot->name = "Update Brush Asset"; - ot->description = "Update the active brush asset in the asset library with current settings"; - ot->idname = "BRUSH_OT_asset_update"; - - ot->exec = brush_asset_update_exec; - ot->poll = brush_asset_update_poll; -} - -static bool brush_asset_revert_poll(bContext *C) -{ - Paint *paint = BKE_paint_get_active_from_context(C); - Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr; - if (paint == nullptr || brush == nullptr) { - return false; - } - - return paint->brush_asset_reference && (brush->id.tag & LIB_TAG_ASSET_MAIN); -} - -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); - - bke::asset_edit_id_revert(*bmain, brush->id, *op->reports); - - WM_main_add_notifier(NC_BRUSH | NA_EDITED, nullptr); - WM_main_add_notifier(NC_TEXTURE | ND_NODES, nullptr); - - return OPERATOR_FINISHED; -} - -static void BRUSH_OT_asset_revert(wmOperatorType *ot) -{ - ot->name = "Revert Brush Asset"; - ot->description = - "Revert the active brush settings to the default values from the asset library"; - ot->idname = "BRUSH_OT_asset_revert"; - - ot->exec = brush_asset_revert_exec; - ot->poll = brush_asset_revert_poll; -} - -} // namespace blender::ed::sculpt_paint - /***** Stencil Control *****/ enum StencilControlMode { -- 2.30.2 From 52e903ac130d78059f6e33cd46c8848d6ad18262 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 1 May 2024 18:11:37 +0200 Subject: [PATCH 198/244] Improve handling of optimization for drawing only visible grid view items --- .../editors/interface/views/grid_view.cc | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/source/blender/editors/interface/views/grid_view.cc b/source/blender/editors/interface/views/grid_view.cc index fe340fe888d..4747c7852e1 100644 --- a/source/blender/editors/interface/views/grid_view.cc +++ b/source/blender/editors/interface/views/grid_view.cc @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "BKE_icons.h" @@ -207,8 +208,9 @@ class BuildOnlyVisibleButtonsHelper { const AbstractGridView &grid_view_; const GridViewStyle &style_; const int cols_per_row_ = 0; - /* Indices of items within the view. Calculated by constructor */ - IndexRange visible_items_range_{}; + /* Indices of items within the view. Calculated by constructor. If this is unset it means all + * items/buttons should be drawn. */ + std::optional visible_items_range_; public: BuildOnlyVisibleButtonsHelper(const View2D &v2d, @@ -229,14 +231,14 @@ BuildOnlyVisibleButtonsHelper::BuildOnlyVisibleButtonsHelper(const View2D &v2d, const int cols_per_row) : grid_view_(grid_view), style_(grid_view.get_style()), cols_per_row_(cols_per_row) { - visible_items_range_ = this->get_visible_range(v2d); + if ((v2d.flag & V2D_IS_INIT) && grid_view.get_item_count_filtered()) { + visible_items_range_ = this->get_visible_range(v2d); + } } IndexRange BuildOnlyVisibleButtonsHelper::get_visible_range(const View2D &v2d) const { - if ((v2d.flag & V2D_IS_INIT) == 0) { - return IndexRange(0, grid_view_.get_item_count_filtered()); - } + BLI_assert(v2d.flag & V2D_IS_INIT); int first_idx_in_view = 0; @@ -257,15 +259,15 @@ IndexRange BuildOnlyVisibleButtonsHelper::get_visible_range(const View2D &v2d) c bool BuildOnlyVisibleButtonsHelper::is_item_visible(const int item_idx) const { - return visible_items_range_.contains(item_idx); + return !visible_items_range_ || visible_items_range_->contains(item_idx); } void BuildOnlyVisibleButtonsHelper::fill_layout_before_visible(uiBlock &block) const { - if (visible_items_range_.is_empty()) { + if (!visible_items_range_ || visible_items_range_->is_empty()) { return; } - const int first_idx_in_view = visible_items_range_.first(); + const int first_idx_in_view = visible_items_range_->first(); if (first_idx_in_view < 1) { return; } @@ -276,11 +278,11 @@ void BuildOnlyVisibleButtonsHelper::fill_layout_before_visible(uiBlock &block) c void BuildOnlyVisibleButtonsHelper::fill_layout_after_visible(uiBlock &block) const { - if (visible_items_range_.is_empty()) { + if (!visible_items_range_ || visible_items_range_->is_empty()) { return; } const int last_item_idx = grid_view_.get_item_count_filtered() - 1; - const int last_visible_idx = visible_items_range_.last(); + const int last_visible_idx = visible_items_range_->last(); if (last_item_idx > last_visible_idx) { const int remaining_rows = (cols_per_row_ > 0) ? ceilf((last_item_idx - last_visible_idx) / -- 2.30.2 From a63028a44c7a6c2bdf44b3288fe39f760d44dfaa Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 1 May 2024 18:58:04 +0200 Subject: [PATCH 199/244] Fix: Truncated ID name and filepath when saving brush asset --- source/blender/blenkernel/intern/asset_edit.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/asset_edit.cc b/source/blender/blenkernel/intern/asset_edit.cc index 36883795433..4dda887168c 100644 --- a/source/blender/blenkernel/intern/asset_edit.cc +++ b/source/blender/blenkernel/intern/asset_edit.cc @@ -171,7 +171,7 @@ static std::string asset_blendfile_path_for_save(const bUserAssetLibrary &user_l char base_name_filesafe[FILE_MAXFILE]; BLI_strncpy(base_name_filesafe, base_name.data(), - std::min(sizeof(base_name_filesafe), size_t(base_name.size()))); + std::min(sizeof(base_name_filesafe), size_t(base_name.size() + 1))); BLI_path_make_safe_filename(base_name_filesafe); const std::string filepath = root_path + SEP + base_name_filesafe + BLENDER_ASSET_FILE_SUFFIX; @@ -233,7 +233,7 @@ static bool asset_write_in_library(Main *bmain, id.flag |= LIB_FAKEUSER; id.tag &= ~LIB_TAG_RUNTIME; id.us = 1; - BLI_strncpy(id.name + 2, name.data(), std::min(sizeof(id.name) - 2, size_t(name.size()))); + BLI_strncpy(id.name + 2, name.data(), std::min(sizeof(id.name) - 2, size_t(name.size() + 1))); id.override_library = nullptr; BKE_blendfile_write_partial_tag_ID(&id, true); -- 2.30.2 From 66a1821683a0a7ddd5f01c072273eeb2d356ea7a Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 1 May 2024 17:01:45 +0200 Subject: [PATCH 200/244] Fix: Asset shelf catalogs not updated on brush asset save as * Update asset shelves in all open areas, the operator can not be expected to be running in the 3D viewport specifically. * Don't use unreachable asset when there is no shelf found with active_shelf_from_area. This is expected behavior in both places the function is used. --- .../editors/asset/intern/asset_shelf.cc | 1 - .../editors/sculpt_paint/brush_asset_ops.cc | 22 ++++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/source/blender/editors/asset/intern/asset_shelf.cc b/source/blender/editors/asset/intern/asset_shelf.cc index 1c9db98234e..9115643be14 100644 --- a/source/blender/editors/asset/intern/asset_shelf.cc +++ b/source/blender/editors/asset/intern/asset_shelf.cc @@ -567,7 +567,6 @@ AssetShelf *active_shelf_from_area(const ScrArea *area) const ARegion *shelf_region = BKE_area_find_region_type(area, RGN_TYPE_ASSET_SHELF); if (!shelf_region) { /* Called in wrong context, area doesn't have a shelf. */ - BLI_assert_unreachable(); return nullptr; } diff --git a/source/blender/editors/sculpt_paint/brush_asset_ops.cc b/source/blender/editors/sculpt_paint/brush_asset_ops.cc index 36dbf9fd393..e51efc5f095 100644 --- a/source/blender/editors/sculpt_paint/brush_asset_ops.cc +++ b/source/blender/editors/sculpt_paint/brush_asset_ops.cc @@ -205,17 +205,19 @@ static asset_system::AssetCatalog &asset_library_ensure_catalogs_in_path( static void show_catalog_in_asset_shelf(const bContext &C, const StringRefNull catalog_path) { - ScrArea *area = CTX_wm_area(&C); - if (!area) { - return; + /* Enable catalog in all visible asset shelves. */ + wmWindowManager *wm = CTX_wm_manager(&C); + LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { + const bScreen *screen = WM_window_get_active_screen(win); + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + const AssetShelf *shelf = asset::shelf::active_shelf_from_area(area); + if (shelf && BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled( + &U, shelf->idname, catalog_path.c_str())) + { + U.runtime.is_dirty = true; + } + } } - const AssetShelf *shelf = asset::shelf::active_shelf_from_area(area); - if (!BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled( - &U, shelf->idname, catalog_path.c_str())) - { - return; - } - U.runtime.is_dirty = true; } static int brush_asset_save_as_exec(bContext *C, wmOperator *op) -- 2.30.2 From 9b67a2ad65fa919e5378e71beda1fb2be328870a Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 1 May 2024 16:05:16 +0200 Subject: [PATCH 201/244] Brush Assets: Resolve some TODO comments in asset writing --- .../blender/blenkernel/intern/asset_edit.cc | 41 ++++++++----------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/source/blender/blenkernel/intern/asset_edit.cc b/source/blender/blenkernel/intern/asset_edit.cc index 4dda887168c..c4d847b028a 100644 --- a/source/blender/blenkernel/intern/asset_edit.cc +++ b/source/blender/blenkernel/intern/asset_edit.cc @@ -23,6 +23,7 @@ #include "BKE_asset_edit.hh" #include "BKE_blendfile.hh" #include "BKE_blendfile_link_append.hh" +#include "BKE_global.hh" #include "BKE_idtype.hh" #include "BKE_lib_id.hh" #include "BKE_lib_remap.hh" @@ -66,16 +67,10 @@ AssetEditBlend::AssetEditBlend(const std::string &filepath) this->main->is_asset_weak_reference_main = true; BLI_assert(!BLI_path_is_rel(filepath.c_str())); - /* Fairly simple check based on filepath only. - * - Ends with `.asset.bend` extensions. - * - Is located in user asset library. - * - * TODO? - * - Check file contents. - * - Check file is writable. - */ + /* Simple check, based on being a writable .asset.blend file in a user asset library. */ this->is_editable = StringRef(filepath).endswith(BLENDER_ASSET_FILE_SUFFIX) && - BKE_preferences_asset_library_containing_path(&U, filepath.c_str()); + BKE_preferences_asset_library_containing_path(&U, filepath.c_str()) && + BLI_file_is_writable(filepath.c_str()); } AssetEditBlend::~AssetEditBlend() @@ -164,10 +159,11 @@ static std::string asset_blendfile_path_for_save(const bUserAssetLibrary &user_l BLI_assert(!root_path.empty()); if (!BLI_dir_create_recursive(root_path.c_str())) { - BKE_report(&reports, RPT_ERROR, "Failed to create asset library directory to save brush"); + BKE_report(&reports, RPT_ERROR, "Failed to create asset library directory to save asset"); return ""; } + /* Make sure filename only contains valid characters for filesystem. */ char base_name_filesafe[FILE_MAXFILE]; BLI_strncpy(base_name_filesafe, base_name.data(), @@ -175,10 +171,12 @@ static std::string asset_blendfile_path_for_save(const bUserAssetLibrary &user_l BLI_path_make_safe_filename(base_name_filesafe); const std::string filepath = root_path + SEP + base_name_filesafe + BLENDER_ASSET_FILE_SUFFIX; + if (!BLI_is_file(filepath.c_str())) { return filepath; } + /* Avoid overwriting existing file by adding number suffix. */ for (int i = 1;; i++) { const std::string filepath = root_path + SEP + base_name_filesafe + "_" + std::to_string(i++) + BLENDER_ASSET_FILE_SUFFIX; @@ -220,41 +218,35 @@ static bool asset_write_in_library(Main *bmain, ID &id = const_cast(id_const); const short prev_flag = id.flag; - const int prev_tag = id.tag; const int prev_us = id.us; const std::string prev_name = id.name + 2; - /* TODO: Remove library overrides stuff now that they are not used for brush assets. */ - IDOverrideLibrary *prev_liboverride = id.override_library; - const int write_flags = 0; /* Could use #G_FILE_COMPRESS ? */ + /* TODO: Use G_FILE_AUTOPACK? But this will require making a copy of datablocks first to avoid + * duplicating data. */ + const int write_flags = G_FILE_COMPRESS; const eBLO_WritePathRemap remap_mode = BLO_WRITE_PATH_REMAP_RELATIVE; BKE_blendfile_write_partial_begin(bmain); id.flag |= LIB_FAKEUSER; - id.tag &= ~LIB_TAG_RUNTIME; id.us = 1; BLI_strncpy(id.name + 2, name.data(), std::min(sizeof(id.name) - 2, size_t(name.size() + 1))); - id.override_library = nullptr; BKE_blendfile_write_partial_tag_ID(&id, true); - /* TODO: check overwriting existing file. */ - /* TODO: ensure filepath contains only valid characters for file system. */ const bool sucess = BKE_blendfile_write_partial( bmain, filepath.c_str(), write_flags, remap_mode, &reports); if (sucess) { - final_full_file_path = std::string(filepath) + SEP + "Brush" + SEP + name; + const IDTypeInfo *idtype = BKE_idtype_get_info_from_id(&id); + final_full_file_path = std::string(filepath) + SEP + std::string(idtype->name) + SEP + name; } BKE_blendfile_write_partial_end(bmain); BKE_blendfile_write_partial_tag_ID(&id, false); id.flag = prev_flag; - id.tag = prev_tag; id.us = prev_us; BLI_strncpy(id.name + 2, prev_name.c_str(), sizeof(id.name) - 2); - id.override_library = prev_liboverride; return sucess; } @@ -332,8 +324,11 @@ static AssetEditBlend *asset_edit_blend_from_id(const ID &id) { BLI_assert(id.tag & LIB_TAG_ASSET_MAIN); + /* TODO: It would be good to make this more efficient, though it's unlikely to be a bottleneck + * for brush assets. It's not easy to add a hash map here because it needs to be kept up to date + * as the main database is edited, which can be done in many places. So this would require hooks + * quite deep in ID management. */ for (AssetEditBlend &asset_blend : asset_edit_blend_get_all()) { - /* TODO: Look into make this whole thing more efficient. */ ListBase *lb = which_libbase(asset_blend.main, GS(id.name)); LISTBASE_FOREACH (ID *, other_id, lb) { if (&id == other_id) { @@ -420,7 +415,7 @@ bool asset_edit_id_revert(Main &global_main, const ID &id, ReportList & /*report } /* Reload entire main, including texture dependencies. This relies on there - * being only a single brush asset per blend file. */ + * being only a single asset per blend file. */ asset_blend->reload(global_main); return true; -- 2.30.2 From 875330afa098c141c51fa291731e2d09afd39866 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 1 May 2024 14:05:54 -0400 Subject: [PATCH 202/244] Cleanup: Remove unused includes --- .../blender/blenkernel/intern/asset_weak_reference.cc | 11 ----------- source/blender/blenkernel/intern/brush.cc | 8 +------- source/blender/editors/asset/ED_asset_shelf.hh | 1 - source/blender/editors/asset/intern/asset_shelf.hh | 1 - 4 files changed, 1 insertion(+), 20 deletions(-) diff --git a/source/blender/blenkernel/intern/asset_weak_reference.cc b/source/blender/blenkernel/intern/asset_weak_reference.cc index cd88a6ac279..89447e719e7 100644 --- a/source/blender/blenkernel/intern/asset_weak_reference.cc +++ b/source/blender/blenkernel/intern/asset_weak_reference.cc @@ -7,26 +7,15 @@ */ #include -#include -#include "BLI_path_util.h" #include "BLI_string.h" -#include "BLI_vector.hh" - -#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_lib_remap.hh" -#include "BKE_main.hh" #include "BLO_read_write.hh" -#include "BLO_readfile.hh" #include "DNA_asset_types.h" diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index e14aabc652a..ac3f42137ef 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -5,24 +5,20 @@ /** \file * \ingroup bke */ +#include #include -#include - #include "MEM_guardedalloc.h" #include "DNA_brush_types.h" #include "DNA_defaults.h" #include "DNA_material_types.h" #include "DNA_scene_types.h" -#include "DNA_space_types.h" #include "BLI_listbase.h" #include "BLI_math_rotation.h" #include "BLI_rand.h" -#include "BLO_readfile.hh" - #include "BLT_translation.hh" #include "BKE_asset.hh" @@ -41,8 +37,6 @@ #include "BKE_preview_image.hh" #include "BKE_texture.h" -#include "AS_asset_library.hh" - #include "IMB_colormanagement.hh" #include "IMB_imbuf.hh" #include "IMB_imbuf_types.hh" diff --git a/source/blender/editors/asset/ED_asset_shelf.hh b/source/blender/editors/asset/ED_asset_shelf.hh index bf0efdd708a..6c6b3a7f39f 100644 --- a/source/blender/editors/asset/ED_asset_shelf.hh +++ b/source/blender/editors/asset/ED_asset_shelf.hh @@ -24,7 +24,6 @@ struct RegionPollParams; struct wmWindowManager; namespace blender { -class StringRef; class StringRefNull; } // namespace blender diff --git a/source/blender/editors/asset/intern/asset_shelf.hh b/source/blender/editors/asset/intern/asset_shelf.hh index a014117ffce..981b9aaa741 100644 --- a/source/blender/editors/asset/intern/asset_shelf.hh +++ b/source/blender/editors/asset/intern/asset_shelf.hh @@ -9,7 +9,6 @@ #pragma once #include "BLI_function_ref.hh" -#include "BLI_string_ref.hh" struct ARegion; struct ARegionType; -- 2.30.2 From f89b5c33f4fa933cc6646b3eba25771b3357201b Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 1 May 2024 14:06:09 -0400 Subject: [PATCH 203/244] Move asset shelf popover template to C++ namespace --- .../asset/intern/asset_shelf_asset_view.cc | 6 +++--- .../blender/editors/include/UI_interface_c.hh | 8 ++++++-- .../interface_template_asset_shelf_popover.cc | 18 ++++++++++-------- source/blender/makesrna/intern/rna_ui_api.cc | 2 +- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/source/blender/editors/asset/intern/asset_shelf_asset_view.cc b/source/blender/editors/asset/intern/asset_shelf_asset_view.cc index 4e6eef1b700..1c6a2178a67 100644 --- a/source/blender/editors/asset/intern/asset_shelf_asset_view.cc +++ b/source/blender/editors/asset/intern/asset_shelf_asset_view.cc @@ -236,15 +236,15 @@ void AssetViewItem::build_context_menu(bContext &C, uiLayout &column) const void AssetViewItem::on_activate(bContext & /*C*/) { - const AssetView &asset_view = dynamic_cast(get_view()); + const AssetView &asset_view = dynamic_cast(this->get_view()); if (asset_view.is_popup_) { - UI_popup_menu_close_from_but(reinterpret_cast(view_item_button())); + UI_popup_menu_close_from_but(reinterpret_cast(this->view_item_button())); } } std::optional AssetViewItem::should_be_active() const { - const AssetView &asset_view = dynamic_cast(get_view()); + const AssetView &asset_view = dynamic_cast(this->get_view()); if (!asset_view.active_asset_) { return false; } diff --git a/source/blender/editors/include/UI_interface_c.hh b/source/blender/editors/include/UI_interface_c.hh index c8a8e89c4c5..01246e914fc 100644 --- a/source/blender/editors/include/UI_interface_c.hh +++ b/source/blender/editors/include/UI_interface_c.hh @@ -2719,8 +2719,12 @@ void uiTemplateAssetView(uiLayout *layout, const char *drag_opname, PointerRNA *r_drag_op_properties); -void uiTemplateAssetShelfPopover( - uiLayout *layout, bContext *C, const char *asset_shelf_id, const char *name, const int icon); +namespace blender::ui { + +void template_asset_shelf_popover( + uiLayout *layout, const bContext *C, StringRefNull asset_shelf_id, StringRef name, int icon); + +} void uiTemplateLightLinkingCollection(uiLayout *layout, uiLayout *context_layout, diff --git a/source/blender/editors/interface/templates/interface_template_asset_shelf_popover.cc b/source/blender/editors/interface/templates/interface_template_asset_shelf_popover.cc index d2ad32f0006..830a91d56b7 100644 --- a/source/blender/editors/interface/templates/interface_template_asset_shelf_popover.cc +++ b/source/blender/editors/interface/templates/interface_template_asset_shelf_popover.cc @@ -17,7 +17,7 @@ #include "ED_asset_shelf.hh" -using namespace blender; +namespace blender::ui { static uiBlock *asset_shelf_block_fn(bContext *C, ARegion *region, void *arg_shelf_type) { @@ -25,17 +25,17 @@ static uiBlock *asset_shelf_block_fn(bContext *C, ARegion *region, void *arg_she return ed::asset::shelf::popup_block_create(C, region, shelf_type); } -void uiTemplateAssetShelfPopover(uiLayout *layout, - bContext *C, - const char *asset_shelf_id, - const char *name, - const BIFIconID icon) +void template_asset_shelf_popover(uiLayout *layout, + const bContext *C, + const StringRefNull asset_shelf_id, + const StringRef name, + const BIFIconID icon) { const ScrArea *area = CTX_wm_area(C); AssetShelfType *shelf_type = ed::asset::shelf::type_find_from_idname(*area->type, asset_shelf_id); if (!shelf_type) { - RNA_warning("Asset shelf type not found: %s", asset_shelf_id); + RNA_warning("Asset shelf type not found: %s", asset_shelf_id.c_str()); return; } @@ -45,7 +45,7 @@ void uiTemplateAssetShelfPopover(uiLayout *layout, if (use_big_size) { return UI_UNIT_X * 6; } - return UI_UNIT_X * (name ? 7 : 1.6f); + return UI_UNIT_X * (name.is_empty() ? 7 : 1.6f); }(); const short height = UI_UNIT_Y * (use_big_size ? 6 : 1); @@ -59,3 +59,5 @@ void uiTemplateAssetShelfPopover(uiLayout *layout, UI_but_flag_enable(but, UI_BUT_DISABLED); } } + +} // namespace blender::ui diff --git a/source/blender/makesrna/intern/rna_ui_api.cc b/source/blender/makesrna/intern/rna_ui_api.cc index 5a67192962e..09ee4d6d7a2 100644 --- a/source/blender/makesrna/intern/rna_ui_api.cc +++ b/source/blender/makesrna/intern/rna_ui_api.cc @@ -974,7 +974,7 @@ void rna_uiTemplateAssetShelfPopover(uiLayout *layout, icon = icon_value; } - uiTemplateAssetShelfPopover(layout, C, asset_shelf_id, name, icon); + blender::ui::template_asset_shelf_popover(layout, C, asset_shelf_id, name, icon); } #else -- 2.30.2 From 66ec79e536ffbea9161fb48e740c80f0798e8ff1 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 1 May 2024 14:15:51 -0400 Subject: [PATCH 204/244] Fix: Correct default tools in affected modes --- source/blender/windowmanager/intern/wm_toolsystem.cc | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/source/blender/windowmanager/intern/wm_toolsystem.cc b/source/blender/windowmanager/intern/wm_toolsystem.cc index c2442a887cb..72da45a101e 100644 --- a/source/blender/windowmanager/intern/wm_toolsystem.cc +++ b/source/blender/windowmanager/intern/wm_toolsystem.cc @@ -657,21 +657,16 @@ static const char *toolsystem_default_tool(const bToolKey *tkey) switch (tkey->mode) { /* Use the names of the enums for each brush tool. */ case CTX_MODE_SCULPT: - return "builtin.brush"; case CTX_MODE_PAINT_VERTEX: case CTX_MODE_PAINT_WEIGHT: case CTX_MODE_PAINT_TEXTURE: case CTX_MODE_PAINT_GPENCIL_LEGACY: case CTX_MODE_PAINT_GREASE_PENCIL: - return "builtin_brush.Draw"; case CTX_MODE_SCULPT_GPENCIL_LEGACY: case CTX_MODE_SCULPT_GREASE_PENCIL: - return "builtin_brush.Push"; case CTX_MODE_WEIGHT_GPENCIL_LEGACY: case CTX_MODE_WEIGHT_GREASE_PENCIL: - return "builtin_brush.Weight"; case CTX_MODE_VERTEX_GPENCIL_LEGACY: - return "builtin_brush.Draw"; case CTX_MODE_SCULPT_CURVES: return "builtin.brush"; /* End temporary hack. */ @@ -685,7 +680,7 @@ static const char *toolsystem_default_tool(const bToolKey *tkey) case SPACE_IMAGE: switch (tkey->mode) { case SI_MODE_PAINT: - return "builtin_brush.Draw"; + return "builtin.brush"; } break; case SPACE_NODE: { -- 2.30.2 From 2194d19c2e1c6e2f2f8129e80a5fd4ae4800b474 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 2 May 2024 12:05:28 -0400 Subject: [PATCH 205/244] Cleanup: Remove outdated comments --- source/blender/windowmanager/intern/wm_toolsystem.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/source/blender/windowmanager/intern/wm_toolsystem.cc b/source/blender/windowmanager/intern/wm_toolsystem.cc index 72da45a101e..e1fa044fb1f 100644 --- a/source/blender/windowmanager/intern/wm_toolsystem.cc +++ b/source/blender/windowmanager/intern/wm_toolsystem.cc @@ -655,7 +655,6 @@ static const char *toolsystem_default_tool(const bToolKey *tkey) switch (tkey->space_type) { case SPACE_VIEW3D: switch (tkey->mode) { - /* Use the names of the enums for each brush tool. */ case CTX_MODE_SCULPT: case CTX_MODE_PAINT_VERTEX: case CTX_MODE_PAINT_WEIGHT: @@ -669,8 +668,6 @@ static const char *toolsystem_default_tool(const bToolKey *tkey) case CTX_MODE_VERTEX_GPENCIL_LEGACY: case CTX_MODE_SCULPT_CURVES: return "builtin.brush"; - /* End temporary hack. */ - case CTX_MODE_PARTICLE: return "builtin_brush.Comb"; case CTX_MODE_EDIT_TEXT: -- 2.30.2 From 91a863eb778ac03913964019376b5f28f0d5e516 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 2 May 2024 12:34:26 -0400 Subject: [PATCH 206/244] Cleanup: Remove unnecessary change --- source/blender/asset_system/AS_asset_library.hh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/source/blender/asset_system/AS_asset_library.hh b/source/blender/asset_system/AS_asset_library.hh index 4e0028ae135..7a53afc1e95 100644 --- a/source/blender/asset_system/AS_asset_library.hh +++ b/source/blender/asset_system/AS_asset_library.hh @@ -114,9 +114,6 @@ class AssetLibrary { */ static void foreach_loaded(FunctionRef fn, bool include_all_library); - static std::string resolve_asset_weak_reference_to_full_path( - const AssetWeakReference &asset_reference); - void load_catalogs(); AssetCatalogService &catalog_service() const; @@ -175,6 +172,8 @@ class AssetLibrary { */ AssetIdentifier asset_identifier_from_library(StringRef relative_asset_path); + std::string resolve_asset_weak_reference_to_full_path(const AssetWeakReference &asset_reference); + eAssetLibraryType library_type() const; StringRefNull name() const; StringRefNull root_path() const; -- 2.30.2 From f110f472822eb052bff804ff946e6f105b5b2d05 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 2 May 2024 12:39:10 -0400 Subject: [PATCH 207/244] Cleanup: Remove unnecessary include and forward declaration --- source/blender/blenkernel/BKE_asset.hh | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/blender/blenkernel/BKE_asset.hh b/source/blender/blenkernel/BKE_asset.hh index 4f6a56e0f26..0b6eda3345c 100644 --- a/source/blender/blenkernel/BKE_asset.hh +++ b/source/blender/blenkernel/BKE_asset.hh @@ -11,7 +11,6 @@ #include "BLI_compiler_attrs.h" #include "BLI_utildefines.h" -#include "DNA_ID_enums.h" #include "DNA_asset_types.h" struct AssetLibraryReference; @@ -21,7 +20,6 @@ struct BlendDataReader; struct BlendWriter; struct ID; struct IDProperty; -struct Main; struct PreviewImage; using PreSaveFn = void (*)(void *asset_ptr, AssetMetaData *asset_data); -- 2.30.2 From 4b0c45600008d72301425aa5cc6839aa4f70b992 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 2 May 2024 20:29:44 +0200 Subject: [PATCH 208/244] Brush Asset: Replace hacky partial write by main copy --- source/blender/blenkernel/BKE_lib_id.hh | 2 + .../blender/blenkernel/intern/asset_edit.cc | 126 +++++++++++------- source/blender/blenkernel/intern/lib_id.cc | 6 + 3 files changed, 89 insertions(+), 45 deletions(-) diff --git a/source/blender/blenkernel/BKE_lib_id.hh b/source/blender/blenkernel/BKE_lib_id.hh index 0b76c3bd5fb..6f1c0bbd712 100644 --- a/source/blender/blenkernel/BKE_lib_id.hh +++ b/source/blender/blenkernel/BKE_lib_id.hh @@ -205,6 +205,8 @@ enum { * duplicate scene/collections, or objects. */ LIB_ID_COPY_RIGID_BODY_NO_COLLECTION_HANDLING = 1 << 28, + /* Copy asset metadata. */ + LIB_ID_COPY_ASSET_METADATA = 1 << 29, /* *** Helper 'defines' gathering most common flag sets. *** */ /** Shape-keys are not real ID's, more like local data to geometry IDs. */ diff --git a/source/blender/blenkernel/intern/asset_edit.cc b/source/blender/blenkernel/intern/asset_edit.cc index c4d847b028a..f4e1d98aeda 100644 --- a/source/blender/blenkernel/intern/asset_edit.cc +++ b/source/blender/blenkernel/intern/asset_edit.cc @@ -27,6 +27,7 @@ #include "BKE_idtype.hh" #include "BKE_lib_id.hh" #include "BKE_lib_remap.hh" +#include "BKE_library.hh" #include "BKE_main.hh" #include "BKE_preferences.h" #include "BKE_report.hh" @@ -188,6 +189,71 @@ static std::string asset_blendfile_path_for_save(const bUserAssetLibrary &user_l return ""; } +static void asset_main_create_expander(void * /*handle*/, Main * /*bmain*/, void *vid) +{ + ID *id = static_cast(vid); + + if (id && (id->tag & LIB_TAG_DOIT) == 0) { + id->tag |= LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT; + } +} + +static Main *asset_main_create_from_ID(Main *bmain_src, ID &id_asset, ID **id_asset_new) +{ + /* Tag asset ID and its dependencies. */ + ID *id_src; + FOREACH_MAIN_ID_BEGIN (bmain_src, id_src) { + id_src->tag &= ~(LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT); + } + FOREACH_MAIN_ID_END; + + id_asset.tag |= LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT; + + BLO_main_expander(asset_main_create_expander); + BLO_expand_main(nullptr, bmain_src); + + /* Create main and copy all tagged datablocks. */ + Main *bmain_dst = BKE_main_new(); + STRNCPY(bmain_dst->filepath, bmain_src->filepath); + + blender::bke::id::IDRemapper id_remapper; + + FOREACH_MAIN_ID_BEGIN (bmain_src, id_src) { + if (id_src->tag & LIB_TAG_DOIT) { + /* Note that this will not copy Library datablocks, and all copied + * datablocks will become local as a result. */ + ID *id_dst = BKE_id_copy_ex(bmain_dst, + id_src, + nullptr, + LIB_ID_CREATE_NO_USER_REFCOUNT | LIB_ID_CREATE_NO_DEG_TAG | + ((id_src == &id_asset) ? LIB_ID_COPY_ASSET_METADATA : 0)); + id_remapper.add(id_src, id_dst); + if (id_src == &id_asset) { + *id_asset_new = id_dst; + } + } + else { + id_remapper.add(id_src, nullptr); + } + + id_src->tag &= ~(LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT); + } + FOREACH_MAIN_ID_END; + + /* Remap datablock pointers. */ + BKE_libblock_remap_multiple_raw(bmain_dst, id_remapper, ID_REMAP_SKIP_USER_CLEAR); + + /* Compute reference counts. */ + ID *id_dst; + FOREACH_MAIN_ID_BEGIN (bmain_dst, id_dst) { + id_dst->tag &= ~LIB_TAG_NO_USER_REFCOUNT; + } + FOREACH_MAIN_ID_END; + BKE_main_id_refcount_recompute(bmain_dst, false); + + return bmain_dst; +} + static bool asset_write_in_library(Main *bmain, const ID &id_const, const StringRef name, @@ -195,60 +261,30 @@ static bool asset_write_in_library(Main *bmain, std::string &final_full_file_path, ReportList &reports) { - /* TODO: Comment seems to be resolved by separate #Main storage? - * XXX - * FIXME - * - * This code is _pure evil_. It does in-place manipulation on IDs in global Main database, - * temporarilly remove them and add them back... - * - * Use it as-is for now (in a similar way as python API or copy-to-buffer works). Nut the whole - * 'BKE_blendfile_write_partial' code needs to be completely refactored. - * - * Ideas: - * - Have `BKE_blendfile_write_partial_begin` return a new temp Main. - * - Replace `BKE_blendfile_write_partial_tag_ID` by API to add IDs to this temp Main. - * + This should _duplicate_ the ID, not remove the original one from the source Main! - * - Have API to automatically also duplicate dependencies into temp Main. - * + Have options to e.g. make all duplicated IDs 'local' (i.e. remove their library data). - * - `BKE_blendfile_write_partial` then simply write the given temp main. - * - `BKE_blendfile_write_partial_end` frees the temp Main. - */ - ID &id = const_cast(id_const); - const short prev_flag = id.flag; - const int prev_us = id.us; - const std::string prev_name = id.name + 2; - /* TODO: Use G_FILE_AUTOPACK? But this will require making a copy of datablocks first to avoid - * duplicating data. */ + ID *new_id = nullptr; + Main *new_main = asset_main_create_from_ID(bmain, id, &new_id); + + std::string new_name = name; + BKE_libblock_rename(new_main, new_id, new_name.c_str()); + id_fake_user_set(new_id); + + BlendFileWriteParams blend_file_write_params{}; + blend_file_write_params.remap_mode = BLO_WRITE_PATH_REMAP_RELATIVE; + const int write_flags = G_FILE_COMPRESS; - const eBLO_WritePathRemap remap_mode = BLO_WRITE_PATH_REMAP_RELATIVE; + const bool success = BLO_write_file( + new_main, filepath.c_str(), write_flags, &blend_file_write_params, &reports); - BKE_blendfile_write_partial_begin(bmain); - - id.flag |= LIB_FAKEUSER; - id.us = 1; - BLI_strncpy(id.name + 2, name.data(), std::min(sizeof(id.name) - 2, size_t(name.size() + 1))); - - BKE_blendfile_write_partial_tag_ID(&id, true); - - const bool sucess = BKE_blendfile_write_partial( - bmain, filepath.c_str(), write_flags, remap_mode, &reports); - - if (sucess) { + if (success) { const IDTypeInfo *idtype = BKE_idtype_get_info_from_id(&id); final_full_file_path = std::string(filepath) + SEP + std::string(idtype->name) + SEP + name; } - BKE_blendfile_write_partial_end(bmain); + BKE_main_free(new_main); - BKE_blendfile_write_partial_tag_ID(&id, false); - id.flag = prev_flag; - id.us = prev_us; - BLI_strncpy(id.name + 2, prev_name.c_str(), sizeof(id.name) - 2); - - return sucess; + return success; } void AssetEditBlend::reload(Main &global_main) diff --git a/source/blender/blenkernel/intern/lib_id.cc b/source/blender/blenkernel/intern/lib_id.cc index 543e7d97ea6..a478098c469 100644 --- a/source/blender/blenkernel/intern/lib_id.cc +++ b/source/blender/blenkernel/intern/lib_id.cc @@ -1553,6 +1553,12 @@ void BKE_libblock_copy_in_lib(Main *bmain, } } + if (flag & LIB_ID_COPY_ASSET_METADATA) { + if (id->asset_data) { + new_id->asset_data = BKE_asset_metadata_copy(id->asset_data); + } + } + if ((flag & LIB_ID_CREATE_NO_DEG_TAG) == 0 && (flag & LIB_ID_CREATE_NO_MAIN) == 0) { DEG_id_type_tag(bmain, GS(new_id->name)); } -- 2.30.2 From d0474e632a73d0f90fa2a562c3e57cff89325226 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 2 May 2024 20:52:25 +0200 Subject: [PATCH 209/244] Brush Assets: Pack images and other data when saving brush asset --- source/blender/blenkernel/intern/asset_edit.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/blender/blenkernel/intern/asset_edit.cc b/source/blender/blenkernel/intern/asset_edit.cc index f4e1d98aeda..1fbffd702a6 100644 --- a/source/blender/blenkernel/intern/asset_edit.cc +++ b/source/blender/blenkernel/intern/asset_edit.cc @@ -29,6 +29,7 @@ #include "BKE_lib_remap.hh" #include "BKE_library.hh" #include "BKE_main.hh" +#include "BKE_packedFile.h" #include "BKE_preferences.h" #include "BKE_report.hh" @@ -273,6 +274,8 @@ static bool asset_write_in_library(Main *bmain, BlendFileWriteParams blend_file_write_params{}; blend_file_write_params.remap_mode = BLO_WRITE_PATH_REMAP_RELATIVE; + BKE_packedfile_pack_all(new_main, nullptr, false); + const int write_flags = G_FILE_COMPRESS; const bool success = BLO_write_file( new_main, filepath.c_str(), write_flags, &blend_file_write_params, &reports); -- 2.30.2 From ba61fe8a4f0cd232fd624090f4e7fbda35d065ee Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 2 May 2024 18:58:22 -0400 Subject: [PATCH 210/244] Refactor: Move BrushAssetShelf definition to properties_paint_common.py Allow using the definition outside of space_view3d.py in a way that doesn't cause circular dependencies. --- .../startup/bl_ui/properties_paint_common.py | 47 +++++++++++++++++++ scripts/startup/bl_ui/space_image.py | 4 +- scripts/startup/bl_ui/space_view3d.py | 47 +------------------ 3 files changed, 49 insertions(+), 49 deletions(-) diff --git a/scripts/startup/bl_ui/properties_paint_common.py b/scripts/startup/bl_ui/properties_paint_common.py index 8e2e9f6b5b6..5d9eb22bb25 100644 --- a/scripts/startup/bl_ui/properties_paint_common.py +++ b/scripts/startup/bl_ui/properties_paint_common.py @@ -2,9 +2,56 @@ # # SPDX-License-Identifier: GPL-2.0-or-later +import bpy from bpy.types import Menu +class BrushAssetShelf: + bl_options = {'DEFAULT_VISIBLE', 'NO_ASSET_DRAG', 'STORE_ENABLED_CATALOGS_IN_PREFERENCES'} + bl_activate_operator = "BRUSH_OT_asset_select" + bl_default_preview_size = 48 + + @classmethod + def poll(cls, context): + return context.object and context.object.mode == cls.mode + + @classmethod + def asset_poll(cls, asset): + if asset.id_type != 'BRUSH': + return False + + return asset.metadata.get(cls.mode_prop, False) + + @classmethod + def get_active_asset(cls): + paint_settings = UnifiedPaintPanel.paint_settings(bpy.context) + return paint_settings.brush_asset_reference if paint_settings else None + + @classmethod + def draw_context_menu(self, context, asset, layout): + # Currently this menu adds operators that deal with the affected brush and don't take the + # asset into account. Luckily that is okay for now, since right clicking in the grid view + # also activates the item. + layout.menu_contents("VIEW3D_MT_brush_context_menu") + + # Not nice, but needed unfortunately. + @staticmethod + def get_shelf_name_from_mode(obmode): + mode_map = { + 'SCULPT': "VIEW3D_AST_brush_sculpt", + 'SCULPT_CURVES': "VIEW3D_AST_brush_sculpt_curves", + 'VERTEX_PAINT': "VIEW3D_AST_brush_vertex_paint", + 'WEIGHT_PAINT': "VIEW3D_AST_brush_weight_paint", + 'TEXTURE_PAINT': "VIEW3D_AST_brush_texture_paint", + 'PAINT_GPENCIL': "VIEW3D_AST_brush_gpencil_paint", + 'PAINT_GREASE_PENCIL': "VIEW3D_AST_brush_grease_pencil_paint", + 'SCULPT_GPENCIL': "VIEW3D_AST_brush_gpencil_sculpt", + 'VERTEX_GPENCIL': "VIEW3D_AST_brush_gpencil_vertex", + 'WEIGHT_GPENCIL': "VIEW3D_AST_brush_gpencil_weight", + } + return mode_map[obmode] + + class UnifiedPaintPanel: # subclass must set # bl_space_type = 'IMAGE_EDITOR' diff --git a/scripts/startup/bl_ui/space_image.py b/scripts/startup/bl_ui/space_image.py index 2b8fe8e6e74..7338c88657d 100644 --- a/scripts/startup/bl_ui/space_image.py +++ b/scripts/startup/bl_ui/space_image.py @@ -24,6 +24,7 @@ from bl_ui.properties_paint_common import ( SmoothStrokePanel, FalloffPanel, DisplayPanel, + BrushAssetShelf, ) from bl_ui.properties_grease_pencil_common import ( AnnotationDataPanel, @@ -31,9 +32,6 @@ from bl_ui.properties_grease_pencil_common import ( from bl_ui.space_toolsystem_common import ( ToolActivePanelHelper, ) -from bl_ui.space_view3d import ( - BrushAssetShelf, -) from bpy.app.translations import ( contexts as i18n_contexts, diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index 7e481ac5e8b..6fc9be71297 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -13,6 +13,7 @@ from bl_ui.properties_paint_common import ( brush_basic_texpaint_settings, brush_basic_gpencil_weight_settings, brush_basic_grease_pencil_weight_settings, + BrushAssetShelf, ) from bl_ui.properties_grease_pencil_common import ( AnnotationDataPanel, @@ -9074,52 +9075,6 @@ class VIEW3D_PT_viewport_debug(Panel): layout.prop(overlay, "use_debug_freeze_view_culling") -class BrushAssetShelf: - bl_options = {'DEFAULT_VISIBLE', 'NO_ASSET_DRAG', 'STORE_ENABLED_CATALOGS_IN_PREFERENCES'} - bl_activate_operator = "BRUSH_OT_asset_select" - bl_default_preview_size = 48 - - @classmethod - def poll(cls, context): - return context.object and context.object.mode == cls.mode - - @classmethod - def asset_poll(cls, asset): - if asset.id_type != 'BRUSH': - return False - - return asset.metadata.get(cls.mode_prop, False) - - @classmethod - def get_active_asset(cls): - paint_settings = UnifiedPaintPanel.paint_settings(bpy.context) - return paint_settings.brush_asset_reference if paint_settings else None - - @classmethod - def draw_context_menu(self, context, asset, layout): - # Currently this menu adds operators that deal with the affected brush and don't take the - # asset into account. Luckily that is okay for now, since right clicking in the grid view - # also activates the item. - layout.menu_contents("VIEW3D_MT_brush_context_menu") - - # Not nice, but needed unfortunately. - @staticmethod - def get_shelf_name_from_mode(obmode): - mode_map = { - 'SCULPT': "VIEW3D_AST_brush_sculpt", - 'SCULPT_CURVES': "VIEW3D_AST_brush_sculpt_curves", - 'VERTEX_PAINT': "VIEW3D_AST_brush_vertex_paint", - 'WEIGHT_PAINT': "VIEW3D_AST_brush_weight_paint", - 'TEXTURE_PAINT': "VIEW3D_AST_brush_texture_paint", - 'PAINT_GPENCIL': "VIEW3D_AST_brush_gpencil_paint", - 'PAINT_GREASE_PENCIL': "VIEW3D_AST_brush_grease_pencil_paint", - 'SCULPT_GPENCIL': "VIEW3D_AST_brush_gpencil_sculpt", - 'VERTEX_GPENCIL': "VIEW3D_AST_brush_gpencil_vertex", - 'WEIGHT_GPENCIL': "VIEW3D_AST_brush_gpencil_weight", - } - return mode_map[obmode] - - class View3DAssetShelf(BrushAssetShelf): bl_space_type = "VIEW_3D" -- 2.30.2 From 69b2eed49b46b4200b4899ba66076cb71c3f0ede Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 2 May 2024 18:58:57 -0400 Subject: [PATCH 211/244] Cleanup: Remove unhelpful comment --- scripts/startup/bl_ui/properties_paint_common.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/startup/bl_ui/properties_paint_common.py b/scripts/startup/bl_ui/properties_paint_common.py index 5d9eb22bb25..b0357060b4f 100644 --- a/scripts/startup/bl_ui/properties_paint_common.py +++ b/scripts/startup/bl_ui/properties_paint_common.py @@ -34,7 +34,6 @@ class BrushAssetShelf: # also activates the item. layout.menu_contents("VIEW3D_MT_brush_context_menu") - # Not nice, but needed unfortunately. @staticmethod def get_shelf_name_from_mode(obmode): mode_map = { -- 2.30.2 From 5695298832de3ccafd4ba72a8867aa34d80372df Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 2 May 2024 21:40:39 -0400 Subject: [PATCH 212/244] Brush Assets: Remove unnecessary brush tool icon handling and GP code One grease pencil operator added a local brush if one didn't exist already. That shouldn't be necessary anymore because the brushes will all be assets, so there will always be brushes available. Similarly, the "reset" operators aren't useful anymore because we reset to the state stored in assets rather than some other default state, and that's handled by the asset system. The grease pencil icon code managed icons for each brush tool. That's now unnecessary for the same reason-- the icons/previes are managed by the asset system. A follow up PR will make the brush UI in the 3D view sidebar follow this design better. --- source/blender/blenkernel/intern/brush.cc | 33 --- .../blenloader/intern/versioning_280.cc | 3 - .../blenloader/intern/versioning_defaults.cc | 3 - .../editors/gpencil_legacy/gpencil_data.cc | 277 ------------------ .../editors/gpencil_legacy/gpencil_intern.hh | 3 - .../editors/gpencil_legacy/gpencil_ops.cc | 3 - .../editors/gpencil_legacy/gpencil_paint.cc | 1 - .../editors/interface/interface_icons.cc | 260 ---------------- .../blender/editors/sculpt_paint/paint_ops.cc | 181 ------------ source/blender/makesdna/DNA_brush_enums.h | 35 --- source/blender/makesdna/DNA_brush_types.h | 4 +- source/blender/makesrna/intern/rna_brush.cc | 100 +------ 12 files changed, 2 insertions(+), 901 deletions(-) diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index ac3f42137ef..6ec8696212c 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -550,7 +550,6 @@ void BKE_brush_init_gpencil_settings(Brush *brush) brush->gpencil_settings->draw_strength = 1.0f; brush->gpencil_settings->draw_jitter = 0.0f; brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE; - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PEN; /* curves */ brush->gpencil_settings->curve_sensitivity = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); @@ -755,7 +754,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) copy_v2_fl(brush->gpencil_settings->aspect_ratio, 1.0f); brush->gpencil_tool = GPAINT_TOOL_DRAW; - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_AIRBRUSH; zero_v3(brush->secondary_rgb); @@ -793,7 +791,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) BKE_curvemapping_init(custom_curve); brush_gpencil_curvemap_reset(custom_curve->cm, 3, GPCURVE_PRESET_INK); - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_INK; brush->gpencil_tool = GPAINT_TOOL_DRAW; zero_v3(brush->secondary_rgb); @@ -830,7 +827,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) BKE_curvemapping_init(custom_curve); brush_gpencil_curvemap_reset(custom_curve->cm, 3, GPCURVE_PRESET_INKNOISE); - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_INKNOISE; brush->gpencil_tool = GPAINT_TOOL_DRAW; zero_v3(brush->secondary_rgb); @@ -867,7 +863,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) BKE_curvemapping_init(custom_curve); brush_gpencil_curvemap_reset(custom_curve->cm, 4, GPCURVE_PRESET_MARKER); - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_MARKER; brush->gpencil_tool = GPAINT_TOOL_DRAW; zero_v3(brush->secondary_rgb); @@ -908,7 +903,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) BKE_curvemapping_init(custom_curve); brush_gpencil_curvemap_reset(custom_curve->cm, 4, GPCURVE_PRESET_CHISEL_STRENGTH); - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_CHISEL; brush->gpencil_tool = GPAINT_TOOL_DRAW; zero_v3(brush->secondary_rgb); @@ -939,7 +933,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) brush->gpencil_settings->draw_jitter = 0.0f; brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE; - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PEN; brush->gpencil_tool = GPAINT_TOOL_DRAW; zero_v3(brush->secondary_rgb); @@ -970,7 +963,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) brush->gpencil_settings->draw_jitter = 0.0f; brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE; - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PENCIL; brush->gpencil_tool = GPAINT_TOOL_DRAW; zero_v3(brush->secondary_rgb); @@ -1003,7 +995,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) brush->gpencil_settings->draw_jitter = 0.0f; brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE; - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PENCIL; brush->gpencil_tool = GPAINT_TOOL_DRAW; zero_v3(brush->secondary_rgb); @@ -1026,7 +1017,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) brush->gpencil_settings->flag |= GP_BRUSH_FILL_SHOW_EXTENDLINES; - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_FILL; brush->gpencil_tool = GPAINT_TOOL_FILL; brush->gpencil_settings->vertex_mode = GPPAINT_MODE_FILL; @@ -1039,7 +1029,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) brush->gpencil_settings->flag |= GP_BRUSH_DEFAULT_ERASER; brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; brush->gpencil_settings->flag |= GP_BRUSH_USE_STRENGTH_PRESSURE; - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_SOFT; brush->gpencil_tool = GPAINT_TOOL_ERASE; brush->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_SOFT; brush->gpencil_settings->era_strength_f = 100.0f; @@ -1054,7 +1043,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) brush->gpencil_settings->era_strength_f = 100.0f; brush->gpencil_settings->era_thickness_f = 50.0f; - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_HARD; brush->gpencil_tool = GPAINT_TOOL_ERASE; break; @@ -1063,7 +1051,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) brush->size = 30.0f; brush->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_HARD; - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_HARD; brush->gpencil_tool = GPAINT_TOOL_ERASE; break; @@ -1072,13 +1059,11 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) brush->size = 30.0f; brush->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_STROKE; - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_STROKE; brush->gpencil_tool = GPAINT_TOOL_ERASE; break; } case GP_BRUSH_PRESET_TINT: { - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_TINT; brush->gpencil_tool = GPAINT_TOOL_TINT; brush->size = 25.0f; @@ -1091,7 +1076,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) break; } case GP_BRUSH_PRESET_VERTEX_DRAW: { - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_VERTEX_DRAW; brush->gpencil_vertex_tool = GPVERTEX_TOOL_DRAW; brush->size = 25.0f; @@ -1104,7 +1088,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) break; } case GP_BRUSH_PRESET_VERTEX_BLUR: { - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_VERTEX_BLUR; brush->gpencil_vertex_tool = GPVERTEX_TOOL_BLUR; brush->size = 25.0f; @@ -1117,7 +1100,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) break; } case GP_BRUSH_PRESET_VERTEX_AVERAGE: { - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_VERTEX_AVERAGE; brush->gpencil_vertex_tool = GPVERTEX_TOOL_AVERAGE; brush->size = 25.0f; @@ -1130,7 +1112,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) break; } case GP_BRUSH_PRESET_VERTEX_SMEAR: { - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_VERTEX_SMEAR; brush->gpencil_vertex_tool = GPVERTEX_TOOL_SMEAR; brush->size = 25.0f; @@ -1143,7 +1124,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) break; } case GP_BRUSH_PRESET_VERTEX_REPLACE: { - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_VERTEX_REPLACE; brush->gpencil_vertex_tool = GPVERTEX_TOOL_REPLACE; brush->size = 25.0f; @@ -1156,7 +1136,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) break; } case GP_BRUSH_PRESET_SMOOTH_STROKE: { - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_GPBRUSH_SMOOTH; brush->gpencil_sculpt_tool = GPSCULPT_TOOL_SMOOTH; brush->size = 25.0f; @@ -1169,7 +1148,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) break; } case GP_BRUSH_PRESET_STRENGTH_STROKE: { - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_GPBRUSH_STRENGTH; brush->gpencil_sculpt_tool = GPSCULPT_TOOL_STRENGTH; brush->size = 25.0f; @@ -1182,7 +1160,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) break; } case GP_BRUSH_PRESET_THICKNESS_STROKE: { - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_GPBRUSH_THICKNESS; brush->gpencil_sculpt_tool = GPSCULPT_TOOL_THICKNESS; brush->size = 25.0f; @@ -1195,7 +1172,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) break; } case GP_BRUSH_PRESET_GRAB_STROKE: { - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_GPBRUSH_GRAB; brush->gpencil_sculpt_tool = GPSCULPT_TOOL_GRAB; brush->gpencil_settings->flag &= ~GP_BRUSH_USE_PRESSURE; @@ -1208,7 +1184,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) break; } case GP_BRUSH_PRESET_PUSH_STROKE: { - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_GPBRUSH_PUSH; brush->gpencil_sculpt_tool = GPSCULPT_TOOL_PUSH; brush->size = 25.0f; @@ -1221,7 +1196,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) break; } case GP_BRUSH_PRESET_TWIST_STROKE: { - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_GPBRUSH_TWIST; brush->gpencil_sculpt_tool = GPSCULPT_TOOL_TWIST; brush->size = 50.0f; @@ -1234,7 +1208,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) break; } case GP_BRUSH_PRESET_PINCH_STROKE: { - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_GPBRUSH_PINCH; brush->gpencil_sculpt_tool = GPSCULPT_TOOL_PINCH; brush->size = 50.0f; @@ -1247,7 +1220,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) break; } case GP_BRUSH_PRESET_RANDOMIZE_STROKE: { - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_GPBRUSH_RANDOMIZE; brush->gpencil_sculpt_tool = GPSCULPT_TOOL_RANDOMIZE; brush->size = 25.0f; @@ -1260,7 +1232,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) break; } case GP_BRUSH_PRESET_CLONE_STROKE: { - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_GPBRUSH_CLONE; brush->gpencil_sculpt_tool = GPSCULPT_TOOL_CLONE; brush->gpencil_settings->flag &= ~GP_BRUSH_USE_PRESSURE; @@ -1273,7 +1244,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) break; } case GP_BRUSH_PRESET_WEIGHT_DRAW: { - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_GPBRUSH_WEIGHT; brush->gpencil_weight_tool = GPWEIGHT_TOOL_DRAW; brush->size = 25.0f; @@ -1287,7 +1257,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) break; } case GP_BRUSH_PRESET_WEIGHT_BLUR: { - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_VERTEX_BLUR; brush->gpencil_weight_tool = GPWEIGHT_TOOL_BLUR; brush->size = 50.0f; @@ -1301,7 +1270,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) break; } case GP_BRUSH_PRESET_WEIGHT_AVERAGE: { - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_VERTEX_BLUR; brush->gpencil_weight_tool = GPWEIGHT_TOOL_AVERAGE; brush->size = 50.0f; @@ -1315,7 +1283,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) break; } case GP_BRUSH_PRESET_WEIGHT_SMEAR: { - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_VERTEX_BLUR; brush->gpencil_weight_tool = GPWEIGHT_TOOL_SMEAR; brush->size = 50.0f; diff --git a/source/blender/blenloader/intern/versioning_280.cc b/source/blender/blenloader/intern/versioning_280.cc index 0f6a87aacb8..7242aff0434 100644 --- a/source/blender/blenloader/intern/versioning_280.cc +++ b/source/blender/blenloader/intern/versioning_280.cc @@ -2836,9 +2836,6 @@ void do_versions_after_linking_280(FileData *fd, Main *bmain) Material *ma; /* Pen Soft brush. */ brush = (Brush *)do_versions_rename_id(bmain, ID_BR, "Draw Soft", "Pencil Soft"); - if (brush) { - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PEN; - } do_versions_rename_id(bmain, ID_BR, "Draw Pencil", "Pencil"); do_versions_rename_id(bmain, ID_BR, "Draw Pen", "Pen"); do_versions_rename_id(bmain, ID_BR, "Draw Ink", "Ink Pen"); diff --git a/source/blender/blenloader/intern/versioning_defaults.cc b/source/blender/blenloader/intern/versioning_defaults.cc index 639ed5b1e27..cd0e3de9dd9 100644 --- a/source/blender/blenloader/intern/versioning_defaults.cc +++ b/source/blender/blenloader/intern/versioning_defaults.cc @@ -466,9 +466,6 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) /* Pen Soft brush. */ brush = reinterpret_cast( do_versions_rename_id(bmain, ID_BR, "Draw Soft", "Pencil Soft")); - if (brush) { - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PEN; - } /* Ink Pen brush. */ do_versions_rename_id(bmain, ID_BR, "Draw Ink", "Ink Pen"); diff --git a/source/blender/editors/gpencil_legacy/gpencil_data.cc b/source/blender/editors/gpencil_legacy/gpencil_data.cc index 2514a98bdba..b9102befc31 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_data.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_data.cc @@ -1952,283 +1952,6 @@ void GPENCIL_OT_material_lock_unused(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ************************************************ */ -/* Drawing Brushes Operators */ - -/* ******************* Brush resets ************************** */ -static int gpencil_brush_reset_exec(bContext *C, wmOperator * /*op*/) -{ - Main *bmain = CTX_data_main(C); - ToolSettings *ts = CTX_data_tool_settings(C); - const enum eContextObjectMode mode = CTX_data_mode_enum(C); - - switch (mode) { - case CTX_MODE_PAINT_GPENCIL_LEGACY: { - Paint *paint = &ts->gp_paint->paint; - Brush *brush = BKE_paint_brush(paint); - if (brush && brush->gpencil_settings) { - BKE_gpencil_brush_preset_set(bmain, brush, brush->gpencil_settings->preset_type); - } - break; - } - case CTX_MODE_SCULPT_GPENCIL_LEGACY: { - Paint *paint = &ts->gp_sculptpaint->paint; - Brush *brush = BKE_paint_brush(paint); - if (brush && brush->gpencil_settings) { - BKE_gpencil_brush_preset_set(bmain, brush, brush->gpencil_settings->preset_type); - } - break; - } - case CTX_MODE_WEIGHT_GPENCIL_LEGACY: { - Paint *paint = &ts->gp_weightpaint->paint; - Brush *brush = BKE_paint_brush(paint); - if (brush && brush->gpencil_settings) { - BKE_gpencil_brush_preset_set(bmain, brush, brush->gpencil_settings->preset_type); - } - break; - } - case CTX_MODE_VERTEX_GPENCIL_LEGACY: { - Paint *paint = &ts->gp_vertexpaint->paint; - Brush *brush = BKE_paint_brush(paint); - if (brush && brush->gpencil_settings) { - BKE_gpencil_brush_preset_set(bmain, brush, brush->gpencil_settings->preset_type); - } - break; - } - default: - break; - } - - /* notifiers */ - WM_main_add_notifier(NC_BRUSH | NA_EDITED, nullptr); - - return OPERATOR_FINISHED; -} - -void GPENCIL_OT_brush_reset(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Reset Brush"; - ot->idname = "GPENCIL_OT_brush_reset"; - ot->description = "Reset brush to default parameters"; - - /* api callbacks */ - ot->exec = gpencil_brush_reset_exec; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -static Brush *gpencil_brush_get_first_by_mode(Main *bmain, - Paint * /*paint*/, - const enum eContextObjectMode mode, - char tool) -{ - Brush *brush_next = nullptr; - for (Brush *brush = static_cast(bmain->brushes.first); brush; brush = brush_next) { - brush_next = static_cast(brush->id.next); - - if (brush->gpencil_settings == nullptr) { - continue; - } - - if ((mode == CTX_MODE_PAINT_GPENCIL_LEGACY) && (brush->gpencil_tool == tool)) { - return brush; - } - - if ((mode == CTX_MODE_SCULPT_GPENCIL_LEGACY) && (brush->gpencil_sculpt_tool == tool)) { - return brush; - } - - if ((mode == CTX_MODE_WEIGHT_GPENCIL_LEGACY) && (brush->gpencil_weight_tool == tool)) { - return brush; - } - - if ((mode == CTX_MODE_VERTEX_GPENCIL_LEGACY) && (brush->gpencil_vertex_tool == tool)) { - return brush; - } - } - - return nullptr; -} - -static void gpencil_brush_delete_mode_brushes(Main *bmain, - Paint *paint, - const enum eContextObjectMode mode) -{ - Brush *brush_active = BKE_paint_brush(paint); - Brush *brush_next = nullptr; - for (Brush *brush = static_cast(bmain->brushes.first); brush; brush = brush_next) { - brush_next = static_cast(brush->id.next); - - if ((brush->gpencil_settings == nullptr) && (brush->ob_mode != OB_MODE_PAINT_GPENCIL_LEGACY)) { - continue; - } - - short preset = (brush->gpencil_settings) ? brush->gpencil_settings->preset_type : - short(GP_BRUSH_PRESET_UNKNOWN); - - if (preset != GP_BRUSH_PRESET_UNKNOWN) { - /* Verify to delete only the brushes of the current mode. */ - if (mode == CTX_MODE_PAINT_GPENCIL_LEGACY) { - if ((preset < GP_BRUSH_PRESET_AIRBRUSH) || (preset > GP_BRUSH_PRESET_TINT)) { - continue; - } - if ((brush_active) && (brush_active->gpencil_tool != brush->gpencil_tool)) { - continue; - } - } - - if (mode == CTX_MODE_SCULPT_GPENCIL_LEGACY) { - if ((preset < GP_BRUSH_PRESET_SMOOTH_STROKE) || (preset > GP_BRUSH_PRESET_CLONE_STROKE)) { - continue; - } - if ((brush_active) && (brush_active->gpencil_sculpt_tool != brush->gpencil_sculpt_tool)) { - continue; - } - } - - if (mode == CTX_MODE_WEIGHT_GPENCIL_LEGACY) { - if ((preset < GP_BRUSH_PRESET_WEIGHT_DRAW) || (preset > GP_BRUSH_PRESET_WEIGHT_SMEAR)) { - continue; - } - if ((brush_active) && (brush_active->gpencil_weight_tool != brush->gpencil_weight_tool)) { - continue; - } - } - - if (mode == CTX_MODE_VERTEX_GPENCIL_LEGACY) { - if ((preset < GP_BRUSH_PRESET_VERTEX_DRAW) || (preset > GP_BRUSH_PRESET_VERTEX_REPLACE)) { - continue; - } - if ((brush_active) && (brush_active->gpencil_vertex_tool != brush->gpencil_vertex_tool)) { - continue; - } - } - } - - /* Before delete, un-pin any material of the brush. */ - if ((brush->gpencil_settings) && (brush->gpencil_settings->material != nullptr)) { - brush->gpencil_settings->material = nullptr; - brush->gpencil_settings->flag &= ~GP_BRUSH_MATERIAL_PINNED; - } - - BKE_brush_delete(bmain, brush); - if (brush == brush_active) { - brush_active = nullptr; - } - } -} - -static int gpencil_brush_reset_all_exec(bContext *C, wmOperator * /*op*/) -{ - Main *bmain = CTX_data_main(C); - ToolSettings *ts = CTX_data_tool_settings(C); - const enum eContextObjectMode mode = CTX_data_mode_enum(C); - Paint *paint = nullptr; - - switch (mode) { - case CTX_MODE_PAINT_GPENCIL_LEGACY: { - paint = &ts->gp_paint->paint; - break; - } - case CTX_MODE_SCULPT_GPENCIL_LEGACY: { - paint = &ts->gp_sculptpaint->paint; - break; - } - case CTX_MODE_WEIGHT_GPENCIL_LEGACY: { - paint = &ts->gp_weightpaint->paint; - break; - } - case CTX_MODE_VERTEX_GPENCIL_LEGACY: { - paint = &ts->gp_vertexpaint->paint; - break; - } - default: - break; - } - - char tool = '0'; - if (paint) { - Brush *brush_active = BKE_paint_brush(paint); - if (brush_active) { - switch (mode) { - case CTX_MODE_PAINT_GPENCIL_LEGACY: { - tool = brush_active->gpencil_tool; - break; - } - case CTX_MODE_SCULPT_GPENCIL_LEGACY: { - tool = brush_active->gpencil_sculpt_tool; - break; - } - case CTX_MODE_WEIGHT_GPENCIL_LEGACY: { - tool = brush_active->gpencil_weight_tool; - break; - } - case CTX_MODE_VERTEX_GPENCIL_LEGACY: { - tool = brush_active->gpencil_vertex_tool; - break; - } - default: { - tool = brush_active->gpencil_tool; - break; - } - } - } - - gpencil_brush_delete_mode_brushes(bmain, paint, mode); - - switch (mode) { - case CTX_MODE_PAINT_GPENCIL_LEGACY: { - BKE_brush_gpencil_paint_presets(bmain, ts, true); - break; - } - case CTX_MODE_SCULPT_GPENCIL_LEGACY: { - BKE_brush_gpencil_sculpt_presets(bmain, ts, true); - break; - } - case CTX_MODE_WEIGHT_GPENCIL_LEGACY: { - BKE_brush_gpencil_weight_presets(bmain, ts, true); - break; - } - case CTX_MODE_VERTEX_GPENCIL_LEGACY: { - BKE_brush_gpencil_vertex_presets(bmain, ts, true); - break; - } - default: { - break; - } - } - - BKE_paint_brush_validate(bmain, paint); - - /* Set Again the first brush of the mode. */ - Brush *deft_brush = gpencil_brush_get_first_by_mode(bmain, paint, mode, tool); - if (deft_brush) { - BKE_paint_brush_set(paint, deft_brush); - } - /* notifiers */ - DEG_relations_tag_update(bmain); - WM_main_add_notifier(NC_BRUSH | NA_EDITED, nullptr); - } - - return OPERATOR_FINISHED; -} - -void GPENCIL_OT_brush_reset_all(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Reset All Brushes"; - ot->idname = "GPENCIL_OT_brush_reset_all"; - ot->description = "Delete all mode brushes and recreate a default set"; - - /* api callbacks */ - ot->exec = gpencil_brush_reset_all_exec; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - /*********************** Vertex Groups ***********************************/ static bool gpencil_vertex_group_poll(bContext *C) diff --git a/source/blender/editors/gpencil_legacy/gpencil_intern.hh b/source/blender/editors/gpencil_legacy/gpencil_intern.hh index 5f067b177b7..48d36911c55 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_intern.hh +++ b/source/blender/editors/gpencil_legacy/gpencil_intern.hh @@ -619,9 +619,6 @@ void GPENCIL_OT_extract_palette_vertex(wmOperatorType *ot); void GPENCIL_OT_transform_fill(wmOperatorType *ot); void GPENCIL_OT_reset_transform_fill(wmOperatorType *ot); -void GPENCIL_OT_brush_reset(wmOperatorType *ot); -void GPENCIL_OT_brush_reset_all(wmOperatorType *ot); - /* undo stack ---------- */ void gpencil_undo_init(bGPdata *gpd); diff --git a/source/blender/editors/gpencil_legacy/gpencil_ops.cc b/source/blender/editors/gpencil_legacy/gpencil_ops.cc index a50cdd40057..26de6fc6841 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_ops.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_ops.cc @@ -715,9 +715,6 @@ void ED_operatortypes_gpencil_legacy() WM_operatortype_append(GPENCIL_OT_transform_fill); WM_operatortype_append(GPENCIL_OT_reset_transform_fill); - WM_operatortype_append(GPENCIL_OT_brush_reset); - WM_operatortype_append(GPENCIL_OT_brush_reset_all); - /* vertex groups */ WM_operatortype_append(GPENCIL_OT_vertex_group_assign); WM_operatortype_append(GPENCIL_OT_vertex_group_remove_from); diff --git a/source/blender/editors/gpencil_legacy/gpencil_paint.cc b/source/blender/editors/gpencil_legacy/gpencil_paint.cc index 6470e29e59b..82c565df7a8 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_paint.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_paint.cc @@ -1949,7 +1949,6 @@ static Brush *gpencil_get_default_eraser(Main *bmain, ToolSettings *ts) brush_dft = BKE_brush_add_gpencil(bmain, ts, "Soft Eraser", OB_MODE_PAINT_GPENCIL_LEGACY); brush_dft->size = 30.0f; brush_dft->gpencil_settings->flag |= GP_BRUSH_DEFAULT_ERASER; - brush_dft->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_SOFT; brush_dft->gpencil_tool = GPAINT_TOOL_ERASE; brush_dft->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_SOFT; diff --git a/source/blender/editors/interface/interface_icons.cc b/source/blender/editors/interface/interface_icons.cc index fc6112d42eb..f39ad2b16ce 100644 --- a/source/blender/editors/interface/interface_icons.cc +++ b/source/blender/editors/interface/interface_icons.cc @@ -543,92 +543,6 @@ static void vicon_gplayer_color_draw(Icon *icon, int x, int y, int w, int h) immUnbindProgram(); } -static void init_brush_icons() -{ - -# define INIT_BRUSH_ICON(icon_id, name) \ - { \ - const uchar *rect = (const uchar *)datatoc_##name##_png; \ - const int size = datatoc_##name##_png_size; \ - DrawInfo *di = def_internal_icon(nullptr, icon_id, 0, 0, w, ICON_TYPE_BUFFER, 0); \ - di->data.buffer.image->datatoc_rect = rect; \ - di->data.buffer.image->datatoc_size = size; \ - } \ - ((void)0) - /* end INIT_BRUSH_ICON */ - - const int w = 96; /* warning, brush size hardcoded in C, but it gets scaled */ - - INIT_BRUSH_ICON(ICON_BRUSH_BLOB, blob); - INIT_BRUSH_ICON(ICON_BRUSH_BLUR, blur); - INIT_BRUSH_ICON(ICON_BRUSH_CLAY, clay); - INIT_BRUSH_ICON(ICON_BRUSH_CLAY_STRIPS, claystrips); - INIT_BRUSH_ICON(ICON_BRUSH_CLONE, clone); - INIT_BRUSH_ICON(ICON_BRUSH_CREASE, crease); - INIT_BRUSH_ICON(ICON_BRUSH_SCULPT_DRAW, draw); - INIT_BRUSH_ICON(ICON_BRUSH_FILL, fill); - INIT_BRUSH_ICON(ICON_BRUSH_FLATTEN, flatten); - INIT_BRUSH_ICON(ICON_BRUSH_GRAB, grab); - INIT_BRUSH_ICON(ICON_BRUSH_INFLATE, inflate); - INIT_BRUSH_ICON(ICON_BRUSH_LAYER, layer); - INIT_BRUSH_ICON(ICON_BRUSH_MASK, mask); - INIT_BRUSH_ICON(ICON_BRUSH_MIX, mix); - INIT_BRUSH_ICON(ICON_BRUSH_NUDGE, nudge); - INIT_BRUSH_ICON(ICON_BRUSH_PAINT_SELECT, paint_select); - INIT_BRUSH_ICON(ICON_BRUSH_PINCH, pinch); - INIT_BRUSH_ICON(ICON_BRUSH_SCRAPE, scrape); - INIT_BRUSH_ICON(ICON_BRUSH_SMEAR, smear); - INIT_BRUSH_ICON(ICON_BRUSH_SMOOTH, smooth); - INIT_BRUSH_ICON(ICON_BRUSH_SNAKE_HOOK, snake_hook); - INIT_BRUSH_ICON(ICON_BRUSH_SOFTEN, soften); - INIT_BRUSH_ICON(ICON_BRUSH_TEXDRAW, texdraw); - INIT_BRUSH_ICON(ICON_BRUSH_TEXFILL, texfill); - INIT_BRUSH_ICON(ICON_BRUSH_TEXMASK, texmask); - INIT_BRUSH_ICON(ICON_BRUSH_THUMB, thumb); - INIT_BRUSH_ICON(ICON_BRUSH_ROTATE, twist); - - /* grease pencil sculpt */ - INIT_BRUSH_ICON(ICON_GPBRUSH_SMOOTH, gp_brush_smooth); - INIT_BRUSH_ICON(ICON_GPBRUSH_THICKNESS, gp_brush_thickness); - INIT_BRUSH_ICON(ICON_GPBRUSH_STRENGTH, gp_brush_strength); - INIT_BRUSH_ICON(ICON_GPBRUSH_GRAB, gp_brush_grab); - INIT_BRUSH_ICON(ICON_GPBRUSH_PUSH, gp_brush_push); - INIT_BRUSH_ICON(ICON_GPBRUSH_TWIST, gp_brush_twist); - INIT_BRUSH_ICON(ICON_GPBRUSH_PINCH, gp_brush_pinch); - INIT_BRUSH_ICON(ICON_GPBRUSH_RANDOMIZE, gp_brush_randomize); - INIT_BRUSH_ICON(ICON_GPBRUSH_CLONE, gp_brush_clone); - INIT_BRUSH_ICON(ICON_GPBRUSH_WEIGHT, gp_brush_weight); - - /* grease pencil drawing brushes */ - INIT_BRUSH_ICON(ICON_GPBRUSH_PENCIL, gp_brush_pencil); - INIT_BRUSH_ICON(ICON_GPBRUSH_PEN, gp_brush_pen); - INIT_BRUSH_ICON(ICON_GPBRUSH_INK, gp_brush_ink); - INIT_BRUSH_ICON(ICON_GPBRUSH_INKNOISE, gp_brush_inknoise); - INIT_BRUSH_ICON(ICON_GPBRUSH_BLOCK, gp_brush_block); - INIT_BRUSH_ICON(ICON_GPBRUSH_MARKER, gp_brush_marker); - INIT_BRUSH_ICON(ICON_GPBRUSH_FILL, gp_brush_fill); - INIT_BRUSH_ICON(ICON_GPBRUSH_AIRBRUSH, gp_brush_airbrush); - INIT_BRUSH_ICON(ICON_GPBRUSH_CHISEL, gp_brush_chisel); - INIT_BRUSH_ICON(ICON_GPBRUSH_ERASE_SOFT, gp_brush_erase_soft); - INIT_BRUSH_ICON(ICON_GPBRUSH_ERASE_HARD, gp_brush_erase_hard); - INIT_BRUSH_ICON(ICON_GPBRUSH_ERASE_STROKE, gp_brush_erase_stroke); - - /* Curves sculpt. */ - INIT_BRUSH_ICON(ICON_BRUSH_CURVES_ADD, curves_sculpt_add); - INIT_BRUSH_ICON(ICON_BRUSH_CURVES_COMB, curves_sculpt_comb); - INIT_BRUSH_ICON(ICON_BRUSH_CURVES_CUT, curves_sculpt_cut); - INIT_BRUSH_ICON(ICON_BRUSH_CURVES_DELETE, curves_sculpt_delete); - INIT_BRUSH_ICON(ICON_BRUSH_CURVES_DENSITY, curves_sculpt_density); - INIT_BRUSH_ICON(ICON_BRUSH_CURVES_GROW_SHRINK, curves_sculpt_grow_shrink); - INIT_BRUSH_ICON(ICON_BRUSH_CURVES_PINCH, curves_sculpt_pinch); - INIT_BRUSH_ICON(ICON_BRUSH_CURVES_PUFF, curves_sculpt_puff); - INIT_BRUSH_ICON(ICON_BRUSH_CURVES_SLIDE, curves_sculpt_slide); - INIT_BRUSH_ICON(ICON_BRUSH_CURVES_SMOOTH, curves_sculpt_smooth); - INIT_BRUSH_ICON(ICON_BRUSH_CURVES_SNAKE_HOOK, curves_sculpt_snake_hook); - -# undef INIT_BRUSH_ICON -} - static DrawInfo *g_di_event_list = nullptr; int UI_icon_from_event_type(short event_type, short event_value) @@ -1401,7 +1315,6 @@ void UI_icons_init() init_iconfile_list(&iconfilelist); UI_icons_reload_internal_textures(); init_internal_icons(); - init_brush_icons(); init_event_icons(); #endif } @@ -2220,176 +2133,6 @@ static void ui_id_icon_render(const bContext *C, ID *id, bool use_jobs) } } -static int ui_id_brush_get_icon(const bContext *C, ID *id) -{ - Brush *br = (Brush *)id; - - if (br->flag & BRUSH_CUSTOM_ICON) { - BKE_icon_id_ensure(id); - ui_id_icon_render(C, id, true); - } - else { - Object *ob = CTX_data_active_object(C); - const EnumPropertyItem *items = nullptr; - PaintMode paint_mode = PaintMode::Invalid; - ScrArea *area = CTX_wm_area(C); - char space_type = area->spacetype; - /* Fallback to 3D view. */ - if (space_type == SPACE_PROPERTIES) { - space_type = SPACE_VIEW3D; - } - - /* XXX: this is not nice, should probably make brushes - * be strictly in one paint mode only to avoid - * checking various context stuff here */ - - if ((space_type == SPACE_VIEW3D) && ob) { - if (ob->mode & OB_MODE_SCULPT) { - paint_mode = PaintMode::Sculpt; - } - else if (ob->mode & OB_MODE_VERTEX_PAINT) { - paint_mode = PaintMode::Vertex; - } - else if (ob->mode & OB_MODE_WEIGHT_PAINT) { - paint_mode = PaintMode::Weight; - } - else if (ob->mode & OB_MODE_TEXTURE_PAINT) { - paint_mode = PaintMode::Texture3D; - } - else if (ob->mode & OB_MODE_SCULPT_CURVES) { - paint_mode = PaintMode::SculptCurves; - } - } - else if (space_type == SPACE_IMAGE) { - if (area->spacetype == space_type) { - const SpaceImage *sima = static_cast(area->spacedata.first); - if (sima->mode == SI_MODE_PAINT) { - paint_mode = PaintMode::Texture2D; - } - } - } - - /* reset the icon */ - if ((ob != nullptr) && (ob->mode & OB_MODE_ALL_PAINT_GPENCIL) && - (br->gpencil_settings != nullptr)) - { - switch (br->gpencil_settings->icon_id) { - case GP_BRUSH_ICON_PENCIL: - br->id.icon_id = ICON_GPBRUSH_PENCIL; - break; - case GP_BRUSH_ICON_PEN: - br->id.icon_id = ICON_GPBRUSH_PEN; - break; - case GP_BRUSH_ICON_INK: - br->id.icon_id = ICON_GPBRUSH_INK; - break; - case GP_BRUSH_ICON_INKNOISE: - br->id.icon_id = ICON_GPBRUSH_INKNOISE; - break; - case GP_BRUSH_ICON_BLOCK: - br->id.icon_id = ICON_GPBRUSH_BLOCK; - break; - case GP_BRUSH_ICON_MARKER: - br->id.icon_id = ICON_GPBRUSH_MARKER; - break; - case GP_BRUSH_ICON_FILL: - br->id.icon_id = ICON_GPBRUSH_FILL; - break; - case GP_BRUSH_ICON_AIRBRUSH: - br->id.icon_id = ICON_GPBRUSH_AIRBRUSH; - break; - case GP_BRUSH_ICON_CHISEL: - br->id.icon_id = ICON_GPBRUSH_CHISEL; - break; - case GP_BRUSH_ICON_ERASE_SOFT: - br->id.icon_id = ICON_GPBRUSH_ERASE_SOFT; - break; - case GP_BRUSH_ICON_ERASE_HARD: - br->id.icon_id = ICON_GPBRUSH_ERASE_HARD; - break; - case GP_BRUSH_ICON_ERASE_STROKE: - br->id.icon_id = ICON_GPBRUSH_ERASE_STROKE; - break; - case GP_BRUSH_ICON_TINT: - br->id.icon_id = ICON_BRUSH_TEXDRAW; - break; - case GP_BRUSH_ICON_VERTEX_DRAW: - br->id.icon_id = ICON_BRUSH_MIX; - break; - case GP_BRUSH_ICON_VERTEX_BLUR: - br->id.icon_id = ICON_BRUSH_BLUR; - break; - case GP_BRUSH_ICON_VERTEX_AVERAGE: - br->id.icon_id = ICON_BRUSH_BLUR; - break; - case GP_BRUSH_ICON_VERTEX_SMEAR: - br->id.icon_id = ICON_BRUSH_BLUR; - break; - case GP_BRUSH_ICON_VERTEX_REPLACE: - br->id.icon_id = ICON_BRUSH_MIX; - break; - case GP_BRUSH_ICON_GPBRUSH_SMOOTH: - br->id.icon_id = ICON_GPBRUSH_SMOOTH; - break; - case GP_BRUSH_ICON_GPBRUSH_THICKNESS: - br->id.icon_id = ICON_GPBRUSH_THICKNESS; - break; - case GP_BRUSH_ICON_GPBRUSH_STRENGTH: - br->id.icon_id = ICON_GPBRUSH_STRENGTH; - break; - case GP_BRUSH_ICON_GPBRUSH_RANDOMIZE: - br->id.icon_id = ICON_GPBRUSH_RANDOMIZE; - break; - case GP_BRUSH_ICON_GPBRUSH_GRAB: - br->id.icon_id = ICON_GPBRUSH_GRAB; - break; - case GP_BRUSH_ICON_GPBRUSH_PUSH: - br->id.icon_id = ICON_GPBRUSH_PUSH; - break; - case GP_BRUSH_ICON_GPBRUSH_TWIST: - br->id.icon_id = ICON_GPBRUSH_TWIST; - break; - case GP_BRUSH_ICON_GPBRUSH_PINCH: - br->id.icon_id = ICON_GPBRUSH_PINCH; - break; - case GP_BRUSH_ICON_GPBRUSH_CLONE: - br->id.icon_id = ICON_GPBRUSH_CLONE; - break; - case GP_BRUSH_ICON_GPBRUSH_WEIGHT: - br->id.icon_id = ICON_GPBRUSH_WEIGHT; - break; - case GP_BRUSH_ICON_GPBRUSH_BLUR: - br->id.icon_id = ICON_BRUSH_BLUR; - break; - case GP_BRUSH_ICON_GPBRUSH_AVERAGE: - br->id.icon_id = ICON_BRUSH_BLUR; - break; - case GP_BRUSH_ICON_GPBRUSH_SMEAR: - br->id.icon_id = ICON_BRUSH_BLUR; - break; - default: - br->id.icon_id = ICON_GPBRUSH_PEN; - break; - } - return id->icon_id; - } - - if (paint_mode != PaintMode::Invalid) { - items = BKE_paint_get_tool_enum_from_paintmode(paint_mode); - const uint tool_offset = BKE_paint_get_brush_tool_offset_from_paintmode(paint_mode); - const int tool_type = *(char *)POINTER_OFFSET(br, tool_offset); - if (!items || !RNA_enum_icon_from_value(items, tool_type, &id->icon_id)) { - id->icon_id = 0; - } - } - else { - id->icon_id = 0; - } - } - - return id->icon_id; -} - static int ui_id_screen_get_icon(const bContext *C, ID *id) { BKE_icon_id_ensure(id); @@ -2405,9 +2148,6 @@ int ui_id_icon_get(const bContext *C, ID *id, const bool big) /* icon */ switch (GS(id->name)) { - case ID_BR: - iconid = ui_id_brush_get_icon(C, id); - break; case ID_MA: /* fall through */ case ID_TE: /* fall through */ case ID_IM: /* fall through */ diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 1d17f9ae075..58298435820 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -47,186 +47,6 @@ #include "paint_intern.hh" #include "sculpt_intern.hh" -static eGPBrush_Presets gpencil_get_brush_preset_from_tool(bToolRef *tool, - enum eContextObjectMode mode) -{ - switch (mode) { - case CTX_MODE_PAINT_GPENCIL_LEGACY: { - if (STREQ(tool->runtime->data_block, "DRAW")) { - return GP_BRUSH_PRESET_PENCIL; - } - if (STREQ(tool->runtime->data_block, "FILL")) { - return GP_BRUSH_PRESET_FILL_AREA; - } - if (STREQ(tool->runtime->data_block, "ERASE")) { - return GP_BRUSH_PRESET_ERASER_SOFT; - } - if (STREQ(tool->runtime->data_block, "TINT")) { - return GP_BRUSH_PRESET_TINT; - } - break; - } - case CTX_MODE_SCULPT_GPENCIL_LEGACY: { - if (STREQ(tool->runtime->data_block, "SMOOTH")) { - return GP_BRUSH_PRESET_SMOOTH_STROKE; - } - if (STREQ(tool->runtime->data_block, "STRENGTH")) { - return GP_BRUSH_PRESET_STRENGTH_STROKE; - } - if (STREQ(tool->runtime->data_block, "THICKNESS")) { - return GP_BRUSH_PRESET_THICKNESS_STROKE; - } - if (STREQ(tool->runtime->data_block, "GRAB")) { - return GP_BRUSH_PRESET_GRAB_STROKE; - } - if (STREQ(tool->runtime->data_block, "PUSH")) { - return GP_BRUSH_PRESET_PUSH_STROKE; - } - if (STREQ(tool->runtime->data_block, "TWIST")) { - return GP_BRUSH_PRESET_TWIST_STROKE; - } - if (STREQ(tool->runtime->data_block, "PINCH")) { - return GP_BRUSH_PRESET_PINCH_STROKE; - } - if (STREQ(tool->runtime->data_block, "RANDOMIZE")) { - return GP_BRUSH_PRESET_RANDOMIZE_STROKE; - } - if (STREQ(tool->runtime->data_block, "CLONE")) { - return GP_BRUSH_PRESET_CLONE_STROKE; - } - break; - } - case CTX_MODE_WEIGHT_GPENCIL_LEGACY: { - if (STREQ(tool->runtime->data_block, "DRAW")) { - return GP_BRUSH_PRESET_WEIGHT_DRAW; - } - if (STREQ(tool->runtime->data_block, "BLUR")) { - return GP_BRUSH_PRESET_WEIGHT_BLUR; - } - if (STREQ(tool->runtime->data_block, "AVERAGE")) { - return GP_BRUSH_PRESET_WEIGHT_AVERAGE; - } - if (STREQ(tool->runtime->data_block, "SMEAR")) { - return GP_BRUSH_PRESET_WEIGHT_SMEAR; - } - break; - } - case CTX_MODE_VERTEX_GPENCIL_LEGACY: { - if (STREQ(tool->runtime->data_block, "DRAW")) { - return GP_BRUSH_PRESET_VERTEX_DRAW; - } - if (STREQ(tool->runtime->data_block, "BLUR")) { - return GP_BRUSH_PRESET_VERTEX_BLUR; - } - if (STREQ(tool->runtime->data_block, "AVERAGE")) { - return GP_BRUSH_PRESET_VERTEX_AVERAGE; - } - if (STREQ(tool->runtime->data_block, "SMEAR")) { - return GP_BRUSH_PRESET_VERTEX_SMEAR; - } - if (STREQ(tool->runtime->data_block, "REPLACE")) { - return GP_BRUSH_PRESET_VERTEX_REPLACE; - } - break; - } - default: - return GP_BRUSH_PRESET_UNKNOWN; - } - return GP_BRUSH_PRESET_UNKNOWN; -} - -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); // TODO: add to asset main? - - if (br) { - br = (Brush *)BKE_id_copy(bmain, &br->id); - } - else { - /* Get the active tool to determine what type of brush is active. */ - bScreen *screen = CTX_wm_screen(C); - if (screen == nullptr) { - return OPERATOR_CANCELLED; - } - - bToolRef *tool = nullptr; - LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { - if (area->spacetype == SPACE_VIEW3D) { - /* Check the current tool is a brush. */ - bToolRef *tref = area->runtime.tool; - if (tref && tref->runtime && tref->runtime->data_block[0]) { - tool = tref; - break; - } - } - } - - if (tool == nullptr) { - return OPERATOR_CANCELLED; - } - - /* Get Brush mode base on context mode. */ - const enum eContextObjectMode mode = CTX_data_mode_enum(C); - eObjectMode obmode = OB_MODE_PAINT_GPENCIL_LEGACY; - switch (mode) { - case CTX_MODE_PAINT_GPENCIL_LEGACY: - obmode = OB_MODE_PAINT_GPENCIL_LEGACY; - break; - case CTX_MODE_SCULPT_GPENCIL_LEGACY: - obmode = OB_MODE_SCULPT_GPENCIL_LEGACY; - break; - case CTX_MODE_WEIGHT_GPENCIL_LEGACY: - obmode = OB_MODE_WEIGHT_GPENCIL_LEGACY; - break; - case CTX_MODE_VERTEX_GPENCIL_LEGACY: - obmode = OB_MODE_VERTEX_GPENCIL_LEGACY; - break; - default: - return OPERATOR_CANCELLED; - break; - } - - /* Get brush preset using the actual tool. */ - eGPBrush_Presets preset = gpencil_get_brush_preset_from_tool(tool, mode); - - /* Capitalize Brush name first letter using the tool name. */ - char name[64]; - STRNCPY(name, tool->runtime->data_block); - BLI_str_tolower_ascii(name, sizeof(name)); - name[0] = BLI_toupper_ascii(name[0]); - - /* Create the brush and assign default values. */ - br = BKE_brush_add(bmain, name, obmode); - if (br) { - BKE_brush_init_gpencil_settings(br); - BKE_gpencil_brush_preset_set(bmain, br, preset); - } - } - - if (br) { - id_us_min(&br->id); /* fake user only */ - BKE_paint_brush_set(paint, br); - } - - return OPERATOR_FINISHED; -} - -static void BRUSH_OT_add_gpencil(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Add Drawing Brush"; - ot->description = "Add brush for Grease Pencil"; - ot->idname = "BRUSH_OT_add_gpencil"; - - /* api callbacks */ - ot->exec = brush_add_gpencil_exec; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - static int brush_scale_size_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); @@ -1150,7 +970,6 @@ void ED_operatortypes_paint() WM_operatortype_append(PAINTCURVE_OT_cursor); /* brush */ - WM_operatortype_append(BRUSH_OT_add_gpencil); WM_operatortype_append(BRUSH_OT_scale_size); WM_operatortype_append(BRUSH_OT_curve_preset); WM_operatortype_append(BRUSH_OT_sculpt_curves_falloff_preset); diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h index e338c0ed568..9c8ebb063cc 100644 --- a/source/blender/makesdna/DNA_brush_enums.h +++ b/source/blender/makesdna/DNA_brush_enums.h @@ -165,41 +165,6 @@ typedef enum eGP_BrushMode { GP_BRUSH_MODE_VERTEXCOLOR = 2, } eGP_BrushMode; -/* BrushGpencilSettings default brush icons */ -typedef enum eGP_BrushIcons { - GP_BRUSH_ICON_PENCIL = 1, - GP_BRUSH_ICON_PEN = 2, - GP_BRUSH_ICON_INK = 3, - GP_BRUSH_ICON_INKNOISE = 4, - GP_BRUSH_ICON_BLOCK = 5, - GP_BRUSH_ICON_MARKER = 6, - GP_BRUSH_ICON_FILL = 7, - GP_BRUSH_ICON_ERASE_SOFT = 8, - GP_BRUSH_ICON_ERASE_HARD = 9, - GP_BRUSH_ICON_ERASE_STROKE = 10, - GP_BRUSH_ICON_AIRBRUSH = 11, - GP_BRUSH_ICON_CHISEL = 12, - GP_BRUSH_ICON_TINT = 13, - GP_BRUSH_ICON_VERTEX_DRAW = 14, - GP_BRUSH_ICON_VERTEX_BLUR = 15, - GP_BRUSH_ICON_VERTEX_AVERAGE = 16, - GP_BRUSH_ICON_VERTEX_SMEAR = 17, - GP_BRUSH_ICON_VERTEX_REPLACE = 18, - GP_BRUSH_ICON_GPBRUSH_SMOOTH = 19, - GP_BRUSH_ICON_GPBRUSH_THICKNESS = 20, - GP_BRUSH_ICON_GPBRUSH_STRENGTH = 21, - GP_BRUSH_ICON_GPBRUSH_RANDOMIZE = 22, - GP_BRUSH_ICON_GPBRUSH_GRAB = 23, - GP_BRUSH_ICON_GPBRUSH_PUSH = 24, - GP_BRUSH_ICON_GPBRUSH_TWIST = 25, - GP_BRUSH_ICON_GPBRUSH_PINCH = 26, - GP_BRUSH_ICON_GPBRUSH_CLONE = 27, - GP_BRUSH_ICON_GPBRUSH_WEIGHT = 28, - GP_BRUSH_ICON_GPBRUSH_BLUR = 29, - GP_BRUSH_ICON_GPBRUSH_AVERAGE = 30, - GP_BRUSH_ICON_GPBRUSH_SMEAR = 31, -} eGP_BrushIcons; - typedef enum eBrushCurvePreset { BRUSH_CURVE_CUSTOM = 0, BRUSH_CURVE_SMOOTH = 1, diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 9d7f630ee0c..ad8a7562c04 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -59,7 +59,7 @@ typedef struct BrushGpencilSettings { char _pad2[2]; /* Type of caps: eGPDstroke_Caps. */ int8_t caps_type; - char _pad[5]; + char _pad[1]; int flag2; @@ -69,8 +69,6 @@ typedef struct BrushGpencilSettings { int fill_draw_mode; /** Type of gap filling extension to use. */ int fill_extend_mode; - /** Icon identifier. */ - int icon_id; /** Maximum distance before generate new point for very fast mouse movements. */ int input_samples; diff --git a/source/blender/makesrna/intern/rna_brush.cc b/source/blender/makesrna/intern/rna_brush.cc index 6e22c308461..c48c9e7d490 100644 --- a/source/blender/makesrna/intern/rna_brush.cc +++ b/source/blender/makesrna/intern/rna_brush.cc @@ -391,50 +391,6 @@ static EnumPropertyItem rna_enum_gpencil_brush_modes_items[] = { {GP_BRUSH_MODE_VERTEXCOLOR, "VERTEXCOLOR", 0, "Vertex Color", "Use always Vertex Color mode"}, {0, nullptr, 0, nullptr, nullptr}}; -static EnumPropertyItem rna_enum_gpencil_brush_paint_icons_items[] = { - {GP_BRUSH_ICON_PENCIL, "PENCIL", ICON_GPBRUSH_PENCIL, "Pencil", ""}, - {GP_BRUSH_ICON_PEN, "PEN", ICON_GPBRUSH_PEN, "Pen", ""}, - {GP_BRUSH_ICON_INK, "INK", ICON_GPBRUSH_INK, "Ink", ""}, - {GP_BRUSH_ICON_INKNOISE, "INKNOISE", ICON_GPBRUSH_INKNOISE, "Ink Noise", ""}, - {GP_BRUSH_ICON_BLOCK, "BLOCK", ICON_GPBRUSH_BLOCK, "Block", ""}, - {GP_BRUSH_ICON_MARKER, "MARKER", ICON_GPBRUSH_MARKER, "Marker", ""}, - {GP_BRUSH_ICON_AIRBRUSH, "AIRBRUSH", ICON_GPBRUSH_AIRBRUSH, "Airbrush", ""}, - {GP_BRUSH_ICON_CHISEL, "CHISEL", ICON_GPBRUSH_CHISEL, "Chisel", ""}, - {GP_BRUSH_ICON_FILL, "FILL", ICON_GPBRUSH_FILL, "Fill", ""}, - {GP_BRUSH_ICON_ERASE_SOFT, "SOFT", ICON_GPBRUSH_ERASE_SOFT, "Eraser Soft", ""}, - {GP_BRUSH_ICON_ERASE_HARD, "HARD", ICON_GPBRUSH_ERASE_HARD, "Eraser Hard", ""}, - {GP_BRUSH_ICON_ERASE_STROKE, "STROKE", ICON_GPBRUSH_ERASE_STROKE, "Eraser Stroke", ""}, - {0, nullptr, 0, nullptr, nullptr}, -}; - -static EnumPropertyItem rna_enum_gpencil_brush_sculpt_icons_items[] = { - {GP_BRUSH_ICON_GPBRUSH_SMOOTH, "SMOOTH", ICON_GPBRUSH_SMOOTH, "Smooth", ""}, - {GP_BRUSH_ICON_GPBRUSH_THICKNESS, "THICKNESS", ICON_GPBRUSH_THICKNESS, "Thickness", ""}, - {GP_BRUSH_ICON_GPBRUSH_STRENGTH, "STRENGTH", ICON_GPBRUSH_STRENGTH, "Strength", ""}, - {GP_BRUSH_ICON_GPBRUSH_RANDOMIZE, "RANDOMIZE", ICON_GPBRUSH_RANDOMIZE, "Randomize", ""}, - {GP_BRUSH_ICON_GPBRUSH_GRAB, "GRAB", ICON_GPBRUSH_GRAB, "Grab", ""}, - {GP_BRUSH_ICON_GPBRUSH_PUSH, "PUSH", ICON_GPBRUSH_PUSH, "Push", ""}, - {GP_BRUSH_ICON_GPBRUSH_TWIST, "TWIST", ICON_GPBRUSH_TWIST, "Twist", ""}, - {GP_BRUSH_ICON_GPBRUSH_PINCH, "PINCH", ICON_GPBRUSH_PINCH, "Pinch", ""}, - {GP_BRUSH_ICON_GPBRUSH_CLONE, "CLONE", ICON_GPBRUSH_CLONE, "Clone", ""}, - {0, nullptr, 0, nullptr, nullptr}, -}; - -static EnumPropertyItem rna_enum_gpencil_brush_weight_icons_items[] = { - {GP_BRUSH_ICON_GPBRUSH_WEIGHT, "DRAW", ICON_GPBRUSH_WEIGHT, "Draw", ""}, - {GP_BRUSH_ICON_GPBRUSH_BLUR, "BLUR", ICON_BRUSH_BLUR, "Blur", ""}, - {GP_BRUSH_ICON_GPBRUSH_AVERAGE, "AVERAGE", ICON_BRUSH_BLUR, "Average", ""}, - {GP_BRUSH_ICON_GPBRUSH_SMEAR, "SMEAR", ICON_BRUSH_BLUR, "Smear", ""}, - {0, nullptr, 0, nullptr, nullptr}, -}; -static EnumPropertyItem rna_enum_gpencil_brush_vertex_icons_items[] = { - {GP_BRUSH_ICON_VERTEX_DRAW, "DRAW", ICON_BRUSH_MIX, "Draw", ""}, - {GP_BRUSH_ICON_VERTEX_BLUR, "BLUR", ICON_BRUSH_BLUR, "Blur", ""}, - {GP_BRUSH_ICON_VERTEX_AVERAGE, "AVERAGE", ICON_BRUSH_BLUR, "Average", ""}, - {GP_BRUSH_ICON_VERTEX_SMEAR, "SMEAR", ICON_BRUSH_BLUR, "Smear", ""}, - {GP_BRUSH_ICON_VERTEX_REPLACE, "REPLACE", ICON_BRUSH_MIX, "Replace", ""}, - {0, nullptr, 0, nullptr, nullptr}, -}; #endif #ifdef RNA_RUNTIME @@ -1113,33 +1069,6 @@ static void rna_BrushGpencilSettings_use_material_pin_update(bContext *C, Pointe WM_event_add_notifier(C, NC_SPACE | ND_SPACE_PROPERTIES, nullptr); } -static void rna_BrushGpencilSettings_eraser_mode_update(Main * /*bmain*/, - Scene *scene, - PointerRNA * /*ptr*/) -{ - ToolSettings *ts = scene->toolsettings; - Paint *paint = &ts->gp_paint->paint; - Brush *brush = BKE_paint_brush(paint); - - /* set eraser icon */ - if ((brush) && (brush->gpencil_tool == GPAINT_TOOL_ERASE)) { - switch (brush->gpencil_settings->eraser_mode) { - case GP_BRUSH_ERASER_SOFT: - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_SOFT; - break; - case GP_BRUSH_ERASER_HARD: - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_HARD; - break; - case GP_BRUSH_ERASER_STROKE: - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_STROKE; - break; - default: - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_SOFT; - break; - } - } -} - static bool rna_BrushGpencilSettings_material_poll(PointerRNA * /*ptr*/, PointerRNA value) { Material *ma = (Material *)value.data; @@ -1722,32 +1651,6 @@ static void rna_def_gpencil_options(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, nullptr); - /* brush standard icon */ - prop = RNA_def_property(srna, "gpencil_paint_icon", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, nullptr, "icon_id"); - RNA_def_property_enum_items(prop, rna_enum_gpencil_brush_paint_icons_items); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Grease Pencil Icon", ""); - - prop = RNA_def_property(srna, "gpencil_sculpt_icon", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, nullptr, "icon_id"); - RNA_def_property_enum_items(prop, rna_enum_gpencil_brush_sculpt_icons_items); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_GPENCIL); - RNA_def_property_ui_text(prop, "Grease Pencil Icon", ""); - - prop = RNA_def_property(srna, "gpencil_weight_icon", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, nullptr, "icon_id"); - RNA_def_property_enum_items(prop, rna_enum_gpencil_brush_weight_icons_items); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Grease Pencil Icon", ""); - - prop = RNA_def_property(srna, "gpencil_vertex_icon", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, nullptr, "icon_id"); - RNA_def_property_enum_items(prop, rna_enum_gpencil_brush_vertex_icons_items); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Grease Pencil Icon", ""); - /* Mode type. */ prop = RNA_def_property(srna, "vertex_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, nullptr, "vertex_mode"); @@ -1944,8 +1847,7 @@ static void rna_def_gpencil_options(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Mode", "Eraser Mode"); RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_GPENCIL); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update( - prop, NC_GPENCIL | ND_DATA, "rna_BrushGpencilSettings_eraser_mode_update"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, nullptr); prop = RNA_def_property(srna, "caps_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, nullptr, "caps_type"); -- 2.30.2 From 0d126f6547efac3b6f31f645fae4d4b6d7511840 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 3 May 2024 10:36:24 -0400 Subject: [PATCH 213/244] Cleanup: Remove unreachable assert Being so strict here isn't helpful, it's not hard to trigger this --- source/blender/editors/sculpt_paint/brush_asset_ops.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/source/blender/editors/sculpt_paint/brush_asset_ops.cc b/source/blender/editors/sculpt_paint/brush_asset_ops.cc index e51efc5f095..b606d33f026 100644 --- a/source/blender/editors/sculpt_paint/brush_asset_ops.cc +++ b/source/blender/editors/sculpt_paint/brush_asset_ops.cc @@ -162,7 +162,6 @@ static bool brush_asset_save_as_poll(bContext *C) if (!paint->brush_asset_reference) { /* The brush should always be an imported asset. We use this asset reference to find * which library and catalog the brush came from, as defaults for the popup. */ - BLI_assert_unreachable(); return false; } -- 2.30.2 From 360f082f8dc4bad3f2c93e3b4aa682dc3a050e2e Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 3 May 2024 11:06:24 -0400 Subject: [PATCH 214/244] Brush Assets: Switch from template ID to asset shelf popup Also move the old brush icon to a separate panel. Also make GPv2 modes work properly and mostly fixes GPv3, though I think there are problems with grease pencil itself for that too. --- .../startup/bl_ui/properties_paint_common.py | 34 ++--- scripts/startup/bl_ui/space_image.py | 14 +- .../startup/bl_ui/space_toolsystem_toolbar.py | 24 +++- scripts/startup/bl_ui/space_view3d.py | 109 ++++++++++++-- scripts/startup/bl_ui/space_view3d_toolbar.py | 135 ++---------------- 5 files changed, 158 insertions(+), 158 deletions(-) diff --git a/scripts/startup/bl_ui/properties_paint_common.py b/scripts/startup/bl_ui/properties_paint_common.py index ce35c2e3ee0..ac48c8c8a13 100644 --- a/scripts/startup/bl_ui/properties_paint_common.py +++ b/scripts/startup/bl_ui/properties_paint_common.py @@ -189,7 +189,7 @@ class BrushPanel(UnifiedPaintPanel): class BrushSelectPanel(BrushPanel): - bl_label = "Brushes" + bl_label = "Brush Asset" def draw(self, context): layout = self.layout @@ -197,9 +197,15 @@ class BrushSelectPanel(BrushPanel): brush = settings.brush 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", rows=3, cols=8, hide_buttons=True) + + preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 + fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' + row.column().template_asset_shelf_popover( + BrushAssetShelf.get_shelf_name_from_mode(context.object.mode), + name=brush.name if brush else None, + icon=fallback_icon, + icon_value=preview_icon_id, + ) if brush is None: return @@ -207,18 +213,6 @@ class BrushSelectPanel(BrushPanel): col = row.column() col.menu("VIEW3D_MT_brush_context_menu", icon='DOWNARROW_HLT', text="") - 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 - - col = layout.column(heading="Custom Icon", align=True) - row = col.row() - row.prop(brush, "use_custom_icon", text="") - sub = row.row() - sub.active = brush.use_custom_icon - sub.prop(brush, "icon_filepath", text="") - class ColorPalettePanel(BrushPanel): bl_label = "Color Palette" @@ -1205,6 +1199,14 @@ def brush_settings_advanced(layout, context, brush, popover=False): col.prop(brush, "use_paint_image", text="Texture Paint") col.prop(brush, "use_paint_sculpt_curves", text="Sculpt Curves") + if len(brush.icon_filepath) > 0: + header, panel = layout.panel("legacy", default_closed=True) + header.label(text="Legacy Icon") + if panel: + panel.label(text="Brush asset icons have moved to the asset preview", icon='ERROR') + panel.prop(brush, "use_custom_icon") + panel.prop(brush, "icon_filepath") + def draw_color_settings(context, layout, brush, color_type=False): """Draw color wheel and gradient settings.""" diff --git a/scripts/startup/bl_ui/space_image.py b/scripts/startup/bl_ui/space_image.py index 7338c88657d..05125b7d00d 100644 --- a/scripts/startup/bl_ui/space_image.py +++ b/scripts/startup/bl_ui/space_image.py @@ -765,9 +765,17 @@ class _draw_tool_settings_context_mode: return paint = context.tool_settings.image_paint - layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True) - brush = paint.brush + + preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 + fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' + layout.template_asset_shelf_popover( + BrushAssetShelf.get_shelf_name_from_mode(context.object.mode), + name=brush.name if brush else None, + icon=fallback_icon, + icon_value=preview_icon_id, + ) + if brush is None: return @@ -1183,7 +1191,7 @@ class IMAGE_PT_udim_tiles(Panel): class IMAGE_PT_paint_select(Panel, ImagePaintPanel, BrushSelectPanel): - bl_label = "Brushes" + bl_label = "Brush Asset" bl_context = ".paint_common_2d" bl_category = "Tool" diff --git a/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 4426c4e19db..6fb65e2cf6f 100644 --- a/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -18,7 +18,9 @@ from bl_ui.space_toolsystem_common import ( ToolSelectPanelHelper, ToolDef, ) - +from bl_ui.properties_paint_common import ( + BrushAssetShelf, +) from bpy.app.translations import pgettext_tip as tip_ @@ -1951,7 +1953,15 @@ class _defs_paint_grease_pencil: row = layout.row(align=True) tool_settings = context.scene.tool_settings settings = tool_settings.gpencil_paint - row.template_ID_preview(settings, "brush", rows=3, cols=8, hide_buttons=True) + + preview_icon_id = brush.preview.icon_id if brush.preview else 0 + fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' + row.template_asset_shelf_popover( + BrushAssetShelf.get_shelf_name_from_mode(context.object.mode), + name=brush.name, + icon=fallback_icon, + icon_value=preview_icon_id, + ) from bl_ui.properties_paint_common import ( brush_basic_grease_pencil_paint_settings, @@ -2333,7 +2343,15 @@ class _defs_gpencil_paint: row = layout.row(align=True) tool_settings = context.scene.tool_settings settings = tool_settings.gpencil_paint - row.template_ID_preview(settings, "brush", rows=3, cols=8, hide_buttons=True) + + preview_icon_id = brush.preview.icon_id if brush.preview else 0 + fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' + layout.template_asset_shelf_popover( + BrushAssetShelf.get_shelf_name_from_mode(context.object.mode), + name=brush.name, + icon=fallback_icon, + icon_value=preview_icon_id, + ) from bl_ui.properties_paint_common import ( brush_basic_gpencil_paint_settings, diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index 0433865489e..c24975e9969 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -306,9 +306,18 @@ class _draw_tool_settings_context_mode: return False paint = context.tool_settings.image_paint - layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True) - brush = paint.brush + + brush_name = brush.name if brush else None + preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 + fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' + layout.template_asset_shelf_popover( + BrushAssetShelf.get_shelf_name_from_mode(context.object.mode), + name=brush_name, + icon=fallback_icon, + icon_value=preview_icon_id, + ) + if brush is None: return False @@ -322,9 +331,18 @@ class _draw_tool_settings_context_mode: return False paint = context.tool_settings.vertex_paint - layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True) - brush = paint.brush + + brush_name = brush.name if brush else None + preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 + fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' + layout.template_asset_shelf_popover( + BrushAssetShelf.get_shelf_name_from_mode(context.object.mode), + name=brush_name, + icon=fallback_icon, + icon_value=preview_icon_id, + ) + if brush is None: return False @@ -338,8 +356,18 @@ class _draw_tool_settings_context_mode: return False paint = context.tool_settings.weight_paint - layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True) brush = paint.brush + + brush_name = brush.name if brush else None + preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 + fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' + layout.template_asset_shelf_popover( + BrushAssetShelf.get_shelf_name_from_mode(context.object.mode), + name=brush_name, + icon=fallback_icon, + icon_value=preview_icon_id, + ) + if brush is None: return False @@ -401,7 +429,15 @@ class _draw_tool_settings_context_mode: row = layout.row(align=True) settings = tool_settings.gpencil_paint - row.template_ID_preview(settings, "brush", rows=3, cols=8, hide_buttons=True) + + preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 + fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' + layout.template_asset_shelf_popover( + BrushAssetShelf.get_shelf_name_from_mode(context.object.mode), + name=brush.name, + icon=fallback_icon, + icon_value=preview_icon_id, + ) if ob and brush.gpencil_tool in {'FILL', 'DRAW'}: from bl_ui.properties_paint_common import ( @@ -429,6 +465,16 @@ class _draw_tool_settings_context_mode: paint = tool_settings.gpencil_sculpt_paint brush = paint.brush + brush_name = brush.name if brush else None + preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 + fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' + layout.template_asset_shelf_popover( + BrushAssetShelf.get_shelf_name_from_mode(context.object.mode), + name=brush_name, + icon=fallback_icon, + icon_value=preview_icon_id, + ) + from bl_ui.properties_paint_common import ( brush_basic_gpencil_sculpt_settings, ) @@ -442,12 +488,19 @@ class _draw_tool_settings_context_mode: return False paint = context.tool_settings.gpencil_sculpt_paint - layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True) - brush = paint.brush if brush is None: return False + preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 + fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' + layout.template_asset_shelf_popover( + BrushAssetShelf.get_shelf_name_from_mode(context.object.mode), + name=brush.name, + icon=fallback_icon, + icon_value=preview_icon_id, + ) + tool_settings = context.tool_settings capabilities = brush.sculpt_capabilities @@ -503,7 +556,14 @@ class _draw_tool_settings_context_mode: paint = tool_settings.gpencil_weight_paint brush = paint.brush - layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True) + preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 + fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' + layout.template_asset_shelf_popover( + BrushAssetShelf.get_shelf_name_from_mode(context.object.mode), + name=brush.name, + icon=fallback_icon, + icon_value=preview_icon_id, + ) brush_basic_gpencil_weight_settings(layout, context, brush, compact=True) @@ -518,12 +578,19 @@ class _draw_tool_settings_context_mode: return False paint = context.tool_settings.gpencil_weight_paint - layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True) - brush = paint.brush if brush is None: return False + preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 + fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' + layout.template_asset_shelf_popover( + BrushAssetShelf.get_shelf_name_from_mode(context.object.mode), + name=brush.name, + icon=fallback_icon, + icon_value=preview_icon_id, + ) + brush_basic_grease_pencil_weight_settings(layout, context, brush, compact=True) layout.popover("VIEW3D_PT_tools_grease_pencil_weight_options", text="Options") @@ -542,7 +609,15 @@ class _draw_tool_settings_context_mode: row = layout.row(align=True) settings = tool_settings.gpencil_vertex_paint - row.template_ID_preview(settings, "brush", rows=3, cols=8, hide_buttons=True) + + preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 + fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' + layout.template_asset_shelf_popover( + BrushAssetShelf.get_shelf_name_from_mode(context.object.mode), + name=brush.name, + icon=fallback_icon, + icon_value=preview_icon_id, + ) if brush.gpencil_vertex_tool not in {'BLUR', 'AVERAGE', 'SMEAR'}: row.separator(factor=0.4) @@ -695,7 +770,15 @@ class _draw_tool_settings_context_mode: return False row = layout.row(align=True) - row.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True) + + preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 + fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' + layout.template_asset_shelf_popover( + BrushAssetShelf.get_shelf_name_from_mode(context.object.mode), + name=brush.name, + icon=fallback_icon, + icon_value=preview_icon_id, + ) grease_pencil_tool = brush.gpencil_tool diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index bfd40264a8b..a43034ae346 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -28,6 +28,7 @@ from bl_ui.properties_paint_common import ( brush_settings, brush_settings_advanced, draw_color_settings, + BrushAssetShelf, ) from bl_ui.utils import PresetPanel @@ -83,8 +84,6 @@ class VIEW3D_MT_brush_gpencil_context_menu(Menu): layout.label(text="No Brushes currently available", icon='INFO') return - layout.operator("gpencil.brush_reset") - layout.operator("gpencil.brush_reset_all") class View3DPanel: @@ -355,7 +354,7 @@ class VIEW3D_PT_tools_particlemode(Panel, View3DPaintPanel): # TODO, move to space_view3d.py class VIEW3D_PT_tools_brush_select(Panel, View3DPaintBrushPanel, BrushSelectPanel): bl_context = ".paint_common" - bl_label = "Brushes" + bl_label = "Brush Asset" # TODO, move to space_view3d.py @@ -1599,30 +1598,8 @@ class GreasePencilPaintPanel: return True -class VIEW3D_PT_tools_grease_pencil_brush_select(Panel, View3DPanel, GreasePencilPaintPanel): - bl_label = "Brushes" - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - - tool_settings = context.scene.tool_settings - gpencil_paint = tool_settings.gpencil_paint - - row = layout.row() - row.column().template_ID_preview(gpencil_paint, "brush", new="brush.add_gpencil", rows=3, cols=8) - - col = row.column() - col.menu("VIEW3D_MT_brush_gpencil_context_menu", icon='DOWNARROW_HLT', text="") - - if context.mode == 'PAINT_GPENCIL': - brush = tool_settings.gpencil_paint.brush - if brush is not None: - col.prop(brush, "use_custom_icon", toggle=True, icon='FILE_IMAGE', text="") - - if brush.use_custom_icon: - layout.row().prop(brush, "icon_filepath", text="") +class VIEW3D_PT_tools_grease_pencil_brush_select(Panel, View3DPanel, GreasePencilPaintPanel, BrushSelectPanel): + bl_label = "Brush Asset" class VIEW3D_PT_tools_grease_pencil_brush_settings(Panel, View3DPanel, GreasePencilPaintPanel): @@ -2010,31 +1987,8 @@ class GreasePencilSculptPanel: return True -class VIEW3D_PT_tools_grease_pencil_sculpt_select(Panel, View3DPanel, GreasePencilSculptPanel): - bl_label = "Brushes" - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - - tool_settings = context.scene.tool_settings - gpencil_paint = tool_settings.gpencil_sculpt_paint - - row = layout.row() - row.column().template_ID_preview(gpencil_paint, "brush", new="brush.add_gpencil", rows=3, cols=8) - - col = row.column() - col.menu("VIEW3D_MT_brush_gpencil_context_menu", icon='DOWNARROW_HLT', text="") - - if context.mode == 'SCULPT_GPENCIL': - brush = tool_settings.gpencil_sculpt_paint.brush - if brush is not None: - col.prop(brush, "use_custom_icon", toggle=True, icon='FILE_IMAGE', text="") - - if (brush.use_custom_icon): - layout.row().prop(brush, "icon_filepath", text="") - +class VIEW3D_PT_tools_grease_pencil_sculpt_select(Panel, View3DPanel, GreasePencilSculptPanel, BrushSelectPanel): + bl_label = "Brush Asset" class VIEW3D_PT_tools_grease_pencil_sculpt_settings(Panel, View3DPanel, GreasePencilSculptPanel): bl_label = "Brush Settings" @@ -2122,30 +2076,8 @@ class GreasePencilWeightPanel: return True -class VIEW3D_PT_tools_grease_pencil_weight_paint_select(View3DPanel, Panel, GreasePencilWeightPanel): - bl_label = "Brushes" - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - - tool_settings = context.scene.tool_settings - gpencil_paint = tool_settings.gpencil_weight_paint - - row = layout.row() - row.column().template_ID_preview(gpencil_paint, "brush", new="brush.add_gpencil", rows=3, cols=8) - - col = row.column() - col.menu("VIEW3D_MT_brush_gpencil_context_menu", icon='DOWNARROW_HLT', text="") - - if context.mode in {'WEIGHT_GPENCIL', 'WEIGHT_GREASE_PENCIL'}: - brush = tool_settings.gpencil_weight_paint.brush - if brush is not None: - col.prop(brush, "use_custom_icon", toggle=True, icon='FILE_IMAGE', text="") - - if (brush.use_custom_icon): - layout.row().prop(brush, "icon_filepath", text="") +class VIEW3D_PT_tools_grease_pencil_weight_paint_select(View3DPanel, Panel, GreasePencilWeightPanel, BrushSelectPanel): + bl_label = "Brush Asset" class VIEW3D_PT_tools_grease_pencil_weight_paint_settings(Panel, View3DPanel, GreasePencilWeightPanel): @@ -2221,30 +2153,8 @@ class GreasePencilVertexPanel: return True -class VIEW3D_PT_tools_grease_pencil_vertex_paint_select(View3DPanel, Panel, GreasePencilVertexPanel): - bl_label = "Brushes" - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - - tool_settings = context.scene.tool_settings - gpencil_paint = tool_settings.gpencil_vertex_paint - - row = layout.row() - row.column().template_ID_preview(gpencil_paint, "brush", new="brush.add_gpencil", rows=3, cols=8) - - col = row.column() - col.menu("VIEW3D_MT_brush_gpencil_context_menu", icon='DOWNARROW_HLT', text="") - - if context.mode == 'VERTEX_GPENCIL': - brush = tool_settings.gpencil_vertex_paint.brush - if brush is not None: - col.prop(brush, "use_custom_icon", toggle=True, icon='FILE_IMAGE', text="") - - if (brush.use_custom_icon): - layout.row().prop(brush, "icon_filepath", text="") +class VIEW3D_PT_tools_grease_pencil_vertex_paint_select(View3DPanel, Panel, GreasePencilVertexPanel, BrushSelectPanel): + bl_label = "Brush Asset" class VIEW3D_PT_tools_grease_pencil_vertex_paint_settings(Panel, View3DPanel, GreasePencilVertexPanel): @@ -2516,29 +2426,8 @@ class GreasePencilV3PaintPanel: return True -class VIEW3D_PT_tools_grease_pencil_v3_brush_select(Panel, View3DPanel, GreasePencilV3PaintPanel): - bl_label = "Brushes" - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - - tool_settings = context.scene.tool_settings - gpencil_paint = tool_settings.gpencil_paint - - row = layout.row() - row.column().template_ID_preview(gpencil_paint, "brush", new="brush.add_gpencil", rows=3, cols=8) - - col = row.column() - col.menu("VIEW3D_MT_brush_gpencil_context_menu", icon='DOWNARROW_HLT', text="") - - brush = tool_settings.gpencil_paint.brush - if brush is not None: - col.prop(brush, "use_custom_icon", toggle=True, icon='FILE_IMAGE', text="") - - if brush.use_custom_icon: - layout.row().prop(brush, "icon_filepath", text="") +class VIEW3D_PT_tools_grease_pencil_v3_brush_select(Panel, View3DPanel, GreasePencilV3PaintPanel, BrushSelectPanel): + bl_label = "Brush Asset" class VIEW3D_PT_tools_grease_pencil_v3_brush_settings(Panel, View3DPanel, GreasePencilV3PaintPanel): -- 2.30.2 From b816d06c3d768c1be4fb512cb146692dc6652c6a Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 3 May 2024 11:08:02 -0400 Subject: [PATCH 215/244] Cleanup: Use references for asset shelf popover function --- source/blender/editors/include/UI_interface_c.hh | 2 +- .../interface_template_asset_shelf_popover.cc | 12 ++++++------ source/blender/makesrna/intern/rna_ui_api.cc | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/source/blender/editors/include/UI_interface_c.hh b/source/blender/editors/include/UI_interface_c.hh index 01246e914fc..90183fa0037 100644 --- a/source/blender/editors/include/UI_interface_c.hh +++ b/source/blender/editors/include/UI_interface_c.hh @@ -2722,7 +2722,7 @@ void uiTemplateAssetView(uiLayout *layout, namespace blender::ui { void template_asset_shelf_popover( - uiLayout *layout, const bContext *C, StringRefNull asset_shelf_id, StringRef name, int icon); + uiLayout &layout, const bContext &C, StringRefNull asset_shelf_id, StringRef name, int icon); } diff --git a/source/blender/editors/interface/templates/interface_template_asset_shelf_popover.cc b/source/blender/editors/interface/templates/interface_template_asset_shelf_popover.cc index 830a91d56b7..39525c3a16f 100644 --- a/source/blender/editors/interface/templates/interface_template_asset_shelf_popover.cc +++ b/source/blender/editors/interface/templates/interface_template_asset_shelf_popover.cc @@ -25,13 +25,13 @@ static uiBlock *asset_shelf_block_fn(bContext *C, ARegion *region, void *arg_she return ed::asset::shelf::popup_block_create(C, region, shelf_type); } -void template_asset_shelf_popover(uiLayout *layout, - const bContext *C, +void template_asset_shelf_popover(uiLayout &layout, + const bContext &C, const StringRefNull asset_shelf_id, const StringRef name, const BIFIconID icon) { - const ScrArea *area = CTX_wm_area(C); + const ScrArea *area = CTX_wm_area(&C); AssetShelfType *shelf_type = ed::asset::shelf::type_find_from_idname(*area->type, asset_shelf_id); if (!shelf_type) { @@ -39,7 +39,7 @@ void template_asset_shelf_popover(uiLayout *layout, return; } - const ARegion *region = CTX_wm_region(C); + const ARegion *region = CTX_wm_region(&C); const bool use_big_size = !RGN_TYPE_IS_HEADER_ANY(region->regiontype); const short width = [&]() -> short { if (use_big_size) { @@ -49,13 +49,13 @@ void template_asset_shelf_popover(uiLayout *layout, }(); const short height = UI_UNIT_Y * (use_big_size ? 6 : 1); - uiBlock *block = uiLayoutGetBlock(layout); + uiBlock *block = uiLayoutGetBlock(&layout); uiBut *but = uiDefBlockBut( block, asset_shelf_block_fn, shelf_type, name, 0, 0, width, height, "Select an asset"); ui_def_but_icon(but, icon, UI_HAS_ICON); UI_but_drawflag_enable(but, UI_BUT_ICON_LEFT); - if (ed::asset::shelf::type_poll(*C, *area->type, shelf_type) == false) { + if (ed::asset::shelf::type_poll(C, *area->type, shelf_type) == false) { UI_but_flag_enable(but, UI_BUT_DISABLED); } } diff --git a/source/blender/makesrna/intern/rna_ui_api.cc b/source/blender/makesrna/intern/rna_ui_api.cc index 09ee4d6d7a2..b0f260e0834 100644 --- a/source/blender/makesrna/intern/rna_ui_api.cc +++ b/source/blender/makesrna/intern/rna_ui_api.cc @@ -974,7 +974,7 @@ void rna_uiTemplateAssetShelfPopover(uiLayout *layout, icon = icon_value; } - blender::ui::template_asset_shelf_popover(layout, C, asset_shelf_id, name, icon); + blender::ui::template_asset_shelf_popover(*layout, *C, asset_shelf_id, name, icon); } #else -- 2.30.2 From 80822ea0206b9c697b6b557f16341d2f97266573 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 3 May 2024 11:08:55 -0400 Subject: [PATCH 216/244] UI: Clarify warning text about asset previews --- scripts/startup/bl_ui/properties_paint_common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/startup/bl_ui/properties_paint_common.py b/scripts/startup/bl_ui/properties_paint_common.py index ac48c8c8a13..2903113801e 100644 --- a/scripts/startup/bl_ui/properties_paint_common.py +++ b/scripts/startup/bl_ui/properties_paint_common.py @@ -1203,7 +1203,7 @@ def brush_settings_advanced(layout, context, brush, popover=False): header, panel = layout.panel("legacy", default_closed=True) header.label(text="Legacy Icon") if panel: - panel.label(text="Brush asset icons have moved to the asset preview", icon='ERROR') + panel.label(text="Brush icons have moved to the asset preview image", icon='ERROR') panel.prop(brush, "use_custom_icon") panel.prop(brush, "icon_filepath") -- 2.30.2 From 0d2fbe77923b758df4aacaab15089c79b8439dfc Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 3 May 2024 17:43:31 +0200 Subject: [PATCH 217/244] Fix preview icon drawing of asset shelf button in sidebar Text still needs fixing --- .../interface_template_asset_shelf_popover.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/interface/templates/interface_template_asset_shelf_popover.cc b/source/blender/editors/interface/templates/interface_template_asset_shelf_popover.cc index 39525c3a16f..371aabc5f7d 100644 --- a/source/blender/editors/interface/templates/interface_template_asset_shelf_popover.cc +++ b/source/blender/editors/interface/templates/interface_template_asset_shelf_popover.cc @@ -41,6 +41,7 @@ void template_asset_shelf_popover(uiLayout &layout, const ARegion *region = CTX_wm_region(&C); const bool use_big_size = !RGN_TYPE_IS_HEADER_ANY(region->regiontype); + const bool use_preview_icon = use_big_size; const short width = [&]() -> short { if (use_big_size) { return UI_UNIT_X * 6; @@ -52,8 +53,13 @@ void template_asset_shelf_popover(uiLayout &layout, uiBlock *block = uiLayoutGetBlock(&layout); uiBut *but = uiDefBlockBut( block, asset_shelf_block_fn, shelf_type, name, 0, 0, width, height, "Select an asset"); - ui_def_but_icon(but, icon, UI_HAS_ICON); - UI_but_drawflag_enable(but, UI_BUT_ICON_LEFT); + if (use_preview_icon) { + ui_def_but_icon(but, icon, UI_HAS_ICON | UI_BUT_ICON_PREVIEW); + } + else { + ui_def_but_icon(but, icon, UI_HAS_ICON); + UI_but_drawflag_enable(but, UI_BUT_ICON_LEFT); + } if (ed::asset::shelf::type_poll(C, *area->type, shelf_type) == false) { UI_but_flag_enable(but, UI_BUT_DISABLED); -- 2.30.2 From be6acd64610c57afef53fd69053f0dde094c2792 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 3 May 2024 18:07:45 +0200 Subject: [PATCH 218/244] Show brush name text field in sidebar --- scripts/startup/bl_ui/properties_paint_common.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/startup/bl_ui/properties_paint_common.py b/scripts/startup/bl_ui/properties_paint_common.py index 2903113801e..ba796c59e55 100644 --- a/scripts/startup/bl_ui/properties_paint_common.py +++ b/scripts/startup/bl_ui/properties_paint_common.py @@ -198,14 +198,16 @@ class BrushSelectPanel(BrushPanel): row = layout.row() + col = row.column(align=True) preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' - row.column().template_asset_shelf_popover( + col.template_asset_shelf_popover( BrushAssetShelf.get_shelf_name_from_mode(context.object.mode), - name=brush.name if brush else None, icon=fallback_icon, icon_value=preview_icon_id, ) + if brush: + col.prop(brush, "name", text="") if brush is None: return -- 2.30.2 From 0978d048530db9e32a7e9a970e22ab75a6fdefed Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 3 May 2024 11:44:55 -0400 Subject: [PATCH 219/244] Brush Assets: Remove unnecessary "ensure brush" versioning code These local brushes aren't necessary anymore, assets are used instead. --- .../blenloader/intern/versioning_defaults.cc | 140 +----------------- 1 file changed, 4 insertions(+), 136 deletions(-) diff --git a/source/blender/blenloader/intern/versioning_defaults.cc b/source/blender/blenloader/intern/versioning_defaults.cc index cd0e3de9dd9..ff0bf4f6fe4 100644 --- a/source/blender/blenloader/intern/versioning_defaults.cc +++ b/source/blender/blenloader/intern/versioning_defaults.cc @@ -735,143 +735,11 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) brush->input_samples = 1; } + /* Change the spacing of the Smear brush to 3.0% */ + if (Brush *brush = static_cast( + BLI_findstring(&bmain->brushes, "Smear", offsetof(ID, name) + 2))) { - /* Change the spacing of the Smear brush to 3.0% */ - const char *brush_name; - Brush *brush; - - brush_name = "Smear"; - brush = static_cast( - BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2)); - if (brush) { - brush->spacing = 3.0; - } - - brush_name = "Draw Sharp"; - brush = static_cast( - BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2)); - if (!brush) { - brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT); - id_us_min(&brush->id); - brush->sculpt_tool = SCULPT_TOOL_DRAW_SHARP; - } - - brush_name = "Elastic Deform"; - brush = static_cast( - BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2)); - if (!brush) { - brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT); - id_us_min(&brush->id); - brush->sculpt_tool = SCULPT_TOOL_ELASTIC_DEFORM; - } - - brush_name = "Pose"; - brush = static_cast( - BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2)); - if (!brush) { - brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT); - id_us_min(&brush->id); - brush->sculpt_tool = SCULPT_TOOL_POSE; - } - - brush_name = "Multi-plane Scrape"; - brush = static_cast( - BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2)); - if (!brush) { - brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT); - id_us_min(&brush->id); - brush->sculpt_tool = SCULPT_TOOL_MULTIPLANE_SCRAPE; - } - - brush_name = "Clay Thumb"; - brush = static_cast( - BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2)); - if (!brush) { - brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT); - id_us_min(&brush->id); - brush->sculpt_tool = SCULPT_TOOL_CLAY_THUMB; - } - - brush_name = "Cloth"; - brush = static_cast( - BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2)); - if (!brush) { - brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT); - id_us_min(&brush->id); - brush->sculpt_tool = SCULPT_TOOL_CLOTH; - } - - brush_name = "Slide Relax"; - brush = static_cast( - BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2)); - if (!brush) { - brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT); - id_us_min(&brush->id); - brush->sculpt_tool = SCULPT_TOOL_SLIDE_RELAX; - } - - brush_name = "Paint"; - brush = static_cast( - BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2)); - if (!brush) { - brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT); - id_us_min(&brush->id); - brush->sculpt_tool = SCULPT_TOOL_PAINT; - } - - brush_name = "Smear"; - brush = static_cast( - BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2)); - if (!brush) { - brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT); - id_us_min(&brush->id); - brush->sculpt_tool = SCULPT_TOOL_SMEAR; - } - - brush_name = "Boundary"; - brush = static_cast( - BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2)); - if (!brush) { - brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT); - id_us_min(&brush->id); - brush->sculpt_tool = SCULPT_TOOL_BOUNDARY; - } - - brush_name = "Simplify"; - brush = static_cast( - BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2)); - if (!brush) { - brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT); - id_us_min(&brush->id); - brush->sculpt_tool = SCULPT_TOOL_SIMPLIFY; - } - - brush_name = "Draw Face Sets"; - brush = static_cast( - BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2)); - if (!brush) { - brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT); - id_us_min(&brush->id); - brush->sculpt_tool = SCULPT_TOOL_DRAW_FACE_SETS; - } - - brush_name = "Multires Displacement Eraser"; - brush = static_cast( - BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2)); - if (!brush) { - brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT); - id_us_min(&brush->id); - brush->sculpt_tool = SCULPT_TOOL_DISPLACEMENT_ERASER; - } - - brush_name = "Multires Displacement Smear"; - brush = static_cast( - BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2)); - if (!brush) { - brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT); - id_us_min(&brush->id); - brush->sculpt_tool = SCULPT_TOOL_DISPLACEMENT_SMEAR; - } + brush->spacing = 3.0; } { -- 2.30.2 From cc18a5da17d248bb2541a7c0409da36c3998cd28 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 3 May 2024 12:20:53 -0400 Subject: [PATCH 220/244] Brush Assets: Remove unnecessary addition of local brushes We can assume all the necessary brushes exist in the essentials library. Local brushes should never really be necessary for a typical user-- we always edit them directly in their asset libraries. --- source/blender/blenkernel/BKE_brush.hh | 22 - source/blender/blenkernel/intern/brush.cc | 1040 +---------------- .../blenloader/intern/versioning_defaults.cc | 13 - .../editors/gpencil_legacy/gpencil_edit.cc | 17 +- .../editors/gpencil_legacy/gpencil_merge.cc | 7 +- .../editors/gpencil_legacy/gpencil_paint.cc | 96 -- .../gpencil_legacy/gpencil_primitive.cc | 6 +- .../editors/gpencil_legacy/gpencil_utils.cc | 7 - 8 files changed, 15 insertions(+), 1193 deletions(-) diff --git a/source/blender/blenkernel/BKE_brush.hh b/source/blender/blenkernel/BKE_brush.hh index 48502674e84..e0e4289c670 100644 --- a/source/blender/blenkernel/BKE_brush.hh +++ b/source/blender/blenkernel/BKE_brush.hh @@ -38,10 +38,6 @@ void BKE_brush_system_exit(); * another is assumed to be used by the caller. */ Brush *BKE_brush_add(Main *bmain, const char *name, eObjectMode ob_mode); -/** - * Add a new gp-brush. - */ -Brush *BKE_brush_add_gpencil(Main *bmain, ToolSettings *ts, const char *name, eObjectMode mode); /** * Delete a Brush. */ @@ -57,24 +53,6 @@ Brush *BKE_brush_first_search(Main *bmain, eObjectMode ob_mode); void BKE_brush_sculpt_reset(Brush *brush); -/** - * Create a set of grease pencil Drawing presets. - */ -void BKE_brush_gpencil_paint_presets(Main *bmain, ToolSettings *ts, bool reset); -/** - * Create a set of grease pencil Vertex Paint presets. - */ -void BKE_brush_gpencil_vertex_presets(Main *bmain, ToolSettings *ts, bool reset); -/** - * Create a set of grease pencil Sculpt Paint presets. - */ -void BKE_brush_gpencil_sculpt_presets(Main *bmain, ToolSettings *ts, bool reset); -/** - * Create a set of grease pencil Weight Paint presets. - */ -void BKE_brush_gpencil_weight_presets(Main *bmain, ToolSettings *ts, bool reset); -void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, short type); - void BKE_brush_jitter_pos(const Scene *scene, Brush *brush, const float pos[2], diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index 6ec8696212c..0339db23902 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -534,6 +534,14 @@ Brush *BKE_brush_add(Main *bmain, const char *name, const eObjectMode ob_mode) if (ob_mode == OB_MODE_SCULPT_CURVES) { BKE_brush_init_curves_sculpt_settings(brush); } + else if (ELEM(ob_mode, + OB_MODE_PAINT_GPENCIL_LEGACY, + OB_MODE_SCULPT_GPENCIL_LEGACY, + OB_MODE_WEIGHT_GPENCIL_LEGACY, + OB_MODE_VERTEX_GPENCIL_LEGACY)) + { + BKE_brush_init_gpencil_settings(brush); + } return brush; } @@ -564,45 +572,6 @@ void BKE_brush_init_gpencil_settings(Brush *brush) brush->gpencil_settings->curve_rand_value = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); } -Brush *BKE_brush_add_gpencil(Main *bmain, ToolSettings *ts, const char *name, eObjectMode mode) -{ - Paint *paint = nullptr; - Brush *brush; - switch (mode) { - case OB_MODE_PAINT_GPENCIL_LEGACY: { - paint = &ts->gp_paint->paint; - break; - } - case OB_MODE_SCULPT_GPENCIL_LEGACY: { - paint = &ts->gp_sculptpaint->paint; - break; - } - case OB_MODE_WEIGHT_GPENCIL_LEGACY: { - paint = &ts->gp_weightpaint->paint; - break; - } - case OB_MODE_VERTEX_GPENCIL_LEGACY: { - paint = &ts->gp_vertexpaint->paint; - break; - } - default: - paint = &ts->gp_paint->paint; - } - - brush = BKE_brush_add(bmain, name, mode); - - BKE_paint_brush_set(paint, brush); - id_us_min(&brush->id); - - brush->size = 3; - - /* grease pencil basic settings */ - BKE_brush_init_gpencil_settings(brush); - - /* return brush */ - return brush; -} - bool BKE_brush_delete(Main *bmain, Brush *brush) { if (brush->id.tag & LIB_TAG_INDIRECT) { @@ -619,999 +588,6 @@ bool BKE_brush_delete(Main *bmain, Brush *brush) return true; } -/** Local grease pencil curve mapping preset. */ -using eGPCurveMappingPreset = enum eGPCurveMappingPreset { - GPCURVE_PRESET_PENCIL = 0, - GPCURVE_PRESET_INK = 1, - GPCURVE_PRESET_INKNOISE = 2, - GPCURVE_PRESET_MARKER = 3, - GPCURVE_PRESET_CHISEL_SENSIVITY = 4, - GPCURVE_PRESET_CHISEL_STRENGTH = 5, -}; - -static void brush_gpencil_curvemap_reset(CurveMap *cuma, int tot, eGPCurveMappingPreset preset) -{ - if (cuma->curve) { - MEM_freeN(cuma->curve); - } - - cuma->totpoint = tot; - cuma->curve = (CurveMapPoint *)MEM_callocN(cuma->totpoint * sizeof(CurveMapPoint), __func__); - - switch (preset) { - case GPCURVE_PRESET_PENCIL: - cuma->curve[0].x = 0.0f; - cuma->curve[0].y = 0.0f; - cuma->curve[1].x = 0.75115f; - cuma->curve[1].y = 0.25f; - cuma->curve[2].x = 1.0f; - cuma->curve[2].y = 1.0f; - break; - case GPCURVE_PRESET_INK: - cuma->curve[0].x = 0.0f; - cuma->curve[0].y = 0.0f; - cuma->curve[1].x = 0.63448f; - cuma->curve[1].y = 0.375f; - cuma->curve[2].x = 1.0f; - cuma->curve[2].y = 1.0f; - break; - case GPCURVE_PRESET_INKNOISE: - cuma->curve[0].x = 0.0f; - cuma->curve[0].y = 0.0f; - cuma->curve[1].x = 0.55f; - cuma->curve[1].y = 0.45f; - cuma->curve[2].x = 0.85f; - cuma->curve[2].y = 1.0f; - break; - case GPCURVE_PRESET_MARKER: - cuma->curve[0].x = 0.0f; - cuma->curve[0].y = 0.0f; - cuma->curve[1].x = 0.38f; - cuma->curve[1].y = 0.22f; - cuma->curve[2].x = 0.65f; - cuma->curve[2].y = 0.68f; - cuma->curve[3].x = 1.0f; - cuma->curve[3].y = 1.0f; - break; - case GPCURVE_PRESET_CHISEL_SENSIVITY: - cuma->curve[0].x = 0.0f; - cuma->curve[0].y = 0.0f; - cuma->curve[1].x = 0.25f; - cuma->curve[1].y = 0.40f; - cuma->curve[2].x = 1.0f; - cuma->curve[2].y = 1.0f; - break; - case GPCURVE_PRESET_CHISEL_STRENGTH: - cuma->curve[0].x = 0.0f; - cuma->curve[0].y = 0.0f; - cuma->curve[1].x = 0.31f; - cuma->curve[1].y = 0.22f; - cuma->curve[2].x = 0.61f; - cuma->curve[2].y = 0.88f; - cuma->curve[3].x = 1.0f; - cuma->curve[3].y = 1.0f; - break; - default: - break; - } - - MEM_SAFE_FREE(cuma->table); -} - -void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) -{ -#define SMOOTH_STROKE_RADIUS 40 -#define SMOOTH_STROKE_FACTOR 0.9f -#define ACTIVE_SMOOTH 0.35f - - CurveMapping *custom_curve = nullptr; - - /* Optionally assign a material preset. */ - enum { - PRESET_MATERIAL_NONE = 0, - PRESET_MATERIAL_DOT_STROKE, - } material_preset = PRESET_MATERIAL_NONE; - - /* Set general defaults at brush level. */ - brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS; - brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR; - - brush->rgb[0] = 0.498f; - brush->rgb[1] = 1.0f; - brush->rgb[2] = 0.498f; - - brush->secondary_rgb[0] = 1.0f; - brush->secondary_rgb[1] = 1.0f; - brush->secondary_rgb[2] = 1.0f; - - brush->curve_preset = BRUSH_CURVE_SMOOTH; - - if (brush->gpencil_settings == nullptr) { - return; - } - - /* Set preset type. */ - brush->gpencil_settings->preset_type = type; - - /* Set vertex mix factor. */ - brush->gpencil_settings->vertex_mode = GPPAINT_MODE_BOTH; - brush->gpencil_settings->vertex_factor = 1.0f; - brush->gpencil_settings->material_alt = nullptr; - - switch (type) { - case GP_BRUSH_PRESET_AIRBRUSH: { - brush->size = 300.0f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - - brush->gpencil_settings->draw_strength = 0.4f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_STRENGTH_PRESSURE; - - brush->gpencil_settings->input_samples = 10; - brush->gpencil_settings->active_smooth = ACTIVE_SMOOTH; - brush->gpencil_settings->draw_angle = 0.0f; - brush->gpencil_settings->draw_angle_factor = 0.0f; - brush->gpencil_settings->hardness = 0.9f; - copy_v2_fl(brush->gpencil_settings->aspect_ratio, 1.0f); - - brush->gpencil_tool = GPAINT_TOOL_DRAW; - - zero_v3(brush->secondary_rgb); - - material_preset = PRESET_MATERIAL_DOT_STROKE; - - break; - } - case GP_BRUSH_PRESET_INK_PEN: { - - brush->size = 60.0f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - - brush->gpencil_settings->draw_strength = 1.0f; - - brush->gpencil_settings->input_samples = 10; - brush->gpencil_settings->active_smooth = ACTIVE_SMOOTH; - brush->gpencil_settings->draw_angle = 0.0f; - brush->gpencil_settings->draw_angle_factor = 0.0f; - brush->gpencil_settings->hardness = 1.0f; - copy_v2_fl(brush->gpencil_settings->aspect_ratio, 1.0f); - - brush->gpencil_settings->flag |= GP_BRUSH_GROUP_SETTINGS; - brush->gpencil_settings->draw_smoothfac = 0.1f; - brush->gpencil_settings->draw_smoothlvl = 1; - brush->gpencil_settings->draw_subdivide = 0; - brush->gpencil_settings->simplify_f = 0.002f; - - brush->gpencil_settings->draw_random_press = 0.0f; - brush->gpencil_settings->draw_jitter = 0.0f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE; - - /* Curve. */ - custom_curve = brush->gpencil_settings->curve_sensitivity; - BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f, HD_AUTO); - BKE_curvemapping_init(custom_curve); - brush_gpencil_curvemap_reset(custom_curve->cm, 3, GPCURVE_PRESET_INK); - - brush->gpencil_tool = GPAINT_TOOL_DRAW; - - zero_v3(brush->secondary_rgb); - break; - } - case GP_BRUSH_PRESET_INK_PEN_ROUGH: { - brush->size = 60.0f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - - brush->gpencil_settings->draw_strength = 1.0f; - - brush->gpencil_settings->input_samples = 10; - brush->gpencil_settings->active_smooth = ACTIVE_SMOOTH; - brush->gpencil_settings->draw_angle = 0.0f; - brush->gpencil_settings->draw_angle_factor = 0.0f; - brush->gpencil_settings->hardness = 1.0f; - copy_v2_fl(brush->gpencil_settings->aspect_ratio, 1.0f); - - brush->gpencil_settings->flag &= ~GP_BRUSH_GROUP_SETTINGS; - brush->gpencil_settings->draw_smoothfac = 0.0f; - brush->gpencil_settings->draw_smoothlvl = 2; - brush->gpencil_settings->draw_subdivide = 0; - brush->gpencil_settings->simplify_f = 0.000f; - - brush->gpencil_settings->flag |= GP_BRUSH_GROUP_RANDOM; - brush->gpencil_settings->draw_random_press = 0.6f; - brush->gpencil_settings->draw_random_strength = 0.0f; - brush->gpencil_settings->draw_jitter = 0.0f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE; - - /* Curve. */ - custom_curve = brush->gpencil_settings->curve_sensitivity; - BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f, HD_AUTO); - BKE_curvemapping_init(custom_curve); - brush_gpencil_curvemap_reset(custom_curve->cm, 3, GPCURVE_PRESET_INKNOISE); - - brush->gpencil_tool = GPAINT_TOOL_DRAW; - - zero_v3(brush->secondary_rgb); - break; - } - case GP_BRUSH_PRESET_MARKER_BOLD: { - brush->size = 150.0f; - brush->gpencil_settings->flag &= ~GP_BRUSH_USE_PRESSURE; - - brush->gpencil_settings->draw_strength = 0.3f; - - brush->gpencil_settings->input_samples = 10; - brush->gpencil_settings->active_smooth = ACTIVE_SMOOTH; - brush->gpencil_settings->draw_angle = 0.0f; - brush->gpencil_settings->draw_angle_factor = 0.0f; - brush->gpencil_settings->hardness = 1.0f; - copy_v2_fl(brush->gpencil_settings->aspect_ratio, 1.0f); - - brush->gpencil_settings->flag |= GP_BRUSH_GROUP_SETTINGS; - brush->gpencil_settings->draw_smoothfac = 0.1f; - brush->gpencil_settings->draw_smoothlvl = 1; - brush->gpencil_settings->draw_subdivide = 0; - brush->gpencil_settings->simplify_f = 0.002f; - - brush->gpencil_settings->flag &= ~GP_BRUSH_GROUP_RANDOM; - brush->gpencil_settings->draw_random_press = 0.0f; - brush->gpencil_settings->draw_random_strength = 0.0f; - brush->gpencil_settings->draw_jitter = 0.0f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE; - - /* Curve. */ - custom_curve = brush->gpencil_settings->curve_sensitivity; - BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f, HD_AUTO); - BKE_curvemapping_init(custom_curve); - brush_gpencil_curvemap_reset(custom_curve->cm, 4, GPCURVE_PRESET_MARKER); - - brush->gpencil_tool = GPAINT_TOOL_DRAW; - - zero_v3(brush->secondary_rgb); - break; - } - case GP_BRUSH_PRESET_MARKER_CHISEL: { - brush->size = 150.0f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - - brush->gpencil_settings->draw_strength = 1.0f; - - brush->gpencil_settings->input_samples = 10; - brush->gpencil_settings->active_smooth = 0.3f; - brush->gpencil_settings->draw_angle = DEG2RAD(35.0f); - brush->gpencil_settings->draw_angle_factor = 0.5f; - brush->gpencil_settings->hardness = 1.0f; - copy_v2_fl(brush->gpencil_settings->aspect_ratio, 1.0f); - - brush->gpencil_settings->flag |= GP_BRUSH_GROUP_SETTINGS; - brush->gpencil_settings->draw_smoothfac = 0.0f; - brush->gpencil_settings->draw_smoothlvl = 1; - brush->gpencil_settings->draw_subdivide = 0; - brush->gpencil_settings->simplify_f = 0.002f; - - brush->gpencil_settings->flag &= ~GP_BRUSH_GROUP_RANDOM; - brush->gpencil_settings->draw_random_press = 0.0f; - brush->gpencil_settings->draw_jitter = 0.0f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE; - - /* Curve. */ - custom_curve = brush->gpencil_settings->curve_sensitivity; - BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f, HD_AUTO); - BKE_curvemapping_init(custom_curve); - brush_gpencil_curvemap_reset(custom_curve->cm, 3, GPCURVE_PRESET_CHISEL_SENSIVITY); - - custom_curve = brush->gpencil_settings->curve_strength; - BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f, HD_AUTO); - BKE_curvemapping_init(custom_curve); - brush_gpencil_curvemap_reset(custom_curve->cm, 4, GPCURVE_PRESET_CHISEL_STRENGTH); - - brush->gpencil_tool = GPAINT_TOOL_DRAW; - - zero_v3(brush->secondary_rgb); - break; - } - case GP_BRUSH_PRESET_PEN: { - brush->size = 25.0f; - brush->gpencil_settings->flag &= ~GP_BRUSH_USE_PRESSURE; - - brush->gpencil_settings->draw_strength = 1.0f; - brush->gpencil_settings->flag &= ~GP_BRUSH_USE_STRENGTH_PRESSURE; - - brush->gpencil_settings->input_samples = 10; - brush->gpencil_settings->active_smooth = ACTIVE_SMOOTH; - brush->gpencil_settings->draw_angle = 0.0f; - brush->gpencil_settings->draw_angle_factor = 0.0f; - brush->gpencil_settings->hardness = 1.0f; - copy_v2_fl(brush->gpencil_settings->aspect_ratio, 1.0f); - - brush->gpencil_settings->flag |= GP_BRUSH_GROUP_SETTINGS; - brush->gpencil_settings->draw_smoothfac = 0.0f; - brush->gpencil_settings->draw_smoothlvl = 1; - brush->gpencil_settings->draw_subdivide = 1; - brush->gpencil_settings->simplify_f = 0.002f; - - brush->gpencil_settings->draw_random_press = 0.0f; - brush->gpencil_settings->draw_random_strength = 0.0f; - brush->gpencil_settings->draw_jitter = 0.0f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE; - - brush->gpencil_tool = GPAINT_TOOL_DRAW; - - zero_v3(brush->secondary_rgb); - break; - } - case GP_BRUSH_PRESET_PENCIL_SOFT: { - brush->size = 80.0f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - - brush->gpencil_settings->draw_strength = 0.4f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_STRENGTH_PRESSURE; - - brush->gpencil_settings->input_samples = 10; - brush->gpencil_settings->active_smooth = ACTIVE_SMOOTH; - brush->gpencil_settings->draw_angle = 0.0f; - brush->gpencil_settings->draw_angle_factor = 0.0f; - brush->gpencil_settings->hardness = 0.8f; - copy_v2_fl(brush->gpencil_settings->aspect_ratio, 1.0f); - - brush->gpencil_settings->flag |= GP_BRUSH_GROUP_SETTINGS; - brush->gpencil_settings->draw_smoothfac = 0.0f; - brush->gpencil_settings->draw_smoothlvl = 1; - brush->gpencil_settings->draw_subdivide = 0; - brush->gpencil_settings->simplify_f = 0.000f; - - brush->gpencil_settings->draw_random_press = 0.0f; - brush->gpencil_settings->draw_random_strength = 0.0f; - brush->gpencil_settings->draw_jitter = 0.0f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE; - - brush->gpencil_tool = GPAINT_TOOL_DRAW; - - zero_v3(brush->secondary_rgb); - - material_preset = PRESET_MATERIAL_DOT_STROKE; - - break; - } - case GP_BRUSH_PRESET_PENCIL: { - brush->size = 20.0f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - - brush->gpencil_settings->draw_strength = 0.6f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_STRENGTH_PRESSURE; - - brush->gpencil_settings->input_samples = 10; - brush->gpencil_settings->active_smooth = ACTIVE_SMOOTH; - brush->gpencil_settings->draw_angle = 0.0f; - brush->gpencil_settings->draw_angle_factor = 0.0f; - brush->gpencil_settings->hardness = 1.0f; - copy_v2_fl(brush->gpencil_settings->aspect_ratio, 1.0f); - - brush->gpencil_settings->flag |= GP_BRUSH_GROUP_SETTINGS; - brush->gpencil_settings->draw_smoothfac = 0.0f; - brush->gpencil_settings->draw_smoothlvl = 1; - brush->gpencil_settings->draw_subdivide = 0; - brush->gpencil_settings->simplify_f = 0.002f; - - brush->gpencil_settings->draw_random_press = 0.0f; - brush->gpencil_settings->draw_jitter = 0.0f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE; - - brush->gpencil_tool = GPAINT_TOOL_DRAW; - - zero_v3(brush->secondary_rgb); - break; - } - case GP_BRUSH_PRESET_FILL_AREA: { - brush->size = 5.0f; - - brush->gpencil_settings->fill_threshold = 0.1f; - brush->gpencil_settings->fill_simplylvl = 1; - brush->gpencil_settings->fill_factor = 1.0f; - - brush->gpencil_settings->draw_strength = 1.0f; - brush->gpencil_settings->hardness = 1.0f; - copy_v2_fl(brush->gpencil_settings->aspect_ratio, 1.0f); - brush->gpencil_settings->draw_smoothfac = 0.1f; - brush->gpencil_settings->draw_smoothlvl = 1; - brush->gpencil_settings->draw_subdivide = 1; - brush->gpencil_settings->dilate_pixels = 1; - - brush->gpencil_settings->flag |= GP_BRUSH_FILL_SHOW_EXTENDLINES; - - brush->gpencil_tool = GPAINT_TOOL_FILL; - brush->gpencil_settings->vertex_mode = GPPAINT_MODE_FILL; - - zero_v3(brush->secondary_rgb); - break; - } - case GP_BRUSH_PRESET_ERASER_SOFT: { - brush->size = 30.0f; - brush->gpencil_settings->draw_strength = 0.5f; - brush->gpencil_settings->flag |= GP_BRUSH_DEFAULT_ERASER; - brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - brush->gpencil_settings->flag |= GP_BRUSH_USE_STRENGTH_PRESSURE; - brush->gpencil_tool = GPAINT_TOOL_ERASE; - brush->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_SOFT; - brush->gpencil_settings->era_strength_f = 100.0f; - brush->gpencil_settings->era_thickness_f = 10.0f; - - break; - } - case GP_BRUSH_PRESET_ERASER_HARD: { - brush->size = 30.0f; - brush->gpencil_settings->draw_strength = 1.0f; - brush->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_SOFT; - brush->gpencil_settings->era_strength_f = 100.0f; - brush->gpencil_settings->era_thickness_f = 50.0f; - - brush->gpencil_tool = GPAINT_TOOL_ERASE; - - break; - } - case GP_BRUSH_PRESET_ERASER_POINT: { - brush->size = 30.0f; - brush->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_HARD; - - brush->gpencil_tool = GPAINT_TOOL_ERASE; - - break; - } - case GP_BRUSH_PRESET_ERASER_STROKE: { - brush->size = 30.0f; - brush->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_STROKE; - - brush->gpencil_tool = GPAINT_TOOL_ERASE; - - break; - } - case GP_BRUSH_PRESET_TINT: { - brush->gpencil_tool = GPAINT_TOOL_TINT; - - brush->size = 25.0f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - - brush->gpencil_settings->draw_strength = 0.8f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_STRENGTH_PRESSURE; - - zero_v3(brush->secondary_rgb); - break; - } - case GP_BRUSH_PRESET_VERTEX_DRAW: { - brush->gpencil_vertex_tool = GPVERTEX_TOOL_DRAW; - - brush->size = 25.0f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - - brush->gpencil_settings->draw_strength = 0.8f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_STRENGTH_PRESSURE; - - zero_v3(brush->secondary_rgb); - break; - } - case GP_BRUSH_PRESET_VERTEX_BLUR: { - brush->gpencil_vertex_tool = GPVERTEX_TOOL_BLUR; - - brush->size = 25.0f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - - brush->gpencil_settings->draw_strength = 0.8f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_STRENGTH_PRESSURE; - - zero_v3(brush->secondary_rgb); - break; - } - case GP_BRUSH_PRESET_VERTEX_AVERAGE: { - brush->gpencil_vertex_tool = GPVERTEX_TOOL_AVERAGE; - - brush->size = 25.0f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - - brush->gpencil_settings->draw_strength = 0.8f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_STRENGTH_PRESSURE; - - zero_v3(brush->secondary_rgb); - break; - } - case GP_BRUSH_PRESET_VERTEX_SMEAR: { - brush->gpencil_vertex_tool = GPVERTEX_TOOL_SMEAR; - - brush->size = 25.0f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - - brush->gpencil_settings->draw_strength = 0.8f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_STRENGTH_PRESSURE; - - zero_v3(brush->secondary_rgb); - break; - } - case GP_BRUSH_PRESET_VERTEX_REPLACE: { - brush->gpencil_vertex_tool = GPVERTEX_TOOL_REPLACE; - - brush->size = 25.0f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - - brush->gpencil_settings->draw_strength = 0.8f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_STRENGTH_PRESSURE; - - zero_v3(brush->secondary_rgb); - break; - } - case GP_BRUSH_PRESET_SMOOTH_STROKE: { - brush->gpencil_sculpt_tool = GPSCULPT_TOOL_SMOOTH; - - brush->size = 25.0f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - - brush->gpencil_settings->draw_strength = 0.3f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_STRENGTH_PRESSURE; - brush->gpencil_settings->sculpt_mode_flag |= GP_SCULPT_FLAGMODE_APPLY_POSITION; - - break; - } - case GP_BRUSH_PRESET_STRENGTH_STROKE: { - brush->gpencil_sculpt_tool = GPSCULPT_TOOL_STRENGTH; - - brush->size = 25.0f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - - brush->gpencil_settings->draw_strength = 0.3f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_STRENGTH_PRESSURE; - brush->gpencil_settings->sculpt_mode_flag |= GP_SCULPT_FLAGMODE_APPLY_POSITION; - - break; - } - case GP_BRUSH_PRESET_THICKNESS_STROKE: { - brush->gpencil_sculpt_tool = GPSCULPT_TOOL_THICKNESS; - - brush->size = 25.0f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - - brush->gpencil_settings->draw_strength = 0.5f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_STRENGTH_PRESSURE; - brush->gpencil_settings->sculpt_mode_flag |= GP_SCULPT_FLAGMODE_APPLY_POSITION; - - break; - } - case GP_BRUSH_PRESET_GRAB_STROKE: { - brush->gpencil_sculpt_tool = GPSCULPT_TOOL_GRAB; - brush->gpencil_settings->flag &= ~GP_BRUSH_USE_PRESSURE; - - brush->size = 25.0f; - - brush->gpencil_settings->draw_strength = 0.3f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_STRENGTH_PRESSURE; - brush->gpencil_settings->sculpt_mode_flag |= GP_SCULPT_FLAGMODE_APPLY_POSITION; - - break; - } - case GP_BRUSH_PRESET_PUSH_STROKE: { - brush->gpencil_sculpt_tool = GPSCULPT_TOOL_PUSH; - - brush->size = 25.0f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - - brush->gpencil_settings->draw_strength = 0.3f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_STRENGTH_PRESSURE; - brush->gpencil_settings->sculpt_mode_flag |= GP_SCULPT_FLAGMODE_APPLY_POSITION; - - break; - } - case GP_BRUSH_PRESET_TWIST_STROKE: { - brush->gpencil_sculpt_tool = GPSCULPT_TOOL_TWIST; - - brush->size = 50.0f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - - brush->gpencil_settings->draw_strength = 0.3f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_STRENGTH_PRESSURE; - brush->gpencil_settings->sculpt_mode_flag |= GP_SCULPT_FLAGMODE_APPLY_POSITION; - - break; - } - case GP_BRUSH_PRESET_PINCH_STROKE: { - brush->gpencil_sculpt_tool = GPSCULPT_TOOL_PINCH; - - brush->size = 50.0f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - - brush->gpencil_settings->draw_strength = 0.5f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_STRENGTH_PRESSURE; - brush->gpencil_settings->sculpt_mode_flag |= GP_SCULPT_FLAGMODE_APPLY_POSITION; - - break; - } - case GP_BRUSH_PRESET_RANDOMIZE_STROKE: { - brush->gpencil_sculpt_tool = GPSCULPT_TOOL_RANDOMIZE; - - brush->size = 25.0f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - - brush->gpencil_settings->draw_strength = 0.5f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_STRENGTH_PRESSURE; - brush->gpencil_settings->sculpt_mode_flag |= GP_SCULPT_FLAGMODE_APPLY_POSITION; - - break; - } - case GP_BRUSH_PRESET_CLONE_STROKE: { - brush->gpencil_sculpt_tool = GPSCULPT_TOOL_CLONE; - brush->gpencil_settings->flag &= ~GP_BRUSH_USE_PRESSURE; - - brush->size = 25.0f; - - brush->gpencil_settings->draw_strength = 1.0f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_STRENGTH_PRESSURE; - brush->gpencil_settings->sculpt_mode_flag |= GP_SCULPT_FLAGMODE_APPLY_POSITION; - - break; - } - case GP_BRUSH_PRESET_WEIGHT_DRAW: { - brush->gpencil_weight_tool = GPWEIGHT_TOOL_DRAW; - - brush->size = 25.0f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - - brush->alpha = 0.3f; - brush->gpencil_settings->draw_strength = 0.3f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_STRENGTH_PRESSURE; - brush->gpencil_settings->sculpt_mode_flag |= GP_SCULPT_FLAGMODE_APPLY_POSITION; - - break; - } - case GP_BRUSH_PRESET_WEIGHT_BLUR: { - brush->gpencil_weight_tool = GPWEIGHT_TOOL_BLUR; - - brush->size = 50.0f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - - brush->alpha = 0.3f; - brush->gpencil_settings->draw_strength = 0.3f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_STRENGTH_PRESSURE; - brush->gpencil_settings->sculpt_mode_flag |= GP_SCULPT_FLAGMODE_APPLY_POSITION; - - break; - } - case GP_BRUSH_PRESET_WEIGHT_AVERAGE: { - brush->gpencil_weight_tool = GPWEIGHT_TOOL_AVERAGE; - - brush->size = 50.0f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - - brush->alpha = 0.3f; - brush->gpencil_settings->draw_strength = 0.3f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_STRENGTH_PRESSURE; - brush->gpencil_settings->sculpt_mode_flag |= GP_SCULPT_FLAGMODE_APPLY_POSITION; - - break; - } - case GP_BRUSH_PRESET_WEIGHT_SMEAR: { - brush->gpencil_weight_tool = GPWEIGHT_TOOL_SMEAR; - - brush->size = 50.0f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - - brush->alpha = 0.3f; - brush->gpencil_settings->draw_strength = 0.3f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_STRENGTH_PRESSURE; - brush->gpencil_settings->sculpt_mode_flag |= GP_SCULPT_FLAGMODE_APPLY_POSITION; - - break; - } - default: - break; - } - - switch (material_preset) { - case PRESET_MATERIAL_NONE: - break; - case PRESET_MATERIAL_DOT_STROKE: { - /* Create and link Black Dots material to brush. - * This material is required because the brush uses the material - * to define how the stroke is drawn. */ - const char *ma_id = "Dots Stroke"; - Material *ma = (Material *)BLI_findstring(&bmain->materials, ma_id, offsetof(ID, name) + 2); - if (ma == nullptr) { - ma = BKE_gpencil_material_add(bmain, ma_id); - ma->gp_style->mode = GP_MATERIAL_MODE_DOT; - BLI_assert(ma->id.us == 1); - id_us_min(&ma->id); - } - - BKE_gpencil_brush_material_set(brush, ma); - - /* Pin the material to the brush. */ - brush->gpencil_settings->flag |= GP_BRUSH_MATERIAL_PINNED; - break; - } - } -} - -static Brush *gpencil_brush_ensure( - Main *bmain, ToolSettings *ts, const char *brush_name, eObjectMode mode, bool *r_is_new) -{ - *r_is_new = false; - Brush *brush = (Brush *)BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2); - - /* If the brush exist, but the type is not GPencil or the mode is wrong, create a new one. */ - if ((brush != nullptr) && ((brush->gpencil_settings == nullptr) || (brush->ob_mode != mode))) { - brush = nullptr; - } - - if (brush == nullptr) { - brush = BKE_brush_add_gpencil(bmain, ts, brush_name, mode); - *r_is_new = true; - } - - if (brush->gpencil_settings == nullptr) { - BKE_brush_init_gpencil_settings(brush); - } - - return brush; -} - -void BKE_brush_gpencil_paint_presets(Main *bmain, ToolSettings *ts, const bool reset) -{ - bool is_new = false; - - Paint *paint = &ts->gp_paint->paint; - Brush *brush_prev = BKE_paint_brush(paint); - Brush *brush, *deft_draw; - /* Airbrush brush. */ - brush = gpencil_brush_ensure(bmain, ts, "Airbrush", OB_MODE_PAINT_GPENCIL_LEGACY, &is_new); - if ((reset) || (is_new)) { - BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_AIRBRUSH); - } - - /* Ink Pen brush. */ - brush = gpencil_brush_ensure(bmain, ts, "Ink Pen", OB_MODE_PAINT_GPENCIL_LEGACY, &is_new); - if ((reset) || (is_new)) { - BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_INK_PEN); - } - - /* Ink Pen Rough brush. */ - brush = gpencil_brush_ensure(bmain, ts, "Ink Pen Rough", OB_MODE_PAINT_GPENCIL_LEGACY, &is_new); - if ((reset) || (is_new)) { - BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_INK_PEN_ROUGH); - } - - /* Marker Bold brush. */ - brush = gpencil_brush_ensure(bmain, ts, "Marker Bold", OB_MODE_PAINT_GPENCIL_LEGACY, &is_new); - if ((reset) || (is_new)) { - BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_MARKER_BOLD); - } - - /* Marker Chisel brush. */ - brush = gpencil_brush_ensure(bmain, ts, "Marker Chisel", OB_MODE_PAINT_GPENCIL_LEGACY, &is_new); - if ((reset) || (is_new)) { - BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_MARKER_CHISEL); - } - - /* Pen brush. */ - brush = gpencil_brush_ensure(bmain, ts, "Pen", OB_MODE_PAINT_GPENCIL_LEGACY, &is_new); - if ((reset) || (is_new)) { - BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_PEN); - } - - /* Pencil Soft brush. */ - brush = gpencil_brush_ensure(bmain, ts, "Pencil Soft", OB_MODE_PAINT_GPENCIL_LEGACY, &is_new); - if ((reset) || (is_new)) { - BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_PENCIL_SOFT); - } - - /* Pencil brush. */ - brush = gpencil_brush_ensure(bmain, ts, "Pencil", OB_MODE_PAINT_GPENCIL_LEGACY, &is_new); - if ((reset) || (is_new)) { - BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_PENCIL); - } - deft_draw = brush; /* save default brush. */ - - /* Fill brush. */ - brush = gpencil_brush_ensure(bmain, ts, "Fill Area", OB_MODE_PAINT_GPENCIL_LEGACY, &is_new); - if ((reset) || (is_new)) { - BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_FILL_AREA); - } - - /* Soft Eraser brush. */ - brush = gpencil_brush_ensure(bmain, ts, "Eraser Soft", OB_MODE_PAINT_GPENCIL_LEGACY, &is_new); - if ((reset) || (is_new)) { - BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_ERASER_SOFT); - } - - /* Hard Eraser brush. */ - brush = gpencil_brush_ensure(bmain, ts, "Eraser Hard", OB_MODE_PAINT_GPENCIL_LEGACY, &is_new); - if ((reset) || (is_new)) { - BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_ERASER_HARD); - } - - /* Point Eraser brush. */ - brush = gpencil_brush_ensure(bmain, ts, "Eraser Point", OB_MODE_PAINT_GPENCIL_LEGACY, &is_new); - if ((reset) || (is_new)) { - BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_ERASER_POINT); - } - - /* Stroke Eraser brush. */ - brush = gpencil_brush_ensure(bmain, ts, "Eraser Stroke", OB_MODE_PAINT_GPENCIL_LEGACY, &is_new); - if ((reset) || (is_new)) { - BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_ERASER_STROKE); - } - - /* Tint brush. */ - brush = gpencil_brush_ensure(bmain, ts, "Tint", OB_MODE_PAINT_GPENCIL_LEGACY, &is_new); - if ((reset) || (is_new)) { - BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_TINT); - } - - /* Set default Draw brush. */ - if ((reset == false) && (brush_prev != nullptr)) { - BKE_paint_brush_set(paint, brush_prev); - } - else { - BKE_paint_brush_set(paint, deft_draw); - } -} - -void BKE_brush_gpencil_vertex_presets(Main *bmain, ToolSettings *ts, const bool reset) -{ - bool is_new = false; - - Paint *vertexpaint = &ts->gp_vertexpaint->paint; - Brush *brush_prev = BKE_paint_brush(vertexpaint); - Brush *brush, *deft_vertex; - /* Vertex Draw brush. */ - brush = gpencil_brush_ensure(bmain, ts, "Vertex Draw", OB_MODE_VERTEX_GPENCIL_LEGACY, &is_new); - if ((reset) || (is_new)) { - BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_VERTEX_DRAW); - } - deft_vertex = brush; /* save default brush. */ - - /* Vertex Blur brush. */ - brush = gpencil_brush_ensure(bmain, ts, "Vertex Blur", OB_MODE_VERTEX_GPENCIL_LEGACY, &is_new); - if ((reset) || (is_new)) { - BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_VERTEX_BLUR); - } - /* Vertex Average brush. */ - brush = gpencil_brush_ensure( - bmain, ts, "Vertex Average", OB_MODE_VERTEX_GPENCIL_LEGACY, &is_new); - if ((reset) || (is_new)) { - BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_VERTEX_AVERAGE); - } - /* Vertex Smear brush. */ - brush = gpencil_brush_ensure(bmain, ts, "Vertex Smear", OB_MODE_VERTEX_GPENCIL_LEGACY, &is_new); - if ((reset) || (is_new)) { - BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_VERTEX_SMEAR); - } - /* Vertex Replace brush. */ - brush = gpencil_brush_ensure( - bmain, ts, "Vertex Replace", OB_MODE_VERTEX_GPENCIL_LEGACY, &is_new); - if ((reset) || (is_new)) { - BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_VERTEX_REPLACE); - } - - /* Set default Vertex brush. */ - if ((reset == false) && (brush_prev != nullptr)) { - BKE_paint_brush_set(vertexpaint, brush_prev); - } - else { - BKE_paint_brush_set(vertexpaint, deft_vertex); - } -} - -void BKE_brush_gpencil_sculpt_presets(Main *bmain, ToolSettings *ts, const bool reset) -{ - bool is_new = false; - - Paint *sculptpaint = &ts->gp_sculptpaint->paint; - Brush *brush_prev = BKE_paint_brush(sculptpaint); - Brush *brush, *deft_sculpt; - - /* Smooth brush. */ - brush = gpencil_brush_ensure(bmain, ts, "Smooth Stroke", OB_MODE_SCULPT_GPENCIL_LEGACY, &is_new); - if ((reset) || (is_new)) { - BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_SMOOTH_STROKE); - } - deft_sculpt = brush; - - /* Strength brush. */ - brush = gpencil_brush_ensure( - bmain, ts, "Strength Stroke", OB_MODE_SCULPT_GPENCIL_LEGACY, &is_new); - if ((reset) || (is_new)) { - BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_STRENGTH_STROKE); - } - - /* Thickness brush. */ - brush = gpencil_brush_ensure( - bmain, ts, "Thickness Stroke", OB_MODE_SCULPT_GPENCIL_LEGACY, &is_new); - if ((reset) || (is_new)) { - BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_THICKNESS_STROKE); - } - - /* Grab brush. */ - brush = gpencil_brush_ensure(bmain, ts, "Grab Stroke", OB_MODE_SCULPT_GPENCIL_LEGACY, &is_new); - if ((reset) || (is_new)) { - BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_GRAB_STROKE); - } - - /* Push brush. */ - brush = gpencil_brush_ensure(bmain, ts, "Push Stroke", OB_MODE_SCULPT_GPENCIL_LEGACY, &is_new); - if ((reset) || (is_new)) { - BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_PUSH_STROKE); - } - - /* Twist brush. */ - brush = gpencil_brush_ensure(bmain, ts, "Twist Stroke", OB_MODE_SCULPT_GPENCIL_LEGACY, &is_new); - if ((reset) || (is_new)) { - BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_TWIST_STROKE); - } - - /* Pinch brush. */ - brush = gpencil_brush_ensure(bmain, ts, "Pinch Stroke", OB_MODE_SCULPT_GPENCIL_LEGACY, &is_new); - if ((reset) || (is_new)) { - BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_PINCH_STROKE); - } - - /* Randomize brush. */ - brush = gpencil_brush_ensure( - bmain, ts, "Randomize Stroke", OB_MODE_SCULPT_GPENCIL_LEGACY, &is_new); - if ((reset) || (is_new)) { - BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_RANDOMIZE_STROKE); - } - - /* Clone brush. */ - brush = gpencil_brush_ensure(bmain, ts, "Clone Stroke", OB_MODE_SCULPT_GPENCIL_LEGACY, &is_new); - if ((reset) || (is_new)) { - BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_CLONE_STROKE); - } - - /* Set default brush. */ - if ((reset == false) && (brush_prev != nullptr)) { - BKE_paint_brush_set(sculptpaint, brush_prev); - } - else { - BKE_paint_brush_set(sculptpaint, deft_sculpt); - } -} - -void BKE_brush_gpencil_weight_presets(Main *bmain, ToolSettings *ts, const bool reset) -{ - bool is_new = false; - - Paint *weightpaint = &ts->gp_weightpaint->paint; - Brush *brush_prev = BKE_paint_brush(weightpaint); - Brush *brush, *deft_weight; - - /* Weight Draw brush. */ - brush = gpencil_brush_ensure(bmain, ts, "Weight Draw", OB_MODE_WEIGHT_GPENCIL_LEGACY, &is_new); - if ((reset) || (is_new)) { - BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_WEIGHT_DRAW); - } - deft_weight = brush; /* save default brush. */ - - /* Weight Blur brush. */ - brush = gpencil_brush_ensure(bmain, ts, "Weight Blur", OB_MODE_WEIGHT_GPENCIL_LEGACY, &is_new); - if ((reset) || (is_new)) { - BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_WEIGHT_BLUR); - } - - /* Weight Average brush. */ - brush = gpencil_brush_ensure( - bmain, ts, "Weight Average", OB_MODE_WEIGHT_GPENCIL_LEGACY, &is_new); - if ((reset) || (is_new)) { - BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_WEIGHT_AVERAGE); - } - - /* Weight Smear brush. */ - brush = gpencil_brush_ensure(bmain, ts, "Weight Smear", OB_MODE_WEIGHT_GPENCIL_LEGACY, &is_new); - if ((reset) || (is_new)) { - BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_WEIGHT_SMEAR); - } - - /* Set default brush. */ - if ((reset == false) && (brush_prev != nullptr)) { - BKE_paint_brush_set(weightpaint, brush_prev); - } - else { - BKE_paint_brush_set(weightpaint, deft_weight); - } -} - void BKE_brush_init_curves_sculpt_settings(Brush *brush) { if (brush->curves_sculpt_settings == nullptr) { diff --git a/source/blender/blenloader/intern/versioning_defaults.cc b/source/blender/blenloader/intern/versioning_defaults.cc index ff0bf4f6fe4..589c4735717 100644 --- a/source/blender/blenloader/intern/versioning_defaults.cc +++ b/source/blender/blenloader/intern/versioning_defaults.cc @@ -540,19 +540,6 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { ToolSettings *ts = scene->toolsettings; - if (ts->gp_paint) { - BKE_brush_gpencil_paint_presets(bmain, ts, true); - } - if (ts->gp_sculptpaint) { - BKE_brush_gpencil_sculpt_presets(bmain, ts, true); - } - if (ts->gp_vertexpaint) { - BKE_brush_gpencil_vertex_presets(bmain, ts, true); - } - if (ts->gp_weightpaint) { - BKE_brush_gpencil_weight_presets(bmain, ts, true); - } - /* Ensure new Paint modes. */ BKE_paint_ensure_from_paintmode(bmain, scene, PaintMode::VertexGPencil); BKE_paint_ensure_from_paintmode(bmain, scene, PaintMode::SculptGPencil); diff --git a/source/blender/editors/gpencil_legacy/gpencil_edit.cc b/source/blender/editors/gpencil_legacy/gpencil_edit.cc index b3ff26148d3..2703264b4af 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_edit.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_edit.cc @@ -423,16 +423,13 @@ static int gpencil_paintmode_toggle_exec(bContext *C, wmOperator *op) BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_paint); BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_vertexpaint); - BKE_brush_gpencil_paint_presets(bmain, ts, false); - /* Ensure Palette by default. */ BKE_gpencil_palette_ensure(bmain, CTX_data_scene(C)); Paint *paint = &ts->gp_paint->paint; Brush *brush = BKE_paint_brush(paint); - /* if not exist, create a new one */ - if ((brush == nullptr) || (brush->gpencil_settings == nullptr)) { - BKE_brush_gpencil_paint_presets(bmain, ts, true); + if (brush && !brush->gpencil_settings) { + BKE_brush_init_gpencil_settings(brush); } BKE_paint_brush_validate(bmain, &ts->gp_paint->paint); } @@ -573,10 +570,6 @@ static int gpencil_sculptmode_toggle_exec(bContext *C, wmOperator *op) if (mode == OB_MODE_SCULPT_GPENCIL_LEGACY) { /* Be sure we have brushes. */ BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_sculptpaint); - - const bool reset_mode = (BKE_paint_brush(&ts->gp_sculptpaint->paint) == nullptr); - BKE_brush_gpencil_sculpt_presets(bmain, ts, reset_mode); - BKE_paint_brush_validate(bmain, &ts->gp_sculptpaint->paint); } @@ -717,9 +710,6 @@ static int gpencil_weightmode_toggle_exec(bContext *C, wmOperator *op) ED_paint_cursor_start(weight_paint, grease_pencil_poll_weight_cursor); } - const bool reset_mode = (BKE_paint_brush(weight_paint) == nullptr); - BKE_brush_gpencil_weight_presets(bmain, ts, reset_mode); - BKE_paint_brush_validate(bmain, weight_paint); } @@ -832,9 +822,6 @@ static int gpencil_vertexmode_toggle_exec(bContext *C, wmOperator *op) BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_paint); BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_vertexpaint); - const bool reset_mode = (BKE_paint_brush(&ts->gp_vertexpaint->paint) == nullptr); - BKE_brush_gpencil_vertex_presets(bmain, ts, reset_mode); - BKE_paint_brush_validate(bmain, &ts->gp_vertexpaint->paint); /* Ensure Palette by default. */ diff --git a/source/blender/editors/gpencil_legacy/gpencil_merge.cc b/source/blender/editors/gpencil_legacy/gpencil_merge.cc index 6e85e908ec7..8e669120446 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_merge.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_merge.cc @@ -99,12 +99,9 @@ static bGPDstroke *gpencil_prepare_stroke(bContext *C, wmOperator *op, int totpo Paint *paint = &ts->gp_paint->paint; Brush *brush = BKE_paint_brush(paint); - /* if not exist, create a new one */ - if ((brush == nullptr) || (brush->gpencil_settings == nullptr)) { - /* create new brushes */ - BKE_brush_gpencil_paint_presets(bmain, ts, false); + if (brush && !brush->gpencil_settings) { + BKE_brush_init_gpencil_settings(brush); } - brush = BKE_paint_brush(paint); /* frame */ short add_frame_mode; diff --git a/source/blender/editors/gpencil_legacy/gpencil_paint.cc b/source/blender/editors/gpencil_legacy/gpencil_paint.cc index 82c565df7a8..1a66e13a174 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_paint.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_paint.cc @@ -1914,50 +1914,6 @@ static void gpencil_session_validatebuffer(tGPsdata *p) } } -/* helper to get default eraser and create one if no eraser brush */ -static Brush *gpencil_get_default_eraser(Main *bmain, ToolSettings *ts) -{ - Brush *brush_dft = nullptr; - Paint *paint = &ts->gp_paint->paint; - Brush *brush_prev = BKE_paint_brush(paint); - for (Brush *brush = static_cast(bmain->brushes.first); brush; - brush = static_cast(brush->id.next)) - { - if (brush->gpencil_settings == nullptr) { - continue; - } - if ((brush->ob_mode == OB_MODE_PAINT_GPENCIL_LEGACY) && - (brush->gpencil_tool == GPAINT_TOOL_ERASE)) - { - /* save first eraser to use later if no default */ - if (brush_dft == nullptr) { - brush_dft = brush; - } - /* found default */ - if (brush->gpencil_settings->flag & GP_BRUSH_DEFAULT_ERASER) { - return brush; - } - } - } - /* if no default, but exist eraser brush, return this and set as default */ - if (brush_dft) { - brush_dft->gpencil_settings->flag |= GP_BRUSH_DEFAULT_ERASER; - return brush_dft; - } - /* create a new soft eraser brush */ - - brush_dft = BKE_brush_add_gpencil(bmain, ts, "Soft Eraser", OB_MODE_PAINT_GPENCIL_LEGACY); - brush_dft->size = 30.0f; - brush_dft->gpencil_settings->flag |= GP_BRUSH_DEFAULT_ERASER; - brush_dft->gpencil_tool = GPAINT_TOOL_ERASE; - brush_dft->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_SOFT; - - /* reset current brush */ - BKE_paint_brush_set(paint, brush_prev); - - return brush_dft; -} - /* helper to set default eraser and disable others */ static void gpencil_set_default_eraser(Main *bmain, Brush *brush_dft) { @@ -1979,55 +1935,6 @@ static void gpencil_set_default_eraser(Main *bmain, Brush *brush_dft) } } -/* initialize a drawing brush */ -static void gpencil_init_drawing_brush(bContext *C, tGPsdata *p) -{ - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - ToolSettings *ts = CTX_data_tool_settings(C); - - Paint *paint = &ts->gp_paint->paint; - bool changed = false; - Brush *brush = BKE_paint_brush(paint); - - /* if not exist, create a new one */ - if ((brush == nullptr) || (brush->gpencil_settings == nullptr)) { - /* create new brushes */ - BKE_brush_gpencil_paint_presets(bmain, ts, true); - changed = true; - brush = BKE_paint_brush(paint); - } - /* Be sure curves are initialized. */ - BKE_curvemapping_init(brush->gpencil_settings->curve_sensitivity); - BKE_curvemapping_init(brush->gpencil_settings->curve_strength); - BKE_curvemapping_init(brush->gpencil_settings->curve_jitter); - BKE_curvemapping_init(brush->gpencil_settings->curve_rand_pressure); - BKE_curvemapping_init(brush->gpencil_settings->curve_rand_strength); - BKE_curvemapping_init(brush->gpencil_settings->curve_rand_uv); - BKE_curvemapping_init(brush->gpencil_settings->curve_rand_hue); - BKE_curvemapping_init(brush->gpencil_settings->curve_rand_saturation); - BKE_curvemapping_init(brush->gpencil_settings->curve_rand_value); - - /* Assign to temp #tGPsdata */ - p->brush = BKE_paint_brush(paint); - if (p->brush->gpencil_tool != GPAINT_TOOL_ERASE) { - p->eraser = gpencil_get_default_eraser(p->bmain, ts); - } - else { - p->eraser = p->brush; - } - /* set new eraser as default */ - gpencil_set_default_eraser(p->bmain, p->eraser); - - /* use radius of eraser */ - p->radius = short(p->eraser->size); - - /* Need this update to synchronize brush with draw manager. */ - if (changed) { - DEG_id_tag_update(&scene->id, ID_RECALC_SYNC_TO_EVAL); - } -} - /* initialize a paint brush and a default color if not exist */ static void gpencil_init_colors(tGPsdata *p) { @@ -2122,9 +2029,6 @@ static bool gpencil_session_initdata(bContext *C, wmOperator *op, tGPsdata *p) /* clear out buffer (stored in gp-data), in case something contaminated it */ gpencil_session_validatebuffer(p); - /* set brush and create a new one if null */ - gpencil_init_drawing_brush(C, p); - /* setup active color */ /* region where paint was originated */ int totcol = p->ob->totcol; diff --git a/source/blender/editors/gpencil_legacy/gpencil_primitive.cc b/source/blender/editors/gpencil_legacy/gpencil_primitive.cc index fc31c755e9a..6a59eb95828 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_primitive.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_primitive.cc @@ -1212,12 +1212,12 @@ static void gpencil_primitive_init(bContext *C, wmOperator *op) /* if brush doesn't exist, create a new set (fix damaged files from old versions) */ Brush *brush = BKE_paint_brush(paint); - if ((brush == nullptr) || (brush->gpencil_settings == nullptr)) { - BKE_brush_gpencil_paint_presets(bmain, ts, true); + if (brush && !brush->gpencil_settings) { + BKE_brush_init_gpencil_settings(brush); } /* Set brush. */ - tgpi->brush = BKE_paint_brush(paint); + tgpi->brush = brush; /* control points */ tgpi->gpd->runtime.cp_points = static_cast( diff --git a/source/blender/editors/gpencil_legacy/gpencil_utils.cc b/source/blender/editors/gpencil_legacy/gpencil_utils.cc index 6c505caf73e..d1e06b664db 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_utils.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_utils.cc @@ -1429,13 +1429,6 @@ void ED_gpencil_add_defaults(bContext *C, Object *ob) ToolSettings *ts = CTX_data_tool_settings(C); BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_paint); - Paint *paint = &ts->gp_paint->paint; - Brush *brush = BKE_paint_brush(paint); - /* if not exist, create a new one */ - if ((brush == nullptr) || (brush->gpencil_settings == nullptr)) { - /* create new brushes */ - BKE_brush_gpencil_paint_presets(bmain, ts, true); - } /* ensure a color exists and is assigned to object */ BKE_gpencil_object_material_ensure_from_active_input_toolsettings(bmain, ob, ts); -- 2.30.2 From 078e56eff697462c2c0df4554c293b8533a1a22f Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 3 May 2024 12:23:17 -0400 Subject: [PATCH 221/244] Cleanup: Unused variable --- source/blender/editors/gpencil_legacy/gpencil_merge.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/source/blender/editors/gpencil_legacy/gpencil_merge.cc b/source/blender/editors/gpencil_legacy/gpencil_merge.cc index 8e669120446..dd46944cc70 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_merge.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_merge.cc @@ -85,7 +85,6 @@ static void gpencil_insert_points_to_stroke(bGPDstroke *gps, static bGPDstroke *gpencil_prepare_stroke(bContext *C, wmOperator *op, int totpoints) { - Main *bmain = CTX_data_main(C); ToolSettings *ts = CTX_data_tool_settings(C); Object *ob = CTX_data_active_object(C); bGPdata *gpd = static_cast(ob->data); -- 2.30.2 From 545d9b19187491ee6ec88c3961bdb8905e311056 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 3 May 2024 18:22:09 +0200 Subject: [PATCH 222/244] Fix missing asset shelf popup label in header Mistake in f89b5c33f4. --- .../templates/interface_template_asset_shelf_popover.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/interface/templates/interface_template_asset_shelf_popover.cc b/source/blender/editors/interface/templates/interface_template_asset_shelf_popover.cc index 371aabc5f7d..8f471e21cea 100644 --- a/source/blender/editors/interface/templates/interface_template_asset_shelf_popover.cc +++ b/source/blender/editors/interface/templates/interface_template_asset_shelf_popover.cc @@ -46,7 +46,7 @@ void template_asset_shelf_popover(uiLayout &layout, if (use_big_size) { return UI_UNIT_X * 6; } - return UI_UNIT_X * (name.is_empty() ? 7 : 1.6f); + return UI_UNIT_X * (name.is_empty() ? 1.6f : 7); }(); const short height = UI_UNIT_Y * (use_big_size ? 6 : 1); -- 2.30.2 From dc9e0ac6fa312863d82c8d392d7572e91c93a31e Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 3 May 2024 18:51:13 +0200 Subject: [PATCH 223/244] Cleanuo: Remove more unnecessary brush versioning code All these brushes are getting deleted at the end of the function. --- .../blenloader/intern/versioning_defaults.cc | 105 +----------------- 1 file changed, 2 insertions(+), 103 deletions(-) diff --git a/source/blender/blenloader/intern/versioning_defaults.cc b/source/blender/blenloader/intern/versioning_defaults.cc index 589c4735717..59d483b34b3 100644 --- a/source/blender/blenloader/intern/versioning_defaults.cc +++ b/source/blender/blenloader/intern/versioning_defaults.cc @@ -452,40 +452,8 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) BLO_update_defaults_workspace(workspace, app_template); } - /* New grease pencil brushes and vertex paint setup. */ + /* Grease pencil materials and paint modes setup. */ { - /* Update Grease Pencil brushes. */ - Brush *brush; - - /* Pencil brush. */ - do_versions_rename_id(bmain, ID_BR, "Draw Pencil", "Pencil"); - - /* Pen brush. */ - do_versions_rename_id(bmain, ID_BR, "Draw Pen", "Pen"); - - /* Pen Soft brush. */ - brush = reinterpret_cast( - do_versions_rename_id(bmain, ID_BR, "Draw Soft", "Pencil Soft")); - - /* Ink Pen brush. */ - do_versions_rename_id(bmain, ID_BR, "Draw Ink", "Ink Pen"); - - /* Ink Pen Rough brush. */ - do_versions_rename_id(bmain, ID_BR, "Draw Noise", "Ink Pen Rough"); - - /* Marker Bold brush. */ - do_versions_rename_id(bmain, ID_BR, "Draw Marker", "Marker Bold"); - - /* Marker Chisel brush. */ - do_versions_rename_id(bmain, ID_BR, "Draw Block", "Marker Chisel"); - - /* Remove useless Fill Area.001 brush. */ - brush = static_cast( - BLI_findstring(&bmain->brushes, "Fill Area.001", offsetof(ID, name) + 2)); - if (brush) { - BKE_id_delete(bmain, brush); - } - /* Rename and fix materials and enable default object lights on. */ if (app_template && STREQ(app_template, "2D_Animation")) { Material *ma = nullptr; @@ -536,7 +504,7 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) } } - /* Reset all grease pencil brushes. */ + /* Reset grease pencil paint modes. */ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { ToolSettings *ts = scene->toolsettings; @@ -694,75 +662,6 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) } /* Brushes */ - { - /* Enable for UV sculpt (other brush types will be created as needed), - * without this the grab brush will be active but not selectable from the list. */ - const char *brush_name = "Grab"; - Brush *brush = static_cast( - BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2)); - if (brush) { - brush->ob_mode |= OB_MODE_EDIT; - } - } - - LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { - brush->blur_kernel_radius = 2; - - /* Use full strength for all non-sculpt brushes, - * when painting we want to use full color/weight always. - * - * Note that sculpt is an exception, - * its values are overwritten by #BKE_brush_sculpt_reset below. */ - brush->alpha = 1.0; - - /* Enable anti-aliasing by default. */ - brush->sampling_flag |= BRUSH_PAINT_ANTIALIASING; - - /* By default, each brush should use a single input sample. */ - brush->input_samples = 1; - } - - /* Change the spacing of the Smear brush to 3.0% */ - if (Brush *brush = static_cast( - BLI_findstring(&bmain->brushes, "Smear", offsetof(ID, name) + 2))) - { - brush->spacing = 3.0; - } - - { - LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { - /* Use the same tool icon color in the brush cursor */ - if (brush->ob_mode & OB_MODE_SCULPT) { - BLI_assert(brush->sculpt_tool != 0); - BKE_brush_sculpt_reset(brush); - } - - /* Set the default texture mapping. - * Do it for all brushes, since some of them might be coming from the startup file. */ - brush->mtex.brush_map_mode = MTEX_MAP_MODE_VIEW; - brush->mask_mtex.brush_map_mode = MTEX_MAP_MODE_VIEW; - } - } - - { - const Brush *default_brush = DNA_struct_default_get(Brush); - LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { - brush->automasking_start_normal_limit = default_brush->automasking_start_normal_limit; - brush->automasking_start_normal_falloff = default_brush->automasking_start_normal_falloff; - - brush->automasking_view_normal_limit = default_brush->automasking_view_normal_limit; - brush->automasking_view_normal_falloff = default_brush->automasking_view_normal_falloff; - } - } - - { - LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { - if (!brush->automasking_cavity_curve) { - brush->automasking_cavity_curve = BKE_sculpt_default_cavity_curve(); - } - } - } - { /* Remove default brushes replaced by assets. Also remove outliner `treestore` that may point * to brushes. Normally the treestore is updated properly but it doesn't seem to update during -- 2.30.2 From c090f059d1fb83c449a4912d60e5388ce0635877 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 3 May 2024 19:27:50 +0200 Subject: [PATCH 224/244] Cleanup: Remove unused function --- .../editors/gpencil_legacy/gpencil_paint.cc | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/source/blender/editors/gpencil_legacy/gpencil_paint.cc b/source/blender/editors/gpencil_legacy/gpencil_paint.cc index 1a66e13a174..0865eb52ef1 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_paint.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_paint.cc @@ -1914,27 +1914,6 @@ static void gpencil_session_validatebuffer(tGPsdata *p) } } -/* helper to set default eraser and disable others */ -static void gpencil_set_default_eraser(Main *bmain, Brush *brush_dft) -{ - if (brush_dft == nullptr) { - return; - } - - for (Brush *brush = static_cast(bmain->brushes.first); brush; - brush = static_cast(brush->id.next)) - { - if ((brush->gpencil_settings) && (brush->gpencil_tool == GPAINT_TOOL_ERASE)) { - if (brush == brush_dft) { - brush->gpencil_settings->flag |= GP_BRUSH_DEFAULT_ERASER; - } - else if (brush->gpencil_settings->flag & GP_BRUSH_DEFAULT_ERASER) { - brush->gpencil_settings->flag &= ~GP_BRUSH_DEFAULT_ERASER; - } - } - } -} - /* initialize a paint brush and a default color if not exist */ static void gpencil_init_colors(tGPsdata *p) { -- 2.30.2 From a25c1b9418d284d31eff1b62b4c22746d80d9a2f Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 3 May 2024 19:42:16 +0200 Subject: [PATCH 225/244] Fix: Issues restoring brush after secondary smooth brush usage Storing the brush pointer instead of the name should be fine for the duration of a brush stroke. --- .../blender/editors/sculpt_paint/paint_vertex.cc | 15 ++++++--------- source/blender/editors/sculpt_paint/sculpt.cc | 14 ++++++-------- .../blender/editors/sculpt_paint/sculpt_intern.hh | 2 +- 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/source/blender/editors/sculpt_paint/paint_vertex.cc b/source/blender/editors/sculpt_paint/paint_vertex.cc index 5936d9ec376..7b8faf16c66 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.cc +++ b/source/blender/editors/sculpt_paint/paint_vertex.cc @@ -420,19 +420,17 @@ bool mode_toggle_poll_test(bContext *C) void smooth_brush_toggle_off(const bContext *C, Paint *paint, StrokeCache *cache) { - Main *bmain = CTX_data_main(C); Brush *brush = BKE_paint_brush(paint); /* The current brush should match with what we have stored in the cache. */ BLI_assert(brush == cache->brush); - /* If saved_active_brush_name is not set, brush was not switched/affected in + /* If saved_active_brush is not set, brush was not switched/affected in * smooth_brush_toggle_on(). */ - Brush *saved_active_brush = (Brush *)BKE_libblock_find_name( - bmain, ID_BR, cache->saved_active_brush_name); - if (saved_active_brush) { + if (cache->saved_active_brush) { Scene *scene = CTX_data_scene(C); BKE_brush_size_set(scene, brush, cache->saved_smooth_size); - BKE_paint_brush_set(paint, saved_active_brush); + BKE_paint_brush_set(paint, cache->saved_active_brush); + cache->saved_active_brush = nullptr; } } /* Initialize the stroke cache invariants from operator properties */ @@ -591,14 +589,13 @@ void smooth_brush_toggle_on(const bContext *C, Paint *paint, StrokeCache *cache) if (!smooth_brush) { BKE_paint_brush_set(paint, cur_brush); CLOG_WARN(&LOG, "Switching to the blur (smooth) brush not possible, corresponding brush not"); - cache->saved_active_brush_name[0] = '\0'; + cache->saved_active_brush = nullptr; return; } int cur_brush_size = BKE_brush_size_get(scene, cur_brush); - STRNCPY(cache->saved_active_brush_name, cur_brush->id.name + 2); - + cache->saved_active_brush = cur_brush; cache->saved_smooth_size = BKE_brush_size_get(scene, smooth_brush); BKE_brush_size_set(scene, smooth_brush, cur_brush_size); BKE_curvemapping_init(smooth_brush->curve); diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 1dd5c69fb62..99931a48cc4 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -4227,13 +4227,13 @@ static void smooth_brush_toggle_on(const bContext *C, Paint *paint, StrokeCache if (!smooth_brush) { BKE_paint_brush_set(paint, cur_brush); CLOG_WARN(&LOG, "Switching to the smooth brush not possible, corresponding brush not"); - cache->saved_active_brush_name[0] = '\0'; + cache->saved_active_brush = nullptr; return; } int cur_brush_size = BKE_brush_size_get(scene, cur_brush); - STRNCPY(cache->saved_active_brush_name, cur_brush->id.name + 2); + cache->saved_active_brush = cur_brush; cache->saved_smooth_size = BKE_brush_size_get(scene, smooth_brush); BKE_brush_size_set(scene, smooth_brush, cur_brush_size); @@ -4242,7 +4242,6 @@ static void smooth_brush_toggle_on(const bContext *C, Paint *paint, StrokeCache static void smooth_brush_toggle_off(const bContext *C, Paint *paint, StrokeCache *cache) { - Main *bmain = CTX_data_main(C); Brush *brush = BKE_paint_brush(paint); if (brush->sculpt_tool == SCULPT_TOOL_MASK) { @@ -4260,14 +4259,13 @@ static void smooth_brush_toggle_off(const bContext *C, Paint *paint, StrokeCache return; } - /* If saved_active_brush_name is not set, brush was not switched/affected in + /* If saved_active_brush is not set, brush was not switched/affected in * smooth_brush_toggle_on(). */ - Brush *saved_active_brush = (Brush *)BKE_libblock_find_name( - bmain, ID_BR, cache->saved_active_brush_name); - if (saved_active_brush) { + if (cache->saved_active_brush) { Scene *scene = CTX_data_scene(C); BKE_brush_size_set(scene, brush, cache->saved_smooth_size); - BKE_paint_brush_set(paint, saved_active_brush); + BKE_paint_brush_set(paint, cache->saved_active_brush); + cache->saved_active_brush = nullptr; } } diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index 5992f9d1404..ddf43f59c69 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -508,7 +508,7 @@ struct StrokeCache { float vertex_rotation; /* amount to rotate the vertices when using rotate brush */ Dial *dial; - char saved_active_brush_name[MAX_ID_NAME]; + Brush *saved_active_brush; char saved_mask_brush_tool; int saved_smooth_size; /* smooth tool copies the size of the current tool */ bool alt_smooth; -- 2.30.2 From bbdee3a52065639b603ea866d048b7e039462b3c Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 3 May 2024 20:45:10 +0200 Subject: [PATCH 226/244] Cleanup: make format --- .../startup/bl_ui/properties_paint_common.py | 26 +++++++++---------- .../startup/bl_ui/space_toolsystem_toolbar.py | 2 +- scripts/startup/bl_ui/space_view3d_toolbar.py | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/scripts/startup/bl_ui/properties_paint_common.py b/scripts/startup/bl_ui/properties_paint_common.py index ba796c59e55..1151a1fe237 100644 --- a/scripts/startup/bl_ui/properties_paint_common.py +++ b/scripts/startup/bl_ui/properties_paint_common.py @@ -36,19 +36,19 @@ class BrushAssetShelf: @staticmethod def get_shelf_name_from_mode(obmode): - mode_map = { - 'SCULPT': "VIEW3D_AST_brush_sculpt", - 'SCULPT_CURVES': "VIEW3D_AST_brush_sculpt_curves", - 'VERTEX_PAINT': "VIEW3D_AST_brush_vertex_paint", - 'WEIGHT_PAINT': "VIEW3D_AST_brush_weight_paint", - 'TEXTURE_PAINT': "VIEW3D_AST_brush_texture_paint", - 'PAINT_GPENCIL': "VIEW3D_AST_brush_gpencil_paint", - 'PAINT_GREASE_PENCIL': "VIEW3D_AST_brush_grease_pencil_paint", - 'SCULPT_GPENCIL': "VIEW3D_AST_brush_gpencil_sculpt", - 'VERTEX_GPENCIL': "VIEW3D_AST_brush_gpencil_vertex", - 'WEIGHT_GPENCIL': "VIEW3D_AST_brush_gpencil_weight", - } - return mode_map[obmode] + mode_map = { + 'SCULPT': "VIEW3D_AST_brush_sculpt", + 'SCULPT_CURVES': "VIEW3D_AST_brush_sculpt_curves", + 'VERTEX_PAINT': "VIEW3D_AST_brush_vertex_paint", + 'WEIGHT_PAINT': "VIEW3D_AST_brush_weight_paint", + 'TEXTURE_PAINT': "VIEW3D_AST_brush_texture_paint", + 'PAINT_GPENCIL': "VIEW3D_AST_brush_gpencil_paint", + 'PAINT_GREASE_PENCIL': "VIEW3D_AST_brush_grease_pencil_paint", + 'SCULPT_GPENCIL': "VIEW3D_AST_brush_gpencil_sculpt", + 'VERTEX_GPENCIL': "VIEW3D_AST_brush_gpencil_vertex", + 'WEIGHT_GPENCIL': "VIEW3D_AST_brush_gpencil_weight", + } + return mode_map[obmode] class UnifiedPaintPanel: diff --git a/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 6fb65e2cf6f..f351d01e3dc 100644 --- a/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -2755,7 +2755,6 @@ class _defs_gpencil_sculpt: ] - class _defs_grease_pencil_sculpt: @staticmethod def poll_select_mask(context): @@ -2893,6 +2892,7 @@ class _defs_gpencil_vertex: ) ] + class _defs_node_select: @ToolDef.from_fn diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index a43034ae346..00f52b7dcca 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -85,7 +85,6 @@ class VIEW3D_MT_brush_gpencil_context_menu(Menu): return - class View3DPanel: bl_space_type = 'VIEW_3D' bl_region_type = 'UI' @@ -1990,6 +1989,7 @@ class GreasePencilSculptPanel: class VIEW3D_PT_tools_grease_pencil_sculpt_select(Panel, View3DPanel, GreasePencilSculptPanel, BrushSelectPanel): bl_label = "Brush Asset" + class VIEW3D_PT_tools_grease_pencil_sculpt_settings(Panel, View3DPanel, GreasePencilSculptPanel): bl_label = "Brush Settings" -- 2.30.2 From b4e3246a7aa9d7c8c0fca0e263bed3514fdb7deb Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 3 May 2024 21:02:28 +0200 Subject: [PATCH 227/244] Fix: Crash painting with default grease pencil brushes Previous refactor removed too much code, and soft eraser still needs to be made to work again after this. --- .../editors/gpencil_legacy/gpencil_fill.cc | 4 ++ .../editors/gpencil_legacy/gpencil_paint.cc | 44 +++++++++++++++++++ .../gpencil_legacy/gpencil_sculpt_paint.cc | 7 ++- .../intern/grease_pencil_primitive.cc | 3 ++ 4 files changed, 57 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/gpencil_legacy/gpencil_fill.cc b/source/blender/editors/gpencil_legacy/gpencil_fill.cc index b0c66af13c9..d8cc90ab544 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_fill.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_fill.cc @@ -26,6 +26,7 @@ #include "DNA_object_types.h" #include "DNA_windowmanager_types.h" +#include "BKE_brush.hh" #include "BKE_context.hh" #include "BKE_deform.hh" #include "BKE_gpencil_geom_legacy.h" @@ -2405,6 +2406,9 @@ static tGPDfill *gpencil_session_init_fill(bContext *C, wmOperator *op) /* save filling parameters */ Brush *brush = BKE_paint_brush(&ts->gp_paint->paint); + if (brush && !brush->gpencil_settings) { + BKE_brush_init_gpencil_settings(brush); + } tgpf->brush = brush; tgpf->flag = brush->gpencil_settings->flag; tgpf->fill_threshold = brush->gpencil_settings->fill_threshold; diff --git a/source/blender/editors/gpencil_legacy/gpencil_paint.cc b/source/blender/editors/gpencil_legacy/gpencil_paint.cc index 0865eb52ef1..22cac4d295c 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_paint.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_paint.cc @@ -1914,6 +1914,43 @@ static void gpencil_session_validatebuffer(tGPsdata *p) } } +/* initialize a drawing brush */ +static void gpencil_init_drawing_brush(bContext *C, tGPsdata *p) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + Paint *paint = &ts->gp_paint->paint; + Brush *brush = BKE_paint_brush(paint); + + if (brush == nullptr) { + return; + } + + /* Be sure curves are initialized. */ + BKE_curvemapping_init(brush->gpencil_settings->curve_sensitivity); + BKE_curvemapping_init(brush->gpencil_settings->curve_strength); + BKE_curvemapping_init(brush->gpencil_settings->curve_jitter); + BKE_curvemapping_init(brush->gpencil_settings->curve_rand_pressure); + BKE_curvemapping_init(brush->gpencil_settings->curve_rand_strength); + BKE_curvemapping_init(brush->gpencil_settings->curve_rand_uv); + BKE_curvemapping_init(brush->gpencil_settings->curve_rand_hue); + BKE_curvemapping_init(brush->gpencil_settings->curve_rand_saturation); + BKE_curvemapping_init(brush->gpencil_settings->curve_rand_value); + + /* Assign to temp #tGPsdata */ + p->brush = brush; + if (p->brush->gpencil_tool != GPAINT_TOOL_ERASE) { + /* TODO: make this work again with "Smooth Eraser" essentials brush. + * See od gpencil_set_default_eraser and gpencil_set_default_eraser. */ + p->eraser = p->brush; + } + else { + p->eraser = p->brush; + } + + /* use radius of eraser */ + p->radius = short(p->eraser->size); +} + /* initialize a paint brush and a default color if not exist */ static void gpencil_init_colors(tGPsdata *p) { @@ -2008,6 +2045,13 @@ static bool gpencil_session_initdata(bContext *C, wmOperator *op, tGPsdata *p) /* clear out buffer (stored in gp-data), in case something contaminated it */ gpencil_session_validatebuffer(p); + /* set brush and create a new one if null */ + gpencil_init_drawing_brush(C, p); + if (p->brush == nullptr) { + p->status = GP_STATUS_ERROR; + return false; + } + /* setup active color */ /* region where paint was originated */ int totcol = p->ob->totcol; diff --git a/source/blender/editors/gpencil_legacy/gpencil_sculpt_paint.cc b/source/blender/editors/gpencil_legacy/gpencil_sculpt_paint.cc index 2f582141d53..9c007bff967 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_sculpt_paint.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_sculpt_paint.cc @@ -1204,6 +1204,9 @@ static bool gpencil_sculpt_brush_init(bContext *C, wmOperator *op) Paint *paint = &ts->gp_sculptpaint->paint; Brush *brush = BKE_paint_brush(paint); + if (brush && !brush->gpencil_settings) { + BKE_brush_init_gpencil_settings(brush); + } gso->brush = brush; BKE_curvemapping_init(gso->brush->curve); @@ -2179,7 +2182,9 @@ static Brush *gpencil_sculpt_get_smooth_brush(tGP_BrushEditData *gso) Main *bmain = gso->bmain; Brush *brush = static_cast( BLI_findstring(&bmain->brushes, "Smooth Stroke", offsetof(ID, name) + 2)); - + if (brush && !brush->gpencil_settings) { + BKE_brush_init_gpencil_settings(brush); + } return brush; } diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_primitive.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_primitive.cc index 68bb41bfb73..70a59b374e9 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_primitive.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_primitive.cc @@ -676,6 +676,9 @@ static int grease_pencil_primitive_invoke(bContext *C, wmOperator *op, const wmE Paint *paint = &vc.scene->toolsettings->gp_paint->paint; ptd.brush = BKE_paint_brush(paint); + if (ptd.brush->gpencil_settings == nullptr) { + BKE_brush_init_gpencil_settings(ptd.brush); + } ptd.settings = ptd.brush->gpencil_settings; BKE_curvemapping_init(ptd.settings->curve_sensitivity); -- 2.30.2 From 07c6a06b6f99873e861f2fb59b2d9995cd2d437d Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 3 May 2024 15:35:17 -0400 Subject: [PATCH 228/244] Cleanup: Remove unnecessary descructor definition --- source/blender/editors/asset/intern/asset_shelf_asset_view.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/source/blender/editors/asset/intern/asset_shelf_asset_view.cc b/source/blender/editors/asset/intern/asset_shelf_asset_view.cc index 1c6a2178a67..bf05830a661 100644 --- a/source/blender/editors/asset/intern/asset_shelf_asset_view.cc +++ b/source/blender/editors/asset/intern/asset_shelf_asset_view.cc @@ -52,7 +52,6 @@ class AssetView : public ui::AbstractGridView { public: AssetView(const AssetLibraryReference &library_ref, const AssetShelf &shelf, bool is_popup); - ~AssetView(); void build_items() override; bool begin_filtering(const bContext &C) const override; @@ -109,8 +108,6 @@ AssetView::AssetView(const AssetLibraryReference &library_ref, } } -AssetView::~AssetView() {} - void AssetView::build_items() { const asset_system::AssetLibrary *library = list::library_get_once_available(library_ref_); -- 2.30.2 From 08bd22eed78163552d152ab892426efa4e98a58b Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 3 May 2024 16:13:48 -0400 Subject: [PATCH 229/244] Fix: Issues with asset shelf in image editor Make the asset shelf always show in the image editor's paint UI mode. And fix the popup to show in the sidebar. That required changing the way we retrieve the brush asset shelf name from the context. ALso fix a few panels that didn't check for whether there was a brush. While that shouldn't really happen it does currently and it's nice to avoid the errors. --- .../startup/bl_ui/properties_paint_common.py | 25 +++++++++++-------- scripts/startup/bl_ui/space_image.py | 22 +++++++++------- .../startup/bl_ui/space_toolsystem_toolbar.py | 4 +-- scripts/startup/bl_ui/space_view3d.py | 22 ++++++++-------- 4 files changed, 40 insertions(+), 33 deletions(-) diff --git a/scripts/startup/bl_ui/properties_paint_common.py b/scripts/startup/bl_ui/properties_paint_common.py index 1151a1fe237..0b8881e3388 100644 --- a/scripts/startup/bl_ui/properties_paint_common.py +++ b/scripts/startup/bl_ui/properties_paint_common.py @@ -19,8 +19,9 @@ class BrushAssetShelf: def asset_poll(cls, asset): if asset.id_type != 'BRUSH': return False - - return asset.metadata.get(cls.mode_prop, False) + if hasattr(cls, "mode_prop"): + return asset.metadata.get(cls.mode_prop, False) + return True @classmethod def get_active_asset(cls): @@ -35,20 +36,22 @@ class BrushAssetShelf: layout.menu_contents("VIEW3D_MT_brush_context_menu") @staticmethod - def get_shelf_name_from_mode(obmode): + def get_shelf_name_from_context(context): mode_map = { 'SCULPT': "VIEW3D_AST_brush_sculpt", - 'SCULPT_CURVES': "VIEW3D_AST_brush_sculpt_curves", - 'VERTEX_PAINT': "VIEW3D_AST_brush_vertex_paint", - 'WEIGHT_PAINT': "VIEW3D_AST_brush_weight_paint", - 'TEXTURE_PAINT': "VIEW3D_AST_brush_texture_paint", + 'PAINT_VERTEX': "VIEW3D_AST_brush_vertex_paint", + 'PAINT_WEIGHT': "VIEW3D_AST_brush_weight_paint", + 'PAINT_TEXTURE': "VIEW3D_AST_brush_texture_paint", + 'PAINT_2D': "IMAGE_AST_brush_paint", 'PAINT_GPENCIL': "VIEW3D_AST_brush_gpencil_paint", - 'PAINT_GREASE_PENCIL': "VIEW3D_AST_brush_grease_pencil_paint", 'SCULPT_GPENCIL': "VIEW3D_AST_brush_gpencil_sculpt", - 'VERTEX_GPENCIL': "VIEW3D_AST_brush_gpencil_vertex", 'WEIGHT_GPENCIL': "VIEW3D_AST_brush_gpencil_weight", + 'VERTEX_GPENCIL': "VIEW3D_AST_brush_gpencil_vertex", + 'SCULPT_CURVES': "VIEW3D_AST_brush_sculpt_curves", + 'PAINT_GREASE_PENCIL': "VIEW3D_AST_brush_grease_pencil_paint", } - return mode_map[obmode] + mode = UnifiedPaintPanel.get_brush_mode(context) + return mode_map[mode] class UnifiedPaintPanel: @@ -202,7 +205,7 @@ class BrushSelectPanel(BrushPanel): preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' col.template_asset_shelf_popover( - BrushAssetShelf.get_shelf_name_from_mode(context.object.mode), + BrushAssetShelf.get_shelf_name_from_context(context), icon=fallback_icon, icon_value=preview_icon_id, ) diff --git a/scripts/startup/bl_ui/space_image.py b/scripts/startup/bl_ui/space_image.py index 05125b7d00d..01cae1918d0 100644 --- a/scripts/startup/bl_ui/space_image.py +++ b/scripts/startup/bl_ui/space_image.py @@ -770,7 +770,7 @@ class _draw_tool_settings_context_mode: preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' layout.template_asset_shelf_popover( - BrushAssetShelf.get_shelf_name_from_mode(context.object.mode), + BrushAssetShelf.get_shelf_name_from_context(context), name=brush.name if brush else None, icon=fallback_icon, icon_value=preview_icon_id, @@ -1229,8 +1229,8 @@ class IMAGE_PT_paint_settings_advanced(Panel, ImagePaintPanel): settings = context.tool_settings.image_paint brush = settings.brush - - brush_settings_advanced(layout.column(), context, brush, self.is_popover) + if brush: + brush_settings_advanced(layout.column(), context, brush, self.is_popover) class IMAGE_PT_paint_color(Panel, ImagePaintPanel): @@ -1243,16 +1243,17 @@ class IMAGE_PT_paint_color(Panel, ImagePaintPanel): def poll(cls, context): settings = context.tool_settings.image_paint brush = settings.brush + if not brush: + return False capabilities = brush.image_paint_capabilities - return capabilities.has_color def draw(self, context): layout = self.layout settings = context.tool_settings.image_paint brush = settings.brush - - draw_color_settings(context, layout, brush, color_type=True) + if brush: + draw_color_settings(context, layout, brush, color_type=True) class IMAGE_PT_paint_swatches(Panel, ImagePaintPanel, ColorPalettePanel): @@ -1701,10 +1702,13 @@ class ImageAssetShelf(BrushAssetShelf): bl_space_type = "IMAGE_EDITOR" -class IMAGE_AST_brush_sculpt(ImageAssetShelf, AssetShelf): - mode = 'TEXTURE_PAINT' +class IMAGE_AST_brush_paint(ImageAssetShelf, AssetShelf): mode_prop = "use_paint_image" + @classmethod + def poll(cls, context): + return context.space_data and context.space_data.ui_mode == 'PAINT' + classes = ( IMAGE_MT_view, @@ -1775,7 +1779,7 @@ classes = ( IMAGE_PT_overlay_uv_edit_geometry, IMAGE_PT_overlay_texture_paint, IMAGE_PT_overlay_image, - IMAGE_AST_brush_sculpt, + IMAGE_AST_brush_paint, ) diff --git a/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/scripts/startup/bl_ui/space_toolsystem_toolbar.py index f351d01e3dc..e35e7ca4d49 100644 --- a/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -1957,7 +1957,7 @@ class _defs_paint_grease_pencil: preview_icon_id = brush.preview.icon_id if brush.preview else 0 fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' row.template_asset_shelf_popover( - BrushAssetShelf.get_shelf_name_from_mode(context.object.mode), + BrushAssetShelf.get_shelf_name_from_context(context), name=brush.name, icon=fallback_icon, icon_value=preview_icon_id, @@ -2347,7 +2347,7 @@ class _defs_gpencil_paint: preview_icon_id = brush.preview.icon_id if brush.preview else 0 fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' layout.template_asset_shelf_popover( - BrushAssetShelf.get_shelf_name_from_mode(context.object.mode), + BrushAssetShelf.get_shelf_name_from_context(context), name=brush.name, icon=fallback_icon, icon_value=preview_icon_id, diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index 459465d8535..21c653decdb 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -242,7 +242,7 @@ class _draw_tool_settings_context_mode: preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' layout.template_asset_shelf_popover( - BrushAssetShelf.get_shelf_name_from_mode(context.object.mode), + BrushAssetShelf.get_shelf_name_from_context(context), name=brush_name, icon=fallback_icon, icon_value=preview_icon_id, @@ -312,7 +312,7 @@ class _draw_tool_settings_context_mode: preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' layout.template_asset_shelf_popover( - BrushAssetShelf.get_shelf_name_from_mode(context.object.mode), + BrushAssetShelf.get_shelf_name_from_context(context), name=brush_name, icon=fallback_icon, icon_value=preview_icon_id, @@ -337,7 +337,7 @@ class _draw_tool_settings_context_mode: preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' layout.template_asset_shelf_popover( - BrushAssetShelf.get_shelf_name_from_mode(context.object.mode), + BrushAssetShelf.get_shelf_name_from_context(context), name=brush_name, icon=fallback_icon, icon_value=preview_icon_id, @@ -362,7 +362,7 @@ class _draw_tool_settings_context_mode: preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' layout.template_asset_shelf_popover( - BrushAssetShelf.get_shelf_name_from_mode(context.object.mode), + BrushAssetShelf.get_shelf_name_from_context(context), name=brush_name, icon=fallback_icon, icon_value=preview_icon_id, @@ -433,7 +433,7 @@ class _draw_tool_settings_context_mode: preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' layout.template_asset_shelf_popover( - BrushAssetShelf.get_shelf_name_from_mode(context.object.mode), + BrushAssetShelf.get_shelf_name_from_context(context), name=brush.name, icon=fallback_icon, icon_value=preview_icon_id, @@ -469,7 +469,7 @@ class _draw_tool_settings_context_mode: preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' layout.template_asset_shelf_popover( - BrushAssetShelf.get_shelf_name_from_mode(context.object.mode), + BrushAssetShelf.get_shelf_name_from_context(context), name=brush_name, icon=fallback_icon, icon_value=preview_icon_id, @@ -495,7 +495,7 @@ class _draw_tool_settings_context_mode: preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' layout.template_asset_shelf_popover( - BrushAssetShelf.get_shelf_name_from_mode(context.object.mode), + BrushAssetShelf.get_shelf_name_from_context(context), name=brush.name, icon=fallback_icon, icon_value=preview_icon_id, @@ -559,7 +559,7 @@ class _draw_tool_settings_context_mode: preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' layout.template_asset_shelf_popover( - BrushAssetShelf.get_shelf_name_from_mode(context.object.mode), + BrushAssetShelf.get_shelf_name_from_context(context), name=brush.name, icon=fallback_icon, icon_value=preview_icon_id, @@ -585,7 +585,7 @@ class _draw_tool_settings_context_mode: preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' layout.template_asset_shelf_popover( - BrushAssetShelf.get_shelf_name_from_mode(context.object.mode), + BrushAssetShelf.get_shelf_name_from_context(context), name=brush.name, icon=fallback_icon, icon_value=preview_icon_id, @@ -613,7 +613,7 @@ class _draw_tool_settings_context_mode: preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' layout.template_asset_shelf_popover( - BrushAssetShelf.get_shelf_name_from_mode(context.object.mode), + BrushAssetShelf.get_shelf_name_from_context(context), name=brush.name, icon=fallback_icon, icon_value=preview_icon_id, @@ -774,7 +774,7 @@ class _draw_tool_settings_context_mode: preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' layout.template_asset_shelf_popover( - BrushAssetShelf.get_shelf_name_from_mode(context.object.mode), + BrushAssetShelf.get_shelf_name_from_context(context), name=brush.name, icon=fallback_icon, icon_value=preview_icon_id, -- 2.30.2 From 75b0949ec474493f27b87d51e1c7e3c5b3ea0c5b Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 3 May 2024 23:24:29 +0200 Subject: [PATCH 230/244] Adjust asset shelf type polling for popups after changes in main --- .../blender/editors/asset/ED_asset_shelf.hh | 11 +++--- .../editors/asset/intern/asset_shelf.cc | 39 +++++++++++++++---- .../editors/asset/intern/asset_shelf_popup.cc | 6 +-- .../interface_template_asset_shelf_popover.cc | 6 +-- 4 files changed, 41 insertions(+), 21 deletions(-) diff --git a/source/blender/editors/asset/ED_asset_shelf.hh b/source/blender/editors/asset/ED_asset_shelf.hh index 1ec31672f68..0c4d8817197 100644 --- a/source/blender/editors/asset/ED_asset_shelf.hh +++ b/source/blender/editors/asset/ED_asset_shelf.hh @@ -70,15 +70,16 @@ void header_regiontype_register(ARegionType *region_type, const int space_type); /* -------------------------------------------------------------------- */ /** \name Asset Shelf Type * \{ */ - + void type_register(std::unique_ptr type); void type_unregister(const AssetShelfType &shelf_type); /** - * Poll an asset shelf type for display as a permanent region in a space of a given type (the - * type's #bl_space_type). + * Poll an asset shelf type for display as a popup. Doesn't check for space-type (the type's + * #bl_space_type) since popups should ignore this to allow displaying in any space. + * + * Permanent/non-popup asset shelf regions should use #type_poll_for_space_type() instead. */ -bool type_poll(const bContext &C, const AssetShelfType *shelf_type, const int space_type); - +bool type_poll_for_popup(const bContext &C, const AssetShelfType *shelf_type); AssetShelfType *type_find_from_idname(const StringRef idname); /** \} */ diff --git a/source/blender/editors/asset/intern/asset_shelf.cc b/source/blender/editors/asset/intern/asset_shelf.cc index 460055f7a40..1945c3a67ce 100644 --- a/source/blender/editors/asset/intern/asset_shelf.cc +++ b/source/blender/editors/asset/intern/asset_shelf.cc @@ -77,14 +77,11 @@ void type_unregister(const AssetShelfType &shelf_type) shelf_types.remove(it - shelf_types.begin()); } -bool type_poll(const bContext &C, const AssetShelfType *shelf_type, const int space_type) +static bool type_poll_no_spacetype_check(const bContext &C, const AssetShelfType *shelf_type) { if (!shelf_type) { return false; } - if (shelf_type->space_type && (space_type != shelf_type->space_type)) { - return false; - } #ifndef NDEBUG const Vector> &shelf_types = static_shelf_types(); @@ -99,6 +96,31 @@ bool type_poll(const bContext &C, const AssetShelfType *shelf_type, const int sp return !shelf_type->poll || shelf_type->poll(&C, shelf_type); } +bool type_poll_for_popup(const bContext &C, const AssetShelfType *shelf_type) +{ + return type_poll_no_spacetype_check(C, shelf_type); +} + +/** + * Poll an asset shelf type for display as a permanent region in a space of a given type (the + * type's #bl_space_type). + * + * Popup asset shelves should use #type_poll_for_popup() instead. + */ +static bool type_poll_for_non_popup(const bContext &C, + const AssetShelfType *shelf_type, + const int space_type) +{ + if (!shelf_type) { + return false; + } + if (shelf_type->space_type && (space_type != shelf_type->space_type)) { + return false; + } + + return type_poll_no_spacetype_check(C, shelf_type); +} + AssetShelfType *type_find_from_idname(const StringRef idname) { for (const std::unique_ptr &shelf_type : static_shelf_types()) { @@ -187,7 +209,8 @@ static AssetShelf *update_active_shelf(const bContext &C, /* Case 1: */ if (shelf_regiondata.active_shelf && - type_poll(C, ensure_shelf_has_type(*shelf_regiondata.active_shelf), space_type)) + type_poll_for_non_popup( + C, ensure_shelf_has_type(*shelf_regiondata.active_shelf), space_type)) { /* Not a strong precondition, but if this is wrong something weird might be going on. */ BLI_assert(shelf_regiondata.active_shelf == shelf_regiondata.shelves.first); @@ -201,7 +224,7 @@ static AssetShelf *update_active_shelf(const bContext &C, continue; } - if (type_poll(C, ensure_shelf_has_type(*shelf), space_type)) { + if (type_poll_for_non_popup(C, ensure_shelf_has_type(*shelf), space_type)) { /* Found a valid previously activated shelf, reactivate it. */ activate_shelf(shelf_regiondata, *shelf); return shelf; @@ -210,7 +233,7 @@ static AssetShelf *update_active_shelf(const bContext &C, /* Case 3: */ for (const std::unique_ptr &shelf_type : static_shelf_types()) { - if (type_poll(C, shelf_type.get(), space_type)) { + if (type_poll_for_non_popup(C, shelf_type.get(), space_type)) { AssetShelf *new_shelf = create_shelf_from_type(*shelf_type); BLI_addhead(&shelf_regiondata.shelves, new_shelf); /* Moves ownership to the regiondata. */ @@ -259,7 +282,7 @@ static bool asset_shelf_space_poll(const bContext *C, const SpaceLink *space_lin { /* Is there any asset shelf type registered that returns true for it's poll? */ for (const std::unique_ptr &shelf_type : static_shelf_types()) { - if (type_poll(*C, shelf_type.get(), space_link->spacetype)) { + if (type_poll_for_non_popup(*C, shelf_type.get(), space_link->spacetype)) { return true; } } diff --git a/source/blender/editors/asset/intern/asset_shelf_popup.cc b/source/blender/editors/asset/intern/asset_shelf_popup.cc index 27b7de6698e..6048cc7026b 100644 --- a/source/blender/editors/asset/intern/asset_shelf_popup.cc +++ b/source/blender/editors/asset/intern/asset_shelf_popup.cc @@ -53,20 +53,18 @@ void type_popup_unlink(const AssetShelfType &shelf_type) static AssetShelf *get_shelf_for_popup(const bContext *C, AssetShelfType &shelf_type) { - const SpaceType *space_type = BKE_spacetype_from_id(shelf_type.space_type); - Vector &popup_shelves = StaticPopupShelves::shelves(); for (AssetShelf *shelf : popup_shelves) { if (STREQ(shelf->idname, shelf_type.idname)) { - if (type_poll(*C, *space_type, type_ensure(*space_type, *shelf))) { + if (type_poll_for_popup(*C, ensure_shelf_has_type(*shelf))) { return shelf; } break; } } - if (type_poll(*C, *space_type, &shelf_type)) { + if (type_poll_for_popup(*C, &shelf_type)) { AssetShelf *new_shelf = create_shelf_from_type(shelf_type); popup_shelves.append(new_shelf); return new_shelf; diff --git a/source/blender/editors/interface/templates/interface_template_asset_shelf_popover.cc b/source/blender/editors/interface/templates/interface_template_asset_shelf_popover.cc index 8f471e21cea..421a5d00547 100644 --- a/source/blender/editors/interface/templates/interface_template_asset_shelf_popover.cc +++ b/source/blender/editors/interface/templates/interface_template_asset_shelf_popover.cc @@ -31,9 +31,7 @@ void template_asset_shelf_popover(uiLayout &layout, const StringRef name, const BIFIconID icon) { - const ScrArea *area = CTX_wm_area(&C); - AssetShelfType *shelf_type = ed::asset::shelf::type_find_from_idname(*area->type, - asset_shelf_id); + AssetShelfType *shelf_type = ed::asset::shelf::type_find_from_idname(asset_shelf_id); if (!shelf_type) { RNA_warning("Asset shelf type not found: %s", asset_shelf_id.c_str()); return; @@ -61,7 +59,7 @@ void template_asset_shelf_popover(uiLayout &layout, UI_but_drawflag_enable(but, UI_BUT_ICON_LEFT); } - if (ed::asset::shelf::type_poll(C, *area->type, shelf_type) == false) { + if (ed::asset::shelf::type_poll_for_popup(C, shelf_type) == false) { UI_but_flag_enable(but, UI_BUT_DISABLED); } } -- 2.30.2 From 5e6dbe5ebf153653d51d0e18470a7a61b06ce455 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 3 May 2024 23:32:29 +0200 Subject: [PATCH 231/244] Cleanup: Unnecessary include --- source/blender/editors/interface/views/interface_view.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/source/blender/editors/interface/views/interface_view.cc b/source/blender/editors/interface/views/interface_view.cc index 583537200af..8f198bfcc2c 100644 --- a/source/blender/editors/interface/views/interface_view.cc +++ b/source/blender/editors/interface/views/interface_view.cc @@ -22,7 +22,6 @@ #include #include "DNA_screen_types.h" -#include "DNA_windowmanager_types.h" #include "BKE_screen.hh" -- 2.30.2 From 887590c5fa25250ae42e5f4f573a106cea6f409f Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 6 May 2024 15:39:57 +0200 Subject: [PATCH 232/244] Polish layout of asset shelf popup - Ensure all items in the grid view have the correct width. Also fixes the issue where items in the last row had a different width. - Fix width of catalog tree (would sometimes increase/decrease width) - Improve padding around popup content - Properly attach the popup to the button Note that previews are bigger now, think the same size as in other search boxes with previews. We can decide on the default size still. --- .../editors/asset/intern/asset_shelf_popup.cc | 20 ++++++++++++------- .../editors/interface/views/grid_view.cc | 8 +++++++- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/source/blender/editors/asset/intern/asset_shelf_popup.cc b/source/blender/editors/asset/intern/asset_shelf_popup.cc index 6048cc7026b..71fdad5b5bf 100644 --- a/source/blender/editors/asset/intern/asset_shelf_popup.cc +++ b/source/blender/editors/asset/intern/asset_shelf_popup.cc @@ -171,31 +171,37 @@ uiBlock *popup_block_create(const bContext *C, ARegion *region, AssetShelfType * const uiStyle *style = UI_style_get_dpi(); - const float pad = 0.2f * UI_UNIT_Y; /* UI_MENU_PADDING */ + const int layout_width = UI_UNIT_X * 40; + const int left_col_width = 10 * UI_UNIT_X; + const int right_col_width = layout_width - left_col_width; uiLayout *layout = UI_block_layout( - block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, pad, 0, UI_UNIT_X * 40, 0, pad / 2, style); + block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, layout_width, 0, 0, style); PointerRNA library_ref_ptr = RNA_pointer_create( &screen->id, &RNA_AssetLibraryReference, &shelf->settings.asset_library_reference); uiLayoutSetContextPointer(layout, "asset_library_reference", &library_ref_ptr); uiLayout *row = uiLayoutRow(layout, false); - uiLayout *sub = uiLayoutRow(row, false); - uiLayoutSetUnitsX(sub, 10); - uiLayoutSetFixedSize(sub, true); - uiLayout *catalogs_col = uiLayoutColumn(sub, false); + uiLayout *catalogs_col = uiLayoutColumn(row, false); + uiLayoutSetUnitsX(catalogs_col, left_col_width / UI_UNIT_X); + uiLayoutSetFixedSize(catalogs_col, true); library_selector_draw(C, catalogs_col, *shelf); catalog_tree_draw(*catalogs_col, *shelf); uiLayout *right_col = uiLayoutColumn(row, false); - sub = uiLayoutRow(right_col, false); + uiLayout *sub = uiLayoutRow(right_col, false); /* Same as file/asset browser header. */ PointerRNA shelf_ptr = RNA_pointer_create(&screen->id, &RNA_AssetShelf, shelf); uiItemR(sub, &shelf_ptr, "search_filter", UI_ITEM_R_IMMEDIATE, "", ICON_VIEWZOOM); uiLayout *asset_view_col = uiLayoutColumn(right_col, false); + uiLayoutSetUnitsX(asset_view_col, right_col_width / UI_UNIT_X); + uiLayoutSetFixedSize(asset_view_col, true); build_asset_view(*asset_view_col, shelf->settings.asset_library_reference, *shelf, *C, *region); + UI_block_bounds_set_normal(block, 0.3f * U.widget_unit); + UI_block_direction_set(block, UI_DIR_DOWN); + return block; } diff --git a/source/blender/editors/interface/views/grid_view.cc b/source/blender/editors/interface/views/grid_view.cc index 4747c7852e1..e2b0da22b43 100644 --- a/source/blender/editors/interface/views/grid_view.cc +++ b/source/blender/editors/interface/views/grid_view.cc @@ -356,7 +356,13 @@ void GridViewLayoutBuilder::build_from_view(const AbstractGridView &grid_view, uiLayout &layout = *uiLayoutColumn(parent_layout, true); const GridViewStyle &style = grid_view.get_style(); - const int cols_per_row = std::max(uiLayoutGetWidth(&layout) / style.tile_width, 1); + /* We might not actually know the width available for the grid view. Let's just assume that + * either there is a fixed width defined via #uiLayoutSetUnitsX() or that the layout is close to + * the root level and inherits its width. Might need a more reliable method. */ + const int guessed_layout_width = (uiLayoutGetUnitsX(parent_layout) > 0) ? + uiLayoutGetUnitsX(parent_layout) * UI_UNIT_X : + uiLayoutGetWidth(parent_layout); + const int cols_per_row = std::max(guessed_layout_width / style.tile_width, 1); BuildOnlyVisibleButtonsHelper build_visible_helper(v2d, grid_view, cols_per_row); -- 2.30.2 From 2718b2e5c8a03ed26789cb6602e0014c98a81405 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 6 May 2024 15:49:37 +0200 Subject: [PATCH 233/244] Always show names in asset shelf popup Not showing this is mostly for space saving and aesthetic reasons, which are not as relevant for the popup that isn't permanently on screen. --- source/blender/editors/asset/intern/asset_shelf_popup.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/editors/asset/intern/asset_shelf_popup.cc b/source/blender/editors/asset/intern/asset_shelf_popup.cc index 71fdad5b5bf..af6098d7f10 100644 --- a/source/blender/editors/asset/intern/asset_shelf_popup.cc +++ b/source/blender/editors/asset/intern/asset_shelf_popup.cc @@ -66,6 +66,7 @@ static AssetShelf *get_shelf_for_popup(const bContext *C, AssetShelfType &shelf_ if (type_poll_for_popup(*C, &shelf_type)) { AssetShelf *new_shelf = create_shelf_from_type(shelf_type); + new_shelf->settings.display_flag |= ASSETSHELF_SHOW_NAMES; popup_shelves.append(new_shelf); return new_shelf; } -- 2.30.2 From 6f07be5cbdcfb12080f3a93276a0a64b0b3e4f39 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 6 May 2024 15:54:06 +0200 Subject: [PATCH 234/244] Cleanup: Fix up some outdated names, comments and includes --- source/blender/blenkernel/BKE_asset_edit.hh | 2 +- source/blender/blenkernel/BKE_main.hh | 13 ++++++++----- source/blender/blenkernel/intern/asset_edit.cc | 12 ++++++------ source/blender/blenkernel/intern/blender.cc | 1 - source/blender/blenkernel/intern/lib_id.cc | 8 ++++---- source/blender/blenkernel/intern/main.cc | 2 +- source/blender/blenkernel/intern/paint.cc | 7 ++----- source/blender/blenkernel/intern/scene.cc | 3 --- .../interface/templates/interface_templates.cc | 2 -- .../blender/editors/sculpt_paint/brush_asset_ops.cc | 4 ++-- source/blender/editors/space_image/image_ops.cc | 1 - source/blender/makesdna/DNA_ID.h | 2 +- source/blender/makesrna/intern/rna_ID.cc | 3 +-- source/blender/makesrna/intern/rna_main_api.cc | 2 -- 14 files changed, 26 insertions(+), 36 deletions(-) diff --git a/source/blender/blenkernel/BKE_asset_edit.hh b/source/blender/blenkernel/BKE_asset_edit.hh index d64d989c8a9..113f1407015 100644 --- a/source/blender/blenkernel/BKE_asset_edit.hh +++ b/source/blender/blenkernel/BKE_asset_edit.hh @@ -14,7 +14,7 @@ * * Each asset blend file is loaded into a separate main database, including the * asset datablocks and their dependencies. These datablocks are all tagged with - * LIB_TAG_ASSET_MAIN. These can not be linked with other datablocks in the + * LIB_TAG_ASSET_EDIT_MAIN. These can not be linked with other datablocks in the * current blend file. * * For editable assets in user asset libraries, each asset is stored in its own diff --git a/source/blender/blenkernel/BKE_main.hh b/source/blender/blenkernel/BKE_main.hh index 85820619100..b977ed7a3d4 100644 --- a/source/blender/blenkernel/BKE_main.hh +++ b/source/blender/blenkernel/BKE_main.hh @@ -194,7 +194,7 @@ struct Main { /** * True if main used to store weakly referenced assets. */ - bool is_asset_weak_reference_main; + bool is_asset_edit_main; BlendThumbnail *blen_thumb; @@ -516,11 +516,14 @@ int set_listbasepointers(Main *main, ListBase *lb[]); /** * Return main database this ID is a member of. * - * Use this in operator and draw code instead of assuming the main - * in the context owns datablocks. Some datablock can be part of - * main datablocks from asset libraries instead. + * This works for the global main database and asset edit databases. + * So only datablocks that are directly editable in the user interface. * - * Optionally can verify membership of global_main, but this is expensive. + * Use this in operator and draw code instead of assuming the main + * in the context owns datablocks. + * + * Optionally can verify that this datablock is one of these databases. + * This is slow and mainly meant for asserts. */ Main *BKE_main_from_id(Main *global_main, const ID *id, bool verify = false); diff --git a/source/blender/blenkernel/intern/asset_edit.cc b/source/blender/blenkernel/intern/asset_edit.cc index 1fbffd702a6..ce5f305fa37 100644 --- a/source/blender/blenkernel/intern/asset_edit.cc +++ b/source/blender/blenkernel/intern/asset_edit.cc @@ -66,7 +66,7 @@ struct AssetEditBlend { AssetEditBlend::AssetEditBlend(const std::string &filepath) : filepath(std::move(filepath)), main(BKE_main_new()) { - this->main->is_asset_weak_reference_main = true; + this->main->is_asset_edit_main = true; BLI_assert(!BLI_path_is_rel(filepath.c_str())); /* Simple check, based on being a writable .asset.blend file in a user asset library. */ @@ -125,7 +125,7 @@ ID *AssetEditBlend::ensure_id(const ID_Type id_type, const char *asset_name) BKE_blendfile_link_append_context_free(lapp_context); - BKE_main_id_tag_all(this->main, LIB_TAG_ASSET_MAIN, true); + BKE_main_id_tag_all(this->main, LIB_TAG_ASSET_EDIT_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)); @@ -294,7 +294,7 @@ void AssetEditBlend::reload(Main &global_main) { Main *old_main = this->main; this->main = BKE_main_new(); - this->main->is_asset_weak_reference_main = true; + this->main->is_asset_edit_main = true; /* Fill fresh main database with same datablock as before. */ LibraryLink_Params lapp_params{}; @@ -322,7 +322,7 @@ void AssetEditBlend::reload(Main &global_main) BKE_blendfile_link_append_context_free(lapp_context); - BKE_main_id_tag_all(this->main, LIB_TAG_ASSET_MAIN, true); + BKE_main_id_tag_all(this->main, LIB_TAG_ASSET_EDIT_MAIN, true); /* Remap old to new. */ bke::id::IDRemapper mappings; @@ -361,7 +361,7 @@ static Vector &asset_edit_blend_get_all() static AssetEditBlend *asset_edit_blend_from_id(const ID &id) { - BLI_assert(id.tag & LIB_TAG_ASSET_MAIN); + BLI_assert(id.tag & LIB_TAG_ASSET_EDIT_MAIN); /* TODO: It would be good to make this more efficient, though it's unlikely to be a bottleneck * for brush assets. It's not easy to add a hash map here because it needs to be kept up to date @@ -515,7 +515,7 @@ ID *asset_edit_id_from_weak_reference(Main &global_main, bool asset_edit_id_is_editable(const ID &id) { - if (!(id.tag & LIB_TAG_ASSET_MAIN)) { + if (!(id.tag & LIB_TAG_ASSET_EDIT_MAIN)) { return false; } diff --git a/source/blender/blenkernel/intern/blender.cc b/source/blender/blenkernel/intern/blender.cc index efc7ef907fa..70eaadb689e 100644 --- a/source/blender/blenkernel/intern/blender.cc +++ b/source/blender/blenkernel/intern/blender.cc @@ -34,7 +34,6 @@ #include "BKE_idprop.hh" #include "BKE_main.hh" #include "BKE_node.hh" -#include "BKE_preferences.h" #include "BKE_report.hh" #include "BKE_screen.hh" #include "BKE_studiolight.h" diff --git a/source/blender/blenkernel/intern/lib_id.cc b/source/blender/blenkernel/intern/lib_id.cc index 6be80746ff6..cc7d73e8309 100644 --- a/source/blender/blenkernel/intern/lib_id.cc +++ b/source/blender/blenkernel/intern/lib_id.cc @@ -1328,8 +1328,8 @@ void *BKE_libblock_alloc_in_lib(Main *bmain, if ((flag & LIB_ID_CREATE_NO_DEG_TAG) == 0) { DEG_id_type_tag(bmain, type); } - if (bmain->is_asset_weak_reference_main) { - id->tag |= LIB_TAG_ASSET_MAIN; + if (bmain->is_asset_edit_main) { + id->tag |= LIB_TAG_ASSET_EDIT_MAIN; } } else { @@ -1563,8 +1563,8 @@ void BKE_libblock_copy_in_lib(Main *bmain, DEG_id_type_tag(bmain, GS(new_id->name)); } - if (bmain && bmain->is_asset_weak_reference_main) { - new_id->tag |= LIB_TAG_ASSET_MAIN; + if (bmain && bmain->is_asset_edit_main) { + new_id->tag |= LIB_TAG_ASSET_EDIT_MAIN; } *r_newid = new_id; diff --git a/source/blender/blenkernel/intern/main.cc b/source/blender/blenkernel/intern/main.cc index 586160a0e76..bcc56d96494 100644 --- a/source/blender/blenkernel/intern/main.cc +++ b/source/blender/blenkernel/intern/main.cc @@ -972,7 +972,7 @@ Main *BKE_main_from_id(Main *global_main, const ID *id, const bool verify) if (id == nullptr || (id->tag & LIB_TAG_NO_MAIN)) { return nullptr; } - if (id->tag & LIB_TAG_ASSET_MAIN) { + if (id->tag & LIB_TAG_ASSET_EDIT_MAIN) { return blender::bke::asset_edit_main(*id); } diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index ec42091863b..3622cb00d49 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -624,7 +624,7 @@ static bool paint_brush_set_from_asset_reference(Main *bmain, Paint *paint) Brush *brush = reinterpret_cast(blender::bke::asset_edit_id_from_weak_reference( *bmain, ID_BR, *paint->brush_asset_reference)); - BLI_assert(brush == nullptr || (brush->id.tag & LIB_TAG_ASSET_MAIN)); + BLI_assert(brush == nullptr || (brush->id.tag & LIB_TAG_ASSET_EDIT_MAIN)); /* Ensure we have a brush with appropriate mode to assign. * Could happen if contents of asset blend was manually changed. */ @@ -669,7 +669,7 @@ static void paint_brush_asset_update(Paint &paint, MEM_delete(paint.brush_asset_reference); paint.brush_asset_reference = nullptr; - if (brush == nullptr || brush != paint.brush || !(brush->id.tag & LIB_TAG_ASSET_MAIN)) { + if (brush == nullptr || brush != paint.brush || !(brush->id.tag & LIB_TAG_ASSET_EDIT_MAIN)) { return; } @@ -724,9 +724,6 @@ static void paint_brush_set_default_reference(Paint *paint) case OB_MODE_SCULPT_CURVES: name = "Comb Curves"; break; - case OB_MODE_EDIT: - /* TODO: UV sculpt. */ - break; case OB_MODE_PAINT_GPENCIL_LEGACY: name = "Pencil"; break; diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc index 53561901d26..ca5a58e5f79 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -987,9 +987,6 @@ static void scene_foreach_path(ID *id, BPathForeachPathData *bpath_data) if (scene->ed != nullptr) { SEQ_for_each_callback(&scene->ed->seqbase, seq_foreach_path_callback, bpath_data); } - - /* TODO: Also handle `asset_weak_reference` here? Probably not, conceptually this is not a file - * path. */ } static void scene_blend_write(BlendWriter *writer, ID *id, const void *id_address) diff --git a/source/blender/editors/interface/templates/interface_templates.cc b/source/blender/editors/interface/templates/interface_templates.cc index 5abd6c2cf29..72bc4ff39aa 100644 --- a/source/blender/editors/interface/templates/interface_templates.cc +++ b/source/blender/editors/interface/templates/interface_templates.cc @@ -44,8 +44,6 @@ #include "BLF_api.hh" #include "BLT_translation.hh" -#include "BKE_action.h" -#include "BKE_asset.hh" #include "BKE_blender_version.h" #include "BKE_blendfile.hh" #include "BKE_colorband.hh" diff --git a/source/blender/editors/sculpt_paint/brush_asset_ops.cc b/source/blender/editors/sculpt_paint/brush_asset_ops.cc index b606d33f026..94eab32519b 100644 --- a/source/blender/editors/sculpt_paint/brush_asset_ops.cc +++ b/source/blender/editors/sculpt_paint/brush_asset_ops.cc @@ -663,7 +663,7 @@ static bool brush_asset_update_poll(bContext *C) return false; } - if ((brush->id.tag & LIB_TAG_ASSET_MAIN) == 0) { + if ((brush->id.tag & LIB_TAG_ASSET_EDIT_MAIN) == 0) { return false; } @@ -721,7 +721,7 @@ static bool brush_asset_revert_poll(bContext *C) return false; } - return paint->brush_asset_reference && (brush->id.tag & LIB_TAG_ASSET_MAIN); + return paint->brush_asset_reference && (brush->id.tag & LIB_TAG_ASSET_EDIT_MAIN); } static int brush_asset_revert_exec(bContext *C, wmOperator *op) diff --git a/source/blender/editors/space_image/image_ops.cc b/source/blender/editors/space_image/image_ops.cc index 6582d19d0b9..eccfc260fb8 100644 --- a/source/blender/editors/space_image/image_ops.cc +++ b/source/blender/editors/space_image/image_ops.cc @@ -34,7 +34,6 @@ #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.hh" diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index d8c75f069de..64be752dbd2 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -1008,7 +1008,7 @@ enum { * 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 << 27, + LIB_TAG_ASSET_EDIT_MAIN = 1 << 27, /* ------------------------------------------------------------------------------------------- */ diff --git a/source/blender/makesrna/intern/rna_ID.cc b/source/blender/makesrna/intern/rna_ID.cc index 66c3af100b1..1601fe6864e 100644 --- a/source/blender/makesrna/intern/rna_ID.cc +++ b/source/blender/makesrna/intern/rna_ID.cc @@ -16,7 +16,6 @@ #include "BLI_utildefines.h" -#include "BKE_asset.hh" #include "BKE_icons.h" #include "BKE_lib_id.hh" #include "BKE_main_namemap.hh" @@ -2322,7 +2321,7 @@ static void rna_def_ID(BlenderRNA *brna) 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_boolean_sdna(prop, nullptr, "tag", LIB_TAG_ASSET_EDIT_MAIN); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Asset Library Data", diff --git a/source/blender/makesrna/intern/rna_main_api.cc b/source/blender/makesrna/intern/rna_main_api.cc index 07c1bd43aa5..101d108a42b 100644 --- a/source/blender/makesrna/intern/rna_main_api.cc +++ b/source/blender/makesrna/intern/rna_main_api.cc @@ -135,8 +135,6 @@ static void rna_Main_ID_remove(Main *bmain, 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); -- 2.30.2 From fa0a0d38f8b55f9c923893cfea346123e17d442f Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 6 May 2024 16:10:12 +0200 Subject: [PATCH 235/244] Cleanup: Move block options to top of function Actually more than cleanup, this ensures the same setup is done if the asset shelf can't be created/found. --- source/blender/editors/asset/intern/asset_shelf_popup.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/asset/intern/asset_shelf_popup.cc b/source/blender/editors/asset/intern/asset_shelf_popup.cc index af6098d7f10..c4c989a4a4e 100644 --- a/source/blender/editors/asset/intern/asset_shelf_popup.cc +++ b/source/blender/editors/asset/intern/asset_shelf_popup.cc @@ -163,6 +163,8 @@ uiBlock *popup_block_create(const bContext *C, ARegion *region, AssetShelfType * uiBlock *block = UI_block_begin(C, region, "_popup", UI_EMBOSS); UI_block_flag_enable(block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_POPOVER); UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP); + UI_block_bounds_set_normal(block, 0.3f * U.widget_unit); + UI_block_direction_set(block, UI_DIR_DOWN); AssetShelf *shelf = get_shelf_for_popup(C, *shelf_type); if (!shelf) { @@ -200,9 +202,6 @@ uiBlock *popup_block_create(const bContext *C, ARegion *region, AssetShelfType * uiLayoutSetFixedSize(asset_view_col, true); build_asset_view(*asset_view_col, shelf->settings.asset_library_reference, *shelf, *C, *region); - UI_block_bounds_set_normal(block, 0.3f * U.widget_unit); - UI_block_direction_set(block, UI_DIR_DOWN); - return block; } -- 2.30.2 From a6958a89ac0c092c1d94653d39ba9f71f90a7686 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 7 May 2024 14:40:38 +0200 Subject: [PATCH 236/244] Brush Assets: Add operator to choose brush preview image A simple operator like the existing `ed.lib_id_load_custom_preview` operator. I didn't reuse that because the requirements are a bit different. In particular we want to re-save the asset when the preview image is changed. The operator is available as a new entry in the existing brush asset context menu. Pull Request: https://projects.blender.org/blender/blender/pulls/121505 --- scripts/startup/bl_ui/space_view3d_toolbar.py | 1 + .../editors/sculpt_paint/brush_asset_ops.cc | 66 ++++++++++++++++++- .../editors/sculpt_paint/paint_intern.hh | 1 + .../blender/editors/sculpt_paint/paint_ops.cc | 1 + 4 files changed, 67 insertions(+), 2 deletions(-) diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index 00f52b7dcca..e4b02fd7eb9 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -54,6 +54,7 @@ class VIEW3D_MT_brush_context_menu(Menu): layout.separator() layout.operator("brush.asset_edit_metadata", text="Edit Metadata") + layout.operator("brush.asset_load_preview", text="Edit Preview Image...") layout.operator("brush.asset_update", text="Update Asset") layout.operator("brush.asset_revert", text="Revert to Asset") else: diff --git a/source/blender/editors/sculpt_paint/brush_asset_ops.cc b/source/blender/editors/sculpt_paint/brush_asset_ops.cc index 94eab32519b..cc288af6662 100644 --- a/source/blender/editors/sculpt_paint/brush_asset_ops.cc +++ b/source/blender/editors/sculpt_paint/brush_asset_ops.cc @@ -2,6 +2,7 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BLI_fileops.h" #include "BLI_path_util.h" #include "BLI_string.h" @@ -16,6 +17,7 @@ #include "BKE_context.hh" #include "BKE_paint.hh" #include "BKE_preferences.h" +#include "BKE_preview_image.hh" #include "BKE_report.hh" #include "AS_asset_catalog_path.hh" @@ -428,8 +430,8 @@ void BRUSH_OT_asset_save_as(wmOperatorType *ot) static int brush_asset_edit_metadata_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); - const Paint *paint = BKE_paint_get_active_from_context(C); - const Brush *brush = (paint) ? BKE_paint_brush_for_read(paint) : nullptr; + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *brush = BKE_paint_brush(paint); BLI_assert(ID_IS_ASSET(&brush->id)); const AssetWeakReference &brush_weak_ref = *paint->brush_asset_reference; const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref( @@ -586,6 +588,66 @@ void BRUSH_OT_asset_edit_metadata(wmOperatorType *ot) RNA_def_string(ot->srna, "description", nullptr, MAX_NAME, "Description", ""); } +static int brush_asset_load_preview_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); + BLI_assert(ID_IS_ASSET(&brush->id)); + const AssetWeakReference &brush_weak_ref = *paint->brush_asset_reference; + const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref( + *C, brush_weak_ref, op->reports); + if (!asset) { + return OPERATOR_CANCELLED; + } + const AssetLibraryReference library_ref = *library_to_library_ref(asset->owner_asset_library()); + + char filepath[FILE_MAX]; + RNA_string_get(op->ptr, "filepath", filepath); + if (!BLI_is_file(filepath)) { + BKE_reportf(op->reports, RPT_ERROR, "File not found '%s'", filepath); + return OPERATOR_CANCELLED; + } + + BKE_previewimg_id_custom_set(&brush->id, filepath); + + if (!bke::asset_edit_id_save(*bmain, brush->id, *op->reports)) { + return OPERATOR_CANCELLED; + } + + refresh_asset_library(C, library_ref); + WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_EDITED, nullptr); + + return OPERATOR_FINISHED; +} + +static int brush_asset_load_preview_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (RNA_struct_property_is_set(op->ptr, "filepath")) { + return brush_asset_load_preview_exec(C, op); + } + return WM_operator_filesel(C, op, event); +} + +void BRUSH_OT_asset_load_preview(wmOperatorType *ot) +{ + ot->name = "Load Preview Image"; + ot->description = "Choose a preview image for the brush"; + ot->idname = "BRUSH_OT_asset_load_preview"; + + ot->exec = brush_asset_load_preview_exec; + ot->invoke = brush_asset_load_preview_invoke; + ot->poll = brush_asset_edit_metadata_poll; + + WM_operator_properties_filesel(ot, + FILE_TYPE_FOLDER | FILE_TYPE_IMAGE, + FILE_SPECIAL, + FILE_OPENFILE, + WM_FILESEL_FILEPATH, + FILE_DEFAULTDISPLAY, + FILE_SORT_DEFAULT); +} + static bool brush_asset_delete_poll(bContext *C) { Paint *paint = BKE_paint_get_active_from_context(C); diff --git a/source/blender/editors/sculpt_paint/paint_intern.hh b/source/blender/editors/sculpt_paint/paint_intern.hh index 823531e5fc7..24a66a9a050 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.hh +++ b/source/blender/editors/sculpt_paint/paint_intern.hh @@ -111,6 +111,7 @@ bool paint_brush_tool_poll(bContext *C); void BRUSH_OT_asset_select(wmOperatorType *ot); void BRUSH_OT_asset_save_as(wmOperatorType *ot); void BRUSH_OT_asset_edit_metadata(wmOperatorType *ot); +void BRUSH_OT_asset_load_preview(wmOperatorType *ot); void BRUSH_OT_asset_delete(wmOperatorType *ot); void BRUSH_OT_asset_update(wmOperatorType *ot); void BRUSH_OT_asset_revert(wmOperatorType *ot); diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 58298435820..36495ee6e6a 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -979,6 +979,7 @@ void ED_operatortypes_paint() WM_operatortype_append(BRUSH_OT_asset_select); WM_operatortype_append(BRUSH_OT_asset_save_as); WM_operatortype_append(BRUSH_OT_asset_edit_metadata); + WM_operatortype_append(BRUSH_OT_asset_load_preview); WM_operatortype_append(BRUSH_OT_asset_delete); WM_operatortype_append(BRUSH_OT_asset_update); WM_operatortype_append(BRUSH_OT_asset_revert); -- 2.30.2 From 9ec37cd91981c6dbe3f2fd0aaa8e8272aa618098 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 7 May 2024 15:54:07 +0200 Subject: [PATCH 237/244] Cleanup: Avoid repeated asset shelf popup template placement Pretty easy to share code here. Pull Request: https://projects.blender.org/blender/blender/pulls/121515 --- .../startup/bl_ui/properties_paint_common.py | 19 ++-- scripts/startup/bl_ui/space_image.py | 9 +- .../startup/bl_ui/space_toolsystem_toolbar.py | 18 +-- scripts/startup/bl_ui/space_view3d.py | 104 ++---------------- 4 files changed, 26 insertions(+), 124 deletions(-) diff --git a/scripts/startup/bl_ui/properties_paint_common.py b/scripts/startup/bl_ui/properties_paint_common.py index 14cc8cb77c1..bcab5c42cec 100644 --- a/scripts/startup/bl_ui/properties_paint_common.py +++ b/scripts/startup/bl_ui/properties_paint_common.py @@ -53,6 +53,17 @@ class BrushAssetShelf: mode = UnifiedPaintPanel.get_brush_mode(context) return mode_map[mode] + @staticmethod + def draw_popup_selector(layout, context, brush, show_name=True): + preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 + + layout.template_asset_shelf_popover( + BrushAssetShelf.get_shelf_name_from_context(context), + name=brush.name if (brush and show_name) else None, + icon='BRUSH_DATA' if not preview_icon_id else 'NONE', + icon_value=preview_icon_id, + ) + class UnifiedPaintPanel: # subclass must set @@ -198,13 +209,7 @@ class BrushSelectPanel(BrushPanel): row = layout.row() col = row.column(align=True) - preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 - fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' - col.template_asset_shelf_popover( - BrushAssetShelf.get_shelf_name_from_context(context), - icon=fallback_icon, - icon_value=preview_icon_id, - ) + BrushAssetShelf.draw_popup_selector(col, context, brush, show_name=False) if brush: col.prop(brush, "name", text="") diff --git a/scripts/startup/bl_ui/space_image.py b/scripts/startup/bl_ui/space_image.py index 01cae1918d0..ca319eaa14f 100644 --- a/scripts/startup/bl_ui/space_image.py +++ b/scripts/startup/bl_ui/space_image.py @@ -767,14 +767,7 @@ class _draw_tool_settings_context_mode: paint = context.tool_settings.image_paint brush = paint.brush - preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 - fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' - layout.template_asset_shelf_popover( - BrushAssetShelf.get_shelf_name_from_context(context), - name=brush.name if brush else None, - icon=fallback_icon, - icon_value=preview_icon_id, - ) + BrushAssetShelf.draw_popup_selector(layout, context, brush) if brush is None: return diff --git a/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/scripts/startup/bl_ui/space_toolsystem_toolbar.py index e35e7ca4d49..a7795686cf8 100644 --- a/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -1954,14 +1954,7 @@ class _defs_paint_grease_pencil: tool_settings = context.scene.tool_settings settings = tool_settings.gpencil_paint - preview_icon_id = brush.preview.icon_id if brush.preview else 0 - fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' - row.template_asset_shelf_popover( - BrushAssetShelf.get_shelf_name_from_context(context), - name=brush.name, - icon=fallback_icon, - icon_value=preview_icon_id, - ) + BrushAssetShelf.draw_popup_selector(row, context, brush) from bl_ui.properties_paint_common import ( brush_basic_grease_pencil_paint_settings, @@ -2344,14 +2337,7 @@ class _defs_gpencil_paint: tool_settings = context.scene.tool_settings settings = tool_settings.gpencil_paint - preview_icon_id = brush.preview.icon_id if brush.preview else 0 - fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' - layout.template_asset_shelf_popover( - BrushAssetShelf.get_shelf_name_from_context(context), - name=brush.name, - icon=fallback_icon, - icon_value=preview_icon_id, - ) + BrushAssetShelf.draw_popup_selector(layout, context, brush) from bl_ui.properties_paint_common import ( brush_basic_gpencil_paint_settings, diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index 21c653decdb..b8bdb09e313 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -238,15 +238,7 @@ class _draw_tool_settings_context_mode: paint = context.tool_settings.sculpt brush = paint.brush - brush_name = brush.name if brush else None - preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 - fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' - layout.template_asset_shelf_popover( - BrushAssetShelf.get_shelf_name_from_context(context), - name=brush_name, - icon=fallback_icon, - icon_value=preview_icon_id, - ) + BrushAssetShelf.draw_popup_selector(layout, context, brush) if brush is None: return False @@ -308,15 +300,7 @@ class _draw_tool_settings_context_mode: paint = context.tool_settings.image_paint brush = paint.brush - brush_name = brush.name if brush else None - preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 - fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' - layout.template_asset_shelf_popover( - BrushAssetShelf.get_shelf_name_from_context(context), - name=brush_name, - icon=fallback_icon, - icon_value=preview_icon_id, - ) + BrushAssetShelf.draw_popup_selector(layout, context, brush) if brush is None: return False @@ -333,15 +317,7 @@ class _draw_tool_settings_context_mode: paint = context.tool_settings.vertex_paint brush = paint.brush - brush_name = brush.name if brush else None - preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 - fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' - layout.template_asset_shelf_popover( - BrushAssetShelf.get_shelf_name_from_context(context), - name=brush_name, - icon=fallback_icon, - icon_value=preview_icon_id, - ) + BrushAssetShelf.draw_popup_selector(layout, context, brush) if brush is None: return False @@ -358,15 +334,7 @@ class _draw_tool_settings_context_mode: paint = context.tool_settings.weight_paint brush = paint.brush - brush_name = brush.name if brush else None - preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 - fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' - layout.template_asset_shelf_popover( - BrushAssetShelf.get_shelf_name_from_context(context), - name=brush_name, - icon=fallback_icon, - icon_value=preview_icon_id, - ) + BrushAssetShelf.draw_popup_selector(layout, context, brush) if brush is None: return False @@ -430,14 +398,7 @@ class _draw_tool_settings_context_mode: row = layout.row(align=True) settings = tool_settings.gpencil_paint - preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 - fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' - layout.template_asset_shelf_popover( - BrushAssetShelf.get_shelf_name_from_context(context), - name=brush.name, - icon=fallback_icon, - icon_value=preview_icon_id, - ) + BrushAssetShelf.draw_popup_selector(layout, context, brush) if ob and brush.gpencil_tool in {'FILL', 'DRAW'}: from bl_ui.properties_paint_common import ( @@ -465,15 +426,7 @@ class _draw_tool_settings_context_mode: paint = tool_settings.gpencil_sculpt_paint brush = paint.brush - brush_name = brush.name if brush else None - preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 - fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' - layout.template_asset_shelf_popover( - BrushAssetShelf.get_shelf_name_from_context(context), - name=brush_name, - icon=fallback_icon, - icon_value=preview_icon_id, - ) + BrushAssetShelf.draw_popup_selector(layout, context, brush) from bl_ui.properties_paint_common import ( brush_basic_gpencil_sculpt_settings, @@ -492,14 +445,7 @@ class _draw_tool_settings_context_mode: if brush is None: return False - preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 - fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' - layout.template_asset_shelf_popover( - BrushAssetShelf.get_shelf_name_from_context(context), - name=brush.name, - icon=fallback_icon, - icon_value=preview_icon_id, - ) + BrushAssetShelf.draw_popup_selector(layout, context, brush) tool_settings = context.tool_settings capabilities = brush.sculpt_capabilities @@ -556,14 +502,7 @@ class _draw_tool_settings_context_mode: paint = tool_settings.gpencil_weight_paint brush = paint.brush - preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 - fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' - layout.template_asset_shelf_popover( - BrushAssetShelf.get_shelf_name_from_context(context), - name=brush.name, - icon=fallback_icon, - icon_value=preview_icon_id, - ) + BrushAssetShelf.draw_popup_selector(layout, context, brush) brush_basic_gpencil_weight_settings(layout, context, brush, compact=True) @@ -582,14 +521,7 @@ class _draw_tool_settings_context_mode: if brush is None: return False - preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 - fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' - layout.template_asset_shelf_popover( - BrushAssetShelf.get_shelf_name_from_context(context), - name=brush.name, - icon=fallback_icon, - icon_value=preview_icon_id, - ) + BrushAssetShelf.draw_popup_selector(layout, context, brush) brush_basic_grease_pencil_weight_settings(layout, context, brush, compact=True) @@ -610,14 +542,7 @@ class _draw_tool_settings_context_mode: row = layout.row(align=True) settings = tool_settings.gpencil_vertex_paint - preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 - fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' - layout.template_asset_shelf_popover( - BrushAssetShelf.get_shelf_name_from_context(context), - name=brush.name, - icon=fallback_icon, - icon_value=preview_icon_id, - ) + BrushAssetShelf.draw_popup_selector(layout, context, brush) if brush.gpencil_vertex_tool not in {'BLUR', 'AVERAGE', 'SMEAR'}: row.separator(factor=0.4) @@ -771,14 +696,7 @@ class _draw_tool_settings_context_mode: row = layout.row(align=True) - preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0 - fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE' - layout.template_asset_shelf_popover( - BrushAssetShelf.get_shelf_name_from_context(context), - name=brush.name, - icon=fallback_icon, - icon_value=preview_icon_id, - ) + BrushAssetShelf.draw_popup_selector(layout, context, brush) grease_pencil_tool = brush.gpencil_tool -- 2.30.2 From a6fb4bb3d7111a441ad1d7dcc1bd22d82cd118d0 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 6 May 2024 15:47:53 +0200 Subject: [PATCH 238/244] Refactor: More changes for separate main databases Handle node editor, RNA function calls, image unpack, and a few more things that were missed. --- source/blender/blenkernel/BKE_context.hh | 1 + source/blender/blenkernel/BKE_node.hh | 2 +- source/blender/blenkernel/intern/context.cc | 5 + source/blender/editors/include/ED_util.hh | 8 +- .../templates/interface_templates.cc | 25 ++--- .../blender/editors/render/render_shading.cc | 9 +- source/blender/editors/sound/sound_ops.cc | 24 ++--- .../blender/editors/space_image/image_ops.cc | 92 ++++++------------- .../blender/editors/space_node/clipboard.cc | 4 +- .../editors/space_node/link_drag_search.cc | 10 +- source/blender/editors/space_node/node_add.cc | 60 ++++++------ .../blender/editors/space_node/node_draw.cc | 2 +- .../blender/editors/space_node/node_edit.cc | 57 +++++++----- .../node_geometry_attribute_search.cc | 3 +- .../blender/editors/space_node/node_group.cc | 22 +++-- .../editors/space_node/node_relationships.cc | 37 ++++---- .../blender/editors/space_node/node_select.cc | 4 +- .../editors/space_node/node_shader_preview.cc | 2 +- .../editors/space_node/node_templates.cc | 8 +- source/blender/editors/util/ed_util.cc | 17 +--- source/blender/makesrna/intern/makesrna.cc | 3 +- source/blender/makesrna/intern/rna_ID.cc | 14 +-- source/blender/makesrna/intern/rna_access.cc | 7 +- source/blender/makesrna/intern/rna_brush.cc | 4 +- .../blender/makesrna/intern/rna_image_api.cc | 2 +- source/blender/makesrna/intern/rna_light.cc | 2 +- .../blender/makesrna/intern/rna_linestyle.cc | 3 +- .../blender/makesrna/intern/rna_material.cc | 4 +- .../makesrna/intern/rna_node_socket.cc | 5 +- .../blender/makesrna/intern/rna_nodetree.cc | 13 ++- source/blender/makesrna/intern/rna_object.cc | 31 ++++--- .../blender/makesrna/intern/rna_object_api.cc | 4 +- source/blender/makesrna/intern/rna_scene.cc | 7 +- source/blender/makesrna/intern/rna_texture.cc | 6 +- source/blender/makesrna/intern/rna_world.cc | 7 +- .../nodes/composite/node_composite_tree.cc | 2 +- .../nodes/geometry/node_geometry_tree.cc | 2 +- .../blender/nodes/shader/node_shader_tree.cc | 2 +- .../nodes/texture/node_texture_tree.cc | 2 +- 39 files changed, 239 insertions(+), 273 deletions(-) diff --git a/source/blender/blenkernel/BKE_context.hh b/source/blender/blenkernel/BKE_context.hh index ebd8a137881..d868ba3f146 100644 --- a/source/blender/blenkernel/BKE_context.hh +++ b/source/blender/blenkernel/BKE_context.hh @@ -347,6 +347,7 @@ int ctx_data_list_count(const bContext *C, /* Data Context Members */ Main *CTX_data_main(const bContext *C); +Main *CTX_data_main_from_id(const bContext *C, const ID *id); Scene *CTX_data_scene(const bContext *C); /** * This is tricky. Sometimes the user overrides the render_layer diff --git a/source/blender/blenkernel/BKE_node.hh b/source/blender/blenkernel/BKE_node.hh index b97ba0c2c78..2db3cd63f79 100644 --- a/source/blender/blenkernel/BKE_node.hh +++ b/source/blender/blenkernel/BKE_node.hh @@ -400,7 +400,7 @@ struct bNodeTreeType { /* callbacks */ /* Iteration over all node classes. */ - void (*foreach_nodeclass)(Scene *scene, void *calldata, bNodeClassCallback func); + void (*foreach_nodeclass)(void *calldata, bNodeClassCallback func); /* Check visibility in the node editor */ bool (*poll)(const bContext *C, bNodeTreeType *ntreetype); /* Select a node tree from the context */ diff --git a/source/blender/blenkernel/intern/context.cc b/source/blender/blenkernel/intern/context.cc index 0186d8ed12e..8d49d3fc29d 100644 --- a/source/blender/blenkernel/intern/context.cc +++ b/source/blender/blenkernel/intern/context.cc @@ -1058,6 +1058,11 @@ Main *CTX_data_main(const bContext *C) return C->data.main; } +Main *CTX_data_main_from_id(const bContext *C, const ID *id) +{ + return BKE_main_from_id(CTX_data_main(C), id); +} + void CTX_data_main_set(bContext *C, Main *bmain) { C->data.main = bmain; diff --git a/source/blender/editors/include/ED_util.hh b/source/blender/editors/include/ED_util.hh index a4b5b7fa2e9..d3d7e7011b9 100644 --- a/source/blender/editors/include/ED_util.hh +++ b/source/blender/editors/include/ED_util.hh @@ -121,9 +121,5 @@ void apply_keyb_grid( bool shift, bool ctrl, float *val, float fac1, float fac2, float fac3, int invert); /* where else to go ? */ -void unpack_menu(bContext *C, - const char *opname, - const char *id_name, - const char *abs_name, - const char *folder, - PackedFile *pf); +void unpack_menu( + bContext *C, const char *opname, const char *abs_name, const char *folder, PackedFile *pf); diff --git a/source/blender/editors/interface/templates/interface_templates.cc b/source/blender/editors/interface/templates/interface_templates.cc index 3ceae619492..1f8fc74c8e7 100644 --- a/source/blender/editors/interface/templates/interface_templates.cc +++ b/source/blender/editors/interface/templates/interface_templates.cc @@ -962,7 +962,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) TemplateID *template_ui = (TemplateID *)arg_litem; PointerRNA idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop); ID *id = static_cast(idptr.data); - Main *id_main = BKE_main_from_id(CTX_data_main(C), id); + Main *bmain = CTX_data_main_from_id(C, id); const int event = POINTER_AS_INT(arg_event); const char *undo_push_label = nullptr; @@ -1014,11 +1014,10 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) case UI_ID_LOCAL: if (id) { if (CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) { - template_id_liboverride_hierarchy_make( - C, id_main, template_ui, &idptr, &undo_push_label); + template_id_liboverride_hierarchy_make(C, bmain, template_ui, &idptr, &undo_push_label); } else { - if (BKE_lib_id_make_local(id_main, id, 0)) { + if (BKE_lib_id_make_local(bmain, id, 0)) { BKE_id_newptr_and_tag_clear(id); /* Reassign to get proper updates/notifiers. */ @@ -1035,11 +1034,10 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) case UI_ID_OVERRIDE: if (id && ID_IS_OVERRIDE_LIBRARY(id)) { if (CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) { - template_id_liboverride_hierarchy_make( - C, id_main, template_ui, &idptr, &undo_push_label); + template_id_liboverride_hierarchy_make(C, bmain, template_ui, &idptr, &undo_push_label); } else { - BKE_lib_override_library_make_local(id_main, id); + BKE_lib_override_library_make_local(bmain, 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); @@ -1056,13 +1054,13 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) /* make copy */ if (do_scene_obj) { Scene *scene = CTX_data_scene(C); - blender::ed::object::object_single_user_make(id_main, scene, (Object *)id); + blender::ed::object::object_single_user_make(bmain, scene, (Object *)id); WM_event_add_notifier(C, NC_WINDOW, nullptr); - DEG_relations_tag_update(id_main); + DEG_relations_tag_update(bmain); } else { id_single_user(C, id, &template_ui->ptr, template_ui->prop); - DEG_relations_tag_update(id_main); + DEG_relations_tag_update(bmain); } undo_push_label = "Make Single User"; } @@ -1766,15 +1764,12 @@ 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_main_from_id(id_main, ptr->owner_id); - } + Main *bmain = (ptr->owner_id) ? CTX_data_main_from_id(C, ptr->owner_id) : CTX_data_main(C); 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(id_main, idcode); + template_ui->idlb = which_libbase(bmain, 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/render/render_shading.cc b/source/blender/editors/render/render_shading.cc index a75d9c17aad..44f68f126e1 100644 --- a/source/blender/editors/render/render_shading.cc +++ b/source/blender/editors/render/render_shading.cc @@ -828,18 +828,15 @@ static int new_texture_exec(bContext *C, wmOperator * /*op*/) UI_context_active_but_prop_get_templateID(C, &ptr, &prop); - Main *id_main = CTX_data_main(C); - if (ptr.owner_id) { - id_main = BKE_main_from_id(id_main, ptr.owner_id); - } + Main *bmain = (ptr.owner_id) ? CTX_data_main_from_id(C, ptr.owner_id) : CTX_data_main(C); /* 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(id_main, &tex->id); + tex = (Tex *)BKE_id_copy(bmain, &tex->id); } else { - tex = BKE_texture_add(id_main, DATA_("Texture")); + tex = BKE_texture_add(bmain, DATA_("Texture")); } /* hook into UI */ diff --git a/source/blender/editors/sound/sound_ops.cc b/source/blender/editors/sound/sound_ops.cc index d67bf355bb0..1962fdba665 100644 --- a/source/blender/editors/sound/sound_ops.cc +++ b/source/blender/editors/sound/sound_ops.cc @@ -797,15 +797,14 @@ static int sound_unpack_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); int method = RNA_enum_get(op->ptr, "method"); - bSound *sound = nullptr; + Editing *ed = CTX_data_scene(C)->ed; - /* find the supplied image by name */ - if (RNA_struct_property_is_set(op->ptr, "id")) { - char sndname[MAX_ID_NAME - 2]; - RNA_string_get(op->ptr, "id", sndname); - sound = static_cast(BLI_findstring(&bmain->sounds, sndname, offsetof(ID, name) + 2)); + if (!ed || !ed->act_seq || ed->act_seq->type != SEQ_TYPE_SOUND_RAM) { + return OPERATOR_CANCELLED; } + bSound *sound = ed->act_seq->sound; + if (!sound || !sound->packedfile) { return OPERATOR_CANCELLED; } @@ -824,17 +823,12 @@ static int sound_unpack_exec(bContext *C, wmOperator *op) static int sound_unpack_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) { Editing *ed = CTX_data_scene(C)->ed; - bSound *sound; - - if (RNA_struct_property_is_set(op->ptr, "id")) { - return sound_unpack_exec(C, op); - } if (!ed || !ed->act_seq || ed->act_seq->type != SEQ_TYPE_SOUND_RAM) { return OPERATOR_CANCELLED; } - sound = ed->act_seq->sound; + bSound *sound = ed->act_seq->sound; if (!sound || !sound->packedfile) { return OPERATOR_CANCELLED; @@ -846,8 +840,7 @@ static int sound_unpack_invoke(bContext *C, wmOperator *op, const wmEvent * /*ev "AutoPack is enabled, so image will be packed again on file save"); } - unpack_menu( - C, "SOUND_OT_unpack", sound->id.name + 2, sound->filepath, "sounds", sound->packedfile); + unpack_menu(C, "SOUND_OT_unpack", sound->filepath, "sounds", sound->packedfile); return OPERATOR_FINISHED; } @@ -870,9 +863,6 @@ static void SOUND_OT_unpack(wmOperatorType *ot) /* properties */ RNA_def_enum( ot->srna, "method", rna_enum_unpack_method_items, PF_USE_LOCAL, "Method", "How to unpack"); - /* XXX: weak!, will fail with library, name collisions */ - RNA_def_string( - ot->srna, "id", nullptr, MAX_ID_NAME - 2, "Sound Name", "Sound data-block name to unpack"); } /* ******************************************************* */ diff --git a/source/blender/editors/space_image/image_ops.cc b/source/blender/editors/space_image/image_ops.cc index eccfc260fb8..4ce0f7f9474 100644 --- a/source/blender/editors/space_image/image_ops.cc +++ b/source/blender/editors/space_image/image_ops.cc @@ -1339,14 +1339,12 @@ static int image_open_exec(bContext *C, wmOperator *op) ImageOpenData *iod = static_cast(op->customdata); - Main *id_main = CTX_data_main(C); - if (iod->pprop.ptr.owner_id) { - id_main = BKE_main_from_id(id_main, iod->pprop.ptr.owner_id); - } + Main *bmain = (iod->pprop.ptr.owner_id) ? CTX_data_main_from_id(C, iod->pprop.ptr.owner_id) : + CTX_data_main(C); - ListBase ranges = ED_image_filesel_detect_sequences(id_main, op, use_udim); + ListBase ranges = ED_image_filesel_detect_sequences(bmain, op, use_udim); LISTBASE_FOREACH (ImageFrameRange *, range, &ranges) { - Image *ima_range = image_open_single(id_main, op, range, use_multiview); + Image *ima_range = image_open_single(bmain, op, range, use_multiview); /* take the first image */ if ((ima == nullptr) && ima_range) { @@ -1378,7 +1376,7 @@ static int image_open_exec(bContext *C, wmOperator *op) } else if (area && area->spacetype == SPACE_IMAGE) { SpaceImage *sima = static_cast(area->spacedata.first); - ED_space_image_set(id_main, sima, ima, false); + ED_space_image_set(bmain, sima, ima, false); iuser = &sima->iuser; } else { @@ -1418,9 +1416,9 @@ static int image_open_exec(bContext *C, wmOperator *op) } /* XXX BKE_packedfile_unpack_image frees image buffers */ - ED_preview_kill_jobs(CTX_wm_manager(C), id_main); + ED_preview_kill_jobs(CTX_wm_manager(C), bmain); - BKE_image_signal(id_main, ima, iuser, IMA_SIGNAL_RELOAD); + BKE_image_signal(bmain, ima, iuser, IMA_SIGNAL_RELOAD); WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima); MEM_freeN(op->customdata); @@ -1819,6 +1817,7 @@ void IMAGE_OT_replace(wmOperatorType *ot) * \{ */ struct ImageSaveData { + Main *bmain; ImageUser *iuser; Image *image; ImageSaveOptions opts; @@ -1858,16 +1857,16 @@ static bool save_image_op( static ImageSaveData *image_save_as_init(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); Image *image = image_from_context(C); ImageUser *iuser = image_user_from_context(C); Scene *scene = CTX_data_scene(C); ImageSaveData *isd = static_cast(MEM_callocN(sizeof(*isd), __func__)); + isd->bmain = CTX_data_main_from_id(C, &image->id); isd->image = image; isd->iuser = iuser; - if (!BKE_image_save_options_init(&isd->opts, bmain, scene, image, iuser, true, false)) { + if (!BKE_image_save_options_init(&isd->opts, isd->bmain, scene, image, iuser, true, false)) { BKE_image_save_options_free(&isd->opts); MEM_freeN(isd); return nullptr; @@ -1913,7 +1912,6 @@ static void image_save_as_free(wmOperator *op) static int image_save_as_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); ImageSaveData *isd; if (op->customdata) { @@ -1926,10 +1924,10 @@ static int image_save_as_exec(bContext *C, wmOperator *op) } } - image_save_options_from_op(bmain, &isd->opts, op); + image_save_options_from_op(isd->bmain, &isd->opts, op); BKE_image_save_options_update(&isd->opts, isd->image); - save_image_op(bmain, isd->image, isd->iuser, op, &isd->opts); + save_image_op(isd->bmain, isd->image, isd->iuser, op, &isd->opts); if (isd->opts.save_copy == false) { BKE_image_free_packedfiles(isd->image); @@ -1942,10 +1940,9 @@ static int image_save_as_exec(bContext *C, wmOperator *op) static bool image_save_as_check(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); ImageSaveData *isd = static_cast(op->customdata); - image_save_options_from_op(bmain, &isd->opts, op); + image_save_options_from_op(isd->bmain, &isd->opts, op); BKE_image_save_options_update(&isd->opts, isd->image); return WM_operator_filesel_ensure_ext_imtype(op, &isd->opts.im_format); @@ -2127,8 +2124,8 @@ static bool image_save_poll(bContext *C) static int image_save_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); Image *image = image_from_context(C); + Main *bmain = CTX_data_main_from_id(C, &image->id); ImageUser *iuser = image_user_from_context(C); Scene *scene = CTX_data_scene(C); ImageSaveOptions opts; @@ -2461,8 +2458,8 @@ void IMAGE_OT_save_all_modified(wmOperatorType *ot) static int image_reload_exec(bContext *C, wmOperator * /*op*/) { - Main *bmain = CTX_data_main(C); Image *ima = image_from_context(C); + Main *bmain = CTX_data_main_from_id(C, &ima->id); ImageUser *iuser = image_user_from_context(C); if (!ima) { @@ -2470,7 +2467,7 @@ static int image_reload_exec(bContext *C, wmOperator * /*op*/) } /* XXX BKE_packedfile_unpack_image frees image buffers */ - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + ED_preview_kill_jobs(CTX_wm_manager(C), bmain); BKE_image_signal(bmain, ima, iuser, IMA_SIGNAL_RELOAD); DEG_id_tag_update(&ima->id, 0); @@ -2531,9 +2528,7 @@ static void image_new_free(wmOperator *op) static int image_new_exec(bContext *C, wmOperator *op) { - SpaceImage *sima; Image *ima; - Main *id_main; PropertyRNA *prop; char name_buffer[MAX_ID_NAME - 2]; const char *name; @@ -2544,11 +2539,9 @@ static int image_new_exec(bContext *C, wmOperator *op) ImageNewData *data = image_new_init(C, op); /* retrieve state */ - sima = CTX_wm_space_image(C); - id_main = CTX_data_main(C); - if (data->pprop.ptr.owner_id) { - id_main = BKE_main_from_id(id_main, data->pprop.ptr.owner_id); - } + SpaceImage *sima = CTX_wm_space_image(C); + Main *bmain = (data->pprop.ptr.owner_id) ? CTX_data_main_from_id(C, data->pprop.ptr.owner_id) : + CTX_data_main(C); prop = RNA_struct_find_property(op->ptr, "name"); RNA_property_string_get(op->ptr, prop, name_buffer); @@ -2572,7 +2565,7 @@ static int image_new_exec(bContext *C, wmOperator *op) color[3] = 1.0f; } - ima = BKE_image_add_generated(id_main, + ima = BKE_image_add_generated(bmain, width, height, name, @@ -2600,7 +2593,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(id_main, sima, ima, false); + ED_space_image_set(bmain, sima, ima, false); } else { /* #BKE_image_add_generated creates one user by default, remove it if image is not linked to @@ -2608,7 +2601,7 @@ static int image_new_exec(bContext *C, wmOperator *op) id_us_min(&ima->id); } - BKE_image_signal(id_main, ima, (sima) ? &sima->iuser : nullptr, IMA_SIGNAL_USER_NEW_IMAGE); + BKE_image_signal(bmain, ima, (sima) ? &sima->iuser : nullptr, IMA_SIGNAL_USER_NEW_IMAGE); WM_event_add_notifier(C, NC_IMAGE | NA_ADDED, ima); @@ -3261,28 +3254,16 @@ void IMAGE_OT_resize(wmOperatorType *ot) /** \name Pack Operator * \{ */ -static bool image_pack_test(bContext *C, wmOperator *op) +static int image_pack_exec(bContext *C, wmOperator *op) { Image *ima = image_from_context(C); if (!ima) { - return false; + return OPERATOR_CANCELLED; } if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE)) { BKE_report(op->reports, RPT_ERROR, "Packing movies or image sequences not supported"); - return false; - } - - return true; -} - -static int image_pack_exec(bContext *C, wmOperator *op) -{ - Main *bmain = CTX_data_main(C); - Image *ima = image_from_context(C); - - if (!image_pack_test(C, op)) { return OPERATOR_CANCELLED; } @@ -3290,6 +3271,7 @@ static int image_pack_exec(bContext *C, wmOperator *op) BKE_image_memorypack(ima); } else { + Main *bmain = CTX_data_main_from_id(C, &ima->id); BKE_image_packfiles(op->reports, ima, ID_BLEND_PATH(bmain, &ima->id)); } @@ -3320,20 +3302,9 @@ void IMAGE_OT_pack(wmOperatorType *ot) static int image_unpack_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); Image *ima = image_from_context(C); int method = RNA_enum_get(op->ptr, "method"); - /* find the supplied image by name */ - if (RNA_struct_property_is_set(op->ptr, "id")) { - char imaname[MAX_ID_NAME - 2]; - RNA_string_get(op->ptr, "id", imaname); - ima = static_cast(BLI_findstring(&bmain->images, imaname, offsetof(ID, name) + 2)); - if (!ima) { - ima = image_from_context(C); - } - } - if (!ima || !BKE_image_has_packedfile(ima)) { return OPERATOR_CANCELLED; } @@ -3350,9 +3321,10 @@ static int image_unpack_exec(bContext *C, wmOperator *op) } /* XXX BKE_packedfile_unpack_image frees image buffers */ - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + Main *bmain = CTX_data_main_from_id(C, &ima->id); + ED_preview_kill_jobs(CTX_wm_manager(C), bmain); - BKE_packedfile_unpack_image(CTX_data_main(C), op->reports, ima, ePF_FileStatus(method)); + BKE_packedfile_unpack_image(bmain, op->reports, ima, ePF_FileStatus(method)); WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima); @@ -3363,10 +3335,6 @@ static int image_unpack_invoke(bContext *C, wmOperator *op, const wmEvent * /*ev { Image *ima = image_from_context(C); - if (RNA_struct_property_is_set(op->ptr, "id")) { - return image_unpack_exec(C, op); - } - if (!ima || !BKE_image_has_packedfile(ima)) { return OPERATOR_CANCELLED; } @@ -3384,7 +3352,6 @@ static int image_unpack_invoke(bContext *C, wmOperator *op, const wmEvent * /*ev unpack_menu(C, "IMAGE_OT_unpack", - ima->id.name + 2, ima->filepath, "textures", BKE_image_has_packedfile(ima) ? @@ -3411,9 +3378,6 @@ void IMAGE_OT_unpack(wmOperatorType *ot) /* properties */ RNA_def_enum( ot->srna, "method", rna_enum_unpack_method_items, PF_USE_LOCAL, "Method", "How to unpack"); - /* XXX, weak!, will fail with library, name collisions */ - RNA_def_string( - ot->srna, "id", nullptr, MAX_ID_NAME - 2, "Image Name", "Image data-block name to unpack"); } /** \} */ diff --git a/source/blender/editors/space_node/clipboard.cc b/source/blender/editors/space_node/clipboard.cc index 302e80bb6d2..fd565bfc74d 100644 --- a/source/blender/editors/space_node/clipboard.cc +++ b/source/blender/editors/space_node/clipboard.cc @@ -335,9 +335,9 @@ void NODE_OT_clipboard_copy(wmOperatorType *ot) static int node_clipboard_paste_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); SpaceNode &snode = *CTX_wm_space_node(C); bNodeTree &tree = *snode.edittree; + Main *bmain = CTX_data_main_from_id(C, &tree.id); NodeClipboard &clipboard = get_node_clipboard(); if (clipboard.nodes.is_empty()) { @@ -351,7 +351,7 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op) "Some nodes references to other IDs could not be restored, will be left empty"); } - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + ED_preview_kill_jobs(CTX_wm_manager(C), bmain); node_deselect_all(tree); diff --git a/source/blender/editors/space_node/link_drag_search.cc b/source/blender/editors/space_node/link_drag_search.cc index 04905d92650..242342eb739 100644 --- a/source/blender/editors/space_node/link_drag_search.cc +++ b/source/blender/editors/space_node/link_drag_search.cc @@ -95,11 +95,12 @@ static void add_group_input_node_fn(nodes::LinkSearchOpParams ¶ms) nullptr); socket_iface->init_from_socket_instance(¶ms.socket); params.node_tree.tree_interface.active_item_set(&socket_iface->item); + Main *bmain = CTX_data_main_from_id(¶ms.C, ¶ms.node_tree.id); bNode &group_input = params.add_node("NodeGroupInput"); /* This is necessary to create the new sockets in the other input nodes. */ - ED_node_tree_propagate_change(¶ms.C, CTX_data_main(¶ms.C), ¶ms.node_tree); + ED_node_tree_propagate_change(¶ms.C, bmain, ¶ms.node_tree); /* Hide the new input in all other group input nodes, to avoid making them taller. */ for (bNode *node : params.node_tree.all_nodes()) { @@ -123,8 +124,7 @@ static void add_group_input_node_fn(nodes::LinkSearchOpParams ¶ms) socket->flag &= ~SOCK_HIDDEN; nodeAddLink(¶ms.node_tree, &group_input, socket, ¶ms.node, ¶ms.socket); - bke::node_socket_move_default_value( - *CTX_data_main(¶ms.C), params.node_tree, params.socket, *socket); + bke::node_socket_move_default_value(*bmain, params.node_tree, params.socket, *socket); } } @@ -201,7 +201,7 @@ static void search_link_ops_for_asset_metadata(const bNodeTree &node_tree, search_link_ops.append( {asset_name + " " + UI_MENU_ARROW_SEP + socket_name, [&asset, socket_property, in_out](nodes::LinkSearchOpParams ¶ms) { - Main &bmain = *CTX_data_main(¶ms.C); + Main &bmain = *CTX_data_main_from_id(¶ms.C, ¶ms.node_tree.id); bNode &node = params.add_node(params.node_tree.typeinfo->group_idname); node.flag &= ~NODE_OPTIONS; @@ -345,7 +345,6 @@ static void link_drag_search_update_fn( static void link_drag_search_exec_fn(bContext *C, void *arg1, void *arg2) { - Main &bmain = *CTX_data_main(C); SpaceNode &snode = *CTX_wm_space_node(C); bNodeTree &node_tree = *snode.edittree; LinkDragSearchStorage &storage = *static_cast(arg1); @@ -379,6 +378,7 @@ static void link_drag_search_exec_fn(bContext *C, void *arg1, void *arg2) /* Ideally it would be possible to tag the node tree in some way so it updates only after the * translate operation is finished, but normally moving nodes around doesn't cause updates. */ + Main &bmain = *CTX_data_main_from_id(C, &node_tree.id); ED_node_tree_propagate_change(C, &bmain, &node_tree); /* Start translation operator with the new node. */ diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc index 529ed7c16d6..68afda2db1a 100644 --- a/source/blender/editors/space_node/node_add.cc +++ b/source/blender/editors/space_node/node_add.cc @@ -71,8 +71,8 @@ static void position_node_based_on_mouse(bNode &node, const float2 &location) bNode *add_node(const bContext &C, const StringRef idname, const float2 &location) { SpaceNode &snode = *CTX_wm_space_node(&C); - Main &bmain = *CTX_data_main(&C); bNodeTree &node_tree = *snode.edittree; + Main &bmain = *CTX_data_main_from_id(&C, &node_tree.id); node_deselect_all(node_tree); @@ -93,8 +93,8 @@ bNode *add_node(const bContext &C, const StringRef idname, const float2 &locatio bNode *add_static_node(const bContext &C, int type, const float2 &location) { SpaceNode &snode = *CTX_wm_space_node(&C); - Main &bmain = *CTX_data_main(&C); bNodeTree &node_tree = *snode.edittree; + Main &bmain = *CTX_data_main_from_id(&C, &node_tree.id); node_deselect_all(node_tree); @@ -145,6 +145,7 @@ static int add_reroute_exec(bContext *C, wmOperator *op) const ARegion ®ion = *CTX_wm_region(C); SpaceNode &snode = *CTX_wm_space_node(C); bNodeTree &ntree = *snode.edittree; + Main &bmain = *CTX_data_main_from_id(C, &ntree.id); Vector path; RNA_BEGIN (op->ptr, itemptr, "path") { @@ -168,7 +169,7 @@ static int add_reroute_exec(bContext *C, wmOperator *op) ntree.ensure_topology_cache(); const Vector frame_nodes = ntree.nodes_by_type("NodeFrame"); - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + ED_preview_kill_jobs(CTX_wm_manager(C), &bmain); /* All link "cuts" that start at a particular output socket. Deduplicating new reroutes per * output socket is useful because it allows reusing reroutes for connected intersections. @@ -223,7 +224,7 @@ static int add_reroute_exec(bContext *C, wmOperator *op) } } - ED_node_tree_propagate_change(C, CTX_data_main(C), &ntree); + ED_node_tree_propagate_change(C, &bmain, &ntree); return OPERATOR_FINISHED; } @@ -291,9 +292,9 @@ static bool node_group_add_poll(const bNodeTree &node_tree, static int node_add_group_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); SpaceNode *snode = CTX_wm_space_node(C); bNodeTree *ntree = snode->edittree; + Main *bmain = CTX_data_main_from_id(C, &ntree->id); bNodeTree *node_group = reinterpret_cast( WM_operator_properties_id_lookup_from_name_or_session_uid(bmain, op->ptr, ID_NT)); @@ -304,7 +305,7 @@ static int node_add_group_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + ED_preview_kill_jobs(CTX_wm_manager(C), bmain); const char *node_idname = node_group_idname(C); if (node_idname[0] == '\0') { @@ -398,9 +399,9 @@ static bool add_node_group_asset(const bContext &C, const asset_system::AssetRepresentation &asset, ReportList &reports) { - Main &bmain = *CTX_data_main(&C); SpaceNode &snode = *CTX_wm_space_node(&C); bNodeTree &edit_tree = *snode.edittree; + Main &bmain = *CTX_data_main_from_id(&C, &edit_tree.id); bNodeTree *node_group = reinterpret_cast( asset::asset_local_id_ensure_imported(bmain, asset)); @@ -414,7 +415,7 @@ static bool add_node_group_asset(const bContext &C, return false; } - ED_preview_kill_jobs(CTX_wm_manager(&C), CTX_data_main(&C)); + ED_preview_kill_jobs(CTX_wm_manager(&C), &bmain); bNode *group_node = add_node( C, ntreeTypeFind(node_group->idname)->group_idname, snode.runtime->cursor); @@ -510,9 +511,9 @@ void NODE_OT_add_group_asset(wmOperatorType *ot) static int node_add_object_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); SpaceNode *snode = CTX_wm_space_node(C); bNodeTree *ntree = snode->edittree; + Main *bmain = CTX_data_main_from_id(C, &ntree->id); Object *object = reinterpret_cast( WM_operator_properties_id_lookup_from_name_or_session_uid(bmain, op->ptr, ID_OB)); @@ -521,7 +522,7 @@ static int node_add_object_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + ED_preview_kill_jobs(CTX_wm_manager(C), bmain); bNode *object_node = add_static_node(*C, GEO_NODE_OBJECT_INFO, snode->runtime->cursor); if (!object_node) { @@ -596,9 +597,9 @@ void NODE_OT_add_object(wmOperatorType *ot) static int node_add_collection_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); SpaceNode &snode = *CTX_wm_space_node(C); bNodeTree &ntree = *snode.edittree; + Main *bmain = CTX_data_main_from_id(C, &ntree.id); Collection *collection = reinterpret_cast( WM_operator_properties_id_lookup_from_name_or_session_uid(bmain, op->ptr, ID_GR)); @@ -607,7 +608,7 @@ static int node_add_collection_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + ED_preview_kill_jobs(CTX_wm_manager(C), bmain); bNode *collection_node = add_static_node(*C, GEO_NODE_COLLECTION_INFO, snode.runtime->cursor); if (!collection_node) { @@ -736,8 +737,10 @@ static int node_add_file_modal(bContext *C, wmOperator *op, const wmEvent *event static int node_add_file_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); SpaceNode &snode = *CTX_wm_space_node(C); + bNodeTree &node_tree = *snode.edittree; + Main *bmain = CTX_data_main_from_id(C, &node_tree.id); + int type = 0; switch (snode.nodetree->type) { case NTREE_SHADER: @@ -807,14 +810,13 @@ static int node_add_file_exec(bContext *C, wmOperator *op) } /* Set new nodes as selected. */ - bNodeTree &node_tree = *snode.edittree; node_deselect_all(node_tree); for (bNode *node : nodes) { nodeSetSelected(node, true); } ED_node_set_active(bmain, &snode, &node_tree, nodes[0], nullptr); - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + ED_preview_kill_jobs(CTX_wm_manager(C), bmain); ED_node_tree_propagate_change(C, bmain, snode.edittree); DEG_relations_tag_update(bmain); @@ -898,15 +900,16 @@ static bool node_add_mask_poll(bContext *C) static int node_add_mask_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); SpaceNode &snode = *CTX_wm_space_node(C); + bNodeTree &node_tree = *snode.edittree; + Main *bmain = CTX_data_main_from_id(C, &node_tree.id); ID *mask = WM_operator_properties_id_lookup_from_name_or_session_uid(bmain, op->ptr, ID_MSK); if (!mask) { return OPERATOR_CANCELLED; } - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + ED_preview_kill_jobs(CTX_wm_manager(C), bmain); bNode *node = add_static_node(*C, CMP_NODE_MASK, snode.runtime->cursor); @@ -949,9 +952,9 @@ void NODE_OT_add_mask(wmOperatorType *ot) static int node_add_material_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); SpaceNode *snode = CTX_wm_space_node(C); bNodeTree *ntree = snode->edittree; + Main *bmain = CTX_data_main_from_id(C, &ntree->id); Material *material = reinterpret_cast( WM_operator_properties_id_lookup_from_name_or_session_uid(bmain, op->ptr, ID_MA)); @@ -960,7 +963,7 @@ static int node_add_material_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + ED_preview_kill_jobs(CTX_wm_manager(C), bmain); bNode *material_node = add_static_node(*C, GEO_NODE_INPUT_MATERIAL, snode->runtime->cursor); if (!material_node) { @@ -1028,16 +1031,10 @@ void NODE_OT_add_material(wmOperatorType *ot) static int new_node_tree_exec(bContext *C, wmOperator *op) { SpaceNode *snode = CTX_wm_space_node(C); - Main *bmain = CTX_data_main(C); - bNodeTree *ntree; - PointerRNA ptr; - PropertyRNA *prop; const char *idname; - char treename_buf[MAX_ID_NAME - 2]; - const char *treename; if (RNA_struct_property_is_set(op->ptr, "type")) { - prop = RNA_struct_find_property(op->ptr, "type"); + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "type"); RNA_property_enum_identifier(C, op->ptr, prop, RNA_property_enum_get(op->ptr, prop), &idname); } else if (snode) { @@ -1052,6 +1049,8 @@ static int new_node_tree_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + char treename_buf[MAX_ID_NAME - 2]; + const char *treename; if (RNA_struct_property_is_set(op->ptr, "name")) { RNA_string_get(op->ptr, "name", treename_buf); treename = treename_buf; @@ -1061,11 +1060,16 @@ static int new_node_tree_exec(bContext *C, wmOperator *op) treename = type->ui_name; } - ntree = ntreeAddTree(bmain, treename, idname); - /* Hook into UI. */ + PointerRNA ptr; + PropertyRNA *prop; UI_context_active_but_prop_get_templateID(C, &ptr, &prop); + Main *bmain = (ptr.owner_id) ? CTX_data_main_from_id(C, ptr.owner_id) : CTX_data_main(C); + + /* Add node tree and assign. */ + bNodeTree *ntree = ntreeAddTree(bmain, treename, idname); + if (prop) { /* #RNA_property_pointer_set increases the user count, fixed here as the editor is the initial * user. */ diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 66286e0894f..fc27c09aa60 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -2361,9 +2361,9 @@ static void node_draw_sockets(const View2D &v2d, static void node_panel_toggle_button_cb(bContext *C, void *panel_state_argv, void *ntree_argv) { - Main *bmain = CTX_data_main(C); bNodePanelState *panel_state = static_cast(panel_state_argv); bNodeTree *ntree = static_cast(ntree_argv); + Main *bmain = CTX_data_main_from_id(C, &ntree->id); panel_state->flag ^= NODE_PANEL_COLLAPSED; diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index b13bed11152..9af9749abbd 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -373,7 +373,7 @@ void ED_node_composite_job(const bContext *C, bNodeTree *nodetree, Scene *scene_ { using namespace blender::ed::space_node; - Main *bmain = CTX_data_main(C); + Main *bmain = CTX_data_main_from_id(C, &nodetree->id); Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); @@ -545,7 +545,7 @@ bool ED_node_supports_preview(SpaceNode *snode) void ED_node_shader_default(const bContext *C, ID *id) { - Main *bmain = CTX_data_main(C); + Main *bmain = CTX_data_main_from_id(C, id); if (GS(id->name) == ID_MA) { /* Materials */ @@ -642,7 +642,8 @@ void ED_node_composit_default(const bContext *C, Scene *sce) bNodeSocket *tosock = (bNodeSocket *)out->inputs.first; nodeAddLink(sce->nodetree, in, fromsock, out, tosock); - BKE_ntree_update_main_tree(CTX_data_main(C), sce->nodetree, nullptr); + Main *bmain = CTX_data_main_from_id(C, &sce->nodetree->id); + BKE_ntree_update_main_tree(bmain, sce->nodetree, nullptr); } void ED_node_texture_default(const bContext *C, Tex *tex) @@ -670,7 +671,8 @@ void ED_node_texture_default(const bContext *C, Tex *tex) bNodeSocket *tosock = (bNodeSocket *)out->inputs.first; nodeAddLink(tex->nodetree, in, fromsock, out, tosock); - BKE_ntree_update_main_tree(CTX_data_main(C), tex->nodetree, nullptr); + Main *bmain = CTX_data_main_from_id(C, &tex->nodetree->id); + BKE_ntree_update_main_tree(bmain, tex->nodetree, nullptr); } namespace blender::ed::space_node { @@ -1364,9 +1366,9 @@ void remap_node_pairing(bNodeTree &dst_tree, const Map & static int node_duplicate_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); SpaceNode *snode = CTX_wm_space_node(C); bNodeTree *ntree = snode->edittree; + Main *bmain = CTX_data_main_from_id(C, &ntree->id); const bool keep_inputs = RNA_boolean_get(op->ptr, "keep_inputs"); bool linked = RNA_boolean_get(op->ptr, "linked") || ((U.dupflag & USER_DUP_NTREE) == 0); const bool dupli_node_tree = !linked; @@ -1504,10 +1506,10 @@ void NODE_OT_duplicate(wmOperatorType *ot) /* Goes over all scenes, reads render layers. */ static int node_read_viewlayers_exec(bContext *C, wmOperator * /*op*/) { - Main *bmain = CTX_data_main(C); SpaceNode *snode = CTX_wm_space_node(C); Scene *curscene = CTX_data_scene(C); bNodeTree &edit_tree = *snode->edittree; + Main *bmain = CTX_data_main_from_id(C, &edit_tree.id); ED_preview_kill_jobs(CTX_wm_manager(C), bmain); @@ -1698,7 +1700,8 @@ static int node_preview_toggle_exec(bContext *C, wmOperator * /*op*/) node_flag_toggle_exec(snode, NODE_PREVIEW); - ED_node_tree_propagate_change(C, CTX_data_main(C), snode->edittree); + Main *bmain = CTX_data_main_from_id(C, &snode->edittree->id); + ED_node_tree_propagate_change(C, bmain, snode->edittree); return OPERATOR_FINISHED; } @@ -1749,7 +1752,8 @@ static int node_deactivate_viewer_exec(bContext *C, wmOperator * /*op*/) } } - ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree); + Main *bmain = CTX_data_main_from_id(C, &snode.edittree->id); + ED_node_tree_propagate_change(C, bmain, snode.edittree); return OPERATOR_FINISHED; } @@ -1803,13 +1807,14 @@ void NODE_OT_options_toggle(wmOperatorType *ot) static int node_socket_toggle_exec(bContext *C, wmOperator * /*op*/) { SpaceNode *snode = CTX_wm_space_node(C); + Main *bmain = CTX_data_main_from_id(C, &snode->edittree->id); /* Sanity checking (poll callback checks this already). */ if ((snode == nullptr) || (snode->edittree == nullptr)) { return OPERATOR_CANCELLED; } - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + ED_preview_kill_jobs(CTX_wm_manager(C), bmain); /* Toggle for all selected nodes */ bool hidden = false; @@ -1828,7 +1833,7 @@ static int node_socket_toggle_exec(bContext *C, wmOperator * /*op*/) } } - ED_node_tree_propagate_change(C, CTX_data_main(C), snode->edittree); + ED_node_tree_propagate_change(C, bmain, snode->edittree); WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); /* Hack to force update of the button state after drawing, see #112462. */ @@ -1860,8 +1865,8 @@ void NODE_OT_hide_socket_toggle(wmOperatorType *ot) static int node_mute_exec(bContext *C, wmOperator * /*op*/) { - Main *bmain = CTX_data_main(C); SpaceNode *snode = CTX_wm_space_node(C); + Main *bmain = CTX_data_main_from_id(C, &snode->edittree->id); ED_preview_kill_jobs(CTX_wm_manager(C), bmain); @@ -1900,8 +1905,8 @@ void NODE_OT_mute_toggle(wmOperatorType *ot) static int node_delete_exec(bContext *C, wmOperator * /*op*/) { - Main *bmain = CTX_data_main(C); SpaceNode *snode = CTX_wm_space_node(C); + Main *bmain = CTX_data_main_from_id(C, &snode->edittree->id); ED_preview_kill_jobs(CTX_wm_manager(C), bmain); @@ -1954,6 +1959,7 @@ static bool node_switch_view_poll(bContext *C) static int node_switch_view_exec(bContext *C, wmOperator * /*op*/) { SpaceNode *snode = CTX_wm_space_node(C); + Main *bmain = CTX_data_main_from_id(C, &snode->edittree->id); LISTBASE_FOREACH_MUTABLE (bNode *, node, &snode->edittree->nodes) { if (node->flag & SELECT) { @@ -1962,7 +1968,7 @@ static int node_switch_view_exec(bContext *C, wmOperator * /*op*/) } } - ED_node_tree_propagate_change(C, CTX_data_main(C), snode->edittree); + ED_node_tree_propagate_change(C, bmain, snode->edittree); return OPERATOR_FINISHED; } @@ -1990,10 +1996,10 @@ void NODE_OT_switch_view_update(wmOperatorType *ot) static int node_delete_reconnect_exec(bContext *C, wmOperator * /*op*/) { - Main *bmain = CTX_data_main(C); SpaceNode *snode = CTX_wm_space_node(C); + Main *bmain = CTX_data_main_from_id(C, &snode->edittree->id); - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + ED_preview_kill_jobs(CTX_wm_manager(C), bmain); /* Delete paired nodes as well. */ node_select_paired(*snode->edittree); @@ -2056,7 +2062,8 @@ static int node_output_file_add_socket_exec(bContext *C, wmOperator *op) RNA_string_get(op->ptr, "file_path", file_path); ntreeCompositOutputFileAddSocket(ntree, node, file_path, &scene->r.im_format); - ED_node_tree_propagate_change(C, CTX_data_main(C), snode->edittree); + Main *bmain = CTX_data_main_from_id(C, &snode->edittree->id); + ED_node_tree_propagate_change(C, bmain, snode->edittree); return OPERATOR_FINISHED; } @@ -2109,7 +2116,8 @@ static int node_output_file_remove_active_socket_exec(bContext *C, wmOperator * return OPERATOR_CANCELLED; } - ED_node_tree_propagate_change(C, CTX_data_main(C), ntree); + Main *bmain = CTX_data_main_from_id(C, &ntree->id); + ED_node_tree_propagate_change(C, bmain, ntree); return OPERATOR_FINISHED; } @@ -2180,8 +2188,9 @@ static int node_output_file_move_active_socket_exec(bContext *C, wmOperator *op) nimf->active_input++; } + Main *bmain = CTX_data_main_from_id(C, &snode->edittree->id); BKE_ntree_update_tag_node_property(snode->edittree, node); - ED_node_tree_propagate_change(C, CTX_data_main(C), snode->edittree); + ED_node_tree_propagate_change(C, bmain, snode->edittree); return OPERATOR_FINISHED; } @@ -2327,7 +2336,6 @@ static bool node_shader_script_update_text_recursive(RenderEngine *engine, static int node_shader_script_update_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); SpaceNode *snode = CTX_wm_space_node(C); PointerRNA nodeptr = CTX_data_pointer_get_type(C, "node", &RNA_ShaderNodeScript); @@ -2360,7 +2368,7 @@ static int node_shader_script_update_exec(bContext *C, wmOperator *op) Text *text = (Text *)CTX_data_pointer_get_type(C, "edit_text", &RNA_Text).data; if (text) { - + Main *bmain = CTX_data_main(C); VectorSet done_trees; FOREACH_NODETREE_BEGIN (bmain, ntree, id) { @@ -2502,9 +2510,10 @@ static int clear_viewer_border_exec(bContext *C, wmOperator * /*op*/) { SpaceNode *snode = CTX_wm_space_node(C); bNodeTree *btree = snode->nodetree; + Main *bmain = CTX_data_main_from_id(C, &btree->id); btree->flag &= ~NTREE_VIEWER_BORDER; - ED_node_tree_propagate_change(C, CTX_data_main(C), btree); + ED_node_tree_propagate_change(C, bmain, btree); WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); return OPERATOR_FINISHED; @@ -2553,7 +2562,8 @@ static int node_cryptomatte_add_socket_exec(bContext *C, wmOperator * /*op*/) ntreeCompositCryptomatteAddSocket(ntree, node); - ED_node_tree_propagate_change(C, CTX_data_main(C), ntree); + Main *bmain = CTX_data_main_from_id(C, &ntree->id); + ED_node_tree_propagate_change(C, bmain, ntree); return OPERATOR_FINISHED; } @@ -2603,7 +2613,8 @@ static int node_cryptomatte_remove_socket_exec(bContext *C, wmOperator * /*op*/) return OPERATOR_CANCELLED; } - ED_node_tree_propagate_change(C, CTX_data_main(C), ntree); + Main *bmain = CTX_data_main_from_id(C, &ntree->id); + ED_node_tree_propagate_change(C, bmain, ntree); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_node/node_geometry_attribute_search.cc b/source/blender/editors/space_node/node_geometry_attribute_search.cc index 04b08a2474e..918d3c8a19d 100644 --- a/source/blender/editors/space_node/node_geometry_attribute_search.cc +++ b/source/blender/editors/space_node/node_geometry_attribute_search.cc @@ -211,7 +211,8 @@ static void attribute_search_exec_fn(bContext *C, void *data_v, void *item_v) /* Make the output socket with the new type on the attribute input node active. */ nodes::update_node_declaration_and_sockets(*node_tree, *node); BKE_ntree_update_tag_node_property(node_tree, node); - ED_node_tree_propagate_change(C, CTX_data_main(C), node_tree); + Main *bmain = CTX_data_main_from_id(C, &node_tree->id); + ED_node_tree_propagate_change(C, bmain, node_tree); } } diff --git a/source/blender/editors/space_node/node_group.cc b/source/blender/editors/space_node/node_group.cc index 824e01a5766..93b6fa6d8d2 100644 --- a/source/blender/editors/space_node/node_group.cc +++ b/source/blender/editors/space_node/node_group.cc @@ -169,10 +169,11 @@ static void remap_pairing(bNodeTree &dst_tree, static int node_group_edit_exec(bContext *C, wmOperator *op) { SpaceNode *snode = CTX_wm_space_node(C); + Main *bmain = CTX_data_main_from_id(C, &snode->edittree->id); const char *node_idname = node_group_idname(C); const bool exit = RNA_boolean_get(op->ptr, "exit"); - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + ED_preview_kill_jobs(CTX_wm_manager(C), bmain); stop_preview_job(*CTX_wm_manager(C)); bNode *gnode = node_group_get_active(C, node_idname); @@ -452,8 +453,8 @@ static void node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode) static int node_group_ungroup_exec(bContext *C, wmOperator * /*op*/) { - Main *bmain = CTX_data_main(C); SpaceNode *snode = CTX_wm_space_node(C); + Main *bmain = CTX_data_main_from_id(C, &snode->edittree->id); const char *node_idname = node_group_idname(C); ED_preview_kill_jobs(CTX_wm_manager(C), bmain); @@ -474,7 +475,7 @@ static int node_group_ungroup_exec(bContext *C, wmOperator * /*op*/) for (bNode *node : nodes_to_ungroup) { node_group_ungroup(bmain, snode->edittree, node); } - ED_node_tree_propagate_change(C, CTX_data_main(C), nullptr); + ED_node_tree_propagate_change(C, bmain, nullptr); return OPERATOR_FINISHED; } @@ -625,8 +626,8 @@ static const EnumPropertyItem node_group_separate_types[] = { static int node_group_separate_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); SpaceNode *snode = CTX_wm_space_node(C); + Main *bmain = CTX_data_main_from_id(C, &snode->edittree->id); int type = RNA_enum_get(op->ptr, "type"); ED_preview_kill_jobs(CTX_wm_manager(C), bmain); @@ -660,7 +661,7 @@ static int node_group_separate_exec(bContext *C, wmOperator *op) /* switch to parent tree */ ED_node_tree_pop(snode); - ED_node_tree_propagate_change(C, CTX_data_main(C), nullptr); + ED_node_tree_propagate_change(C, bmain, nullptr); return OPERATOR_FINISHED; } @@ -926,7 +927,7 @@ static void node_group_make_insert_selected(const bContext &C, bNode *gnode, const VectorSet &nodes_to_move) { - Main *bmain = CTX_data_main(&C); + Main *bmain = CTX_data_main_from_id(&C, &ntree.id); bNodeTree &group = *reinterpret_cast(gnode->id); BLI_assert(!nodes_to_move.contains(gnode)); @@ -1206,7 +1207,7 @@ static bNode *node_group_make_from_nodes(const bContext &C, const char *ntype, const char *ntreetype) { - Main *bmain = CTX_data_main(&C); + Main *bmain = CTX_data_main_from_id(&C, &ntree.id); float2 min, max; get_min_max_of_nodes(nodes_to_group, false, min, max); @@ -1232,9 +1233,9 @@ static int node_group_make_exec(bContext *C, wmOperator *op) bNodeTree &ntree = *snode.edittree; const char *ntree_idname = group_ntree_idname(C); const char *node_idname = node_group_idname(C); - Main *bmain = CTX_data_main(C); + Main *bmain = CTX_data_main_from_id(C, &ntree.id); - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + ED_preview_kill_jobs(CTX_wm_manager(C), bmain); stop_preview_job(*CTX_wm_manager(C)); VectorSet nodes_to_group = get_nodes_to_group(ntree, nullptr); @@ -1286,9 +1287,10 @@ static int node_group_insert_exec(bContext *C, wmOperator *op) { SpaceNode *snode = CTX_wm_space_node(C); bNodeTree *ntree = snode->edittree; + Main *bmain = CTX_data_main_from_id(C, &ntree->id); const char *node_idname = node_group_idname(C); - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + ED_preview_kill_jobs(CTX_wm_manager(C), bmain); stop_preview_job(*CTX_wm_manager(C)); bNode *gnode = node_group_get_active(C, node_idname); diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index 5446bea1cab..da459140c24 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -576,7 +576,7 @@ static void finalize_viewer_link(const bContext &C, bNode &viewer_node, bNodeLink &viewer_link) { - Main *bmain = CTX_data_main(&C); + Main *bmain = CTX_data_main_from_id(&C, &snode.edittree->id); remove_links_to_unavailable_viewer_sockets(*snode.edittree, viewer_node); viewer_link.flag &= ~NODE_LINK_MUTED; viewer_node.flag &= ~NODE_MUTED; @@ -683,12 +683,13 @@ static int node_active_link_viewer_exec(bContext *C, wmOperator * /*op*/) { SpaceNode &snode = *CTX_wm_space_node(C); bNode *node = nodeGetActive(snode.edittree); + Main *bmain = CTX_data_main_from_id(C, &snode.edittree->id); if (!node) { return OPERATOR_CANCELLED; } - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + ED_preview_kill_jobs(CTX_wm_manager(C), bmain); bNodeSocket *socket_to_view = nullptr; LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) { @@ -702,7 +703,7 @@ static int node_active_link_viewer_exec(bContext *C, wmOperator * /*op*/) return OPERATOR_CANCELLED; } - ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree); + ED_node_tree_propagate_change(C, bmain, snode.edittree); return OPERATOR_FINISHED; } @@ -1027,10 +1028,10 @@ static void node_remove_existing_links_if_needed(bNodeLinkDrag &nldrag, bNodeTre static void add_dragged_links_to_tree(bContext &C, bNodeLinkDrag &nldrag) { - Main *bmain = CTX_data_main(&C); ARegion ®ion = *CTX_wm_region(&C); SpaceNode &snode = *CTX_wm_space_node(&C); bNodeTree &ntree = *snode.edittree; + Main *bmain = CTX_data_main_from_id(&C, &ntree.id); /* Handle node links already occupying the socket. */ if (const bNodeSocket *linked_socket = nldrag.hovered_socket) { @@ -1353,9 +1354,9 @@ static std::unique_ptr node_link_init(ARegion ®ion, static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - Main &bmain = *CTX_data_main(C); SpaceNode &snode = *CTX_wm_space_node(C); ARegion ®ion = *CTX_wm_region(C); + Main *bmain = CTX_data_main_from_id(C, &snode.edittree->id); bool detach = RNA_boolean_get(op->ptr, "detach"); @@ -1366,7 +1367,7 @@ static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event) UI_view2d_region_to_view(®ion.v2d, mval[0], mval[1], &cursor[0], &cursor[1]); RNA_float_set_array(op->ptr, "drag_start", cursor); - ED_preview_kill_jobs(CTX_wm_manager(C), &bmain); + ED_preview_kill_jobs(CTX_wm_manager(C), bmain); std::unique_ptr nldrag = node_link_init(region, snode, cursor, detach); if (!nldrag) { @@ -1433,12 +1434,12 @@ void NODE_OT_link(wmOperatorType *ot) /* Makes a link between selected output and input sockets. */ static int node_make_link_exec(bContext *C, wmOperator *op) { - Main &bmain = *CTX_data_main(C); SpaceNode &snode = *CTX_wm_space_node(C); bNodeTree &node_tree = *snode.edittree; + Main *bmain = CTX_data_main_from_id(C, &node_tree.id); const bool replace = RNA_boolean_get(op->ptr, "replace"); - ED_preview_kill_jobs(CTX_wm_manager(C), &bmain); + ED_preview_kill_jobs(CTX_wm_manager(C), bmain); snode_autoconnect(snode, true, replace); @@ -1446,7 +1447,7 @@ static int node_make_link_exec(bContext *C, wmOperator *op) node_deselect_all_input_sockets(node_tree, false); node_deselect_all_output_sockets(node_tree, false); - ED_node_tree_propagate_change(C, &bmain, &node_tree); + ED_node_tree_propagate_change(C, bmain, &node_tree); return OPERATOR_FINISHED; } @@ -1478,7 +1479,6 @@ void NODE_OT_link_make(wmOperatorType *ot) static int cut_links_exec(bContext *C, wmOperator *op) { - Main &bmain = *CTX_data_main(C); SpaceNode &snode = *CTX_wm_space_node(C); const ARegion ®ion = *CTX_wm_region(C); @@ -1501,9 +1501,11 @@ static int cut_links_exec(bContext *C, wmOperator *op) bool found = false; + bNodeTree &node_tree = *snode.edittree; + Main &bmain = *CTX_data_main_from_id(C, &node_tree.id); + ED_preview_kill_jobs(CTX_wm_manager(C), &bmain); - bNodeTree &node_tree = *snode.edittree; node_tree.ensure_topology_cache(); Set links_to_remove; @@ -1535,7 +1537,7 @@ static int cut_links_exec(bContext *C, wmOperator *op) update_multi_input_indices_for_removed_links(*node); } - ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree); + ED_node_tree_propagate_change(C, &bmain, &node_tree); if (found) { return OPERATOR_FINISHED; } @@ -1586,10 +1588,10 @@ bool all_links_muted(const bNodeSocket &socket) static int mute_links_exec(bContext *C, wmOperator *op) { - Main &bmain = *CTX_data_main(C); SpaceNode &snode = *CTX_wm_space_node(C); const ARegion ®ion = *CTX_wm_region(C); bNodeTree &ntree = *snode.edittree; + Main &bmain = *CTX_data_main_from_id(C, &ntree.id); Vector path; RNA_BEGIN (op->ptr, itemptr, "path") { @@ -1665,7 +1667,7 @@ static int mute_links_exec(bContext *C, wmOperator *op) } } - ED_node_tree_propagate_change(C, CTX_data_main(C), &ntree); + ED_node_tree_propagate_change(C, &bmain, &ntree); return OPERATOR_FINISHED; } @@ -1704,8 +1706,9 @@ static int detach_links_exec(bContext *C, wmOperator * /*op*/) { SpaceNode &snode = *CTX_wm_space_node(C); bNodeTree &ntree = *snode.edittree; + Main &bmain = *CTX_data_main_from_id(C, &ntree.id); - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + ED_preview_kill_jobs(CTX_wm_manager(C), &bmain); for (bNode *node : ntree.all_nodes()) { if (node->flag & SELECT) { @@ -1713,7 +1716,7 @@ static int detach_links_exec(bContext *C, wmOperator * /*op*/) } } - ED_node_tree_propagate_change(C, CTX_data_main(C), &ntree); + ED_node_tree_propagate_change(C, &bmain, &ntree); return OPERATOR_FINISHED; } @@ -1824,9 +1827,9 @@ static void node_join_attach_recursive(bNodeTree &ntree, static int node_join_exec(bContext *C, wmOperator * /*op*/) { - Main &bmain = *CTX_data_main(C); SpaceNode &snode = *CTX_wm_space_node(C); bNodeTree &ntree = *snode.edittree; + Main &bmain = *CTX_data_main_from_id(C, &ntree.id); const VectorSet selected_nodes = get_selected_nodes(ntree); diff --git a/source/blender/editors/space_node/node_select.cc b/source/blender/editors/space_node/node_select.cc index 5d0450e317f..e038d31472d 100644 --- a/source/blender/editors/space_node/node_select.cc +++ b/source/blender/editors/space_node/node_select.cc @@ -512,9 +512,9 @@ void NODE_OT_select_grouped(wmOperatorType *ot) void node_select_single(bContext &C, bNode &node) { - Main *bmain = CTX_data_main(&C); SpaceNode &snode = *CTX_wm_space_node(&C); bNodeTree &node_tree = *snode.edittree; + Main *bmain = CTX_data_main_from_id(&C, &node_tree.id); const Object *ob = CTX_data_active_object(&C); const Scene *scene = CTX_data_scene(&C); const wmWindowManager *wm = CTX_wm_manager(&C); @@ -543,9 +543,9 @@ static bool node_mouse_select(bContext *C, const int2 mval, SelectPick_Params *params) { - Main &bmain = *CTX_data_main(C); SpaceNode &snode = *CTX_wm_space_node(C); bNodeTree &node_tree = *snode.edittree; + Main &bmain = *CTX_data_main_from_id(C, &node_tree.id); ARegion ®ion = *CTX_wm_region(C); const Object *ob = CTX_data_active_object(C); const Scene *scene = CTX_data_scene(C); diff --git a/source/blender/editors/space_node/node_shader_preview.cc b/source/blender/editors/space_node/node_shader_preview.cc index b7fd483bd21..bd01c7e3d0a 100644 --- a/source/blender/editors/space_node/node_shader_preview.cc +++ b/source/blender/editors/space_node/node_shader_preview.cc @@ -803,7 +803,7 @@ static void ensure_nodetree_previews(const bContext &C, job_data->scene = scene; job_data->tree_previews = &tree_previews; - job_data->bmain = CTX_data_main(&C); + job_data->bmain = CTX_data_main_from_id(&C, &displayed_nodetree->id); job_data->mat_copy = duplicate_material(material); job_data->rendering_node = nullptr; job_data->rendering_AOVs = false; diff --git a/source/blender/editors/space_node/node_templates.cc b/source/blender/editors/space_node/node_templates.cc index 31d47ce5b66..d48787df732 100644 --- a/source/blender/editors/space_node/node_templates.cc +++ b/source/blender/editors/space_node/node_templates.cc @@ -195,7 +195,7 @@ static void node_socket_add_replace(const bContext *C, int type, NodeLinkItem *item) { - Main *bmain = CTX_data_main(C); + Main *bmain = CTX_data_main_from_id(C, &ntree->id); bNode *node_from; bNodeSocket *sock_from_tmp; bNode *node_prev = nullptr; @@ -600,7 +600,6 @@ static void node_menu_column_foreach_cb(void *calldata, int nclass, const char * static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_p) { - Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); uiBlock *block = uiLayoutGetBlock(layout); uiBut *but = (uiBut *)but_p; @@ -608,6 +607,7 @@ static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_ NodeLinkArg *arg = (NodeLinkArg *)but->func_argN; bNodeSocket *sock = arg->sock; bNodeTreeType *ntreetype = arg->ntree->typeinfo; + Main *bmain = CTX_data_main_from_id(C, &arg->ntree->id); UI_block_layout_set_current(block, layout); split = uiLayoutSplit(layout, 0.0f, false); @@ -617,7 +617,7 @@ static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_ arg->layout = split; if (ntreetype && ntreetype->foreach_nodeclass) { - ntreetype->foreach_nodeclass(scene, arg, node_menu_column_foreach_cb); + ntreetype->foreach_nodeclass(arg, node_menu_column_foreach_cb); } column = uiLayoutColumn(split, false); @@ -722,9 +722,9 @@ static void ui_node_draw_input(uiLayout &layout, static void node_panel_toggle_button_cb(bContext *C, void *panel_state_argv, void *ntree_argv) { - Main *bmain = CTX_data_main(C); bNodePanelState *panel_state = static_cast(panel_state_argv); bNodeTree *ntree = static_cast(ntree_argv); + Main *bmain = CTX_data_main_from_id(C, &ntree->id); panel_state->flag ^= NODE_PANEL_COLLAPSED; diff --git a/source/blender/editors/util/ed_util.cc b/source/blender/editors/util/ed_util.cc index 585064ad685..900bf404402 100644 --- a/source/blender/editors/util/ed_util.cc +++ b/source/blender/editors/util/ed_util.cc @@ -358,12 +358,8 @@ void apply_keyb_grid( } } -void unpack_menu(bContext *C, - const char *opname, - const char *id_name, - const char *abs_name, - const char *folder, - PackedFile *pf) +void unpack_menu( + bContext *C, const char *opname, const char *abs_name, const char *folder, PackedFile *pf) { Main *bmain = CTX_data_main(C); PointerRNA props_ptr; @@ -385,7 +381,6 @@ void unpack_menu(bContext *C, UI_ITEM_NONE, &props_ptr); RNA_enum_set(&props_ptr, "method", PF_REMOVE); - RNA_string_set(&props_ptr, "id", id_name); if (blendfile_path[0] != '\0') { char local_name[FILE_MAXDIR + FILE_MAX], fi[FILE_MAX]; @@ -399,7 +394,6 @@ void unpack_menu(bContext *C, uiItemFullO_ptr( layout, ot, line, ICON_NONE, nullptr, WM_OP_EXEC_DEFAULT, UI_ITEM_NONE, &props_ptr); RNA_enum_set(&props_ptr, "method", PF_WRITE_LOCAL); - RNA_string_set(&props_ptr, "id", id_name); break; case PF_CMP_EQUAL: @@ -408,7 +402,6 @@ void unpack_menu(bContext *C, uiItemFullO_ptr( layout, ot, line, ICON_NONE, nullptr, WM_OP_EXEC_DEFAULT, UI_ITEM_NONE, &props_ptr); RNA_enum_set(&props_ptr, "method", PF_USE_LOCAL); - RNA_string_set(&props_ptr, "id", id_name); break; case PF_CMP_DIFFERS: @@ -417,14 +410,12 @@ void unpack_menu(bContext *C, uiItemFullO_ptr( layout, ot, line, ICON_NONE, nullptr, WM_OP_EXEC_DEFAULT, UI_ITEM_NONE, &props_ptr); RNA_enum_set(&props_ptr, "method", PF_USE_LOCAL); - RNA_string_set(&props_ptr, "id", id_name); SNPRINTF(line, IFACE_("Overwrite %s"), local_name); // uiItemEnumO_ptr(layout, ot, line, ICON_NONE, "method", PF_WRITE_LOCAL); uiItemFullO_ptr( layout, ot, line, ICON_NONE, nullptr, WM_OP_EXEC_DEFAULT, UI_ITEM_NONE, &props_ptr); RNA_enum_set(&props_ptr, "method", PF_WRITE_LOCAL); - RNA_string_set(&props_ptr, "id", id_name); break; } } @@ -437,7 +428,6 @@ void unpack_menu(bContext *C, uiItemFullO_ptr( layout, ot, line, ICON_NONE, nullptr, WM_OP_EXEC_DEFAULT, UI_ITEM_NONE, &props_ptr); RNA_enum_set(&props_ptr, "method", PF_WRITE_ORIGINAL); - RNA_string_set(&props_ptr, "id", id_name); break; case PF_CMP_EQUAL: SNPRINTF(line, IFACE_("Use %s (identical)"), abs_name); @@ -445,7 +435,6 @@ void unpack_menu(bContext *C, uiItemFullO_ptr( layout, ot, line, ICON_NONE, nullptr, WM_OP_EXEC_DEFAULT, UI_ITEM_NONE, &props_ptr); RNA_enum_set(&props_ptr, "method", PF_USE_ORIGINAL); - RNA_string_set(&props_ptr, "id", id_name); break; case PF_CMP_DIFFERS: SNPRINTF(line, IFACE_("Use %s (differs)"), abs_name); @@ -453,14 +442,12 @@ void unpack_menu(bContext *C, uiItemFullO_ptr( layout, ot, line, ICON_NONE, nullptr, WM_OP_EXEC_DEFAULT, UI_ITEM_NONE, &props_ptr); RNA_enum_set(&props_ptr, "method", PF_USE_ORIGINAL); - RNA_string_set(&props_ptr, "id", id_name); SNPRINTF(line, IFACE_("Overwrite %s"), abs_name); // uiItemEnumO_ptr(layout, ot, line, ICON_NONE, "method", PF_WRITE_ORIGINAL); uiItemFullO_ptr( layout, ot, line, ICON_NONE, nullptr, WM_OP_EXEC_DEFAULT, UI_ITEM_NONE, &props_ptr); RNA_enum_set(&props_ptr, "method", PF_WRITE_ORIGINAL); - RNA_string_set(&props_ptr, "id", id_name); break; } diff --git a/source/blender/makesrna/intern/makesrna.cc b/source/blender/makesrna/intern/makesrna.cc index ede8ab2557c..66b1b445ed6 100644 --- a/source/blender/makesrna/intern/makesrna.cc +++ b/source/blender/makesrna/intern/makesrna.cc @@ -3378,7 +3378,8 @@ static void rna_def_function_funcs(FILE *f, StructDefRNA *dsrna, FunctionDefRNA fprintf(f, ", "); } first = 0; - fprintf(f, "CTX_data_main(C)"); /* may have direct access later */ + /* May have direct access later. */ + fprintf(f, "(_ptr->owner_id) ? CTX_data_main_from_id(C, _ptr->owner_id) : CTX_data_main(C)"); } if (func->flag & FUNC_USE_CONTEXT) { diff --git a/source/blender/makesrna/intern/rna_ID.cc b/source/blender/makesrna/intern/rna_ID.cc index 1601fe6864e..dcd0ede8913 100644 --- a/source/blender/makesrna/intern/rna_ID.cc +++ b/source/blender/makesrna/intern/rna_ID.cc @@ -289,9 +289,9 @@ int rna_ID_name_length(PointerRNA *ptr) void rna_ID_name_set(PointerRNA *ptr, const char *value) { ID *id = (ID *)ptr->data; - Main *id_main = BKE_main_from_id(G_MAIN, id); + Main *bmain = BKE_main_from_id(G_MAIN, id); - BKE_libblock_rename(id_main, id, value); + BKE_libblock_rename(bmain, id, value); if (GS(id->name) == ID_OB) { Object *ob = (Object *)id; @@ -1172,14 +1172,14 @@ bool rna_IDMaterials_assign_int(PointerRNA *ptr, int key, const PointerRNA *assi return false; } - Main *id_main = BKE_main_from_id(G_MAIN, id); + Main *bmain = BKE_main_from_id(G_MAIN, id); if (mat) { - if (id_main != BKE_main_from_id(G_MAIN, &mat->id)) { + if (bmain != BKE_main_from_id(G_MAIN, &mat->id)) { return false; } } - BKE_id_material_assign(id_main, id, mat, key + 1); + BKE_id_material_assign(bmain, id, mat, key + 1); return true; } @@ -1231,8 +1231,8 @@ 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_main_from_id(G_MAIN, &lib->id); - BKE_library_filepath_set(id_main, lib, value); + Main *bmain = BKE_main_from_id(G_MAIN, &lib->id); + BKE_library_filepath_set(bmain, lib, value); } /* ***** ImagePreview ***** */ diff --git a/source/blender/makesrna/intern/rna_access.cc b/source/blender/makesrna/intern/rna_access.cc index d6a8f60436a..496b7f2db67 100644 --- a/source/blender/makesrna/intern/rna_access.cc +++ b/source/blender/makesrna/intern/rna_access.cc @@ -2390,11 +2390,8 @@ bool RNA_property_update_check(PropertyRNA *prop) void RNA_property_update(bContext *C, PointerRNA *ptr, PropertyRNA *prop) { - Main *main = CTX_data_main(C); - if (ptr->owner_id) { - main = BKE_main_from_id(main, ptr->owner_id); - } - rna_property_update(C, main, CTX_data_scene(C), ptr, prop); + Main *bmain = (ptr->owner_id) ? CTX_data_main_from_id(C, ptr->owner_id) : CTX_data_main(C); + rna_property_update(C, bmain, CTX_data_scene(C), ptr, prop); } void RNA_property_update_main(Main *bmain, Scene *scene, PointerRNA *ptr, PropertyRNA *prop) diff --git a/source/blender/makesrna/intern/rna_brush.cc b/source/blender/makesrna/intern/rna_brush.cc index da27cc082d5..94db3aa46c9 100644 --- a/source/blender/makesrna/intern/rna_brush.cc +++ b/source/blender/makesrna/intern/rna_brush.cc @@ -730,20 +730,20 @@ static void rna_Brush_material_update(bContext * /*C*/, PointerRNA * /*ptr*/) static void rna_Brush_main_tex_update(bContext *C, PointerRNA *ptr) { - Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); Brush *br = (Brush *)ptr->data; + Main *bmain = CTX_data_main_from_id(C, &br->id); BKE_paint_invalidate_overlay_tex(scene, view_layer, br->mtex.tex); rna_Brush_update(bmain, scene, ptr); } static void rna_Brush_secondary_tex_update(bContext *C, PointerRNA *ptr) { - Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); Brush *br = (Brush *)ptr->data; + Main *bmain = CTX_data_main_from_id(C, &br->id); BKE_paint_invalidate_overlay_tex(scene, view_layer, br->mask_mtex.tex); rna_Brush_update(bmain, scene, ptr); } diff --git a/source/blender/makesrna/intern/rna_image_api.cc b/source/blender/makesrna/intern/rna_image_api.cc index 2dc5e5bfcdd..538b849bfe5 100644 --- a/source/blender/makesrna/intern/rna_image_api.cc +++ b/source/blender/makesrna/intern/rna_image_api.cc @@ -56,7 +56,7 @@ static void rna_Image_save_render(Image *image, Scene *scene, const int quality) { - Main *bmain = CTX_data_main(C); + Main *bmain = CTX_data_main_from_id(C, &image->id); if (scene == nullptr) { scene = CTX_data_scene(C); diff --git a/source/blender/makesrna/intern/rna_light.cc b/source/blender/makesrna/intern/rna_light.cc index 306ae3e2661..dc053d784be 100644 --- a/source/blender/makesrna/intern/rna_light.cc +++ b/source/blender/makesrna/intern/rna_light.cc @@ -78,7 +78,7 @@ static void rna_Light_use_nodes_update(bContext *C, PointerRNA *ptr) ED_node_shader_default(C, &la->id); } - rna_Light_update(CTX_data_main(C), CTX_data_scene(C), ptr); + rna_Light_update(CTX_data_main_from_id(C, &la->id), CTX_data_scene(C), ptr); } #else diff --git a/source/blender/makesrna/intern/rna_linestyle.cc b/source/blender/makesrna/intern/rna_linestyle.cc index 44cabed816f..a963816570a 100644 --- a/source/blender/makesrna/intern/rna_linestyle.cc +++ b/source/blender/makesrna/intern/rna_linestyle.cc @@ -368,13 +368,14 @@ static void rna_LineStyle_update(Main * /*bmain*/, Scene * /*scene*/, PointerRNA static void rna_LineStyle_use_nodes_update(bContext *C, PointerRNA *ptr) { + Main *bmain = CTX_data_main_from_id(C, ptr->owner_id); FreestyleLineStyle *linestyle = (FreestyleLineStyle *)ptr->data; if (linestyle->use_nodes && linestyle->nodetree == nullptr) { BKE_linestyle_default_shader(C, linestyle); } - rna_LineStyle_update(CTX_data_main(C), CTX_data_scene(C), ptr); + rna_LineStyle_update(bmain, CTX_data_scene(C), ptr); } static LineStyleModifier *rna_LineStyle_color_modifier_add(FreestyleLineStyle *linestyle, diff --git a/source/blender/makesrna/intern/rna_material.cc b/source/blender/makesrna/intern/rna_material.cc index be8ce3beb05..b47cd97756a 100644 --- a/source/blender/makesrna/intern/rna_material.cc +++ b/source/blender/makesrna/intern/rna_material.cc @@ -153,7 +153,7 @@ static void rna_Material_texpaint_begin(CollectionPropertyIterator *iter, Pointe static void rna_Material_active_paint_texture_index_update(bContext *C, PointerRNA *ptr) { - Main *bmain = CTX_data_main(C); + Main *bmain = CTX_data_main_from_id(C, ptr->owner_id); Material *ma = (Material *)ptr->owner_id; if (ma->use_nodes && ma->nodetree) { @@ -193,8 +193,8 @@ static void rna_Material_active_paint_texture_index_update(bContext *C, PointerR static void rna_Material_use_nodes_update(bContext *C, PointerRNA *ptr) { + Main *bmain = CTX_data_main_from_id(C, ptr->owner_id); Material *ma = (Material *)ptr->data; - Main *bmain = CTX_data_main(C); if (ma->use_nodes && ma->nodetree == nullptr) { ED_node_shader_default(C, &ma->id); diff --git a/source/blender/makesrna/intern/rna_node_socket.cc b/source/blender/makesrna/intern/rna_node_socket.cc index 9f16f8dcf50..70db4838638 100644 --- a/source/blender/makesrna/intern/rna_node_socket.cc +++ b/source/blender/makesrna/intern/rna_node_socket.cc @@ -401,13 +401,14 @@ void rna_NodeSocketStandard_vector_range( static void rna_NodeSocketStandard_value_update(bContext *C, PointerRNA *ptr) { /* default update */ - rna_NodeSocket_update(CTX_data_main(C), CTX_data_scene(C), ptr); + Main *bmain = CTX_data_main_from_id(C, ptr->owner_id); + rna_NodeSocket_update(bmain, CTX_data_scene(C), ptr); } static void rna_NodeSocketStandard_value_and_relation_update(bContext *C, PointerRNA *ptr) { rna_NodeSocketStandard_value_update(C, ptr); - Main *bmain = CTX_data_main(C); + Main *bmain = CTX_data_main_from_id(C, ptr->owner_id); DEG_relations_tag_update(bmain); } diff --git a/source/blender/makesrna/intern/rna_nodetree.cc b/source/blender/makesrna/intern/rna_nodetree.cc index 5a5279a5aa0..81aed29fd4c 100644 --- a/source/blender/makesrna/intern/rna_nodetree.cc +++ b/source/blender/makesrna/intern/rna_nodetree.cc @@ -1084,7 +1084,7 @@ static bNode *rna_NodeTree_node_new(bNodeTree *ntree, ntreeTexCheckCyclics(ntree); } - Main *bmain = CTX_data_main(C); + Main *bmain = CTX_data_main_from_id(C, &ntree->id); ED_node_tree_propagate_change(C, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); @@ -1279,7 +1279,7 @@ static bool rna_NodeTree_contains_tree(bNodeTree *tree, bNodeTree *sub_tree) static void rna_NodeTree_interface_update(bNodeTree *ntree, bContext *C) { - Main *bmain = CTX_data_main(C); + Main *bmain = CTX_data_main_from_id(C, &ntree->id); ntree->tree_interface.tag_items_changed(); ED_node_tree_propagate_change(nullptr, bmain, ntree); } @@ -2181,8 +2181,9 @@ void rna_Node_update_relations(Main *bmain, Scene *scene, PointerRNA *ptr) static void rna_Node_socket_value_update(ID *id, bNode * /*node*/, bContext *C) { + Main *bmain = CTX_data_main_from_id(C, id); BKE_ntree_update_tag_all(reinterpret_cast(id)); - ED_node_tree_propagate_change(C, CTX_data_main(C), reinterpret_cast(id)); + ED_node_tree_propagate_change(C, bmain, reinterpret_cast(id)); } static void rna_Node_select_set(PointerRNA *ptr, bool value) @@ -3298,8 +3299,9 @@ static bool rna_Node_pair_with_output( int &output_node_id = zone_type.get_corresponding_output_id(*node); output_node_id = output_node->identifier; + Main *bmain = CTX_data_main_from_id(C, &ntree->id); BKE_ntree_update_tag_node_property(ntree, node); - ED_node_tree_propagate_change(C, CTX_data_main(C), ntree); + ED_node_tree_propagate_change(C, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); return true; } @@ -3540,7 +3542,8 @@ static bNodeSocket *rna_NodeOutputFile_slots_new( sock = ntreeCompositOutputFileAddSocket(ntree, node, name, im_format); - ED_node_tree_propagate_change(C, CTX_data_main(C), ntree); + Main *bmain = CTX_data_main_from_id(C, &ntree->id); + ED_node_tree_propagate_change(C, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); return sock; diff --git a/source/blender/makesrna/intern/rna_object.cc b/source/blender/makesrna/intern/rna_object.cc index dcd3b6f11f4..91e2c8146e8 100644 --- a/source/blender/makesrna/intern/rna_object.cc +++ b/source/blender/makesrna/intern/rna_object.cc @@ -1756,8 +1756,9 @@ bool rna_Object_constraints_override_apply(Main *bmain, static ModifierData *rna_Object_modifier_new( Object *object, bContext *C, ReportList *reports, const char *name, int type) { + Main *bmain = CTX_data_main_from_id(C, &object->id); ModifierData *md = blender::ed::object::modifier_add( - reports, CTX_data_main(C), CTX_data_scene(C), object, name, type); + reports, bmain, CTX_data_scene(C), object, name, type); WM_main_add_notifier(NC_OBJECT | ND_MODIFIER | NA_ADDED, object); @@ -1769,9 +1770,9 @@ static void rna_Object_modifier_remove(Object *object, ReportList *reports, PointerRNA *md_ptr) { + Main *bmain = CTX_data_main_from_id(C, &object->id); ModifierData *md = static_cast(md_ptr->data); - if (blender::ed::object::modifier_remove( - reports, CTX_data_main(C), CTX_data_scene(C), object, md) == false) + if (blender::ed::object::modifier_remove(reports, bmain, CTX_data_scene(C), object, md) == false) { /* error is already set */ return; @@ -1784,7 +1785,8 @@ static void rna_Object_modifier_remove(Object *object, static void rna_Object_modifier_clear(Object *object, bContext *C) { - blender::ed::object::modifiers_clear(CTX_data_main(C), CTX_data_scene(C), object); + Main *bmain = CTX_data_main_from_id(C, &object->id); + blender::ed::object::modifiers_clear(bmain, CTX_data_scene(C), object); WM_main_add_notifier(NC_OBJECT | ND_MODIFIER | NA_REMOVED, object); } @@ -1915,8 +1917,9 @@ bool rna_Object_modifiers_override_apply(Main *bmain, static GpencilModifierData *rna_Object_greasepencil_modifier_new( Object *object, bContext *C, ReportList *reports, const char *name, int type) { + Main *bmain = CTX_data_main_from_id(C, &object->id); return blender::ed::object::gpencil_modifier_add( - reports, CTX_data_main(C), CTX_data_scene(C), object, name, type); + reports, bmain, CTX_data_scene(C), object, name, type); } static void rna_Object_greasepencil_modifier_remove(Object *object, @@ -1924,10 +1927,9 @@ static void rna_Object_greasepencil_modifier_remove(Object *object, ReportList *reports, PointerRNA *gmd_ptr) { + Main *bmain = CTX_data_main_from_id(C, &object->id); GpencilModifierData *gmd = static_cast(gmd_ptr->data); - if (blender::ed::object::gpencil_modifier_remove(reports, CTX_data_main(C), object, gmd) == - false) - { + if (blender::ed::object::gpencil_modifier_remove(reports, bmain, object, gmd) == false) { /* error is already set */ return; } @@ -1939,7 +1941,8 @@ static void rna_Object_greasepencil_modifier_remove(Object *object, static void rna_Object_greasepencil_modifier_clear(Object *object, bContext *C) { - blender::ed::object::gpencil_modifier_clear(CTX_data_main(C), object); + Main *bmain = CTX_data_main_from_id(C, &object->id); + blender::ed::object::gpencil_modifier_clear(bmain, object); WM_main_add_notifier(NC_OBJECT | ND_MODIFIER | NA_REMOVED, object); } @@ -2000,8 +2003,8 @@ bool rna_Object_greasepencil_modifiers_override_apply( static ShaderFxData *rna_Object_shaderfx_new( Object *object, bContext *C, ReportList *reports, const char *name, int type) { - return blender::ed::object::shaderfx_add( - reports, CTX_data_main(C), CTX_data_scene(C), object, name, type); + Main *bmain = CTX_data_main_from_id(C, &object->id); + return blender::ed::object::shaderfx_add(reports, bmain, CTX_data_scene(C), object, name, type); } static void rna_Object_shaderfx_remove(Object *object, @@ -2009,8 +2012,9 @@ static void rna_Object_shaderfx_remove(Object *object, ReportList *reports, PointerRNA *gmd_ptr) { + Main *bmain = CTX_data_main_from_id(C, &object->id); ShaderFxData *gmd = static_cast(gmd_ptr->data); - if (blender::ed::object::shaderfx_remove(reports, CTX_data_main(C), object, gmd) == false) { + if (blender::ed::object::shaderfx_remove(reports, bmain, object, gmd) == false) { /* error is already set */ return; } @@ -2022,7 +2026,8 @@ static void rna_Object_shaderfx_remove(Object *object, static void rna_Object_shaderfx_clear(Object *object, bContext *C) { - blender::ed::object::shaderfx_clear(CTX_data_main(C), object); + Main *bmain = CTX_data_main_from_id(C, &object->id); + blender::ed::object::shaderfx_clear(bmain, object); WM_main_add_notifier(NC_OBJECT | ND_MODIFIER | NA_REMOVED, object); } diff --git a/source/blender/makesrna/intern/rna_object_api.cc b/source/blender/makesrna/intern/rna_object_api.cc index 3eea518473e..acac097c66d 100644 --- a/source/blender/makesrna/intern/rna_object_api.cc +++ b/source/blender/makesrna/intern/rna_object_api.cc @@ -463,7 +463,7 @@ static void rna_Object_to_curve_clear(Object *object) static PointerRNA rna_Object_shape_key_add( Object *ob, bContext *C, ReportList *reports, const char *name, bool from_mix) { - Main *bmain = CTX_data_main(C); + Main *bmain = CTX_data_main_from_id(C, &ob->id); KeyBlock *kb = nullptr; if ((kb = BKE_object_shapekey_insert(bmain, ob, name, from_mix))) { @@ -815,7 +815,7 @@ bool rna_Object_generate_gpencil_strokes(Object *ob, ob->id.name + 2); return false; } - Main *bmain = CTX_data_main(C); + Main *bmain = CTX_data_main_from_id(C, &ob->id); Scene *scene = CTX_data_scene(C); BKE_gpencil_convert_curve( diff --git a/source/blender/makesrna/intern/rna_scene.cc b/source/blender/makesrna/intern/rna_scene.cc index 3bb64819c3f..70f02bb5f15 100644 --- a/source/blender/makesrna/intern/rna_scene.cc +++ b/source/blender/makesrna/intern/rna_scene.cc @@ -1981,10 +1981,11 @@ static std::optional rna_SceneRenderView_path(const PointerRNA *ptr static void rna_Scene_use_nodes_update(bContext *C, PointerRNA *ptr) { Scene *scene = (Scene *)ptr->data; + Main *bmain = CTX_data_main_from_id(C, &scene->id); if (scene->use_nodes && scene->nodetree == nullptr) { ED_node_composit_default(C, scene); } - DEG_relations_tag_update(CTX_data_main(C)); + DEG_relations_tag_update(bmain); } static void rna_Physics_relations_update(Main *bmain, Scene * /*scene*/, PointerRNA * /*ptr*/) @@ -2142,7 +2143,7 @@ static void rna_Scene_simplify_update_impl(Main *bmain, static void rna_Scene_use_simplify_update(bContext *C, PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; - Main *bmain = CTX_data_main(C); + Main *bmain = CTX_data_main_from_id(C, &scene->id); Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); rna_Scene_simplify_update_impl(bmain, scene, false, depsgraph); } @@ -2150,7 +2151,7 @@ static void rna_Scene_use_simplify_update(bContext *C, PointerRNA *ptr) static void rna_Scene_simplify_volume_update(bContext *C, PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; - Main *bmain = CTX_data_main(C); + Main *bmain = CTX_data_main_from_id(C, &scene->id); Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); if (scene->r.mode & R_SIMPLIFY) { rna_Scene_simplify_update_impl(bmain, scene, false, depsgraph); diff --git a/source/blender/makesrna/intern/rna_texture.cc b/source/blender/makesrna/intern/rna_texture.cc index 208f87e7c31..6e6f18e574f 100644 --- a/source/blender/makesrna/intern/rna_texture.cc +++ b/source/blender/makesrna/intern/rna_texture.cc @@ -242,7 +242,8 @@ static void rna_Texture_type_set(PointerRNA *ptr, int value) void rna_TextureSlotTexture_update(bContext *C, PointerRNA *ptr) { - DEG_relations_tag_update(CTX_data_main(C)); + Main *bmain = CTX_data_main_from_id(C, ptr->owner_id); + DEG_relations_tag_update(bmain); rna_TextureSlot_update(C, ptr); } @@ -440,6 +441,7 @@ static void rna_Texture_use_color_ramp_set(PointerRNA *ptr, bool value) static void rna_Texture_use_nodes_update(bContext *C, PointerRNA *ptr) { Tex *tex = (Tex *)ptr->data; + Main *bmain = CTX_data_main_from_id(C, &tex->id); if (tex->use_nodes) { tex->type = 0; @@ -449,7 +451,7 @@ static void rna_Texture_use_nodes_update(bContext *C, PointerRNA *ptr) } } - rna_Texture_nodes_update(CTX_data_main(C), CTX_data_scene(C), ptr); + rna_Texture_nodes_update(bmain, CTX_data_scene(C), ptr); } static void rna_ImageTexture_mipmap_set(PointerRNA *ptr, bool value) diff --git a/source/blender/makesrna/intern/rna_world.cc b/source/blender/makesrna/intern/rna_world.cc index 962f7d25c6e..22579377b76 100644 --- a/source/blender/makesrna/intern/rna_world.cc +++ b/source/blender/makesrna/intern/rna_world.cc @@ -76,16 +76,15 @@ static void rna_World_draw_update(Main * /*bmain*/, Scene * /*scene*/, PointerRN static void rna_World_use_nodes_update(bContext *C, PointerRNA *ptr) { World *wrld = (World *)ptr->data; - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); + Main *bmain = CTX_data_main_from_id(C, &wrld->id); if (wrld->use_nodes && wrld->nodetree == nullptr) { ED_node_shader_default(C, &wrld->id); } DEG_relations_tag_update(bmain); - rna_World_update(bmain, scene, ptr); - rna_World_draw_update(bmain, scene, ptr); + rna_World_update(bmain, nullptr, ptr); + rna_World_draw_update(bmain, nullptr, ptr); } void rna_World_lightgroup_get(PointerRNA *ptr, char *value) diff --git a/source/blender/nodes/composite/node_composite_tree.cc b/source/blender/nodes/composite/node_composite_tree.cc index d8e2356c147..ecd28b33974 100644 --- a/source/blender/nodes/composite/node_composite_tree.cc +++ b/source/blender/nodes/composite/node_composite_tree.cc @@ -46,7 +46,7 @@ static void composite_get_from_context( *r_ntree = scene->nodetree; } -static void foreach_nodeclass(Scene * /*scene*/, void *calldata, bNodeClassCallback func) +static void foreach_nodeclass(void *calldata, bNodeClassCallback func) { func(calldata, NODE_CLASS_INPUT, N_("Input")); func(calldata, NODE_CLASS_OUTPUT, N_("Output")); diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc index 7343d2afa0f..f84ceb5e78a 100644 --- a/source/blender/nodes/geometry/node_geometry_tree.cc +++ b/source/blender/nodes/geometry/node_geometry_tree.cc @@ -71,7 +71,7 @@ static void geometry_node_tree_update(bNodeTree *ntree) ntree_update_reroute_nodes(ntree); } -static void foreach_nodeclass(Scene * /*scene*/, void *calldata, bNodeClassCallback func) +static void foreach_nodeclass(void *calldata, bNodeClassCallback func) { func(calldata, NODE_CLASS_INPUT, N_("Input")); func(calldata, NODE_CLASS_GEOMETRY, N_("Geometry")); diff --git a/source/blender/nodes/shader/node_shader_tree.cc b/source/blender/nodes/shader/node_shader_tree.cc index 1ea1551ee43..7b7c6b85fe5 100644 --- a/source/blender/nodes/shader/node_shader_tree.cc +++ b/source/blender/nodes/shader/node_shader_tree.cc @@ -106,7 +106,7 @@ static void shader_get_from_context( } } -static void foreach_nodeclass(Scene * /*scene*/, void *calldata, bNodeClassCallback func) +static void foreach_nodeclass(void *calldata, bNodeClassCallback func) { func(calldata, NODE_CLASS_INPUT, N_("Input")); func(calldata, NODE_CLASS_OUTPUT, N_("Output")); diff --git a/source/blender/nodes/texture/node_texture_tree.cc b/source/blender/nodes/texture/node_texture_tree.cc index b05d5f2d4d5..4c9754e0150 100644 --- a/source/blender/nodes/texture/node_texture_tree.cc +++ b/source/blender/nodes/texture/node_texture_tree.cc @@ -77,7 +77,7 @@ static void texture_get_from_context( } } -static void foreach_nodeclass(Scene * /*scene*/, void *calldata, bNodeClassCallback func) +static void foreach_nodeclass(void *calldata, bNodeClassCallback func) { func(calldata, NODE_CLASS_INPUT, N_("Input")); func(calldata, NODE_CLASS_OUTPUT, N_("Output")); -- 2.30.2 From 43c731166950ebd99dbfe9d94014ad7d1c5ee2fd Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 7 May 2024 20:13:09 +0200 Subject: [PATCH 239/244] Fix missing secondary grease pencil smooth stroke brush --- source/blender/blenkernel/BKE_paint.hh | 1 + source/blender/blenkernel/intern/paint.cc | 11 +++++++++++ .../blender/editors/gpencil_legacy/gpencil_paint.cc | 2 +- .../editors/gpencil_legacy/gpencil_sculpt_paint.cc | 3 +-- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/BKE_paint.hh b/source/blender/blenkernel/BKE_paint.hh index f54c8adf617..82b188c3afa 100644 --- a/source/blender/blenkernel/BKE_paint.hh +++ b/source/blender/blenkernel/BKE_paint.hh @@ -202,6 +202,7 @@ PaintMode BKE_paintmode_get_from_tool(const bToolRef *tref); Brush *BKE_paint_brush(Paint *paint); const Brush *BKE_paint_brush_for_read(const Paint *paint); +Brush *BKE_paint_brush_from_essentials(Main *bmain, const char *name); bool BKE_paint_brush_set(Paint *paint, Brush *brush); bool BKE_paint_brush_set_default(Main *bmain, Paint *paint); diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 3622cb00d49..d363c495f55 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -691,6 +691,17 @@ bool BKE_paint_brush_asset_set(Paint *paint, return true; } +Brush *BKE_paint_brush_from_essentials(Main *bmain, const char *name) +{ + AssetWeakReference weak_ref; + weak_ref.asset_library_type = eAssetLibraryType::ASSET_LIBRARY_ESSENTIALS; + weak_ref.relative_asset_identifier = BLI_sprintfN("brushes/essentials_brushes.blend/Brush/%s", + name); + + return reinterpret_cast( + blender::bke::asset_edit_id_from_weak_reference(*bmain, ID_BR, weak_ref)); +} + static void paint_brush_set_essentials_reference(Paint *paint, const char *name) { /* Set brush asset reference to a named brush in the essentials asset library. */ diff --git a/source/blender/editors/gpencil_legacy/gpencil_paint.cc b/source/blender/editors/gpencil_legacy/gpencil_paint.cc index 22cac4d295c..98a2897efdf 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_paint.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_paint.cc @@ -1941,7 +1941,7 @@ static void gpencil_init_drawing_brush(bContext *C, tGPsdata *p) if (p->brush->gpencil_tool != GPAINT_TOOL_ERASE) { /* TODO: make this work again with "Smooth Eraser" essentials brush. * See od gpencil_set_default_eraser and gpencil_set_default_eraser. */ - p->eraser = p->brush; + p->eraser = BKE_paint_brush_from_essentials(CTX_data_main(C), "Smooth Eraser"); } else { p->eraser = p->brush; diff --git a/source/blender/editors/gpencil_legacy/gpencil_sculpt_paint.cc b/source/blender/editors/gpencil_legacy/gpencil_sculpt_paint.cc index 9c007bff967..04548eb45e8 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_sculpt_paint.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_sculpt_paint.cc @@ -2180,8 +2180,7 @@ static void gpencil_sculpt_brush_apply(bContext *C, wmOperator *op, PointerRNA * static Brush *gpencil_sculpt_get_smooth_brush(tGP_BrushEditData *gso) { Main *bmain = gso->bmain; - Brush *brush = static_cast( - BLI_findstring(&bmain->brushes, "Smooth Stroke", offsetof(ID, name) + 2)); + Brush *brush = BKE_paint_brush_from_essentials(bmain, "Smooth Stroke"); if (brush && !brush->gpencil_settings) { BKE_brush_init_gpencil_settings(brush); } -- 2.30.2 From 9cf3b1ed77eb965434b4547b2c74a162886fa98d Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 7 May 2024 20:51:08 +0200 Subject: [PATCH 240/244] Fix: Remove code that should not have been committed yet --- source/blender/editors/gpencil_legacy/gpencil_paint.cc | 2 +- source/blender/editors/space_image/image_ops.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/gpencil_legacy/gpencil_paint.cc b/source/blender/editors/gpencil_legacy/gpencil_paint.cc index 98a2897efdf..22cac4d295c 100644 --- a/source/blender/editors/gpencil_legacy/gpencil_paint.cc +++ b/source/blender/editors/gpencil_legacy/gpencil_paint.cc @@ -1941,7 +1941,7 @@ static void gpencil_init_drawing_brush(bContext *C, tGPsdata *p) if (p->brush->gpencil_tool != GPAINT_TOOL_ERASE) { /* TODO: make this work again with "Smooth Eraser" essentials brush. * See od gpencil_set_default_eraser and gpencil_set_default_eraser. */ - p->eraser = BKE_paint_brush_from_essentials(CTX_data_main(C), "Smooth Eraser"); + p->eraser = p->brush; } else { p->eraser = p->brush; diff --git a/source/blender/editors/space_image/image_ops.cc b/source/blender/editors/space_image/image_ops.cc index 4ce0f7f9474..a8b6dc51ccc 100644 --- a/source/blender/editors/space_image/image_ops.cc +++ b/source/blender/editors/space_image/image_ops.cc @@ -1938,7 +1938,7 @@ static int image_save_as_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -static bool image_save_as_check(bContext *C, wmOperator *op) +static bool image_save_as_check(bContext * /*C*/, wmOperator *op) { ImageSaveData *isd = static_cast(op->customdata); -- 2.30.2 From 627340e8714ef74656360ee29ea5bb078b73aabe Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 8 May 2024 15:22:38 +0200 Subject: [PATCH 241/244] Brush Assets: Slightly flatten hierarchy and consistency in catalog names Mesh > Sculpt to Mesh Sculpt, Painting to Paint, Transforming to Transform. --- release/datafiles/assets | 2 +- source/blender/blenloader/intern/versioning_userdef.cc | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/release/datafiles/assets b/release/datafiles/assets index 7181d6dccb9..fdfae4e706b 160000 --- a/release/datafiles/assets +++ b/release/datafiles/assets @@ -1 +1 @@ -Subproject commit 7181d6dccb9fe4184340f9f5b1c381f8089fe4ec +Subproject commit fdfae4e706ba4f3cad31b2d3822584b59a8fef85 diff --git a/source/blender/blenloader/intern/versioning_userdef.cc b/source/blender/blenloader/intern/versioning_userdef.cc index 07a3762495c..f5149c04c55 100644 --- a/source/blender/blenloader/intern/versioning_userdef.cc +++ b/source/blender/blenloader/intern/versioning_userdef.cc @@ -952,11 +952,11 @@ void blo_do_versions_userdef(UserDef *userdef) { BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled( - userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh/Sculpt/Cloth"); + userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh Sculpt/Cloth"); BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled( - userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh/Sculpt/General"); + userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh Sculpt/General"); BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled( - userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh/Sculpt/Painting"); + userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh Sculpt/Paint"); } /** -- 2.30.2 From 346e4911ee2049931badd5d74050b100fd40eee6 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 8 May 2024 15:34:27 +0200 Subject: [PATCH 242/244] Fix: Missing grease pencil modes in brush asset versioning Still doesn't fix the missing brush selection in the 2D animation template though. --- source/blender/blenloader/intern/versioning_400.cc | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/source/blender/blenloader/intern/versioning_400.cc b/source/blender/blenloader/intern/versioning_400.cc index 894a80d472f..75512badda9 100644 --- a/source/blender/blenloader/intern/versioning_400.cc +++ b/source/blender/blenloader/intern/versioning_400.cc @@ -2078,10 +2078,17 @@ static void update_paint_modes_for_brush_assets(Main &bmain) } if (!ELEM(tref->mode, CTX_MODE_SCULPT, - CTX_MODE_SCULPT_CURVES, - CTX_MODE_PAINT_TEXTURE, CTX_MODE_PAINT_VERTEX, - CTX_MODE_PAINT_WEIGHT)) + CTX_MODE_PAINT_WEIGHT, + CTX_MODE_PAINT_TEXTURE, + CTX_MODE_PAINT_GPENCIL_LEGACY, + CTX_MODE_PAINT_GREASE_PENCIL, + CTX_MODE_SCULPT_GPENCIL_LEGACY, + CTX_MODE_SCULPT_GREASE_PENCIL, + CTX_MODE_WEIGHT_GPENCIL_LEGACY, + CTX_MODE_WEIGHT_GREASE_PENCIL, + CTX_MODE_VERTEX_GPENCIL_LEGACY, + CTX_MODE_SCULPT_CURVES)) { continue; } -- 2.30.2 From 6f4ed4c82501f387182ea34fa29775600afe0690 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 8 May 2024 12:23:44 -0400 Subject: [PATCH 243/244] Fix: Some GPv3 modes have missing asset shelf --- scripts/startup/bl_ui/properties_paint_common.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/startup/bl_ui/properties_paint_common.py b/scripts/startup/bl_ui/properties_paint_common.py index bcab5c42cec..f6b1f81667f 100644 --- a/scripts/startup/bl_ui/properties_paint_common.py +++ b/scripts/startup/bl_ui/properties_paint_common.py @@ -47,8 +47,11 @@ class BrushAssetShelf: 'SCULPT_GPENCIL': "VIEW3D_AST_brush_gpencil_sculpt", 'WEIGHT_GPENCIL': "VIEW3D_AST_brush_gpencil_weight", 'VERTEX_GPENCIL': "VIEW3D_AST_brush_gpencil_vertex", + 'PAINT_GREASE_PENCIL': "VIEW3D_AST_brush_gpencil_paint", + 'SCULPT_GREASE_PENCIL': "VIEW3D_AST_brush_gpencil_sculpt", + 'WEIGHT_GREASE_PENCIL': "VIEW3D_AST_brush_gpencil_weight", + 'VERTEX_GREASE_PENCIL': "VIEW3D_AST_brush_gpencil_vertex", 'SCULPT_CURVES': "VIEW3D_AST_brush_sculpt_curves", - 'PAINT_GREASE_PENCIL': "VIEW3D_AST_brush_grease_pencil_paint", } mode = UnifiedPaintPanel.get_brush_mode(context) return mode_map[mode] -- 2.30.2 From 1e12a4eb0da70cdbbdf65149b7414c28b22378ff Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 8 May 2024 13:03:54 -0400 Subject: [PATCH 244/244] Cleanup: Remove unnecessary include --- source/blender/editors/render/render_shading.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/source/blender/editors/render/render_shading.cc b/source/blender/editors/render/render_shading.cc index 44f68f126e1..9f8158cb55c 100644 --- a/source/blender/editors/render/render_shading.cc +++ b/source/blender/editors/render/render_shading.cc @@ -35,7 +35,6 @@ #include "BKE_anim_data.hh" #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" -- 2.30.2