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:
Diffstat (limited to 'source/blender/editors/gpencil')
-rw-r--r--source/blender/editors/gpencil/CMakeLists.txt2
-rw-r--r--source/blender/editors/gpencil/annotate_paint.c26
-rw-r--r--source/blender/editors/gpencil/gpencil_add_blank.c101
-rw-r--r--source/blender/editors/gpencil/gpencil_add_lineart.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_add_monkey.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_add_stroke.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_bake_animation.c448
-rw-r--r--source/blender/editors/gpencil/gpencil_convert.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_data.c360
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c441
-rw-r--r--source/blender/editors/gpencil/gpencil_fill.c22
-rw-r--r--source/blender/editors/gpencil/gpencil_intern.h7
-rw-r--r--source/blender/editors/gpencil/gpencil_interpolate.c77
-rw-r--r--source/blender/editors/gpencil/gpencil_mesh.c26
-rw-r--r--source/blender/editors/gpencil/gpencil_ops.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c56
-rw-r--r--source/blender/editors/gpencil/gpencil_primitive.c44
-rw-r--r--source/blender/editors/gpencil/gpencil_select.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_trace_ops.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_trace_utils.c10
-rw-r--r--source/blender/editors/gpencil/gpencil_utils.c40
21 files changed, 1369 insertions, 311 deletions
diff --git a/source/blender/editors/gpencil/CMakeLists.txt b/source/blender/editors/gpencil/CMakeLists.txt
index 47ae90acb74..bff7310e9f7 100644
--- a/source/blender/editors/gpencil/CMakeLists.txt
+++ b/source/blender/editors/gpencil/CMakeLists.txt
@@ -36,10 +36,12 @@ set(SRC
annotate_paint.c
drawgpencil.c
editaction_gpencil.c
+ gpencil_add_blank.c
gpencil_add_lineart.c
gpencil_add_monkey.c
gpencil_add_stroke.c
gpencil_armature.c
+ gpencil_bake_animation.c
gpencil_convert.c
gpencil_data.c
gpencil_edit.c
diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c
index e9817f82090..c155587e95a 100644
--- a/source/blender/editors/gpencil/annotate_paint.c
+++ b/source/blender/editors/gpencil/annotate_paint.c
@@ -660,10 +660,14 @@ static short annotation_stroke_addpoint(tGPsdata *p,
View3D *v3d = p->area->spacedata.first;
view3d_region_operator_needs_opengl(p->win, p->region);
- ED_view3d_autodist_init(p->depsgraph,
- p->region,
- v3d,
- (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0);
+ ED_view3d_depth_override(p->depsgraph,
+ p->region,
+ v3d,
+ NULL,
+ (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE) ?
+ V3D_DEPTH_GPENCIL_ONLY :
+ V3D_DEPTH_NO_GPENCIL,
+ false);
}
/* convert screen-coordinates to appropriate coordinates (and store them) */
@@ -1222,7 +1226,7 @@ static void annotation_stroke_doeraser(tGPsdata *p)
if (p->flags & GP_PAINTFLAG_V3D_ERASER_DEPTH) {
View3D *v3d = p->area->spacedata.first;
view3d_region_operator_needs_opengl(p->win, p->region);
- ED_view3d_autodist_init(p->depsgraph, p->region, v3d, 0);
+ ED_view3d_depth_override(p->depsgraph, p->region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, false);
}
}
@@ -1544,7 +1548,7 @@ static void annotation_paint_initstroke(tGPsdata *p,
if (p->gpl == NULL) {
/* tag for annotations */
p->gpd->flag |= GP_DATA_ANNOTATIONS;
- p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("Note"), true);
+ p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("Note"), true, false);
if (p->custom_color[3]) {
copy_v3_v3(p->gpl->color, p->custom_color);
@@ -1695,8 +1699,14 @@ static void annotation_paint_strokeend(tGPsdata *p)
/* need to restore the original projection settings before packing up */
view3d_region_operator_needs_opengl(p->win, p->region);
- ED_view3d_autodist_init(
- p->depsgraph, p->region, v3d, (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0);
+ ED_view3d_depth_override(p->depsgraph,
+ p->region,
+ v3d,
+ NULL,
+ (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE) ?
+ V3D_DEPTH_GPENCIL_ONLY :
+ V3D_DEPTH_NO_GPENCIL,
+ false);
}
/* check if doing eraser or not */
diff --git a/source/blender/editors/gpencil/gpencil_add_blank.c b/source/blender/editors/gpencil/gpencil_add_blank.c
new file mode 100644
index 00000000000..3aa16e54597
--- /dev/null
+++ b/source/blender/editors/gpencil/gpencil_add_blank.c
@@ -0,0 +1,101 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2017 Blender Foundation
+ * This is a new part of Blender
+ */
+
+/** \file
+ * \ingroup edgpencil
+ */
+
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+#include "BKE_context.h"
+#include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
+#include "BKE_main.h"
+#include "BKE_material.h"
+
+#include "DEG_depsgraph.h"
+
+#include "ED_gpencil.h"
+
+/* Definition of the most important info from a color */
+typedef struct ColorTemplate {
+ const char *name;
+ float line[4];
+ float fill[4];
+} ColorTemplate;
+
+/* Add color an ensure duplications (matched by name) */
+static int gpencil_stroke_material(Main *bmain, Object *ob, const ColorTemplate *pct)
+{
+ int index;
+ Material *ma = BKE_gpencil_object_material_ensure_by_name(bmain, ob, pct->name, &index);
+
+ copy_v4_v4(ma->gp_style->stroke_rgba, pct->line);
+ srgb_to_linearrgb_v4(ma->gp_style->stroke_rgba, ma->gp_style->stroke_rgba);
+
+ copy_v4_v4(ma->gp_style->fill_rgba, pct->fill);
+ srgb_to_linearrgb_v4(ma->gp_style->fill_rgba, ma->gp_style->fill_rgba);
+
+ return index;
+}
+
+/* ***************************************************************** */
+/* Stroke Geometry */
+
+/* ***************************************************************** */
+/* Color Data */
+
+static const ColorTemplate gp_stroke_material_black = {
+ "Black",
+ {0.0f, 0.0f, 0.0f, 1.0f},
+ {0.0f, 0.0f, 0.0f, 0.0f},
+};
+
+/* ***************************************************************** */
+/* Blank API */
+
+/* Add a Simple empty object with one layer and one color. */
+void ED_gpencil_create_blank(bContext *C, Object *ob, float UNUSED(mat[4][4]))
+{
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+ bGPdata *gpd = (bGPdata *)ob->data;
+
+ /* create colors */
+ int color_black = gpencil_stroke_material(bmain, ob, &gp_stroke_material_black);
+
+ /* set first color as active and in brushes */
+ ob->actcol = color_black + 1;
+
+ /* layers */
+ bGPDlayer *layer = BKE_gpencil_layer_addnew(gpd, "GP_Layer", true, false);
+
+ /* frames */
+ BKE_gpencil_frame_addnew(layer, CFRA);
+
+ /* update depsgraph */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ gpd->flag |= GP_DATA_CACHE_IS_DIRTY;
+}
diff --git a/source/blender/editors/gpencil/gpencil_add_lineart.c b/source/blender/editors/gpencil/gpencil_add_lineart.c
index 6b28c6ec13e..ac0da0ad1db 100644
--- a/source/blender/editors/gpencil/gpencil_add_lineart.c
+++ b/source/blender/editors/gpencil/gpencil_add_lineart.c
@@ -96,7 +96,7 @@ void ED_gpencil_create_lineart(bContext *C, Object *ob)
ob->actcol = color_black + 1;
/* layers */
- bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true);
+ bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true, false);
/* frames */
BKE_gpencil_frame_addnew(lines, 0);
diff --git a/source/blender/editors/gpencil/gpencil_add_monkey.c b/source/blender/editors/gpencil/gpencil_add_monkey.c
index 4497d963c6d..d8734c4ae6b 100644
--- a/source/blender/editors/gpencil/gpencil_add_monkey.c
+++ b/source/blender/editors/gpencil/gpencil_add_monkey.c
@@ -837,8 +837,8 @@ void ED_gpencil_create_monkey(bContext *C, Object *ob, float mat[4][4])
/* layers */
/* NOTE: For now, we just add new layers, to make it easier to separate out old/new instances */
- bGPDlayer *Fills = BKE_gpencil_layer_addnew(gpd, "Fills", false);
- bGPDlayer *Lines = BKE_gpencil_layer_addnew(gpd, "Lines", true);
+ bGPDlayer *Fills = BKE_gpencil_layer_addnew(gpd, "Fills", false, false);
+ bGPDlayer *Lines = BKE_gpencil_layer_addnew(gpd, "Lines", true, false);
/* frames */
/* NOTE: No need to check for existing, as this will take care of it for us */
diff --git a/source/blender/editors/gpencil/gpencil_add_stroke.c b/source/blender/editors/gpencil/gpencil_add_stroke.c
index 26237636526..e95496b51ee 100644
--- a/source/blender/editors/gpencil/gpencil_add_stroke.c
+++ b/source/blender/editors/gpencil/gpencil_add_stroke.c
@@ -225,8 +225,8 @@ void ED_gpencil_create_stroke(bContext *C, Object *ob, float mat[4][4])
ob->actcol = color_black + 1;
/* layers */
- bGPDlayer *colors = BKE_gpencil_layer_addnew(gpd, "Colors", false);
- bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true);
+ bGPDlayer *colors = BKE_gpencil_layer_addnew(gpd, "Colors", false, false);
+ bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true, false);
/* frames */
bGPDframe *frame_color = BKE_gpencil_frame_addnew(colors, CFRA);
diff --git a/source/blender/editors/gpencil/gpencil_bake_animation.c b/source/blender/editors/gpencil/gpencil_bake_animation.c
new file mode 100644
index 00000000000..30ebc9189c5
--- /dev/null
+++ b/source/blender/editors/gpencil/gpencil_bake_animation.c
@@ -0,0 +1,448 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2021 Blender Foundation
+ * This is a new part of Blender
+ */
+
+/** \file
+ * \ingroup edgpencil
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_ghash.h"
+#include "BLI_math.h"
+
+#include "DNA_anim_types.h"
+#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+
+#include "BKE_anim_data.h"
+#include "BKE_context.h"
+#include "BKE_duplilist.h"
+#include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
+#include "BKE_layer.h"
+#include "BKE_main.h"
+#include "BKE_material.h"
+#include "BKE_object.h"
+#include "BKE_report.h"
+#include "BKE_scene.h"
+
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "ED_gpencil.h"
+#include "ED_transform_snap_object_context.h"
+
+#include "gpencil_intern.h"
+
+const EnumPropertyItem rna_gpencil_reproject_type_items[] = {
+ {GP_REPROJECT_KEEP, "KEEP", 0, "No Reproject", ""},
+ {GP_REPROJECT_FRONT, "FRONT", 0, "Front", "Reproject the strokes using the X-Z plane"},
+ {GP_REPROJECT_SIDE, "SIDE", 0, "Side", "Reproject the strokes using the Y-Z plane"},
+ {GP_REPROJECT_TOP, "TOP", 0, "Top", "Reproject the strokes using the X-Y plane"},
+ {GP_REPROJECT_VIEW,
+ "VIEW",
+ 0,
+ "View",
+ "Reproject the strokes to end up on the same plane, as if drawn from the current "
+ "viewpoint "
+ "using 'Cursor' Stroke Placement"},
+ {GP_REPROJECT_CURSOR,
+ "CURSOR",
+ 0,
+ "Cursor",
+ "Reproject the strokes using the orientation of 3D cursor"},
+ {0, NULL, 0, NULL, NULL},
+};
+
+/* Check frame_end is always > start frame! */
+static void gpencil_bake_set_frame_end(struct Main *UNUSED(main),
+ struct Scene *UNUSED(scene),
+ struct PointerRNA *ptr)
+{
+ int frame_start = RNA_int_get(ptr, "frame_start");
+ int frame_end = RNA_int_get(ptr, "frame_end");
+
+ if (frame_end <= frame_start) {
+ RNA_int_set(ptr, "frame_end", frame_start + 1);
+ }
+}
+
+/* Extract mesh animation to Grease Pencil. */
+static bool gpencil_bake_grease_pencil_animation_poll(bContext *C)
+{
+ Object *obact = CTX_data_active_object(C);
+ if (CTX_data_mode_enum(C) != CTX_MODE_OBJECT) {
+ return false;
+ }
+
+ /* Check if grease pencil or empty for dupli groups. */
+ if ((obact == NULL) || ((obact->type != OB_GPENCIL) && (obact->type != OB_EMPTY))) {
+ return false;
+ }
+
+ /* Only if the current view is 3D View. */
+ ScrArea *area = CTX_wm_area(C);
+ return (area && area->spacetype);
+}
+
+typedef struct GpBakeOb {
+ struct GpBakeOb *next, *prev;
+ Object *ob;
+} GpBakeOb;
+
+/* Get list of keyframes used by selected objects. */
+static void animdata_keyframe_list_get(ListBase *ob_list,
+ const bool only_selected,
+ GHash *r_keyframes)
+{
+ /* Loop all objects to get the list of keyframes used. */
+ LISTBASE_FOREACH (GpBakeOb *, elem, ob_list) {
+ Object *ob = elem->ob;
+ AnimData *adt = BKE_animdata_from_id(&ob->id);
+ if ((adt == NULL) || (adt->action == NULL)) {
+ continue;
+ }
+ LISTBASE_FOREACH (FCurve *, fcurve, &adt->action->curves) {
+ int i;
+ BezTriple *bezt;
+ for (i = 0, bezt = fcurve->bezt; i < fcurve->totvert; i++, bezt++) {
+ /* Keyframe number is x value of point. */
+ if ((bezt->f2 & SELECT) || (!only_selected)) {
+ /* Insert only one key for each keyframe number. */
+ int key = (int)bezt->vec[1][0];
+ if (!BLI_ghash_haskey(r_keyframes, POINTER_FROM_INT(key))) {
+ BLI_ghash_insert(r_keyframes, POINTER_FROM_INT(key), POINTER_FROM_INT(key));
+ }
+ }
+ }
+ }
+ }
+}
+
+static void gpencil_bake_duplilist(Depsgraph *depsgraph, Scene *scene, Object *ob, ListBase *list)
+{
+ GpBakeOb *elem = NULL;
+ ListBase *lb;
+ DupliObject *dob;
+ lb = object_duplilist(depsgraph, scene, ob);
+ for (dob = lb->first; dob; dob = dob->next) {
+ if (dob->ob->type != OB_GPENCIL) {
+ continue;
+ }
+
+ elem = MEM_callocN(sizeof(GpBakeOb), __func__);
+ elem->ob = dob->ob;
+ BLI_addtail(list, elem);
+ }
+
+ free_object_duplilist(lb);
+}
+
+static void gpencil_bake_ob_list(bContext *C, Depsgraph *depsgraph, Scene *scene, ListBase *list)
+{
+ GpBakeOb *elem = NULL;
+
+ /* Add active object. In some files this could not be in selected array. */
+ Object *obact = CTX_data_active_object(C);
+
+ if (obact->type == OB_GPENCIL) {
+ elem = MEM_callocN(sizeof(GpBakeOb), __func__);
+ elem->ob = obact;
+ BLI_addtail(list, elem);
+ }
+ /* Add duplilist. */
+ else if (obact->type == OB_EMPTY) {
+ gpencil_bake_duplilist(depsgraph, scene, obact, list);
+ }
+
+ /* Add other selected objects. */
+ CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
+ if (ob == obact) {
+ continue;
+ }
+ /* Add selected objects.*/
+ if (ob->type == OB_GPENCIL) {
+ elem = MEM_callocN(sizeof(GpBakeOb), __func__);
+ elem->ob = ob;
+ BLI_addtail(list, elem);
+ }
+
+ /* Add duplilist. */
+ if (ob->type == OB_EMPTY) {
+ gpencil_bake_duplilist(depsgraph, scene, ob, list);
+ }
+ }
+ CTX_DATA_END;
+}
+
+static void gpencil_bake_free_ob_list(ListBase *list)
+{
+ LISTBASE_FOREACH_MUTABLE (GpBakeOb *, elem, list) {
+ MEM_SAFE_FREE(elem);
+ }
+}
+
+static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op)
+{
+ Main *bmain = CTX_data_main(C);
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Scene *scene = CTX_data_scene(C);
+ ARegion *region = CTX_wm_region(C);
+ View3D *v3d = CTX_wm_view3d(C);
+
+ ListBase ob_selected_list = {NULL, NULL};
+ gpencil_bake_ob_list(C, depsgraph, scene, &ob_selected_list);
+
+ /* Grab all relevant settings. */
+ const int step = RNA_int_get(op->ptr, "step");
+
+ const int frame_start = (scene->r.sfra > RNA_int_get(op->ptr, "frame_start")) ?
+ scene->r.sfra :
+ RNA_int_get(op->ptr, "frame_start");
+
+ const int frame_end = (scene->r.efra < RNA_int_get(op->ptr, "frame_end")) ?
+ scene->r.efra :
+ RNA_int_get(op->ptr, "frame_end");
+
+ const bool only_selected = RNA_boolean_get(op->ptr, "only_selected");
+ const int frame_offset = RNA_int_get(op->ptr, "frame_target") - frame_start;
+ const int project_type = RNA_enum_get(op->ptr, "project_type");
+
+ /* Create a new grease pencil object. */
+ Object *ob_gpencil = NULL;
+ ushort local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0;
+ ob_gpencil = ED_gpencil_add_object(C, scene->cursor.location, local_view_bits);
+ float invmat[4][4];
+ invert_m4_m4(invmat, ob_gpencil->obmat);
+
+ bGPdata *gpd_dst = (bGPdata *)ob_gpencil->data;
+ gpd_dst->draw_mode = GP_DRAWMODE_2D;
+
+ /* Set cursor to indicate working. */
+ WM_cursor_wait(true);
+
+ GP_SpaceConversion gsc = {NULL};
+ SnapObjectContext *sctx = NULL;
+ if (project_type != GP_REPROJECT_KEEP) {
+ /* Init space conversion stuff. */
+ gpencil_point_conversion_init(C, &gsc);
+ /* Move the grease pencil object to conversion data. */
+ gsc.ob = ob_gpencil;
+
+ /* Init snap context for geometry projection. */
+ sctx = ED_transform_snap_object_context_create_view3d(scene, 0, region, CTX_wm_view3d(C));
+ }
+
+ /* Loop all frame range. */
+ int oldframe = (int)DEG_get_ctime(depsgraph);
+ int key = -1;
+
+ /* Get list of keyframes. */
+ GHash *keyframe_list = BLI_ghash_int_new(__func__);
+ if (only_selected) {
+ animdata_keyframe_list_get(&ob_selected_list, only_selected, keyframe_list);
+ }
+
+ for (int i = frame_start; i < frame_end + 1; i++) {
+ key++;
+ /* Jump if not step limit but include last frame always. */
+ if ((key % step != 0) && (i != frame_end)) {
+ continue;
+ }
+
+ /* Check if frame is in the list of frames to be exported. */
+ if ((only_selected) && (!BLI_ghash_haskey(keyframe_list, POINTER_FROM_INT(i)))) {
+ continue;
+ }
+
+ /* Move scene to new frame. */
+ CFRA = i;
+ BKE_scene_graph_update_for_newframe(depsgraph);
+
+ /* Loop all objects in the list. */
+ LISTBASE_FOREACH (GpBakeOb *, elem, &ob_selected_list) {
+ Object *ob_eval = (Object *)DEG_get_evaluated_object(depsgraph, elem->ob);
+ bGPdata *gpd_src = ob_eval->data;
+
+ LISTBASE_FOREACH (bGPDlayer *, gpl_src, &gpd_src->layers) {
+ /* Create destination layer. */
+ char *layer_name;
+ layer_name = BLI_sprintfN("%s_%s", elem->ob->id.name + 2, gpl_src->info);
+ bGPDlayer *gpl_dst = BKE_gpencil_layer_named_get(gpd_dst, layer_name);
+ if (gpl_dst == NULL) {
+ gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, layer_name, true, false);
+ }
+ MEM_freeN(layer_name);
+
+ /* Layer Transform matrix. */
+ float matrix[4][4];
+ BKE_gpencil_layer_transform_matrix_get(depsgraph, elem->ob, gpl_src, matrix);
+
+ /* Duplicate frame. */
+ bGPDframe *gpf_src = BKE_gpencil_layer_frame_get(gpl_src, CFRA, GP_GETFRAME_USE_PREV);
+ if (gpf_src == NULL) {
+ continue;
+ }
+ bGPDframe *gpf_dst = BKE_gpencil_frame_duplicate(gpf_src, true);
+ gpf_dst->framenum = CFRA + frame_offset;
+ gpf_dst->flag &= ~GP_FRAME_SELECT;
+ BLI_addtail(&gpl_dst->frames, gpf_dst);
+
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf_dst->strokes) {
+ /* Create material of the stroke. */
+ Material *ma_src = BKE_object_material_get(elem->ob, gps->mat_nr + 1);
+ bool found = false;
+ for (int index = 0; index < ob_gpencil->totcol; index++) {
+ Material *ma_dst = BKE_object_material_get(ob_gpencil, index + 1);
+ if (ma_src == ma_dst) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ BKE_object_material_slot_add(bmain, ob_gpencil);
+ BKE_object_material_assign(
+ bmain, ob_gpencil, ma_src, ob_gpencil->totcol, BKE_MAT_ASSIGN_USERPREF);
+ }
+
+ /* Set new material index. */
+ gps->mat_nr = BKE_gpencil_object_material_index_get(ob_gpencil, ma_src);
+
+ /* Update point location to new object space. */
+ for (int j = 0; j < gps->totpoints; j++) {
+ bGPDspoint *pt = &gps->points[j];
+ mul_m4_v3(matrix, &pt->x);
+ mul_m4_v3(invmat, &pt->x);
+ }
+
+ /* Reproject stroke. */
+ if (project_type != GP_REPROJECT_KEEP) {
+ ED_gpencil_stroke_reproject(
+ depsgraph, &gsc, sctx, gpl_dst, gpf_dst, gps, project_type, false);
+ }
+ else {
+ BKE_gpencil_stroke_geometry_update(gpd_dst, gps);
+ }
+ }
+ }
+ }
+ }
+ /* Return scene frame state and DB to original state. */
+ CFRA = oldframe;
+ BKE_scene_graph_update_for_newframe(depsgraph);
+
+ /* Free memory. */
+ gpencil_bake_free_ob_list(&ob_selected_list);
+ if (sctx != NULL) {
+ ED_transform_snap_object_context_destroy(sctx);
+ }
+ /* Free temp hash table. */
+ if (keyframe_list != NULL) {
+ BLI_ghash_free(keyframe_list, NULL, NULL);
+ }
+
+ /* Notifiers. */
+ DEG_relations_tag_update(bmain);
+ DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
+ DEG_id_tag_update(&gpd_dst->id, ID_RECALC_COPY_ON_WRITE);
+ WM_event_add_notifier(C, NC_OBJECT | NA_ADDED, NULL);
+ WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
+
+ /* Reset cursor. */
+ WM_cursor_wait(false);
+
+ /* done */
+ return OPERATOR_FINISHED;
+}
+
+static int gpencil_bake_grease_pencil_animation_invoke(bContext *C,
+ wmOperator *op,
+ const wmEvent *UNUSED(event))
+{
+ PropertyRNA *prop;
+ Scene *scene = CTX_data_scene(C);
+
+ prop = RNA_struct_find_property(op->ptr, "frame_start");
+ if (!RNA_property_is_set(op->ptr, prop)) {
+ const int frame_start = RNA_property_int_get(op->ptr, prop);
+ if (frame_start < scene->r.sfra) {
+ RNA_property_int_set(op->ptr, prop, scene->r.sfra);
+ }
+ }
+
+ prop = RNA_struct_find_property(op->ptr, "frame_end");
+ if (!RNA_property_is_set(op->ptr, prop)) {
+ const int frame_end = RNA_property_int_get(op->ptr, prop);
+ if (frame_end > scene->r.efra) {
+ RNA_property_int_set(op->ptr, prop, scene->r.efra);
+ }
+ }
+
+ /* Show popup dialog to allow editing. */
+ return WM_operator_props_dialog_popup(C, op, 250);
+}
+
+void GPENCIL_OT_bake_grease_pencil_animation(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Bake Object Transform to Grease Pencil";
+ ot->idname = "GPENCIL_OT_bake_grease_pencil_animation";
+ ot->description = "Bake grease pencil object transform to grease pencil keyframes";
+
+ /* callbacks */
+ ot->invoke = gpencil_bake_grease_pencil_animation_invoke;
+ ot->exec = gpencil_bake_grease_pencil_animation_exec;
+ ot->poll = gpencil_bake_grease_pencil_animation_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ prop = RNA_def_int(
+ ot->srna, "frame_start", 1, 1, 100000, "Start Frame", "The start frame", 1, 100000);
+
+ prop = RNA_def_int(
+ ot->srna, "frame_end", 250, 1, 100000, "End Frame", "The end frame of animation", 1, 100000);
+ RNA_def_property_update_runtime(prop, gpencil_bake_set_frame_end);
+
+ prop = RNA_def_int(ot->srna, "step", 1, 1, 100, "Step", "Step between generated frames", 1, 100);
+
+ RNA_def_boolean(
+ ot->srna, "only_selected", 0, "Only Selected Keyframes", "Convert only selected keyframes");
+ RNA_def_int(
+ ot->srna, "frame_target", 1, 1, 100000, "Target Frame", "Destination frame", 1, 100000);
+
+ RNA_def_enum(ot->srna,
+ "project_type",
+ rna_gpencil_reproject_type_items,
+ GP_REPROJECT_KEEP,
+ "Projection Type",
+ "");
+}
diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c
index ac75ae44c8a..8ab413e907c 100644
--- a/source/blender/editors/gpencil/gpencil_convert.c
+++ b/source/blender/editors/gpencil/gpencil_convert.c
@@ -1857,7 +1857,7 @@ static int image_to_gpencil_exec(bContext *C, wmOperator *op)
/* Add layer and frame. */
bGPdata *gpd = (bGPdata *)ob->data;
- bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, "Image Layer", true);
+ bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, "Image Layer", true, false);
bGPDframe *gpf = BKE_gpencil_frame_addnew(gpl, CFRA);
done = BKE_gpencil_from_image(sima, gpd, gpf, size, is_mask);
diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c
index fd2758c8a08..d9a807d17ab 100644
--- a/source/blender/editors/gpencil/gpencil_data.c
+++ b/source/blender/editors/gpencil/gpencil_data.c
@@ -129,7 +129,7 @@ static int gpencil_data_add_exec(bContext *C, wmOperator *op)
gpd->flag |= GP_DATA_ANNOTATIONS;
/* add new layer (i.e. a "note") */
- BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true);
+ BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true, false);
/* notifiers */
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
@@ -231,7 +231,7 @@ static int gpencil_layer_add_exec(bContext *C, wmOperator *op)
/* mark as annotation */
(*gpd_ptr)->flag |= GP_DATA_ANNOTATIONS;
- BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true);
+ BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true, false);
gpd = *gpd_ptr;
}
else {
@@ -239,7 +239,7 @@ static int gpencil_layer_add_exec(bContext *C, wmOperator *op)
Object *ob = CTX_data_active_object(C);
if ((ob != NULL) && (ob->type == OB_GPENCIL)) {
gpd = (bGPdata *)ob->data;
- bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
+ bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true, false);
/* Add a new frame to make it visible in Dopesheet. */
if (gpl != NULL) {
gpl->actframe = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_ADD_NEW);
@@ -524,7 +524,6 @@ enum {
static bool gpencil_layer_duplicate_object_poll(bContext *C)
{
- ViewLayer *view_layer = CTX_data_view_layer(C);
Object *ob = CTX_data_active_object(C);
if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
return false;
@@ -537,90 +536,75 @@ static bool gpencil_layer_duplicate_object_poll(bContext *C)
return false;
}
- /* check there are more grease pencil objects */
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
- if ((base->object != ob) && (base->object->type == OB_GPENCIL)) {
- return true;
- }
- }
-
- return false;
+ return true;
}
static int gpencil_layer_duplicate_object_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
- Scene *scene = CTX_data_scene(C);
- char name[MAX_ID_NAME - 2];
- RNA_string_get(op->ptr, "object", name);
-
- if (name[0] == '\0') {
- return OPERATOR_CANCELLED;
- }
-
- Object *ob_dst = (Object *)BKE_scene_object_find_by_name(scene, name);
-
- int mode = RNA_enum_get(op->ptr, "mode");
+ const bool only_active = RNA_boolean_get(op->ptr, "only_active");
+ const int mode = RNA_enum_get(op->ptr, "mode");
Object *ob_src = CTX_data_active_object(C);
bGPdata *gpd_src = (bGPdata *)ob_src->data;
- bGPDlayer *gpl_src = BKE_gpencil_layer_active_get(gpd_src);
+ bGPDlayer *gpl_active = BKE_gpencil_layer_active_get(gpd_src);
- /* Sanity checks. */
- if (ELEM(NULL, gpd_src, gpl_src, ob_dst)) {
- return OPERATOR_CANCELLED;
- }
- /* Cannot copy itself and check destination type. */
- if ((ob_src == ob_dst) || (ob_dst->type != OB_GPENCIL)) {
- return OPERATOR_CANCELLED;
- }
-
- bGPdata *gpd_dst = (bGPdata *)ob_dst->data;
-
- /* Create new layer. */
- bGPDlayer *gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl_src->info, true);
- /* Need to copy some variables (not all). */
- gpl_dst->onion_flag = gpl_src->onion_flag;
- gpl_dst->thickness = gpl_src->thickness;
- gpl_dst->line_change = gpl_src->line_change;
- copy_v4_v4(gpl_dst->tintcolor, gpl_src->tintcolor);
- gpl_dst->opacity = gpl_src->opacity;
-
- /* Create all frames. */
- LISTBASE_FOREACH (bGPDframe *, gpf_src, &gpl_src->frames) {
-
- if ((mode == GP_LAYER_COPY_OBJECT_ACT_FRAME) && (gpf_src != gpl_src->actframe)) {
+ CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
+ if ((ob == ob_src) || (ob->type != OB_GPENCIL)) {
continue;
}
+ bGPdata *gpd_dst = (bGPdata *)ob->data;
+ LISTBASE_FOREACH_BACKWARD (bGPDlayer *, gpl_src, &gpd_src->layers) {
+ if ((only_active) && (gpl_src != gpl_active)) {
+ continue;
+ }
+ /* Create new layer (adding at head of the list). */
+ bGPDlayer *gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl_src->info, true, true);
+ /* Need to copy some variables (not all). */
+ gpl_dst->onion_flag = gpl_src->onion_flag;
+ gpl_dst->thickness = gpl_src->thickness;
+ gpl_dst->line_change = gpl_src->line_change;
+ copy_v4_v4(gpl_dst->tintcolor, gpl_src->tintcolor);
+ gpl_dst->opacity = gpl_src->opacity;
+
+ /* Create all frames. */
+ LISTBASE_FOREACH (bGPDframe *, gpf_src, &gpl_src->frames) {
+
+ if ((mode == GP_LAYER_COPY_OBJECT_ACT_FRAME) && (gpf_src != gpl_src->actframe)) {
+ continue;
+ }
- /* Create new frame. */
- bGPDframe *gpf_dst = BKE_gpencil_frame_addnew(gpl_dst, gpf_src->framenum);
+ /* Create new frame. */
+ bGPDframe *gpf_dst = BKE_gpencil_frame_addnew(gpl_dst, gpf_src->framenum);
- /* Copy strokes. */
- LISTBASE_FOREACH (bGPDstroke *, gps_src, &gpf_src->strokes) {
+ /* Copy strokes. */
+ LISTBASE_FOREACH (bGPDstroke *, gps_src, &gpf_src->strokes) {
- /* Make copy of source stroke. */
- bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true, true);
+ /* Make copy of source stroke. */
+ bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true, true);
- /* Check if material is in destination object,
- * otherwise add the slot with the material. */
- Material *ma_src = BKE_object_material_get(ob_src, gps_src->mat_nr + 1);
- if (ma_src != NULL) {
- int idx = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma_src);
+ /* Check if material is in destination object,
+ * otherwise add the slot with the material. */
+ Material *ma_src = BKE_object_material_get(ob_src, gps_src->mat_nr + 1);
+ if (ma_src != NULL) {
+ int idx = BKE_gpencil_object_material_ensure(bmain, ob, ma_src);
- /* Reassign the stroke material to the right slot in destination object. */
- gps_dst->mat_nr = idx;
- }
+ /* Reassign the stroke material to the right slot in destination object. */
+ gps_dst->mat_nr = idx;
+ }
- /* Add new stroke to frame. */
- BLI_addtail(&gpf_dst->strokes, gps_dst);
+ /* Add new stroke to frame. */
+ BLI_addtail(&gpf_dst->strokes, gps_dst);
+ }
+ }
}
+ /* notifiers */
+ DEG_id_tag_update(&gpd_dst->id,
+ ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
+ DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
}
+ CTX_DATA_END;
- /* notifiers */
- DEG_id_tag_update(&gpd_dst->id,
- ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
- DEG_id_tag_update(&ob_dst->id, ID_RECALC_COPY_ON_WRITE);
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
return OPERATOR_FINISHED;
@@ -628,6 +612,8 @@ static int gpencil_layer_duplicate_object_exec(bContext *C, wmOperator *op)
void GPENCIL_OT_layer_duplicate_object(wmOperatorType *ot)
{
+ PropertyRNA *prop;
+
static const EnumPropertyItem copy_mode[] = {
{GP_LAYER_COPY_OBJECT_ALL_FRAME, "ALL", 0, "All Frames", ""},
{GP_LAYER_COPY_OBJECT_ACT_FRAME, "ACTIVE", 0, "Active Frame", ""},
@@ -637,7 +623,7 @@ void GPENCIL_OT_layer_duplicate_object(wmOperatorType *ot)
/* identifiers */
ot->name = "Duplicate Layer to New Object";
ot->idname = "GPENCIL_OT_layer_duplicate_object";
- ot->description = "Make a copy of the active Grease Pencil layer to new object";
+ ot->description = "Make a copy of the active Grease Pencil layer to selected object";
/* callbacks */
ot->exec = gpencil_layer_duplicate_object_exec;
@@ -646,11 +632,14 @@ void GPENCIL_OT_layer_duplicate_object(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
- ot->prop = RNA_def_string(
- ot->srna, "object", NULL, MAX_ID_NAME - 2, "Object", "Name of the destination object");
- RNA_def_property_flag(ot->prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ ot->prop = RNA_def_enum(ot->srna, "mode", copy_mode, GP_LAYER_COPY_OBJECT_ALL_FRAME, "Mode", "");
- RNA_def_enum(ot->srna, "mode", copy_mode, GP_LAYER_COPY_OBJECT_ALL_FRAME, "Mode", "");
+ prop = RNA_def_boolean(ot->srna,
+ "only_active",
+ true,
+ "Only Active",
+ "Copy only active Layer, uncheck to append all layers");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
/* ********************* Duplicate Frame ************************** */
@@ -1443,7 +1432,7 @@ static int gpencil_layer_change_exec(bContext *C, wmOperator *op)
/* Get layer or create new one */
if (layer_num == -1) {
/* Create layer */
- gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
+ gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true, false);
}
else {
/* Try to get layer */
@@ -1555,6 +1544,7 @@ static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op)
const int direction = RNA_enum_get(op->ptr, "direction");
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
+ bGPDstroke *gps_target = NULL;
bool changed = false;
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
@@ -1569,7 +1559,6 @@ static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op)
if (gpf == NULL) {
continue;
}
- bool gpf_lock = false;
/* verify if any selected stroke is in the extreme of the stack and select to move */
for (gps = gpf->strokes.first; gps; gps = gps->next) {
/* only if selected */
@@ -1582,18 +1571,19 @@ static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op)
if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
continue;
}
+ bool gpf_lock = false;
/* some stroke is already at front*/
if (ELEM(direction, GP_STROKE_MOVE_TOP, GP_STROKE_MOVE_UP)) {
if (gps == gpf->strokes.last) {
gpf_lock = true;
- continue;
+ gps_target = gps;
}
}
/* Some stroke is already at bottom. */
if (ELEM(direction, GP_STROKE_MOVE_BOTTOM, GP_STROKE_MOVE_DOWN)) {
if (gps == gpf->strokes.first) {
gpf_lock = true;
- continue;
+ gps_target = gps;
}
}
/* add to list (if not locked) */
@@ -1602,47 +1592,74 @@ static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op)
}
}
}
+
+ const int target_index = (gps_target) ? BLI_findindex(&gpf->strokes, gps_target) : -1;
+ int prev_index = target_index;
/* Now do the movement of the stroke */
- if (!gpf_lock) {
- switch (direction) {
- /* Bring to Front */
- case GP_STROKE_MOVE_TOP:
- LISTBASE_FOREACH (LinkData *, link, &selected) {
- gps = link->data;
- BLI_remlink(&gpf->strokes, gps);
+ switch (direction) {
+ /* Bring to Front */
+ case GP_STROKE_MOVE_TOP:
+ LISTBASE_FOREACH (LinkData *, link, &selected) {
+ gps = link->data;
+ BLI_remlink(&gpf->strokes, gps);
+ if (gps_target) {
+ BLI_insertlinkbefore(&gpf->strokes, gps_target, gps);
+ }
+ else {
BLI_addtail(&gpf->strokes, gps);
- changed = true;
}
- break;
- /* Bring Forward */
- case GP_STROKE_MOVE_UP:
- LISTBASE_FOREACH_BACKWARD (LinkData *, link, &selected) {
- gps = link->data;
- BLI_listbase_link_move(&gpf->strokes, gps, 1);
- changed = true;
+ changed = true;
+ }
+ break;
+ /* Bring Forward */
+ case GP_STROKE_MOVE_UP:
+ LISTBASE_FOREACH_BACKWARD (LinkData *, link, &selected) {
+ gps = link->data;
+ if (gps_target) {
+ int gps_index = BLI_findindex(&gpf->strokes, gps);
+ if (gps_index + 1 >= prev_index) {
+ prev_index = gps_index;
+ continue;
+ }
+ prev_index = gps_index;
+ }
+ BLI_listbase_link_move(&gpf->strokes, gps, 1);
+ changed = true;
+ }
+ break;
+ /* Send Backward */
+ case GP_STROKE_MOVE_DOWN:
+ LISTBASE_FOREACH (LinkData *, link, &selected) {
+ gps = link->data;
+ if (gps_target) {
+ int gps_index = BLI_findindex(&gpf->strokes, gps);
+ if (gps_index - 1 <= prev_index) {
+ prev_index = gps_index;
+ continue;
+ }
+ prev_index = gps_index;
}
- break;
- /* Send Backward */
- case GP_STROKE_MOVE_DOWN:
- LISTBASE_FOREACH (LinkData *, link, &selected) {
- gps = link->data;
- BLI_listbase_link_move(&gpf->strokes, gps, -1);
- changed = true;
+ BLI_listbase_link_move(&gpf->strokes, gps, -1);
+ changed = true;
+ }
+ break;
+ /* Send to Back */
+ case GP_STROKE_MOVE_BOTTOM:
+ LISTBASE_FOREACH_BACKWARD (LinkData *, link, &selected) {
+ gps = link->data;
+ BLI_remlink(&gpf->strokes, gps);
+ if (gps_target) {
+ BLI_insertlinkafter(&gpf->strokes, gps_target, gps);
}
- break;
- /* Send to Back */
- case GP_STROKE_MOVE_BOTTOM:
- LISTBASE_FOREACH_BACKWARD (LinkData *, link, &selected) {
- gps = link->data;
- BLI_remlink(&gpf->strokes, gps);
+ else {
BLI_addhead(&gpf->strokes, gps);
- changed = true;
}
- break;
- default:
- BLI_assert(0);
- break;
- }
+ changed = true;
+ }
+ break;
+ default:
+ BLI_assert(0);
+ break;
}
BLI_freelistN(&selected);
}
@@ -3561,6 +3578,79 @@ void GPENCIL_OT_set_active_material(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+/* ********************* Append Materials in a new object ************************** */
+static bool gpencil_materials_copy_to_object_poll(bContext *C)
+{
+ Object *ob = CTX_data_active_object(C);
+ if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
+ return false;
+ }
+ short *totcolp = BKE_object_material_len_p(ob);
+ if (*totcolp == 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static int gpencil_materials_copy_to_object_exec(bContext *C, wmOperator *op)
+{
+ Main *bmain = CTX_data_main(C);
+ const bool only_active = RNA_boolean_get(op->ptr, "only_active");
+ Object *ob_src = CTX_data_active_object(C);
+ Material *ma_active = BKE_gpencil_material(ob_src, ob_src->actcol);
+
+ CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
+ if ((ob == ob_src) || (ob->type != OB_GPENCIL)) {
+ continue;
+ }
+ /* Duplicate materials. */
+ for (int i = 0; i < ob_src->totcol; i++) {
+ Material *ma_src = BKE_object_material_get(ob_src, i + 1);
+ if (only_active && ma_src != ma_active) {
+ continue;
+ }
+
+ if (ma_src != NULL) {
+ BKE_gpencil_object_material_ensure(bmain, ob, ma_src);
+ }
+ }
+
+ /* notifiers */
+ DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
+ }
+ CTX_DATA_END;
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_materials_copy_to_object(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Copy Materials to Selected Object";
+ ot->idname = "GPENCIL_OT_materials_copy_to_object";
+ ot->description = "Append Materials of the active Grease Pencil to other object";
+
+ /* callbacks */
+ ot->exec = gpencil_materials_copy_to_object_exec;
+ ot->poll = gpencil_materials_copy_to_object_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ prop = RNA_def_boolean(ot->srna,
+ "only_active",
+ true,
+ "Only Active",
+ "Append only active material, uncheck to append all materials");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+}
+
/* Parent GPencil object to Lattice */
bool ED_gpencil_add_lattice_modifier(const bContext *C,
ReportList *reports,
@@ -3717,3 +3807,51 @@ void GPENCIL_OT_layer_mask_remove(wmOperatorType *ot)
ot->exec = gpencil_layer_mask_remove_exec;
ot->poll = gpencil_active_layer_poll;
}
+
+static int gpencil_layer_mask_move_exec(bContext *C, wmOperator *op)
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
+ const int direction = RNA_enum_get(op->ptr, "type");
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd, gpl)) {
+ return OPERATOR_CANCELLED;
+ }
+ if (gpl->act_mask > 0) {
+ bGPDlayer_Mask *mask = BLI_findlink(&gpl->mask_layers, gpl->act_mask - 1);
+ if (mask != NULL) {
+ BLI_assert(ELEM(direction, -1, 0, 1)); /* we use value below */
+ if (BLI_listbase_link_move(&gpl->mask_layers, mask, direction)) {
+ gpl->act_mask += direction;
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ }
+ }
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_layer_mask_move(wmOperatorType *ot)
+{
+ static const EnumPropertyItem slot_move[] = {
+ {GP_LAYER_MOVE_UP, "UP", 0, "Up", ""},
+ {GP_LAYER_MOVE_DOWN, "DOWN", 0, "Down", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ /* identifiers */
+ ot->name = "Move Grease Pencil Layer Mask";
+ ot->idname = "GPENCIL_OT_layer_mask_move";
+ ot->description = "Move the active Grease Pencil mask layer up/down in the list";
+
+ /* api callbacks */
+ ot->exec = gpencil_layer_mask_move_exec;
+ ot->poll = gpencil_active_layer_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ ot->prop = RNA_def_enum(ot->srna, "type", slot_move, 0, "Type", "");
+}
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index 4bbd475dd2c..f29f5187015 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -1677,7 +1677,7 @@ static int gpencil_strokes_paste_exec(bContext *C, wmOperator *op)
if (gpl == NULL) {
/* no active layer - let's just create one */
- gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
+ gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true, false);
}
else if ((BKE_gpencil_layer_is_editable(gpl) == false) && (type == GP_COPY_TO_ACTIVE)) {
BKE_report(
@@ -1818,18 +1818,13 @@ static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = (bGPdata *)ob->data;
- Scene *scene = CTX_data_scene(C);
bGPDlayer *target_layer = NULL;
ListBase strokes = {NULL, NULL};
int layer_num = RNA_int_get(op->ptr, "layer");
const bool use_autolock = (bool)(gpd->flag & GP_DATA_AUTOLOCK_LAYERS);
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
- if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) {
- BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition");
- return OPERATOR_CANCELLED;
- }
-
- /* if autolock enabled, disabled now */
+ /* If autolock enabled, disabled now. */
if (use_autolock) {
gpd->flag &= ~GP_DATA_AUTOLOCK_LAYERS;
}
@@ -1840,7 +1835,7 @@ static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op)
}
else {
/* Create a new layer. */
- target_layer = BKE_gpencil_layer_addnew(gpd, "GP_Layer", true);
+ target_layer = BKE_gpencil_layer_addnew(gpd, "GP_Layer", true, false);
}
if (target_layer == NULL) {
@@ -1852,53 +1847,59 @@ static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- /* Extract all strokes to move to this layer
- * NOTE: We need to do this in a two-pass system to avoid conflicts with strokes
- * getting repeatedly moved
- */
- CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
- bGPDframe *gpf = gpl->actframe;
-
- /* skip if no frame with strokes, or if this is the layer we're moving strokes to */
- if ((gpl == target_layer) || (gpf == NULL)) {
+ /* Extract all strokes to move to this layer. */
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl_src, editable_gpencil_layers) {
+ /* Skip if this is the layer we're moving strokes to. */
+ if (gpl_src == target_layer) {
continue;
}
+ bGPDframe *init_gpf = (is_multiedit) ? gpl_src->frames.first : gpl_src->actframe;
+ for (bGPDframe *gpf_src = init_gpf; gpf_src; gpf_src = gpf_src->next) {
+ if ((gpf_src == gpl_src->actframe) ||
+ ((gpf_src->flag & GP_FRAME_SELECT) && (is_multiedit))) {
+ if (gpf_src == NULL) {
+ continue;
+ }
- /* make copies of selected strokes, and deselect these once we're done */
- LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
+ bGPDstroke *gpsn = NULL;
+ BLI_listbase_clear(&strokes);
+ for (bGPDstroke *gps = gpf_src->strokes.first; gps; gps = gpsn) {
+ gpsn = gps->next;
+ /* Skip strokes that are invalid for current view. */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
+ }
+ /* Check if the color is editable. */
+ if (ED_gpencil_stroke_material_editable(ob, gpl_src, gps) == false) {
+ continue;
+ }
- /* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps) == false) {
- continue;
- }
+ if (gps->flag & GP_STROKE_SELECT) {
+ BLI_remlink(&gpf_src->strokes, gps);
+ BLI_addtail(&strokes, gps);
+ }
+ }
+ /* Paste them all in one go. */
+ if (strokes.first) {
+ bGPDframe *gpf_dst = BKE_gpencil_layer_frame_get(
+ target_layer, gpf_src->framenum, GP_GETFRAME_ADD_NEW);
- /* Check if the color is editable. */
- if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
- continue;
+ BLI_movelisttolist(&gpf_dst->strokes, &strokes);
+ BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL));
+ }
}
-
- /* TODO: Don't just move entire strokes - instead, only copy the selected portions... */
- if (gps->flag & GP_STROKE_SELECT) {
- BLI_remlink(&gpf->strokes, gps);
- BLI_addtail(&strokes, gps);
+ /* If not multi-edit, exit loop. */
+ if (!is_multiedit) {
+ break;
}
}
-
- /* if new layer and autolock, lock old layer */
+ /* If new layer and autolock, lock old layer. */
if ((layer_num == -1) && (use_autolock)) {
- gpl->flag |= GP_LAYER_LOCKED;
+ gpl_src->flag |= GP_LAYER_LOCKED;
}
}
CTX_DATA_END;
- /* Paste them all in one go */
- if (strokes.first) {
- bGPDframe *gpf = BKE_gpencil_layer_frame_get(target_layer, CFRA, GP_GETFRAME_ADD_NEW);
-
- BLI_movelisttolist(&gpf->strokes, &strokes);
- BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL));
- }
-
/* back autolock status */
if (use_autolock) {
gpd->flag |= GP_DATA_AUTOLOCK_LAYERS;
@@ -2778,7 +2779,7 @@ void GPENCIL_OT_dissolve(wmOperatorType *ot)
/* Poll callback for snap operators */
/* NOTE: For now, we only allow these in the 3D view, as other editors do not
- * define a cursor or gridstep which can be used
+ * define a cursor or grid-step which can be used.
*/
static bool gpencil_snap_poll(bContext *C)
{
@@ -3442,7 +3443,7 @@ void GPENCIL_OT_stroke_caps_set(wmOperatorType *ot)
{GP_STROKE_CAPS_TOGGLE_BOTH, "TOGGLE", 0, "Both", ""},
{GP_STROKE_CAPS_TOGGLE_START, "START", 0, "Start", ""},
{GP_STROKE_CAPS_TOGGLE_END, "END", 0, "End", ""},
- {GP_STROKE_CAPS_TOGGLE_DEFAULT, "TOGGLE", 0, "Default", "Set as default rounded"},
+ {GP_STROKE_CAPS_TOGGLE_DEFAULT, "DEFAULT", 0, "Default", "Set as default rounded"},
{0, NULL, 0, NULL, NULL},
};
@@ -3762,6 +3763,7 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op)
const eGP_ReprojectModes mode = RNA_enum_get(op->ptr, "type");
const bool keep_original = RNA_boolean_get(op->ptr, "keep_original");
const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
/* Init snap context for geometry projection. */
SnapObjectContext *sctx = NULL;
@@ -3774,36 +3776,55 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op)
int cfra_prv = INT_MIN;
/* Go through each editable + selected stroke, adjusting each of its points one by one... */
- GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
- bool curve_select = false;
- if (is_curve_edit && gps->editcurve != NULL) {
- curve_select = gps->editcurve->flag & GP_CURVE_SELECT;
- }
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
- if (gps->flag & GP_STROKE_SELECT || curve_select) {
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
+ if (gpf == NULL) {
+ continue;
+ }
+ for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
+ }
+ bool curve_select = false;
+ if (is_curve_edit && gps->editcurve != NULL) {
+ curve_select = gps->editcurve->flag & GP_CURVE_SELECT;
+ }
- /* update frame to get the new location of objects */
- if ((mode == GP_REPROJECT_SURFACE) && (cfra_prv != gpf_->framenum)) {
- cfra_prv = gpf_->framenum;
- CFRA = gpf_->framenum;
- BKE_scene_graph_update_for_newframe(depsgraph);
- }
+ if (gps->flag & GP_STROKE_SELECT || curve_select) {
- ED_gpencil_stroke_reproject(depsgraph, &gsc, sctx, gpl, gpf_, gps, mode, keep_original);
+ /* update frame to get the new location of objects */
+ if ((mode == GP_REPROJECT_SURFACE) && (cfra_prv != gpf->framenum)) {
+ cfra_prv = gpf->framenum;
+ CFRA = gpf->framenum;
+ BKE_scene_graph_update_for_newframe(depsgraph);
+ }
- if (is_curve_edit && gps->editcurve != NULL) {
- BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps);
- /* Update the selection from the stroke to the curve. */
- BKE_gpencil_editcurve_stroke_sync_selection(gpd, gps, gps->editcurve);
+ ED_gpencil_stroke_reproject(depsgraph, &gsc, sctx, gpl, gpf, gps, mode, keep_original);
- gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
- BKE_gpencil_stroke_geometry_update(gpd, gps);
- }
+ if (is_curve_edit && gps->editcurve != NULL) {
+ BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps);
+ /* Update the selection from the stroke to the curve. */
+ BKE_gpencil_editcurve_stroke_sync_selection(gpd, gps, gps->editcurve);
- changed = true;
+ gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
+ }
+
+ changed = true;
+ }
+ }
+ }
+ /* If not multi-edit, exit loop. */
+ if (!is_multiedit) {
+ break;
+ }
}
}
- GP_EDITABLE_STROKES_END(gpstroke_iter);
+ CTX_DATA_END;
/* return frame state and DB to original state */
CFRA = oldframe;
@@ -3832,7 +3853,8 @@ void GPENCIL_OT_reproject(wmOperatorType *ot)
"VIEW",
0,
"View",
- "Reproject the strokes to end up on the same plane, as if drawn from the current viewpoint "
+ "Reproject the strokes to end up on the same plane, as if drawn from the current "
+ "viewpoint "
"using 'Cursor' Stroke Placement"},
{GP_REPROJECT_SURFACE,
"SURFACE",
@@ -3851,7 +3873,8 @@ void GPENCIL_OT_reproject(wmOperatorType *ot)
ot->name = "Reproject Strokes";
ot->idname = "GPENCIL_OT_reproject";
ot->description =
- "Reproject the selected strokes from the current viewpoint as if they had been newly drawn "
+ "Reproject the selected strokes from the current viewpoint as if they had been newly "
+ "drawn "
"(e.g. to fix problems from accidental 3D cursor movement or accidental viewport changes, "
"or for matching deforming geometry)";
@@ -3951,7 +3974,7 @@ static void gpencil_smooth_stroke(bContext *C, wmOperator *op)
}
if (smooth_thickness) {
/* thickness need to repeat process several times */
- for (int r2 = 0; r2 < r * 20; r2++) {
+ for (int r2 = 0; r2 < 20; r2++) {
BKE_gpencil_stroke_smooth_thickness(gps, i, factor);
}
}
@@ -3981,6 +4004,11 @@ static int gpencil_count_subdivision_cuts(bGPDstroke *gps)
}
}
+ if ((gps->flag & GP_STROKE_CYCLIC) && (gps->points[0].flag & GP_SPOINT_SELECT) &&
+ (gps->points[gps->totpoints - 1].flag & GP_SPOINT_SELECT)) {
+ totnewpoints++;
+ }
+
return totnewpoints;
}
@@ -4079,6 +4107,47 @@ static void gpencil_stroke_subdivide(bGPDstroke *gps, const int cuts)
}
}
}
+
+ /* Subdivide between last and first point. */
+ if (gps->flag & GP_STROKE_CYCLIC) {
+ bGPDspoint *pt = &temp_points[oldtotpoints - 1];
+ bGPDspoint *next = &temp_points[0];
+ if ((pt->flag & GP_SPOINT_SELECT) && (next->flag & GP_SPOINT_SELECT)) {
+ bGPDspoint *pt_final = &gps->points[i2];
+ if (gps->dvert != NULL) {
+ dvert_final = &gps->dvert[i2];
+ }
+ /* Interpolate all values */
+ interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f);
+ pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f);
+ pt_final->strength = interpf(pt->strength, next->strength, 0.5f);
+ CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f);
+ interp_v4_v4v4(pt_final->vert_color, pt->vert_color, next->vert_color, 0.5f);
+ pt_final->time = interpf(pt->time, next->time, 0.5f);
+ pt_final->flag |= GP_SPOINT_SELECT;
+
+ /* interpolate weights */
+ if (gps->dvert != NULL) {
+ dvert = &temp_dverts[oldtotpoints - 1];
+ dvert_next = &temp_dverts[0];
+ dvert_final = &gps->dvert[i2];
+
+ dvert_final->totweight = dvert->totweight;
+ dvert_final->dw = MEM_dupallocN(dvert->dw);
+
+ /* interpolate weight values */
+ for (int d = 0; d < dvert->totweight; d++) {
+ MDeformWeight *dw_a = &dvert->dw[d];
+ if (dvert_next->totweight > d) {
+ MDeformWeight *dw_b = &dvert_next->dw[d];
+ MDeformWeight *dw_final = &dvert_final->dw[d];
+ dw_final->weight = interpf(dw_a->weight, dw_b->weight, 0.5f);
+ }
+ }
+ }
+ }
+ }
+
/* free temp memory */
MEM_SAFE_FREE(temp_points);
MEM_SAFE_FREE(temp_dverts);
@@ -4162,7 +4231,8 @@ void GPENCIL_OT_stroke_subdivide(wmOperatorType *ot)
ot->name = "Subdivide Stroke";
ot->idname = "GPENCIL_OT_stroke_subdivide";
ot->description =
- "Subdivide between continuous selected points of the stroke adding a point half way between "
+ "Subdivide between continuous selected points of the stroke adding a point half way "
+ "between "
"them";
/* api callbacks */
@@ -4474,6 +4544,9 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op)
eGP_SeparateModes mode = RNA_enum_get(op->ptr, "mode");
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_src);
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd_src);
+
/* sanity checks */
if (ELEM(NULL, gpd_src)) {
return OPERATOR_CANCELLED;
@@ -4484,8 +4557,22 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_src);
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd_src);
+ /* Cancel if nothing selected. */
+ if (ELEM(mode, GP_SEPARATE_POINT, GP_SEPARATE_STROKE)) {
+ bool has_selected = false;
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ if (ED_gpencil_layer_has_selected_stroke(gpl, is_multiedit)) {
+ has_selected = true;
+ break;
+ }
+ }
+ CTX_DATA_END;
+
+ if (!has_selected) {
+ BKE_report(op->reports, RPT_ERROR, "Nothing selected");
+ return OPERATOR_CANCELLED;
+ }
+ }
/* Create a new object. */
/* Take into account user preferences for duplicating actions. */
@@ -4530,7 +4617,7 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op)
if (gps->flag & GP_STROKE_SELECT) {
/* add layer if not created before */
if (gpl_dst == NULL) {
- gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl->info, false);
+ gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl->info, false, false);
}
/* add frame if not created before */
@@ -4979,17 +5066,27 @@ static int gpencil_cutter_lasso_select(bContext *C,
/* init space conversion stuff */
gpencil_point_conversion_init(C, &gsc);
- /* deselect all strokes first */
- CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
- int i;
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- pt->flag &= ~GP_SPOINT_SELECT;
- }
+ /* Deselect all strokes. */
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ if (gps->flag & GP_STROKE_SELECT) {
+ int i;
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ pt->flag &= ~GP_SPOINT_SELECT;
+ }
- gps->flag &= ~GP_STROKE_SELECT;
- BKE_gpencil_stroke_select_index_reset(gps);
+ gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_reset(gps);
+ }
+ }
+ /* if not multiedit, exit loop. */
+ if (!is_multiedit) {
+ break;
+ }
+ }
}
- CTX_DATA_END;
/* Select points */
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
@@ -5250,3 +5347,181 @@ void GPENCIL_OT_stroke_merge_by_distance(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Stroke Normalize Operator
+ * \{ */
+
+typedef enum eGP_NormalizeMode {
+ GP_NORMALIZE_THICKNESS = 0,
+ GP_NORMALIZE_OPACITY,
+} eGP_NormalizeMode;
+
+static bool gpencil_stroke_normalize_poll(bContext *C)
+{
+ Object *ob = CTX_data_active_object(C);
+ if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
+ return false;
+ }
+ bGPdata *gpd = (bGPdata *)ob->data;
+ if (gpd == NULL) {
+ return false;
+ }
+
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
+
+ return ((gpl != NULL) && (ob->mode == OB_MODE_EDIT_GPENCIL));
+}
+
+static void gpencil_stroke_normalize_ui(bContext *UNUSED(C), wmOperator *op)
+{
+ uiLayout *layout = op->layout;
+ uiLayout *row;
+
+ const eGP_NormalizeMode mode = RNA_enum_get(op->ptr, "mode");
+
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ row = uiLayoutRow(layout, true);
+ uiItemR(row, op->ptr, "mode", 0, NULL, ICON_NONE);
+
+ if (mode == GP_NORMALIZE_THICKNESS) {
+ row = uiLayoutRow(layout, true);
+ uiItemR(row, op->ptr, "value", 0, NULL, ICON_NONE);
+ }
+ else if (mode == GP_NORMALIZE_OPACITY) {
+ row = uiLayoutRow(layout, true);
+ uiItemR(row, op->ptr, "factor", 0, NULL, ICON_NONE);
+ }
+}
+
+static int gpencil_stroke_normalize_exec(bContext *C, wmOperator *op)
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+
+ /* Sanity checks. */
+ if (ELEM(NULL, gpd)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ const eGP_NormalizeMode mode = RNA_enum_get(op->ptr, "mode");
+ const int value = RNA_int_get(op->ptr, "value");
+ const float factor = RNA_float_get(op->ptr, "factor");
+
+ /* Go through each editable + selected stroke. */
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
+
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
+
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
+
+ if (gpf == NULL) {
+ continue;
+ }
+
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+
+ /* Skip strokes that are invalid for current view. */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
+ }
+
+ bool selected = (is_curve_edit) ? gps->editcurve->flag |= GP_CURVE_SELECT :
+ (gps->flag & GP_STROKE_SELECT);
+ if (!selected) {
+ continue;
+ }
+
+ float stroke_thickness_inv = 1.0f / max_ii(gps->thickness, 1);
+ /* Fill opacity need to be managed before. */
+ if (mode == GP_NORMALIZE_OPACITY) {
+ gps->fill_opacity_fac = factor;
+ CLAMP(gps->fill_opacity_fac, 0.0f, 1.0f);
+ }
+
+ /* Loop all Polyline points. */
+ if (!is_curve_edit) {
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ if (mode == GP_NORMALIZE_THICKNESS) {
+ pt->pressure = max_ff((float)value * stroke_thickness_inv, 0.0f);
+ }
+ else if (mode == GP_NORMALIZE_OPACITY) {
+ pt->strength = factor;
+ CLAMP(pt->strength, 0.0f, 1.0f);
+ }
+ }
+ }
+ else {
+ /* Loop all Bezier points. */
+ for (int i = 0; i < gps->editcurve->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gps->editcurve->curve_points[i];
+ if (mode == GP_NORMALIZE_THICKNESS) {
+ gpc_pt->pressure = max_ff((float)value * stroke_thickness_inv, 0.0f);
+ }
+ else if (mode == GP_NORMALIZE_OPACITY) {
+ gpc_pt->strength = factor;
+ CLAMP(gpc_pt->strength, 0.0f, 1.0f);
+ }
+ }
+
+ gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
+ }
+ }
+ /* If not multiedit, exit loop. */
+ if (!is_multiedit) {
+ break;
+ }
+ }
+ }
+ }
+ CTX_DATA_END;
+
+ /* notifiers */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_stroke_normalize(wmOperatorType *ot)
+{
+ static const EnumPropertyItem prop_gpencil_normalize_modes[] = {
+ {GP_NORMALIZE_THICKNESS,
+ "THICKNESS",
+ 0,
+ "Thickness",
+ "Normalizes the stroke thickness by making all points use the same thickness value"},
+ {GP_NORMALIZE_OPACITY,
+ "OPACITY",
+ 0,
+ "Opacity",
+ "Normalizes the stroke opacity by making all points use the same opacity value"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ /* identifiers */
+ ot->name = "Normalize Stroke";
+ ot->idname = "GPENCIL_OT_stroke_normalize";
+ ot->description = "Normalize stroke attributes";
+
+ /* api callbacks */
+ ot->exec = gpencil_stroke_normalize_exec;
+ ot->poll = gpencil_stroke_normalize_poll;
+ ot->ui = gpencil_stroke_normalize_ui;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* props */
+ ot->prop = RNA_def_enum(
+ ot->srna, "mode", prop_gpencil_normalize_modes, 0, "Mode", "Attribute to be normalized");
+ RNA_def_float(ot->srna, "factor", 1.0f, 0.0f, 1.0f, "Factor", "", 0.0f, 1.0f);
+ RNA_def_int(ot->srna, "value", 10, 0, 1000, "Value", "Value", 0, 1000);
+}
+
+/** \} */
diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c
index 4749f40fac5..f74e211dd65 100644
--- a/source/blender/editors/gpencil/gpencil_fill.c
+++ b/source/blender/editors/gpencil/gpencil_fill.c
@@ -1029,9 +1029,15 @@ static void gpencil_invert_image(tGPDfill *tgpf)
/* Red->Green */
else if (color[0] == 1.0f) {
set_pixel(ibuf, v, fill_col[1]);
- /* Add thickness of 2 pixels to avoid too thin lines. */
- int offset = (v % ibuf->x < center) ? 1 : -1;
- set_pixel(ibuf, v + offset, fill_col[1]);
+ /* Add thickness of 2 pixels to avoid too thin lines, but avoid extremes of the pixel line.
+ */
+ int row = v / ibuf->x;
+ int lowpix = row * ibuf->x;
+ int highpix = lowpix + ibuf->x - 1;
+ if ((v > lowpix) && (v < highpix)) {
+ int offset = (v % ibuf->x < center) ? 1 : -1;
+ set_pixel(ibuf, v + offset, fill_col[1]);
+ }
}
else {
/* Set to Transparent. */
@@ -1241,6 +1247,7 @@ static bool dilate_shape(ImBuf *ibuf)
static void gpencil_get_outline_points(tGPDfill *tgpf, const bool dilate)
{
ImBuf *ibuf;
+ Brush *brush = tgpf->brush;
float rgba[4];
void *lock;
int v[2];
@@ -1273,7 +1280,9 @@ static void gpencil_get_outline_points(tGPDfill *tgpf, const bool dilate)
/* Dilate. */
if (dilate) {
- dilate_shape(ibuf);
+ for (int i = 0; i < brush->gpencil_settings->dilate_pixels; i++) {
+ dilate_shape(ibuf);
+ }
}
for (int idx = imagesize - 1; idx != 0; idx--) {
@@ -1363,7 +1372,8 @@ static void gpencil_get_depth_array(tGPDfill *tgpf)
if (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_VIEW) {
/* need to restore the original projection settings before packing up */
view3d_region_operator_needs_opengl(tgpf->win, tgpf->region);
- ED_view3d_autodist_init(tgpf->depsgraph, tgpf->region, tgpf->v3d, 0);
+ ED_view3d_depth_override(
+ tgpf->depsgraph, tgpf->region, tgpf->v3d, NULL, V3D_DEPTH_NO_GPENCIL, false);
/* Since strokes are so fine, when using their depth we need a margin
* otherwise they might get missed. */
@@ -1679,7 +1689,7 @@ static tGPDfill *gpencil_session_init_fill(bContext *C, wmOperator *op)
tgpf->gpd = gpd;
tgpf->gpl = BKE_gpencil_layer_active_get(gpd);
if (tgpf->gpl == NULL) {
- tgpf->gpl = BKE_gpencil_layer_addnew(tgpf->gpd, DATA_("GP_Layer"), true);
+ tgpf->gpl = BKE_gpencil_layer_addnew(tgpf->gpd, DATA_("GP_Layer"), true, false);
}
tgpf->lock_axis = ts->gp_sculpt.lock_axis;
diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h
index c6f74c39beb..276e8c81e0f 100644
--- a/source/blender/editors/gpencil/gpencil_intern.h
+++ b/source/blender/editors/gpencil/gpencil_intern.h
@@ -421,6 +421,7 @@ void GPENCIL_OT_layer_duplicate_object(struct wmOperatorType *ot);
void GPENCIL_OT_layer_mask_add(struct wmOperatorType *ot);
void GPENCIL_OT_layer_mask_remove(struct wmOperatorType *ot);
+void GPENCIL_OT_layer_mask_move(struct wmOperatorType *ot);
void GPENCIL_OT_hide(struct wmOperatorType *ot);
void GPENCIL_OT_reveal(struct wmOperatorType *ot);
@@ -443,6 +444,7 @@ void GPENCIL_OT_frame_clean_duplicate(struct wmOperatorType *ot);
void GPENCIL_OT_convert(struct wmOperatorType *ot);
void GPENCIL_OT_bake_mesh_animation(struct wmOperatorType *ot);
+void GPENCIL_OT_bake_grease_pencil_animation(struct wmOperatorType *ot);
void GPENCIL_OT_image_to_grease_pencil(struct wmOperatorType *ot);
void GPENCIL_OT_trace_image(struct wmOperatorType *ot);
@@ -486,6 +488,7 @@ void GPENCIL_OT_stroke_trim(struct wmOperatorType *ot);
void GPENCIL_OT_stroke_merge_by_distance(struct wmOperatorType *ot);
void GPENCIL_OT_stroke_merge_material(struct wmOperatorType *ot);
void GPENCIL_OT_stroke_reset_vertex_color(struct wmOperatorType *ot);
+void GPENCIL_OT_stroke_normalize(struct wmOperatorType *ot);
void GPENCIL_OT_material_to_vertex_color(struct wmOperatorType *ot);
void GPENCIL_OT_extract_palette_vertex(struct wmOperatorType *ot);
@@ -537,6 +540,7 @@ void GPENCIL_OT_material_lock_unused(struct wmOperatorType *ot);
void GPENCIL_OT_material_select(struct wmOperatorType *ot);
void GPENCIL_OT_material_set(struct wmOperatorType *ot);
void GPENCIL_OT_set_active_material(struct wmOperatorType *ot);
+void GPENCIL_OT_materials_copy_to_object(struct wmOperatorType *ot);
/* convert old 2.7 files to 2.8 */
void GPENCIL_OT_convert_old_files(struct wmOperatorType *ot);
@@ -748,4 +752,7 @@ struct GP_EditableStrokes_Iter {
} \
(void)0
+/* Reused items for bake operators. */
+extern const EnumPropertyItem rna_gpencil_reproject_type_items[];
+
/* ****************************************************** */
diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c
index bfa1ee6bcaf..0062e363cdf 100644
--- a/source/blender/editors/gpencil/gpencil_interpolate.c
+++ b/source/blender/editors/gpencil/gpencil_interpolate.c
@@ -84,7 +84,8 @@ typedef struct tGPDinterpolate_layer {
/** interpolate factor */
float factor;
- /* Hash tablets to create temp relationship between strokes. */
+ /* List of strokes and Hash tablets to create temp relationship between strokes. */
+ struct ListBase selected_strokes;
struct GHash *used_strokes;
struct GHash *pair_strokes;
@@ -282,6 +283,7 @@ static void gpencil_stroke_pair_table(bContext *C,
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
/* Create hash tablets with relationship between strokes. */
+ BLI_listbase_clear(&tgpil->selected_strokes);
tgpil->used_strokes = BLI_ghash_ptr_new(__func__);
tgpil->pair_strokes = BLI_ghash_ptr_new(__func__);
@@ -315,7 +317,8 @@ static void gpencil_stroke_pair_table(bContext *C,
if (ELEM(NULL, gps_from, gps_to)) {
continue;
}
- /* Insert the pair entry in the hash table. */
+ /* Insert the pair entry in the hash table and the list of strokes to keep order. */
+ BLI_addtail(&tgpil->selected_strokes, BLI_genericNodeN(gps_from));
BLI_ghash_insert(tgpil->pair_strokes, gps_from, gps_to);
}
}
@@ -405,10 +408,13 @@ static void gpencil_interpolate_update_strokes(bContext *C, tGPDinterpolate *tgp
/* Clear previous interpolations. */
gpencil_interpolate_free_tagged_strokes(tgpil->interFrame);
- GHashIterator gh_iter;
- GHASH_ITER (gh_iter, tgpil->pair_strokes) {
- bGPDstroke *gps_from = (bGPDstroke *)BLI_ghashIterator_getKey(&gh_iter);
- bGPDstroke *gps_to = (bGPDstroke *)BLI_ghashIterator_getValue(&gh_iter);
+ LISTBASE_FOREACH (LinkData *, link, &tgpil->selected_strokes) {
+ bGPDstroke *gps_from = link->data;
+ if (!BLI_ghash_haskey(tgpil->pair_strokes, gps_from)) {
+ continue;
+ }
+ bGPDstroke *gps_to = (bGPDstroke *)BLI_ghash_lookup(tgpil->pair_strokes, gps_from);
+
/* Create new stroke. */
bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true);
new_stroke->flag |= GP_STROKE_TAG;
@@ -527,10 +533,12 @@ static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
gpencil_stroke_pair_table(C, tgpi, tgpil);
/* Create new strokes data with interpolated points reading original stroke. */
- GHashIterator gh_iter;
- GHASH_ITER (gh_iter, tgpil->pair_strokes) {
- bGPDstroke *gps_from = (bGPDstroke *)BLI_ghashIterator_getKey(&gh_iter);
- bGPDstroke *gps_to = (bGPDstroke *)BLI_ghashIterator_getValue(&gh_iter);
+ LISTBASE_FOREACH (LinkData *, link, &tgpil->selected_strokes) {
+ bGPDstroke *gps_from = link->data;
+ if (!BLI_ghash_haskey(tgpil->pair_strokes, gps_from)) {
+ continue;
+ }
+ bGPDstroke *gps_to = (bGPDstroke *)BLI_ghash_lookup(tgpil->pair_strokes, gps_from);
/* If destination stroke is smaller, resize new_stroke to size of gps_to stroke. */
if (gps_from->totpoints > gps_to->totpoints) {
@@ -658,6 +666,9 @@ static void gpencil_interpolate_exit(bContext *C, wmOperator *op)
MEM_SAFE_FREE(tgpil->nextFrame);
MEM_SAFE_FREE(tgpil->interFrame);
+ /* Free list of strokes. */
+ BLI_freelistN(&tgpil->selected_strokes);
+
/* Free Hash tablets. */
if (tgpil->used_strokes != NULL) {
BLI_ghash_free(tgpil->used_strokes, NULL, NULL);
@@ -1292,9 +1303,9 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
bGPDframe *nextFrame = BKE_gpencil_frame_duplicate(gpf_next, true);
/* Create a table with source and target pair of strokes. */
+ ListBase selected_strokes = {NULL};
GHash *used_strokes = BLI_ghash_ptr_new(__func__);
GHash *pair_strokes = BLI_ghash_ptr_new(__func__);
-
LISTBASE_FOREACH (bGPDstroke *, gps_from, &prevFrame->strokes) {
bGPDstroke *gps_to = NULL;
/* Only selected. */
@@ -1342,7 +1353,9 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
}
}
- /* Insert the pair entry in the hash table. */
+ /* Insert the pair entry in the hash table and in the list of strokes to keep same order.
+ */
+ BLI_addtail(&selected_strokes, BLI_genericNodeN(gps_from));
BLI_ghash_insert(pair_strokes, gps_from, gps_to);
}
@@ -1369,11 +1382,12 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
}
/* Apply the factor to all pair of strokes. */
- GHashIterator gh_iter;
- GHASH_ITER (gh_iter, pair_strokes) {
- bGPDstroke *gps_from = (bGPDstroke *)BLI_ghashIterator_getKey(&gh_iter);
- bGPDstroke *gps_to = (bGPDstroke *)BLI_ghashIterator_getValue(&gh_iter);
-
+ LISTBASE_FOREACH (LinkData *, link, &selected_strokes) {
+ bGPDstroke *gps_from = link->data;
+ if (!BLI_ghash_haskey(pair_strokes, gps_from)) {
+ continue;
+ }
+ bGPDstroke *gps_to = (bGPDstroke *)BLI_ghash_lookup(pair_strokes, gps_from);
/* Create new stroke. */
bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true);
new_stroke->flag |= GP_STROKE_TAG;
@@ -1394,6 +1408,8 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
}
}
+ BLI_freelistN(&selected_strokes);
+
/* Free Hash tablets. */
if (used_strokes != NULL) {
BLI_ghash_free(used_strokes, NULL, NULL);
@@ -1419,34 +1435,31 @@ static void gpencil_interpolate_seq_ui(bContext *C, wmOperator *op)
{
uiLayout *layout = op->layout;
uiLayout *col, *row;
- PointerRNA ptr;
-
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
const eGP_Interpolate_Type type = RNA_enum_get(op->ptr, "type");
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
row = uiLayoutRow(layout, true);
- uiItemR(row, &ptr, "step", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "step", 0, NULL, ICON_NONE);
row = uiLayoutRow(layout, true);
- uiItemR(row, &ptr, "layers", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "layers", 0, NULL, ICON_NONE);
if (CTX_data_mode_enum(C) == CTX_MODE_EDIT_GPENCIL) {
row = uiLayoutRow(layout, true);
- uiItemR(row, &ptr, "interpolate_selected_only", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "interpolate_selected_only", 0, NULL, ICON_NONE);
}
row = uiLayoutRow(layout, true);
- uiItemR(row, &ptr, "flip", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "flip", 0, NULL, ICON_NONE);
col = uiLayoutColumn(layout, true);
- uiItemR(col, &ptr, "smooth_factor", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "smooth_steps", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "smooth_factor", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "smooth_steps", 0, NULL, ICON_NONE);
row = uiLayoutRow(layout, true);
- uiItemR(row, &ptr, "type", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "type", 0, NULL, ICON_NONE);
if (type == GP_IPO_CURVEMAP) {
/* Get an RNA pointer to ToolSettings to give to the custom curve. */
@@ -1460,16 +1473,16 @@ static void gpencil_interpolate_seq_ui(bContext *C, wmOperator *op)
}
else if (type != GP_IPO_LINEAR) {
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "easing", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "easing", 0, NULL, ICON_NONE);
if (type == GP_IPO_BACK) {
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "back", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "back", 0, NULL, ICON_NONE);
}
else if (type == GP_IPO_ELASTIC) {
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "amplitude", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "amplitude", 0, NULL, ICON_NONE);
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "period", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "period", 0, NULL, ICON_NONE);
}
}
}
@@ -1690,7 +1703,7 @@ static bool gpencil_interpolate_reverse_poll(bContext *C)
if (area == NULL) {
return false;
}
- if ((area->spacetype != SPACE_VIEW3D) && (area->spacetype != SPACE_ACTION)) {
+ if (!ELEM(area->spacetype, SPACE_VIEW3D, SPACE_ACTION)) {
return false;
}
diff --git a/source/blender/editors/gpencil/gpencil_mesh.c b/source/blender/editors/gpencil/gpencil_mesh.c
index b7ed77801c0..55468dffab0 100644
--- a/source/blender/editors/gpencil/gpencil_mesh.c
+++ b/source/blender/editors/gpencil/gpencil_mesh.c
@@ -402,25 +402,6 @@ static int gpencil_bake_mesh_animation_invoke(bContext *C,
void GPENCIL_OT_bake_mesh_animation(wmOperatorType *ot)
{
- static const EnumPropertyItem reproject_type[] = {
- {GP_REPROJECT_KEEP, "KEEP", 0, "No Reproject", ""},
- {GP_REPROJECT_FRONT, "FRONT", 0, "Front", "Reproject the strokes using the X-Z plane"},
- {GP_REPROJECT_SIDE, "SIDE", 0, "Side", "Reproject the strokes using the Y-Z plane"},
- {GP_REPROJECT_TOP, "TOP", 0, "Top", "Reproject the strokes using the X-Y plane"},
- {GP_REPROJECT_VIEW,
- "VIEW",
- 0,
- "View",
- "Reproject the strokes to end up on the same plane, as if drawn from the current viewpoint "
- "using 'Cursor' Stroke Placement"},
- {GP_REPROJECT_CURSOR,
- "CURSOR",
- 0,
- "Cursor",
- "Reproject the strokes using the orientation of 3D cursor"},
- {0, NULL, 0, NULL, NULL},
- };
-
static const EnumPropertyItem target_object_modes[] = {
{GP_TARGET_OB_NEW, "NEW", 0, "New Object", ""},
{GP_TARGET_OB_SELECTED, "SELECTED", 0, "Selected Object", ""},
@@ -491,5 +472,10 @@ void GPENCIL_OT_bake_mesh_animation(wmOperatorType *ot)
RNA_def_int(
ot->srna, "frame_target", 1, 1, 100000, "Target Frame", "Destination frame", 1, 100000);
- RNA_def_enum(ot->srna, "project_type", reproject_type, GP_REPROJECT_VIEW, "Projection Type", "");
+ RNA_def_enum(ot->srna,
+ "project_type",
+ rna_gpencil_reproject_type_items,
+ GP_REPROJECT_VIEW,
+ "Projection Type",
+ "");
}
diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c
index 1a6cb5670c4..35640cf3b66 100644
--- a/source/blender/editors/gpencil/gpencil_ops.c
+++ b/source/blender/editors/gpencil/gpencil_ops.c
@@ -601,6 +601,7 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_layer_mask_add);
WM_operatortype_append(GPENCIL_OT_layer_mask_remove);
+ WM_operatortype_append(GPENCIL_OT_layer_mask_move);
WM_operatortype_append(GPENCIL_OT_hide);
WM_operatortype_append(GPENCIL_OT_reveal);
@@ -621,6 +622,7 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_convert);
WM_operatortype_append(GPENCIL_OT_bake_mesh_animation);
+ WM_operatortype_append(GPENCIL_OT_bake_grease_pencil_animation);
WM_operatortype_append(GPENCIL_OT_image_to_grease_pencil);
#ifdef WITH_POTRACE
@@ -647,9 +649,11 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_stroke_merge_by_distance);
WM_operatortype_append(GPENCIL_OT_stroke_merge_material);
WM_operatortype_append(GPENCIL_OT_stroke_reset_vertex_color);
+ WM_operatortype_append(GPENCIL_OT_stroke_normalize);
WM_operatortype_append(GPENCIL_OT_material_to_vertex_color);
WM_operatortype_append(GPENCIL_OT_extract_palette_vertex);
+ WM_operatortype_append(GPENCIL_OT_materials_copy_to_object);
WM_operatortype_append(GPENCIL_OT_transform_fill);
WM_operatortype_append(GPENCIL_OT_reset_transform_fill);
diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c
index 1217a3a7e8f..e40748e5f6e 100644
--- a/source/blender/editors/gpencil/gpencil_paint.c
+++ b/source/blender/editors/gpencil/gpencil_paint.c
@@ -1332,7 +1332,7 @@ static float view3d_point_depth(const RegionView3D *rv3d, const float co[3])
/* only erase stroke points that are visible */
static bool gpencil_stroke_eraser_is_occluded(
- tGPsdata *p, bGPDlayer *gpl, const bGPDspoint *pt, const int x, const int y)
+ tGPsdata *p, bGPDlayer *gpl, bGPDspoint *pt, const int x, const int y)
{
Object *obact = (Object *)p->ownerPtr.data;
Brush *brush = p->brush;
@@ -1364,7 +1364,11 @@ static bool gpencil_stroke_eraser_is_occluded(
mul_v3_m4v3(fpt, diff_mat, &pt->x);
const float depth_pt = view3d_point_depth(rv3d, fpt);
+ /* Checked occlusion flag. */
+ pt->flag |= GP_SPOINT_TEMP_TAG;
if (depth_pt > depth_mval) {
+ /* Is occluded. */
+ pt->flag |= GP_SPOINT_TEMP_TAG2;
return true;
}
}
@@ -1540,6 +1544,10 @@ static void gpencil_stroke_eraser_dostroke(tGPsdata *p,
for (i = 0; i < gps->totpoints; i++) {
bGPDspoint *pt = &gps->points[i];
pt->flag &= ~GP_SPOINT_TAG;
+ /* Occlusion already checked. */
+ pt->flag &= ~GP_SPOINT_TEMP_TAG;
+ /* Point is occluded. */
+ pt->flag &= ~GP_SPOINT_TEMP_TAG2;
}
/* First Pass: Loop over the points in the stroke
@@ -1585,9 +1593,23 @@ static void gpencil_stroke_eraser_dostroke(tGPsdata *p,
* - this assumes that linewidth is irrelevant
*/
if (gpencil_stroke_inside_circle(mval, radius, pc0[0], pc0[1], pc2[0], pc2[1])) {
- if ((gpencil_stroke_eraser_is_occluded(p, gpl, pt0, pc0[0], pc0[1]) == false) ||
- (gpencil_stroke_eraser_is_occluded(p, gpl, pt1, pc1[0], pc1[1]) == false) ||
- (gpencil_stroke_eraser_is_occluded(p, gpl, pt2, pc2[0], pc2[1]) == false)) {
+
+ bool is_occluded_pt0, is_occluded_pt1, is_occluded_pt2 = true;
+ is_occluded_pt0 = (pt0 && ((pt0->flag & GP_SPOINT_TEMP_TAG) != 0)) ?
+ ((pt0->flag & GP_SPOINT_TEMP_TAG2) != 0) :
+ gpencil_stroke_eraser_is_occluded(p, gpl, pt0, pc0[0], pc0[1]);
+ if (is_occluded_pt0) {
+ is_occluded_pt1 = ((pt1->flag & GP_SPOINT_TEMP_TAG) != 0) ?
+ ((pt1->flag & GP_SPOINT_TEMP_TAG2) != 0) :
+ gpencil_stroke_eraser_is_occluded(p, gpl, pt1, pc1[0], pc1[1]);
+ if (is_occluded_pt1) {
+ is_occluded_pt2 = ((pt2->flag & GP_SPOINT_TEMP_TAG) != 0) ?
+ ((pt2->flag & GP_SPOINT_TEMP_TAG2) != 0) :
+ gpencil_stroke_eraser_is_occluded(p, gpl, pt2, pc2[0], pc2[1]);
+ }
+ }
+
+ if (!is_occluded_pt0 || !is_occluded_pt1 || !is_occluded_pt2) {
/* Point is affected: */
/* Adjust thickness
* - Influence of eraser falls off with distance from the middle of the eraser
@@ -1722,7 +1744,7 @@ static void gpencil_stroke_doeraser(tGPsdata *p)
if ((gp_settings != NULL) && (gp_settings->flag & GP_BRUSH_OCCLUDE_ERASER)) {
View3D *v3d = p->area->spacedata.first;
view3d_region_operator_needs_opengl(p->win, p->region);
- ED_view3d_autodist_init(p->depsgraph, p->region, v3d, 0);
+ ED_view3d_depth_override(p->depsgraph, p->region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, false);
}
}
@@ -2120,7 +2142,7 @@ static void gpencil_paint_initstroke(tGPsdata *p,
/* get active layer (or add a new one if non-existent) */
p->gpl = BKE_gpencil_layer_active_get(p->gpd);
if (p->gpl == NULL) {
- p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("GP_Layer"), true);
+ p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("GP_Layer"), true, false);
changed = true;
if (p->custom_color[3]) {
copy_v3_v3(p->gpl->color, p->custom_color);
@@ -2305,8 +2327,14 @@ static void gpencil_paint_strokeend(tGPsdata *p)
/* need to restore the original projection settings before packing up */
view3d_region_operator_needs_opengl(p->win, p->region);
- ED_view3d_autodist_init(
- p->depsgraph, p->region, v3d, (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0);
+ ED_view3d_depth_override(p->depsgraph,
+ p->region,
+ v3d,
+ NULL,
+ (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ?
+ V3D_DEPTH_GPENCIL_ONLY :
+ V3D_DEPTH_NO_GPENCIL,
+ false);
}
/* check if doing eraser or not */
@@ -3259,7 +3287,7 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event
gpencil_guide_event_handling(C, op, event, p);
}
- if (ob && (ob->type == OB_GPENCIL) && ((p->gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0)) {
+ if ((ob->type == OB_GPENCIL) && ((p->gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0)) {
/* FIXME: use the mode switching operator, this misses notifiers, messages. */
/* Just set paintmode flag... */
p->gpd->flag |= GP_DATA_STROKE_PAINTMODE;
@@ -3671,14 +3699,6 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
* is essential for ensuring that they can quickly return to that view
*/
}
- else if ((event->type == EVT_BKEY) && (event->val == KM_RELEASE)) {
- /* Add Blank Frame
- * - Since this operator is non-modal, we can just call it here, and keep going...
- * - This operator is especially useful when animating
- */
- WM_operator_name_call(C, "GPENCIL_OT_blank_frame_add", WM_OP_EXEC_DEFAULT, NULL);
- estate = OPERATOR_RUNNING_MODAL;
- }
else if ((!ELEM(p->paintmode, GP_PAINTMODE_ERASER, GP_PAINTMODE_SET_CP))) {
gpencil_guide_event_handling(C, op, event, p);
estate = OPERATOR_RUNNING_MODAL;
@@ -3691,7 +3711,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* Exit painting mode (and/or end current stroke).
*
*/
- if (ELEM(event->type, EVT_RETKEY, EVT_PADENTER, EVT_ESCKEY, EVT_SPACEKEY, EVT_EKEY)) {
+ if (ELEM(event->type, EVT_RETKEY, EVT_PADENTER, EVT_ESCKEY, EVT_SPACEKEY)) {
p->status = GP_STATUS_DONE;
estate = OPERATOR_FINISHED;
diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c
index dfff0ce639e..5f02bbf0a77 100644
--- a/source/blender/editors/gpencil/gpencil_primitive.c
+++ b/source/blender/editors/gpencil/gpencil_primitive.c
@@ -314,7 +314,7 @@ static void gpencil_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi)
/* if layer doesn't exist, create a new one */
if (gpl == NULL) {
- gpl = BKE_gpencil_layer_addnew(tgpi->gpd, DATA_("Primitives"), true);
+ gpl = BKE_gpencil_layer_addnew(tgpi->gpd, DATA_("Primitives"), true, false);
}
tgpi->gpl = gpl;
@@ -785,10 +785,14 @@ static void gpencil_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
/* need to restore the original projection settings before packing up */
view3d_region_operator_needs_opengl(tgpi->win, tgpi->region);
- ED_view3d_autodist_init(tgpi->depsgraph,
- tgpi->region,
- tgpi->v3d,
- (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0);
+ ED_view3d_depth_override(tgpi->depsgraph,
+ tgpi->region,
+ tgpi->v3d,
+ NULL,
+ (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ?
+ V3D_DEPTH_GPENCIL_ONLY :
+ V3D_DEPTH_NO_GPENCIL,
+ false);
depth_arr = MEM_mallocN(sizeof(float) * gps->totpoints, "depth_points");
tGPspoint *ptc = &points2D[0];
@@ -1532,24 +1536,22 @@ static void gpencil_primitive_strength(tGPDprimitive *tgpi, bool reset)
Brush *brush = tgpi->brush;
BrushGpencilSettings *brush_settings = brush->gpencil_settings;
- if (brush) {
- if (reset) {
- brush_settings->draw_strength = tgpi->brush_strength;
- tgpi->brush_strength = 0.0f;
- }
- else {
- if (tgpi->brush_strength == 0.0f) {
- tgpi->brush_strength = brush_settings->draw_strength;
- }
- float move[2];
- sub_v2_v2v2(move, tgpi->mval, tgpi->mvalo);
- float adjust = (move[1] > 0.0f) ? 0.01f : -0.01f;
- brush_settings->draw_strength += adjust * fabsf(len_manhattan_v2(move));
+ if (reset) {
+ brush_settings->draw_strength = tgpi->brush_strength;
+ tgpi->brush_strength = 0.0f;
+ }
+ else {
+ if (tgpi->brush_strength == 0.0f) {
+ tgpi->brush_strength = brush_settings->draw_strength;
}
-
- /* limit low limit because below 0.2f the stroke is invisible */
- CLAMP(brush_settings->draw_strength, 0.2f, 1.0f);
+ float move[2];
+ sub_v2_v2v2(move, tgpi->mval, tgpi->mvalo);
+ float adjust = (move[1] > 0.0f) ? 0.01f : -0.01f;
+ brush_settings->draw_strength += adjust * fabsf(len_manhattan_v2(move));
}
+
+ /* limit low limit because below 0.2f the stroke is invisible */
+ CLAMP(brush_settings->draw_strength, 0.2f, 1.0f);
}
/* brush size */
diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c
index aab08e9c8c4..e1776988186 100644
--- a/source/blender/editors/gpencil/gpencil_select.c
+++ b/source/blender/editors/gpencil/gpencil_select.c
@@ -505,7 +505,7 @@ void GPENCIL_OT_select_alternate(wmOperatorType *ot)
/* properties */
RNA_def_boolean(ot->srna,
"unselect_ends",
- true,
+ false,
"Unselect Ends",
"Do not select the first and last point of the stroke");
}
diff --git a/source/blender/editors/gpencil/gpencil_trace_ops.c b/source/blender/editors/gpencil/gpencil_trace_ops.c
index d2e5fa3db32..cd75f9313ad 100644
--- a/source/blender/editors/gpencil/gpencil_trace_ops.c
+++ b/source/blender/editors/gpencil/gpencil_trace_ops.c
@@ -207,7 +207,7 @@ static void trace_initialize_job_data(TraceJob *trace_job)
trace_job->gpd = (bGPdata *)trace_job->ob_gpencil->data;
trace_job->gpl = BKE_gpencil_layer_active_get(trace_job->gpd);
if (trace_job->gpl == NULL) {
- trace_job->gpl = BKE_gpencil_layer_addnew(trace_job->gpd, DATA_("Trace"), true);
+ trace_job->gpl = BKE_gpencil_layer_addnew(trace_job->gpd, DATA_("Trace"), true, false);
}
}
diff --git a/source/blender/editors/gpencil/gpencil_trace_utils.c b/source/blender/editors/gpencil/gpencil_trace_utils.c
index ada777d43f3..12c38fb2744 100644
--- a/source/blender/editors/gpencil/gpencil_trace_utils.c
+++ b/source/blender/editors/gpencil/gpencil_trace_utils.c
@@ -281,7 +281,6 @@ void ED_gpencil_trace_data_to_strokes(Main *bmain,
mat_mask_idx = ob->totcol - 1;
}
- potrace_path_t *path = st->plist;
int n, *tag;
potrace_dpoint_t(*c)[3];
@@ -289,7 +288,7 @@ void ED_gpencil_trace_data_to_strokes(Main *bmain,
* good results using the Potrace data. */
const float scalef = 0.008f * scale;
/* Draw each curve. */
- path = st->plist;
+ potrace_path_t *path = st->plist;
while (path != NULL) {
n = path->curve.n;
tag = path->curve.tag;
@@ -308,9 +307,16 @@ void ED_gpencil_trace_data_to_strokes(Main *bmain,
if (gps->totpoints == 0) {
add_point(gps, scalef, offset, c[n - 1][2].x, c[n - 1][2].y);
}
+ else {
+ add_point(gps, scalef, offset, last[0], last[1]);
+ }
+
add_point(gps, scalef, offset, c[i][1].x, c[i][1].y);
add_point(gps, scalef, offset, c[i][2].x, c[i][2].y);
+
+ last[0] = c[i][2].x;
+ last[1] = c[i][2].y;
break;
}
case POTRACE_CURVETO: {
diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c
index 574670de7ca..c9ef340b9d3 100644
--- a/source/blender/editors/gpencil/gpencil_utils.c
+++ b/source/blender/editors/gpencil/gpencil_utils.c
@@ -543,6 +543,38 @@ bool gpencil_stroke_inside_circle(const float mval[2], int rad, int x0, int y0,
}
/* ******************************************************** */
+/* Selection Validity Testing */
+
+bool ED_gpencil_frame_has_selected_stroke(const bGPDframe *gpf)
+{
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ if (gps->flag & GP_STROKE_SELECT) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool ED_gpencil_layer_has_selected_stroke(const bGPDlayer *gpl, const bool is_multiedit)
+{
+ bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
+ if (ED_gpencil_frame_has_selected_stroke(gpf)) {
+ return true;
+ }
+ }
+ /* If not multiedit, exit loop. */
+ if (!is_multiedit) {
+ break;
+ }
+ }
+
+ return false;
+}
+
+/* ******************************************************** */
/* Stroke Validity Testing */
/* Check whether given stroke can be edited given the supplied context */
@@ -648,7 +680,7 @@ void gpencil_point_conversion_init(bContext *C, GP_SpaceConversion *r_gsc)
view3d_operator_needs_opengl(C);
view3d_region_operator_needs_opengl(win, region);
- ED_view3d_autodist_init(depsgraph, region, v3d, 0);
+ ED_view3d_depth_override(depsgraph, region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, false);
/* for camera view set the subrect */
if (rv3d->persp == RV3D_CAMOB) {
@@ -1253,7 +1285,11 @@ void ED_gpencil_stroke_reproject(Depsgraph *depsgraph,
float location[3] = {0.0f, 0.0f, 0.0f};
float normal[3] = {0.0f, 0.0f, 0.0f};
- ED_view3d_win_to_ray(region, xy, &ray_start[0], &ray_normal[0]);
+ BLI_assert(gps->flag & GP_STROKE_3DSPACE);
+ BLI_assert(gsc->area && gsc->area->spacetype == SPACE_VIEW3D);
+ const View3D *v3d = gsc->area->spacedata.first;
+ ED_view3d_win_to_ray_clipped(
+ depsgraph, region, v3d, xy, &ray_start[0], &ray_normal[0], true);
if (ED_transform_snap_object_project_ray(sctx,
depsgraph,
&(const struct SnapObjectParams){