From 34ce85571f4feff59efad91ebd72e4aa942bae60 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Thu, 11 Aug 2011 18:18:46 +0000 Subject: [PATCH] Camera tracking integration =========================== Return non-blocking behavior to grease pencil sketch mode. It is needed to implement more accurate line drawing based on using mouse click on where line "knot" should be added and allowing pan/zoom between such knots selection. There are still issues with vertex paint/sculpt modes, but they aren't critical -- just behavior could be improved here. --- source/blender/editors/gpencil/CMakeLists.txt | 1 + .../blender/editors/gpencil/gpencil_intern.h | 6 + .../blender/editors/gpencil/gpencil_paint.c | 133 +++++++++------- source/blender/editors/gpencil/gpencil_undo.c | 142 ++++++++++++++++++ source/blender/editors/include/ED_gpencil.h | 3 + source/blender/editors/util/undo.c | 5 + 6 files changed, 234 insertions(+), 56 deletions(-) create mode 100644 source/blender/editors/gpencil/gpencil_undo.c diff --git a/source/blender/editors/gpencil/CMakeLists.txt b/source/blender/editors/gpencil/CMakeLists.txt index 7a2f196fd6d..b312f397939 100644 --- a/source/blender/editors/gpencil/CMakeLists.txt +++ b/source/blender/editors/gpencil/CMakeLists.txt @@ -42,6 +42,7 @@ set(SRC gpencil_edit.c gpencil_ops.c gpencil_paint.c + gpencil_undo.c gpencil_intern.h ) diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index 6ecdc2b054b..b0091394f5a 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -39,6 +39,7 @@ /* ***************************************************** */ /* Operator Defines */ +struct bGPdata; struct wmOperatorType; /* drawing ---------- */ @@ -63,6 +64,11 @@ void GPENCIL_OT_active_frame_delete(struct wmOperatorType *ot); void GPENCIL_OT_convert(struct wmOperatorType *ot); +/* undo stack ---------- */ + +void gpencil_undo_init(struct bGPdata *gpd); +void gpencil_undo_push(struct bGPdata *gpd); +void gpencil_undo_finish(void); /******************************************************* */ /* FILTERED ACTION DATA - TYPES ---> XXX DEPRECEATED OLD ANIM SYSTEM CODE! */ diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 01a0c81d736..44d2603451e 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -895,8 +895,10 @@ static void gp_session_validatebuffer (tGPsdata *p) /* clear memory of buffer (or allocate it if starting a new session) */ if (gpd->sbuffer) memset(gpd->sbuffer, 0, sizeof(tGPspoint)*GP_STROKE_BUFFER_MAX); - else + else { + //printf("\t\tGP - allocate sbuffer\n"); gpd->sbuffer= MEM_callocN(sizeof(tGPspoint)*GP_STROKE_BUFFER_MAX, "gp_session_strokebuffer"); + } /* reset indices */ gpd->sbuffer_size = 0; @@ -1075,7 +1077,12 @@ static tGPsdata *gp_session_initpaint (bContext *C) } /* set edit flags - so that buffer will get drawn */ - G.f |= G_GREASEPENCIL; + if((G.f & G_GREASEPENCIL)==0) { + G.f |= G_GREASEPENCIL; + + /* initialize undo stack */ + gpencil_undo_init(p->gpd); + } /* clear out buffer (stored in gp-data), in case something contaminated it */ gp_session_validatebuffer(p); @@ -1101,6 +1108,7 @@ static void gp_session_cleanup (tGPsdata *p) /* free stroke buffer */ if (gpd->sbuffer) { + //printf("\t\tGP - free sbuffer\n"); MEM_freeN(gpd->sbuffer); gpd->sbuffer= NULL; } @@ -1276,7 +1284,8 @@ static void gp_paint_strokeend (tGPsdata *p) static void gp_paint_cleanup (tGPsdata *p) { /* finish off a stroke */ - gp_paint_strokeend(p); + if(p->gpd) + gp_paint_strokeend(p); /* "unlock" frame */ if (p->gpf) @@ -1292,6 +1301,9 @@ static void gpencil_draw_exit (bContext *C, wmOperator *op) /* clear edit flags */ G.f &= ~G_GREASEPENCIL; + /* clear undo stack */ + gpencil_undo_finish(); + /* restore cursor to indicate end of drawing */ WM_cursor_restore(CTX_wm_window(C)); @@ -1621,6 +1633,7 @@ static int gpencil_draw_invoke (bContext *C, wmOperator *op, wmEvent *event) //printf("\tGP - hotkey invoked... waiting for click-drag\n"); } + WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL, NULL); /* add a modal handler for this operator, so that we can then draw continuous strokes */ WM_event_add_modal_handler(C, op); return OPERATOR_RUNNING_MODAL; @@ -1638,16 +1651,60 @@ static int gpencil_area_exists(bContext *C, ScrArea *satest) return 0; } +static tGPsdata *gpencil_stroke_begin(bContext *C, wmOperator *op) +{ + tGPsdata *p= op->customdata; + + /* we must check that we're still within the area that we're set up to work from + * otherwise we could crash (see bug #20586) + */ + if (CTX_wm_area(C) != p->sa) { + printf("\t\t\tGP - wrong area execution abort! \n"); + p->status= GP_STATUS_ERROR; + } + + /* free pointer used by previous stroke */ + if(p) + MEM_freeN(p); + + //printf("\t\tGP - start stroke \n"); + + /* we may need to set up paint env again if we're resuming */ + // XXX: watch it with the paintmode! in future, it'd be nice to allow changing paint-mode when in sketching-sessions + // XXX: with tablet events, we may event want to check for eraser here, for nicer tablet support + + gpencil_draw_init(C, op); + + p= op->customdata; + + if(p->status != GP_STATUS_ERROR) + p->status= GP_STATUS_PAINTING; + + return op->customdata; +} + +static void gpencil_stroke_end(wmOperator *op) +{ + tGPsdata *p= op->customdata; + + gp_paint_cleanup(p); + + gpencil_undo_push(p->gpd); + + gp_session_cleanup(p); + + p->status= GP_STATUS_IDLING; + + p->gpd= NULL; + p->gpl= NULL; + p->gpf= NULL; +} + /* events handling during interactive drawing part of operator */ static int gpencil_draw_modal (bContext *C, wmOperator *op, wmEvent *event) { tGPsdata *p= op->customdata; - //int estate = OPERATOR_PASS_THROUGH; /* default exit state - not handled, so let others have a share of the pie */ - /* currently, grease pencil conflicts with such operators as undo and set object mode - which makes behavior of operator totally unpredictable and crash for some cases. - the only way to solve this proper is to ger rid of pointers to data which can - chage stored in operator custom data (sergey) */ - int estate = OPERATOR_RUNNING_MODAL; + int estate = OPERATOR_PASS_THROUGH; /* default exit state - not handled, so let others have a share of the pie */ // if (event->type == NDOF_MOTION) // return OPERATOR_PASS_THROUGH; @@ -1681,11 +1738,13 @@ static int gpencil_draw_modal (bContext *C, wmOperator *op, wmEvent *event) if (GPENCIL_SKETCH_SESSIONS_ON(p->scene)) { /* end stroke only, and then wait to resume painting soon */ //printf("\t\tGP - end stroke only\n"); - gp_paint_cleanup(p); - p->status= GP_STATUS_IDLING; + gpencil_stroke_end(op); /* we've just entered idling state, so this event was processed (but no others yet) */ estate = OPERATOR_RUNNING_MODAL; + + /* stroke could be smoothed, send notifier to refresh screen */ + ED_region_tag_redraw(p->ar); } else { //printf("\t\tGP - end of stroke + op\n"); @@ -1693,35 +1752,19 @@ static int gpencil_draw_modal (bContext *C, wmOperator *op, wmEvent *event) estate = OPERATOR_FINISHED; } } - else { + else if (event->val == KM_PRESS) { /* not painting, so start stroke (this should be mouse-button down) */ - /* we must check that we're still within the area that we're set up to work from - * otherwise we could crash (see bug #20586) - */ - if (CTX_wm_area(C) != p->sa) { - //printf("\t\t\tGP - wrong area execution abort! \n"); - p->status= GP_STATUS_ERROR; + p= gpencil_stroke_begin(C, op); + + if (p->status == GP_STATUS_ERROR) { estate = OPERATOR_CANCELLED; } - else { - //printf("\t\tGP - start stroke \n"); - p->status= GP_STATUS_PAINTING; - - /* we may need to set up paint env again if we're resuming */ - // XXX: watch it with the paintmode! in future, it'd be nice to allow changing paint-mode when in sketching-sessions - // XXX: with tablet events, we may event want to check for eraser here, for nicer tablet support - gp_paint_initstroke(p, p->paintmode); - - if (p->status == GP_STATUS_ERROR) { - estate = OPERATOR_CANCELLED; - } - } + } else { + p->status = GP_STATUS_IDLING; } } - - /* handle mode-specific events */ if (p->status == GP_STATUS_PAINTING) { /* handle painting mouse-movements? */ @@ -1733,7 +1776,7 @@ static int gpencil_draw_modal (bContext *C, wmOperator *op, wmEvent *event) /* finish painting operation if anything went wrong just now */ if (p->status == GP_STATUS_ERROR) { - //printf("\t\t\t\tGP - add error done! \n"); + printf("\t\t\t\tGP - add error done! \n"); estate = OPERATOR_CANCELLED; } else { @@ -1750,28 +1793,6 @@ static int gpencil_draw_modal (bContext *C, wmOperator *op, wmEvent *event) estate = OPERATOR_RUNNING_MODAL; } } - else if (p->status == GP_STATUS_IDLING) { - /* standard undo/redo shouldn't be allowed to execute or else it causes crashes, so catch it here */ - // FIXME: this is a hardcoded hotkey that can't be changed - // TODO: catch redo as well, but how? - if (event->type == ZKEY && event->val == KM_RELEASE) { - /* oskey = cmd key on macs as they seem to use cmd-z for undo as well? */ - if ((event->ctrl) || (event->oskey)) { - /* just delete last stroke, which will look like undo to the end user */ - //printf("caught attempted undo event... deleting last stroke \n"); - gpencil_frame_delete_laststroke(p->gpl, p->gpf); - /* undoing the last line can free p->gpf - * note, could do this in a bit more of an elegant way then a search but it at least prevents a crash */ - if(BLI_findindex(&p->gpl->frames, p->gpf) == -1) { - p->gpf= NULL; - } - - /* event handled, so force refresh */ - ED_region_tag_redraw(p->ar); /* just active area for now, since doing whole screen is too slow */ - estate = OPERATOR_RUNNING_MODAL; - } - } - } /* gpencil modal operator stores area, which can be removed while using it (like fullscreen) */ if(0==gpencil_area_exists(C, p->sa)) diff --git a/source/blender/editors/gpencil/gpencil_undo.c b/source/blender/editors/gpencil/gpencil_undo.c new file mode 100644 index 00000000000..bd337a1c7e1 --- /dev/null +++ b/source/blender/editors/gpencil/gpencil_undo.c @@ -0,0 +1,142 @@ +/* + * $Id$ + * + * ***** 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. + * + * The Original Code is Copyright (C) 2011 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation, + * Sergey Sharybin + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_gpencil_types.h" +#include "DNA_listBase.h" +#include "DNA_windowmanager_types.h" + +#include "BKE_context.h" +#include "BKE_gpencil.h" + +#include "BLI_listbase.h" + +#include "ED_gpencil.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "gpencil_intern.h" + +typedef struct bGPundonode { + struct bGPundonode *next, *prev; + + struct bGPdata *gpd; +} bGPundonode; + +static ListBase undo_nodes = {NULL, NULL}; +static bGPundonode *cur_node = NULL; + +int ED_undo_gpencil_step(bContext *C, int step, const char *name) +{ + bGPdata **gpd_ptr= NULL, *new_gpd= NULL; + PointerRNA ptr; + + if(name) /* currently unsupported */ + return OPERATOR_CANCELLED; + + gpd_ptr= gpencil_data_get_pointers(C, NULL); + + (void) step; + + if(step==1) { /* undo */ + //printf("\t\tGP - undo step\n"); + if(cur_node->prev) { + cur_node= cur_node->prev; + new_gpd= cur_node->gpd; + } + } + else if (step==-1) { + //printf("\t\tGP - redo step\n"); + if(cur_node->next) { + cur_node= cur_node->next; + new_gpd= cur_node->gpd; + } + } + + if(new_gpd) { + if(gpd_ptr) { + if(*gpd_ptr) { + bGPdata *gpd= *gpd_ptr; + bGPDlayer *gpl, *gpld; + + free_gpencil_layers(&gpd->layers); + + /* copy layers */ + gpd->layers.first= gpd->layers.last= NULL; + + for (gpl= new_gpd->layers.first; gpl; gpl= gpl->next) { + /* make a copy of source layer and its data */ + gpld= gpencil_layer_duplicate(gpl); + BLI_addtail(&gpd->layers, gpld); + } + } + } + } + + WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL|NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void gpencil_undo_init(bGPdata *gpd) +{ + gpencil_undo_push(gpd); +} + +void gpencil_undo_push(bGPdata *gpd) +{ + bGPundonode *undo_node= MEM_callocN(sizeof(bGPundonode), "gpencil undo node"); + + //printf("\t\tGP - undo push\n"); + + undo_node->gpd= gpencil_data_duplicate(gpd); + cur_node= undo_node; + + BLI_addtail(&undo_nodes, undo_node); +} + +void gpencil_undo_finish(void) +{ + bGPundonode *undo_node= undo_nodes.first; + + while (undo_node) { + free_gpencil_data(undo_node->gpd); + MEM_freeN(undo_node->gpd); + + undo_node= undo_node->next; + } + + BLI_freelistN(&undo_nodes); + + cur_node= NULL; +} diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h index e5715316a31..dc25317e4f3 100644 --- a/source/blender/editors/include/ED_gpencil.h +++ b/source/blender/editors/include/ED_gpencil.h @@ -108,4 +108,7 @@ void paste_gpdata(void); void snap_gplayer_frames(struct bGPDlayer *gpl, short mode); void mirror_gplayer_frames(struct bGPDlayer *gpl, short mode); +/* ------------ Grease-Pencil Undo System ------------------ */ +int ED_undo_gpencil_step(struct bContext *C, int step, const char *name); + #endif /* ED_GPENCIL_H */ diff --git a/source/blender/editors/util/undo.c b/source/blender/editors/util/undo.c index a2381a208ef..503b11b9c71 100644 --- a/source/blender/editors/util/undo.c +++ b/source/blender/editors/util/undo.c @@ -54,6 +54,7 @@ #include "ED_armature.h" #include "ED_particle.h" #include "ED_curve.h" +#include "ED_gpencil.h" #include "ED_mball.h" #include "ED_mesh.h" #include "ED_object.h" @@ -126,6 +127,10 @@ static int ed_undo_step(bContext *C, int step, const char *undoname) Object *obact= CTX_data_active_object(C); ScrArea *sa= CTX_wm_area(C); + if(G.f & G_GREASEPENCIL) { + return ED_undo_gpencil_step(C, step, undoname); + } + if(sa && sa->spacetype==SPACE_IMAGE) { SpaceImage *sima= (SpaceImage *)sa->spacedata.first;