Commit: 235941fe91daf5efc322d75a5e1f175071a23d06 Author: Falk David Date: Thu Feb 3 12:30:07 2022 +0100 Branches: greasepencil-object https://developer.blender.org/rB235941fe91daf5efc322d75a5e1f175071a23d06
Merge branch 'patch/gpencil-update-on-write' into patch/gpencil-undo-system =================================================================== =================================================================== diff --cc source/blender/editors/gpencil/gpencil_undo.c index bf9d9d4ee80,ec70febc80c..86b1e5e9f7c --- a/source/blender/editors/gpencil/gpencil_undo.c +++ b/source/blender/editors/gpencil/gpencil_undo.c @@@ -192,418 -190,3 +192,418 @@@ void gpencil_undo_finish(void cur_node = NULL; } + +/* -------------------------------------------------------------------- */ +/** \name Implements ED Undo System + * \{ */ + +typedef struct GPencilUndoData { + GPencilUpdateCache *gpd_cache_data; + /* Scene frame for this step. */ + int cfra; + /* Store the grease pencil mode we are in. */ + eObjectMode mode; +} GPencilUndoData; + +typedef struct GPencilUndoStep { + UndoStep step; + GPencilUndoData *undo_data; +} GPencilUndoStep; + +static bool change_gpencil_mode(bContext *C, Object *ob, eObjectMode mode) +{ + if (ob->mode == mode) { + return false; + } + bGPdata *gpd = (bGPdata *)ob->data; + ob->mode = mode; + ED_gpencil_setup_modes(C, gpd, mode); + + return true; +} + +static void gpencil_data_to_undo_data(bGPdata *gpd, GPencilUndoData *gpd_undo_data) +{ + GPencilUpdateCache *update_cache = gpd->runtime.update_cache; + + if (update_cache == NULL) { + /* Need a full-copy of the grease pencil undo_data. */ + bGPdata *gpd_copy = NULL; + BKE_gpencil_data_duplicate(NULL, gpd, &gpd_copy); + gpd_copy->id.session_uuid = gpd->id.session_uuid; + + gpd_undo_data->gpd_cache_data = BKE_gpencil_create_update_cache(gpd_copy, true); + } + else { + gpd_undo_data->gpd_cache_data = BKE_gpencil_duplicate_update_cache_and_data(update_cache); + } +} + +typedef struct tGPencilUpdateCacheUndoTraverseData { + bGPdata *gpd; + bGPDlayer *gpl; + bGPDframe *gpf; + bGPDstroke *gps; + int gpl_index; + int gpf_index; + int gps_index; + bool tag_update_cache; +} tGPencilUpdateCacheUndoTraverseData; + +static bool gpencil_decode_undo_data_layer_cb(GPencilUpdateCache *gpl_cache, void *user_data) +{ + tGPencilUpdateCacheUndoTraverseData *td = (tGPencilUpdateCacheUndoTraverseData *)user_data; + td->gpl = BLI_findlinkfrom((Link *)td->gpl, gpl_cache->index - td->gpl_index); + td->gpl_index = gpl_cache->index; + bGPDlayer *gpl_new = (bGPDlayer *)gpl_cache->data; + + if (gpl_cache->flag == GP_UPDATE_NODE_FULL_COPY) { + /* Do a full copy of the layer. */ + bGPDlayer *gpl_next = td->gpl->next; + BKE_gpencil_layer_delete(td->gpd, td->gpl); + + td->gpl = BKE_gpencil_layer_duplicate(gpl_new, true, true); + BLI_insertlinkbefore(&td->gpd->layers, gpl_next, td->gpl); + + if (td->tag_update_cache) { + /* Tag the layer here. */ + BKE_gpencil_tag_full_update(td->gpd, td->gpl, NULL, NULL); + } + return true; + } + else if (gpl_cache->flag == GP_UPDATE_NODE_LIGHT_COPY) { + BKE_gpencil_layer_copy_settings(gpl_new, td->gpl); + if (td->tag_update_cache) { + BKE_gpencil_tag_light_update(td->gpd, td->gpl, NULL, NULL); + } + } + + td->gpf = td->gpl->frames.first; + td->gpf_index = 0; + return false; +} + +static bool gpencil_decode_undo_data_frame_cb(GPencilUpdateCache *gpf_cache, void *user_data) +{ + tGPencilUpdateCacheUndoTraverseData *td = (tGPencilUpdateCacheUndoTraverseData *)user_data; + td->gpf = BLI_findlinkfrom((Link *)td->gpf, gpf_cache->index - td->gpf_index); + td->gpf_index = gpf_cache->index; + bGPDframe *gpf_new = (bGPDframe *)gpf_cache->data; + + if (gpf_cache->flag == GP_UPDATE_NODE_FULL_COPY) { + /* Do a full copy of the frame. */ + bGPDframe *gpf_next = td->gpf->next; + + bool update_actframe = (td->gpl->actframe == td->gpf) ? true : false; + BKE_gpencil_free_strokes(td->gpf); + BLI_freelinkN(&td->gpl->frames, td->gpf); + + td->gpf = BKE_gpencil_frame_duplicate(gpf_new, true); + BLI_insertlinkbefore(&td->gpl->frames, gpf_next, td->gpf); + + if (update_actframe) { + td->gpl->actframe = td->gpf; + } + if (td->tag_update_cache) { + /* Tag the frame here. */ + BKE_gpencil_tag_full_update(td->gpd, td->gpl, td->gpf, NULL); + } + return true; + } + else if (gpf_cache->flag == GP_UPDATE_NODE_LIGHT_COPY) { + BKE_gpencil_frame_copy_settings(gpf_new, td->gpf); + if (td->tag_update_cache) { + BKE_gpencil_tag_light_update(td->gpd, td->gpl, td->gpf, NULL); + } + } + + td->gps = td->gpf->strokes.first; + td->gps_index = 0; + return false; +} + +static bool gpencil_decode_undo_data_stroke_cb(GPencilUpdateCache *gps_cache, void *user_data) +{ + tGPencilUpdateCacheUndoTraverseData *td = (tGPencilUpdateCacheUndoTraverseData *)user_data; + td->gps = BLI_findlinkfrom((Link *)td->gps, gps_cache->index - td->gps_index); + td->gps_index = gps_cache->index; + bGPDstroke *gps_new = (bGPDstroke *)gps_cache->data; + + if (gps_cache->flag == GP_UPDATE_NODE_FULL_COPY) { + /* Do a full copy of the stroke. */ + bGPDstroke *gps_next = td->gps->next; + + BLI_remlink(&td->gpf->strokes, td->gps); + BKE_gpencil_free_stroke(td->gps); + + td->gps = BKE_gpencil_stroke_duplicate(gps_new, true, true); + BLI_insertlinkbefore(&td->gpf->strokes, gps_next, td->gps); + + if (td->tag_update_cache) { + /* Tag the stroke here. */ + BKE_gpencil_tag_full_update(td->gpd, td->gpl, td->gpf, td->gps); + } + } + else if (gps_cache->flag == GP_UPDATE_NODE_LIGHT_COPY) { + BKE_gpencil_stroke_copy_settings(gps_new, td->gps); + if (td->tag_update_cache) { + BKE_gpencil_tag_light_update(td->gpd, td->gpl, td->gpf, td->gps); + } + } + return false; +} + +static bool gpencil_undo_data_to_gpencil_data(GPencilUndoData *gpd_undo_data, + bGPdata *gpd, + bool tag_gpd_update_cache) +{ + GPencilUpdateCache *update_cache = gpd_undo_data->gpd_cache_data; + + BLI_assert(update_cache != NULL); + + if (update_cache->flag == GP_UPDATE_NODE_FULL_COPY) { + /* Full-copy. */ + BKE_gpencil_free_data(gpd, true); + BKE_gpencil_data_duplicate(NULL, update_cache->data, &gpd); + if (tag_gpd_update_cache) { + BKE_gpencil_tag_full_update(gpd, NULL, NULL, NULL); + } + return true; + } + else if (update_cache->flag == GP_UPDATE_NODE_LIGHT_COPY) { + BKE_gpencil_data_copy_settings(update_cache->data, gpd); + if (tag_gpd_update_cache) { + BKE_gpencil_tag_light_update(gpd, NULL, NULL, NULL); + } + } + + GPencilUpdateCacheTraverseSettings ts = {{ + gpencil_decode_undo_data_layer_cb, + gpencil_decode_undo_data_frame_cb, + gpencil_decode_undo_data_stroke_cb, + }}; + + tGPencilUpdateCacheUndoTraverseData data = { + .gpd = gpd, + .gpl = gpd->layers.first, + .gpf = NULL, + .gps = NULL, + .gpl_index = 0, + .gpf_index = 0, + .gps_index = 0, + .tag_update_cache = tag_gpd_update_cache, + }; + + BKE_gpencil_traverse_update_cache(update_cache, &ts, &data); + + return true; +} + +static bool gpencil_undosys_poll(bContext *C) +{ + if (!U.experimental.use_gpencil_undo_system) { + return false; + } + bGPdata *gpd = ED_gpencil_data_get_active(C); + return GPENCIL_ANY_MODE(gpd); +} + +static bool gpencil_undosys_step_encode(struct bContext *C, + struct Main *UNUSED(bmain), + UndoStep *us_p) +{ + GPencilUndoStep *us = (GPencilUndoStep *)us_p; + + UndoStack *undo_stack = ED_undo_stack_get(); + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = (bGPdata *)ob->data; + + bool only_frame_changed = false; + + /* In case the step we are about to encode would be the first in the gpencil undo system, ensure + * that we do a full-copy. */ + if (undo_stack->step_active == NULL || + undo_stack->step_active->type != BKE_UNDOSYS_TYPE_GPENCIL) { + BKE_gpencil_tag_full_update(gpd, NULL, NULL, NULL); + } + /* If the ID of the grease pencil object was not tagged or the update cache is empty, we assume + * the data hasn't changed. */ + else if ((gpd->id.recalc & ID_RECALC_ALL) == 0 && gpd->runtime.update_cache == NULL) { + /* If the previous step is of our undo system, check if the frame changed. */ + if (undo_stack->step_active && undo_stack->step_active->type == BKE_UNDOSYS_TYPE_GPENCIL) { + GPencilUndoStep *us_prev = (GPencilUndoStep *)undo_stack->step_active; + /* We want to be able to undo frame changes, so check this here. */ + only_frame_changed = us_prev->undo_data->cfra != scene->r.cfra; + if (!only_frame_changed) { + /* If the frame did not change, we don't need to encode anything, return. */ + return false; + } + } + else { + /* No change (that we want to undo) happend, return. */ + return false; + } + } + + /* TODO: Handle this case properly once the update cache is more widly used. We avoid full-copies + * for now at the expense of to being able to undo them. */ +#if 1 + if (!only_frame_changed && gpd->runtime.update_cache == NULL) { + return false; + } +#endif + + us->undo_data = MEM_callocN(sizeof(GPencilUndoData), __func__); + us->undo_data->cfra = scene->r.cfra; + us->undo_data->mode = ob->mode; + + /* If that step only encodes a frame change (data itself has not changed), return early. */ + if (only_frame_changed) { + return true; + } + + gpencil_data_to_undo_data(gpd, us->undo_data); + gpd->flag |= GP_DATA_UPDATE_CACHE_UNDO_ENCODED; + return true; +} + +static void gpencil_undosys_step_decode(struct bContext *C, + struct Main *UNUSED(bmain), + UndoStep *us_p, + const eUndoStepDir dir, + bool UNUSED(is_final)) +{ + GPencilUndoStep *us = (GPencilUndoStep *)us_p; + GPencilUndoData *undo_data = us->undo_data; + + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = (bGPdata *)ob->data; + + if (gpd == NULL) @@ Diff output truncated at 10240 characters. @@ _______________________________________________ Bf-blender-cvs mailing list Bf-blender-cvs@blender.org List details, subscription details or unsubscribe: https://lists.blender.org/mailman/listinfo/bf-blender-cvs