From 651b8fb14eb6ee5cbfa98bffe80a966a0753b14e Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 19 Mar 2018 14:17:59 +0100 Subject: Undo: unified undo system w/ linear history - Use a single undo history for all operations. - UndoType's are registered and poll the context to check if they should be used when performing an undo push. - Mode switching is used to ensure the state is correct before undo data is restored. - Some undo types accumulate changes (image & text editing) others store the state multiple times (with de-duplication). This is supported by checking UndoStack.mode `ACCUMULATE` / `STORE`. - Each undo step stores ID datablocks they use with utilities to help manage restoring correct ID's. Needed since global undo is now mixed with other modes undo. - Currently performs each undo step when going up/down history Previously this wasn't done, making history fail in some cases. This can be optimized to skip some combinations of undo steps. grease-pencil is an exception which has not been updated since it integrates undo into the draw-session. See D3113 --- source/blender/editors/sculpt_paint/CMakeLists.txt | 1 - source/blender/editors/sculpt_paint/paint_curve.c | 22 +- .../editors/sculpt_paint/paint_curve_undo.c | 162 +++++--- source/blender/editors/sculpt_paint/paint_image.c | 15 +- .../blender/editors/sculpt_paint/paint_image_2d.c | 6 +- .../editors/sculpt_paint/paint_image_proj.c | 5 +- .../editors/sculpt_paint/paint_image_undo.c | 161 ++++++-- source/blender/editors/sculpt_paint/paint_intern.h | 10 +- source/blender/editors/sculpt_paint/paint_undo.c | 410 --------------------- .../blender/editors/sculpt_paint/sculpt_intern.h | 2 + source/blender/editors/sculpt_paint/sculpt_undo.c | 171 +++++++-- 11 files changed, 418 insertions(+), 547 deletions(-) delete mode 100644 source/blender/editors/sculpt_paint/paint_undo.c (limited to 'source/blender/editors/sculpt_paint') diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index adead9a8b9e..9527dc4fe83 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -51,7 +51,6 @@ set(SRC paint_mask.c paint_ops.c paint_stroke.c - paint_undo.c paint_utils.c paint_vertex.c paint_vertex_color_ops.c diff --git a/source/blender/editors/sculpt_paint/paint_curve.c b/source/blender/editors/sculpt_paint/paint_curve.c index 8d9812f41d9..120514762f4 100644 --- a/source/blender/editors/sculpt_paint/paint_curve.c +++ b/source/blender/editors/sculpt_paint/paint_curve.c @@ -40,6 +40,7 @@ #include "BKE_paint.h" #include "ED_view3d.h" +#include "ED_paint.h" #include "WM_api.h" #include "WM_types.h" @@ -203,7 +204,7 @@ static void paintcurve_point_add(bContext *C, wmOperator *op, const int loc[2]) br->paint_curve = pc = BKE_paint_curve_add(bmain, "PaintCurve"); } - ED_paintcurve_undo_push(C, op, pc); + ED_paintcurve_undo_push_begin(op->type->name); pcp = MEM_mallocN((pc->tot_points + 1) * sizeof(PaintCurvePoint), "PaintCurvePoint"); add_index = pc->add_index; @@ -241,6 +242,8 @@ static void paintcurve_point_add(bContext *C, wmOperator *op, const int loc[2]) pcp[add_index].bez.h1 = HD_ALIGN; } + ED_paintcurve_undo_push_end(); + WM_paint_cursor_tag_redraw(window, ar); } @@ -302,7 +305,7 @@ static int paintcurve_delete_point_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - ED_paintcurve_undo_push(C, op, pc); + ED_paintcurve_undo_push_begin(op->type->name); #define DELETE_TAG 2 @@ -342,6 +345,8 @@ static int paintcurve_delete_point_exec(bContext *C, wmOperator *op) #undef DELETE_TAG + ED_paintcurve_undo_push_end(); + WM_paint_cursor_tag_redraw(window, ar); return OPERATOR_FINISHED; @@ -379,7 +384,7 @@ static bool paintcurve_point_select(bContext *C, wmOperator *op, const int loc[2 if (!pc) return false; - ED_paintcurve_undo_push(C, op, pc); + ED_paintcurve_undo_push_begin(op->type->name); if (toggle) { PaintCurvePoint *pcp; @@ -444,10 +449,14 @@ static bool paintcurve_point_select(bContext *C, wmOperator *op, const int loc[2 } } - if (!pcp) + if (!pcp) { + ED_paintcurve_undo_push_end(); return false; + } } + ED_paintcurve_undo_push_end(); + WM_paint_cursor_tag_redraw(window, ar); return true; @@ -562,9 +571,6 @@ static int paintcurve_slide_invoke(bContext *C, wmOperator *op, const wmEvent *e psd->align = align; op->customdata = psd; - if (do_select) - ED_paintcurve_undo_push(C, op, pc); - /* first, clear all selection from points */ for (i = 0; i < pc->tot_points; i++) pc->points[i].bez.f1 = pc->points[i].bez.f3 = pc->points[i].bez.f2 = 0; @@ -587,6 +593,8 @@ static int paintcurve_slide_modal(bContext *C, wmOperator *op, const wmEvent *ev if (event->type == psd->event && event->val == KM_RELEASE) { MEM_freeN(psd); + ED_paintcurve_undo_push_begin(op->type->name); + ED_paintcurve_undo_push_end(); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/sculpt_paint/paint_curve_undo.c b/source/blender/editors/sculpt_paint/paint_curve_undo.c index 70f92999864..d5b7496fa3e 100644 --- a/source/blender/editors/sculpt_paint/paint_curve_undo.c +++ b/source/blender/editors/sculpt_paint/paint_curve_undo.c @@ -30,9 +30,13 @@ #include "DNA_space_types.h" #include "BLI_string.h" +#include "BLI_array_utils.h" #include "BKE_context.h" #include "BKE_paint.h" +#include "BKE_global.h" +#include "BKE_main.h" +#include "BKE_undo_system.h" #include "ED_paint.h" @@ -41,89 +45,125 @@ #include "paint_intern.h" -typedef struct UndoCurve { - struct UndoImageTile *next, *prev; +/* -------------------------------------------------------------------- */ +/** \name Undo Conversion + * \{ */ +typedef struct UndoCurve { PaintCurvePoint *points; /* points of curve */ int tot_points; - int active_point; - - char idname[MAX_ID_NAME]; /* name instead of pointer*/ + int add_index; } UndoCurve; -static void paintcurve_undo_restore(bContext *C, ListBase *lb) +static void undocurve_from_paintcurve(UndoCurve *uc, const PaintCurve *pc) { - Paint *p = BKE_paint_get_active_from_context(C); - UndoCurve *uc; - PaintCurve *pc = NULL; + BLI_assert(BLI_array_is_zeroed(uc, 1)); + uc->points = MEM_dupallocN(pc->points); + uc->tot_points = pc->tot_points; + uc->add_index = pc->add_index; +} - if (p->brush) { - pc = p->brush->paint_curve; - } +static void undocurve_to_paintcurve(const UndoCurve *uc, PaintCurve *pc) +{ + MEM_SAFE_FREE(pc->points); + pc->points = MEM_dupallocN(uc->points); + pc->tot_points = uc->tot_points; + pc->add_index = uc->add_index; +} - if (!pc) { - return; - } +static void undocurve_free_data(UndoCurve *uc) +{ + MEM_SAFE_FREE(uc->points); +} - uc = (UndoCurve *)lb->first; +/** \} */ - if (STREQLEN(uc->idname, pc->id.name, BLI_strnlen(uc->idname, sizeof(uc->idname)))) { - SWAP(PaintCurvePoint *, pc->points, uc->points); - SWAP(int, pc->tot_points, uc->tot_points); - SWAP(int, pc->add_index, uc->active_point); - } -} +/* -------------------------------------------------------------------- */ +/** \name Implements ED Undo System + * \{ */ -static void paintcurve_undo_delete(ListBase *lb) +typedef struct PaintCurveUndoStep { + UndoStep step; + PaintCurve *pc; + UndoCurve data; +} PaintCurveUndoStep; + +static bool paintcurve_undosys_poll(bContext *C) { - UndoCurve *uc; - uc = (UndoCurve *)lb->first; + Paint *p = BKE_paint_get_active_from_context(C); + return (p->brush && p->brush->paint_curve); +} - if (uc->points) - MEM_freeN(uc->points); - uc->points = NULL; +static void paintcurve_undosys_step_encode_init(struct bContext *C, UndoStep *us_p) +{ + /* XXX, use to set the undo type only. */ + UNUSED_VARS(C, us_p); } -/** - * \note This is called before executing steps (not after). - */ -void ED_paintcurve_undo_push(bContext *C, wmOperator *op, PaintCurve *pc) +static bool paintcurve_undosys_step_encode(struct bContext *C, UndoStep *us_p) { - ePaintMode mode = BKE_paintmode_get_active_from_context(C); - ListBase *lb = NULL; - int undo_stack_id; - UndoCurve *uc; - - switch (mode) { - case ePaintTexture2D: - case ePaintTextureProjective: - undo_stack_id = UNDO_PAINT_IMAGE; - break; - - case ePaintSculpt: - undo_stack_id = UNDO_PAINT_MESH; - break; - - default: - /* do nothing, undo is handled by global */ - return; + Paint *p = BKE_paint_get_active_from_context(C); + PaintCurve *pc = p ? (p->brush ? p->brush->paint_curve : NULL) : NULL; + if (pc == NULL) { + return false; } + PaintCurveUndoStep *us = (PaintCurveUndoStep *)us_p; + BLI_assert(us->step.data_size == 0); - ED_undo_paint_push_begin(undo_stack_id, op->type->name, - paintcurve_undo_restore, paintcurve_undo_delete, NULL); - lb = undo_paint_push_get_list(undo_stack_id); + us->pc = pc; + undocurve_from_paintcurve(&us->data, pc); - uc = MEM_callocN(sizeof(*uc), "Undo_curve"); + return true; +} - lb->first = uc; +static void paintcurve_undosys_step_decode(struct bContext *UNUSED(C), UndoStep *us_p, int UNUSED(dir)) +{ + PaintCurveUndoStep *us = (PaintCurveUndoStep *)us_p; + undocurve_to_paintcurve(&us->data, us->pc); +} - BLI_strncpy(uc->idname, pc->id.name, sizeof(uc->idname)); - uc->tot_points = pc->tot_points; - uc->active_point = pc->add_index; - uc->points = MEM_dupallocN(pc->points); +static void paintcurve_undosys_step_free(UndoStep *us_p) +{ + PaintCurveUndoStep *us = (PaintCurveUndoStep *)us_p; + undocurve_free_data(&us->data); +} + +/* Export for ED_undo_sys. */ +void ED_paintcurve_undosys_type(UndoType *ut) +{ + ut->name = "Paint Curve"; + /* don't poll for now */ + ut->poll = paintcurve_undosys_poll; + ut->step_encode_init = paintcurve_undosys_step_encode_init; + ut->step_encode = paintcurve_undosys_step_encode; + ut->step_decode = paintcurve_undosys_step_decode; + ut->step_free = paintcurve_undosys_step_free; + + ut->mode = BKE_UNDOTYPE_MODE_STORE; + ut->use_context = false; + + ut->step_size = sizeof(PaintCurveUndoStep); +} + +/** \} */ - undo_paint_push_count_alloc(undo_stack_id, sizeof(*uc) + sizeof(*pc->points) * pc->tot_points); - ED_undo_paint_push_end(undo_stack_id); +/* -------------------------------------------------------------------- */ +/** \name Utilities + * \{ */ + +void ED_paintcurve_undo_push_begin(const char *name) +{ + bContext *C = NULL; /* special case, we never read from this. */ + wmWindowManager *wm = G.main->wm.first; + BKE_undosys_step_push_init_with_type(wm->undo_stack, C, name, BKE_UNDOSYS_TYPE_PAINTCURVE); } + +void ED_paintcurve_undo_push_end(void) +{ + wmWindowManager *wm = G.main->wm.first; /* XXX, avoids adding extra arg. */ + BKE_undosys_step_push(wm->undo_stack, NULL, NULL); +} + +/** \} */ diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index c6472b258ca..5d133ee622f 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -58,6 +58,9 @@ #include "BKE_material.h" #include "BKE_node.h" #include "BKE_paint.h" +#include "BKE_undo_system.h" + +#include "BKE_global.h" #include "UI_interface.h" #include "UI_view2d.h" @@ -145,9 +148,11 @@ void ED_imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int imapaint_region_tiles(ibuf, x, y, w, h, &tilex, &tiley, &tilew, &tileh); + ListBase *undo_tiles = ED_image_undo_get_tiles(); + for (ty = tiley; ty <= tileh; ty++) for (tx = tilex; tx <= tilew; tx++) - image_undo_push_tile(ima, ibuf, &tmpibuf, tx, ty, NULL, NULL, false, find_old); + image_undo_push_tile(undo_tiles, ima, ibuf, &tmpibuf, tx, ty, NULL, NULL, false, find_old); ibuf->userflags |= IB_BITMAPDIRTY; @@ -491,7 +496,8 @@ static void paint_stroke_update_step(bContext *C, struct PaintStroke *stroke, Po BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * alphafac)); if ((brush->flag & BRUSH_DRAG_DOT) || (brush->flag & BRUSH_ANCHORED)) { - ED_image_undo_restore(); + UndoStack *ustack = CTX_wm_manager(C)->undo_stack; + ED_image_undo_restore(ustack->step_init); } if (pop->mode == PAINT_MODE_3D_PROJECT) { @@ -1174,14 +1180,17 @@ void PAINT_OT_brush_colors_flip(wmOperatorType *ot) void ED_imapaint_bucket_fill(struct bContext *C, float color[3], wmOperator *op) { + wmWindowManager *wm = CTX_wm_manager(C); SpaceImage *sima = CTX_wm_space_image(C); Image *ima = sima->image; + BKE_undosys_step_push_init_with_type(wm->undo_stack, C, op->type->name, BKE_UNDOSYS_TYPE_IMAGE); + ED_image_undo_push_begin(op->type->name); paint_2d_bucket_fill(C, color, NULL, NULL, NULL); - ED_undo_paint_push_end(UNDO_PAINT_IMAGE); + BKE_undosys_step_push(wm->undo_stack, C, op->type->name); DAG_id_tag_update(&ima->id, 0); } diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index 894277402a7..c1dd31ea055 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -1035,6 +1035,8 @@ static void paint_2d_do_making_brush(ImagePaintState *s, ImBuf tmpbuf; IMB_initImBuf(&tmpbuf, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, 0); + ListBase *undo_tiles = ED_image_undo_get_tiles(); + for (int ty = tiley; ty <= tileh; ty++) { for (int tx = tilex; tx <= tilew; tx++) { /* retrieve original pixels + mask from undo buffer */ @@ -1043,9 +1045,9 @@ static void paint_2d_do_making_brush(ImagePaintState *s, int origy = region->desty - ty * IMAPAINT_TILE_SIZE; if (s->canvas->rect_float) - tmpbuf.rect_float = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask, false); + tmpbuf.rect_float = image_undo_find_tile(undo_tiles, s->image, s->canvas, tx, ty, &mask, false); else - tmpbuf.rect = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask, false); + tmpbuf.rect = image_undo_find_tile(undo_tiles, s->image, s->canvas, tx, ty, &mask, false); IMB_rectblend(s->canvas, &tmpbuf, frombuf, mask, curveb, texmaskb, mask_max, diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 60fe8555ba2..e5bddae971d 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -1499,15 +1499,16 @@ static int project_paint_undo_subtiles(const TileInfo *tinf, int tx, int ty) if (generate_tile) { + ListBase *undo_tiles = ED_image_undo_get_tiles(); volatile void *undorect; if (tinf->masked) { undorect = image_undo_push_tile( - pjIma->ima, pjIma->ibuf, tinf->tmpibuf, + undo_tiles, pjIma->ima, pjIma->ibuf, tinf->tmpibuf, tx, ty, &pjIma->maskRect[tile_index], &pjIma->valid[tile_index], true, false); } else { undorect = image_undo_push_tile( - pjIma->ima, pjIma->ibuf, tinf->tmpibuf, + undo_tiles, pjIma->ima, pjIma->ibuf, tinf->tmpibuf, tx, ty, NULL, &pjIma->valid[tile_index], true, false); } diff --git a/source/blender/editors/sculpt_paint/paint_image_undo.c b/source/blender/editors/sculpt_paint/paint_image_undo.c index b5b64eb3e16..9d1987943a5 100644 --- a/source/blender/editors/sculpt_paint/paint_image_undo.c +++ b/source/blender/editors/sculpt_paint/paint_image_undo.c @@ -29,6 +29,10 @@ #include "BLI_threads.h" #include "DNA_image_types.h" +#include "DNA_windowmanager_types.h" +#include "DNA_object_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" @@ -37,6 +41,8 @@ #include "BKE_depsgraph.h" #include "BKE_image.h" #include "BKE_main.h" +#include "BKE_global.h" +#include "BKE_undo_system.h" #include "ED_paint.h" @@ -44,10 +50,14 @@ #include "paint_intern.h" +/* -------------------------------------------------------------------- */ +/** \name Undo Conversion + * \{ */ + typedef struct UndoImageTile { struct UndoImageTile *next, *prev; - char idname[MAX_ID_NAME]; /* name instead of pointer*/ + char idname[MAX_ID_NAME]; /* name instead of pointer */ char ibufname[IMB_FILENAME_SIZE]; union { @@ -64,6 +74,8 @@ typedef struct UndoImageTile { short source, use_float; char gen_type; bool valid; + + size_t undo_size; } UndoImageTile; /* this is a static resource for non-globality, @@ -129,13 +141,14 @@ static void undo_copy_tile(UndoImageTile *tile, ImBuf *tmpibuf, ImBuf *ibuf, Cop } } -void *image_undo_find_tile(Image *ima, ImBuf *ibuf, int x_tile, int y_tile, unsigned short **mask, bool validate) +void *image_undo_find_tile( + ListBase *undo_tiles, + Image *ima, ImBuf *ibuf, int x_tile, int y_tile, unsigned short **mask, bool validate) { - ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE); UndoImageTile *tile; short use_float = ibuf->rect_float ? 1 : 0; - for (tile = lb->first; tile; tile = tile->next) { + for (tile = undo_tiles->first; tile; tile = tile->next) { if (tile->x == x_tile && tile->y == y_tile && ima->gen_type == tile->gen_type && ima->source == tile->source) { if (tile->use_float == use_float) { if (STREQ(tile->idname, ima->id.name) && STREQ(tile->ibufname, ibuf->name)) { @@ -161,10 +174,10 @@ void *image_undo_find_tile(Image *ima, ImBuf *ibuf, int x_tile, int y_tile, unsi } void *image_undo_push_tile( + ListBase *undo_tiles, Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, int y_tile, unsigned short **mask, bool **valid, bool proj, bool find_prev) { - ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE); UndoImageTile *tile; int allocsize; short use_float = ibuf->rect_float ? 1 : 0; @@ -174,7 +187,7 @@ void *image_undo_push_tile( /* in projective painting we keep accounting of tiles, so if we need one pushed, just push! */ if (find_prev) { - data = image_undo_find_tile(ima, ibuf, x_tile, y_tile, mask, true); + data = image_undo_find_tile(undo_tiles, ima, ibuf, x_tile, y_tile, mask, true); if (data) { return data; } @@ -214,8 +227,7 @@ void *image_undo_push_tile( if (proj) { BLI_spin_lock(&undolock); } - undo_paint_push_count_alloc(UNDO_PAINT_IMAGE, allocsize); - BLI_addtail(lb, tile); + BLI_addtail(undo_tiles, tile); if (proj) { BLI_spin_unlock(&undolock); @@ -225,10 +237,10 @@ void *image_undo_push_tile( void image_undo_remove_masks(void) { - ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE); + ListBase *undo_tiles = ED_image_undo_get_tiles(); UndoImageTile *tile; - for (tile = lb->first; tile; tile = tile->next) { + for (tile = undo_tiles->first; tile; tile = tile->next) { if (tile->mask) { MEM_freeN(tile->mask); tile->mask = NULL; @@ -346,50 +358,145 @@ static void image_undo_free_list(ListBase *lb) void ED_image_undo_push_begin(const char *name) { - ED_undo_paint_push_begin(UNDO_PAINT_IMAGE, name, image_undo_restore_list, image_undo_free_list, NULL); + bContext *C = NULL; /* special case, we never read from this. */ + wmWindowManager *wm = G.main->wm.first; + BKE_undosys_step_push_init_with_type(wm->undo_stack, C, name, BKE_UNDOSYS_TYPE_IMAGE); } void ED_image_undo_push_end(void) { - ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE); + wmWindowManager *wm = G.main->wm.first; /* XXX, avoids adding extra arg. */ + BKE_undosys_step_push(wm->undo_stack, NULL, NULL); +} + +static void image_undo_invalidate(void) +{ UndoImageTile *tile; - int deallocsize = 0; + ListBase *lb = ED_image_undo_get_tiles(); + + for (tile = lb->first; tile; tile = tile->next) { + tile->valid = false; + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Implements ED Undo System + * \{ */ + +typedef struct ImageUndoStep { + UndoStep step; + ListBase tiles; +} ImageUndoStep; + +static bool image_undosys_poll(bContext *C) +{ + Object *obact = CTX_data_active_object(C); + + ScrArea *sa = CTX_wm_area(C); + if (sa && (sa->spacetype == SPACE_IMAGE)) { + SpaceImage *sima = (SpaceImage *)sa->spacedata.first; + if ((obact && (obact->mode & OB_MODE_TEXTURE_PAINT)) || (sima->mode == SI_MODE_PAINT)) { + return true; + } + } + else if (sa && (sa->spacetype == SPACE_VIEW3D)) { + if (obact && (obact->mode & OB_MODE_TEXTURE_PAINT)) { + return true; + } + } + return false; +} + +static void image_undosys_step_encode_init(struct bContext *UNUSED(C), UndoStep *us_p) +{ + ImageUndoStep *us = (ImageUndoStep *)us_p; + /* dummy, memory is cleared anyway. */ + BLI_listbase_clear(&us->tiles); +} + +static bool image_undosys_step_encode(struct bContext *UNUSED(C), UndoStep *us_p) +{ + /* dummy, encoding is done along the way by adding tiles + * to the current 'ImageUndoStep' added by encode_init. */ + ImageUndoStep *us = (ImageUndoStep *)us_p; + + BLI_assert(us->step.data_size == 0); + int allocsize = IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE * 4; /* first dispose of invalid tiles (may happen due to drag dot for instance) */ - for (tile = lb->first; tile;) { + for (UndoImageTile *tile = us->tiles.first; tile;) { if (!tile->valid) { UndoImageTile *tmp_tile = tile->next; - deallocsize += allocsize * ((tile->use_float) ? sizeof(float) : sizeof(char)); MEM_freeN(tile->rect.pt); - BLI_freelinkN(lb, tile); + BLI_freelinkN(&us->tiles, tile); tile = tmp_tile; } else { + us->step.data_size += allocsize * ((tile->use_float) ? sizeof(float) : sizeof(char)); tile = tile->next; } } - /* don't forget to remove the size of deallocated tiles */ - undo_paint_push_count_alloc(UNDO_PAINT_IMAGE, -deallocsize); + return true; +} - ED_undo_paint_push_end(UNDO_PAINT_IMAGE); +static void image_undosys_step_decode(struct bContext *C, UndoStep *us_p, int UNUSED(dir)) +{ + ImageUndoStep *us = (ImageUndoStep *)us_p; + image_undo_restore_list(C, &us->tiles); } -static void image_undo_invalidate(void) +static void image_undosys_step_free(UndoStep *us_p) { - UndoImageTile *tile; - ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE); + ImageUndoStep *us = (ImageUndoStep *)us_p; + image_undo_free_list(&us->tiles); +} - for (tile = lb->first; tile; tile = tile->next) { - tile->valid = false; - } +/* Export for ED_undo_sys. */ +void ED_image_undosys_type(UndoType *ut) +{ + ut->name = "Image"; + ut->poll = image_undosys_poll; + ut->step_encode_init = image_undosys_step_encode_init; + ut->step_encode = image_undosys_step_encode; + ut->step_decode = image_undosys_step_decode; + ut->step_free = image_undosys_step_free; + + ut->mode = BKE_UNDOTYPE_MODE_ACCUMULATE; + ut->use_context = true; + + ut->step_size = sizeof(ImageUndoStep); +} + +/** \} */ + + +/* -------------------------------------------------------------------- */ +/** \name Utilities + * \{ */ + +ListBase *ED_image_undosys_step_get_tiles(UndoStep *us_p) +{ + ImageUndoStep *us = (ImageUndoStep *)us_p; + return &us->tiles; +} + +ListBase *ED_image_undo_get_tiles(void) +{ + wmWindowManager *wm = G.main->wm.first; /* XXX, avoids adding extra arg. */ + UndoStep *us = BKE_undosys_stack_init_or_active_with_type(wm->undo_stack, BKE_UNDOSYS_TYPE_IMAGE); + return ED_image_undosys_step_get_tiles(us); } /* restore painting image to previous state. Used for anchored and drag-dot style brushes*/ -void ED_image_undo_restore(void) +void ED_image_undo_restore(UndoStep *us) { - ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE); + ListBase *lb = ED_image_undosys_step_get_tiles(us); image_undo_restore_runtime(lb); image_undo_invalidate(); } + +/** \} */ diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index 52be4be4c0d..63a12b3c145 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -55,6 +55,7 @@ struct wmOperator; struct wmOperatorType; struct wmWindowManager; struct DMCoNo; +struct UndoStep; enum ePaintMode; /* paint_stroke.c */ @@ -221,15 +222,20 @@ void PAINT_OT_add_simple_uvs(struct wmOperatorType *ot); /* paint_image_undo.c */ void *image_undo_find_tile( + ListBase *undo_tiles, struct Image *ima, struct ImBuf *ibuf, int x_tile, int y_tile, unsigned short **mask, bool validate); void *image_undo_push_tile( + ListBase *undo_tiles, struct Image *ima, struct ImBuf *ibuf, struct ImBuf **tmpibuf, int x_tile, int y_tile, unsigned short **, bool **valid, bool proj, bool find_prev); void image_undo_remove_masks(void); void image_undo_init_locks(void); void image_undo_end_locks(void); +struct ListBase *ED_image_undosys_step_get_tiles(struct UndoStep *us_p); +struct ListBase *ED_image_undo_get_tiles(void); + /* sculpt_uv.c */ int uv_sculpt_poll(struct bContext *C); int uv_sculpt_keymap_poll(struct bContext *C); @@ -304,10 +310,6 @@ typedef enum { void set_brush_rc_props(struct PointerRNA *ptr, const char *paint, const char *prop, const char *secondary_prop, RCFlags flags); -/* paint_undo.c */ -struct ListBase *undo_paint_push_get_list(int type); -void undo_paint_push_count_alloc(int type, int size); - /* paint_hide.c */ typedef enum { diff --git a/source/blender/editors/sculpt_paint/paint_undo.c b/source/blender/editors/sculpt_paint/paint_undo.c deleted file mode 100644 index 27d3f6648a2..00000000000 --- a/source/blender/editors/sculpt_paint/paint_undo.c +++ /dev/null @@ -1,410 +0,0 @@ -/* - * ***** BEGIN GPL LICENSE BLOCK ***** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * ***** END GPL LICENSE BLOCK ***** - */ - -/** \file blender/editors/sculpt_paint/paint_undo.c - * \ingroup edsculpt - * \brief Undo system for painting and sculpting. - */ - -#include -#include - -#include "MEM_guardedalloc.h" - -#include "BLI_listbase.h" -#include "BLI_utildefines.h" -#include "BLI_string.h" - -#include "DNA_userdef_types.h" - -#include "BKE_blender_undo.h" -#include "BKE_context.h" -#include "BKE_global.h" - -#include "ED_paint.h" - -#include "paint_intern.h" - -typedef struct UndoElem { - struct UndoElem *next, *prev; - char name[BKE_UNDO_STR_MAX]; - uintptr_t undosize; - - ListBase elems; - - UndoRestoreCb restore; - UndoFreeCb free; - UndoCleanupCb cleanup; -} UndoElem; - -typedef struct UndoStack { - int type; - ListBase elems; - UndoElem *current; -} UndoStack; - -static UndoStack ImageUndoStack = {UNDO_PAINT_IMAGE, {NULL, NULL}, NULL}; -static UndoStack MeshUndoStack = {UNDO_PAINT_MESH, {NULL, NULL}, NULL}; - -/* Generic */ - -static void undo_restore(bContext *C, UndoStack *UNUSED(stack), UndoElem *uel) -{ - if (uel && uel->restore) - uel->restore(C, &uel->elems); -} - -static void undo_elem_free(UndoStack *UNUSED(stack), UndoElem *uel) -{ - if (uel && uel->free) { - uel->free(&uel->elems); - BLI_freelistN(&uel->elems); - } -} - -static void undo_stack_push_begin(UndoStack *stack, const char *name, UndoRestoreCb restore, UndoFreeCb free, UndoCleanupCb cleanup) -{ - UndoElem *uel; - int nr; - - /* Undo push is split up in begin and end, the reason is that as painting - * happens more tiles/nodes are added to the list, and at the very end we - * know how much memory the undo used to remove old undo elements */ - - /* remove all undos after (also when stack->current==NULL) */ - while (stack->elems.last != stack->current) { - uel = stack->elems.last; - undo_elem_free(stack, uel); - BLI_freelinkN(&stack->elems, uel); - } - - /* make new */ - stack->current = uel = MEM_callocN(sizeof(UndoElem), "undo file"); - uel->restore = restore; - uel->free = free; - uel->cleanup = cleanup; - BLI_addtail(&stack->elems, uel); - - /* name can be a dynamic string */ - BLI_strncpy(uel->name, name, sizeof(uel->name)); - - /* limit amount to the maximum amount*/ - nr = 0; - uel = stack->elems.last; - while (uel) { - nr++; - if (nr == U.undosteps) break; - uel = uel->prev; - } - if (uel) { - while (stack->elems.first != uel) { - UndoElem *first = stack->elems.first; - undo_elem_free(stack, first); - BLI_freelinkN(&stack->elems, first); - } - } -} - -static void undo_stack_push_end(UndoStack *stack) -{ - UndoElem *uel; - uintptr_t totmem, maxmem; - int totundo = 0; - - /* first limit to undo steps */ - uel = stack->elems.last; - - while (uel) { - totundo++; - if (totundo > U.undosteps) break; - uel = uel->prev; - } - - if (uel) { - UndoElem *first; - - /* in case the undo steps are zero, the current pointer will be invalid */ - if (uel == stack->current) - stack->current = NULL; - - do { - first = stack->elems.first; - undo_elem_free(stack, first); - BLI_freelinkN(&stack->elems, first); - } while (first != uel); - } - - if (U.undomemory != 0) { - /* limit to maximum memory (afterwards, we can't know in advance) */ - totmem = 0; - maxmem = ((uintptr_t)U.undomemory) * 1024 * 1024; - - uel = stack->elems.last; - while (uel) { - totmem += uel->undosize; - if (totmem > maxmem) break; - uel = uel->prev; - } - - if (uel) { - while (stack->elems.first != uel) { - UndoElem *first = stack->elems.first; - undo_elem_free(stack, first); - BLI_freelinkN(&stack->elems, first); - } - } - } -} - -static void undo_stack_cleanup(UndoStack *stack, bContext *C) -{ - UndoElem *uel = stack->elems.first; - bool stack_reset = false; - - while (uel) { - if (uel->cleanup && uel->cleanup(C, &uel->elems)) { - UndoElem *uel_tmp = uel->next; - if (stack->current == uel) { - stack->current = NULL; - stack_reset = true; - } - undo_elem_free(stack, uel); - BLI_freelinkN(&stack->elems, uel); - uel = uel_tmp; - } - else - uel = uel->next; - } - if (stack_reset) { - stack->current = stack->elems.last; - } - -} - -static int undo_stack_step(bContext *C, UndoStack *stack, int step, const char *name) -{ - UndoElem *undo; - - /* first cleanup any old undo steps that may belong to invalid data */ - undo_stack_cleanup(stack, C); - - if (step == 1) { - if (stack->current == NULL) { - /* pass */ - } - else { - if (!name || STREQ(stack->current->name, name)) { - if (G.debug & G_DEBUG_WM) { - printf("%s: undo '%s'\n", __func__, stack->current->name); - } - undo_restore(C, stack, stack->current); - stack->current = stack->current->prev; - return 1; - } - } - } - else if (step == -1) { - if ((stack->current != NULL && stack->current->next == NULL) || BLI_listbase_is_empty(&stack->elems)) { - /* pass */ - } - else { - if (!name || STREQ(stack->current->name, name)) { - undo = (stack->current && stack->current->next) ? stack->current->next : stack->elems.first; - undo_restore(C, stack, undo); - stack->current = undo; - if (G.debug & G_DEBUG_WM) { - printf("%s: redo %s\n", __func__, undo->name); - } - return 1; - } - } - } - - return 0; -} - -static void undo_stack_free(UndoStack *stack) -{ - UndoElem *uel; - - for (uel = stack->elems.first; uel; uel = uel->next) - undo_elem_free(stack, uel); - - BLI_freelistN(&stack->elems); - stack->current = NULL; -} - -/* Exported Functions */ - -void ED_undo_paint_push_begin(int type, const char *name, UndoRestoreCb restore, UndoFreeCb free, UndoCleanupCb cleanup) -{ - if (type == UNDO_PAINT_IMAGE) - undo_stack_push_begin(&ImageUndoStack, name, restore, free, cleanup); - else if (type == UNDO_PAINT_MESH) - undo_stack_push_begin(&MeshUndoStack, name, restore, free, cleanup); -} - -ListBase *undo_paint_push_get_list(int type) -{ - if (type == UNDO_PAINT_IMAGE) { - if (ImageUndoStack.current) { - return &ImageUndoStack.current->elems; - } - } - else if (type == UNDO_PAINT_MESH) { - if (MeshUndoStack.current) { - return &MeshUndoStack.current->elems; - } - } - - return NULL; -} - -void undo_paint_push_count_alloc(int type, int size) -{ - if (type == UNDO_PAINT_IMAGE) - ImageUndoStack.current->undosize += size; - else if (type == UNDO_PAINT_MESH) - MeshUndoStack.current->undosize += size; -} - -void ED_undo_paint_push_end(int type) -{ - if (type == UNDO_PAINT_IMAGE) - undo_stack_push_end(&ImageUndoStack); - else if (type == UNDO_PAINT_MESH) - undo_stack_push_end(&MeshUndoStack); -} - -int ED_undo_paint_step(bContext *C, int type, int step, const char *name) -{ - if (type == UNDO_PAINT_IMAGE) - return undo_stack_step(C, &ImageUndoStack, step, name); - else if (type == UNDO_PAINT_MESH) - return undo_stack_step(C, &MeshUndoStack, step, name); - - return 0; -} - -static void undo_step_num(bContext *C, UndoStack *stack, int step) -{ - UndoElem *uel; - int a = 0; - int curnum = BLI_findindex(&stack->elems, stack->current); - - for (uel = stack->elems.first; uel; uel = uel->next, a++) { - if (a == step) break; - } - - if (curnum > a) { - while (a++ != curnum) - undo_stack_step(C, stack, 1, NULL); - } - else if (curnum < a) { - while (a-- != curnum) - undo_stack_step(C, stack, -1, NULL); - } -} - -void ED_undo_paint_step_num(bContext *C, int type, int step) -{ - if (type == UNDO_PAINT_IMAGE) - undo_step_num(C, &ImageUndoStack, step); - else if (type == UNDO_PAINT_MESH) - undo_step_num(C, &MeshUndoStack, step); -} - -static char *undo_stack_get_name(UndoStack *stack, int nr, bool *r_active) -{ - UndoElem *uel; - - if (r_active) *r_active = false; - - uel = BLI_findlink(&stack->elems, nr); - if (uel) { - if (r_active && (uel == stack->current)) { - *r_active = true; - } - return uel->name; - } - - return NULL; -} - -const char *ED_undo_paint_get_name(bContext *C, int type, int nr, bool *r_active) -{ - - if (type == UNDO_PAINT_IMAGE) { - undo_stack_cleanup(&ImageUndoStack, C); - return undo_stack_get_name(&ImageUndoStack, nr, r_active); - } - else if (type == UNDO_PAINT_MESH) { - undo_stack_cleanup(&MeshUndoStack, C); - return undo_stack_get_name(&MeshUndoStack, nr, r_active); - } - return NULL; -} - -bool ED_undo_paint_empty(int type) -{ - UndoStack *stack; - - if (type == UNDO_PAINT_IMAGE) - stack = &ImageUndoStack; - else if (type == UNDO_PAINT_MESH) - stack = &MeshUndoStack; - else - return true; - - if (stack->current == NULL) { - return true; - } - - return false; -} - -bool ED_undo_paint_is_valid(int type, const char *name) -{ - UndoStack *stack; - - if (type == UNDO_PAINT_IMAGE) - stack = &ImageUndoStack; - else if (type == UNDO_PAINT_MESH) - stack = &MeshUndoStack; - else - return 0; - - if (stack->current == NULL) { - /* pass */ - } - else { - if (name && STREQ(stack->current->name, name)) - return 1; - else - return stack->elems.first != stack->elems.last; - } - return 0; -} - -void ED_undo_paint_free(void) -{ - undo_stack_free(&ImageUndoStack); - undo_stack_free(&MeshUndoStack); -} diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index cf1937f14d4..e46760258e1 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -122,6 +122,8 @@ typedef struct SculptUndoNode { /* shape keys */ char shapeName[sizeof(((KeyBlock *)0))->name]; + + size_t undo_size; } SculptUndoNode; /* Factor of brush to have rake point following behind diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index 794ac14483a..4e2bcab9f36 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -48,6 +48,8 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_mesh_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" #include "BKE_ccg.h" #include "BKE_context.h" @@ -57,6 +59,9 @@ #include "BKE_key.h" #include "BKE_mesh.h" #include "BKE_subsurf.h" +#include "BKE_global.h" +#include "BKE_main.h" +#include "BKE_undo_system.h" #include "WM_api.h" #include "WM_types.h" @@ -64,12 +69,21 @@ #include "GPU_buffers.h" #include "ED_paint.h" +#include "ED_object.h" +#include "ED_sculpt.h" #include "bmesh.h" #include "paint_intern.h" #include "sculpt_intern.h" -/************************** Undo *************************/ + +typedef struct UndoSculpt { + ListBase nodes; + + size_t undo_size; +} UndoSculpt; + +static UndoSculpt *sculpt_undo_get_nodes(void); static void update_cb(PBVHNode *node, void *rebuild) { @@ -454,7 +468,7 @@ static int sculpt_undo_bmesh_restore(bContext *C, return false; } -static void sculpt_undo_restore(bContext *C, ListBase *lb) +static void sculpt_undo_restore_list(bContext *C, ListBase *lb) { Scene *scene = CTX_data_scene(C); Sculpt *sd = CTX_data_tool_settings(C)->sculpt; @@ -574,7 +588,7 @@ static void sculpt_undo_restore(bContext *C, ListBase *lb) } } -static void sculpt_undo_free(ListBase *lb) +static void sculpt_undo_free_list(ListBase *lb) { SculptUndoNode *unode; int i; @@ -617,6 +631,8 @@ static void sculpt_undo_free(ListBase *lb) } } +/* Most likely we don't need this. */ +#if 0 static bool sculpt_undo_cleanup(bContext *C, ListBase *lb) { Object *ob = CTX_data_active_object(C); @@ -633,16 +649,17 @@ static bool sculpt_undo_cleanup(bContext *C, ListBase *lb) return false; } +#endif SculptUndoNode *sculpt_undo_get_node(PBVHNode *node) { - ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_MESH); + UndoSculpt *usculpt = sculpt_undo_get_nodes(); - if (!lb) { + if (usculpt == NULL) { return NULL; } - return BLI_findptr(lb, node, offsetof(SculptUndoNode, node)); + return BLI_findptr(&usculpt->nodes, node, offsetof(SculptUndoNode, node)); } static void sculpt_undo_alloc_and_store_hidden(PBVH *pbvh, @@ -668,10 +685,11 @@ static void sculpt_undo_alloc_and_store_hidden(PBVH *pbvh, } } -static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, - SculptUndoType type) +static SculptUndoNode *sculpt_undo_alloc_node( + Object *ob, PBVHNode *node, + SculptUndoType type) { - ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_MESH); + UndoSculpt *usculpt = sculpt_undo_get_nodes(); SculptUndoNode *unode; SculptSession *ss = ob->sculpt; int totvert, allvert, totgrid, maxgrid, gridsize, *grids; @@ -696,23 +714,23 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, /* general TODO, fix count_alloc */ switch (type) { case SCULPT_UNDO_COORDS: - unode->co = MEM_mapallocN(sizeof(float) * 3 * allvert, "SculptUndoNode.co"); - unode->no = MEM_mapallocN(sizeof(short) * 3 * allvert, "SculptUndoNode.no"); - undo_paint_push_count_alloc(UNDO_PAINT_MESH, - (sizeof(float) * 3 + - sizeof(short) * 3 + - sizeof(int)) * allvert); + unode->co = MEM_mapallocN(sizeof(float[3]) * allvert, "SculptUndoNode.co"); + unode->no = MEM_mapallocN(sizeof(short[3]) * allvert, "SculptUndoNode.no"); + + usculpt->undo_size = (sizeof(float[3]) + sizeof(short[3]) + sizeof(int)) * allvert; break; case SCULPT_UNDO_HIDDEN: if (maxgrid) sculpt_undo_alloc_and_store_hidden(ss->pbvh, unode); else unode->vert_hidden = BLI_BITMAP_NEW(allvert, "SculptUndoNode.vert_hidden"); - + break; case SCULPT_UNDO_MASK: unode->mask = MEM_mapallocN(sizeof(float) * allvert, "SculptUndoNode.mask"); - undo_paint_push_count_alloc(UNDO_PAINT_MESH, (sizeof(float) * sizeof(int)) * allvert); + + usculpt->undo_size += (sizeof(float) * sizeof(int)) * allvert; + break; case SCULPT_UNDO_DYNTOPO_BEGIN: case SCULPT_UNDO_DYNTOPO_END: @@ -721,7 +739,7 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, break; } - BLI_addtail(lb, unode); + BLI_addtail(&usculpt->nodes, unode); if (maxgrid) { /* multires */ @@ -798,12 +816,13 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, SculptUndoType type) { - ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_MESH); - SculptUndoNode *unode = lb->first; + UndoSculpt *usculpt = sculpt_undo_get_nodes(); SculptSession *ss = ob->sculpt; PBVHVertexIter vd; - if (!lb->first) { + SculptUndoNode *unode = usculpt->nodes.first; + + if (unode == NULL) { unode = MEM_callocN(sizeof(*unode), __func__); BLI_strncpy(unode->idname, ob->id.name, sizeof(unode->idname)); @@ -842,7 +861,7 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, unode->bm_entry = BM_log_entry_add(ss->bm_log); } - BLI_addtail(lb, unode); + BLI_addtail(&usculpt->nodes, unode); } if (node) { @@ -883,8 +902,9 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, return unode; } -SculptUndoNode *sculpt_undo_push_node(Object *ob, PBVHNode *node, - SculptUndoType type) +SculptUndoNode *sculpt_undo_push_node( + Object *ob, PBVHNode *node, + SculptUndoType type) { SculptSession *ss = ob->sculpt; SculptUndoNode *unode; @@ -954,17 +974,18 @@ SculptUndoNode *sculpt_undo_push_node(Object *ob, PBVHNode *node, void sculpt_undo_push_begin(const char *name) { - ED_undo_paint_push_begin(UNDO_PAINT_MESH, name, - sculpt_undo_restore, sculpt_undo_free, sculpt_undo_cleanup); + bContext *C = NULL; /* special case, we never read from this. */ + wmWindowManager *wm = G.main->wm.first; + BKE_undosys_step_push_init_with_type(wm->undo_stack, C, name, BKE_UNDOSYS_TYPE_SCULPT); } void sculpt_undo_push_end(void) { - ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_MESH); + UndoSculpt *usculpt = sculpt_undo_get_nodes(); SculptUndoNode *unode; /* we don't need normals in the undo stack */ - for (unode = lb->first; unode; unode = unode->next) { + for (unode = usculpt->nodes.first; unode; unode = unode->next) { if (unode->no) { MEM_freeN(unode->no); unode->no = NULL; @@ -974,7 +995,97 @@ void sculpt_undo_push_end(void) BKE_pbvh_node_layer_disp_free(unode->node); } - ED_undo_paint_push_end(UNDO_PAINT_MESH); + wmWindowManager *wm = G.main->wm.first; /* XXX, avoids adding extra arg. */ + BKE_undosys_step_push(wm->undo_stack, NULL, NULL); +} + +/* -------------------------------------------------------------------- */ +/** \name Implements ED Undo System + * \{ */ + +typedef struct SculptUndoStep { + UndoStep step; + /* note: will split out into list for multi-object-sculpt-mode. */ + UndoSculpt data; +} SculptUndoStep; + +static bool sculpt_undosys_poll(bContext *C) +{ + ScrArea *sa = CTX_wm_area(C); + if (sa && (sa->spacetype == SPACE_VIEW3D)) { + Object *obact = CTX_data_active_object(C); + if (obact && (obact->mode & OB_MODE_SCULPT)) { + return true; + } + } + return false; +} + +static void sculpt_undosys_step_encode_init(struct bContext *UNUSED(C), UndoStep *us_p) +{ + SculptUndoStep *us = (SculptUndoStep *)us_p; + /* dummy, memory is cleared anyway. */ + BLI_listbase_clear(&us->data.nodes); +} + +static bool sculpt_undosys_step_encode(struct bContext *UNUSED(C), UndoStep *us_p) +{ + /* dummy, encoding is done along the way by adding tiles + * to the current 'SculptUndoStep' added by encode_init. */ + SculptUndoStep *us = (SculptUndoStep *)us_p; + us->step.data_size = us->data.undo_size; + return true; +} + +static void sculpt_undosys_step_decode(struct bContext *C, UndoStep *us_p, int UNUSED(dir)) +{ + /* TODO(campbell): undo_system: use low-level API to set mode. */ + ED_object_mode_set(C, OB_MODE_SCULPT); + BLI_assert(sculpt_undosys_poll(C)); + + SculptUndoStep *us = (SculptUndoStep *)us_p; + sculpt_undo_restore_list(C, &us->data.nodes); +} - WM_file_tag_modified(); +static void sculpt_undosys_step_free(UndoStep *us_p) +{ + SculptUndoStep *us = (SculptUndoStep *)us_p; + sculpt_undo_free_list(&us->data.nodes); } + +/* Export for ED_undo_sys. */ +void ED_sculpt_undosys_type(UndoType *ut) +{ + ut->name = "Sculpt"; + ut->poll = sculpt_undosys_poll; + ut->step_encode_init = sculpt_undosys_step_encode_init; + ut->step_encode = sculpt_undosys_step_encode; + ut->step_decode = sculpt_undosys_step_decode; + ut->step_free = sculpt_undosys_step_free; + + ut->mode = BKE_UNDOTYPE_MODE_ACCUMULATE; + ut->use_context = true; + + ut->step_size = sizeof(SculptUndoStep); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Utilities + * \{ */ + +static UndoSculpt *sculpt_undosys_step_get_nodes(UndoStep *us_p) +{ + SculptUndoStep *us = (SculptUndoStep *)us_p; + return &us->data; +} + +static UndoSculpt *sculpt_undo_get_nodes(void) +{ + wmWindowManager *wm = G.main->wm.first; /* XXX, avoids adding extra arg. */ + UndoStep *us = BKE_undosys_stack_init_or_active_with_type(wm->undo_stack, BKE_UNDOSYS_TYPE_SCULPT); + return sculpt_undosys_step_get_nodes(us); +} + +/** \} */ -- cgit v1.2.3