Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/scripts/startup/bl_ui/space_userpref.py39
-rw-r--r--source/blender/blenkernel/BKE_gpencil.h11
-rw-r--r--source/blender/blenkernel/BKE_gpencil_update_cache.h20
-rw-r--r--source/blender/blenkernel/BKE_object.h1
-rw-r--r--source/blender/blenkernel/BKE_undo_system.h1
-rw-r--r--source/blender/blenkernel/intern/gpencil.c43
-rw-r--r--source/blender/blenkernel/intern/gpencil_update_cache.c253
-rw-r--r--source/blender/blenkernel/intern/undo_system.c1
-rw-r--r--source/blender/blenlib/intern/DLRB_tree.c2
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc3
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c3
-rw-r--r--source/blender/editors/gpencil/gpencil_undo.c409
-rw-r--r--source/blender/editors/include/ED_gpencil.h3
-rw-r--r--source/blender/editors/undo/undo_system_types.c3
-rw-r--r--source/blender/makesdna/DNA_gpencil_types.h2
-rw-r--r--source/blender/makesdna/DNA_userdef_types.h2
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c31
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)