commit 7d3ab95d018a1ad974264ff076e3b649bd56a3b6 Author: Campbell Barton Date: Mon Aug 8 11:55:08 2022 +1000 Experiment supporting undo for smooth view operations (apply on 8f915f0efba3da652ad06fa9fcfba7d6ca26e719). Resolve the issue by disabling smooth-view when an undo step needs to be pushed. diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index c16e5ce5655..b2132315ceb 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -1416,7 +1416,8 @@ static void direct_link_region(BlendDataReader *reader, ARegion *region, int spa rv3d->sms = NULL; rv3d->smooth_timer = NULL; - rv3d->rflag &= ~(RV3D_NAVIGATING | RV3D_PAINTING); + rv3d->rflag &= ~(RV3D_NAVIGATING | RV3D_PAINTING | + RV3D_SMOOTH_VIEW_UNHANDLED_CAMERA_LOCKED); rv3d->runtime_viewlock = 0; } } diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 931bb7be8bf..7d31950c869 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -1196,6 +1196,14 @@ bool ED_view3d_camera_lock_autokey(struct View3D *v3d, void ED_view3d_lock_clear(struct View3D *v3d); +/** + * Check if creating an undo step should be performed if the viewport moves. + * \return true if #ED_view3d_camera_lock_undo_push would do an undo push. + */ +bool ED_view3d_camera_lock_undo_test(const View3D *v3d, + const RegionView3D *rv3d, + struct bContext *C); + /** * Create an undo step when the camera is locked to the view. * \param str: The name of the undo step (typically #wmOperatorType.name should be used). diff --git a/source/blender/editors/space_view3d/view3d_navigate.c b/source/blender/editors/space_view3d/view3d_navigate.c index 88e004aac48..5b3a7483ac1 100644 --- a/source/blender/editors/space_view3d/view3d_navigate.c +++ b/source/blender/editors/space_view3d/view3d_navigate.c @@ -495,6 +495,8 @@ static void axis_set_view(bContext *C, .camera_old = v3d->camera, .ofs = rv3d->ofs, .quat = quat, + /* No undo because this switches to/from camera. */ + .undo_str = NULL, }); } else if (orig_persp == RV3D_CAMOB && v3d->camera) { @@ -518,6 +520,8 @@ static void axis_set_view(bContext *C, .ofs = ofs, .quat = quat, .dist = &dist, + /* No undo because this switches to/from camera. */ + .undo_str = NULL, }); } else { @@ -540,6 +544,8 @@ static void axis_set_view(bContext *C, &(const V3D_SmoothParams){ .quat = quat, .dyn_ofs = dyn_ofs_pt, + /* No undo because this isn't a camera view. */ + .undo_str = NULL, }); } } @@ -694,6 +700,8 @@ static void view3d_from_minmax(bContext *C, .camera_old = v3d->camera, .ofs = new_ofs, .dist = ok_dist ? &new_dist : NULL, + /* The caller needs to use undo begin/end calls. */ + .undo_str = NULL, }); } else { @@ -704,6 +712,8 @@ static void view3d_from_minmax(bContext *C, &(const V3D_SmoothParams){ .ofs = new_ofs, .dist = ok_dist ? &new_dist : NULL, + /* The caller needs to use undo begin/end calls. */ + .undo_str = NULL, }); } @@ -736,6 +746,7 @@ static void view3d_from_minmax_multi(bContext *C, static int view3d_all_exec(bContext *C, wmOperator *op) { + ScrArea *area = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); View3D *v3d = CTX_wm_view3d(C); RegionView3D *rv3d = CTX_wm_region_view3d(C); @@ -802,6 +813,7 @@ static int view3d_all_exec(bContext *C, wmOperator *op) /* This is an approximation, see function documentation for details. */ ED_view3d_clipping_clamp_minmax(rv3d, min, max); } + ED_view3d_smooth_view_undo_begin(C, area); if (use_all_regions) { view3d_from_minmax_multi(C, v3d, min, max, true, smooth_viewtx); @@ -810,6 +822,8 @@ static int view3d_all_exec(bContext *C, wmOperator *op) view3d_from_minmax(C, v3d, region, min, max, true, smooth_viewtx); } + ED_view3d_smooth_view_undo_end(C, area, op->type->name, false); + return OPERATOR_FINISHED; } @@ -842,6 +856,7 @@ void VIEW3D_OT_view_all(wmOperatorType *ot) static int viewselected_exec(bContext *C, wmOperator *op) { + ScrArea *area = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); View3D *v3d = CTX_wm_view3d(C); RegionView3D *rv3d = CTX_wm_region_view3d(C); @@ -971,6 +986,8 @@ static int viewselected_exec(bContext *C, wmOperator *op) ED_view3d_clipping_clamp_minmax(rv3d, min, max); } + ED_view3d_smooth_view_undo_begin(C, area); + if (use_all_regions) { view3d_from_minmax_multi(C, v3d, min, max, ok_dist, smooth_viewtx); } @@ -978,6 +995,8 @@ static int viewselected_exec(bContext *C, wmOperator *op) view3d_from_minmax(C, v3d, region, min, max, ok_dist, smooth_viewtx); } + ED_view3d_smooth_view_undo_end(C, area, op->type->name, false); + return OPERATOR_FINISHED; } @@ -1020,8 +1039,14 @@ static int viewcenter_cursor_exec(bContext *C, wmOperator *op) /* non camera center */ float new_ofs[3]; negate_v3_v3(new_ofs, scene->cursor.location); - ED_view3d_smooth_view( - C, v3d, region, smooth_viewtx, &(const V3D_SmoothParams){.ofs = new_ofs}); + ED_view3d_smooth_view(C, + v3d, + region, + smooth_viewtx, + &(const V3D_SmoothParams){ + .ofs = new_ofs, + .undo_str = op->type->name, + }); /* Smooth view does view-lock #RV3D_BOXVIEW copy. */ } @@ -1074,8 +1099,14 @@ static int viewcenter_pick_invoke(bContext *C, wmOperator *op, const wmEvent *ev ED_view3d_win_to_3d_int(v3d, region, new_ofs, event->mval, new_ofs); } negate_v3(new_ofs); - ED_view3d_smooth_view( - C, v3d, region, smooth_viewtx, &(const V3D_SmoothParams){.ofs = new_ofs}); + ED_view3d_smooth_view(C, + v3d, + region, + smooth_viewtx, + &(const V3D_SmoothParams){ + .ofs = new_ofs, + .undo_str = op->type->name, + }); } return OPERATOR_FINISHED; @@ -1318,17 +1349,20 @@ static int view_camera_exec(bContext *C, wmOperator *op) /* finally do snazzy view zooming */ rv3d->persp = RV3D_CAMOB; - ED_view3d_smooth_view(C, - v3d, - region, - smooth_viewtx, - &(const V3D_SmoothParams){ - .camera = v3d->camera, - .ofs = rv3d->ofs, - .quat = rv3d->viewquat, - .dist = &rv3d->dist, - .lens = &v3d->lens, - }); + ED_view3d_smooth_view( + C, + v3d, + region, + smooth_viewtx, + &(const V3D_SmoothParams){ + .camera = v3d->camera, + .ofs = rv3d->ofs, + .quat = rv3d->viewquat, + .dist = &rv3d->dist, + .lens = &v3d->lens, + /* No undo because this changes cameras (and wont move the camera). */ + .undo_str = NULL, + }); } else { /* return to settings of last view */ @@ -1475,6 +1509,9 @@ static int vieworbit_exec(bContext *C, wmOperator *op) &(const V3D_SmoothParams){ .quat = quat_new, .dyn_ofs = dyn_ofs_pt, + /* Group as successive orbit may run by holding a key. */ + .undo_str = op->type->name, + .undo_grouped = true, }); return OPERATOR_FINISHED; diff --git a/source/blender/editors/space_view3d/view3d_navigate.h b/source/blender/editors/space_view3d/view3d_navigate.h index fc7bc11295a..721476ace57 100644 --- a/source/blender/editors/space_view3d/view3d_navigate.h +++ b/source/blender/editors/space_view3d/view3d_navigate.h @@ -231,6 +231,14 @@ typedef struct V3D_SmoothParams { /** Alternate rotation center, when set `ofs` must be NULL. */ const float *dyn_ofs; + + /** When non-NULL, perform undo pushes when transforming the camera. */ + const char *undo_str; + /** + * When true use grouped undo pushes, use for incremental viewport manipulation + * which are likely to be activated by holding a key or from the mouse-wheel. + */ + bool undo_grouped; } V3D_SmoothParams; /** @@ -251,6 +259,22 @@ void ED_view3d_smooth_view(struct bContext *C, int smooth_viewtx, const V3D_SmoothParams *sview); +/** + * Call before multiple smooth-view operations begin to properly handle undo. + * + * \note Only use explicit undo calls when multiple calls to smooth-view are necessary + * or when calling #ED_view3d_smooth_view_ex. + * Otherwise pass in #V3D_SmoothParams.undo_str so an undo step is pushed as needed. + */ +void ED_view3d_smooth_view_undo_begin(struct bContext *C, struct ScrArea *area); +/** + * Run after multiple smooth-view operations have run to push undo as needed. + */ +void ED_view3d_smooth_view_undo_end(struct bContext *C, + struct ScrArea *area, + const char *undo_str, + bool undo_grouped); + /** * Apply the smooth-view immediately, use when we need to start a new view operation. * (so we don't end up half-applying a view operation when pressing keys quickly). diff --git a/source/blender/editors/space_view3d/view3d_navigate_roll.c b/source/blender/editors/space_view3d/view3d_navigate_roll.c index 087ca72211e..3c15fdf3b64 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_roll.c +++ b/source/blender/editors/space_view3d/view3d_navigate_roll.c @@ -202,6 +202,9 @@ static int viewroll_exec(bContext *C, wmOperator *op) &(const V3D_SmoothParams){ .quat = quat_new, .dyn_ofs = dyn_ofs_pt, + /* Group as successive roll may run by holding a key. */ + .undo_str = op->type->name, + .undo_grouped = true, }); viewops_data_free(C, op->customdata); diff --git a/source/blender/editors/space_view3d/view3d_navigate_smoothview.c b/source/blender/editors/space_view3d/view3d_navigate_smoothview.c index 48af126d8a9..cb9fa40cd4e 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_smoothview.c +++ b/source/blender/editors/space_view3d/view3d_navigate_smoothview.c @@ -8,6 +8,7 @@ #include "MEM_guardedalloc.h" +#include "BLI_listbase.h" #include "BLI_math.h" #include "BKE_context.h" @@ -17,10 +18,97 @@ #include "WM_api.h" #include "ED_screen.h" +#include "ED_undo.h" #include "view3d_intern.h" #include "view3d_navigate.h" /* own include */ +/* -------------------------------------------------------------------- */ +/** \name Smooth View Undo Handling + * + * When the camera is locked to the viewport smooth-view operations + * may need to perform an undo push. + * + * In this case smooth-view is ignored and the undo-push is done immediately. + * + * NOTE(@campbellbarton): While it is possible to allow the animation to play and defer the + * undo-push, this becomes unreasonably complex, as it's (technically) possible to have multiple + * areas at once moving different cameras. + * Another complication is the possibility of an operator running which does an undo push + * before smooth-view is complete, causing undo to be out-of-order. + * Even though these corner cases could be checked & accounted for they add quite some complexity + * to what is already a corner case. So the simplest solution is to immediately apply smooth + * view and perform an undo push. + * \{ */ + +void ED_view3d_smooth_view_undo_begin(bContext *C, ScrArea *area) +{ + const View3D *v3d = area->spacedata.first; + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { + if (region->regiontype != RGN_TYPE_WINDOW) { + continue; + } + RegionView3D *rv3d = region->regiondata; + const bool do_undo = ED_view3d_camera_lock_undo_test(v3d, rv3d, C); + SET_FLAG_FROM_TEST(rv3d->rflag, do_undo, RV3D_SMOOTH_VIEW_UNHANDLED_CAMERA_LOCKED); + } +} + +void ED_view3d_smooth_view_undo_end(bContext *C, + ScrArea *area, + const char *undo_str, + const bool undo_grouped) +{ + View3D *v3d = area->spacedata.first; + /* An undo push should be performed. */ + bool do_undo = false; + /* When set this is an animated smooth-view (not applied immediately). */ + bool is_interactive = false; + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { + if (region->regiontype != RGN_TYPE_WINDOW) { + continue; + } + RegionView3D *rv3d = region->regiondata; + if ((rv3d->rflag & RV3D_SMOOTH_VIEW_UNHANDLED_CAMERA_LOCKED) == 0) { + if (do_undo == false && ED_view3d_camera_lock_undo_test(v3d, rv3d, C)) { + do_undo = true; + } + if (rv3d->sms) { + is_interactive = true; + } + } + rv3d->rflag &= ~RV3D_SMOOTH_VIEW_UNHANDLED_CAMERA_LOCKED; + } + + if (!do_undo) { + return; + } + + if (is_interactive) { + const wmWindowManager *wm = CTX_wm_manager(C); + if (ED_screen_animation_playing(wm)) { + /* Exception, support recording animation with a locked camera. */ + return; + } + } + + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { + if (region->regiontype != RGN_TYPE_WINDOW) { + continue; + } + ED_view3d_smooth_view_force_finish(C, v3d, region); + } + + if (undo_grouped) { + ED_undo_grouped_push(C, undo_str); + } + else { + ED_undo_push(C, undo_str); + } +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Smooth View Operator & Utilities * @@ -86,6 +174,11 @@ void ED_view3d_smooth_view_ex( const int smooth_viewtx, const V3D_SmoothParams *sview) { + /* In this case use #ED_view3d_smooth_view_undo_begin & end functions + * instead of passing in undo. */ + BLI_assert_msg(sview->undo_str == NULL, + "Only the 'ED_view3d_smooth_view' version of this function handles undo!"); + RegionView3D *rv3d = region->regiondata; struct SmoothView3DStore sms = {{0}}; @@ -236,6 +329,10 @@ void ED_view3d_smooth_view_ex( WM_event_add_mousemove(win); } + + if (sms.to_camera == false) { + rv3d->rflag &= ~RV3D_SMOOTH_VIEW_UNHANDLED_CAMERA_LOCKED; + } } void ED_view3d_smooth_view(bContext *C, @@ -249,7 +346,21 @@ void ED_view3d_smooth_view(bContext *C, wmWindow *win = CTX_wm_window(C); ScrArea *area = CTX_wm_area(C); - ED_view3d_smooth_view_ex(depsgraph, wm, win, area, v3d, region, smooth_viewtx, sview); + /* #ED_view3d_smooth_view_ex asserts this is not set as it doesn't support undo. */ + struct V3D_SmoothParams sview_no_undo = *sview; + sview_no_undo.undo_str = NULL; + sview_no_undo.undo_grouped = false; + + const bool do_undo = (sview->undo_str != NULL); + if (do_undo) { + ED_view3d_smooth_view_undo_begin(C, area); + } + + ED_view3d_smooth_view_ex(depsgraph, wm, win, area, v3d, region, smooth_viewtx, &sview_no_undo); + + if (do_undo) { + ED_view3d_smooth_view_undo_end(C, area, sview->undo_str, sview->undo_grouped); + } } /* only meant for timer usage */ diff --git a/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c b/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c index f834efe4a7b..eaabee9e891 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c +++ b/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c @@ -173,6 +173,7 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op) &(const V3D_SmoothParams){ .ofs = new_ofs, .dist = &new_dist, + .undo_str = op->type->name, }); if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) { diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c index 99f8cbc975b..0d88824a784 100644 --- a/source/blender/editors/space_view3d/view3d_utils.c +++ b/source/blender/editors/space_view3d/view3d_utils.c @@ -689,6 +689,18 @@ bool ED_view3d_camera_lock_autokey(View3D *v3d, return false; } +bool ED_view3d_camera_lock_undo_test(const View3D *v3d, + const RegionView3D *rv3d, + struct bContext *C) +{ + if (ED_view3d_camera_lock_check(v3d, rv3d)) { + if (ED_undo_is_memfile_compatible(C)) { + return true; + } + } + return false; +} + /** * Create a MEMFILE undo-step for locked camera movement when transforming the view. * Edit and texture paint mode don't use MEMFILE undo so undo push is skipped for them. @@ -699,16 +711,14 @@ bool ED_view3d_camera_lock_autokey(View3D *v3d, static bool view3d_camera_lock_undo_ex( const char *str, View3D *v3d, RegionView3D *rv3d, struct bContext *C, bool undo_group) { - if (ED_view3d_camera_lock_check(v3d, rv3d)) { - if (ED_undo_is_memfile_compatible(C)) { - if (undo_group) { - ED_undo_grouped_push(C, str); - } - else { - ED_undo_push(C, str); - } - return true; + if (ED_view3d_camera_lock_undo_test(v3d, rv3d, C)) { + if (undo_group) { + ED_undo_grouped_push(C, str); + } + else { + ED_undo_push(C, str); } + return true; } return false; } diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index fc88737ca70..b8042a9f215 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -202,6 +202,8 @@ static void sync_viewport_camera_smoothview(bContext *C, .quat = other_rv3d->viewquat, .dist = &other_rv3d->dist, .lens = &other_v3d->lens, + /* No undo because this switches cameras. */ + .undo_str = NULL, }); } else { @@ -256,6 +258,8 @@ static int view3d_setobjectascamera_exec(bContext *C, wmOperator *op) .quat = rv3d->viewquat, .dist = &rv3d->dist, .lens = &v3d->lens, + /* No undo because this switches cameras. */ + .undo_str = NULL, }); } @@ -939,6 +943,8 @@ static bool view3d_localview_init(const Depsgraph *depsgraph, .quat = rv3d->viewquat, .dist = ok_dist ? &dist_new : NULL, .lens = &v3d->lens, + /* No undo because this doesn't move the camera. */ + .undo_str = NULL, }); } } @@ -1008,6 +1014,8 @@ static void view3d_localview_exit(const Depsgraph *depsgraph, .ofs = rv3d->localvd->ofs, .quat = rv3d->localvd->viewquat, .dist = &rv3d->localvd->dist, + /* No undo because this doesn't move the camera. */ + .undo_str = NULL, }); } diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index 8554d070dc3..94ef849be38 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -387,12 +387,18 @@ enum { #define RV3D_NAVIGATING (1 << 3) #define RV3D_GPULIGHT_UPDATE (1 << 4) #define RV3D_PAINTING (1 << 5) -/*#define RV3D_IS_GAME_ENGINE (1 << 5) */ /* UNUSED */ /** * Disable zbuffer offset, skip calls to #ED_view3d_polygon_offset. * Use when precise surface depth is needed and picking bias isn't, see T45434). */ -#define RV3D_ZOFFSET_DISABLED 64 +#define RV3D_ZOFFSET_DISABLED (1 << 6) +/** + * Use to tag the view as being manipulated by smooth-view. + * \note When cleared, it's known that smooth-view changed this #RegionView3D. + * Track the "unhandled" state so the value is always cleared, so the caller can optionally set it. + * See #ED_view3d_smooth_view_undo_begin / #ED_view3d_smooth_view_undo_end for details. + */ +#define RV3D_SMOOTH_VIEW_UNHANDLED_CAMERA_LOCKED (1 << 7) /** #RegionView3D.viewlock */ enum {