commit b5eee2228867aea8af4a3f5b8c7be21f6db94687 Author: Campbell Barton Date: Sat Aug 6 19:31:47 2022 +1000 Experiment supporting undo for smooth view operations (apply on 8f915f0efba3da652ad06fa9fcfba7d6ca26e719). Resolve the issue by postponing the undo-push. Proof-of-concept change as this has problems when users manage to perform an undo-push while smooth-view is in progress. diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index c16e5ce5655..1cc2cdff5b3 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -1414,7 +1414,6 @@ static void direct_link_region(BlendDataReader *reader, ARegion *region, int spa rv3d->render_engine = NULL; rv3d->sms = NULL; - rv3d->smooth_timer = NULL; rv3d->rflag &= ~(RV3D_NAVIGATING | RV3D_PAINTING); rv3d->runtime_viewlock = 0; diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index a423a842019..38ae2134fbd 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -1025,7 +1025,6 @@ static void *view3d_main_region_duplicate(void *poin) new->render_engine = NULL; new->sms = NULL; - new->smooth_timer = NULL; return new; } diff --git a/source/blender/editors/space_view3d/view3d_navigate.c b/source/blender/editors/space_view3d/view3d_navigate.c index 88e004aac48..cd33682f216 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 = {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 = {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 = {NULL}, }); } } @@ -694,6 +700,7 @@ static void view3d_from_minmax(bContext *C, .camera_old = v3d->camera, .ofs = new_ofs, .dist = ok_dist ? &new_dist : NULL, + .undo = {.str = "View All"}, }); } else { @@ -704,6 +711,7 @@ static void view3d_from_minmax(bContext *C, &(const V3D_SmoothParams){ .ofs = new_ofs, .dist = ok_dist ? &new_dist : NULL, + .undo = {.str = "View All"}, }); } @@ -803,6 +811,12 @@ static int view3d_all_exec(bContext *C, wmOperator *op) ED_view3d_clipping_clamp_minmax(rv3d, min, max); } + const V3D_SmoothParams_Undo undo = { + .str = op->type->name, + .grouped = false, + }; + ED_view3d_smooth_view_undo_begin(C, v3d, &undo); + if (use_all_regions) { view3d_from_minmax_multi(C, v3d, min, max, true, smooth_viewtx); } @@ -810,6 +824,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, v3d); + return OPERATOR_FINISHED; } @@ -1020,8 +1036,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 +1096,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; @@ -1277,7 +1305,7 @@ static int view_camera_exec(bContext *C, wmOperator *op) if (rv3d->persp != RV3D_CAMOB) { Object *ob = OBACT(view_layer); - if (!rv3d->smooth_timer) { + if (!v3d->runtime.smooth_timer) { /* store settings of current view before allowing overwriting with camera view * only if we're not currently in a view transition */ @@ -1318,17 +1346,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 = {NULL}, + }); } else { /* return to settings of last view */ @@ -1475,6 +1506,8 @@ 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, .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..63afa96c437 100644 --- a/source/blender/editors/space_view3d/view3d_navigate.h +++ b/source/blender/editors/space_view3d/view3d_navigate.h @@ -220,6 +220,20 @@ void VIEW3D_OT_rotate(struct wmOperatorType *ot); /* view3d_navigate_smoothview.c */ +/** Optionally perform an undo push when the smooth-view operation is finished. */ +typedef struct V3D_SmoothParams_Undo { + /** + * When set, finishing the smooth-view operation may perform an undo-push + * (in the case it's transforming a camera which is locked to the view). + */ + const char *str; + /** + * When set, use grouped undo pushes. + * use for operations that are likely to run by holding a key or mouse-wheel. + */ + bool grouped; +} V3D_SmoothParams_Undo; + /** * Parameters for setting the new 3D Viewport state. * @@ -231,12 +245,14 @@ typedef struct V3D_SmoothParams { /** Alternate rotation center, when set `ofs` must be NULL. */ const float *dyn_ofs; + + V3D_SmoothParams_Undo undo; } V3D_SmoothParams; /** * The arguments are the desired situation. */ -void ED_view3d_smooth_view_ex(const struct Depsgraph *depsgraph, +bool ED_view3d_smooth_view_ex(const struct Depsgraph *depsgraph, struct wmWindowManager *wm, struct wmWindow *win, struct ScrArea *area, @@ -245,7 +261,7 @@ void ED_view3d_smooth_view_ex(const struct Depsgraph *depsgraph, int smooth_viewtx, const V3D_SmoothParams *sview); -void ED_view3d_smooth_view(struct bContext *C, +bool ED_view3d_smooth_view(struct bContext *C, struct View3D *v3d, struct ARegion *region, int smooth_viewtx, @@ -261,6 +277,11 @@ void ED_view3d_smooth_view_force_finish(struct bContext *C, void VIEW3D_OT_smoothview(struct wmOperatorType *ot); +void ED_view3d_smooth_view_undo_begin(struct bContext *C, + struct View3D *v3d, + const V3D_SmoothParams_Undo *undo); +void ED_view3d_smooth_view_undo_end(struct bContext *C, struct View3D *v3d); + /* view3d_navigate_walk.c */ void walk_modal_keymap(struct wmKeyConfig *keyconf); diff --git a/source/blender/editors/space_view3d/view3d_navigate_roll.c b/source/blender/editors/space_view3d/view3d_navigate_roll.c index 087ca72211e..c32a22bc2ea 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_roll.c +++ b/source/blender/editors/space_view3d/view3d_navigate_roll.c @@ -202,6 +202,8 @@ 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, .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..c2c8fc9f1da 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_linklist.h" #include "BLI_math.h" #include "BKE_context.h" @@ -21,6 +22,68 @@ #include "view3d_intern.h" #include "view3d_navigate.h" /* own include */ +/* -------------------------------------------------------------------- */ +/** \name Smooth View Undo + * \{ */ + +struct SmoothViewUndo { + V3D_SmoothParams_Undo undo; + LinkNode *region_undo; +}; + +static void xxx_destroy(View3D *v3d); + +static void xxx_create(View3D *v3d, const V3D_SmoothParams_Undo *undo) +{ + xxx_destroy(v3d); + struct SmoothViewUndo *x = MEM_callocN(sizeof(struct SmoothViewUndo), __func__); + x->undo = *undo; + v3d->runtime.smooth_undo = x; +} + +static void xxx_destroy(View3D *v3d) +{ + struct SmoothViewUndo *x = v3d->runtime.smooth_undo; + BLI_linklist_free(x->region_undo, NULL); + MEM_SAFE_FREE(v3d->runtime.smooth_undo); +} + +static void xxx_push_region(View3D *v3d, ARegion *region) +{ + BLI_assert(v3d->runtime.smooth_undo); + struct SmoothViewUndo *x = v3d->runtime.smooth_undo; + BLI_linklist_prepend(&x->region_undo, region); +} + +void ED_view3d_smooth_view_undo_begin(bContext *UNUSED(C), + View3D *v3d, + const V3D_SmoothParams_Undo *undo) +{ + xxx_create(v3d, undo); +} + +void ED_view3d_smooth_view_undo_end(bContext *C, View3D *v3d) +{ + BLI_assert(v3d->runtime.smooth_undo); + + struct SmoothViewUndo *x = v3d->runtime.smooth_undo; + for (LinkNode *node = x->region_undo; node; node = node->next) { + ARegion *region = node->link; + RegionView3D *rv3d = region->regiondata; + const bool undo_pushed = x->undo.grouped ? + ED_view3d_camera_lock_undo_grouped_push( + x->undo.str, v3d, rv3d, C) : + ED_view3d_camera_lock_undo_push(x->undo.str, v3d, rv3d, C); + + if (undo_pushed) { + break; + } + } + xxx_destroy(v3d); +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Smooth View Operator & Utilities * @@ -75,7 +138,7 @@ static void view3d_smooth_view_state_restore(const struct SmoothView3DState *sms } /* will start timer if appropriate */ -void ED_view3d_smooth_view_ex( +bool ED_view3d_smooth_view_ex( /* avoid passing in the context */ const Depsgraph *depsgraph, wmWindowManager *wm, @@ -157,9 +220,11 @@ void ED_view3d_smooth_view_ex( equals_v4v4(sms.dst.quat, rv3d->viewquat) /* Rotation. */ ) { /* Early return if nothing changed. */ - return; + return true; } + bool finished = false; + /* Skip smooth viewing for external render engine draw. */ if (smooth_viewtx && !(v3d->shading.type == OB_RENDER && rv3d->render_engine)) { @@ -211,11 +276,12 @@ void ED_view3d_smooth_view_ex( rv3d->sms = MEM_mallocN(sizeof(struct SmoothView3DStore), "smoothview v3d"); } *rv3d->sms = sms; - if (rv3d->smooth_timer) { - WM_event_remove_timer(wm, win, rv3d->smooth_timer); + + if (v3d->runtime.smooth_timer) { + WM_event_remove_timer(wm, win, v3d->runtime.smooth_timer); } /* #TIMER1 is hard-coded in key-map. */ - rv3d->smooth_timer = WM_event_add_timer(wm, win, TIMER1, 1.0 / 100.0); + v3d->runtime.smooth_timer = WM_event_add_timer(wm, win, TIMER1, 1.0 / 100.0); } else { /* Animation is disabled, apply immediately. */ @@ -235,10 +301,13 @@ void ED_view3d_smooth_view_ex( ED_region_tag_redraw(region); WM_event_add_mousemove(win); + finished = true; } + + return finished; } -void ED_view3d_smooth_view(bContext *C, +bool ED_view3d_smooth_view(bContext *C, View3D *v3d, ARegion *region, const int smooth_viewtx, @@ -248,8 +317,52 @@ void ED_view3d_smooth_view(bContext *C, wmWindowManager *wm = CTX_wm_manager(C); wmWindow *win = CTX_wm_window(C); ScrArea *area = CTX_wm_area(C); + /* Make a copy that doesn't use undo since #ED_view3d_smooth_view_ex doesn't support undo + * and will assert if this is passed in. */ + struct V3D_SmoothParams sview_copy = *sview; + bool handle_undo = (!v3d->runtime.smooth_undo && sview->undo.str); + if (handle_undo) { + ED_view3d_smooth_view_undo_begin(C, v3d, &sview->undo); + } + sview_copy.undo.str = NULL; + + const bool finished = ED_view3d_smooth_view_ex( + depsgraph, wm, win, area, v3d, region, smooth_viewtx, &sview_copy); + + if (handle_undo) { + ED_view3d_smooth_view_undo_end(C, v3d); + } + return finished; +} + +/** Remove `View3D.runtime.smooth_timer` unless another region is using it. */ +static void view3d_smoothview_clear_unused_timer(wmWindowManager *wm, + wmWindow *win, + View3D *v3d, + ARegion *region) +{ + if (!v3d->runtime.smooth_timer) { + return; + } - ED_view3d_smooth_view_ex(depsgraph, wm, win, area, v3d, region, smooth_viewtx, sview); + /* Passing in the area would be more convenient, but the caller doesn't have access to it. */ + while (region->prev) { + region = region->prev; + } + for (; region; region = region->next) { + if (region->type != RGN_TYPE_WINDOW) { + continue; + } + const RegionView3D *rv3d = region->regiondata; + if (rv3d->sms) { + break; + } + } + if (!region) { + return; /* A region has smooth-view data, exit. */ + } + + WM_event_remove_timer(wm, win, v3d->runtime.smooth_timer); } /* only meant for timer usage */ @@ -261,7 +374,7 @@ static void view3d_smoothview_apply(bContext *C, View3D *v3d, ARegion *region, b float step, step_inv; if (sms->time_allowed != 0.0) { - step = (float)((rv3d->smooth_timer->duration) / sms->time_allowed); + step = (float)((v3d->runtime.smooth_timer->duration) / sms->time_allowed); } else { step = 1.0f; @@ -283,6 +396,9 @@ static void view3d_smoothview_apply(bContext *C, View3D *v3d, ARegion *region, b ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d); ED_view3d_camera_lock_autokey(v3d, rv3d, C, true, true); + if (v3d->runtime.smooth_undo) { + xxx_push_region(v3d, region); + } } if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0) { @@ -292,8 +408,7 @@ static void view3d_smoothview_apply(bContext *C, View3D *v3d, ARegion *region, b MEM_freeN(rv3d->sms); rv3d->sms = NULL; - WM_event_remove_timer(wm, win, rv3d->smooth_timer); - rv3d->smooth_timer = NULL; + view3d_smoothview_clear_unused_timer(wm, win, v3d, region); rv3d->rflag &= ~RV3D_NAVIGATING; /* Event handling won't know if a UI item has been moved under the pointer. */ @@ -342,36 +457,65 @@ static void view3d_smoothview_apply(bContext *C, View3D *v3d, ARegion *region, b } } +static void view3d_smoothview_apply_all_regions_with_undo( + bContext *C, View3D *v3d, ARegion *region, bool sync_boxview, bool force) +{ + bool handle_undo = v3d->runtime.smooth_undo == NULL; + if (handle_undo) { + const V3D_SmoothParams_Undo undo = {.str = "Smooth View", .grouped = false}; + ED_view3d_smooth_view_undo_begin(C, v3d, &undo); + } + + /* Passing in the area would be more convenient, but the caller doesn't have access to it. */ + while (region->prev) { + region = region->prev; + } + for (; region; region = region->next) { + if (region->regiontype != RGN_TYPE_WINDOW) { + continue; + } + const RegionView3D *rv3d = region->regiondata; + if (!rv3d->sms) { + continue; + } + if (force) { + rv3d->sms->time_allowed = 0.0; /* force finishing */ + } + + view3d_smoothview_apply(C, v3d, region, sync_boxview); + + /* Force update of view matrix so tools that run immediately after + * can use them without redrawing first. */ + if (force) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Scene *scene = CTX_data_scene(C); + ED_view3d_update_viewmat(depsgraph, scene, v3d, region, NULL, NULL, NULL, false); + } + } + + if (handle_undo) { + ED_view3d_smooth_view_undo_end(C, v3d); + } +} + static int view3d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) { - View3D *v3d = CTX_wm_view3d(C); - ARegion *region = CTX_wm_region(C); - RegionView3D *rv3d = region->regiondata; + ScrArea *area = CTX_wm_area(C); + View3D *v3d = area->spacedata.first; /* escape if not our timer */ - if (rv3d->smooth_timer == NULL || rv3d->smooth_timer != event->customdata) { + if (v3d->runtime.smooth_timer == NULL || v3d->runtime.smooth_timer != event->customdata) { return OPERATOR_PASS_THROUGH; } - view3d_smoothview_apply(C, v3d, region, true); + view3d_smoothview_apply_all_regions_with_undo(C, v3d, area->regionbase.first, true, false); return OPERATOR_FINISHED; } void ED_view3d_smooth_view_force_finish(bContext *C, View3D *v3d, ARegion *region) { - RegionView3D *rv3d = region->regiondata; - - if (rv3d && rv3d->sms) { - rv3d->sms->time_allowed = 0.0; /* force finishing */ - view3d_smoothview_apply(C, v3d, region, false); - - /* force update of view matrix so tools that run immediately after - * can use them without redrawing first */ - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Scene *scene = CTX_data_scene(C); - ED_view3d_update_viewmat(depsgraph, scene, v3d, region, NULL, NULL, NULL, false); - } + view3d_smoothview_apply_all_regions_with_undo(C, v3d, region, false, true); } void VIEW3D_OT_smoothview(wmOperatorType *ot) 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..616f1cd9c37 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_view.c b/source/blender/editors/space_view3d/view3d_view.c index fc88737ca70..8f28699f94f 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 = {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 = {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 = {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 = {NULL}, }); } diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index 8554d070dc3..8d232d69b68 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -61,7 +61,6 @@ typedef struct RegionView3D { /** Animated smooth view. */ struct SmoothView3DStore *sms; - struct wmTimer *smooth_timer; /** Transform gizmo matrix. */ float twmat[4][4]; @@ -250,6 +249,10 @@ typedef struct View3D_Runtime { char _pad1[4]; /* Only used for overlay stats while in local-view. */ struct SceneStats *local_stats; + + /** Animated smooth view timer. */ + struct wmTimer *smooth_timer; + void *smooth_undo; } View3D_Runtime; /** 3D ViewPort Struct. */