From 03652c851c06371c5ea34dd08db096901711aa03 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 23 Jul 2024 18:23:02 +0200 Subject: [PATCH] Fix #122961: Get out of 3D local views when they get empty. This commit covers the case where all objects get removed from local view, and an undo or blendfile read leads to such empty local views. It also adds code to remapping/foreachID View3D callbacks to tag potential local views as 'maybe empty', and use the View3D refresh callback to actually check and potentially exit the local empty view. Pull Request: https://projects.blender.org/blender/blender/pulls/123128 --- source/blender/editors/include/ED_view3d.hh | 21 +++++++++ .../editors/space_view3d/space_view3d.cc | 31 ++++++++++++- .../editors/space_view3d/view3d_view.cc | 43 ++++++++++++++++++- source/blender/editors/util/ed_util.cc | 17 ++++++++ source/blender/makesdna/DNA_view3d_types.h | 2 + .../blender/windowmanager/intern/wm_files.cc | 15 +++++++ 6 files changed, 127 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/include/ED_view3d.hh b/source/blender/editors/include/ED_view3d.hh index b11b795bdaa..372281ab678 100644 --- a/source/blender/editors/include/ED_view3d.hh +++ b/source/blender/editors/include/ED_view3d.hh @@ -1303,6 +1303,27 @@ void ED_view3d_buttons_region_layout_ex(const bContext *C, /* `view3d_view.cc` */ +/** + * Exit 'local view' of given View3D editor, if it is active and there is nothing to display in it + * anymore. + * + * \param depsgraph Optional, only required for #frame_selected. + * \param frame_selected Frame the newly out-of-local view to show currently visible selected + * objects. Will only do something if a valid #depsgraph pointer is also provided. + * \param smooth_viewtx Smooth transition time (in milliseconds) between current view and final + * view, if changes are hapenning. Currently only used if #frame_selected is enabled. + * + * \return `true` if the local view was actually exited. + */ +bool ED_localview_exit_if_empty(const Depsgraph *depsgraph, + Scene *scene, + ViewLayer *view_layer, + wmWindowManager *wm, + wmWindow *win, + View3D *v3d, + ScrArea *area, + bool frame_selected = true, + int smooth_viewtx = 0); /** * See if current UUID is valid, otherwise set a valid UUID to v3d, * Try to keep the same UUID previously used to allow users to quickly toggle back and forth. diff --git a/source/blender/editors/space_view3d/space_view3d.cc b/source/blender/editors/space_view3d/space_view3d.cc index a3799e7c6a0..9236b80a46b 100644 --- a/source/blender/editors/space_view3d/space_view3d.cc +++ b/source/blender/editors/space_view3d/space_view3d.cc @@ -1112,6 +1112,9 @@ static void view3d_main_region_listener(const wmRegionListenerParams *params) case ND_LAYER_CONTENT: ED_region_tag_redraw(region); WM_gizmomap_tag_refresh(gzmap); + if (v3d->localvd && v3d->localvd->runtime.flag & V3D_RUNTIME_LOCAL_MAYBE_EMPTY) { + ED_area_tag_refresh(area); + } break; case ND_LAYER: if (wmn->reference) { @@ -1315,6 +1318,11 @@ static void view3d_main_region_listener(const wmRegionListenerParams *params) break; case NC_ID: if (ELEM(wmn->action, NA_RENAME, NA_EDITED, NA_ADDED, NA_REMOVED)) { + if (ELEM(wmn->action, NA_EDITED, NA_REMOVED) && v3d->localvd && + v3d->localvd->runtime.flag & V3D_RUNTIME_LOCAL_MAYBE_EMPTY) + { + ED_area_tag_refresh(area); + } ED_region_tag_redraw(region); WM_gizmomap_tag_refresh(gzmap); } @@ -1976,9 +1984,20 @@ static void space_view3d_listener(const wmSpaceTypeListenerParams *params) static void space_view3d_refresh(const bContext *C, ScrArea *area) { - UNUSED_VARS(C); View3D *v3d = (View3D *)area->spacedata.first; MEM_SAFE_FREE(v3d->runtime.local_stats); + + if (v3d->localvd && v3d->localvd->runtime.flag & V3D_RUNTIME_LOCAL_MAYBE_EMPTY) { + ED_localview_exit_if_empty(CTX_data_ensure_evaluated_depsgraph(C), + CTX_data_scene(C), + CTX_data_view_layer(C), + CTX_wm_manager(C), + CTX_wm_window(C), + v3d, + CTX_wm_area(C), + true, + 300); + } } static void view3d_id_remap_v3d_ob_centers(View3D *v3d, @@ -2033,6 +2052,9 @@ static void view3d_id_remap(ScrArea *area, if (view3d->localvd != nullptr) { /* Object centers in local-view aren't used, see: #52663 */ view3d_id_remap_v3d(area, slink, view3d->localvd, mappings, true); + /* Remapping is potentially modifying ID pointers, and there is a local View3D, mark it for a + * check for emptiness. */ + view3d->localvd->runtime.flag |= V3D_RUNTIME_LOCAL_MAYBE_EMPTY; } BKE_viewer_path_id_remap(&view3d->viewer_path, mappings); } @@ -2045,6 +2067,13 @@ static void view3d_foreach_id(SpaceLink *space_link, LibraryForeachIDData *data) BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, v3d->ob_center, IDWALK_CB_DIRECT_WEAK_LINK); if (v3d->localvd) { BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, v3d->localvd->camera, IDWALK_CB_DIRECT_WEAK_LINK); + + /* If potentially modifying ID pointers, and there is a local View3D, mark it for a check for + * emptiness. */ + const int flags = BKE_lib_query_foreachid_process_flags_get(data); + if ((flags & IDWALK_READONLY) == 0) { + v3d->localvd->runtime.flag |= V3D_RUNTIME_LOCAL_MAYBE_EMPTY; + } } BKE_viewer_path_foreach_id(data, &v3d->viewer_path); } diff --git a/source/blender/editors/space_view3d/view3d_view.cc b/source/blender/editors/space_view3d/view3d_view.cc index 1eb58382367..cca7a8ef552 100644 --- a/source/blender/editors/space_view3d/view3d_view.cc +++ b/source/blender/editors/space_view3d/view3d_view.cc @@ -989,7 +989,7 @@ static void view3d_localview_exit(const Depsgraph *depsgraph, continue; } - if (frame_selected) { + if (frame_selected && depsgraph) { Object *camera_old_rv3d, *camera_new_rv3d; camera_old_rv3d = (rv3d->persp == RV3D_CAMOB) ? camera_old : nullptr; @@ -1018,6 +1018,34 @@ static void view3d_localview_exit(const Depsgraph *depsgraph, } } +bool ED_localview_exit_if_empty(const Depsgraph *depsgraph, + Scene *scene, + ViewLayer *view_layer, + wmWindowManager *wm, + wmWindow *win, + View3D *v3d, + ScrArea *area, + const bool frame_selected, + const int smooth_viewtx) +{ + if (v3d->localvd == nullptr) { + return false; + } + + v3d->localvd->runtime.flag &= ~V3D_RUNTIME_LOCAL_MAYBE_EMPTY; + + BKE_view_layer_synced_ensure(scene, view_layer); + LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) { + if (base->local_view_bits & v3d->local_view_uid) { + return false; + } + } + + view3d_localview_exit( + depsgraph, wm, win, scene, view_layer, area, frame_selected, smooth_viewtx); + return true; +} + static int localview_exec(bContext *C, wmOperator *op) { const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); @@ -1108,6 +1136,19 @@ static int localview_remove_from_exec(bContext *C, wmOperator *op) } } + /* If some object was removed from the local view, exit the local view if it is now empty. */ + if (changed) { + ED_localview_exit_if_empty(CTX_data_ensure_evaluated_depsgraph(C), + scene, + view_layer, + CTX_wm_manager(C), + CTX_wm_window(C), + v3d, + CTX_wm_area(C), + true, + WM_operator_smooth_viewtx_get(op)); + } + if (changed) { DEG_tag_on_visible_update(bmain, false); DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); diff --git a/source/blender/editors/util/ed_util.cc b/source/blender/editors/util/ed_util.cc index cd5dc783ba7..6e739c6a9b6 100644 --- a/source/blender/editors/util/ed_util.cc +++ b/source/blender/editors/util/ed_util.cc @@ -29,6 +29,7 @@ #include "BKE_object.hh" #include "BKE_packedFile.h" #include "BKE_paint.hh" +#include "BKE_scene.hh" #include "BKE_screen.hh" #include "BKE_undo_system.hh" @@ -47,6 +48,7 @@ #include "ED_sculpt.hh" #include "ED_space_api.hh" #include "ED_util.hh" +#include "ED_view3d.hh" #include "GPU_immediate.hh" @@ -71,6 +73,21 @@ void ED_editors_init_for_undo(Main *bmain) BKE_texpaint_slots_refresh_object(scene, ob); ED_paint_proj_mesh_data_check(*scene, *ob, nullptr, nullptr, nullptr, nullptr); } + + /* UI Updates. */ + /* Flag local View3D's to check and exit if they are empty. */ + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_VIEW3D) { + View3D *v3d = reinterpret_cast(sl); + if (v3d->localvd) { + v3d->localvd->runtime.flag |= V3D_RUNTIME_LOCAL_MAYBE_EMPTY; + } + } + } + } + } } } diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index ee6ff29d04b..f0a07ab8b80 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -389,6 +389,8 @@ enum { V3D_RUNTIME_XR_SESSION_ROOT = (1 << 0), /** Some operators override the depth buffer for dedicated occlusion operations. */ V3D_RUNTIME_DEPTHBUF_OVERRIDDEN = (1 << 1), + /** Local view may have become empty, and may need to be exited. */ + V3D_RUNTIME_LOCAL_MAYBE_EMPTY = (1 << 2), }; /** #RegionView3D::persp */ diff --git a/source/blender/windowmanager/intern/wm_files.cc b/source/blender/windowmanager/intern/wm_files.cc index f3611a3668e..a6bb3e19526 100644 --- a/source/blender/windowmanager/intern/wm_files.cc +++ b/source/blender/windowmanager/intern/wm_files.cc @@ -452,6 +452,21 @@ static void wm_file_read_setup_wm_finalize(bContext *C, /* Else just using the new WM read from file, nothing to do. */ BLI_assert(wm_setup_data->old_wm == nullptr); MEM_delete(wm_setup_data); + + /* UI Updates. */ + /* Flag local View3D's to check and exit if they are empty. */ + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_VIEW3D) { + View3D *v3d = reinterpret_cast(sl); + if (v3d->localvd) { + v3d->localvd->runtime.flag |= V3D_RUNTIME_LOCAL_MAYBE_EMPTY; + } + } + } + } + } } /** \} */