diff options
-rw-r--r-- | release/scripts/startup/bl_ui/space_userpref.py | 39 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_gpencil.h | 11 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_gpencil_update_cache.h | 20 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_object.h | 1 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_undo_system.h | 1 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/gpencil.c | 43 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/gpencil_update_cache.c | 253 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/undo_system.c | 1 | ||||
-rw-r--r-- | source/blender/blenlib/intern/DLRB_tree.c | 2 | ||||
-rw-r--r-- | source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc | 3 | ||||
-rw-r--r-- | source/blender/editors/gpencil/gpencil_paint.c | 3 | ||||
-rw-r--r-- | source/blender/editors/gpencil/gpencil_undo.c | 409 | ||||
-rw-r--r-- | source/blender/editors/include/ED_gpencil.h | 3 | ||||
-rw-r--r-- | source/blender/editors/undo/undo_system_types.c | 3 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_gpencil_types.h | 2 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_userdef_types.h | 2 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_userdef.c | 31 |
17 files changed, 773 insertions, 54 deletions
diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index bd92d0d2ff9..a6304211f8a 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -2241,11 +2241,14 @@ class ExperimentalPanel: layout.use_property_split = False layout.use_property_decorate = False - for prop_keywords, reference in items: + for prop_keywords, reference, poll_function in items: split = layout.split(factor=0.66) col = split.split() col.prop(experimental, **prop_keywords) + if poll_function: + col.enabled = poll_function(context) + if reference: if type(reference) is tuple: url_ext = reference[0] @@ -2268,8 +2271,8 @@ class USERPREF_PT_experimental_virtual_reality(ExperimentalPanel, Panel): def draw(self, context): self._draw_items( context, ( - ({"property": "use_virtual_reality_scene_inspection"}, "T71347"), - ({"property": "use_virtual_reality_immersive_drawing"}, "T71348"), + ({"property": "use_virtual_reality_scene_inspection"}, "T71347", None), + ({"property": "use_virtual_reality_immersive_drawing"}, "T71348", None), ) ) """ @@ -2281,10 +2284,10 @@ class USERPREF_PT_experimental_new_features(ExperimentalPanel, Panel): def draw(self, context): self._draw_items( context, ( - ({"property": "use_sculpt_vertex_colors"}, "T71947"), - ({"property": "use_sculpt_tools_tilt"}, "T82877"), - ({"property": "use_extended_asset_browser"}, ("project/view/130/", "Project Page")), - ({"property": "use_override_templates"}, ("T73318", "Milestone 4")), + ({"property": "use_sculpt_vertex_colors"}, "T71947", None), + ({"property": "use_sculpt_tools_tilt"}, "T82877", None), + ({"property": "use_extended_asset_browser"}, ("project/view/130/", "Project Page"), None), + ({"property": "use_override_templates"}, ("T73318", "Milestone 4"), None), ), ) @@ -2295,9 +2298,9 @@ class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel): def draw(self, context): self._draw_items( context, ( - ({"property": "use_new_hair_type"}, "T68981"), - ({"property": "use_new_point_cloud_type"}, "T75717"), - ({"property": "use_full_frame_compositor"}, "T88150"), + ({"property": "use_new_hair_type"}, "T68981", None), + ({"property": "use_new_point_cloud_type"}, "T75717", None), + ({"property": "use_full_frame_compositor"}, "T88150", None), ), ) @@ -2314,13 +2317,15 @@ class USERPREF_PT_experimental_debugging(ExperimentalPanel, Panel): def draw(self, context): self._draw_items( context, ( - ({"property": "use_undo_legacy"}, "T60695"), - ({"property": "override_auto_resync"}, "T83811"), - ({"property": "use_cycles_debug"}, None), - ({"property": "use_geometry_nodes_legacy"}, "T91274"), - ({"property": "show_asset_debug_info"}, None), - ({"property": "use_asset_indexing"}, None), - ({"property": "use_gpencil_update_cache"}, "T95401"), + ({"property": "use_undo_legacy"}, "T60695", None), + ({"property": "override_auto_resync"}, "T83811", None), + ({"property": "use_cycles_debug"}, None, None), + ({"property": "use_geometry_nodes_legacy"}, "T91274", None), + ({"property": "show_asset_debug_info"}, None, None), + ({"property": "use_asset_indexing"}, None, None), + ({"property": "use_gpencil_update_cache"}, "T95401", None), + # Disabled if use_gpencil_update_cache is disabled + ({"property": "use_gpencil_undo_system"}, "TODO", lambda ctx: ctx.preferences.experimental.use_gpencil_update_cache), ), ) diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h index 4aec1e684af..6bd7ba255cf 100644 --- a/source/blender/blenkernel/BKE_gpencil.h +++ b/source/blender/blenkernel/BKE_gpencil.h @@ -222,12 +222,13 @@ struct bGPDstroke *BKE_gpencil_stroke_duplicate(struct bGPDstroke *gps_src, /** * Make a copy of a given gpencil data-block. - * - * XXX: Should this be deprecated? + * \param bmain: Main structure. Can be NULL which will make a localized copy of the data-block + * outside of bmain. + * \param gpd: The grease pencil data-block to duplicate. */ -struct bGPdata *BKE_gpencil_data_duplicate(struct Main *bmain, - const struct bGPdata *gpd, - bool internal_copy); +void BKE_gpencil_data_duplicate(struct Main *bmain, + const struct bGPdata *gpd, + struct bGPdata **gpd_dst); /** * Delete the last stroke of the given frame. diff --git a/source/blender/blenkernel/BKE_gpencil_update_cache.h b/source/blender/blenkernel/BKE_gpencil_update_cache.h index 8c833f6091d..074b7b44635 100644 --- a/source/blender/blenkernel/BKE_gpencil_update_cache.h +++ b/source/blender/blenkernel/BKE_gpencil_update_cache.h @@ -140,11 +140,31 @@ void BKE_gpencil_tag_light_update(struct bGPdata *gpd, struct bGPDstroke *gps); /** + * + */ +GPencilUpdateCache *BKE_gpencil_duplicate_update_cache_and_data(GPencilUpdateCache *gpd_cache); + +/** + * + */ +bool BKE_gpencil_compare_update_caches(GPencilUpdateCache *cache_a, GPencilUpdateCache *cache_b); + +/** * Frees the GPencilUpdateCache on the gpd->runtime. This will not free the data that the cache * node might point to. It assumes that the cache does not own the data. */ void BKE_gpencil_free_update_cache(struct bGPdata *gpd); +/** + * + */ +void BKE_gpencil_free_update_cache_and_data(GPencilUpdateCache *cache); + +/** + * + */ +void BKE_gpencil_print_update_cache(struct bGPdata *gpd); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index 99758f4ad78..a4a9b5fc5ac 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -167,6 +167,7 @@ bool BKE_object_has_mode_data(const struct Object *ob, eObjectMode object_mode); bool BKE_object_is_mode_compat(const struct Object *ob, eObjectMode object_mode); bool BKE_object_data_is_in_editmode(const struct ID *id); +bool BKE_object_data_is_in_paint_mode(const struct ID *id); char *BKE_object_data_editmode_flush_ptr_get(struct ID *id); diff --git a/source/blender/blenkernel/BKE_undo_system.h b/source/blender/blenkernel/BKE_undo_system.h index 88a9ac9d0bf..859051e8a3f 100644 --- a/source/blender/blenkernel/BKE_undo_system.h +++ b/source/blender/blenkernel/BKE_undo_system.h @@ -183,6 +183,7 @@ extern const UndoType *BKE_UNDOSYS_TYPE_PAINTCURVE; extern const UndoType *BKE_UNDOSYS_TYPE_PARTICLE; extern const UndoType *BKE_UNDOSYS_TYPE_SCULPT; extern const UndoType *BKE_UNDOSYS_TYPE_TEXT; +extern const UndoType *BKE_UNDOSYS_TYPE_GPENCIL; /** \} */ diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index b9ebd19a617..68d3c7d1350 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -65,6 +65,7 @@ #include "BKE_material.h" #include "BKE_paint.h" +#include "BLI_dlrbTree.h" #include "BLI_math_color.h" #include "DEG_depsgraph_query.h" @@ -1075,33 +1076,29 @@ void BKE_gpencil_stroke_copy_settings(const bGPDstroke *gps_src, bGPDstroke *gps copy_v4_v4(gps_dst->vert_color_fill, gps_src->vert_color_fill); } -bGPdata *BKE_gpencil_data_duplicate(Main *bmain, const bGPdata *gpd_src, bool internal_copy) +void BKE_gpencil_data_duplicate(Main *bmain, const bGPdata *gpd_src, bGPdata **gpd_dst) { - bGPdata *gpd_dst; - - /* Yuck and super-uber-hyper yuck!!! - * Should be replaceable with a no-main copy (LIB_ID_COPY_NO_MAIN etc.), but not sure about it, - * so for now keep old code for that one. */ - /* error checking */ if (gpd_src == NULL) { - return NULL; + return; } - if (internal_copy) { - /* make a straight copy for undo buffers used during stroke drawing */ - gpd_dst = MEM_dupallocN(gpd_src); + bGPdata *gpd_new = *gpd_dst; + + if (bmain == NULL) { + if (gpd_new == NULL) { + *gpd_dst = MEM_dupallocN(gpd_src); + gpd_new = *gpd_dst; + } + else { + *gpd_new = *gpd_src; + } + greasepencil_copy_data(NULL, (ID *)gpd_new, (ID *)gpd_src, 0); + gpd_new->runtime.update_cache = NULL; } else { - BLI_assert(bmain != NULL); - gpd_dst = (bGPdata *)BKE_id_copy(bmain, &gpd_src->id); + *gpd_dst = (bGPdata *)BKE_id_copy(bmain, &gpd_src->id); } - - /* Copy internal data (layers, etc.) */ - greasepencil_copy_data(bmain, &gpd_dst->id, &gpd_src->id, 0); - - /* return new */ - return gpd_dst; } /* ************************************************** */ @@ -1619,6 +1616,7 @@ void BKE_gpencil_layer_active_set(bGPdata *gpd, bGPDlayer *active) if (gpd->flag & GP_DATA_AUTOLOCK_LAYERS) { gpl->flag |= GP_LAYER_LOCKED; } + BKE_gpencil_tag_light_update(gpd, gpl, NULL, NULL); } /* set as active one */ @@ -1644,6 +1642,7 @@ void BKE_gpencil_layer_autolock_set(bGPdata *gpd, const bool unlock) else { gpl->flag |= GP_LAYER_LOCKED; } + BKE_gpencil_tag_light_update(gpd, gpl, NULL, NULL); } } else { @@ -1653,6 +1652,7 @@ void BKE_gpencil_layer_autolock_set(bGPdata *gpd, const bool unlock) if (unlock) { LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { gpl->flag &= ~GP_LAYER_LOCKED; + BKE_gpencil_tag_light_update(gpd, gpl, NULL, NULL); } } } @@ -3007,7 +3007,10 @@ void BKE_gpencil_update_on_write(bGPdata *gpd_orig, bGPdata *gpd_eval) gpd_eval->flag |= GP_DATA_CACHE_IS_DIRTY; /* TODO: This might cause issues when we have multiple depsgraphs? */ - BKE_gpencil_free_update_cache(gpd_orig); + if ((gpd_orig->flag & GP_DATA_UPDATE_CACHE_UNDO_ENCODED) || + !U.experimental.use_gpencil_undo_system) { + BKE_gpencil_free_update_cache(gpd_orig); + } } /** \} */ diff --git a/source/blender/blenkernel/intern/gpencil_update_cache.c b/source/blender/blenkernel/intern/gpencil_update_cache.c index bcb827ad9ae..82205716f59 100644 --- a/source/blender/blenkernel/intern/gpencil_update_cache.c +++ b/source/blender/blenkernel/intern/gpencil_update_cache.c @@ -167,8 +167,11 @@ static void update_cache_node_create_ex(GPencilUpdateCache *root_cache, } GPencilUpdateCache *gps_cache = update_cache_alloc(gps_index, node_flag, (bGPDstroke *)data); - BLI_dlrbTree_add( - gpf_node->cache->children, cache_node_compare, cache_node_alloc, cache_node_update, gps_cache); + BLI_dlrbTree_add(gpf_node->cache->children, + cache_node_compare, + cache_node_alloc, + cache_node_update, + gps_cache); BLI_dlrbTree_linkedlist_sync(gpf_node->cache->children); } @@ -180,6 +183,10 @@ static void update_cache_node_create( return; } + if (gpd->flag & GP_DATA_UPDATE_CACHE_UNDO_ENCODED) { + BKE_gpencil_free_update_cache(gpd); + } + GPencilUpdateCache *root_cache = gpd->runtime.update_cache; if (root_cache == NULL) { gpd->runtime.update_cache = update_cache_alloc(0, GP_UPDATE_NODE_NO_COPY, NULL); @@ -234,6 +241,143 @@ static void gpencil_traverse_update_cache_ex(GPencilUpdateCache *parent_cache, } } +typedef struct GPencilUpdateCacheDuplicateTraverseData { + GPencilUpdateCache *new_cache; + int gpl_index; + int gpf_index; +} GPencilUpdateCacheDuplicateTraverseData; + +static bool gpencil_duplicate_update_cache_layer_cb(GPencilUpdateCache *cache, void *user_data) +{ + GPencilUpdateCacheDuplicateTraverseData *td = (GPencilUpdateCacheDuplicateTraverseData *) + user_data; + + if (cache->flag == GP_UPDATE_NODE_FULL_COPY) { + /* Do a full copy of the layer. */ + bGPDlayer *gpl = (bGPDlayer *)cache->data; + bGPDlayer *gpl_new = BKE_gpencil_layer_duplicate(gpl, true, true); + update_cache_node_create_ex(td->new_cache, gpl_new, cache->index, -1, -1, true); + return true; + } + else if (cache->flag == GP_UPDATE_NODE_LIGHT_COPY) { + bGPDlayer *gpl = (bGPDlayer *)cache->data; + bGPDlayer *gpl_new = (bGPDlayer *)MEM_dupallocN(gpl); + + gpl_new->prev = gpl_new->next = NULL; + BLI_listbase_clear(&gpl_new->frames); + BLI_listbase_clear(&gpl_new->mask_layers); + update_cache_node_create_ex(td->new_cache, gpl_new, cache->index, -1, -1, false); + } + td->gpl_index = cache->index; + return false; +} + +static bool gpencil_duplicate_update_cache_frame_cb(GPencilUpdateCache *cache, void *user_data) +{ + GPencilUpdateCacheDuplicateTraverseData *td = (GPencilUpdateCacheDuplicateTraverseData *) + user_data; + if (cache->flag == GP_UPDATE_NODE_FULL_COPY) { + bGPDframe *gpf = (bGPDframe *)cache->data; + bGPDframe *gpf_new = BKE_gpencil_frame_duplicate(gpf, true); + update_cache_node_create_ex(td->new_cache, gpf_new, td->gpl_index, cache->index, -1, true); + return true; + } + else if (cache->flag == GP_UPDATE_NODE_LIGHT_COPY) { + bGPDframe *gpf = (bGPDframe *)cache->data; + bGPDframe *gpf_new = MEM_dupallocN(gpf); + gpf_new->prev = gpf_new->next = NULL; + BLI_listbase_clear(&gpf_new->strokes); + update_cache_node_create_ex(td->new_cache, gpf_new, td->gpl_index, cache->index, -1, false); + } + td->gpf_index = cache->index; + return false; +} + +static bool gpencil_duplicate_update_cache_stroke_cb(GPencilUpdateCache *cache, void *user_data) +{ + GPencilUpdateCacheDuplicateTraverseData *td = (GPencilUpdateCacheDuplicateTraverseData *) + user_data; + + if (cache->flag == GP_UPDATE_NODE_FULL_COPY) { + bGPDstroke *gps = (bGPDstroke *)cache->data; + bGPDstroke *gps_new = BKE_gpencil_stroke_duplicate(gps, true, true); + update_cache_node_create_ex( + td->new_cache, gps_new, td->gpl_index, td->gpf_index, cache->index, true); + } + else if (cache->flag == GP_UPDATE_NODE_LIGHT_COPY) { + bGPDstroke *gps = (bGPDstroke *)cache->data; + bGPDstroke *gps_new = MEM_dupallocN(gps); + + gps_new->prev = gps_new->next = NULL; + gps_new->points = NULL; + gps_new->triangles = NULL; + gps_new->dvert = NULL; + gps_new->editcurve = NULL; + + update_cache_node_create_ex( + td->new_cache, gps_new, td->gpl_index, td->gpf_index, cache->index, false); + } + return true; +} + +static bool gpencil_free_update_cache_layer_cb(GPencilUpdateCache *cache, void *UNUSED(user_data)) +{ + if (cache->flag == GP_UPDATE_NODE_FULL_COPY) { + BKE_gpencil_free_frames(cache->data); + BKE_gpencil_free_layer_masks(cache->data); + } + if (cache->data) { + MEM_freeN(cache->data); + } + return cache->flag == GP_UPDATE_NODE_FULL_COPY; +} + +static bool gpencil_free_update_cache_frame_cb(GPencilUpdateCache *cache, void *UNUSED(user_data)) +{ + if (cache->flag == GP_UPDATE_NODE_FULL_COPY) { + BKE_gpencil_free_strokes(cache->data); + } + if (cache->data) { + MEM_freeN(cache->data); + } + return cache->flag == GP_UPDATE_NODE_FULL_COPY; +} + +static bool gpencil_free_update_cache_stroke_cb(GPencilUpdateCache *cache, void *UNUSED(user_data)) +{ + if (cache->flag == GP_UPDATE_NODE_FULL_COPY) { + BKE_gpencil_free_stroke(cache->data); + } + return cache->flag == GP_UPDATE_NODE_FULL_COPY; +} + +static bool gpencil_print_update_cache_layer_cb(GPencilUpdateCache *cache, void *UNUSED(user_data)) +{ + printf(" - Layer: %s | Index: %d | Flag: %d | Tagged Frames: %d\n", + (cache->data ? ((bGPDlayer *)cache->data)->info : "N/A"), + cache->index, + cache->flag, + BLI_listbase_count((ListBase *)cache->children)); + return cache->flag == GP_UPDATE_NODE_FULL_COPY; +} + +static bool gpencil_print_update_cache_frame_cb(GPencilUpdateCache *cache, void *UNUSED(user_data)) +{ + printf(" - Layer: %s | Index: %d | Flag: %d | Tagged Frames: %d\n", + (cache->data ? ((bGPDlayer *)cache->data)->info : "N/A"), + cache->index, + cache->flag, + BLI_listbase_count((ListBase *)cache->children)); + return cache->flag == GP_UPDATE_NODE_FULL_COPY; +} + +static bool gpencil_print_update_cache_stroke_cb(GPencilUpdateCache *cache, + void *UNUSED(user_data)) +{ + printf(" - Stroke Index: %d | | Flag: %d\n", cache->index, cache->flag); + return cache->flag == GP_UPDATE_NODE_FULL_COPY; +} + /* -------------------------------------------------------------------- */ /** \name Update Cache API * @@ -266,6 +410,70 @@ void BKE_gpencil_tag_light_update(bGPdata *gpd, bGPDlayer *gpl, bGPDframe *gpf, } } +GPencilUpdateCache *BKE_gpencil_duplicate_update_cache_and_data(GPencilUpdateCache *gpd_cache) +{ + GPencilUpdateCache *new_cache = update_cache_alloc(0, gpd_cache->flag, NULL); + bGPdata *gpd_new = NULL; + if (gpd_cache->flag == GP_UPDATE_NODE_FULL_COPY) { + BKE_gpencil_data_duplicate(NULL, gpd_cache->data, &gpd_new); + new_cache->data = gpd_new; + return new_cache; + } + else if (gpd_cache->flag == GP_UPDATE_NODE_LIGHT_COPY) { + gpd_new = MEM_dupallocN(gpd_cache->data); + + /* Clear all the pointers, since they shouldn't store anything. */ + BLI_listbase_clear(&gpd_new->layers); + BLI_listbase_clear(&gpd_new->vertex_group_names); + gpd_new->adt = NULL; + gpd_new->mat = NULL; + gpd_new->runtime.update_cache = NULL; + + new_cache->data = gpd_new; + } + + GPencilUpdateCacheTraverseSettings ts = {{gpencil_duplicate_update_cache_layer_cb, + gpencil_duplicate_update_cache_frame_cb, + gpencil_duplicate_update_cache_stroke_cb}}; + + GPencilUpdateCacheDuplicateTraverseData td = { + .new_cache = new_cache, + .gpl_index = -1, + .gpf_index = -1, + }; + + BKE_gpencil_traverse_update_cache(gpd_cache, &ts, &td); + return new_cache; +} + +/** + * Return true if any of the branches in gpd_cache_b are "strictly greater than" the branches in + * gpd_cache_a, e.g. one of them contains more data than their counterpart. + */ +bool BKE_gpencil_compare_update_caches(GPencilUpdateCache *gpd_cache_a, + GPencilUpdateCache *gpd_cache_b) +{ + if (gpd_cache_b->flag == GP_UPDATE_NODE_FULL_COPY) { + return gpd_cache_a->flag != GP_UPDATE_NODE_FULL_COPY; + } + if (gpd_cache_a->flag == GP_UPDATE_NODE_FULL_COPY) { + return false; + } + + LISTBASE_FOREACH (GPencilUpdateCacheNode *, node_b, gpd_cache_b->children) { + GPencilUpdateCacheNode *node_a = (GPencilUpdateCacheNode *)BLI_dlrbTree_search_exact( + gpd_cache_a->children, cache_node_compare, node_b->cache); + if (node_a == NULL) { + return true; + } + + if (BKE_gpencil_compare_update_caches(node_a->cache, node_b->cache)) { + return true; + } + } + return false; +} + void BKE_gpencil_free_update_cache(bGPdata *gpd) { GPencilUpdateCache *gpd_cache = gpd->runtime.update_cache; @@ -273,6 +481,47 @@ void BKE_gpencil_free_update_cache(bGPdata *gpd) update_cache_free(gpd_cache); gpd->runtime.update_cache = NULL; } + gpd->flag &= ~GP_DATA_UPDATE_CACHE_UNDO_ENCODED; +} + +void BKE_gpencil_free_update_cache_and_data(GPencilUpdateCache *gpd_cache) +{ + if (gpd_cache->data != NULL) { + if (gpd_cache->flag == GP_UPDATE_NODE_FULL_COPY) { + BKE_gpencil_free_data(gpd_cache->data, true); + MEM_freeN(gpd_cache->data); + update_cache_free(gpd_cache); + return; + } + MEM_freeN(gpd_cache->data); + } + + GPencilUpdateCacheTraverseSettings ts = {{gpencil_free_update_cache_layer_cb, + gpencil_free_update_cache_frame_cb, + gpencil_free_update_cache_stroke_cb}}; + + BKE_gpencil_traverse_update_cache(gpd_cache, &ts, NULL); + update_cache_free(gpd_cache); +} + +void BKE_gpencil_print_update_cache(bGPdata *gpd) +{ + GPencilUpdateCache *update_cache = gpd->runtime.update_cache; + + if (update_cache == NULL) { + printf("No update cache\n"); + return; + } + printf("Update Cache:\n"); + printf("- GPdata: %s | Flag: %d | Tagged Layers: %d\n", + gpd->id.name, + update_cache->flag, + BLI_listbase_count((ListBase *)update_cache->children)); + + GPencilUpdateCacheTraverseSettings ts = {{gpencil_print_update_cache_layer_cb, + gpencil_print_update_cache_frame_cb, + gpencil_print_update_cache_stroke_cb}}; + BKE_gpencil_traverse_update_cache(update_cache, &ts, NULL); } /** \} */ diff --git a/source/blender/blenkernel/intern/undo_system.c b/source/blender/blenkernel/intern/undo_system.c index 3e263fafe28..1c2ca445e78 100644 --- a/source/blender/blenkernel/intern/undo_system.c +++ b/source/blender/blenkernel/intern/undo_system.c @@ -70,6 +70,7 @@ const UndoType *BKE_UNDOSYS_TYPE_PAINTCURVE = NULL; const UndoType *BKE_UNDOSYS_TYPE_PARTICLE = NULL; const UndoType *BKE_UNDOSYS_TYPE_SCULPT = NULL; const UndoType *BKE_UNDOSYS_TYPE_TEXT = NULL; +const UndoType *BKE_UNDOSYS_TYPE_GPENCIL = NULL; static ListBase g_undo_types = {NULL, NULL}; diff --git a/source/blender/blenlib/intern/DLRB_tree.c b/source/blender/blenlib/intern/DLRB_tree.c index 9c22afeb893..85ff5a6e46f 100644 --- a/source/blender/blenlib/intern/DLRB_tree.c +++ b/source/blender/blenlib/intern/DLRB_tree.c @@ -74,7 +74,7 @@ void BLI_dlrbTree_free(DLRBT_Tree *tree, DLRBT_NFree_FP free_cb) if (tree->first) { /* free list */ if (free_cb) { - LISTBASE_FOREACH_MUTABLE(DLRBT_Node *, node, tree) { + LISTBASE_FOREACH_MUTABLE (DLRBT_Node *, node, tree) { free_cb(node); } BLI_listbase_clear((ListBase *)tree); diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc index ba4855fa713..b0eae8a50f6 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc @@ -924,7 +924,8 @@ ID *deg_update_copy_on_write_datablock(const Depsgraph *depsgraph, const IDNode const ID_Type id_type = GS(id_orig->name); switch (id_type) { - /* For grease pencil, we can avoid a full copy of the data-block and only do an update-on-write. */ + /* For grease pencil, we can avoid a full copy of the data-block and only do an + * update-on-write. */ case ID_GD: { if (check_datablock_expanded(id_cow) && !BKE_gpencil_check_copy_on_write_needed((bGPdata *)id_orig)) { diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 082deab823b..cab44bbef5b 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -280,7 +280,7 @@ typedef struct tGPsdata { static void gpencil_update_cache(bGPdata *gpd) { if (gpd) { - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); gpd->flag |= GP_DATA_CACHE_IS_DIRTY; } } @@ -2240,7 +2240,6 @@ static void gpencil_paint_initstroke(tGPsdata *p, if (!IS_AUTOKEY_ON(scene)) { BKE_report(p->reports, RPT_INFO, "No available frame for creating stroke"); } - return; } p->gpf->flag |= GP_FRAME_PAINT; diff --git a/source/blender/editors/gpencil/gpencil_undo.c b/source/blender/editors/gpencil/gpencil_undo.c index ec70febc80c..aed90ab1332 100644 --- a/source/blender/editors/gpencil/gpencil_undo.c +++ b/source/blender/editors/gpencil/gpencil_undo.c @@ -1,3 +1,4 @@ + /* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -21,24 +22,27 @@ * \ingroup edgpencil */ +#include <stdio.h> #include <stdlib.h> #include <string.h> #include "MEM_guardedalloc.h" +#include "BLI_listbase.h" #include "DNA_gpencil_types.h" #include "DNA_listBase.h" #include "DNA_object_types.h" +#include "DNA_scene_types.h" #include "DNA_windowmanager_types.h" -#include "BLI_listbase.h" - #include "BKE_blender_undo.h" #include "BKE_context.h" #include "BKE_gpencil.h" +#include "BKE_gpencil_update_cache.h" #include "BKE_undo_system.h" #include "ED_gpencil.h" +#include "ED_undo.h" #include "WM_api.h" #include "WM_types.h" @@ -93,9 +97,9 @@ int ED_undo_gpencil_step(bContext *C, const int step) /* copy layers */ BLI_listbase_clear(&gpd->layers); - LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDlayer *, gpl_undo, &gpd->layers) { /* make a copy of source layer and its data */ - gpld = BKE_gpencil_layer_duplicate(gpl, true, true); + gpld = BKE_gpencil_layer_duplicate(gpl_undo, true, true); BLI_addtail(&gpd->layers, gpld); } } @@ -130,6 +134,8 @@ void gpencil_undo_push(bGPdata *gpd) { bGPundonode *undo_node; + // printf("\t\tGP - undo push\n"); + if (cur_node) { /* Remove all undone nodes from stack. */ undo_node = cur_node->next; @@ -170,7 +176,7 @@ void gpencil_undo_push(bGPdata *gpd) /* create new undo node */ undo_node = MEM_callocN(sizeof(bGPundonode), "gpencil undo node"); - undo_node->gpd = BKE_gpencil_data_duplicate(NULL, gpd, true); + BKE_gpencil_data_duplicate(NULL, gpd, &undo_node->gpd); cur_node = undo_node; @@ -190,3 +196,396 @@ 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; + /* Whether this step is only a frame change. */ + bool frame_changed; + /* 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) +{ + printf("gpencil_undosys_poll\n"); + 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 *bmain, UndoStep *us_p) +{ + printf("gpencil_undosys_step_encode: \n"); + GPencilUndoStep *us = (GPencilUndoStep *)us_p; + us->undo_data = MEM_callocN(sizeof(GPencilUndoData), __func__); + us->undo_data->frame_changed = false; + + Scene *scene = CTX_data_scene(C); + us->undo_data->cfra = scene->r.cfra; + + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = (bGPdata *)ob->data; + us->undo_data->mode = ob->mode; + + wmWindowManager *wm = CTX_wm_manager(C); + + // Inspect notifier queue to detect frame change + if (wm->notifier_queue.first) { + wmNotifier *note = (wmNotifier *)wm->notifier_queue.first; + // If frame changed, encode only this information + if (note->category == NC_SCENE && note->data == ND_FRAME) { + us->undo_data->frame_changed = true; + printf("Time changed: %d\n", us->undo_data->cfra); + 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 *bmain, UndoStep *us_p, const eUndoStepDir dir, bool is_final) +{ + printf("gpencil_undosys_step_decode\n"); + 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) { + return; + } + + printf("Direction: %s\n", dir == STEP_UNDO ? "STEP_UNDO" : "STEP_REDO"); + + Scene *scene = CTX_data_scene(C); + + if (undo_data->cfra != scene->r.cfra) { + printf("Restoring frame: %d\n", undo_data->cfra); + scene->r.cfra = undo_data->cfra; + /* TODO: what if we merged a full copy with a frame change? */ + DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK); + WM_event_add_notifier(C, NC_SCENE | ND_FRAME, NULL); + } + + if (change_gpencil_mode(C, ob, undo_data->mode)) { + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL); + return; + } + + if (dir == STEP_UNDO) { + UndoStep *us_iter = us_p; + BLI_assert(us_iter->next != NULL); + UndoStep *us_next = us_p->next; + + GPencilUndoData *data_iter = ((GPencilUndoStep *)us_iter)->undo_data; + GPencilUndoData *data_next = ((GPencilUndoStep *)us_next)->undo_data; + + if (data_next->gpd_cache_data == NULL) { + return; + } + + while ( + data_iter->gpd_cache_data == NULL || + BKE_gpencil_compare_update_caches(data_iter->gpd_cache_data, data_next->gpd_cache_data)) { + us_iter = us_iter->prev; + BLI_assert(us_iter != NULL && us_iter->type == BKE_UNDOSYS_TYPE_GPENCIL); + data_iter = ((GPencilUndoStep *)us_iter)->undo_data; + } + + while (us_iter != us_next) { + if (data_iter->gpd_cache_data != NULL) { + gpencil_undo_data_to_gpencil_data(data_iter, gpd, true); + } + us_iter = us_iter->next; + data_iter = ((GPencilUndoStep *)us_iter)->undo_data; + } + } + else { + if (undo_data->gpd_cache_data == NULL) { + return; + } + + gpencil_undo_data_to_gpencil_data(undo_data, gpd, true); + } + gpd->flag |= GP_DATA_UPDATE_CACHE_UNDO_ENCODED; + + DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL); +} + +static void gpencil_undosys_step_free(UndoStep *us_p) +{ + printf("gpencil_undosys_step_free\n"); + GPencilUndoStep *us = (GPencilUndoStep *)us_p; + GPencilUndoData *us_data = us->undo_data; + + /** + * If this undo step is the first, we want to keep its full copy of the grease pencil undo_data + * (we assume that the first undo step always has this). Otherwise we free the step and its + * undo_data. + */ + if ((us_p->prev == NULL || us_p->prev->type != BKE_UNDOSYS_TYPE_GPENCIL) && us_p->next != NULL && + us_p->next->type == BKE_UNDOSYS_TYPE_GPENCIL) { + GPencilUndoStep *us_next = (GPencilUndoStep *)us_p->next; + GPencilUndoData *us_next_data = us_next->undo_data; + /* If e.g. a frame change happend, there is no cache so in this case we move the gpd pointer to + * that step. */ + if (us_next_data->gpd_cache_data == NULL) { + bGPdata *gpd_copy = us_data->gpd_cache_data->data; + BLI_assert(gpd_copy != NULL); + BLI_assert(us_data->gpd_cache_data->flag == GP_UPDATE_NODE_FULL_COPY); + + us_next_data->gpd_cache_data = BKE_gpencil_create_update_cache(gpd_copy, true); + /* Make sure the gpd_copy is not freed below. */ + us_data->gpd_cache_data->data = NULL; + } + /* If the next step does not have a full copy, we need to apply the changes of the next step + * to our cached gpencil undo_data copy and move it to the next step (it will now be the + * full-copy). */ + else if (us_next_data->gpd_cache_data->flag != GP_UPDATE_NODE_FULL_COPY) { + bGPdata *gpd_copy = us_data->gpd_cache_data->data; + BLI_assert(gpd_copy != NULL); + BLI_assert(us_data->gpd_cache_data->flag == GP_UPDATE_NODE_FULL_COPY); + + gpencil_undo_data_to_gpencil_data(us_next_data, gpd_copy, false); + BKE_gpencil_free_update_cache_and_data(us_next_data->gpd_cache_data); + + us_next_data->gpd_cache_data = BKE_gpencil_create_update_cache(gpd_copy, true); + + /* Make sure the gpd_copy is not freed below. */ + us_data->gpd_cache_data->data = NULL; + } + else { + /* If the next step is a full copy, we can safely free the current step (since the last step + * will be a full-copy). */ + } + } + + if (us_data->gpd_cache_data) { + BKE_gpencil_free_update_cache_and_data(us_data->gpd_cache_data); + } + MEM_freeN(us_data); +} + +void ED_gpencil_undosys_type(UndoType *ut) +{ + ut->name = "Grease Pencil Draw"; + ut->poll = gpencil_undosys_poll; + ut->step_encode = gpencil_undosys_step_encode; + ut->step_decode = gpencil_undosys_step_decode; + ut->step_free = gpencil_undosys_step_free; + + ut->flags = UNDOTYPE_FLAG_NEED_CONTEXT_FOR_ENCODE; + + ut->step_size = sizeof(GPencilUndoStep); +} + +/** \} */ diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h index 919ea3e4a6b..ce98a43d937 100644 --- a/source/blender/editors/include/ED_gpencil.h +++ b/source/blender/editors/include/ED_gpencil.h @@ -52,6 +52,7 @@ struct SnapObjectContext; struct ToolSettings; struct View3D; struct bContext; +struct UndoType; struct Material; struct Object; @@ -353,6 +354,8 @@ int ED_gpencil_session_active(void); */ int ED_undo_gpencil_step(struct bContext *C, int step); /* eUndoStepDir. */ +void ED_gpencil_undosys_type(struct UndoType *ut); + /* ------------ Grease-Pencil Armature ------------------ */ bool ED_gpencil_add_armature(const struct bContext *C, struct ReportList *reports, diff --git a/source/blender/editors/undo/undo_system_types.c b/source/blender/editors/undo/undo_system_types.c index 300ab606a03..4c5e6c173a7 100644 --- a/source/blender/editors/undo/undo_system_types.c +++ b/source/blender/editors/undo/undo_system_types.c @@ -24,6 +24,7 @@ #include "ED_armature.h" #include "ED_curve.h" +#include "ED_gpencil.h" #include "ED_lattice.h" #include "ED_mball.h" #include "ED_mesh.h" @@ -46,6 +47,8 @@ void ED_undosys_type_init(void) BKE_undosys_type_append(ED_lattice_undosys_type); BKE_undosys_type_append(ED_mball_undosys_type); BKE_undosys_type_append(ED_mesh_undosys_type); + /* Grease pencil */ + BKE_UNDOSYS_TYPE_GPENCIL = BKE_undosys_type_append(ED_gpencil_undosys_type); /* Paint Modes */ BKE_UNDOSYS_TYPE_IMAGE = BKE_undosys_type_append(ED_image_undosys_type); diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index 3340782d64a..44be45d705e 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -807,6 +807,8 @@ typedef enum eGPdata_Flag { GP_DATA_CURVE_EDIT_MODE = (1 << 21), /* Use adaptive curve resolution */ GP_DATA_CURVE_ADAPTIVE_RESOLUTION = (1 << 22), + /* Update cache was encoded by the undo system. */ + GP_DATA_UPDATE_CACHE_UNDO_ENCODED = (1 << 23), } eGPdata_Flag; /* gpd->onion_flag */ diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index bfb16346b69..a03bf082861 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -656,6 +656,7 @@ typedef struct UserDef_Experimental { char show_asset_debug_info; char no_asset_indexing; char use_gpencil_update_cache; + char use_gpencil_undo_system; char SANITIZE_AFTER_HERE; /* The following options are automatically sanitized (set to 0) * when the release cycle is not alpha. */ @@ -666,7 +667,6 @@ typedef struct UserDef_Experimental { char use_sculpt_tools_tilt; char use_extended_asset_browser; char use_override_templates; - char _pad[1]; /** `makesdna` does not allow empty structs. */ } UserDef_Experimental; diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 8bdbd63058b..bc0512d70d4 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -38,6 +38,7 @@ #include "BKE_appdir.h" #include "BKE_sound.h" #include "BKE_studiolight.h" +#include "BKE_undo_system.h" #include "RNA_access.h" #include "RNA_define.h" @@ -347,6 +348,26 @@ static void rna_userdef_language_update(Main *UNUSED(bmain), USERDEF_TAG_DIRTY; } +static void rna_userdef_experimental_gpencil_use_update_cache_set(PointerRNA *ptr, + const bool value) +{ + UserDef_Experimental *experimental = (UserDef_Experimental *)ptr->data; + experimental->use_gpencil_update_cache = (char)value; + if (!value) { + RNA_boolean_set(ptr, "use_gpencil_undo_system", false); + } +} + +static void rna_userdef_experimental_use_gpencil_undo_system_set(PointerRNA *ptr, const bool value) +{ + if (!value) { + wmWindowManager *wm = G_MAIN->wm.first; + BKE_undosys_stack_clear(wm->undo_stack); + } + UserDef_Experimental *experimental = (UserDef_Experimental *)ptr->data; + experimental->use_gpencil_undo_system = (char)value; +} + static void rna_userdef_asset_library_name_set(PointerRNA *ptr, const char *value) { bUserAssetLibrary *library = (bUserAssetLibrary *)ptr->data; @@ -6451,6 +6472,16 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) prop = RNA_def_property(srna, "use_gpencil_update_cache", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "use_gpencil_update_cache", 1); RNA_def_property_ui_text(prop, "GPencil Update Cache", "Enable the grease pencil update cache"); + RNA_def_property_boolean_funcs( + prop, NULL, "rna_userdef_experimental_gpencil_use_update_cache_set"); + + prop = RNA_def_property(srna, "use_gpencil_undo_system", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "use_gpencil_undo_system", 1); + RNA_def_property_ui_text(prop, + "GPencil Undo System", + "Enable the grease pencil undo system that uses the update cache"); + RNA_def_property_boolean_funcs( + prop, NULL, "rna_userdef_experimental_use_gpencil_undo_system_set"); } static void rna_def_userdef_addon_collection(BlenderRNA *brna, PropertyRNA *cprop) |