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
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<View3D *>(sl);
|
||||
if (v3d->localvd) {
|
||||
v3d->localvd->runtime.flag |= V3D_RUNTIME_LOCAL_MAYBE_EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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<View3D *>(sl);
|
||||
if (v3d->localvd) {
|
||||
v3d->localvd->runtime.flag |= V3D_RUNTIME_LOCAL_MAYBE_EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
Reference in New Issue
Block a user