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')
-rw-r--r--source/blender/editors/animation/anim_channels_defines.c10
-rw-r--r--source/blender/editors/animation/anim_channels_edit.c2
-rw-r--r--source/blender/editors/armature/armature_naming.c2
-rw-r--r--source/blender/editors/gpencil/CMakeLists.txt6
-rw-r--r--source/blender/editors/gpencil/annotate_draw.c37
-rw-r--r--source/blender/editors/gpencil/annotate_paint.c21
-rw-r--r--source/blender/editors/gpencil/drawgpencil.c304
-rw-r--r--source/blender/editors/gpencil/editaction_gpencil.c58
-rw-r--r--source/blender/editors/gpencil/gpencil_add_monkey.c93
-rw-r--r--source/blender/editors/gpencil/gpencil_add_stroke.c8
-rw-r--r--source/blender/editors/gpencil/gpencil_armature.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_convert.c104
-rw-r--r--source/blender/editors/gpencil/gpencil_data.c571
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c493
-rw-r--r--source/blender/editors/gpencil/gpencil_fill.c170
-rw-r--r--source/blender/editors/gpencil/gpencil_intern.h68
-rw-r--r--source/blender/editors/gpencil/gpencil_interpolate.c145
-rw-r--r--source/blender/editors/gpencil/gpencil_merge.c126
-rw-r--r--source/blender/editors/gpencil/gpencil_ops.c316
-rw-r--r--source/blender/editors/gpencil/gpencil_ops_versioning.c17
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c1374
-rw-r--r--source/blender/editors/gpencil/gpencil_primitive.c119
-rw-r--r--source/blender/editors/gpencil/gpencil_sculpt_paint.c (renamed from source/blender/editors/gpencil/gpencil_brush.c)735
-rw-r--r--source/blender/editors/gpencil/gpencil_select.c202
-rw-r--r--source/blender/editors/gpencil/gpencil_undo.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_utils.c483
-rw-r--r--source/blender/editors/gpencil/gpencil_uv.c587
-rw-r--r--source/blender/editors/gpencil/gpencil_vertex_ops.c899
-rw-r--r--source/blender/editors/gpencil/gpencil_vertex_paint.c1414
-rw-r--r--source/blender/editors/gpencil/gpencil_weight_paint.c901
-rw-r--r--source/blender/editors/include/ED_gpencil.h56
-rw-r--r--source/blender/editors/interface/interface.c6
-rw-r--r--source/blender/editors/interface/interface_eyedropper_gpencil_color.c114
-rw-r--r--source/blender/editors/interface/interface_handlers.c41
-rw-r--r--source/blender/editors/interface/interface_icons.c53
-rw-r--r--source/blender/editors/interface/interface_layout.c2
-rw-r--r--source/blender/editors/interface/interface_templates.c48
-rw-r--r--source/blender/editors/object/object_add.c4
-rw-r--r--source/blender/editors/object/object_edit.c3
-rw-r--r--source/blender/editors/object/object_gpencil_modifier.c7
-rw-r--r--source/blender/editors/object/object_modes.c5
-rw-r--r--source/blender/editors/object/object_transform.c10
-rw-r--r--source/blender/editors/screen/area.c72
-rw-r--r--source/blender/editors/screen/screen_context.c10
-rw-r--r--source/blender/editors/sculpt_paint/paint_ops.c322
-rw-r--r--source/blender/editors/space_action/action_edit.c2
-rw-r--r--source/blender/editors/space_action/action_select.c4
-rw-r--r--source/blender/editors/space_outliner/outliner_draw.c5
-rw-r--r--source/blender/editors/space_outliner/outliner_select.c2
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c6
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_ruler.c13
-rw-r--r--source/blender/editors/space_view3d/view3d_select.c3
-rw-r--r--source/blender/editors/transform/transform_convert.c10
-rw-r--r--source/blender/editors/transform/transform_convert_gpencil.c14
-rw-r--r--source/blender/editors/transform/transform_generics.c7
-rw-r--r--source/blender/editors/transform/transform_gizmo_3d.c6
-rw-r--r--source/blender/editors/undo/ed_undo.c3
57 files changed, 7400 insertions, 2701 deletions
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c
index d1fd1ebd06f..ebde475a075 100644
--- a/source/blender/editors/animation/anim_channels_defines.c
+++ b/source/blender/editors/animation/anim_channels_defines.c
@@ -5105,10 +5105,16 @@ void ANIM_channel_draw_widgets(const bContext *C,
/* Mask Layer. */
UI_block_emboss_set(block, UI_EMBOSS_NONE);
- prop = RNA_struct_find_property(&ptr, "mask_layer");
+ prop = RNA_struct_find_property(&ptr, "use_mask_layer");
gp_rna_path = RNA_path_from_ID_to_property(&ptr, prop);
if (RNA_path_resolve_property(&id_ptr, gp_rna_path, &ptr, &prop)) {
- icon = (gpl->flag & GP_LAYER_USE_MASK) ? ICON_MOD_MASK : ICON_LAYER_ACTIVE;
+ icon = ICON_LAYER_ACTIVE;
+ if (gpl->flag & GP_LAYER_USE_MASK) {
+ icon = ICON_MOD_MASK;
+ }
+ else {
+ icon = ICON_LAYER_ACTIVE;
+ }
uiDefAutoButR(block,
&ptr,
prop,
diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c
index 20f565990e9..8b5e22db61a 100644
--- a/source/blender/editors/animation/anim_channels_edit.c
+++ b/source/blender/editors/animation/anim_channels_edit.c
@@ -3135,7 +3135,7 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index,
if (gpl->flag & GP_LAYER_SELECT) {
ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, gpl, ANIMTYPE_GPLAYER);
/* update other layer status */
- BKE_gpencil_layer_setactive(gpd, gpl);
+ BKE_gpencil_layer_active_set(gpd, gpl);
BKE_gpencil_layer_autolock_set(gpd, false);
DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
}
diff --git a/source/blender/editors/armature/armature_naming.c b/source/blender/editors/armature/armature_naming.c
index 0b286b4ee2b..1f421d23b27 100644
--- a/source/blender/editors/armature/armature_naming.c
+++ b/source/blender/editors/armature/armature_naming.c
@@ -299,7 +299,7 @@ void ED_armature_bone_rename(Main *bmain,
if (ob->type == OB_GPENCIL) {
bGPdata *gpd = (bGPdata *)ob->data;
- for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
if ((gpl->parent != NULL) && (gpl->parent->data == arm)) {
if (STREQ(gpl->parsubstr, oldname)) {
BLI_strncpy(gpl->parsubstr, newname, MAXBONENAME);
diff --git a/source/blender/editors/gpencil/CMakeLists.txt b/source/blender/editors/gpencil/CMakeLists.txt
index 21f1801f7eb..512e1ec6270 100644
--- a/source/blender/editors/gpencil/CMakeLists.txt
+++ b/source/blender/editors/gpencil/CMakeLists.txt
@@ -43,7 +43,6 @@ set(SRC
gpencil_add_monkey.c
gpencil_add_stroke.c
gpencil_armature.c
- gpencil_brush.c
gpencil_convert.c
gpencil_data.c
gpencil_edit.c
@@ -54,9 +53,14 @@ set(SRC
gpencil_ops_versioning.c
gpencil_paint.c
gpencil_primitive.c
+ gpencil_sculpt_paint.c
gpencil_select.c
gpencil_undo.c
gpencil_utils.c
+ gpencil_uv.c
+ gpencil_vertex_paint.c
+ gpencil_vertex_ops.c
+ gpencil_weight_paint.c
gpencil_intern.h
)
diff --git a/source/blender/editors/gpencil/annotate_draw.c b/source/blender/editors/gpencil/annotate_draw.c
index 1a30555a584..ef9b6d2943b 100644
--- a/source/blender/editors/gpencil/annotate_draw.c
+++ b/source/blender/editors/gpencil/annotate_draw.c
@@ -33,6 +33,7 @@
#include "BLI_sys_types.h"
#include "BLI_math.h"
+#include "BLI_listbase.h"
#include "BLI_utildefines.h"
#include "BLF_api.h"
@@ -562,7 +563,7 @@ static void annotation_draw_strokes(bGPdata *UNUSED(gpd),
{
GPU_program_point_size(true);
- for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
/* check if stroke can be drawn */
if (annotation_can_draw_stroke(gps, dflag) == false) {
continue;
@@ -625,7 +626,7 @@ static void annotation_draw_strokes(bGPdata *UNUSED(gpd),
}
/* Draw selected verts for strokes being edited */
-static void annotation_draw_strokes_edit(bGPdata *gpd,
+static void annotation_draw_strokes_edit(bGPdata *UNUSED(gpd),
bGPDlayer *gpl,
const bGPDframe *gpf,
int offsx,
@@ -660,7 +661,7 @@ static void annotation_draw_strokes_edit(bGPdata *gpd,
GPU_program_point_size(true);
/* draw stroke verts */
- for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
/* check if stroke can be drawn */
if (annotation_can_draw_stroke(gps, dflag) == false) {
continue;
@@ -689,6 +690,9 @@ static void annotation_draw_strokes_edit(bGPdata *gpd,
vsize = bsize + 2;
}
+ /* Why? */
+ UNUSED_VARS(vsize);
+
float selectColor[4];
UI_GetThemeColor3fv(TH_GP_VERTEX_SELECT, selectColor);
selectColor[3] = alpha;
@@ -709,31 +713,12 @@ static void annotation_draw_strokes_edit(bGPdata *gpd,
immBegin(GPU_PRIM_POINTS, gps->totpoints);
- /* Draw start and end point differently if enabled stroke direction hint */
- bool show_direction_hint = (gpd->flag & GP_DATA_SHOW_DIRECTION) && (gps->totpoints > 1);
-
/* Draw all the stroke points (selected or not) */
bGPDspoint *pt = gps->points;
for (int i = 0; i < gps->totpoints; i++, pt++) {
/* size and color first */
- if (show_direction_hint && i == 0) {
- /* start point in green bigger */
- immAttr3f(color, 0.0f, 1.0f, 0.0f);
- immAttr1f(size, vsize + 4);
- }
- else if (show_direction_hint && (i == gps->totpoints - 1)) {
- /* end point in red smaller */
- immAttr3f(color, 1.0f, 0.0f, 0.0f);
- immAttr1f(size, vsize + 1);
- }
- else if (pt->flag & GP_SPOINT_SELECT) {
- immAttr3fv(color, selectColor);
- immAttr1f(size, vsize);
- }
- else {
- immAttr3fv(color, gpl->color);
- immAttr1f(size, bsize);
- }
+ immAttr3fv(color, gpl->color);
+ immAttr1f(size, bsize);
/* then position */
if (gps->flag & GP_STROKE_3DSPACE) {
@@ -857,7 +842,7 @@ static void annotation_draw_data_layers(
{
float ink[4];
- for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
/* verify never thickness is less than 1 */
CLAMP_MIN(gpl->thickness, 1.0f);
short lthick = gpl->thickness;
@@ -872,7 +857,7 @@ static void annotation_draw_data_layers(
}
/* get frame to draw */
- bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra, GP_GETFRAME_USE_PREV);
+ bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, cfra, GP_GETFRAME_USE_PREV);
if (gpf == NULL) {
continue;
}
diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c
index fe163e5b6e5..3e33f811225 100644
--- a/source/blender/editors/gpencil/annotate_paint.c
+++ b/source/blender/editors/gpencil/annotate_paint.c
@@ -537,9 +537,7 @@ static short gp_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure
pts->pressure = pt->pressure;
pts->strength = pt->strength;
pts->time = pt->time;
-
- /* force fill recalc */
- gps->flag |= GP_STROKE_RECALC_GEOMETRY;
+ gps->tot_triangles = 0;
}
/* increment counters */
@@ -604,14 +602,13 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
/* copy appropriate settings for stroke */
gps->totpoints = totelem;
gps->thickness = gpl->thickness;
- gps->gradient_f = 1.0f;
- gps->gradient_s[0] = 1.0f;
- gps->gradient_s[1] = 1.0f;
+ gps->fill_opacity_fac = 1.0f;
+ gps->hardeness = 1.0f;
+ copy_v2_fl(gps->aspect_ratio, 1.0f);
+ gps->uv_scale = 1.0f;
gps->flag = gpd->runtime.sbuffer_sflag;
gps->inittime = p->inittime;
-
- /* enable recalculation flag by default (only used if hq fill) */
- gps->flag |= GP_STROKE_RECALC_GEOMETRY;
+ gps->tot_triangles = 0;
/* allocate enough memory for a continuous array for storage points */
gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points");
@@ -1207,7 +1204,7 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps
ToolSettings *ts = scene->toolsettings;
/* get active layer (or add a new one if non-existent) */
- p->gpl = BKE_gpencil_layer_getactive(p->gpd);
+ p->gpl = BKE_gpencil_layer_active_get(p->gpd);
if (p->gpl == NULL) {
/* tag for annotations */
p->gpd->flag |= GP_DATA_ANNOTATIONS;
@@ -1235,7 +1232,7 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps
*/
bool has_layer_to_erase = false;
- if (gpencil_layer_is_editable(p->gpl)) {
+ if (BKE_gpencil_layer_is_editable(p->gpl)) {
/* Ensure that there's stuff to erase here (not including selection mask below)... */
if (p->gpl->actframe && p->gpl->actframe->strokes.first) {
has_layer_to_erase = true;
@@ -1263,7 +1260,7 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps
add_frame_mode = GP_GETFRAME_ADD_NEW;
}
- p->gpf = BKE_gpencil_layer_getframe(p->gpl, CFRA, add_frame_mode);
+ p->gpf = BKE_gpencil_layer_frame_get(p->gpl, CFRA, add_frame_mode);
if (p->gpf == NULL) {
p->status = GP_STATUS_ERROR;
diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c
index 4d879306cec..db13f57b192 100644
--- a/source/blender/editors/gpencil/drawgpencil.c
+++ b/source/blender/editors/gpencil/drawgpencil.c
@@ -233,128 +233,8 @@ static void gp_draw_stroke_volumetric_3d(const bGPDspoint *points,
}
/* --------------- Stroke Fills ----------------- */
-/* calc bounding box in 2d using flat projection data */
-static void gp_calc_2d_bounding_box(
- const float (*points2d)[2], int totpoints, float minv[2], float maxv[2], bool expand)
-{
- copy_v2_v2(minv, points2d[0]);
- copy_v2_v2(maxv, points2d[0]);
-
- for (int i = 1; i < totpoints; i++) {
- /* min */
- if (points2d[i][0] < minv[0]) {
- minv[0] = points2d[i][0];
- }
- if (points2d[i][1] < minv[1]) {
- minv[1] = points2d[i][1];
- }
- /* max */
- if (points2d[i][0] > maxv[0]) {
- maxv[0] = points2d[i][0];
- }
- if (points2d[i][1] > maxv[1]) {
- maxv[1] = points2d[i][1];
- }
- }
- /* If not expanded, use a perfect square */
- if (expand == false) {
- if (maxv[0] > maxv[1]) {
- maxv[1] = maxv[0];
- }
- else {
- maxv[0] = maxv[1];
- }
- }
-}
-
-/* calc texture coordinates using flat projected points */
-static void gp_calc_stroke_text_coordinates(const float (*points2d)[2],
- int totpoints,
- const float minv[2],
- float maxv[2],
- float (*r_uv)[2])
-{
- float d[2];
- d[0] = maxv[0] - minv[0];
- d[1] = maxv[1] - minv[1];
- for (int i = 0; i < totpoints; i++) {
- r_uv[i][0] = (points2d[i][0] - minv[0]) / d[0];
- r_uv[i][1] = (points2d[i][1] - minv[1]) / d[1];
- }
-}
-
-/* Triangulate stroke for high quality fill
- * (this is done only if cache is null or stroke was modified). */
-static void gp_triangulate_stroke_fill(bGPDstroke *gps)
-{
- BLI_assert(gps->totpoints >= 3);
-
- /* allocate memory for temporary areas */
- gps->tot_triangles = gps->totpoints - 2;
- uint(*tmp_triangles)[3] = MEM_mallocN(sizeof(*tmp_triangles) * gps->tot_triangles,
- "GP Stroke temp triangulation");
- float(*points2d)[2] = MEM_mallocN(sizeof(*points2d) * gps->totpoints,
- "GP Stroke temp 2d points");
- float(*uv)[2] = MEM_mallocN(sizeof(*uv) * gps->totpoints, "GP Stroke temp 2d uv data");
-
- int direction = 0;
-
- /* convert to 2d and triangulate */
- BKE_gpencil_stroke_2d_flat(gps->points, gps->totpoints, points2d, &direction);
- BLI_polyfill_calc(points2d, (uint)gps->totpoints, direction, tmp_triangles);
-
- /* calc texture coordinates automatically */
- float minv[2];
- float maxv[2];
- /* first needs bounding box data */
- gp_calc_2d_bounding_box((const float(*)[2])points2d, gps->totpoints, minv, maxv, false);
- /* calc uv data */
- gp_calc_stroke_text_coordinates((const float(*)[2])points2d, gps->totpoints, minv, maxv, uv);
-
- /* Number of triangles */
- gps->tot_triangles = gps->totpoints - 2;
- /* save triangulation data in stroke cache */
- if (gps->tot_triangles > 0) {
- if (gps->triangles == NULL) {
- gps->triangles = MEM_callocN(sizeof(*gps->triangles) * gps->tot_triangles,
- "GP Stroke triangulation");
- }
- else {
- gps->triangles = MEM_recallocN(gps->triangles, sizeof(*gps->triangles) * gps->tot_triangles);
- }
-
- for (int i = 0; i < gps->tot_triangles; i++) {
- bGPDtriangle *stroke_triangle = &gps->triangles[i];
- memcpy(stroke_triangle->verts, tmp_triangles[i], sizeof(uint[3]));
- /* copy texture coordinates */
- copy_v2_v2(stroke_triangle->uv[0], uv[tmp_triangles[i][0]]);
- copy_v2_v2(stroke_triangle->uv[1], uv[tmp_triangles[i][1]]);
- copy_v2_v2(stroke_triangle->uv[2], uv[tmp_triangles[i][2]]);
- }
- }
- else {
- /* No triangles needed - Free anything allocated previously */
- if (gps->triangles) {
- MEM_freeN(gps->triangles);
- }
-
- gps->triangles = NULL;
- }
-
- /* disable recalculation flag */
- if (gps->flag & GP_STROKE_RECALC_GEOMETRY) {
- gps->flag &= ~GP_STROKE_RECALC_GEOMETRY;
- }
-
- /* clear memory */
- MEM_SAFE_FREE(tmp_triangles);
- MEM_SAFE_FREE(points2d);
- MEM_SAFE_FREE(uv);
-}
-
/* add a new fill point and texture coordinates to vertex buffer */
static void gp_add_filldata_tobuffer(const bGPDspoint *pt,
- const float uv[2],
uint pos,
uint texcoord,
short flag,
@@ -375,48 +255,10 @@ static void gp_add_filldata_tobuffer(const bGPDspoint *pt,
fpt[2] = 0.0f; /* 2d always is z=0.0f */
}
- immAttr2f(texcoord, uv[0], uv[1]); /* texture coordinates */
- immVertex3fv(pos, fpt); /* position */
+ immAttr2f(texcoord, pt->uv_fill[0], pt->uv_fill[1]); /* texture coordinates */
+ immVertex3fv(pos, fpt); /* position */
}
-#if 0 /* GPXX disabled, not used in annotations */
-/* assign image texture for filling stroke */
-static int gp_set_filling_texture(Image *image, short flag)
-{
- ImBuf *ibuf;
- uint *bind = &image->bindcode[TEXTARGET_TEXTURE_2D];
- int error = GL_NO_ERROR;
- ImageUser iuser = {NULL};
- void *lock;
-
- iuser.ok = true;
-
- ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock);
-
- if (ibuf == NULL || ibuf->rect == NULL) {
- BKE_image_release_ibuf(image, ibuf, NULL);
- return (int)GL_INVALID_OPERATION;
- }
-
- GPU_create_gl_tex(
- bind, ibuf->rect, ibuf->rect_float, ibuf->x, ibuf->y, GL_TEXTURE_2D, false, false, image);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- if (flag & GP_STYLE_COLOR_TEX_CLAMP) {
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
- }
- else {
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
- }
- BKE_image_release_ibuf(image, ibuf, NULL);
-
- return error;
-}
-#endif
-
/* draw fills for shapes */
static void gp_draw_stroke_fill(bGPdata *gpd,
bGPDstroke *gps,
@@ -428,18 +270,12 @@ static void gp_draw_stroke_fill(bGPdata *gpd,
const float color[4])
{
BLI_assert(gps->totpoints >= 3);
+ BLI_assert(gps->tot_triangles >= 1);
const bool use_mat = (gpd->mat != NULL);
Material *ma = (use_mat) ? gpd->mat[gps->mat_nr] : BKE_material_default_gpencil();
MaterialGPencilStyle *gp_style = (ma) ? ma->gp_style : NULL;
- /* Calculate triangles cache for filling area (must be done only after changes) */
- if ((gps->flag & GP_STROKE_RECALC_GEOMETRY) || (gps->tot_triangles == 0) ||
- (gps->triangles == NULL)) {
- gp_triangulate_stroke_fill(gps);
- }
- BLI_assert(gps->tot_triangles >= 1);
-
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
uint texcoord = GPU_vertformat_attr_add(format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
@@ -450,25 +286,13 @@ static void gp_draw_stroke_fill(bGPdata *gpd,
immUniform1i("fill_type", gp_style->fill_style);
immUniform1f("mix_factor", gp_style->mix_factor);
- immUniform1f("gradient_angle", gp_style->gradient_angle);
- immUniform1f("gradient_radius", gp_style->gradient_radius);
- immUniform1f("pattern_gridsize", gp_style->pattern_gridsize);
- immUniform2fv("gradient_scale", gp_style->gradient_scale);
- immUniform2fv("gradient_shift", gp_style->gradient_shift);
-
immUniform1f("texture_angle", gp_style->texture_angle);
immUniform2fv("texture_scale", gp_style->texture_scale);
immUniform2fv("texture_offset", gp_style->texture_offset);
immUniform1f("texture_opacity", gp_style->texture_opacity);
- immUniform1i("t_mix", (gp_style->flag & GP_STYLE_FILL_TEX_MIX) != 0);
- immUniform1i("t_flip", (gp_style->flag & GP_STYLE_COLOR_FLIP_FILL) != 0);
-#if 0 /* GPXX disabled, not used in annotations */
- /* image texture */
- if ((gp_style->fill_style == GP_STYLE_FILL_STYLE_TEXTURE) ||
- (gp_style->flag & GP_STYLE_COLOR_TEX_MIX)) {
- gp_set_filling_texture(gp_style->ima, gp_style->flag);
- }
-#endif
+ immUniform1i("t_mix", (gp_style->flag & GP_MATERIAL_FILL_TEX_MIX) != 0);
+ immUniform1i("t_flip", (gp_style->flag & GP_MATERIAL_FLIP_FILL) != 0);
+
/* Draw all triangles for filling the polygon (cache must be calculated before) */
immBegin(GPU_PRIM_TRIS, gps->tot_triangles * 3);
/* TODO: use batch instead of immediate mode, to share vertices */
@@ -477,7 +301,6 @@ static void gp_draw_stroke_fill(bGPdata *gpd,
for (int i = 0; i < gps->tot_triangles; i++, stroke_triangle++) {
for (int j = 0; j < 3; j++) {
gp_add_filldata_tobuffer(&gps->points[stroke_triangle->verts[j]],
- stroke_triangle->uv[j],
pos,
texcoord,
gps->flag,
@@ -887,15 +710,15 @@ static void gp_draw_strokes(tGPDdraw *tgpw)
Material *ma = (use_mat) ? tgpw->gpd->mat[gps->mat_nr] : BKE_material_default_gpencil();
MaterialGPencilStyle *gp_style = (ma) ? ma->gp_style : NULL;
- if ((gp_style == NULL) || (gp_style->flag & GP_STYLE_COLOR_HIDE) ||
+ if ((gp_style == NULL) || (gp_style->flag & GP_MATERIAL_HIDE) ||
/* if onion and ghost flag do not draw*/
- (tgpw->onion && (gp_style->flag & GP_STYLE_COLOR_ONIONSKIN))) {
+ (tgpw->onion && (gp_style->flag & GP_MATERIAL_ONIONSKIN))) {
continue;
}
/* if disable fill, the colors with fill must be omitted too except fill boundary strokes */
if ((tgpw->disable_fill == 1) && (gp_style->fill_rgba[3] > 0.0f) &&
- ((gps->flag & GP_STROKE_NOFILL) == 0) && (gp_style->flag & GP_STYLE_FILL_SHOW)) {
+ ((gps->flag & GP_STROKE_NOFILL) == 0) && (gp_style->flag & GP_MATERIAL_FILL_SHOW)) {
continue;
}
@@ -980,7 +803,7 @@ static void gp_draw_strokes(tGPDdraw *tgpw)
}
}
- if (gp_style->mode == GP_STYLE_MODE_DOTS) {
+ if (gp_style->mode == GP_MATERIAL_MODE_DOT) {
/* volumetric stroke drawing */
if (tgpw->disable_fill != 1) {
gp_draw_stroke_volumetric_3d(gps->points, gps->totpoints, sthickness, ink);
@@ -1061,7 +884,7 @@ static void gp_draw_strokes(tGPDdraw *tgpw)
copy_v4_v4(ink, tcolor);
}
}
- if (gp_style->mode == GP_STYLE_MODE_DOTS) {
+ if (gp_style->mode == GP_MATERIAL_MODE_DOT) {
/* blob/disk-based "volumetric" drawing */
gp_draw_stroke_volumetric_2d(gps->points,
gps->totpoints,
@@ -1116,113 +939,8 @@ static void gp_draw_strokes(tGPDdraw *tgpw)
/* ----- General Drawing ------ */
-/* draw interpolate strokes (used only while operator is running) */
-void ED_gp_draw_interpolation(const bContext *C, tGPDinterpolate *tgpi, const int type)
-{
- tGPDdraw tgpw;
- ARegion *region = CTX_wm_region(C);
- RegionView3D *rv3d = region->regiondata;
- tGPDinterpolate_layer *tgpil;
- Object *obact = CTX_data_active_object(C);
- /* Drawing code is expected to run with fully evaluated depsgraph. */
- Depsgraph *depsgraph = CTX_data_expect_evaluated_depsgraph(C);
-
- float color[4];
-
- UI_GetThemeColor3fv(TH_GP_VERTEX_SELECT, color);
- color[3] = 0.6f;
- int dflag = 0;
- /* if 3d stuff, enable flags */
- if (type == REGION_DRAW_POST_VIEW) {
- dflag |= (GP_DRAWDATA_ONLY3D | GP_DRAWDATA_NOSTATUS);
- }
-
- tgpw.rv3d = rv3d;
- tgpw.depsgraph = depsgraph;
- tgpw.ob = obact;
- tgpw.gpd = tgpi->gpd;
- tgpw.offsx = 0;
- tgpw.offsy = 0;
- tgpw.winx = tgpi->region->winx;
- tgpw.winy = tgpi->region->winy;
- tgpw.dflag = dflag;
-
- /* turn on alpha-blending */
- GPU_blend(true);
- for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) {
- /* calculate parent position */
- ED_gpencil_parent_location(depsgraph, obact, tgpi->gpd, tgpil->gpl, tgpw.diff_mat);
- if (tgpil->interFrame) {
- tgpw.gpl = tgpil->gpl;
- tgpw.gpf = tgpil->interFrame;
- tgpw.t_gpf = tgpil->interFrame;
- tgpw.gps = NULL;
-
- tgpw.lthick = tgpil->gpl->line_change;
- tgpw.opacity = 1.0;
- copy_v4_v4(tgpw.tintcolor, color);
- tgpw.onion = true;
- tgpw.custonion = true;
- if (obact->totcol == 0) {
- tgpw.gpd->mat = NULL;
- }
-
- gp_draw_strokes(&tgpw);
- }
- }
- GPU_blend(false);
-}
-
/* wrapper to draw strokes for filling operator */
void ED_gp_draw_fill(tGPDdraw *tgpw)
{
gp_draw_strokes(tgpw);
}
-
-/* draw a short status message in the top-right corner */
-static void UNUSED_FUNCTION(gp_draw_status_text)(const bGPdata *gpd, ARegion *region)
-{
-
- /* Cannot draw any status text when drawing OpenGL Renders */
- if (G.f & G_FLAG_RENDER_VIEWPORT) {
- return;
- }
-
- /* Get bounds of region - Necessary to avoid problems with region overlap. */
- const rcti *rect = ED_region_visible_rect(region);
-
- /* for now, this should only be used to indicate when we are in stroke editmode */
- if (gpd->flag & GP_DATA_STROKE_EDITMODE) {
- const char *printable = IFACE_("GPencil Stroke Editing");
- float printable_size[2];
-
- int font_id = BLF_default();
-
- BLF_width_and_height(
- font_id, printable, BLF_DRAW_STR_DUMMY_MAX, &printable_size[0], &printable_size[1]);
-
- int xco = (rect->xmax - U.widget_unit) - (int)printable_size[0];
- int yco = (rect->ymax - U.widget_unit);
-
- /* text label */
- UI_FontThemeColor(font_id, TH_TEXT_HI);
-#ifdef WITH_INTERNATIONAL
- BLF_draw_default(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX);
-#else
- BLF_draw_default_ascii(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX);
-#endif
-
- /* grease pencil icon... */
- // XXX: is this too intrusive?
- GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
- GPU_blend(true);
-
- xco -= U.widget_unit;
- yco -= (int)printable_size[1] / 2;
-
- UI_icon_draw(xco, yco, ICON_GREASEPENCIL);
-
- GPU_blend(false);
- }
-}
diff --git a/source/blender/editors/gpencil/editaction_gpencil.c b/source/blender/editors/gpencil/editaction_gpencil.c
index f4636e81966..86355787b3c 100644
--- a/source/blender/editors/gpencil/editaction_gpencil.c
+++ b/source/blender/editors/gpencil/editaction_gpencil.c
@@ -58,15 +58,13 @@
/* Loops over the gp-frames for a gp-layer, and applies the given callback */
bool ED_gplayer_frames_looper(bGPDlayer *gpl, Scene *scene, short (*gpf_cb)(bGPDframe *, Scene *))
{
- bGPDframe *gpf;
-
/* error checker */
if (gpl == NULL) {
return false;
}
/* do loop */
- for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
/* execute callback */
if (gpf_cb(gpf, scene)) {
return true;
@@ -83,7 +81,6 @@ bool ED_gplayer_frames_looper(bGPDlayer *gpl, Scene *scene, short (*gpf_cb)(bGPD
/* make a listing all the gp-frames in a layer as cfraelems */
void ED_gplayer_make_cfra_list(bGPDlayer *gpl, ListBase *elems, bool onlysel)
{
- bGPDframe *gpf;
CfraElem *ce;
/* error checking */
@@ -92,7 +89,7 @@ void ED_gplayer_make_cfra_list(bGPDlayer *gpl, ListBase *elems, bool onlysel)
}
/* loop through gp-frames, adding */
- for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
if ((onlysel == 0) || (gpf->flag & GP_FRAME_SELECT)) {
ce = MEM_callocN(sizeof(CfraElem), "CfraElem");
@@ -110,15 +107,13 @@ void ED_gplayer_make_cfra_list(bGPDlayer *gpl, ListBase *elems, bool onlysel)
/* check if one of the frames in this layer is selected */
bool ED_gplayer_frame_select_check(bGPDlayer *gpl)
{
- bGPDframe *gpf;
-
/* error checking */
if (gpl == NULL) {
return false;
}
/* stop at the first one found */
- for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
if (gpf->flag & GP_FRAME_SELECT) {
return true;
}
@@ -151,15 +146,13 @@ static void gpframe_select(bGPDframe *gpf, short select_mode)
/* set all/none/invert select (like above, but with SELECT_* modes) */
void ED_gpencil_select_frames(bGPDlayer *gpl, short select_mode)
{
- bGPDframe *gpf;
-
/* error checking */
if (gpl == NULL) {
return;
}
/* handle according to mode */
- for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
gpframe_select(gpf, select_mode);
}
}
@@ -185,7 +178,7 @@ void ED_gpencil_select_frame(bGPDlayer *gpl, int selx, short select_mode)
return;
}
- gpf = BKE_gpencil_layer_find_frame(gpl, selx);
+ gpf = BKE_gpencil_layer_frame_find(gpl, selx);
if (gpf) {
gpframe_select(gpf, select_mode);
@@ -195,14 +188,12 @@ void ED_gpencil_select_frame(bGPDlayer *gpl, int selx, short select_mode)
/* select the frames in this layer that occur within the bounds specified */
void ED_gplayer_frames_select_box(bGPDlayer *gpl, float min, float max, short select_mode)
{
- bGPDframe *gpf;
-
if (gpl == NULL) {
return;
}
/* only select those frames which are in bounds */
- for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
if (IN_RANGE(gpf->framenum, min, max)) {
gpframe_select(gpf, select_mode);
}
@@ -215,14 +206,12 @@ void ED_gplayer_frames_select_region(KeyframeEditData *ked,
short tool,
short select_mode)
{
- bGPDframe *gpf;
-
if (gpl == NULL) {
return;
}
/* only select frames which are within the region */
- for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
/* construct a dummy point coordinate to do this testing with */
float pt[2] = {0};
@@ -251,7 +240,6 @@ void ED_gplayer_frames_select_region(KeyframeEditData *ked,
/* Delete selected frames */
bool ED_gplayer_frames_delete(bGPDlayer *gpl)
{
- bGPDframe *gpf, *gpfn;
bool changed = false;
/* error checking */
@@ -260,11 +248,9 @@ bool ED_gplayer_frames_delete(bGPDlayer *gpl)
}
/* check for frames to delete */
- for (gpf = gpl->frames.first; gpf; gpf = gpfn) {
- gpfn = gpf->next;
-
+ LISTBASE_FOREACH_MUTABLE (bGPDframe *, gpf, &gpl->frames) {
if (gpf->flag & GP_FRAME_SELECT) {
- BKE_gpencil_layer_delframe(gpl, gpf);
+ BKE_gpencil_layer_frame_delete(gpl, gpf);
changed = true;
}
}
@@ -275,16 +261,13 @@ bool ED_gplayer_frames_delete(bGPDlayer *gpl)
/* Duplicate selected frames from given gp-layer */
void ED_gplayer_frames_duplicate(bGPDlayer *gpl)
{
- bGPDframe *gpf, *gpfn;
-
/* error checking */
if (gpl == NULL) {
return;
}
/* duplicate selected frames */
- for (gpf = gpl->frames.first; gpf; gpf = gpfn) {
- gpfn = gpf->next;
+ LISTBASE_FOREACH_MUTABLE (bGPDframe *, gpf, &gpl->frames) {
/* duplicate this frame */
if (gpf->flag & GP_FRAME_SELECT) {
@@ -304,13 +287,11 @@ void ED_gplayer_frames_duplicate(bGPDlayer *gpl)
*/
void ED_gplayer_frames_keytype_set(bGPDlayer *gpl, short type)
{
- bGPDframe *gpf;
-
if (gpl == NULL) {
return;
}
- for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
if (gpf->flag & GP_FRAME_SELECT) {
gpf->key_type = type;
}
@@ -370,10 +351,9 @@ bool ED_gpencil_anim_copybuf_copy(bAnimContext *ac)
for (ale = anim_data.first; ale; ale = ale->next) {
ListBase copied_frames = {NULL, NULL};
bGPDlayer *gpl = (bGPDlayer *)ale->data;
- bGPDframe *gpf;
/* loop over frames, and copy only selected frames */
- for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
/* if frame is selected, make duplicate it and its strokes */
if (gpf->flag & GP_FRAME_SELECT) {
/* make a copy of this frame */
@@ -489,7 +469,7 @@ bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode)
gpfs->framenum += offset;
/* get frame to copy data into (if no frame returned, then just ignore) */
- gpf = BKE_gpencil_layer_getframe(gpld, gpfs->framenum, GP_GETFRAME_ADD_NEW);
+ gpf = BKE_gpencil_layer_frame_get(gpld, gpfs->framenum, GP_GETFRAME_ADD_NEW);
if (gpf) {
bGPDstroke *gps, *gpsn;
@@ -502,21 +482,15 @@ bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode)
*/
for (gps = gpfs->strokes.first; gps; gps = gps->next) {
/* make a copy of stroke, then of its points array */
- gpsn = MEM_dupallocN(gps);
- gpsn->points = MEM_dupallocN(gps->points);
- if (gps->dvert != NULL) {
- gpsn->dvert = MEM_dupallocN(gps->dvert);
- BKE_gpencil_stroke_weights_duplicate(gps, gpsn);
- }
- /* duplicate triangle information */
- gpsn->triangles = MEM_dupallocN(gps->triangles);
+ gpsn = BKE_gpencil_stroke_duplicate(gps, true);
+
/* append stroke to frame */
BLI_addtail(&gpf->strokes, gpsn);
}
/* if no strokes (i.e. new frame) added, free gpf */
if (BLI_listbase_is_empty(&gpf->strokes)) {
- BKE_gpencil_layer_delframe(gpld, gpf);
+ BKE_gpencil_layer_frame_delete(gpld, gpf);
}
}
diff --git a/source/blender/editors/gpencil/gpencil_add_monkey.c b/source/blender/editors/gpencil/gpencil_add_monkey.c
index 8b4620d233b..f08188c48ce 100644
--- a/source/blender/editors/gpencil/gpencil_add_monkey.c
+++ b/source/blender/editors/gpencil/gpencil_add_monkey.c
@@ -66,17 +66,20 @@ static int gpencil_monkey_color(
ma = BKE_gpencil_object_material_new(bmain, ob, pct->name, &idx);
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);
if (!stroke) {
- ma->gp_style->flag &= ~GP_STYLE_STROKE_SHOW;
+ ma->gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW;
}
if (!fill) {
- ma->gp_style->flag &= ~GP_STYLE_FILL_SHOW;
+ ma->gp_style->flag &= ~GP_MATERIAL_FILL_SHOW;
}
else {
- ma->gp_style->flag |= GP_STYLE_FILL_SHOW;
+ ma->gp_style->flag |= GP_MATERIAL_FILL_SHOW;
}
return idx;
@@ -855,89 +858,117 @@ void ED_gpencil_create_monkey(bContext *C, Object *ob, float mat[4][4])
bGPDframe *frameLines = BKE_gpencil_frame_addnew(Lines, CFRA);
/* generate strokes */
- gps = BKE_gpencil_add_stroke(frameFills, color_Skin, 270, 75);
+ gps = BKE_gpencil_stroke_add(frameFills, color_Skin, 270, 75, false);
BKE_gpencil_stroke_add_points(gps, data0, 270, mat);
+ BKE_gpencil_stroke_geometry_update(gps);
- gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Shadow, 33, 60);
+ gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data1, 33, mat);
+ BKE_gpencil_stroke_geometry_update(gps);
- gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Shadow, 18, 60);
+ gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 18, 60, false);
BKE_gpencil_stroke_add_points(gps, data2, 18, mat);
+ BKE_gpencil_stroke_geometry_update(gps);
- gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Light, 64, 60);
+ gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 64, 60, false);
BKE_gpencil_stroke_add_points(gps, data3, 64, mat);
+ BKE_gpencil_stroke_geometry_update(gps);
- gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Light, 33, 60);
+ gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data4, 33, mat);
+ BKE_gpencil_stroke_geometry_update(gps);
- gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Light, 64, 60);
+ gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 64, 60, false);
BKE_gpencil_stroke_add_points(gps, data5, 64, mat);
+ BKE_gpencil_stroke_geometry_update(gps);
- gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Light, 33, 60);
+ gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data6, 33, mat);
+ BKE_gpencil_stroke_geometry_update(gps);
- gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Light, 18, 40);
+ gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 18, 40, false);
BKE_gpencil_stroke_add_points(gps, data7, 18, mat);
+ BKE_gpencil_stroke_geometry_update(gps);
- gps = BKE_gpencil_add_stroke(frameFills, color_Eyes, 49, 60);
+ gps = BKE_gpencil_stroke_add(frameFills, color_Eyes, 49, 60, false);
BKE_gpencil_stroke_add_points(gps, data8, 49, mat);
+ BKE_gpencil_stroke_geometry_update(gps);
- gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Shadow, 33, 60);
+ gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data9, 33, mat);
+ BKE_gpencil_stroke_geometry_update(gps);
- gps = BKE_gpencil_add_stroke(frameFills, color_Eyes, 49, 60);
+ gps = BKE_gpencil_stroke_add(frameFills, color_Eyes, 49, 60, false);
BKE_gpencil_stroke_add_points(gps, data10, 49, mat);
+ BKE_gpencil_stroke_geometry_update(gps);
- gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Shadow, 18, 40);
+ gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 18, 40, false);
BKE_gpencil_stroke_add_points(gps, data11, 18, mat);
+ BKE_gpencil_stroke_geometry_update(gps);
- gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Shadow, 18, 40);
+ gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 18, 40, false);
BKE_gpencil_stroke_add_points(gps, data12, 18, mat);
+ BKE_gpencil_stroke_geometry_update(gps);
- gps = BKE_gpencil_add_stroke(frameLines, color_Black, 33, 60);
+ gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data13, 33, mat);
+ BKE_gpencil_stroke_geometry_update(gps);
- gps = BKE_gpencil_add_stroke(frameLines, color_Black, 33, 60);
+ gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data14, 33, mat);
+ BKE_gpencil_stroke_geometry_update(gps);
- gps = BKE_gpencil_add_stroke(frameLines, color_Black, 65, 60);
+ gps = BKE_gpencil_stroke_add(frameLines, color_Black, 65, 60, false);
BKE_gpencil_stroke_add_points(gps, data15, 65, mat);
+ BKE_gpencil_stroke_geometry_update(gps);
- gps = BKE_gpencil_add_stroke(frameLines, color_Black, 34, 60);
+ gps = BKE_gpencil_stroke_add(frameLines, color_Black, 34, 60, false);
BKE_gpencil_stroke_add_points(gps, data16, 34, mat);
+ BKE_gpencil_stroke_geometry_update(gps);
- gps = BKE_gpencil_add_stroke(frameLines, color_Black, 33, 60);
+ gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data17, 33, mat);
+ BKE_gpencil_stroke_geometry_update(gps);
- gps = BKE_gpencil_add_stroke(frameLines, color_Black, 33, 40);
+ gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 40, false);
BKE_gpencil_stroke_add_points(gps, data18, 33, mat);
+ BKE_gpencil_stroke_geometry_update(gps);
- gps = BKE_gpencil_add_stroke(frameLines, color_Black, 34, 40);
+ gps = BKE_gpencil_stroke_add(frameLines, color_Black, 34, 40, false);
BKE_gpencil_stroke_add_points(gps, data19, 34, mat);
+ BKE_gpencil_stroke_geometry_update(gps);
- gps = BKE_gpencil_add_stroke(frameLines, color_Black, 33, 60);
+ gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data20, 33, mat);
+ BKE_gpencil_stroke_geometry_update(gps);
- gps = BKE_gpencil_add_stroke(frameLines, color_Black, 64, 60);
+ gps = BKE_gpencil_stroke_add(frameLines, color_Black, 64, 60, false);
BKE_gpencil_stroke_add_points(gps, data21, 64, mat);
+ BKE_gpencil_stroke_geometry_update(gps);
- gps = BKE_gpencil_add_stroke(frameLines, color_Pupils, 26, 60);
+ gps = BKE_gpencil_stroke_add(frameLines, color_Pupils, 26, 60, false);
BKE_gpencil_stroke_add_points(gps, data22, 26, mat);
+ BKE_gpencil_stroke_geometry_update(gps);
- gps = BKE_gpencil_add_stroke(frameLines, color_Pupils, 26, 60);
+ gps = BKE_gpencil_stroke_add(frameLines, color_Pupils, 26, 60, false);
BKE_gpencil_stroke_add_points(gps, data23, 26, mat);
+ BKE_gpencil_stroke_geometry_update(gps);
- gps = BKE_gpencil_add_stroke(frameLines, color_Black, 33, 60);
+ gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data24, 33, mat);
+ BKE_gpencil_stroke_geometry_update(gps);
- gps = BKE_gpencil_add_stroke(frameLines, color_Black, 18, 40);
+ gps = BKE_gpencil_stroke_add(frameLines, color_Black, 18, 40, false);
BKE_gpencil_stroke_add_points(gps, data25, 18, mat);
+ BKE_gpencil_stroke_geometry_update(gps);
- gps = BKE_gpencil_add_stroke(frameLines, color_Black, 18, 40);
+ gps = BKE_gpencil_stroke_add(frameLines, color_Black, 18, 40, false);
BKE_gpencil_stroke_add_points(gps, data26, 18, mat);
+ BKE_gpencil_stroke_geometry_update(gps);
- gps = BKE_gpencil_add_stroke(frameLines, color_Black, 33, 60);
+ gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data27, 33, mat);
+ BKE_gpencil_stroke_geometry_update(gps);
/* update depsgraph */
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
diff --git a/source/blender/editors/gpencil/gpencil_add_stroke.c b/source/blender/editors/gpencil/gpencil_add_stroke.c
index dfcf7f6e765..d4e17144ca2 100644
--- a/source/blender/editors/gpencil/gpencil_add_stroke.c
+++ b/source/blender/editors/gpencil/gpencil_add_stroke.c
@@ -65,10 +65,13 @@ static int gp_stroke_material(Main *bmain, Object *ob, const ColorTemplate *pct,
ma = BKE_gpencil_object_material_new(bmain, ob, pct->name, &idx);
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);
if (fill) {
- ma->gp_style->flag |= GP_STYLE_FILL_SHOW;
+ ma->gp_style->flag |= GP_MATERIAL_FILL_SHOW;
}
return idx;
@@ -240,8 +243,9 @@ void ED_gpencil_create_stroke(bContext *C, Object *ob, float mat[4][4])
UNUSED_VARS(frame_color);
/* generate stroke */
- gps = BKE_gpencil_add_stroke(frame_lines, color_black, 175, 75);
+ gps = BKE_gpencil_stroke_add(frame_lines, color_black, 175, 75, false);
BKE_gpencil_stroke_add_points(gps, data0, 175, mat);
+ BKE_gpencil_stroke_geometry_update(gps);
/* update depsgraph */
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
diff --git a/source/blender/editors/gpencil/gpencil_armature.c b/source/blender/editors/gpencil/gpencil_armature.c
index c0871fd32fc..02913a19523 100644
--- a/source/blender/editors/gpencil/gpencil_armature.c
+++ b/source/blender/editors/gpencil/gpencil_armature.c
@@ -357,7 +357,7 @@ static void gpencil_add_verts_to_dgroups(
}
/* loop all strokes */
- for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
bGPDspoint *pt = NULL;
@@ -368,7 +368,7 @@ static void gpencil_add_verts_to_dgroups(
continue;
}
- for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
/* skip strokes that are invalid for current view */
if (ED_gpencil_stroke_can_use(C, gps) == false) {
continue;
diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c
index 255f17f13cc..934466475a5 100644
--- a/source/blender/editors/gpencil/gpencil_convert.c
+++ b/source/blender/editors/gpencil/gpencil_convert.c
@@ -152,7 +152,7 @@ static const EnumPropertyItem *rna_GPConvert_mode_items(bContext *UNUSED(C),
* - assumes that the active space is the 3D-View
*/
static void gp_strokepoint_convertcoords(bContext *C,
- bGPdata *gpd,
+ bGPdata *UNUSED(gpd),
bGPDlayer *gpl,
bGPDstroke *gps,
bGPDspoint *source_pt,
@@ -174,7 +174,7 @@ static void gp_strokepoint_convertcoords(bContext *C,
/* apply parent transform */
float fpt[3];
- ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat);
+ BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat);
mul_v3_m4v3(fpt, diff_mat, &source_pt->x);
copy_v3_v3(&pt->x, fpt);
@@ -1270,7 +1270,7 @@ static void gp_layer_to_curve(bContext *C,
Collection *collection = CTX_data_collection(C);
Scene *scene = CTX_data_scene(C);
- bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_USE_PREV);
+ bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_USE_PREV);
bGPDstroke *gps, *prev_gps = NULL;
Object *ob;
Curve *cu;
@@ -1410,7 +1410,7 @@ static bool gp_convert_check_has_valid_timing(bContext *C, bGPDlayer *gpl, wmOpe
int i;
bool valid = true;
- if (!gpl || !(gpf = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_USE_PREV)) ||
+ if (!gpl || !(gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_USE_PREV)) ||
!(gps = gpf->strokes.first)) {
return false;
}
@@ -1476,8 +1476,8 @@ static bool gp_convert_poll(bContext *C)
/* only if the current view is 3D View, if there's valid data (i.e. at least one stroke!),
* and if we are not in edit mode!
*/
- return ((sa && sa->spacetype == SPACE_VIEW3D) && (gpl = BKE_gpencil_layer_getactive(gpd)) &&
- (gpf = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_USE_PREV)) &&
+ return ((sa && sa->spacetype == SPACE_VIEW3D) && (gpl = BKE_gpencil_layer_active_get(gpd)) &&
+ (gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_USE_PREV)) &&
(gpf->strokes.first) && (!GPENCIL_ANY_EDIT_MODE(gpd)));
}
@@ -1487,7 +1487,7 @@ static int gp_convert_layer_exec(bContext *C, wmOperator *op)
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = (bGPdata *)ob->data;
- bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
Scene *scene = CTX_data_scene(C);
const int mode = RNA_enum_get(op->ptr, "type");
const bool norm_weights = RNA_boolean_get(op->ptr, "use_normalize_weights");
@@ -1751,4 +1751,92 @@ void GPENCIL_OT_convert(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
-/* ************************************************ */
+/* Generate Grease Pencil from Image. */
+static bool image_to_gpencil_poll(bContext *C)
+{
+ SpaceLink *sl = CTX_wm_space_data(C);
+ if (sl->spacetype == SPACE_IMAGE) {
+ return true;
+ }
+
+ return false;
+}
+
+static int image_to_gpencil_exec(bContext *C, wmOperator *op)
+{
+ const float size = RNA_float_get(op->ptr, "size");
+ const bool is_mask = RNA_boolean_get(op->ptr, "mask");
+
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+ SpaceImage *sima = CTX_wm_space_image(C);
+ bool done = false;
+
+ if (sima->image == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+
+ /* Create Object. */
+ const float *cur = scene->cursor.location;
+ ushort local_view_bits = 0;
+ Object *ob = ED_gpencil_add_object(C, cur, local_view_bits);
+ DEG_relations_tag_update(bmain); /* added object */
+
+ /* Create material slot. */
+ Material *ma = BKE_gpencil_object_material_new(bmain, ob, "Image Material", NULL);
+ MaterialGPencilStyle *gp_style = ma->gp_style;
+ gp_style->mode = GP_MATERIAL_MODE_SQUARE;
+
+ /* Add layer and frame. */
+ bGPdata *gpd = (bGPdata *)ob->data;
+ bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, "Image Layer", true);
+ bGPDframe *gpf = BKE_gpencil_frame_addnew(gpl, CFRA);
+ done = BKE_gpencil_from_image(sima, gpf, size, is_mask);
+
+ if (done) {
+ /* Delete any selected point. */
+ LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
+ gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0);
+ }
+
+ BKE_reportf(op->reports, RPT_INFO, "Object created");
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_image_to_grease_pencil(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Generate Grease Pencil Object using image as source";
+ ot->idname = "GPENCIL_OT_image_to_grease_pencil";
+ ot->description = "Generate a Grease Pencil Object using Image as source";
+
+ /* api callbacks */
+ ot->exec = image_to_gpencil_exec;
+ ot->poll = image_to_gpencil_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ ot->prop = RNA_def_float(ot->srna,
+ "size",
+ 0.005f,
+ 0.0001f,
+ 10.0f,
+ "Point Size",
+ "Size used for graese pencil points",
+ 0.001f,
+ 1.0f);
+ RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE);
+
+ prop = RNA_def_boolean(ot->srna,
+ "mask",
+ false,
+ "Generate Mask",
+ "Create an inverted image for masking using alpha channel");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+}
diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c
index a8c3fb02e11..9b33e999a25 100644
--- a/source/blender/editors/gpencil/gpencil_data.c
+++ b/source/blender/editors/gpencil/gpencil_data.c
@@ -292,7 +292,7 @@ static int gp_layer_remove_exec(bContext *C, wmOperator *op)
bGPdata *gpd = (!is_annotation) ? ED_gpencil_data_get_active(C) :
ED_annotation_data_get_active(C);
- bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
/* sanity checks */
if (ELEM(NULL, gpd, gpl)) {
@@ -309,15 +309,18 @@ static int gp_layer_remove_exec(bContext *C, wmOperator *op)
* - if this is the only layer, this naturally becomes NULL
*/
if (gpl->prev) {
- BKE_gpencil_layer_setactive(gpd, gpl->prev);
+ BKE_gpencil_layer_active_set(gpd, gpl->prev);
}
else {
- BKE_gpencil_layer_setactive(gpd, gpl->next);
+ BKE_gpencil_layer_active_set(gpd, gpl->next);
}
/* delete the layer now... */
BKE_gpencil_layer_delete(gpd, gpl);
+ /* Reorder masking. */
+ BKE_gpencil_layer_mask_sort_all(gpd);
+
/* 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);
@@ -342,7 +345,7 @@ void GPENCIL_OT_layer_remove(wmOperatorType *ot)
static bool gp_active_layer_annotation_poll(bContext *C)
{
bGPdata *gpd = ED_annotation_data_get_active(C);
- bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
return (gpl != NULL);
}
@@ -373,7 +376,7 @@ static int gp_layer_move_exec(bContext *C, wmOperator *op)
bGPdata *gpd = (!is_annotation) ? ED_gpencil_data_get_active(C) :
ED_annotation_data_get_active(C);
- bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
const int direction = RNA_enum_get(op->ptr, "type") * -1;
@@ -384,6 +387,9 @@ static int gp_layer_move_exec(bContext *C, wmOperator *op)
BLI_assert(ELEM(direction, -1, 0, 1)); /* we use value below */
if (BLI_listbase_link_move(&gpd->layers, gpl, direction)) {
+ /* Reorder masking. */
+ BKE_gpencil_layer_mask_sort_all(gpd);
+
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
}
@@ -441,7 +447,7 @@ void GPENCIL_OT_layer_annotation_move(wmOperatorType *ot)
static int gp_layer_copy_exec(bContext *C, wmOperator *UNUSED(op))
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
- bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
bGPDlayer *new_layer;
/* sanity checks */
@@ -460,7 +466,7 @@ static int gp_layer_copy_exec(bContext *C, wmOperator *UNUSED(op))
'.',
offsetof(bGPDlayer, info),
sizeof(new_layer->info));
- BKE_gpencil_layer_setactive(gpd, new_layer);
+ BKE_gpencil_layer_active_set(gpd, new_layer);
/* notifiers */
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
@@ -499,7 +505,7 @@ static bool gp_layer_duplicate_object_poll(bContext *C)
}
bGPdata *gpd = (bGPdata *)ob->data;
- bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
if (gpl == NULL) {
return false;
@@ -532,7 +538,7 @@ static int gp_layer_duplicate_object_exec(bContext *C, wmOperator *op)
Object *ob_src = CTX_data_active_object(C);
bGPdata *gpd_src = (bGPdata *)ob_src->data;
- bGPDlayer *gpl_src = BKE_gpencil_layer_getactive(gpd_src);
+ bGPDlayer *gpl_src = BKE_gpencil_layer_active_get(gpd_src);
/* Sanity checks. */
if (ELEM(NULL, gpd_src, gpl_src, ob_dst)) {
@@ -568,7 +574,7 @@ static int gp_layer_duplicate_object_exec(bContext *C, wmOperator *op)
for (bGPDstroke *gps_src = gpf_src->strokes.first; gps_src; gps_src = gps_src->next) {
/* Make copy of source stroke. */
- bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps_src);
+ bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true);
/* Check if material is in destination object,
* otherwise add the slot with the material. */
@@ -630,21 +636,21 @@ enum {
static int gp_frame_duplicate_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
- bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
+ bGPDlayer *gpl_active = BKE_gpencil_layer_active_get(gpd);
Scene *scene = CTX_data_scene(C);
int mode = RNA_enum_get(op->ptr, "mode");
/* sanity checks */
- if (ELEM(NULL, gpd, gpl)) {
+ if (ELEM(NULL, gpd, gpl_active)) {
return OPERATOR_CANCELLED;
}
if (mode == 0) {
- BKE_gpencil_frame_addcopy(gpl, CFRA);
+ BKE_gpencil_frame_addcopy(gpl_active, CFRA);
}
else {
- for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
if ((gpl->flag & GP_LAYER_LOCKED) == 0) {
BKE_gpencil_frame_addcopy(gpl, CFRA);
}
@@ -700,16 +706,13 @@ static int gp_frame_clean_fill_exec(bContext *C, wmOperator *op)
for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
if ((gpf == gpl->actframe) || (mode == GP_FRAME_CLEAN_FILL_ALL)) {
- bGPDstroke *gps, *gpsn;
if (gpf == NULL) {
continue;
}
/* simply delete strokes which are no fill */
- for (gps = gpf->strokes.first; gps; gps = gpsn) {
- gpsn = gps->next;
-
+ LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
/* skip strokes that are invalid for current view */
if (ED_gpencil_stroke_can_use(C, gps) == false) {
continue;
@@ -778,8 +781,6 @@ static int gp_frame_clean_loose_exec(bContext *C, wmOperator *op)
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
- bGPDstroke *gps = NULL;
- bGPDstroke *gpsn = NULL;
for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
@@ -788,9 +789,7 @@ static int gp_frame_clean_loose_exec(bContext *C, wmOperator *op)
}
/* simply delete strokes which are no loose */
- for (gps = gpf->strokes.first; gps; gps = gpsn) {
- gpsn = gps->next;
-
+ LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
/* skip strokes that are invalid for current view */
if (ED_gpencil_stroke_can_use(C, gps) == false) {
continue;
@@ -861,7 +860,7 @@ void GPENCIL_OT_frame_clean_loose(wmOperatorType *ot)
static int gp_hide_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
- bGPDlayer *layer = BKE_gpencil_layer_getactive(gpd);
+ bGPDlayer *layer = BKE_gpencil_layer_active_get(gpd);
bool unselected = RNA_boolean_get(op->ptr, "unselected");
/* sanity checks */
@@ -870,10 +869,8 @@ static int gp_hide_exec(bContext *C, wmOperator *op)
}
if (unselected) {
- bGPDlayer *gpl;
-
/* hide unselected */
- for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
if (gpl != layer) {
gpl->flag |= GP_LAYER_HIDE;
}
@@ -946,7 +943,6 @@ static void gp_reveal_select_frame(bContext *C, bGPDframe *frame, bool select)
static int gp_reveal_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
- bGPDlayer *gpl;
const bool select = RNA_boolean_get(op->ptr, "select");
/* sanity checks */
@@ -954,8 +950,7 @@ static int gp_reveal_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
-
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
if (gpl->flag & GP_LAYER_HIDE) {
gpl->flag &= ~GP_LAYER_HIDE;
@@ -1008,7 +1003,6 @@ void GPENCIL_OT_reveal(wmOperatorType *ot)
static int gp_lock_all_exec(bContext *C, wmOperator *UNUSED(op))
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
- bGPDlayer *gpl;
/* sanity checks */
if (gpd == NULL) {
@@ -1016,7 +1010,7 @@ static int gp_lock_all_exec(bContext *C, wmOperator *UNUSED(op))
}
/* make all layers non-editable */
- for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
gpl->flag |= GP_LAYER_LOCKED;
}
@@ -1048,7 +1042,6 @@ void GPENCIL_OT_lock_all(wmOperatorType *ot)
static int gp_unlock_all_exec(bContext *C, wmOperator *UNUSED(op))
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
- bGPDlayer *gpl;
/* sanity checks */
if (gpd == NULL) {
@@ -1056,7 +1049,7 @@ static int gp_unlock_all_exec(bContext *C, wmOperator *UNUSED(op))
}
/* make all layers editable again */
- for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
gpl->flag &= ~GP_LAYER_LOCKED;
}
@@ -1087,8 +1080,7 @@ void GPENCIL_OT_unlock_all(wmOperatorType *ot)
static int gp_isolate_layer_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
- bGPDlayer *layer = BKE_gpencil_layer_getactive(gpd);
- bGPDlayer *gpl;
+ bGPDlayer *layer = BKE_gpencil_layer_active_get(gpd);
int flags = GP_LAYER_LOCKED;
bool isolate = false;
@@ -1102,7 +1094,7 @@ static int gp_isolate_layer_exec(bContext *C, wmOperator *op)
}
/* Test whether to isolate or clear all flags */
- for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
/* Skip if this is the active layer */
if (gpl == layer) {
continue;
@@ -1121,7 +1113,7 @@ static int gp_isolate_layer_exec(bContext *C, wmOperator *op)
/* TODO: Include onion-skinning on this list? */
if (isolate) {
/* Set flags on all "other" layers */
- for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
if (gpl == layer) {
continue;
}
@@ -1132,7 +1124,7 @@ static int gp_isolate_layer_exec(bContext *C, wmOperator *op)
}
else {
/* Clear flags - Restore everything else */
- for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
gpl->flag &= ~flags;
}
}
@@ -1172,7 +1164,7 @@ void GPENCIL_OT_layer_isolate(wmOperatorType *ot)
static int gp_merge_layer_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
- bGPDlayer *gpl_next = BKE_gpencil_layer_getactive(gpd);
+ bGPDlayer *gpl_next = BKE_gpencil_layer_active_get(gpd);
bGPDlayer *gpl_current = gpl_next->prev;
if (ELEM(NULL, gpd, gpl_current, gpl_next)) {
@@ -1191,7 +1183,7 @@ static int gp_merge_layer_exec(bContext *C, wmOperator *op)
/* try to find frame in current layer */
bGPDframe *frame = BLI_ghash_lookup(gh_frames_cur, POINTER_FROM_INT(gpf->framenum));
if (!frame) {
- bGPDframe *actframe = BKE_gpencil_layer_getframe(
+ bGPDframe *actframe = BKE_gpencil_layer_frame_get(
gpl_current, gpf->framenum, GP_GETFRAME_USE_PREV);
frame = BKE_gpencil_frame_addnew(gpl_current, gpf->framenum);
/* duplicate strokes of current active frame */
@@ -1203,10 +1195,29 @@ static int gp_merge_layer_exec(bContext *C, wmOperator *op)
BLI_movelisttolist(&frame->strokes, &gpf->strokes);
}
+ /* Add Masks to destination layer. */
+ LISTBASE_FOREACH (bGPDlayer_Mask *, mask, &gpl_next->mask_layers) {
+ /* Don't add merged layers or missing layer names. */
+ if (!BKE_gpencil_layer_named_get(gpd, mask->name) || STREQ(mask->name, gpl_next->info) ||
+ STREQ(mask->name, gpl_current->info)) {
+ continue;
+ }
+ if (!BKE_gpencil_layer_mask_named_get(gpl_current, mask->name)) {
+ bGPDlayer_Mask *mask_new = MEM_dupallocN(mask);
+ BLI_addtail(&gpl_current->mask_layers, mask_new);
+ gpl_current->act_mask++;
+ }
+ }
+ /* Set destination layer as active. */
+ BKE_gpencil_layer_active_set(gpd, gpl_current);
+
/* Now delete next layer */
BKE_gpencil_layer_delete(gpd, gpl_next);
BLI_ghash_free(gh_frames_cur, NULL, NULL);
+ /* Reorder masking. */
+ BKE_gpencil_layer_mask_sort(gpd, gpl_current);
+
/* 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);
@@ -1268,7 +1279,7 @@ static int gp_layer_change_exec(bContext *C, wmOperator *op)
}
/* Set active layer */
- BKE_gpencil_layer_setactive(gpd, gpl);
+ BKE_gpencil_layer_active_set(gpd, gpl);
/* updates */
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
@@ -1297,6 +1308,49 @@ void GPENCIL_OT_layer_change(wmOperatorType *ot)
RNA_def_enum_funcs(ot->prop, ED_gpencil_layers_with_new_enum_itemf);
}
+static int gp_layer_active_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = (bGPdata *)ob->data;
+ int layer_num = RNA_int_get(op->ptr, "layer");
+
+ /* Try to get layer */
+ bGPDlayer *gpl = BLI_findlink(&gpd->layers, layer_num);
+
+ if (gpl == NULL) {
+ BKE_reportf(
+ op->reports, RPT_ERROR, "Cannot change to non-existent layer (index = %d)", layer_num);
+ return OPERATOR_CANCELLED;
+ }
+
+ /* Set active layer */
+ BKE_gpencil_layer_active_set(gpd, gpl);
+
+ /* updates */
+ 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_active(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Active Layer";
+ ot->idname = "GPENCIL_OT_layer_active";
+ ot->description = "Active Grease Pencil layer";
+
+ /* callbacks */
+ ot->exec = gp_layer_active_exec;
+ ot->poll = gp_active_layer_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* GPencil layer to use. */
+ ot->prop = RNA_def_int(ot->srna, "layer", 0, 0, INT_MAX, "Grease Pencil Layer", "", 0, INT_MAX);
+ RNA_def_property_flag(ot->prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+}
/* ************************************************ */
/* ******************* Arrange Stroke Up/Down in drawing order ************************** */
@@ -1312,7 +1366,7 @@ static int gp_stroke_arrange_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = ED_gpencil_data_get_active(C);
- bGPDlayer *gpl_act = BKE_gpencil_layer_getactive(gpd);
+ bGPDlayer *gpl_act = BKE_gpencil_layer_active_get(gpd);
/* sanity checks */
if (ELEM(NULL, gpd, gpl_act, gpl_act->actframe)) {
@@ -1469,7 +1523,7 @@ static int gp_stroke_change_color_exec(bContext *C, wmOperator *op)
}
}
/* try to find slot */
- int idx = BKE_gpencil_object_material_get_index(ob, ma);
+ int idx = BKE_gpencil_object_material_index_get(ob, ma);
if (idx < 0) {
return OPERATOR_CANCELLED;
}
@@ -1494,7 +1548,7 @@ static int gp_stroke_change_color_exec(bContext *C, wmOperator *op)
continue;
}
- for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
/* only if selected */
if (gps->flag & GP_STROKE_SELECT) {
/* skip strokes that are invalid for current view */
@@ -1562,15 +1616,15 @@ static int gp_stroke_lock_color_exec(bContext *C, wmOperator *UNUSED(op))
for (short i = 0; i < *totcol; i++) {
Material *tmp_ma = BKE_object_material_get(ob, i + 1);
if (tmp_ma) {
- tmp_ma->gp_style->flag |= GP_STYLE_COLOR_LOCKED;
+ tmp_ma->gp_style->flag |= GP_MATERIAL_LOCKED;
DEG_id_tag_update(&tmp_ma->id, ID_RECALC_COPY_ON_WRITE);
}
}
/* loop all selected strokes and unlock any color */
- for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
/* only editable and visible layers are considered */
- if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
+ if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
/* only if selected */
if (gps->flag & GP_STROKE_SELECT) {
@@ -1581,7 +1635,7 @@ static int gp_stroke_lock_color_exec(bContext *C, wmOperator *UNUSED(op))
/* unlock color */
Material *tmp_ma = BKE_object_material_get(ob, gps->mat_nr + 1);
if (tmp_ma) {
- tmp_ma->gp_style->flag &= ~GP_STYLE_COLOR_LOCKED;
+ tmp_ma->gp_style->flag &= ~GP_MATERIAL_LOCKED;
DEG_id_tag_update(&tmp_ma->id, ID_RECALC_COPY_ON_WRITE);
}
}
@@ -1619,28 +1673,273 @@ void GPENCIL_OT_stroke_lock_color(wmOperatorType *ot)
/* ************************************************ */
/* Drawing Brushes Operators */
-/* ******************* Brush create presets ************************** */
-static int gp_brush_presets_create_exec(bContext *C, wmOperator *UNUSED(op))
+/* ******************* Brush resets ************************** */
+static int gp_brush_reset_exec(bContext *C, wmOperator *UNUSED(op))
{
Main *bmain = CTX_data_main(C);
ToolSettings *ts = CTX_data_tool_settings(C);
- BKE_brush_gpencil_presets(bmain, ts);
+ const enum eContextObjectMode mode = CTX_data_mode_enum(C);
+ Brush *brush = NULL;
+
+ switch (mode) {
+ case CTX_MODE_PAINT_GPENCIL: {
+ Paint *paint = &ts->gp_paint->paint;
+ brush = paint->brush;
+ if (brush && brush->gpencil_settings) {
+ BKE_gpencil_brush_preset_set(bmain, brush, brush->gpencil_settings->preset_type);
+ }
+ break;
+ }
+ case CTX_MODE_SCULPT_GPENCIL: {
+ Paint *paint = &ts->gp_sculptpaint->paint;
+ brush = paint->brush;
+ if (brush && brush->gpencil_settings) {
+ BKE_gpencil_brush_preset_set(bmain, brush, brush->gpencil_settings->preset_type);
+ }
+ break;
+ }
+ case CTX_MODE_WEIGHT_GPENCIL: {
+ Paint *paint = &ts->gp_weightpaint->paint;
+ brush = paint->brush;
+ if (brush && brush->gpencil_settings) {
+ BKE_gpencil_brush_preset_set(bmain, brush, brush->gpencil_settings->preset_type);
+ }
+ break;
+ }
+ case CTX_MODE_VERTEX_GPENCIL: {
+ Paint *paint = &ts->gp_vertexpaint->paint;
+ brush = paint->brush;
+ if (brush && brush->gpencil_settings) {
+ BKE_gpencil_brush_preset_set(bmain, brush, brush->gpencil_settings->preset_type);
+ }
+ break;
+ }
+ default:
+ break;
+ }
/* notifiers */
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ WM_main_add_notifier(NC_BRUSH | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_brush_reset(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Reset Brush";
+ ot->idname = "GPENCIL_OT_brush_reset";
+ ot->description = "Reset Brush to default parameters";
+
+ /* api callbacks */
+ ot->exec = gp_brush_reset_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+static Brush *gp_brush_get_first_by_mode(Main *bmain,
+ Paint *UNUSED(paint),
+ const enum eContextObjectMode mode,
+ char tool)
+{
+ Brush *brush_next = NULL;
+ for (Brush *brush = bmain->brushes.first; brush; brush = brush_next) {
+ brush_next = brush->id.next;
+
+ if (brush->gpencil_settings == NULL) {
+ continue;
+ }
+
+ if ((mode == CTX_MODE_PAINT_GPENCIL) && (brush->gpencil_tool == tool)) {
+ return brush;
+ }
+
+ if ((mode == CTX_MODE_SCULPT_GPENCIL) && (brush->gpencil_sculpt_tool == tool)) {
+ return brush;
+ }
+
+ if ((mode == CTX_MODE_WEIGHT_GPENCIL) && (brush->gpencil_weight_tool == tool)) {
+ return brush;
+ }
+
+ if ((mode == CTX_MODE_VERTEX_GPENCIL) && (brush->gpencil_vertex_tool == tool)) {
+ return brush;
+ }
+ }
+
+ return NULL;
+}
+
+static void gp_brush_delete_mode_brushes(Main *bmain,
+ Paint *paint,
+ const enum eContextObjectMode mode)
+{
+ Brush *brush_active = paint->brush;
+ Brush *brush_next = NULL;
+ for (Brush *brush = bmain->brushes.first; brush; brush = brush_next) {
+ brush_next = brush->id.next;
+
+ if ((brush->gpencil_settings == NULL) && (brush->ob_mode != OB_MODE_PAINT_GPENCIL)) {
+ continue;
+ }
+
+ short preset = (brush->gpencil_settings) ? brush->gpencil_settings->preset_type :
+ GP_BRUSH_PRESET_UNKNOWN;
+
+ if (preset != GP_BRUSH_PRESET_UNKNOWN) {
+ /* Verify to delete only the brushes of the current mode. */
+ if (mode == CTX_MODE_PAINT_GPENCIL) {
+ if ((preset < GP_BRUSH_PRESET_AIRBRUSH) || (preset > GP_BRUSH_PRESET_TINT)) {
+ continue;
+ }
+ if ((brush_active) && (brush_active->gpencil_tool != brush->gpencil_tool)) {
+ continue;
+ }
+ }
+
+ if (mode == CTX_MODE_SCULPT_GPENCIL) {
+ if ((preset < GP_BRUSH_PRESET_SMOOTH_STROKE) || (preset > GP_BRUSH_PRESET_CLONE_STROKE)) {
+ continue;
+ }
+ if ((brush_active) && (brush_active->gpencil_sculpt_tool != brush->gpencil_sculpt_tool)) {
+ continue;
+ }
+ }
+
+ if (mode == CTX_MODE_WEIGHT_GPENCIL) {
+ if (preset != GP_BRUSH_PRESET_DRAW_WEIGHT) {
+ continue;
+ }
+ if ((brush_active) && (brush_active->gpencil_weight_tool != brush->gpencil_weight_tool)) {
+ continue;
+ }
+ }
+
+ if (mode == CTX_MODE_VERTEX_GPENCIL) {
+ if ((preset < GP_BRUSH_PRESET_VERTEX_DRAW) || (preset > GP_BRUSH_PRESET_VERTEX_REPLACE)) {
+ continue;
+ }
+ if ((brush_active) && (brush_active->gpencil_vertex_tool != brush->gpencil_vertex_tool)) {
+ continue;
+ }
+ }
+ }
+
+ /* Before delete, unpinn any material of the brush. */
+ if ((brush->gpencil_settings) && (brush->gpencil_settings->material != NULL)) {
+ brush->gpencil_settings->material = NULL;
+ brush->gpencil_settings->flag &= ~GP_BRUSH_MATERIAL_PINNED;
+ }
+
+ BKE_brush_delete(bmain, brush);
+ }
+}
+
+static int gp_brush_reset_all_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Main *bmain = CTX_data_main(C);
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ const enum eContextObjectMode mode = CTX_data_mode_enum(C);
+ Paint *paint = NULL;
+
+ switch (mode) {
+ case CTX_MODE_PAINT_GPENCIL: {
+ paint = &ts->gp_paint->paint;
+ break;
+ }
+ case CTX_MODE_SCULPT_GPENCIL: {
+ paint = &ts->gp_sculptpaint->paint;
+ break;
+ }
+ case CTX_MODE_WEIGHT_GPENCIL: {
+ paint = &ts->gp_weightpaint->paint;
+ break;
+ }
+ case CTX_MODE_VERTEX_GPENCIL: {
+ paint = &ts->gp_vertexpaint->paint;
+ break;
+ }
+ default:
+ break;
+ }
+
+ char tool = '0';
+ if (paint) {
+ Brush *brush_active = paint->brush;
+ if (brush_active) {
+ switch (mode) {
+ case CTX_MODE_PAINT_GPENCIL: {
+ tool = brush_active->gpencil_tool;
+ break;
+ }
+ case CTX_MODE_SCULPT_GPENCIL: {
+ tool = brush_active->gpencil_sculpt_tool;
+ break;
+ }
+ case CTX_MODE_WEIGHT_GPENCIL: {
+ tool = brush_active->gpencil_weight_tool;
+ break;
+ }
+ case CTX_MODE_VERTEX_GPENCIL: {
+ tool = brush_active->gpencil_vertex_tool;
+ break;
+ }
+ default: {
+ tool = brush_active->gpencil_tool;
+ break;
+ }
+ }
+ }
+
+ gp_brush_delete_mode_brushes(bmain, paint, mode);
+
+ switch (mode) {
+ case CTX_MODE_PAINT_GPENCIL: {
+ BKE_brush_gpencil_paint_presets(bmain, ts);
+ break;
+ }
+ case CTX_MODE_SCULPT_GPENCIL: {
+ BKE_brush_gpencil_sculpt_presets(bmain, ts);
+ break;
+ }
+ case CTX_MODE_WEIGHT_GPENCIL: {
+ BKE_brush_gpencil_weight_presets(bmain, ts);
+ break;
+ }
+ case CTX_MODE_VERTEX_GPENCIL: {
+ BKE_brush_gpencil_vertex_presets(bmain, ts);
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+
+ BKE_paint_toolslots_brush_validate(bmain, paint);
+
+ /* Set Again the first brush of the mode. */
+ Brush *deft_brush = gp_brush_get_first_by_mode(bmain, paint, mode, tool);
+ if (deft_brush) {
+ BKE_paint_brush_set(paint, deft_brush);
+ }
+ /* notifiers */
+ DEG_relations_tag_update(bmain);
+ WM_main_add_notifier(NC_BRUSH | NA_EDITED, NULL);
+ }
return OPERATOR_FINISHED;
}
-void GPENCIL_OT_brush_presets_create(wmOperatorType *ot)
+void GPENCIL_OT_brush_reset_all(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Create Preset Brushes";
- ot->idname = "GPENCIL_OT_brush_presets_create";
- ot->description = "Create a set of predefined Grease Pencil drawing brushes";
+ ot->name = "Reset All Brushes";
+ ot->idname = "GPENCIL_OT_brush_reset_all";
+ ot->description = "Delete all mode brushes and recreate a default set";
/* api callbacks */
- ot->exec = gp_brush_presets_create_exec;
+ ot->exec = gp_brush_reset_all_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -2323,9 +2622,9 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op)
BKE_object_defgroup_unique_name(vgroup, ob_active);
BLI_addtail(&ob_active->defbase, vgroup);
/* update vertex groups in strokes in original data */
- for (bGPDlayer *gpl_src = gpd->layers.first; gpl_src; gpl_src = gpl_src->next) {
- for (bGPDframe *gpf = gpl_src->frames.first; gpf; gpf = gpf->next) {
- for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl_src, &gpd->layers) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl_src->frames) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
MDeformVert *dvert;
int i;
for (i = 0, dvert = gps->dvert; i < gps->totpoints; i++, dvert++) {
@@ -2372,12 +2671,12 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op)
float inverse_diff_mat[4][4];
/* recalculate all stroke points */
- ED_gpencil_parent_location(depsgraph, ob_iter, gpd_src, gpl_src, diff_mat);
+ BKE_gpencil_parent_matrix_get(depsgraph, ob_iter, gpl_src, diff_mat);
invert_m4_m4(inverse_diff_mat, diff_mat);
Material *ma_src = NULL;
for (bGPDframe *gpf = gpl_new->frames.first; gpf; gpf = gpf->next) {
- for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
/* Reassign material. Look old material and try to find in destination. */
ma_src = BKE_gpencil_material(ob_src, gps->mat_nr + 1);
@@ -2468,7 +2767,8 @@ static bool gpencil_active_color_poll(bContext *C)
return false;
}
-/* **************** Lock and hide any color non used in current layer ************************** */
+/* **************** Lock and hide any color non used in current layer **************************
+ */
static int gpencil_lock_layer_exec(bContext *C, wmOperator *UNUSED(op))
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
@@ -2491,16 +2791,16 @@ static int gpencil_lock_layer_exec(bContext *C, wmOperator *UNUSED(op))
ma = BKE_gpencil_material(ob, i + 1);
if (ma) {
gp_style = ma->gp_style;
- gp_style->flag |= GP_STYLE_COLOR_LOCKED;
- gp_style->flag |= GP_STYLE_COLOR_HIDE;
+ gp_style->flag |= GP_MATERIAL_LOCKED;
+ gp_style->flag |= GP_MATERIAL_HIDE;
DEG_id_tag_update(&ma->id, ID_RECALC_COPY_ON_WRITE);
}
}
/* loop all selected strokes and unlock any color used in active layer */
- for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
/* only editable and visible layers are considered */
- if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL) &&
+ if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL) &&
(gpl->flag & GP_LAYER_ACTIVE)) {
for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
/* skip strokes that are invalid for current view */
@@ -2514,8 +2814,8 @@ static int gpencil_lock_layer_exec(bContext *C, wmOperator *UNUSED(op))
gp_style = ma->gp_style;
/* unlock/unhide color if not unlocked before */
if (gp_style != NULL) {
- gp_style->flag &= ~GP_STYLE_COLOR_LOCKED;
- gp_style->flag &= ~GP_STYLE_COLOR_HIDE;
+ gp_style->flag &= ~GP_MATERIAL_LOCKED;
+ gp_style->flag &= ~GP_MATERIAL_HIDE;
}
}
}
@@ -2555,11 +2855,11 @@ static int gpencil_color_isolate_exec(bContext *C, wmOperator *op)
MaterialGPencilStyle *active_color = BKE_gpencil_material_settings(ob, ob->actcol);
MaterialGPencilStyle *gp_style;
- int flags = GP_STYLE_COLOR_LOCKED;
+ int flags = GP_MATERIAL_LOCKED;
bool isolate = false;
if (RNA_boolean_get(op->ptr, "affect_visibility")) {
- flags |= GP_STYLE_COLOR_HIDE;
+ flags |= GP_MATERIAL_HIDE;
}
if (ELEM(NULL, gpd, active_color)) {
@@ -2677,7 +2977,7 @@ static int gpencil_color_hide_exec(bContext *C, wmOperator *op)
if (ma) {
color = ma->gp_style;
if (active_color != color) {
- color->flag |= GP_STYLE_COLOR_HIDE;
+ color->flag |= GP_MATERIAL_HIDE;
DEG_id_tag_update(&ma->id, ID_RECALC_COPY_ON_WRITE);
}
}
@@ -2685,7 +2985,7 @@ static int gpencil_color_hide_exec(bContext *C, wmOperator *op)
}
else {
/* hide selected/active */
- active_color->flag |= GP_STYLE_COLOR_HIDE;
+ active_color->flag |= GP_MATERIAL_HIDE;
}
/* updates */
@@ -2739,7 +3039,7 @@ static int gpencil_color_reveal_exec(bContext *C, wmOperator *UNUSED(op))
ma = BKE_gpencil_material(ob, i + 1);
if (ma) {
gp_style = ma->gp_style;
- gp_style->flag &= ~GP_STYLE_COLOR_HIDE;
+ gp_style->flag &= ~GP_MATERIAL_HIDE;
DEG_id_tag_update(&ma->id, ID_RECALC_COPY_ON_WRITE);
}
}
@@ -2792,7 +3092,7 @@ static int gpencil_color_lock_all_exec(bContext *C, wmOperator *UNUSED(op))
ma = BKE_gpencil_material(ob, i + 1);
if (ma) {
gp_style = ma->gp_style;
- gp_style->flag |= GP_STYLE_COLOR_LOCKED;
+ gp_style->flag |= GP_MATERIAL_LOCKED;
DEG_id_tag_update(&ma->id, ID_RECALC_COPY_ON_WRITE);
}
}
@@ -2845,7 +3145,7 @@ static int gpencil_color_unlock_all_exec(bContext *C, wmOperator *UNUSED(op))
ma = BKE_gpencil_material(ob, i + 1);
if (ma) {
gp_style = ma->gp_style;
- gp_style->flag &= ~GP_STYLE_COLOR_LOCKED;
+ gp_style->flag &= ~GP_MATERIAL_LOCKED;
DEG_id_tag_update(&ma->id, ID_RECALC_COPY_ON_WRITE);
}
}
@@ -2900,7 +3200,7 @@ static int gpencil_color_select_exec(bContext *C, wmOperator *op)
if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
/* verify something to do */
- for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
/* skip strokes that are invalid for current view */
if (ED_gpencil_stroke_can_use(C, gps) == false) {
continue;
@@ -3057,3 +3357,118 @@ bool ED_gpencil_add_lattice_modifier(const bContext *C,
return true;
}
+
+/* Masking operators */
+static int gp_layer_mask_add_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ bGPdata *gpd = (bGPdata *)ob->data;
+ bGPDlayer *gpl_active = BKE_gpencil_layer_active_get(gpd);
+ if (gpl_active == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+ char name[128];
+ RNA_string_get(op->ptr, "name", name);
+ bGPDlayer *gpl = BKE_gpencil_layer_named_get(gpd, name);
+
+ if (gpl == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "Unable to find layer to add");
+ return OPERATOR_CANCELLED;
+ }
+
+ if (gpl == gpl_active) {
+ BKE_report(op->reports, RPT_ERROR, "Cannot add active layer as mask");
+ return OPERATOR_CANCELLED;
+ }
+
+ if (BKE_gpencil_layer_mask_named_get(gpl_active, name)) {
+ BKE_report(op->reports, RPT_ERROR, "Layer already added");
+ return OPERATOR_CANCELLED;
+ }
+
+ if (gpl_active->act_mask == 256) {
+ BKE_report(op->reports, RPT_ERROR, "Maximum number of masking layers reached");
+ return OPERATOR_CANCELLED;
+ }
+
+ BKE_gpencil_layer_mask_add(gpl_active, name);
+
+ /* Reorder masking. */
+ BKE_gpencil_layer_mask_sort(gpd, gpl_active);
+
+ /* notifiers */
+ if (gpd) {
+ DEG_id_tag_update(&gpd->id,
+ ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
+ }
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_layer_mask_add(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Add New Mask Layer";
+ ot->idname = "GPENCIL_OT_layer_mask_add";
+ ot->description = "Add new layer as masking";
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* callbacks */
+ ot->exec = gp_layer_mask_add_exec;
+ ot->poll = gp_add_poll;
+
+ /* properties */
+ RNA_def_string(ot->srna, "name", NULL, 128, "Layer", "Name of the layer");
+}
+
+static int gp_layer_mask_remove_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob = CTX_data_active_object(C);
+ if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ bGPdata *gpd = (bGPdata *)ob->data;
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
+ if (gpl == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+ if (gpl->act_mask > 0) {
+ bGPDlayer_Mask *mask = BLI_findlink(&gpl->mask_layers, gpl->act_mask - 1);
+ if (mask != NULL) {
+ BKE_gpencil_layer_mask_remove(gpl, mask);
+ if ((gpl->mask_layers.first != NULL) && (gpl->act_mask == 0)) {
+ gpl->act_mask = 1;
+ }
+ }
+ }
+
+ /* Reorder masking. */
+ BKE_gpencil_layer_mask_sort(gpd, gpl);
+
+ /* 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_layer_mask_remove(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Remove Mask Layer";
+ ot->idname = "GPENCIL_OT_layer_mask_remove";
+ ot->description = "Remove Layer Mask";
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* callbacks */
+ ot->exec = gp_layer_mask_remove_exec;
+ ot->poll = gp_active_layer_poll;
+}
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index e5caeb93c73..f8ad34e8d14 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -355,7 +355,7 @@ static int gpencil_paintmode_toggle_exec(bContext *C, wmOperator *op)
Paint *paint = &ts->gp_paint->paint;
/* if not exist, create a new one */
if ((paint->brush == NULL) || (paint->brush->gpencil_settings == NULL)) {
- BKE_brush_gpencil_presets(bmain, ts);
+ BKE_brush_gpencil_paint_presets(bmain, ts);
}
BKE_paint_toolslots_brush_validate(bmain, &ts->gp_paint->paint);
}
@@ -414,6 +414,9 @@ static bool gpencil_sculptmode_toggle_poll(bContext *C)
static int gpencil_sculptmode_toggle_exec(bContext *C, wmOperator *op)
{
+ Main *bmain = CTX_data_main(C);
+ ToolSettings *ts = CTX_data_tool_settings(C);
+
const bool back = RNA_boolean_get(op->ptr, "back");
struct wmMsgBus *mbus = CTX_wm_message_bus(C);
@@ -450,6 +453,12 @@ static int gpencil_sculptmode_toggle_exec(bContext *C, wmOperator *op)
ob->mode = mode;
}
+ if (mode == OB_MODE_SCULPT_GPENCIL) {
+ /* be sure we have brushes */
+ BKE_paint_ensure(ts, (Paint **)&ts->gp_sculptpaint);
+ BKE_paint_toolslots_brush_validate(bmain, &ts->gp_sculptpaint->paint);
+ }
+
/* setup other modes */
ED_gpencil_setup_modes(C, gpd, mode);
/* set cache as dirty */
@@ -504,6 +513,9 @@ static bool gpencil_weightmode_toggle_poll(bContext *C)
static int gpencil_weightmode_toggle_exec(bContext *C, wmOperator *op)
{
+ Main *bmain = CTX_data_main(C);
+ ToolSettings *ts = CTX_data_tool_settings(C);
+
const bool back = RNA_boolean_get(op->ptr, "back");
struct wmMsgBus *mbus = CTX_wm_message_bus(C);
@@ -540,6 +552,12 @@ static int gpencil_weightmode_toggle_exec(bContext *C, wmOperator *op)
ob->mode = mode;
}
+ if (mode == OB_MODE_WEIGHT_GPENCIL) {
+ /* be sure we have brushes */
+ BKE_paint_ensure(ts, (Paint **)&ts->gp_weightpaint);
+ BKE_paint_toolslots_brush_validate(bmain, &ts->gp_weightpaint->paint);
+ }
+
/* setup other modes */
ED_gpencil_setup_modes(C, gpd, mode);
/* set cache as dirty */
@@ -580,6 +598,104 @@ void GPENCIL_OT_weightmode_toggle(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
+/* Vertex Paint Mode Management */
+
+static bool gpencil_vertexmode_toggle_poll(bContext *C)
+{
+ /* if using gpencil object, use this gpd */
+ Object *ob = CTX_data_active_object(C);
+ if ((ob) && (ob->type == OB_GPENCIL)) {
+ return ob->data != NULL;
+ }
+ return ED_gpencil_data_get_active(C) != NULL;
+}
+static int gpencil_vertexmode_toggle_exec(bContext *C, wmOperator *op)
+{
+ const bool back = RNA_boolean_get(op->ptr, "back");
+
+ struct wmMsgBus *mbus = CTX_wm_message_bus(C);
+ Main *bmain = CTX_data_main(C);
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ ToolSettings *ts = CTX_data_tool_settings(C);
+
+ bool is_object = false;
+ short mode;
+ /* if using a gpencil object, use this datablock */
+ Object *ob = CTX_data_active_object(C);
+ if ((ob) && (ob->type == OB_GPENCIL)) {
+ gpd = ob->data;
+ is_object = true;
+ }
+
+ if (gpd == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+
+ /* Just toggle paintmode flag... */
+ gpd->flag ^= GP_DATA_STROKE_VERTEXMODE;
+ /* set mode */
+ if (gpd->flag & GP_DATA_STROKE_VERTEXMODE) {
+ mode = OB_MODE_VERTEX_GPENCIL;
+ }
+ else {
+ mode = OB_MODE_OBJECT;
+ }
+
+ if (is_object) {
+ /* try to back previous mode */
+ if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_VERTEXMODE) == 0) && (back == 1)) {
+ mode = ob->restore_mode;
+ }
+ ob->restore_mode = ob->mode;
+ ob->mode = mode;
+ }
+
+ if (mode == OB_MODE_VERTEX_GPENCIL) {
+ /* be sure we have brushes */
+ BKE_paint_ensure(ts, (Paint **)&ts->gp_vertexpaint);
+ BKE_paint_toolslots_brush_validate(bmain, &ts->gp_vertexpaint->paint);
+ }
+
+ /* setup other modes */
+ ED_gpencil_setup_modes(C, gpd, mode);
+ /* set cache as dirty */
+ 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);
+
+ if (is_object) {
+ WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
+ }
+ if (G.background == false) {
+ WM_toolsystem_update_from_context_view3d(C);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_vertexmode_toggle(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Strokes Vertex Mode Toggle";
+ ot->idname = "GPENCIL_OT_vertexmode_toggle";
+ ot->description = "Enter/Exit vertex paint mode for Grease Pencil strokes";
+
+ /* callbacks */
+ ot->exec = gpencil_vertexmode_toggle_exec;
+ ot->poll = gpencil_vertexmode_toggle_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
+
+ /* properties */
+ prop = RNA_def_boolean(
+ ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+}
+
/* ************************************************ */
/* Stroke Editing Operators */
@@ -659,23 +775,17 @@ static void gp_duplicate_points(const bGPDstroke *gps,
else if (i == gps->totpoints - 1) {
len = i - start_idx + 1;
}
- // printf("copying from %d to %d = %d\n", start_idx, i, len);
/* make copies of the relevant data */
if (len) {
bGPDstroke *gpsd;
/* make a stupid copy first of the entire stroke (to get the flags too) */
- gpsd = MEM_dupallocN(gps);
+ gpsd = BKE_gpencil_stroke_duplicate((bGPDstroke *)gps, false);
/* saves original layer name */
BLI_strncpy(gpsd->runtime.tmp_layerinfo, layername, sizeof(gpsd->runtime.tmp_layerinfo));
- /* initialize triangle memory - will be calculated on next redraw */
- gpsd->triangles = NULL;
- gpsd->flag |= GP_STROKE_RECALC_GEOMETRY;
- gpsd->tot_triangles = 0;
-
/* now, make a new points array, and copy of the relevant parts */
gpsd->points = MEM_mallocN(sizeof(bGPDspoint) * len, "gps stroke points copy");
memcpy(gpsd->points, gps->points + start_idx, sizeof(bGPDspoint) * len);
@@ -695,8 +805,11 @@ static void gp_duplicate_points(const bGPDstroke *gps,
}
}
+ BKE_gpencil_stroke_geometry_update(gpsd);
+
/* add to temp buffer */
gpsd->next = gpsd->prev = NULL;
+
BLI_addtail(new_strokes, gpsd);
/* cleanup + reset for next */
@@ -746,17 +859,12 @@ static int gp_duplicate_exec(bContext *C, wmOperator *op)
bGPDstroke *gpsd;
/* make direct copies of the stroke and its points */
- gpsd = MEM_dupallocN(gps);
+ gpsd = BKE_gpencil_stroke_duplicate(gps, true);
+
BLI_strncpy(gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo));
- gpsd->points = MEM_dupallocN(gps->points);
- if (gps->dvert != NULL) {
- gpsd->dvert = MEM_dupallocN(gps->dvert);
- BKE_gpencil_stroke_weights_duplicate(gps, gpsd);
- }
- /* triangle information - will be calculated on next redraw */
- gpsd->flag |= GP_STROKE_RECALC_GEOMETRY;
- gpsd->triangles = NULL;
+ /* Initialize triangle information. */
+ BKE_gpencil_stroke_geometry_update(gpsd);
/* add to temp buffer */
gpsd->next = gpsd->prev = NULL;
@@ -827,6 +935,7 @@ static void copy_move_point(bGPDstroke *gps,
pt_final->flag = pt->flag;
pt_final->uv_fac = pt->uv_fac;
pt_final->uv_rot = pt->uv_rot;
+ copy_v4_v4(pt_final->vert_color, pt->vert_color);
if (gps->dvert != NULL) {
MDeformVert *dvert = &temp_dverts[from_idx];
@@ -864,7 +973,7 @@ static void gpencil_add_move_points(bGPDframe *gpf, bGPDstroke *gps)
pt = &gps->points[i];
if (pt->flag == GP_SPOINT_SELECT) {
/* duplicate original stroke data */
- bGPDstroke *gps_new = MEM_dupallocN(gps);
+ bGPDstroke *gps_new = BKE_gpencil_stroke_duplicate(gps, false);
gps_new->prev = gps_new->next = NULL;
/* add new points array */
@@ -876,14 +985,15 @@ static void gpencil_add_move_points(bGPDframe *gpf, bGPDstroke *gps)
gps_new->dvert = MEM_callocN(sizeof(MDeformVert), __func__);
}
- gps->flag |= GP_STROKE_RECALC_GEOMETRY;
- gps_new->triangles = NULL;
- gps_new->tot_triangles = 0;
BLI_insertlinkafter(&gpf->strokes, gps, gps_new);
/* copy selected point data to new stroke */
copy_move_point(gps_new, gps->points, gps->dvert, i, 0, true);
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gps_new);
+
/* deselect orinal point */
pt->flag &= ~GP_SPOINT_SELECT;
}
@@ -926,7 +1036,6 @@ static void gpencil_add_move_points(bGPDframe *gpf, bGPDstroke *gps)
copy_move_point(gps, temp_points, temp_dverts, i, i2, false);
i2++;
}
- gps->flag |= GP_STROKE_RECALC_GEOMETRY;
/* If first point, add new point at the beginning. */
if (do_first) {
@@ -951,6 +1060,9 @@ static void gpencil_add_move_points(bGPDframe *gpf, bGPDstroke *gps)
pt->flag |= GP_SPOINT_SELECT;
}
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gps);
+
MEM_SAFE_FREE(temp_points);
MEM_SAFE_FREE(temp_dverts);
}
@@ -1190,7 +1302,8 @@ static int gp_strokes_copy_exec(bContext *C, wmOperator *op)
bGPDstroke *gpsd;
/* make direct copies of the stroke and its points */
- gpsd = MEM_dupallocN(gps);
+ gpsd = BKE_gpencil_stroke_duplicate(gps, false);
+
/* saves original layer name */
BLI_strncpy(gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo));
gpsd->points = MEM_dupallocN(gps->points);
@@ -1199,10 +1312,8 @@ static int gp_strokes_copy_exec(bContext *C, wmOperator *op)
BKE_gpencil_stroke_weights_duplicate(gps, gpsd);
}
- /* triangles cache - will be recalculated on next redraw */
- gpsd->flag |= GP_STROKE_RECALC_GEOMETRY;
- gpsd->tot_triangles = 0;
- gpsd->triangles = NULL;
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gpsd);
/* add to temp buffer */
gpsd->next = gpsd->prev = NULL;
@@ -1267,6 +1378,9 @@ void GPENCIL_OT_copy(wmOperatorType *ot)
static bool gp_strokes_paste_poll(bContext *C)
{
+ if (CTX_wm_area(C)->spacetype != SPACE_VIEW3D) {
+ return false;
+ }
/* 1) Must have GP datablock to paste to
* - We don't need to have an active layer though, as that can easily get added
* - If the active layer is locked, we can't paste there,
@@ -1285,20 +1399,17 @@ typedef enum eGP_PasteMode {
static int gp_strokes_paste_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
- bGPdata *gpd = ED_gpencil_data_get_active(C);
- bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); /* only use active for copy merge */
+ bGPdata *gpd = (bGPdata *)ob->data;
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); /* only use active for copy merge */
Scene *scene = CTX_data_scene(C);
bGPDframe *gpf;
eGP_PasteMode type = RNA_enum_get(op->ptr, "type");
+ const bool on_back = RNA_boolean_get(op->ptr, "paste_back");
GHash *new_colors;
- /* check for various error conditions */
- if (gpd == NULL) {
- BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
- return OPERATOR_CANCELLED;
- }
- else if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) {
+ /* Check for various error conditions. */
+ if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) {
BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition");
return OPERATOR_CANCELLED;
}
@@ -1312,7 +1423,7 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op)
/* no active layer - let's just create one */
gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
}
- else if ((gpencil_layer_is_editable(gpl) == false) && (type == GP_COPY_TO_ACTIVE)) {
+ else if ((BKE_gpencil_layer_is_editable(gpl) == false) && (type == GP_COPY_TO_ACTIVE)) {
BKE_report(
op->reports, RPT_ERROR, "Can not paste strokes when active layer is hidden or locked");
return OPERATOR_CANCELLED;
@@ -1330,17 +1441,6 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op)
}
if (ok == false) {
- /* XXX: this check is not 100% accurate
- * (i.e. image editor is incompatible with normal 2D strokes),
- * but should be enough to give users a good idea of what's going on.
- */
- if (CTX_wm_area(C)->spacetype == SPACE_VIEW3D) {
- BKE_report(op->reports, RPT_ERROR, "Cannot paste 2D strokes in 3D View");
- }
- else {
- BKE_report(op->reports, RPT_ERROR, "Cannot paste 3D strokes in 2D editors");
- }
-
return OPERATOR_CANCELLED;
}
}
@@ -1362,14 +1462,15 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op)
new_colors = gp_copybuf_validate_colormap(C);
/* Copy over the strokes from the buffer (and adjust the colors) */
- for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) {
+ bGPDstroke *gps_init = (!on_back) ? gp_strokes_copypastebuf.first : gp_strokes_copypastebuf.last;
+ for (bGPDstroke *gps = gps_init; gps; gps = (!on_back) ? gps->next : gps->prev) {
if (ED_gpencil_stroke_can_use(C, gps)) {
/* Need to verify if layer exists */
if (type != GP_COPY_TO_ACTIVE) {
gpl = BLI_findstring(&gpd->layers, gps->runtime.tmp_layerinfo, offsetof(bGPDlayer, info));
if (gpl == NULL) {
/* no layer - use active (only if layer deleted before paste) */
- gpl = CTX_data_active_gpencil_layer(C);
+ gpl = BKE_gpencil_layer_active_get(gpd);
}
}
@@ -1378,26 +1479,26 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op)
* we are obliged to add a new frame if one
* doesn't exist already
*/
- gpf = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_ADD_NEW);
+ gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_ADD_NEW);
if (gpf) {
/* Create new stroke */
- bGPDstroke *new_stroke = MEM_dupallocN(gps);
+ bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps, true);
new_stroke->runtime.tmp_layerinfo[0] = '\0';
+ new_stroke->next = new_stroke->prev = NULL;
- new_stroke->points = MEM_dupallocN(gps->points);
- if (gps->dvert != NULL) {
- new_stroke->dvert = MEM_dupallocN(gps->dvert);
- BKE_gpencil_stroke_weights_duplicate(gps, new_stroke);
- }
- new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY;
- new_stroke->triangles = NULL;
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(new_stroke);
- new_stroke->next = new_stroke->prev = NULL;
- BLI_addtail(&gpf->strokes, new_stroke);
+ if (on_back) {
+ BLI_addhead(&gpf->strokes, new_stroke);
+ }
+ else {
+ BLI_addtail(&gpf->strokes, new_stroke);
+ }
/* Remap material */
Material *ma = BLI_ghash_lookup(new_colors, POINTER_FROM_INT(new_stroke->mat_nr));
- new_stroke->mat_nr = BKE_gpencil_object_material_get_index(ob, ma);
+ new_stroke->mat_nr = BKE_gpencil_object_material_index_get(ob, ma);
CLAMP_MIN(new_stroke->mat_nr, 0);
}
}
@@ -1415,6 +1516,8 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op)
void GPENCIL_OT_paste(wmOperatorType *ot)
{
+ PropertyRNA *prop;
+
static const EnumPropertyItem copy_type[] = {
{GP_COPY_TO_ACTIVE, "ACTIVE", 0, "Paste to Active", ""},
{GP_COPY_BY_LAYER, "LAYER", 0, "Paste by Layer", ""},
@@ -1435,6 +1538,10 @@ void GPENCIL_OT_paste(wmOperatorType *ot)
/* properties */
ot->prop = RNA_def_enum(ot->srna, "type", copy_type, GP_COPY_TO_ACTIVE, "Type", "");
+
+ prop = RNA_def_boolean(
+ ot->srna, "paste_back", 0, "Paste on Back", "Add pasted strokes behind all strokes");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
/* ******************* Move To Layer ****************************** */
@@ -1477,7 +1584,6 @@ static int gp_move_to_layer_exec(bContext *C, wmOperator *op)
*/
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
bGPDframe *gpf = gpl->actframe;
- bGPDstroke *gps, *gpsn;
/* skip if no frame with strokes, or if this is the layer we're moving strokes to */
if ((gpl == target_layer) || (gpf == NULL)) {
@@ -1485,8 +1591,7 @@ static int gp_move_to_layer_exec(bContext *C, wmOperator *op)
}
/* make copies of selected strokes, and deselect these once we're done */
- for (gps = gpf->strokes.first; gps; gps = gpsn) {
- gpsn = gps->next;
+ LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
/* skip strokes that are invalid for current view */
if (ED_gpencil_stroke_can_use(C, gps) == false) {
@@ -1514,7 +1619,7 @@ static int gp_move_to_layer_exec(bContext *C, wmOperator *op)
/* Paste them all in one go */
if (strokes.first) {
- bGPDframe *gpf = BKE_gpencil_layer_getframe(target_layer, CFRA, GP_GETFRAME_ADD_NEW);
+ 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));
@@ -1554,32 +1659,13 @@ void GPENCIL_OT_move_to_layer(wmOperatorType *ot)
/* ********************* Add Blank Frame *************************** */
-/* Basically the same as the drawing op */
-static bool UNUSED_FUNCTION(gp_blank_frame_add_poll)(bContext *C)
-{
- if (ED_operator_regionactive(C)) {
- /* check if current context can support GPencil data */
- if (ED_gpencil_data_get_pointers(C, NULL) != NULL) {
- return 1;
- }
- else {
- CTX_wm_operator_poll_msg_set(C, "Failed to find Grease Pencil data to draw into");
- }
- }
- else {
- CTX_wm_operator_poll_msg_set(C, "Active region not set");
- }
-
- return 0;
-}
-
static int gp_blank_frame_add_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
Scene *scene = CTX_data_scene(C);
int cfra = CFRA;
- bGPDlayer *active_gpl = BKE_gpencil_layer_getactive(gpd);
+ bGPDlayer *active_gpl = BKE_gpencil_layer_active_get(gpd);
const bool all_layers = RNA_boolean_get(op->ptr, "all_layers");
@@ -1599,7 +1685,7 @@ static int gp_blank_frame_add_exec(bContext *C, wmOperator *op)
}
/* 1) Check for an existing frame on the current frame */
- bGPDframe *gpf = BKE_gpencil_layer_find_frame(gpl, cfra);
+ bGPDframe *gpf = BKE_gpencil_layer_frame_find(gpl, cfra);
if (gpf) {
/* Shunt all frames after (and including) the existing one later by 1-frame */
for (; gpf; gpf = gpf->next) {
@@ -1608,7 +1694,7 @@ static int gp_blank_frame_add_exec(bContext *C, wmOperator *op)
}
/* 2) Now add a new frame, with nothing in it */
- gpl->actframe = BKE_gpencil_layer_getframe(gpl, cfra, GP_GETFRAME_ADD_NEW);
+ gpl->actframe = BKE_gpencil_layer_frame_get(gpl, cfra, GP_GETFRAME_ADD_NEW);
}
CTX_DATA_END;
@@ -1650,7 +1736,7 @@ void GPENCIL_OT_blank_frame_add(wmOperatorType *ot)
static bool gp_actframe_delete_poll(bContext *C)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
- bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
/* only if there's an active layer with an active frame */
return (gpl && gpl->actframe);
@@ -1659,7 +1745,7 @@ static bool gp_actframe_delete_poll(bContext *C)
static bool gp_annotation_actframe_delete_poll(bContext *C)
{
bGPdata *gpd = ED_annotation_data_get_active(C);
- bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
/* only if there's an active layer with an active frame */
return (gpl && gpl->actframe);
@@ -1673,11 +1759,11 @@ static int gp_actframe_delete_exec(bContext *C, wmOperator *op)
bGPdata *gpd = (!is_annotation) ? ED_gpencil_data_get_active(C) :
ED_annotation_data_get_active(C);
- bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
Scene *scene = CTX_data_scene(C);
- bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_USE_PREV);
+ bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_USE_PREV);
/* if there's no existing Grease-Pencil data there, add some */
if (gpd == NULL) {
@@ -1690,7 +1776,7 @@ static int gp_actframe_delete_exec(bContext *C, wmOperator *op)
}
/* delete it... */
- BKE_gpencil_layer_delframe(gpl, gpf);
+ BKE_gpencil_layer_frame_delete(gpl, gpf);
/* notifiers */
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
@@ -1747,14 +1833,14 @@ static int gp_actframe_delete_all_exec(bContext *C, wmOperator *op)
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
/* try to get the "active" frame - but only if it actually occurs on this frame */
- bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_USE_PREV);
+ bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_USE_PREV);
if (gpf == NULL) {
continue;
}
/* delete it... */
- BKE_gpencil_layer_delframe(gpl, gpf);
+ BKE_gpencil_layer_frame_delete(gpl, gpf);
/* we successfully modified something */
success = true;
@@ -1821,15 +1907,13 @@ static int gp_delete_selected_strokes(bContext *C)
for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
- bGPDstroke *gps, *gpsn;
if (gpf == NULL) {
continue;
}
/* simply delete strokes which are selected */
- for (gps = gpf->strokes.first; gps; gps = gpsn) {
- gpsn = gps->next;
+ LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
/* skip strokes that are invalid for current view */
if (ED_gpencil_stroke_can_use(C, gps) == false) {
@@ -2060,9 +2144,8 @@ static int gp_dissolve_selected_points(bContext *C, eGP_DissolveMode mode)
gps->dvert = new_dvert;
gps->totpoints = tot;
- /* triangles cache needs to be recalculated */
- gps->flag |= GP_STROKE_RECALC_GEOMETRY;
- gps->tot_triangles = 0;
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gps);
/* deselect the stroke, since none of its selected points will still be selected */
gps->flag &= ~GP_STROKE_SELECT;
@@ -2104,7 +2187,7 @@ static void gp_stroke_join_islands(bGPDframe *gpf, bGPDstroke *gps_first, bGPDst
const int totpoints = gps_first->totpoints + gps_last->totpoints;
/* create new stroke */
- bGPDstroke *join_stroke = MEM_dupallocN(gps_first);
+ bGPDstroke *join_stroke = BKE_gpencil_stroke_duplicate(gps_first, false);
join_stroke->points = MEM_callocN(sizeof(bGPDspoint) * totpoints, __func__);
join_stroke->totpoints = totpoints;
@@ -2132,6 +2215,7 @@ static void gp_stroke_join_islands(bGPDframe *gpf, bGPDstroke *gps_first, bGPDst
pt_final->strength = pt->strength;
pt_final->time = delta;
pt_final->flag = pt->flag;
+ copy_v4_v4(pt_final->vert_color, pt->vert_color);
/* retiming with fixed time interval (we cannot determine real time) */
delta += 0.01f;
@@ -2170,6 +2254,8 @@ static void gp_stroke_join_islands(bGPDframe *gpf, bGPDstroke *gps_first, bGPDst
/* add new stroke at head */
BLI_addhead(&gpf->strokes, join_stroke);
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(join_stroke);
/* remove first stroke */
BLI_remlink(&gpf->strokes, gps_first);
@@ -2246,18 +2332,14 @@ void gp_stroke_delete_tagged_points(bGPDframe *gpf,
/* Create each new stroke... */
for (idx = 0; idx < num_islands; idx++) {
tGPDeleteIsland *island = &islands[idx];
- new_stroke = MEM_dupallocN(gps);
+ new_stroke = BKE_gpencil_stroke_duplicate(gps, false);
/* if cyclic and first stroke, save to join later */
if ((is_cyclic) && (gps_first == NULL)) {
gps_first = new_stroke;
}
- /* initialize triangle memory - to be calculated on next redraw */
- new_stroke->triangles = NULL;
- new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY;
new_stroke->flag &= ~GP_STROKE_CYCLIC;
- new_stroke->tot_triangles = 0;
/* Compute new buffer size (+ 1 needed as the endpoint index is "inclusive") */
new_stroke->totpoints = island->end_idx - island->start_idx + 1;
@@ -2320,6 +2402,9 @@ void gp_stroke_delete_tagged_points(bGPDframe *gpf,
BKE_gpencil_free_stroke(new_stroke);
}
else {
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(new_stroke);
+
if (next_stroke) {
BLI_insertlinkbefore(&gpf->strokes, next_stroke, new_stroke);
}
@@ -2355,15 +2440,13 @@ static int gp_delete_selected_points(bContext *C)
for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
- bGPDstroke *gps, *gpsn;
if (gpf == NULL) {
continue;
}
/* simply delete strokes which are selected */
- for (gps = gpf->strokes.first; gps; gps = gpsn) {
- gpsn = gps->next;
+ LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
/* skip strokes that are invalid for current view */
if (ED_gpencil_stroke_can_use(C, gps) == false) {
@@ -2379,7 +2462,7 @@ static int gp_delete_selected_points(bContext *C)
gps->flag &= ~GP_STROKE_SELECT;
/* delete unwanted points by splitting stroke into several smaller ones */
- gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT, false, 0);
+ gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0);
changed = true;
}
@@ -2532,16 +2615,16 @@ static int gp_snap_to_grid(bContext *C, wmOperator *UNUSED(op))
Object *obact = CTX_data_active_object(C);
const float gridf = ED_view3d_grid_view_scale(scene, v3d, rv3d, NULL);
- for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
/* only editable and visible layers are considered */
- if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
+ if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
bGPDframe *gpf = gpl->actframe;
float diff_mat[4][4];
/* calculate difference matrix object */
- ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat);
+ BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat);
- for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
bGPDspoint *pt;
int i;
@@ -2568,7 +2651,7 @@ static int gp_snap_to_grid(bContext *C, wmOperator *UNUSED(op))
/* return data */
copy_v3_v3(&pt->x, fpt);
- gp_apply_parent_point(depsgraph, obact, gpd, gpl, pt);
+ gp_apply_parent_point(depsgraph, obact, gpl, pt);
}
}
}
@@ -2609,16 +2692,16 @@ static int gp_snap_to_cursor(bContext *C, wmOperator *op)
const bool use_offset = RNA_boolean_get(op->ptr, "use_offset");
const float *cursor_global = scene->cursor.location;
- for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
/* only editable and visible layers are considered */
- if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
+ if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
bGPDframe *gpf = gpl->actframe;
float diff_mat[4][4];
/* calculate difference matrix */
- ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat);
+ BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat);
- for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
bGPDspoint *pt;
int i;
@@ -2652,7 +2735,7 @@ static int gp_snap_to_cursor(bContext *C, wmOperator *op)
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
if (pt->flag & GP_SPOINT_SELECT) {
copy_v3_v3(&pt->x, cursor_global);
- gp_apply_parent_point(depsgraph, obact, gpd, gpl, pt);
+ gp_apply_parent_point(depsgraph, obact, gpl, pt);
}
}
}
@@ -2707,16 +2790,16 @@ static int gp_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op))
INIT_MINMAX(min, max);
/* calculate midpoints from selected points */
- for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
/* only editable and visible layers are considered */
- if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
+ if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
bGPDframe *gpf = gpl->actframe;
float diff_mat[4][4];
/* calculate difference matrix */
- ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat);
+ BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat);
- for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
bGPDspoint *pt;
int i;
@@ -2783,7 +2866,7 @@ void GPENCIL_OT_snap_cursor_to_selected(wmOperatorType *ot)
static int gp_stroke_apply_thickness_exec(bContext *C, wmOperator *UNUSED(op))
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
- bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
/* sanity checks */
if (ELEM(NULL, gpd, gpl, gpl->frames.first)) {
@@ -2791,8 +2874,8 @@ static int gp_stroke_apply_thickness_exec(bContext *C, wmOperator *UNUSED(op))
}
/* loop all strokes */
- for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
- for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
/* Apply thickness */
if ((gps->thickness == 0) && (gpl->line_change == 0)) {
gps->thickness = gpl->thickness;
@@ -2866,8 +2949,8 @@ static int gp_stroke_cyclical_set_exec(bContext *C, wmOperator *op)
continue;
}
/* skip hidden or locked colors */
- if (!gp_style || (gp_style->flag & GP_STYLE_COLOR_HIDE) ||
- (gp_style->flag & GP_STYLE_COLOR_LOCKED)) {
+ if (!gp_style || (gp_style->flag & GP_MATERIAL_HIDE) ||
+ (gp_style->flag & GP_MATERIAL_LOCKED)) {
continue;
}
@@ -2891,7 +2974,7 @@ static int gp_stroke_cyclical_set_exec(bContext *C, wmOperator *op)
/* Create new geometry. */
if ((gps->flag & GP_STROKE_CYCLIC) && (geometry)) {
- BKE_gpencil_close_stroke(gps);
+ BKE_gpencil_stroke_close(gps);
}
}
@@ -2980,8 +3063,8 @@ static int gp_stroke_caps_set_exec(bContext *C, wmOperator *op)
continue;
}
/* skip hidden or locked colors */
- if (!gp_style || (gp_style->flag & GP_STYLE_COLOR_HIDE) ||
- (gp_style->flag & GP_STYLE_COLOR_LOCKED)) {
+ if (!gp_style || (gp_style->flag & GP_MATERIAL_HIDE) ||
+ (gp_style->flag & GP_MATERIAL_LOCKED)) {
continue;
}
@@ -3061,6 +3144,7 @@ static void gpencil_flip_stroke(bGPDstroke *gps)
pt.pressure = point->pressure;
pt.strength = point->strength;
pt.time = point->time;
+ copy_v4_v4(pt.vert_color, point->vert_color);
/* replace first point with last point */
point2 = &gps->points[end];
@@ -3071,6 +3155,7 @@ static void gpencil_flip_stroke(bGPDstroke *gps)
point->pressure = point2->pressure;
point->strength = point2->strength;
point->time = point2->time;
+ copy_v4_v4(point->vert_color, point2->vert_color);
/* replace last point with first saved before */
point = &gps->points[end];
@@ -3081,6 +3166,7 @@ static void gpencil_flip_stroke(bGPDstroke *gps)
point->pressure = pt.pressure;
point->strength = pt.strength;
point->time = pt.time;
+ copy_v4_v4(point->vert_color, pt.vert_color);
end--;
}
@@ -3111,6 +3197,7 @@ static void gpencil_stroke_copy_point(bGPDstroke *gps,
newpoint->pressure = pressure;
newpoint->strength = strength;
newpoint->time = point->time + deltatime;
+ copy_v4_v4(newpoint->vert_color, point->vert_color);
if (gps->dvert != NULL) {
MDeformVert *dvert = &gps->dvert[idx];
@@ -3184,8 +3271,7 @@ static void gpencil_stroke_join_strokes(bGPDstroke *gps_a,
static int gp_stroke_join_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
- bGPDlayer *activegpl = BKE_gpencil_layer_getactive(gpd);
- bGPDstroke *gps, *gpsn;
+ bGPDlayer *activegpl = BKE_gpencil_layer_active_get(gpd);
Object *ob = CTX_data_active_object(C);
bGPDframe *gpf_a = NULL;
@@ -3215,8 +3301,7 @@ static int gp_stroke_join_exec(bContext *C, wmOperator *op)
continue;
}
- for (gps = gpf->strokes.first; gps; gps = gpsn) {
- gpsn = gps->next;
+ LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
if (gps->flag & GP_STROKE_SELECT) {
/* skip strokes that are invalid for current view */
if (ED_gpencil_stroke_can_use(C, gps) == false) {
@@ -3241,15 +3326,7 @@ static int gp_stroke_join_exec(bContext *C, wmOperator *op)
/* create a new stroke if was not created before (only created if something to join) */
if (new_stroke == NULL) {
- new_stroke = MEM_dupallocN(stroke_a);
- new_stroke->points = MEM_dupallocN(stroke_a->points);
- if (stroke_a->dvert != NULL) {
- new_stroke->dvert = MEM_dupallocN(stroke_a->dvert);
- BKE_gpencil_stroke_weights_duplicate(stroke_a, new_stroke);
- }
- new_stroke->triangles = NULL;
- new_stroke->tot_triangles = 0;
- new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY;
+ new_stroke = BKE_gpencil_stroke_duplicate(stroke_a, true);
/* if new, set current color */
if (type == GP_STROKE_JOINCOPY) {
@@ -3263,6 +3340,9 @@ static int gp_stroke_join_exec(bContext *C, wmOperator *op)
/* if join only, delete old strokes */
if (type == GP_STROKE_JOIN) {
if (stroke_a) {
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(new_stroke);
+
BLI_insertlinkbefore(&gpf_a->strokes, stroke_a, new_stroke);
BLI_remlink(&gpf->strokes, stroke_a);
BKE_gpencil_free_stroke(stroke_a);
@@ -3287,6 +3367,8 @@ static int gp_stroke_join_exec(bContext *C, wmOperator *op)
if (activegpl->actframe == NULL) {
activegpl->actframe = BKE_gpencil_frame_addnew(activegpl, gpf_a->framenum);
}
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(new_stroke);
BLI_addtail(&activegpl->actframe->strokes, new_stroke);
}
@@ -3347,7 +3429,7 @@ static int gp_stroke_flip_exec(bContext *C, wmOperator *UNUSED(op))
continue;
}
- for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
if (gps->flag & GP_STROKE_SELECT) {
/* skip strokes that are invalid for current view */
if (ED_gpencil_stroke_can_use(C, gps) == false) {
@@ -3460,7 +3542,7 @@ static int gp_strokes_reproject_exec(bContext *C, wmOperator *op)
GP_REPROJECT_TOP,
GP_REPROJECT_CURSOR)) {
if (mode != GP_REPROJECT_CURSOR) {
- ED_gp_get_drawing_reference(scene, ob, gpl, ts->gpencil_v3d_align, origin);
+ ED_gpencil_drawing_reference_get(scene, ob, gpl, ts->gpencil_v3d_align, origin);
}
else {
copy_v3_v3(origin, scene->cursor.location);
@@ -3495,7 +3577,7 @@ static int gp_strokes_reproject_exec(bContext *C, wmOperator *op)
copy_v3_v3(&pt->x, &pt2.x);
/* apply parent again */
- gp_apply_parent_point(depsgraph, ob, gpd, gpl, pt);
+ gp_apply_parent_point(depsgraph, ob, gpl, pt);
}
/* Project screen-space back to 3D space (from current perspective)
* so that all points have been treated the same way. */
@@ -3602,6 +3684,43 @@ void GPENCIL_OT_reproject(wmOperatorType *ot)
ot->srna, "type", reproject_type, GP_REPROJECT_VIEW, "Projection Type", "");
}
+static int gp_recalc_geometry_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob = CTX_data_active_object(C);
+ if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ bGPdata *gpd = (bGPdata *)ob->data;
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ BKE_gpencil_stroke_geometry_update(gps);
+ }
+ }
+ }
+ /* update changed data */
+ 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_recalc_geometry(wmOperatorType *ot)
+{
+
+ /* identifiers */
+ ot->name = "Recalculate internal geometry";
+ ot->idname = "GPENCIL_OT_recalc_geometry";
+ ot->description = "Update all internal geometry data";
+
+ /* callbacks */
+ ot->exec = gp_recalc_geometry_exec;
+ ot->poll = gp_active_layer_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
/* ******************* Stroke subdivide ************************** */
/* helper to smooth */
static void gp_smooth_stroke(bContext *C, wmOperator *op)
@@ -3629,19 +3748,19 @@ static void gp_smooth_stroke(bContext *C, wmOperator *op)
/* perform smoothing */
if (smooth_position) {
- BKE_gpencil_smooth_stroke(gps, i, factor);
+ BKE_gpencil_stroke_smooth(gps, i, factor);
}
if (smooth_strength) {
- BKE_gpencil_smooth_stroke_strength(gps, i, factor);
+ BKE_gpencil_stroke_smooth_strength(gps, i, factor);
}
if (smooth_thickness) {
/* thickness need to repeat process several times */
for (int r2 = 0; r2 < r * 20; r2++) {
- BKE_gpencil_smooth_stroke_thickness(gps, i, factor);
+ BKE_gpencil_stroke_smooth_thickness(gps, i, factor);
}
}
if (smooth_uv) {
- BKE_gpencil_smooth_stroke_uv(gps, i, factor);
+ BKE_gpencil_stroke_smooth_uv(gps, i, factor);
}
}
}
@@ -3710,7 +3829,6 @@ static int gp_stroke_subdivide_exec(bContext *C, wmOperator *op)
if (gps->dvert != NULL) {
gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints);
}
- gps->flag |= GP_STROKE_RECALC_GEOMETRY;
/* loop and interpolate */
i2 = 0;
@@ -3724,6 +3842,7 @@ static int gp_stroke_subdivide_exec(bContext *C, wmOperator *op)
pt_final->strength = pt->strength;
pt_final->time = pt->time;
pt_final->flag = pt->flag;
+ copy_v4_v4(pt_final->vert_color, pt->vert_color);
if (gps->dvert != NULL) {
dvert = &temp_dverts[i];
@@ -3747,6 +3866,7 @@ static int gp_stroke_subdivide_exec(bContext *C, wmOperator *op)
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;
@@ -3780,9 +3900,8 @@ static int gp_stroke_subdivide_exec(bContext *C, wmOperator *op)
MEM_SAFE_FREE(temp_dverts);
}
- /* triangles cache needs to be recalculated */
- gps->flag |= GP_STROKE_RECALC_GEOMETRY;
- gps->tot_triangles = 0;
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gps);
}
}
GP_EDITABLE_STROKES_END(gpstroke_iter);
@@ -3850,7 +3969,7 @@ static int gp_stroke_simplify_exec(bContext *C, wmOperator *op)
GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
if (gps->flag & GP_STROKE_SELECT) {
/* simplify stroke using Ramer-Douglas-Peucker algorithm */
- BKE_gpencil_simplify_stroke(gps, factor);
+ BKE_gpencil_stroke_simplify_adaptive(gps, factor);
}
}
GP_EDITABLE_STROKES_END(gpstroke_iter);
@@ -3899,7 +4018,7 @@ static int gp_stroke_simplify_fixed_exec(bContext *C, wmOperator *op)
GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
if (gps->flag & GP_STROKE_SELECT) {
for (int i = 0; i < steps; i++) {
- BKE_gpencil_simplify_fixed(gps);
+ BKE_gpencil_stroke_simplify_fixed(gps);
}
}
}
@@ -3949,7 +4068,7 @@ static int gp_stroke_sample_exec(bContext *C, wmOperator *op)
/* Go through each editable + selected stroke */
GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
if (gps->flag & GP_STROKE_SELECT) {
- BKE_gpencil_sample_stroke(gps, length, true);
+ BKE_gpencil_stroke_sample(gps, length, true);
}
}
GP_EDITABLE_STROKES_END(gpstroke_iter);
@@ -4001,14 +4120,12 @@ static int gp_stroke_trim_exec(bContext *C, wmOperator *UNUSED(op))
for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
- bGPDstroke *gps, *gpsn;
if (gpf == NULL) {
continue;
}
- for (gps = gpf->strokes.first; gps; gps = gpsn) {
- gpsn = gps->next;
+ LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
/* skip strokes that are invalid for current view */
if (ED_gpencil_stroke_can_use(C, gps) == false) {
@@ -4016,7 +4133,7 @@ static int gp_stroke_trim_exec(bContext *C, wmOperator *UNUSED(op))
}
if (gps->flag & GP_STROKE_SELECT) {
- BKE_gpencil_trim_stroke(gps);
+ BKE_gpencil_stroke_trim(gps);
}
}
/* if not multiedit, exit loop*/
@@ -4066,7 +4183,7 @@ static int gp_stroke_separate_exec(bContext *C, wmOperator *op)
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Base *base_old = CTX_data_active_base(C);
+ Base *base_prev = CTX_data_active_base(C);
bGPdata *gpd_src = ED_gpencil_data_get_active(C);
Object *ob = CTX_data_active_object(C);
@@ -4096,7 +4213,7 @@ static int gp_stroke_separate_exec(bContext *C, wmOperator *op)
/* Take into account user preferences for duplicating actions. */
short dupflag = (U.dupflag & USER_DUP_ACT);
- base_new = ED_object_add_duplicate(bmain, scene, view_layer, base_old, dupflag);
+ base_new = ED_object_add_duplicate(bmain, scene, view_layer, base_prev, dupflag);
ob_dst = base_new->object;
ob_dst->mode = OB_MODE_OBJECT;
/* create new grease pencil datablock */
@@ -4111,7 +4228,6 @@ static int gp_stroke_separate_exec(bContext *C, wmOperator *op)
for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
- bGPDstroke *gps, *gpsn;
if (gpf == NULL) {
continue;
@@ -4119,8 +4235,7 @@ static int gp_stroke_separate_exec(bContext *C, wmOperator *op)
gpf_dst = NULL;
- for (gps = gpf->strokes.first; gps; gps = gpsn) {
- gpsn = gps->next;
+ LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
/* skip strokes that are invalid for current view */
if (ED_gpencil_stroke_can_use(C, gps) == false) {
@@ -4139,7 +4254,7 @@ static int gp_stroke_separate_exec(bContext *C, wmOperator *op)
/* add frame if not created before */
if (gpf_dst == NULL) {
- gpf_dst = BKE_gpencil_layer_getframe(gpl_dst, gpf->framenum, GP_GETFRAME_ADD_NEW);
+ gpf_dst = BKE_gpencil_layer_frame_get(gpl_dst, gpf->framenum, GP_GETFRAME_ADD_NEW);
}
/* add duplicate materials */
@@ -4152,7 +4267,7 @@ static int gp_stroke_separate_exec(bContext *C, wmOperator *op)
/* selected points mode */
if (mode == GP_SEPARATE_POINT) {
/* make copy of source stroke */
- bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps);
+ bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps, true);
/* Reassign material. */
gps_dst->mat_nr = idx;
@@ -4169,7 +4284,7 @@ static int gp_stroke_separate_exec(bContext *C, wmOperator *op)
gp_stroke_delete_tagged_points(gpf_dst, gps_dst, NULL, GP_SPOINT_SELECT, false, 0);
/* delete selected points from origin stroke */
- gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT, false, 0);
+ gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0);
}
/* selected strokes mode */
else if (mode == GP_SEPARATE_STROKE) {
@@ -4200,10 +4315,10 @@ static int gp_stroke_separate_exec(bContext *C, wmOperator *op)
if (gpl) {
/* try to set a new active layer in source datablock */
if (gpl->prev) {
- BKE_gpencil_layer_setactive(gpd_src, gpl->prev);
+ BKE_gpencil_layer_active_set(gpd_src, gpl->prev);
}
else if (gpl->next) {
- BKE_gpencil_layer_setactive(gpd_src, gpl->next);
+ BKE_gpencil_layer_active_set(gpd_src, gpl->next);
}
/* unlink from source datablock */
BLI_remlink(&gpd_src->layers, gpl);
@@ -4212,8 +4327,8 @@ static int gp_stroke_separate_exec(bContext *C, wmOperator *op)
BLI_addtail(&gpd_dst->layers, gpl);
/* add duplicate materials */
- for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
- for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
/* skip strokes that are invalid for current view */
if (ED_gpencil_stroke_can_use(C, gps) == false) {
continue;
@@ -4227,8 +4342,8 @@ static int gp_stroke_separate_exec(bContext *C, wmOperator *op)
/* Ensure destination object has one active layer. */
if (gpd_dst->layers.first != NULL) {
- if (BKE_gpencil_layer_getactive(gpd_dst) == NULL) {
- BKE_gpencil_layer_setactive(gpd_dst, gpd_dst->layers.first);
+ if (BKE_gpencil_layer_active_get(gpd_dst) == NULL) {
+ BKE_gpencil_layer_active_set(gpd_dst, gpd_dst->layers.first);
}
}
@@ -4288,14 +4403,12 @@ static int gp_stroke_split_exec(bContext *C, wmOperator *UNUSED(op))
for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
- bGPDstroke *gps, *gpsn;
if (gpf == NULL) {
continue;
}
- for (gps = gpf->strokes.first; gps; gps = gpsn) {
- gpsn = gps->next;
+ LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
/* skip strokes that are invalid for current view */
if (ED_gpencil_stroke_can_use(C, gps) == false) {
@@ -4308,7 +4421,7 @@ static int gp_stroke_split_exec(bContext *C, wmOperator *UNUSED(op))
/* split selected strokes */
if (gps->flag & GP_STROKE_SELECT) {
/* make copy of source stroke */
- bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps);
+ bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps, true);
/* link to same frame */
BLI_addtail(&gpf->strokes, gps_dst);
@@ -4322,11 +4435,11 @@ static int gp_stroke_split_exec(bContext *C, wmOperator *UNUSED(op))
gp_stroke_delete_tagged_points(gpf, gps_dst, NULL, GP_SPOINT_SELECT, true, 0);
/* delete selected points from origin stroke */
- gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT, false, 0);
+ gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0);
}
}
/* select again tagged points */
- for (gps = gpf->strokes.first; gps; gps = gps->next) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
bGPDspoint *ptn = gps->points;
for (int i2 = 0; i2 < gps->totpoints; i2++, ptn++) {
if (ptn->flag & GP_SPOINT_TAG) {
@@ -4566,8 +4679,7 @@ static int gpencil_cutter_lasso_select(bContext *C,
GP_EDITABLE_STROKES_END(gpstroke_iter);
/* dissolve selected points */
- bGPDstroke *gpsn;
- for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
if (gpl->flag & GP_LAYER_LOCKED) {
continue;
}
@@ -4576,8 +4688,7 @@ static int gpencil_cutter_lasso_select(bContext *C,
if (gpf == NULL) {
continue;
}
- for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gpsn) {
- gpsn = gps->next;
+ LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
if (gps->flag & GP_STROKE_SELECT) {
gpencil_cutter_dissolve(gpl, gps);
}
@@ -4662,11 +4773,11 @@ bool ED_object_gpencil_exit(struct Main *bmain, Object *ob)
bGPdata *gpd = (bGPdata *)ob->data;
gpd->flag &= ~(GP_DATA_STROKE_PAINTMODE | GP_DATA_STROKE_EDITMODE | GP_DATA_STROKE_SCULPTMODE |
- GP_DATA_STROKE_WEIGHTMODE);
+ GP_DATA_STROKE_WEIGHTMODE | GP_DATA_STROKE_VERTEXMODE);
ob->restore_mode = ob->mode;
ob->mode &= ~(OB_MODE_PAINT_GPENCIL | OB_MODE_EDIT_GPENCIL | OB_MODE_SCULPT_GPENCIL |
- OB_MODE_WEIGHT_GPENCIL);
+ OB_MODE_WEIGHT_GPENCIL | OB_MODE_VERTEX_GPENCIL);
/* Inform all CoW versions that we changed the mode. */
DEG_id_tag_update_ex(bmain, &ob->id, ID_RECALC_COPY_ON_WRITE);
@@ -4687,7 +4798,7 @@ static bool gp_merge_by_distance_poll(bContext *C)
return false;
}
- bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
return ((gpl != NULL) && (ob->mode == OB_MODE_EDIT_GPENCIL));
}
@@ -4707,7 +4818,7 @@ static int gp_merge_by_distance_exec(bContext *C, wmOperator *op)
/* Go through each editable selected stroke */
GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
if (gps->flag & GP_STROKE_SELECT) {
- BKE_gpencil_merge_distance_stroke(gpf_, gps, threshold, unselected);
+ BKE_gpencil_stroke_merge_distance(gpf_, gps, threshold, unselected);
}
}
GP_EDITABLE_STROKES_END(gpstroke_iter);
diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c
index d76ab85ad31..c928c1e933a 100644
--- a/source/blender/editors/gpencil/gpencil_fill.c
+++ b/source/blender/editors/gpencil/gpencil_fill.c
@@ -134,6 +134,9 @@ typedef struct tGPDfill {
/* scaling factor */
short fill_factor;
+ /* Frame to use. */
+ int active_cfra;
+
/** number of elements currently in cache */
short sbuffer_used;
/** temporary points */
@@ -228,7 +231,6 @@ static void gp_draw_datablock(tGPDfill *tgpf, const float ink[4])
Object *ob = tgpf->ob;
bGPdata *gpd = tgpf->gpd;
- int cfra_eval = (int)DEG_get_ctime(tgpf->depsgraph);
tGPDdraw tgpw;
tgpw.rv3d = tgpf->rv3d;
@@ -245,9 +247,9 @@ static void gp_draw_datablock(tGPDfill *tgpf, const float ink[4])
GPU_blend(true);
- for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
/* calculate parent position */
- ED_gpencil_parent_location(tgpw.depsgraph, ob, gpd, gpl, tgpw.diff_mat);
+ BKE_gpencil_parent_matrix_get(tgpw.depsgraph, ob, gpl, tgpw.diff_mat);
/* do not draw layer if hidden */
if (gpl->flag & GP_LAYER_HIDE) {
@@ -256,25 +258,25 @@ static void gp_draw_datablock(tGPDfill *tgpf, const float ink[4])
/* if active layer and no keyframe, create a new one */
if (gpl == tgpf->gpl) {
- if ((gpl->actframe == NULL) || (gpl->actframe->framenum != cfra_eval)) {
- BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_ADD_NEW);
+ if ((gpl->actframe == NULL) || (gpl->actframe->framenum != tgpf->active_cfra)) {
+ BKE_gpencil_layer_frame_get(gpl, tgpf->active_cfra, GP_GETFRAME_ADD_NEW);
}
}
/* get frame to draw */
- bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_USE_PREV);
+ bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, tgpf->active_cfra, GP_GETFRAME_USE_PREV);
if (gpf == NULL) {
continue;
}
- for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
/* check if stroke can be drawn */
if ((gps->points == NULL) || (gps->totpoints < 2)) {
continue;
}
/* check if the color is visible */
MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
- if ((gp_style == NULL) || (gp_style->flag & GP_STYLE_COLOR_HIDE)) {
+ if ((gp_style == NULL) || (gp_style->flag & GP_MATERIAL_HIDE)) {
continue;
}
@@ -291,7 +293,7 @@ static void gp_draw_datablock(tGPDfill *tgpf, const float ink[4])
tgpw.onion = true;
tgpw.custonion = true;
- bool textured_stroke = (gp_style->stroke_style == GP_STYLE_STROKE_STYLE_TEXTURE);
+ bool textured_stroke = (gp_style->stroke_style == GP_MATERIAL_STROKE_STYLE_TEXTURE);
/* normal strokes */
if (((tgpf->fill_draw_mode == GP_FILL_DMODE_STROKE) ||
@@ -666,6 +668,62 @@ static void gpencil_boundaryfill_area(tGPDfill *tgpf)
BLI_stack_free(stack);
}
+/* Check if there are some pixel not filled with green. If no points, means nothing to fill. */
+static bool gpencil_check_borders(tGPDfill *tgpf)
+{
+ ImBuf *ibuf;
+ void *lock;
+ ibuf = BKE_image_acquire_ibuf(tgpf->ima, NULL, &lock);
+ int idx;
+ int pixel = 0;
+ float color[4];
+ bool found = false;
+
+ /* horizontal lines */
+ for (idx = 0; idx < ibuf->x; idx++) {
+ /* bottom line */
+ get_pixel(ibuf, idx, color);
+ if (color[1] != 1.0f) {
+ found = true;
+ break;
+ }
+ /* top line */
+ pixel = idx + (ibuf->x * (ibuf->y - 1));
+ get_pixel(ibuf, pixel, color);
+ if (color[1] != 1.0f) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ /* vertical lines */
+ for (idx = 0; idx < ibuf->y; idx++) {
+ /* left line */
+ get_pixel(ibuf, ibuf->x * idx, color);
+ if (color[1] != 1.0f) {
+ found = true;
+ break;
+ }
+ /* right line */
+ pixel = ibuf->x * idx + (ibuf->x - 1);
+ get_pixel(ibuf, pixel, color);
+ if (color[1] != 1.0f) {
+ found = true;
+ break;
+ }
+ }
+ }
+
+ /* release ibuf */
+ if (ibuf) {
+ BKE_image_release_ibuf(tgpf->ima, ibuf, lock);
+ }
+
+ tgpf->ima->id.tag |= LIB_TAG_DOIT;
+
+ return found;
+}
+
/* clean external border of image to avoid infinite loops */
static void gpencil_clean_borders(tGPDfill *tgpf)
{
@@ -1006,8 +1064,6 @@ static void gpencil_points_from_stack(tGPDfill *tgpf)
/* create a grease pencil stroke using points in buffer */
static void gpencil_stroke_from_buffer(tGPDfill *tgpf)
{
- const int cfra_eval = (int)DEG_get_ctime(tgpf->depsgraph);
-
ToolSettings *ts = tgpf->scene->toolsettings;
const char *align_flag = &ts->gpencil_v3d_align;
const bool is_depth = (bool)(*align_flag & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE));
@@ -1026,16 +1082,23 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf)
return;
}
- /* get frame or create a new one */
- tgpf->gpf = BKE_gpencil_layer_getframe(tgpf->gpl, cfra_eval, GP_GETFRAME_ADD_NEW);
+ /* Get frame or create a new one. */
+ tgpf->gpf = BKE_gpencil_layer_frame_get(tgpf->gpl, tgpf->active_cfra, GP_GETFRAME_ADD_NEW);
+
+ /* Set frame as selected. */
+ tgpf->gpf->flag |= GP_FRAME_SELECT;
/* create new stroke */
bGPDstroke *gps = MEM_callocN(sizeof(bGPDstroke), "bGPDstroke");
gps->thickness = brush->size;
- gps->gradient_f = brush->gpencil_settings->gradient_f;
- copy_v2_v2(gps->gradient_s, brush->gpencil_settings->gradient_s);
+ gps->fill_opacity_fac = 1.0f;
+ gps->hardeness = brush->gpencil_settings->hardeness;
+ copy_v2_v2(gps->aspect_ratio, brush->gpencil_settings->aspect_ratio);
gps->inittime = 0.0f;
+ /* Apply the vertex color to fill. */
+ ED_gpencil_fill_vertex_color_set(ts, brush, gps);
+
/* the polygon must be closed, so enabled cyclic */
gps->flag |= GP_STROKE_CYCLIC;
gps->flag |= GP_STROKE_3DSPACE;
@@ -1054,11 +1117,6 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf)
gps->totpoints = tgpf->sbuffer_used;
gps->points = MEM_callocN(sizeof(bGPDspoint) * tgpf->sbuffer_used, "gp_stroke_points");
- /* initialize triangle memory to dummy data */
- gps->tot_triangles = 0;
- gps->triangles = NULL;
- gps->flag |= GP_STROKE_RECALC_GEOMETRY;
-
/* add stroke to frame */
if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) || (tgpf->on_back == true)) {
BLI_addhead(&tgpf->gpf->strokes, gps);
@@ -1093,6 +1151,9 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf)
pt->strength = 1.0f;
pt->time = 0.0f;
+ /* Apply the vertex color to point. */
+ ED_gpencil_point_vertex_color_set(ts, brush, pt);
+
if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) {
MDeformWeight *dw = BKE_defvert_ensure_index(dvert, def_nr);
if (dw) {
@@ -1115,7 +1176,7 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf)
float smoothfac = 1.0f;
for (int r = 0; r < 1; r++) {
for (int i = 0; i < gps->totpoints; i++) {
- BKE_gpencil_smooth_stroke(gps, i, smoothfac - reduce);
+ BKE_gpencil_stroke_smooth(gps, i, smoothfac - reduce);
}
reduce += 0.25f; // reduce the factor
}
@@ -1124,7 +1185,8 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf)
if ((tgpf->lock_axis > GP_LOCKAXIS_VIEW) &&
((ts->gpencil_v3d_align & GP_PROJECT_DEPTH_VIEW) == 0)) {
float origin[3];
- ED_gp_get_drawing_reference(tgpf->scene, tgpf->ob, tgpf->gpl, ts->gpencil_v3d_align, origin);
+ ED_gpencil_drawing_reference_get(
+ tgpf->scene, tgpf->ob, tgpf->gpl, ts->gpencil_v3d_align, origin);
ED_gp_project_stroke_to_plane(
tgpf->scene, tgpf->ob, tgpf->rv3d, gps, origin, tgpf->lock_axis - 1);
}
@@ -1132,7 +1194,7 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf)
/* if parented change position relative to parent object */
for (int a = 0; a < tgpf->sbuffer_used; a++) {
pt = &gps->points[a];
- gp_apply_parent_point(tgpf->depsgraph, tgpf->ob, tgpf->gpd, tgpf->gpl, pt);
+ gp_apply_parent_point(tgpf->depsgraph, tgpf->ob, tgpf->gpl, pt);
}
/* if camera view, reproject flat to view to avoid perspective effect */
@@ -1142,14 +1204,17 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf)
/* simplify stroke */
for (int b = 0; b < tgpf->fill_simplylvl; b++) {
- BKE_gpencil_simplify_fixed(gps);
+ BKE_gpencil_stroke_simplify_fixed(gps);
}
+
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gps);
}
/* ----------------------- */
/* Drawing */
/* Helper: Draw status message while the user is running the operator */
-static void gpencil_fill_status_indicators(bContext *C, tGPDfill *UNUSED(tgpf))
+static void gpencil_fill_status_indicators(bContext *C)
{
const char *status_str = TIP_("Fill: ESC/RMB cancel, LMB Fill, Shift Draw on Back");
ED_workspace_status_text(C, status_str);
@@ -1228,10 +1293,11 @@ static tGPDfill *gp_session_init_fill(bContext *C, wmOperator *UNUSED(op))
/* set GP datablock */
tgpf->gpd = gpd;
- tgpf->gpl = BKE_gpencil_layer_getactive(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->lock_axis = ts->gp_sculpt.lock_axis;
tgpf->oldkey = -1;
@@ -1339,7 +1405,7 @@ static int gpencil_fill_init(bContext *C, wmOperator *op)
tGPDfill *tgpf;
/* cannot paint in locked layer */
bGPdata *gpd = CTX_data_gpencil_data(C);
- bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
if ((gpl) && (gpl->flag & GP_LAYER_LOCKED)) {
return 0;
}
@@ -1401,7 +1467,7 @@ static int gpencil_fill_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE
WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_PAINT_BRUSH);
- gpencil_fill_status_indicators(C, tgpf);
+ gpencil_fill_status_indicators(C);
DEG_id_tag_update(&tgpf->gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
@@ -1416,6 +1482,7 @@ static int gpencil_fill_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE
static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
tGPDfill *tgpf = op->customdata;
+ Scene *scene = tgpf->scene;
int estate = OPERATOR_PASS_THROUGH; /* default exit state - pass through */
@@ -1426,7 +1493,7 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event)
break;
case LEFTMOUSE:
tgpf->on_back = RNA_boolean_get(op->ptr, "on_back");
- /* first time the event is not enabled to show help lines */
+ /* first time the event is not enabled to show help lines. */
if ((tgpf->oldkey != -1) || ((tgpf->flag & GP_BRUSH_FILL_SHOW_HELPLINES) == 0)) {
ARegion *region = BKE_area_find_region_xy(
CTX_wm_area(C), RGN_TYPE_ANY, event->x, event->y);
@@ -1437,42 +1504,55 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event)
in_bounds = BLI_rcti_isect_pt(&region->winrct, event->x, event->y);
if ((in_bounds) && (region->regiontype == RGN_TYPE_WINDOW)) {
- /* TODO GPXX: Verify the mouse click is right for any window size */
tgpf->center[0] = event->mval[0];
tgpf->center[1] = event->mval[1];
+ /* Set active frame as current for filling. */
+ tgpf->active_cfra = CFRA;
+
/* render screen to temp image */
if (gp_render_offscreen(tgpf)) {
/* apply boundary fill */
gpencil_boundaryfill_area(tgpf);
- /* clean borders to avoid infinite loops */
- gpencil_clean_borders(tgpf);
+ /* Check if detected some border to fill. */
+ if (gpencil_check_borders(tgpf)) {
- /* analyze outline */
- gpencil_get_outline_points(tgpf);
+ /* clean borders to avoid infinite loops */
+ gpencil_clean_borders(tgpf);
- /* create array of points from stack */
- gpencil_points_from_stack(tgpf);
+ /* analyze outline */
+ gpencil_get_outline_points(tgpf);
- /* create z-depth array for reproject */
- gpencil_get_depth_array(tgpf);
+ /* create array of points from stack */
+ gpencil_points_from_stack(tgpf);
- /* create stroke and reproject */
- gpencil_stroke_from_buffer(tgpf);
- }
+ /* create z-depth array for reproject */
+ gpencil_get_depth_array(tgpf);
- /* restore size */
- tgpf->region->winx = (short)tgpf->bwinx;
- tgpf->region->winy = (short)tgpf->bwiny;
- tgpf->region->winrct = tgpf->brect;
+ /* create stroke and reproject */
+ gpencil_stroke_from_buffer(tgpf);
+ }
+ else {
+ BKE_report(op->reports, RPT_ERROR, "Fill canceled. No edges detected");
+ }
+ }
/* free temp stack data */
if (tgpf->stack) {
BLI_stack_free(tgpf->stack);
}
+ /* Free memory. */
+ MEM_SAFE_FREE(tgpf->sbuffer);
+ MEM_SAFE_FREE(tgpf->depth_arr);
+
+ /* restore size */
+ tgpf->region->winx = (short)tgpf->bwinx;
+ tgpf->region->winy = (short)tgpf->bwiny;
+ tgpf->region->winrct = tgpf->brect;
+
/* push undo data */
gpencil_undo_push(tgpf->gpd);
diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h
index 077b5b88118..2d36e426835 100644
--- a/source/blender/editors/gpencil/gpencil_intern.h
+++ b/source/blender/editors/gpencil/gpencil_intern.h
@@ -111,6 +111,8 @@ typedef struct tGPDinterpolate_layer {
} tGPDinterpolate_layer;
typedef struct tGPDinterpolate {
+ /** Current depsgraph from context */
+ struct Depsgraph *depsgraph;
/** current scene from context */
struct Scene *scene;
/** area where painting originated */
@@ -138,10 +140,6 @@ typedef struct tGPDinterpolate {
int flag;
NumInput num; /* numeric input */
- /** handle for drawing strokes while operator is running 3d stuff */
- void *draw_handle_3d;
- /** handle for drawing strokes while operator is running screen stuff */
- void *draw_handle_screen;
} tGPDinterpolate;
/* Temporary primitive operation data */
@@ -155,6 +153,8 @@ typedef struct tGPDprimitive {
struct Scene *scene;
/** current active gp object */
struct Object *ob;
+ /** current evaluated gp object */
+ struct Object *ob_eval;
/** area where painting originated */
struct ScrArea *sa;
/** region where painting originated */
@@ -166,7 +166,7 @@ typedef struct tGPDprimitive {
/** current GP datablock */
struct bGPdata *gpd;
/** current material */
- struct Material *mat;
+ struct Material *material;
/** current brush */
struct Brush *brush;
@@ -233,10 +233,6 @@ typedef struct tGPDprimitive {
} tGPDprimitive;
/* Modal Operator Drawing Callbacks ------------------------ */
-
-void ED_gp_draw_interpolation(const struct bContext *C,
- struct tGPDinterpolate *tgpi,
- const int type);
void ED_gp_draw_fill(struct tGPDdraw *tgpw);
/* ***************************************************** */
@@ -284,7 +280,6 @@ void gp_point_to_parent_space(const bGPDspoint *pt, const float diff_mat[4][4],
*/
void gp_apply_parent(struct Depsgraph *depsgraph,
struct Object *obact,
- bGPdata *gpd,
bGPDlayer *gpl,
bGPDstroke *gps);
/**
@@ -292,7 +287,6 @@ void gp_apply_parent(struct Depsgraph *depsgraph,
*/
void gp_apply_parent_point(struct Depsgraph *depsgraph,
struct Object *obact,
- bGPdata *gpd,
bGPDlayer *gpl,
bGPDspoint *pt);
@@ -342,7 +336,6 @@ void gp_stroke_delete_tagged_points(bGPDframe *gpf,
int gp_delete_selected_point_wrap(bContext *C);
void gp_subdivide_stroke(bGPDstroke *gps, const int subdivide);
-void gp_randomize_stroke(bGPDstroke *gps, Brush *brush, struct RNG *rng);
/* Layers Enums -------------------------------------- */
@@ -367,6 +360,14 @@ void GPENCIL_OT_annotate(struct wmOperatorType *ot);
void GPENCIL_OT_draw(struct wmOperatorType *ot);
void GPENCIL_OT_fill(struct wmOperatorType *ot);
+/* Vertex Paint. */
+void GPENCIL_OT_vertex_paint(struct wmOperatorType *ot);
+void GPENCIL_OT_vertex_color_brightness_contrast(struct wmOperatorType *ot);
+void GPENCIL_OT_vertex_color_hsv(struct wmOperatorType *ot);
+void GPENCIL_OT_vertex_color_invert(struct wmOperatorType *ot);
+void GPENCIL_OT_vertex_color_levels(struct wmOperatorType *ot);
+void GPENCIL_OT_vertex_color_set(struct wmOperatorType *ot);
+
/* Guides ----------------------- */
void GPENCIL_OT_guide_rotate(struct wmOperatorType *ot);
@@ -390,6 +391,7 @@ void GPENCIL_OT_selectmode_toggle(struct wmOperatorType *ot);
void GPENCIL_OT_paintmode_toggle(struct wmOperatorType *ot);
void GPENCIL_OT_sculptmode_toggle(struct wmOperatorType *ot);
void GPENCIL_OT_weightmode_toggle(struct wmOperatorType *ot);
+void GPENCIL_OT_vertexmode_toggle(struct wmOperatorType *ot);
void GPENCIL_OT_selection_opacity_toggle(struct wmOperatorType *ot);
void GPENCIL_OT_select(struct wmOperatorType *ot);
@@ -405,6 +407,7 @@ void GPENCIL_OT_select_less(struct wmOperatorType *ot);
void GPENCIL_OT_select_first(struct wmOperatorType *ot);
void GPENCIL_OT_select_last(struct wmOperatorType *ot);
void GPENCIL_OT_select_alternate(struct wmOperatorType *ot);
+void GPENCIL_OT_select_color(struct wmOperatorType *ot);
void GPENCIL_OT_duplicate(struct wmOperatorType *ot);
void GPENCIL_OT_delete(struct wmOperatorType *ot);
@@ -415,16 +418,19 @@ void GPENCIL_OT_extrude(struct wmOperatorType *ot);
void GPENCIL_OT_move_to_layer(struct wmOperatorType *ot);
void GPENCIL_OT_layer_change(struct wmOperatorType *ot);
+void GPENCIL_OT_layer_active(struct wmOperatorType *ot);
void GPENCIL_OT_snap_to_grid(struct wmOperatorType *ot);
void GPENCIL_OT_snap_to_cursor(struct wmOperatorType *ot);
void GPENCIL_OT_snap_cursor_to_selected(struct wmOperatorType *ot);
void GPENCIL_OT_reproject(struct wmOperatorType *ot);
+void GPENCIL_OT_recalc_geometry(struct wmOperatorType *ot);
/* stroke sculpting -- */
void GPENCIL_OT_sculpt_paint(struct wmOperatorType *ot);
+void GPENCIL_OT_weight_paint(struct wmOperatorType *ot);
/* buttons editing --- */
@@ -440,6 +446,9 @@ void GPENCIL_OT_layer_annotation_move(struct wmOperatorType *ot);
void GPENCIL_OT_layer_duplicate(struct wmOperatorType *ot);
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_hide(struct wmOperatorType *ot);
void GPENCIL_OT_reveal(struct wmOperatorType *ot);
@@ -459,6 +468,7 @@ void GPENCIL_OT_frame_clean_fill(struct wmOperatorType *ot);
void GPENCIL_OT_frame_clean_loose(struct wmOperatorType *ot);
void GPENCIL_OT_convert(struct wmOperatorType *ot);
+void GPENCIL_OT_image_to_grease_pencil(struct wmOperatorType *ot);
enum {
GP_STROKE_JOIN = -1,
@@ -498,8 +508,16 @@ void GPENCIL_OT_stroke_merge(struct wmOperatorType *ot);
void GPENCIL_OT_stroke_cutter(struct wmOperatorType *ot);
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_brush_presets_create(struct wmOperatorType *ot);
+void GPENCIL_OT_material_to_vertex_color(struct wmOperatorType *ot);
+void GPENCIL_OT_extract_palette_vertex(struct wmOperatorType *ot);
+
+void GPENCIL_OT_transform_fill(struct wmOperatorType *ot);
+void GPENCIL_OT_reset_transform_fill(struct wmOperatorType *ot);
+
+void GPENCIL_OT_brush_reset(struct wmOperatorType *ot);
+void GPENCIL_OT_brush_reset_all(struct wmOperatorType *ot);
/* undo stack ---------- */
@@ -617,7 +635,7 @@ struct GP_EditableStrokes_Iter {
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_)) { \
- ED_gpencil_parent_location(depsgraph_, obact_, gpd_, gpl, gpstroke_iter.diff_mat); \
+ BKE_gpencil_parent_matrix_get(depsgraph_, obact_, gpl, gpstroke_iter.diff_mat); \
invert_m4_m4(gpstroke_iter.inverse_diff_mat, gpstroke_iter.diff_mat); \
/* loop over strokes */ \
bGPDstroke *gpsn_; \
@@ -643,10 +661,6 @@ struct GP_EditableStrokes_Iter {
} \
(void)0
-#define GPENCIL_ANY_SCULPT_MASK(flag) \
- ((flag & (GP_SCULPT_MASK_SELECTMODE_POINT | GP_SCULPT_MASK_SELECTMODE_STROKE | \
- GP_SCULPT_MASK_SELECTMODE_SEGMENT)))
-
/**
* Iterate over all editable strokes using evaluated data in the current context,
* stopping on each usable layer + stroke pair (i.e. gpl and gps)
@@ -662,26 +676,21 @@ struct GP_EditableStrokes_Iter {
struct GP_EditableStrokes_Iter gpstroke_iter = {{{0}}}; \
Depsgraph *depsgraph_ = CTX_data_ensure_evaluated_depsgraph(C); \
Object *obact_ = CTX_data_active_object(C); \
- Object *obeval_ = DEG_get_evaluated_object(depsgraph_, obact_); \
- bGPdata *gpd_ = CTX_data_gpencil_data(C); \
+ Object *ob_eval_ = (Object *)DEG_get_evaluated_id(depsgraph_, &obact_->id); \
+ bGPdata *gpd_ = (bGPdata *)ob_eval_->data; \
const bool is_multiedit_ = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_); \
- int idx_eval = 0; \
- for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { \
- if (gpencil_layer_is_editable(gpl)) { \
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd_->layers) { \
+ if (BKE_gpencil_layer_is_editable(gpl)) { \
bGPDframe *init_gpf_ = gpl->actframe; \
if (is_multiedit_) { \
init_gpf_ = gpl->frames.first; \
} \
for (bGPDframe *gpf_ = init_gpf_; gpf_; gpf_ = gpf_->next) { \
if ((gpf_ == gpl->actframe) || ((gpf_->flag & GP_FRAME_SELECT) && is_multiedit_)) { \
- ED_gpencil_parent_location(depsgraph_, obact_, gpd_, gpl, gpstroke_iter.diff_mat); \
+ BKE_gpencil_parent_matrix_get(depsgraph_, obact_, gpl, gpstroke_iter.diff_mat); \
invert_m4_m4(gpstroke_iter.inverse_diff_mat, gpstroke_iter.diff_mat); \
- /* get evaluated frame with modifiers applied */ \
- bGPDframe *gpf_eval_ = (!is_multiedit_) ? \
- &obeval_->runtime.gpencil_evaluated_frames[idx_eval] : \
- gpf_; \
/* loop over strokes */ \
- for (bGPDstroke *gps = gpf_eval_->strokes.first; gps; gps = gps->next) { \
+ 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; \
@@ -698,7 +707,6 @@ struct GP_EditableStrokes_Iter {
} \
} \
} \
- idx_eval++; \
} \
} \
(void)0
diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c
index 940fb85f91a..bd00a492035 100644
--- a/source/blender/editors/gpencil/gpencil_interpolate.c
+++ b/source/blender/editors/gpencil/gpencil_interpolate.c
@@ -115,31 +115,38 @@ static void gp_interpolate_update_points(const bGPDstroke *gps_from,
pt->pressure = interpf(prev->pressure, next->pressure, 1.0f - factor);
pt->strength = interpf(prev->strength, next->strength, 1.0f - factor);
CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f);
-
- /* GPXX interpolate dverts */
-#if 0
- MDeformVert *dvert = &new_stroke->dvert[i];
- dvert->totweight = 0;
- dvert->dw = NULL;
-#endif
}
}
/* ****************** Interpolate Interactive *********************** */
+/* Helper: free all temp strokes for display. */
+static void gp_interpolate_free_temp_strokes(bGPDframe *gpf)
+{
+ if (gpf == NULL) {
+ return;
+ }
+ LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
+ if (gps->flag & GP_STROKE_TAG) {
+ BLI_remlink(&gpf->strokes, gps);
+ BKE_gpencil_free_stroke(gps);
+ }
+ }
+}
/* Helper: Update all strokes interpolated */
static void gp_interpolate_update_strokes(bContext *C, tGPDinterpolate *tgpi)
{
bGPdata *gpd = tgpi->gpd;
- tGPDinterpolate_layer *tgpil;
const float shift = tgpi->shift;
- for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) {
- bGPDstroke *new_stroke;
+ LISTBASE_FOREACH (tGPDinterpolate_layer *, tgpil, &tgpi->ilayers) {
const float factor = tgpil->factor + shift;
- for (new_stroke = tgpil->interFrame->strokes.first; new_stroke;
- new_stroke = new_stroke->next) {
+ bGPDframe *gpf = tgpil->gpl->actframe;
+ /* Free temp strokes. */
+ gp_interpolate_free_temp_strokes(gpf);
+
+ LISTBASE_FOREACH (bGPDstroke *, new_stroke, &tgpil->interFrame->strokes) {
bGPDstroke *gps_from, *gps_to;
int stroke_idx;
@@ -156,6 +163,13 @@ static void gp_interpolate_update_strokes(bContext *C, tGPDinterpolate *tgpi)
/* update points position */
if ((gps_from) && (gps_to)) {
gp_interpolate_update_points(gps_from, gps_to, new_stroke, factor);
+
+ /* Add temp strokes. */
+ if (gpf) {
+ bGPDstroke *gps_eval = BKE_gpencil_stroke_duplicate(new_stroke, true);
+ gps_eval->flag |= GP_STROKE_TAG;
+ BLI_addtail(&gpf->strokes, gps_eval);
+ }
}
}
}
@@ -172,19 +186,18 @@ static bool gp_interpolate_check_todo(bContext *C, bGPdata *gpd)
eGP_Interpolate_SettingsFlag flag = ts->gp_interpolate.flag;
/* get layers */
- for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
/* all layers or only active */
if (!(flag & GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS) && !(gpl->flag & GP_LAYER_ACTIVE)) {
continue;
}
/* only editable and visible layers are considered */
- if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) {
+ if (!BKE_gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) {
continue;
}
/* read strokes */
- for (bGPDstroke *gps_from = gpl->actframe->strokes.first; gps_from;
- gps_from = gps_from->next) {
+ LISTBASE_FOREACH (bGPDstroke *, gps_from, &gpl->actframe->strokes) {
bGPDstroke *gps_to;
int fFrame;
@@ -232,7 +245,7 @@ static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
tgpi->high_limit = 2.0f - tgpi->init_factor;
/* set layers */
- for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
tGPDinterpolate_layer *tgpil;
/* all layers or only active */
@@ -240,7 +253,7 @@ static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
continue;
}
/* only editable and visible layers are considered */
- if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) {
+ if (!BKE_gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) {
continue;
}
@@ -262,8 +275,7 @@ static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
(tgpil->nextFrame->framenum - tgpil->prevFrame->framenum + 1);
/* create new strokes data with interpolated points reading original stroke */
- for (bGPDstroke *gps_from = tgpil->prevFrame->strokes.first; gps_from;
- gps_from = gps_from->next) {
+ LISTBASE_FOREACH (bGPDstroke *, gps_from, &tgpil->prevFrame->strokes) {
bGPDstroke *gps_to;
int fFrame;
@@ -293,7 +305,7 @@ static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
}
/* create new stroke */
- new_stroke = BKE_gpencil_stroke_duplicate(gps_from);
+ new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true);
if (valid) {
/* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */
@@ -305,8 +317,6 @@ static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
sizeof(*new_stroke->dvert) * gps_to->totpoints);
}
new_stroke->totpoints = gps_to->totpoints;
- new_stroke->tot_triangles = 0;
- new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY;
}
/* update points position */
gp_interpolate_update_points(gps_from, gps_to, new_stroke, tgpil->factor);
@@ -318,12 +328,10 @@ static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
if (new_stroke->dvert != NULL) {
new_stroke->dvert = MEM_recallocN(new_stroke->dvert, sizeof(*new_stroke->dvert));
}
- new_stroke->tot_triangles = 0;
- new_stroke->triangles = MEM_recallocN(new_stroke->triangles,
- sizeof(*new_stroke->triangles));
- new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY;
}
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(new_stroke);
/* add to strokes */
BLI_addtail(&tgpil->interFrame->strokes, new_stroke);
}
@@ -331,25 +339,6 @@ static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
}
/* ----------------------- */
-/* Drawing Callbacks */
-
-/* Drawing callback for modal operator in screen mode */
-static void gpencil_interpolate_draw_screen(const struct bContext *C,
- ARegion *UNUSED(region),
- void *arg)
-{
- tGPDinterpolate *tgpi = (tGPDinterpolate *)arg;
- ED_gp_draw_interpolation(C, tgpi, REGION_DRAW_POST_PIXEL);
-}
-
-/* Drawing callback for modal operator in 3d mode */
-static void gpencil_interpolate_draw_3d(const bContext *C, ARegion *UNUSED(region), void *arg)
-{
- tGPDinterpolate *tgpi = (tGPDinterpolate *)arg;
- ED_gp_draw_interpolation(C, tgpi, REGION_DRAW_POST_VIEW);
-}
-
-/* ----------------------- */
/* Helper: calculate shift based on position of mouse (we only use x-axis for now.
* since this is more convenient for users to do), and store new shift value
@@ -415,25 +404,23 @@ static void gpencil_interpolate_update(bContext *C, wmOperator *op, tGPDinterpol
static void gpencil_interpolate_exit(bContext *C, wmOperator *op)
{
tGPDinterpolate *tgpi = op->customdata;
- tGPDinterpolate_layer *tgpil;
bGPdata *gpd = tgpi->gpd;
/* don't assume that operator data exists at all */
if (tgpi) {
- /* remove drawing handler */
- if (tgpi->draw_handle_screen) {
- ED_region_draw_cb_exit(tgpi->region->type, tgpi->draw_handle_screen);
- }
- if (tgpi->draw_handle_3d) {
- ED_region_draw_cb_exit(tgpi->region->type, tgpi->draw_handle_3d);
- }
-
/* clear status message area */
ED_area_status_text(tgpi->sa, NULL);
ED_workspace_status_text(C, NULL);
+ /* Clear any temp stroke. */
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ gp_interpolate_free_temp_strokes(gpf);
+ }
+ }
+
/* finally, free memory used by temp data */
- for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) {
+ LISTBASE_FOREACH (tGPDinterpolate_layer *, tgpil, &tgpi->ilayers) {
BKE_gpencil_free_strokes(tgpil->interFrame);
MEM_freeN(tgpil->interFrame);
}
@@ -455,6 +442,7 @@ static bool gp_interpolate_set_init_values(bContext *C, wmOperator *op, tGPDinte
bGPdata *gpd = CTX_data_gpencil_data(C);
/* set current scene and window */
+ tgpi->depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
tgpi->scene = CTX_data_scene(C);
tgpi->sa = CTX_wm_area(C);
tgpi->region = CTX_wm_region(C);
@@ -550,15 +538,6 @@ static int gpencil_interpolate_invoke(bContext *C, wmOperator *op, const wmEvent
tgpi = op->customdata;
}
- /* Enable custom drawing handlers
- * It needs 2 handlers because strokes can in 3d space and screen space
- * and each handler use different coord system
- */
- tgpi->draw_handle_screen = ED_region_draw_cb_activate(
- tgpi->region->type, gpencil_interpolate_draw_screen, tgpi, REGION_DRAW_POST_PIXEL);
- tgpi->draw_handle_3d = ED_region_draw_cb_activate(
- tgpi->region->type, gpencil_interpolate_draw_3d, tgpi, REGION_DRAW_POST_VIEW);
-
/* set cursor to indicate modal */
WM_cursor_modal_set(win, WM_CURSOR_EW_SCROLL);
@@ -579,8 +558,7 @@ static int gpencil_interpolate_modal(bContext *C, wmOperator *op, const wmEvent
tGPDinterpolate *tgpi = op->customdata;
wmWindow *win = CTX_wm_window(C);
bGPDframe *gpf_dst;
- bGPDstroke *gps_src, *gps_dst;
- tGPDinterpolate_layer *tgpil;
+ bGPDstroke *gps_dst;
const bool has_numinput = hasNumInput(&tgpi->num);
switch (event->type) {
@@ -593,26 +571,22 @@ static int gpencil_interpolate_modal(bContext *C, wmOperator *op, const wmEvent
WM_cursor_modal_restore(win);
/* insert keyframes as required... */
- for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) {
- gpf_dst = BKE_gpencil_layer_getframe(tgpil->gpl, tgpi->cframe, GP_GETFRAME_ADD_NEW);
+ LISTBASE_FOREACH (tGPDinterpolate_layer *, tgpil, &tgpi->ilayers) {
+ gpf_dst = BKE_gpencil_layer_frame_get(tgpil->gpl, tgpi->cframe, GP_GETFRAME_ADD_NEW);
gpf_dst->key_type = BEZT_KEYTYPE_BREAKDOWN;
/* copy strokes */
BLI_listbase_clear(&gpf_dst->strokes);
- for (gps_src = tgpil->interFrame->strokes.first; gps_src; gps_src = gps_src->next) {
+ LISTBASE_FOREACH (bGPDstroke *, gps_src, &tgpil->interFrame->strokes) {
if (gps_src->totpoints == 0) {
continue;
}
/* make copy of source stroke, then adjust pointer to points too */
- gps_dst = MEM_dupallocN(gps_src);
- gps_dst->points = MEM_dupallocN(gps_src->points);
- if (gps_src->dvert != NULL) {
- gps_dst->dvert = MEM_dupallocN(gps_src->dvert);
- BKE_gpencil_stroke_weights_duplicate(gps_src, gps_dst);
- }
- gps_dst->triangles = MEM_dupallocN(gps_src->triangles);
- gps_dst->flag |= GP_STROKE_RECALC_GEOMETRY;
+ gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true);
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gps_dst);
+
BLI_addtail(&gpf_dst->strokes, gps_dst);
}
}
@@ -973,7 +947,7 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
}
/* loop all layer to check if need interpolation */
- for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
bGPDframe *prevFrame, *nextFrame;
bGPDstroke *gps_from, *gps_to;
int cframe, fFrame;
@@ -983,7 +957,7 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
continue;
}
/* only editable and visible layers are considered */
- if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) {
+ if (!BKE_gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) {
continue;
}
@@ -1017,7 +991,6 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
/* create new strokes data with interpolated points reading original stroke */
for (gps_from = prevFrame->strokes.first; gps_from; gps_from = gps_from->next) {
- bGPDstroke *new_stroke = NULL;
/* only selected */
if ((flag & GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED) &&
@@ -1042,12 +1015,12 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
/* create a new frame if needed */
if (interFrame == NULL) {
- interFrame = BKE_gpencil_layer_getframe(gpl, cframe, GP_GETFRAME_ADD_NEW);
+ interFrame = BKE_gpencil_layer_frame_get(gpl, cframe, GP_GETFRAME_ADD_NEW);
interFrame->key_type = BEZT_KEYTYPE_BREAKDOWN;
}
/* create new stroke */
- new_stroke = BKE_gpencil_stroke_duplicate(gps_from);
+ bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true);
/* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */
if (gps_from->totpoints > gps_to->totpoints) {
@@ -1064,14 +1037,16 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
new_stroke->dvert = MEM_recallocN(new_stroke->dvert,
sizeof(*new_stroke->dvert) * gps_to->totpoints);
}
+
new_stroke->totpoints = gps_to->totpoints;
- new_stroke->tot_triangles = 0;
- new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY;
}
/* update points position */
gp_interpolate_update_points(gps_from, gps_to, new_stroke, factor);
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(new_stroke);
+
/* add to strokes */
BLI_addtail(&interFrame->strokes, new_stroke);
}
diff --git a/source/blender/editors/gpencil/gpencil_merge.c b/source/blender/editors/gpencil/gpencil_merge.c
index 91339709162..9a7ad8d7220 100644
--- a/source/blender/editors/gpencil/gpencil_merge.c
+++ b/source/blender/editors/gpencil/gpencil_merge.c
@@ -37,6 +37,7 @@
#include "BKE_gpencil.h"
#include "BKE_main.h"
#include "BKE_material.h"
+#include "BKE_report.h"
#include "WM_api.h"
#include "WM_types.h"
@@ -60,6 +61,7 @@ typedef struct tGPencilPointCache {
float x, y, z;
float pressure;
float strength;
+ float vert_color[4];
} tGPencilPointCache;
/* helper function to sort points */
@@ -93,6 +95,7 @@ static void gpencil_insert_points_to_stroke(bGPDstroke *gps,
pt_dst->uv_fac = 1.0f;
pt_dst->uv_rot = 0;
pt_dst->flag |= GP_SPOINT_SELECT;
+ copy_v4_v4(pt_dst->vert_color, point_elem->vert_color);
}
}
@@ -113,7 +116,7 @@ static bGPDstroke *gpencil_prepare_stroke(bContext *C, wmOperator *op, int totpo
/* if not exist, create a new one */
if ((paint->brush == NULL) || (paint->brush->gpencil_settings == NULL)) {
/* create new brushes */
- BKE_brush_gpencil_presets(bmain, ts);
+ BKE_brush_gpencil_paint_presets(bmain, ts);
}
Brush *brush = paint->brush;
@@ -125,25 +128,21 @@ static bGPDstroke *gpencil_prepare_stroke(bContext *C, wmOperator *op, int totpo
else {
add_frame_mode = GP_GETFRAME_ADD_NEW;
}
- bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, add_frame_mode);
+ bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, add_frame_mode);
/* stroke */
bGPDstroke *gps = MEM_callocN(sizeof(bGPDstroke), "gp_stroke");
gps->totpoints = totpoints;
gps->inittime = 0.0f;
gps->thickness = brush->size;
- gps->gradient_f = brush->gpencil_settings->gradient_f;
- copy_v2_v2(gps->gradient_s, brush->gpencil_settings->gradient_s);
+ gps->hardeness = brush->gpencil_settings->hardeness;
+ copy_v2_v2(gps->aspect_ratio, brush->gpencil_settings->aspect_ratio);
gps->flag |= GP_STROKE_SELECT;
gps->flag |= GP_STROKE_3DSPACE;
gps->mat_nr = ob->actcol - 1;
/* allocate memory for points */
gps->points = MEM_callocN(sizeof(bGPDspoint) * totpoints, "gp_stroke_points");
- /* initialize triangle memory to dummy data */
- gps->tot_triangles = 0;
- gps->triangles = NULL;
- gps->flag |= GP_STROKE_RECALC_GEOMETRY;
if (cyclic) {
gps->flag |= GP_STROKE_CYCLIC;
@@ -181,17 +180,14 @@ static void gpencil_get_elements_len(bContext *C, int *totstrokes, int *totpoint
static void gpencil_dissolve_points(bContext *C)
{
- bGPDstroke *gps, *gpsn;
-
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
bGPDframe *gpf = gpl->actframe;
if (gpf == NULL) {
continue;
}
- for (gps = gpf->strokes.first; gps; gps = gpsn) {
- gpsn = gps->next;
- gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_TAG, false, 0);
+ LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
+ gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_TAG, false, 0);
}
}
CTX_DATA_END;
@@ -224,7 +220,7 @@ static void gpencil_calc_points_factor(bContext *C,
if (gpf == NULL) {
continue;
}
- for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
if (gps->flag & GP_STROKE_SELECT) {
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
if (clear_stroke) {
@@ -239,6 +235,7 @@ static void gpencil_calc_points_factor(bContext *C,
copy_v3_v3(&pt2->x, &pt->x);
pt2->pressure = pt->pressure;
pt2->strength = pt->strength;
+ copy_v4_v4(pt2->vert_color, pt->vert_color);
pt->flag &= ~GP_SPOINT_SELECT;
if (clear_point) {
pt->flag |= GP_SPOINT_TAG;
@@ -288,6 +285,7 @@ static void gpencil_calc_points_factor(bContext *C,
copy_v3_v3(&sort_pt->x, &pt2->x);
sort_pt->pressure = pt2->pressure;
sort_pt->strength = pt2->strength;
+ copy_v4_v4(sort_pt->vert_color, pt2->vert_color);
sort_pt->gps = gps_array[i];
@@ -336,6 +334,7 @@ static int gpencil_insert_to_array(tGPencilPointCache *src_array,
dst_elem->pressure = src_elem->pressure;
dst_elem->strength = src_elem->strength;
dst_elem->factor = src_elem->factor;
+ copy_v4_v4(dst_elem->vert_color, src_elem->vert_color);
}
return last;
@@ -458,7 +457,7 @@ static bool gp_strokes_merge_poll(bContext *C)
/* check hidden or locked materials */
MaterialGPencilStyle *gp_style = ma->gp_style;
- if ((gp_style->flag & GP_STYLE_COLOR_HIDE) || (gp_style->flag & GP_STYLE_COLOR_LOCKED)) {
+ if ((gp_style->flag & GP_MATERIAL_HIDE) || (gp_style->flag & GP_MATERIAL_LOCKED)) {
return false;
}
@@ -569,3 +568,100 @@ void GPENCIL_OT_stroke_merge(wmOperatorType *ot)
RNA_def_boolean(ot->srna, "clear_point", 0, "Dissolve Points", "Dissolve old selected points");
RNA_def_boolean(ot->srna, "clear_stroke", 0, "Delete Strokes", "Delete old selected strokes");
}
+
+/* Merge similar materials. */
+static bool gp_stroke_merge_material_poll(bContext *C)
+{
+ /* only supported with grease pencil objects */
+ Object *ob = CTX_data_active_object(C);
+ if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
+ return false;
+ }
+
+ return true;
+}
+
+static int gp_stroke_merge_material_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = (bGPdata *)ob->data;
+ const float hue_threshold = RNA_float_get(op->ptr, "hue_threshold");
+ const float sat_threshold = RNA_float_get(op->ptr, "sat_threshold");
+ const float val_threshold = RNA_float_get(op->ptr, "val_threshold");
+
+ /* Review materials. */
+ GHash *mat_table = BLI_ghash_int_new(__func__);
+
+ short *totcol = BKE_object_material_len_p(ob);
+ if (totcol == 0) {
+ return OPERATOR_CANCELLED;
+ }
+
+ bool changed = BKE_gpencil_merge_materials_table_get(
+ ob, hue_threshold, sat_threshold, val_threshold, mat_table);
+
+ int removed = BLI_ghash_len(mat_table);
+
+ /* Update stroke material index. */
+ if (changed) {
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
+ }
+ if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) {
+ continue;
+ }
+
+ if (BLI_ghash_haskey(mat_table, POINTER_FROM_INT(gps->mat_nr))) {
+ int *idx = BLI_ghash_lookup(mat_table, POINTER_FROM_INT(gps->mat_nr));
+ gps->mat_nr = POINTER_AS_INT(idx);
+ }
+ }
+ }
+ }
+ CTX_DATA_END;
+ }
+
+ /* Free hash memory. */
+ BLI_ghash_free(mat_table, NULL, NULL);
+
+ /* notifiers */
+ if (changed) {
+ BKE_reportf(op->reports, RPT_INFO, "Merged %d materiales of %d", removed, *totcol);
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ }
+ else {
+ BKE_report(op->reports, RPT_INFO, "Nothing to merge");
+ }
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_stroke_merge_material(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Merge Grease Pencil Materials";
+ ot->idname = "GPENCIL_OT_stroke_merge_material";
+ ot->description = "Replace materials in strokes merging similar";
+
+ /* api callbacks */
+ ot->exec = gp_stroke_merge_material_exec;
+ ot->poll = gp_stroke_merge_material_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ prop = RNA_def_float(
+ ot->srna, "hue_threshold", 0.001f, 0.0f, 1.0f, "Hue Threshold", "", 0.0f, 1.0f);
+ prop = RNA_def_float(
+ ot->srna, "sat_threshold", 0.001f, 0.0f, 1.0f, "Saturation Threshold", "", 0.0f, 1.0f);
+ prop = RNA_def_float(
+ ot->srna, "val_threshold", 0.001f, 0.0f, 1.0f, "Value Threshold", "", 0.0f, 1.0f);
+ /* avoid re-using last var */
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+}
diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c
index 7a541cd8f03..275075c38db 100644
--- a/source/blender/editors/gpencil/gpencil_ops.c
+++ b/source/blender/editors/gpencil/gpencil_ops.c
@@ -95,6 +95,60 @@ static bool gp_stroke_paintmode_poll_with_tool(bContext *C, const char gpencil_t
WM_toolsystem_active_tool_is_brush(C) && (brush->gpencil_tool == gpencil_tool));
}
+static bool gp_stroke_vertexmode_poll_with_tool(bContext *C, const char gpencil_vertex_tool)
+{
+ bGPdata *gpd = CTX_data_gpencil_data(C);
+ if (!gpd) {
+ return false;
+ }
+
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ if (!ts || !ts->gp_vertexpaint) {
+ return false;
+ }
+
+ Brush *brush = BKE_paint_brush(&ts->gp_vertexpaint->paint);
+ return ((gpd->flag & GP_DATA_STROKE_VERTEXMODE) && (brush && brush->gpencil_settings) &&
+ WM_toolsystem_active_tool_is_brush(C) &&
+ (brush->gpencil_vertex_tool == gpencil_vertex_tool));
+}
+
+static bool gp_stroke_sculptmode_poll_with_tool(bContext *C, const char gpencil_sculpt_tool)
+{
+ bGPdata *gpd = CTX_data_gpencil_data(C);
+ if (!gpd) {
+ return false;
+ }
+
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ if (!ts || !ts->gp_sculptpaint) {
+ return false;
+ }
+
+ Brush *brush = BKE_paint_brush(&ts->gp_sculptpaint->paint);
+ return ((gpd->flag & GP_DATA_STROKE_SCULPTMODE) && (brush && brush->gpencil_settings) &&
+ WM_toolsystem_active_tool_is_brush(C) &&
+ (brush->gpencil_sculpt_tool == gpencil_sculpt_tool));
+}
+
+static bool gp_stroke_weightmode_poll_with_tool(bContext *C, const char gpencil_weight_tool)
+{
+ bGPdata *gpd = CTX_data_gpencil_data(C);
+ if (!gpd) {
+ return false;
+ }
+
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ if (!ts || !ts->gp_weightpaint) {
+ return false;
+ }
+
+ Brush *brush = BKE_paint_brush(&ts->gp_weightpaint->paint);
+ return ((gpd->flag & GP_DATA_STROKE_WEIGHTMODE) && (brush && brush->gpencil_settings) &&
+ WM_toolsystem_active_tool_is_brush(C) &&
+ (brush->gpencil_weight_tool == gpencil_weight_tool));
+}
+
/* Poll callback for stroke painting (draw brush) */
static bool gp_stroke_paintmode_draw_poll(bContext *C)
{
@@ -113,6 +167,12 @@ static bool gp_stroke_paintmode_fill_poll(bContext *C)
return gp_stroke_paintmode_poll_with_tool(C, GPAINT_TOOL_FILL);
}
+/* Poll callback for stroke painting (tint) */
+static bool gp_stroke_paintmode_tint_poll(bContext *C)
+{
+ return gp_stroke_paintmode_poll_with_tool(C, GPAINT_TOOL_TINT);
+}
+
/* Poll callback for stroke sculpting mode */
static bool gp_stroke_sculptmode_poll(bContext *C)
{
@@ -125,9 +185,8 @@ static bool gp_stroke_sculptmode_poll(bContext *C)
return ((gpd) && (gpd->flag & GP_DATA_STROKE_EDITMODE));
}
else {
- /* weight paint is a submode of sculpt */
if ((ob) && (ob->type == OB_GPENCIL)) {
- return GPENCIL_SCULPT_OR_WEIGHT_MODE(gpd);
+ return GPENCIL_SCULPT_MODE(gpd);
}
}
@@ -141,12 +200,113 @@ static bool gp_stroke_weightmode_poll(bContext *C)
Object *ob = CTX_data_active_object(C);
if ((ob) && (ob->type == OB_GPENCIL)) {
- return (gpd && (gpd->flag & GP_DATA_STROKE_WEIGHTMODE));
+ return GPENCIL_WEIGHT_MODE(gpd);
}
return 0;
}
+/* Poll callback for stroke vertex paint mode */
+static bool gp_stroke_vertexmode_poll(bContext *C)
+{
+ bGPdata *gpd = CTX_data_gpencil_data(C);
+ Object *ob = CTX_data_active_object(C);
+
+ if ((ob) && (ob->type == OB_GPENCIL)) {
+ return (gpd && (gpd->flag & GP_DATA_STROKE_VERTEXMODE));
+ }
+
+ return 0;
+}
+
+/* Poll callback for vertex painting (draw) */
+static bool gp_stroke_vertexmode_draw_poll(bContext *C)
+{
+ return gp_stroke_vertexmode_poll_with_tool(C, GPVERTEX_TOOL_DRAW);
+}
+
+/* Poll callback for vertex painting (blur) */
+static bool gp_stroke_vertexmode_blur_poll(bContext *C)
+{
+ return gp_stroke_vertexmode_poll_with_tool(C, GPVERTEX_TOOL_BLUR);
+}
+
+/* Poll callback for vertex painting (average) */
+static bool gp_stroke_vertexmode_average_poll(bContext *C)
+{
+ return gp_stroke_vertexmode_poll_with_tool(C, GPVERTEX_TOOL_AVERAGE);
+}
+
+/* Poll callback for vertex painting (smear) */
+static bool gp_stroke_vertexmode_smear_poll(bContext *C)
+{
+ return gp_stroke_vertexmode_poll_with_tool(C, GPVERTEX_TOOL_SMEAR);
+}
+
+/* Poll callback for vertex painting (replace) */
+static bool gp_stroke_vertexmode_replace_poll(bContext *C)
+{
+ return gp_stroke_vertexmode_poll_with_tool(C, GPVERTEX_TOOL_REPLACE);
+}
+
+/* Poll callback for sculpt (Smooth) */
+static bool gp_stroke_sculptmode_smooth_poll(bContext *C)
+{
+ return gp_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_SMOOTH);
+}
+/* Poll callback for sculpt (Thickness) */
+static bool gp_stroke_sculptmode_thickness_poll(bContext *C)
+{
+ return gp_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_THICKNESS);
+}
+
+/* Poll callback for sculpt (Strength) */
+static bool gp_stroke_sculptmode_strength_poll(bContext *C)
+{
+ return gp_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_STRENGTH);
+}
+
+/* Poll callback for sculpt (Grab) */
+static bool gp_stroke_sculptmode_grab_poll(bContext *C)
+{
+ return gp_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_GRAB);
+}
+
+/* Poll callback for sculpt (Push) */
+static bool gp_stroke_sculptmode_push_poll(bContext *C)
+{
+ return gp_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_PUSH);
+}
+
+/* Poll callback for sculpt (Twist) */
+static bool gp_stroke_sculptmode_twist_poll(bContext *C)
+{
+ return gp_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_TWIST);
+}
+
+/* Poll callback for sculpt (Pinch) */
+static bool gp_stroke_sculptmode_pinch_poll(bContext *C)
+{
+ return gp_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_PINCH);
+}
+/* Poll callback for sculpt (Randomize) */
+static bool gp_stroke_sculptmode_randomize_poll(bContext *C)
+{
+ return gp_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_RANDOMIZE);
+}
+
+/* Poll callback for sculpt (Clone) */
+static bool gp_stroke_sculptmode_clone_poll(bContext *C)
+{
+ return gp_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_CLONE);
+}
+
+/* Poll callback for weight paint (Draw) */
+static bool gp_stroke_weightmode_draw_poll(bContext *C)
+{
+ return gp_stroke_weightmode_poll_with_tool(C, GPWEIGHT_TOOL_DRAW);
+}
+
/* Stroke Editing Keymap - Only when editmode is enabled */
static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf)
{
@@ -177,6 +337,13 @@ static void ed_keymap_gpencil_painting_fill(wmKeyConfig *keyconf)
keymap->poll = gp_stroke_paintmode_fill_poll;
}
+/* keys for draw with a tint brush */
+static void ed_keymap_gpencil_painting_tint(wmKeyConfig *keyconf)
+{
+ wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Paint (Tint)", 0, 0);
+ keymap->poll = gp_stroke_paintmode_tint_poll;
+}
+
/* Stroke Painting Keymap - Only when paintmode is enabled */
static void ed_keymap_gpencil_painting(wmKeyConfig *keyconf)
{
@@ -200,6 +367,106 @@ static void ed_keymap_gpencil_weightpainting(wmKeyConfig *keyconf)
wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Weight Mode", 0, 0);
keymap->poll = gp_stroke_weightmode_poll;
}
+
+static void ed_keymap_gpencil_vertexpainting(wmKeyConfig *keyconf)
+{
+ /* set poll callback - so that this keymap only gets enabled when stroke vertex is enabled */
+ wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Vertex Mode", 0, 0);
+ keymap->poll = gp_stroke_vertexmode_poll;
+}
+
+/* keys for vertex with a draw brush */
+static void ed_keymap_gpencil_vertexpainting_draw(wmKeyConfig *keyconf)
+{
+ wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Vertex (Draw)", 0, 0);
+ keymap->poll = gp_stroke_vertexmode_draw_poll;
+}
+
+/* keys for vertex with a blur brush */
+static void ed_keymap_gpencil_vertexpainting_blur(wmKeyConfig *keyconf)
+{
+ wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Vertex (Blur)", 0, 0);
+ keymap->poll = gp_stroke_vertexmode_blur_poll;
+}
+/* keys for vertex with a average brush */
+static void ed_keymap_gpencil_vertexpainting_average(wmKeyConfig *keyconf)
+{
+ wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Vertex (Average)", 0, 0);
+ keymap->poll = gp_stroke_vertexmode_average_poll;
+}
+/* keys for vertex with a smear brush */
+static void ed_keymap_gpencil_vertexpainting_smear(wmKeyConfig *keyconf)
+{
+ wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Vertex (Smear)", 0, 0);
+ keymap->poll = gp_stroke_vertexmode_smear_poll;
+}
+/* keys for vertex with a replace brush */
+static void ed_keymap_gpencil_vertexpainting_replace(wmKeyConfig *keyconf)
+{
+ wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Vertex (Replace)", 0, 0);
+ keymap->poll = gp_stroke_vertexmode_replace_poll;
+}
+/* keys for sculpt with a smooth brush */
+static void ed_keymap_gpencil_sculptpainting_smooth(wmKeyConfig *keyconf)
+{
+ wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Sculpt (Smooth)", 0, 0);
+ keymap->poll = gp_stroke_sculptmode_smooth_poll;
+}
+/* keys for sculpt with a thickness brush */
+static void ed_keymap_gpencil_sculptpainting_thickness(wmKeyConfig *keyconf)
+{
+ wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Sculpt (Thickness)", 0, 0);
+ keymap->poll = gp_stroke_sculptmode_thickness_poll;
+}
+/* keys for sculpt with a strength brush */
+static void ed_keymap_gpencil_sculptpainting_strength(wmKeyConfig *keyconf)
+{
+ wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Sculpt (Strength)", 0, 0);
+ keymap->poll = gp_stroke_sculptmode_strength_poll;
+}
+/* keys for sculpt with a grab brush */
+static void ed_keymap_gpencil_sculptpainting_grab(wmKeyConfig *keyconf)
+{
+ wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Sculpt (Grab)", 0, 0);
+ keymap->poll = gp_stroke_sculptmode_grab_poll;
+}
+/* keys for sculpt with a push brush */
+static void ed_keymap_gpencil_sculptpainting_push(wmKeyConfig *keyconf)
+{
+ wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Sculpt (Push)", 0, 0);
+ keymap->poll = gp_stroke_sculptmode_push_poll;
+}
+/* keys for sculpt with a twist brush */
+static void ed_keymap_gpencil_sculptpainting_twist(wmKeyConfig *keyconf)
+{
+ wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Sculpt (Twist)", 0, 0);
+ keymap->poll = gp_stroke_sculptmode_twist_poll;
+}
+/* keys for sculpt with a pinch brush */
+static void ed_keymap_gpencil_sculptpainting_pinch(wmKeyConfig *keyconf)
+{
+ wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Sculpt (Pinch)", 0, 0);
+ keymap->poll = gp_stroke_sculptmode_pinch_poll;
+}
+/* keys for sculpt with a randomize brush */
+static void ed_keymap_gpencil_sculptpainting_randomize(wmKeyConfig *keyconf)
+{
+ wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Sculpt (Randomize)", 0, 0);
+ keymap->poll = gp_stroke_sculptmode_randomize_poll;
+}
+/* keys for sculpt with a clone brush */
+static void ed_keymap_gpencil_sculptpainting_clone(wmKeyConfig *keyconf)
+{
+ wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Sculpt (Clone)", 0, 0);
+ keymap->poll = gp_stroke_sculptmode_clone_poll;
+}
+/* keys for weight with a draw brush */
+static void ed_keymap_gpencil_weightpainting_draw(wmKeyConfig *keyconf)
+{
+ wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Weight (Draw)", 0, 0);
+ keymap->poll = gp_stroke_weightmode_draw_poll;
+}
+
/* ==================== */
void ED_keymap_gpencil(wmKeyConfig *keyconf)
@@ -210,8 +477,25 @@ void ED_keymap_gpencil(wmKeyConfig *keyconf)
ed_keymap_gpencil_painting_draw(keyconf);
ed_keymap_gpencil_painting_erase(keyconf);
ed_keymap_gpencil_painting_fill(keyconf);
+ ed_keymap_gpencil_painting_tint(keyconf);
ed_keymap_gpencil_sculpting(keyconf);
+ ed_keymap_gpencil_sculptpainting_smooth(keyconf);
+ ed_keymap_gpencil_sculptpainting_thickness(keyconf);
+ ed_keymap_gpencil_sculptpainting_strength(keyconf);
+ ed_keymap_gpencil_sculptpainting_grab(keyconf);
+ ed_keymap_gpencil_sculptpainting_push(keyconf);
+ ed_keymap_gpencil_sculptpainting_twist(keyconf);
+ ed_keymap_gpencil_sculptpainting_pinch(keyconf);
+ ed_keymap_gpencil_sculptpainting_randomize(keyconf);
+ ed_keymap_gpencil_sculptpainting_clone(keyconf);
ed_keymap_gpencil_weightpainting(keyconf);
+ ed_keymap_gpencil_weightpainting_draw(keyconf);
+ ed_keymap_gpencil_vertexpainting(keyconf);
+ ed_keymap_gpencil_vertexpainting_draw(keyconf);
+ ed_keymap_gpencil_vertexpainting_blur(keyconf);
+ ed_keymap_gpencil_vertexpainting_average(keyconf);
+ ed_keymap_gpencil_vertexpainting_smear(keyconf);
+ ed_keymap_gpencil_vertexpainting_replace(keyconf);
}
/* ****************************************** */
@@ -226,6 +510,12 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_draw);
WM_operatortype_append(GPENCIL_OT_fill);
+ WM_operatortype_append(GPENCIL_OT_vertex_paint);
+ WM_operatortype_append(GPENCIL_OT_vertex_color_brightness_contrast);
+ WM_operatortype_append(GPENCIL_OT_vertex_color_hsv);
+ WM_operatortype_append(GPENCIL_OT_vertex_color_invert);
+ WM_operatortype_append(GPENCIL_OT_vertex_color_levels);
+ WM_operatortype_append(GPENCIL_OT_vertex_color_set);
/* Guides ----------------------- */
@@ -238,6 +528,7 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_paintmode_toggle);
WM_operatortype_append(GPENCIL_OT_sculptmode_toggle);
WM_operatortype_append(GPENCIL_OT_weightmode_toggle);
+ WM_operatortype_append(GPENCIL_OT_vertexmode_toggle);
WM_operatortype_append(GPENCIL_OT_selection_opacity_toggle);
WM_operatortype_append(GPENCIL_OT_select);
@@ -253,6 +544,7 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_select_first);
WM_operatortype_append(GPENCIL_OT_select_last);
WM_operatortype_append(GPENCIL_OT_select_alternate);
+ WM_operatortype_append(GPENCIL_OT_select_color);
WM_operatortype_append(GPENCIL_OT_duplicate);
WM_operatortype_append(GPENCIL_OT_delete);
@@ -263,6 +555,7 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_move_to_layer);
WM_operatortype_append(GPENCIL_OT_layer_change);
+ WM_operatortype_append(GPENCIL_OT_layer_active);
WM_operatortype_append(GPENCIL_OT_set_active_material);
@@ -271,8 +564,10 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_snap_cursor_to_selected);
WM_operatortype_append(GPENCIL_OT_reproject);
+ WM_operatortype_append(GPENCIL_OT_recalc_geometry);
WM_operatortype_append(GPENCIL_OT_sculpt_paint);
+ WM_operatortype_append(GPENCIL_OT_weight_paint);
/* Editing (Buttons) ------------ */
@@ -288,6 +583,9 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_layer_duplicate);
WM_operatortype_append(GPENCIL_OT_layer_duplicate_object);
+ WM_operatortype_append(GPENCIL_OT_layer_mask_add);
+ WM_operatortype_append(GPENCIL_OT_layer_mask_remove);
+
WM_operatortype_append(GPENCIL_OT_hide);
WM_operatortype_append(GPENCIL_OT_reveal);
WM_operatortype_append(GPENCIL_OT_lock_all);
@@ -306,6 +604,8 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_convert);
+ WM_operatortype_append(GPENCIL_OT_image_to_grease_pencil);
+
WM_operatortype_append(GPENCIL_OT_stroke_arrange);
WM_operatortype_append(GPENCIL_OT_stroke_change_color);
WM_operatortype_append(GPENCIL_OT_stroke_lock_color);
@@ -325,8 +625,16 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_stroke_cutter);
WM_operatortype_append(GPENCIL_OT_stroke_trim);
WM_operatortype_append(GPENCIL_OT_stroke_merge_by_distance);
+ WM_operatortype_append(GPENCIL_OT_stroke_merge_material);
+
+ WM_operatortype_append(GPENCIL_OT_material_to_vertex_color);
+ WM_operatortype_append(GPENCIL_OT_extract_palette_vertex);
+
+ WM_operatortype_append(GPENCIL_OT_transform_fill);
+ WM_operatortype_append(GPENCIL_OT_reset_transform_fill);
- WM_operatortype_append(GPENCIL_OT_brush_presets_create);
+ WM_operatortype_append(GPENCIL_OT_brush_reset);
+ WM_operatortype_append(GPENCIL_OT_brush_reset_all);
/* vertex groups */
WM_operatortype_append(GPENCIL_OT_vertex_group_assign);
diff --git a/source/blender/editors/gpencil/gpencil_ops_versioning.c b/source/blender/editors/gpencil/gpencil_ops_versioning.c
index 3d56cb0fcb1..e5b9d902210 100644
--- a/source/blender/editors/gpencil/gpencil_ops_versioning.c
+++ b/source/blender/editors/gpencil/gpencil_ops_versioning.c
@@ -130,7 +130,6 @@ static int gpencil_convert_old_files_exec(bContext *C, wmOperator *op)
copy_v4_v4(gp_style->fill_rgba, palcolor->fill);
/* set basic settings */
- gp_style->pattern_gridsize = 0.1f;
gp_style->gradient_radius = 0.5f;
ARRAY_SET_ITEMS(gp_style->mix_rgba, 1.0f, 1.0f, 1.0f, 0.2f);
ARRAY_SET_ITEMS(gp_style->gradient_scale, 1.0f, 1.0f);
@@ -138,13 +137,13 @@ static int gpencil_convert_old_files_exec(bContext *C, wmOperator *op)
gp_style->texture_opacity = 1.0f;
gp_style->texture_pixsize = 100.0f;
- gp_style->flag |= GP_STYLE_STROKE_SHOW;
- gp_style->flag |= GP_STYLE_FILL_SHOW;
+ gp_style->flag |= GP_MATERIAL_STROKE_SHOW;
+ gp_style->flag |= GP_MATERIAL_FILL_SHOW;
/* fix strokes */
- for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
- for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
- for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
if ((gps->colorname[0] != '\0') && (STREQ(gps->colorname, palcolor->info))) {
gps->mat_nr = ob->totcol - 1;
gps->colorname[0] = '\0';
@@ -174,7 +173,7 @@ static int gpencil_convert_old_files_exec(bContext *C, wmOperator *op)
for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor;
palcolor = palcolor->next) {
/* fix layers */
- for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
/* unlock/unhide layer */
gpl->flag &= ~GP_LAYER_LOCKED;
gpl->flag &= ~GP_LAYER_HIDE;
@@ -182,8 +181,8 @@ static int gpencil_convert_old_files_exec(bContext *C, wmOperator *op)
gpl->opacity = 1.0f;
/* disable tint */
gpl->tintcolor[3] = 0.0f;
- for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
- for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
if ((gps->colorname[0] != '\0') && (STREQ(gps->colorname, palcolor->info))) {
/* copy color settings */
copy_v4_v4(gpl->color, palcolor->color);
diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c
index afc0e66a8a6..fea589746c4 100644
--- a/source/blender/editors/gpencil/gpencil_paint.c
+++ b/source/blender/editors/gpencil/gpencil_paint.c
@@ -149,6 +149,8 @@ typedef struct tGPsdata {
/** current object. */
Object *ob;
+ /** Obeject eval. */
+ Object *ob_eval;
/** window where painting originated. */
wmWindow *win;
/** area where painting originated. */
@@ -215,6 +217,8 @@ typedef struct tGPsdata {
float imat[4][4];
float mat[4][4];
+ float diff_mat[4][4];
+
/** custom color - hack for enforcing a particular color for track/mask editing. */
float custom_color[4];
@@ -270,11 +274,6 @@ static void gp_update_cache(bGPdata *gpd)
}
}
-static bool gp_stroke_added_check(tGPsdata *p)
-{
- return (p->gpf && p->gpf->strokes.last && p->flags & GP_PAINTFLAG_STROKEADDED);
-}
-
static void gp_stroke_added_enable(tGPsdata *p)
{
BLI_assert(p->gpf->strokes.last != NULL);
@@ -353,7 +352,7 @@ static void gp_get_3d_reference(tGPsdata *p, float vec[3])
if (p->ownerPtr.type == &RNA_Object) {
ob = (Object *)p->ownerPtr.data;
}
- ED_gp_get_drawing_reference(p->scene, ob, p->gpl, *p->align_flag, vec);
+ ED_gpencil_drawing_reference_get(p->scene, ob, p->gpl, *p->align_flag, vec);
}
/* Stroke Editing ---------------------------- */
@@ -483,49 +482,26 @@ static void gp_stroke_convertcoords(tGPsdata *p, const float mval[2], float out[
}
}
-/* apply jitter to stroke */
-static void gp_brush_jitter(bGPdata *gpd,
- Brush *brush,
- tGPspoint *pt,
- const float mval[2],
- const float pressure,
- float r_mval[2],
- RNG *rng)
+/* Apply jitter to stroke point. */
+static void gp_brush_jitter(bGPdata *gpd, tGPspoint *pt, const float amplitude)
{
- float tmp_pressure = pressure;
- if (brush->gpencil_settings->draw_jitter > 0.0f) {
- float curvef = BKE_curvemapping_evaluateF(brush->gpencil_settings->curve_jitter, 0, pressure);
- tmp_pressure = curvef * brush->gpencil_settings->draw_sensitivity;
- }
- /* exponential value */
- const float exfactor = (brush->gpencil_settings->draw_jitter + 2.0f) *
- (brush->gpencil_settings->draw_jitter + 2.0f);
- const float fac = BLI_rng_get_float(rng) * exfactor * tmp_pressure;
- /* Jitter is applied perpendicular to the mouse movement vector (2D space) */
- float mvec[2], svec[2];
- /* mouse movement in ints -> floats */
+ /* Jitter is applied perpendicular to the mouse movement vector (2D space). */
+ float mvec[2];
+ /* Mouse movement in ints -> floats. */
if (gpd->runtime.sbuffer_used > 1) {
- mvec[0] = (mval[0] - (pt - 1)->x);
- mvec[1] = (mval[1] - (pt - 1)->y);
+ tGPspoint *pt_prev = pt - 1;
+ sub_v2_v2v2(mvec, &pt->x, &pt_prev->x);
normalize_v2(mvec);
}
else {
mvec[0] = 0.0f;
mvec[1] = 0.0f;
}
- /* rotate mvec by 90 degrees... */
- svec[0] = -mvec[1];
- svec[1] = mvec[0];
- /* scale the displacement by the random, and apply */
- if (BLI_rng_get_float(rng) > 0.5f) {
- mul_v2_fl(svec, -fac);
- }
- else {
- mul_v2_fl(svec, fac);
- }
-
- r_mval[0] = mval[0] + svec[0];
- r_mval[1] = mval[1] + svec[1];
+ /* Rotate mvec by 90 degrees... */
+ SWAP(float, mvec[0], mvec[1]);
+ mvec[0] -= mvec[0];
+ /* Scale by displacement amount, and apply. */
+ madd_v2_v2fl(&pt->x, mvec, amplitude);
}
/* apply pressure change depending of the angle of the stroke to simulate a pen with shape */
@@ -581,6 +557,7 @@ static void gp_brush_angle(bGPdata *gpd, Brush *brush, tGPspoint *pt, const floa
static void gp_smooth_buffer(tGPsdata *p, float inf, int idx)
{
bGPdata *gpd = p->gpd;
+ GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide;
const short num_points = gpd->runtime.sbuffer_used;
/* Do nothing if not enough points to smooth out */
@@ -628,9 +605,12 @@ static void gp_smooth_buffer(tGPsdata *p, float inf, int idx)
strength += ptd->strength * average_fac;
}
- /* Based on influence factor, blend between original and optimal smoothed coordinate. */
- interp_v2_v2v2(c, c, sco, inf);
- copy_v2_v2(&ptc->x, c);
+ /* Based on influence factor, blend between original and optimal smoothed coordinate but not
+ * for Guide mode. */
+ if (!guide->use_guide) {
+ interp_v2_v2v2(c, c, sco, inf);
+ copy_v2_v2(&ptc->x, c);
+ }
/* Interpolate pressure. */
ptc->pressure = interpf(ptc->pressure, pressure, inf);
/* Interpolate strength. */
@@ -661,140 +641,59 @@ static void gp_smooth_segment(bGPdata *gpd, const float inf, int from_idx, int t
tGPspoint *ptd = &points[i];
float sco[2] = {0.0f};
+ float pressure = 0.0f;
+ float strength = 0.0f;
/* Compute smoothed coordinate by taking the ones nearby */
if (pta) {
madd_v2_v2fl(sco, &pta->x, average_fac);
+ pressure += pta->pressure * average_fac;
+ strength += pta->strength * average_fac;
}
else {
madd_v2_v2fl(sco, &ptc->x, average_fac);
+ pressure += ptc->pressure * average_fac;
+ strength += ptc->strength * average_fac;
}
if (ptb) {
madd_v2_v2fl(sco, &ptb->x, average_fac);
+ pressure += ptb->pressure * average_fac;
+ strength += ptb->strength * average_fac;
}
else {
madd_v2_v2fl(sco, &ptc->x, average_fac);
+ pressure += ptc->pressure * average_fac;
+ strength += ptc->strength * average_fac;
}
madd_v2_v2fl(sco, &ptc->x, average_fac);
+ pressure += ptc->pressure * average_fac;
+ strength += ptc->strength * average_fac;
madd_v2_v2fl(sco, &ptd->x, average_fac);
+ pressure += ptd->pressure * average_fac;
+ strength += ptd->strength * average_fac;
/* Based on influence factor, blend between original and optimal smoothed coordinate. */
interp_v2_v2v2(&ptc->x, &ptc->x, sco, inf);
- }
-}
-
-/* Smooth all the sections created with fake events to avoid abrupt transitions.
- *
- * As the fake events add points between two real events, this produces a straight line, but if
- * there is 3 or more real points that used fakes, the stroke is not smooth and produces abrupt
- * angles.
- * This function reads these segments and finds the real points and smooth with the surrounding
- * points. */
-static void gp_smooth_fake_segments(tGPsdata *p)
-{
- bGPdata *gpd = p->gpd;
- Brush *brush = p->brush;
- if (brush->gpencil_settings->input_samples < 2) {
- return;
- }
-
- tGPspoint *points = (tGPspoint *)gpd->runtime.sbuffer;
- tGPspoint *pt = NULL;
- /* Index where segment starts. */
- int from_idx = 0;
- /* Index where segment ends. */
- int to_idx = 0;
-
- bool doit = false;
- /* Loop all points except the extremes. */
- for (int i = 1; i < gpd->runtime.sbuffer_used - 1; i++) {
- pt = &points[i];
- bool is_fake = (bool)(pt->tflag & GP_TPOINT_FAKE);
- to_idx = i;
-
- /* Detect fake points in the stroke. */
- if ((!doit) && (is_fake)) {
- from_idx = i;
- doit = true;
- }
- /* If detect control point after fake points, select a segment with same length in both sides,
- * except if it is more than stroke length. */
- if ((doit) && (!is_fake)) {
- if (i + (i - from_idx) < gpd->runtime.sbuffer_used - 1) {
- to_idx = i + (i - from_idx);
- /* Smooth this segments (need loop to get cumulative smooth). */
- for (int r = 0; r < 5; r++) {
- gp_smooth_segment(gpd, 0.1f, from_idx, to_idx);
- }
- }
- else {
- break;
- }
- /* Reset to new segments. */
- from_idx = i;
- doit = false;
- }
- }
-}
-
-/* Smooth the section added with fake events when pen moves very fast. */
-static void gp_smooth_fake_events(tGPsdata *p, int size_before, int size_after)
-{
- bGPdata *gpd = p->gpd;
- const short totpoints = size_after - size_before - 1;
- /* Do nothing if not enough data to smooth out. */
- if (totpoints < 1) {
- return;
- }
-
- /* Back two points to get smoother effect. */
- size_before -= 2;
- CLAMP_MIN(size_before, 1);
-
- tGPspoint *points = (tGPspoint *)gpd->runtime.sbuffer;
- /* Extreme points. */
- const tGPspoint *pta = &points[size_before - 1];
- const tGPspoint *ptb = &points[size_after - 1];
- tGPspoint *pt1, *pt2;
- int i;
-
- /* Get total length of the segment to smooth. */
- float totlen = 0.0f;
- for (i = size_before; i < size_after; i++) {
- pt1 = &points[i - 1];
- pt2 = &points[i];
- totlen += len_v2v2(&pt1->x, &pt2->x);
- }
- /* Smooth interpolating the position of the points. */
- float pointlen = 0.0f;
- for (i = size_before; i < size_after - 1; i++) {
- pt1 = &points[i - 1];
- pt2 = &points[i];
- pointlen += len_v2v2(&pt1->x, &pt2->x);
- pt2->pressure = interpf(ptb->pressure, pta->pressure, pointlen / totlen);
- pt2->strength = interpf(ptb->strength, pta->strength, pointlen / totlen);
+ /* Interpolate pressure. */
+ ptc->pressure = interpf(ptc->pressure, pressure, inf);
+ /* Interpolate strength. */
+ ptc->strength = interpf(ptc->strength, strength, inf);
}
}
/* add current stroke-point to buffer (returns whether point was successfully added) */
-static short gp_stroke_addpoint(
- tGPsdata *p, const float mval[2], float pressure, double curtime, bool is_fake)
+static short gp_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure, double curtime)
{
bGPdata *gpd = p->gpd;
Brush *brush = p->brush;
+ BrushGpencilSettings *brush_settings = p->brush->gpencil_settings;
tGPspoint *pt;
- ToolSettings *ts = p->scene->toolsettings;
Object *obact = (Object *)p->ownerPtr.data;
- Depsgraph *depsgraph = p->depsgraph;
RegionView3D *rv3d = p->region->regiondata;
- View3D *v3d = p->sa->spacedata.first;
- MaterialGPencilStyle *gp_style = p->material->gp_style;
- const int def_nr = obact->actdef - 1;
- const bool have_weight = (bool)BLI_findlink(&obact->defbase, def_nr);
/* check painting mode */
if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) {
@@ -843,111 +742,73 @@ static short gp_stroke_addpoint(
return GP_STROKEADD_INVALID;
}
+ /* Set vertex colors for buffer. */
+ ED_gpencil_sbuffer_vertex_color_set(
+ p->depsgraph, p->ob, p->scene->toolsettings, p->brush, p->material);
+
/* get pointer to destination point */
pt = ((tGPspoint *)(gpd->runtime.sbuffer) + gpd->runtime.sbuffer_used);
- /* Set if point was created by fake events. */
- if (is_fake) {
- pt->tflag |= GP_TPOINT_FAKE;
- }
- else {
- pt->tflag &= ~GP_TPOINT_FAKE;
- }
-
/* store settings */
+ pt->strength = brush_settings->draw_strength;
+ pt->pressure = 1.0f;
+ pt->uv_rot = 0.0f;
+ copy_v2_v2(&pt->x, mval);
+
/* pressure */
- if (brush->gpencil_settings->flag & GP_BRUSH_USE_PRESSURE) {
- float curvef = BKE_curvemapping_evaluateF(
- brush->gpencil_settings->curve_sensitivity, 0, pressure);
- pt->pressure = curvef * brush->gpencil_settings->draw_sensitivity;
- }
- else {
- pt->pressure = 1.0f;
+ if (brush_settings->flag & GP_BRUSH_USE_PRESSURE) {
+ pt->pressure *= BKE_curvemapping_evaluateF(brush_settings->curve_sensitivity, 0, pressure);
}
- /* Apply jitter to position */
- if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) &&
- (brush->gpencil_settings->draw_jitter > 0.0f)) {
- float r_mval[2];
- const float jitpress = (brush->gpencil_settings->flag & GP_BRUSH_USE_JITTER_PRESSURE) ?
- pressure :
- 1.0f;
- gp_brush_jitter(gpd, brush, pt, mval, jitpress, r_mval, p->rng);
- copy_v2_v2(&pt->x, r_mval);
- }
- else {
- copy_v2_v2(&pt->x, mval);
+ /* color strength */
+ if (brush_settings->flag & GP_BRUSH_USE_STENGTH_PRESSURE) {
+ pt->strength *= BKE_curvemapping_evaluateF(brush_settings->curve_strength, 0, pressure);
+ CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f);
}
- /* apply randomness to pressure */
- if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) &&
- (brush->gpencil_settings->draw_random_press > 0.0f)) {
- float curvef = BKE_curvemapping_evaluateF(
- brush->gpencil_settings->curve_sensitivity, 0, pressure);
- float tmp_pressure = curvef * brush->gpencil_settings->draw_sensitivity;
- if (BLI_rng_get_float(p->rng) > 0.5f) {
- pt->pressure -= tmp_pressure * brush->gpencil_settings->draw_random_press *
- BLI_rng_get_float(p->rng);
+
+ if (brush_settings->flag & GP_BRUSH_GROUP_RANDOM) {
+ /* Apply jitter to position */
+ if (brush_settings->draw_jitter > 0.0f) {
+ float rand = BLI_rng_get_float(p->rng) * 2.0f - 1.0f;
+ float jitpress = 1.0f;
+ if (brush_settings->flag & GP_BRUSH_USE_JITTER_PRESSURE) {
+ jitpress = BKE_curvemapping_evaluateF(brush_settings->curve_jitter, 0, pressure);
+ }
+ /* FIXME the +2 means minimum jitter is 4 which is a bit strange for UX. */
+ const float exp_factor = brush_settings->draw_jitter + 2.0f;
+ const float fac = rand * square_f(exp_factor) * jitpress;
+ gp_brush_jitter(gpd, pt, fac);
}
- else {
- pt->pressure += tmp_pressure * brush->gpencil_settings->draw_random_press *
- BLI_rng_get_float(p->rng);
+ /* apply randomness to pressure */
+ if (brush_settings->draw_random_press > 0.0f) {
+ float rand = BLI_rng_get_float(p->rng) * 2.0f - 1.0f;
+ pt->pressure *= 1.0 + rand * 2.0 * brush_settings->draw_random_press;
+ CLAMP(pt->pressure, GPENCIL_STRENGTH_MIN, 1.0f);
}
- CLAMP(pt->pressure, GPENCIL_STRENGTH_MIN, 1.0f);
- }
-
- /* apply randomness to uv texture rotation */
- if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) &&
- (brush->gpencil_settings->uv_random > 0.0f)) {
- if (BLI_rng_get_float(p->rng) > 0.5f) {
- pt->uv_rot = (BLI_rng_get_float(p->rng) * M_PI * -1) * brush->gpencil_settings->uv_random;
+ /* apply randomness to uv texture rotation */
+ if (brush_settings->uv_random > 0.0f) {
+ float rand = BLI_rng_get_float(p->rng) * 2.0f - 1.0f;
+ pt->uv_rot += rand * M_PI * brush_settings->uv_random;
+ CLAMP(pt->uv_rot, -M_PI_2, M_PI_2);
}
- else {
- pt->uv_rot = (BLI_rng_get_float(p->rng) * M_PI) * brush->gpencil_settings->uv_random;
+ /* apply randomness to color strength */
+ if (brush_settings->draw_random_strength) {
+ float rand = BLI_rng_get_float(p->rng) * 2.0f - 1.0f;
+ pt->strength *= 1.0 + rand * brush_settings->draw_random_strength;
+ CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f);
}
- CLAMP(pt->uv_rot, -M_PI_2, M_PI_2);
- }
- else {
- pt->uv_rot = 0.0f;
}
/* apply angle of stroke to brush size */
- if (brush->gpencil_settings->draw_angle_factor != 0.0f) {
+ if (brush_settings->draw_angle_factor != 0.0f) {
gp_brush_angle(gpd, brush, pt, mval);
}
- /* color strength */
- if (brush->gpencil_settings->flag & GP_BRUSH_USE_STENGTH_PRESSURE) {
- float curvef = BKE_curvemapping_evaluateF(
- brush->gpencil_settings->curve_strength, 0, pressure);
- float tmp_pressure = curvef * brush->gpencil_settings->draw_sensitivity;
-
- pt->strength = tmp_pressure * brush->gpencil_settings->draw_strength;
- }
- else {
- pt->strength = brush->gpencil_settings->draw_strength;
- }
- CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f);
-
- /* apply randomness to color strength */
- if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) &&
- (brush->gpencil_settings->draw_random_strength > 0.0f)) {
- if (BLI_rng_get_float(p->rng) > 0.5f) {
- pt->strength -= pt->strength * brush->gpencil_settings->draw_random_strength *
- BLI_rng_get_float(p->rng);
- }
- else {
- pt->strength += pt->strength * brush->gpencil_settings->draw_random_strength *
- BLI_rng_get_float(p->rng);
- }
- CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f);
- }
-
/* point time */
pt->time = (float)(curtime - p->inittime);
/* point uv (only 3d view) */
if ((p->sa->spacetype == SPACE_VIEW3D) && (gpd->runtime.sbuffer_used > 0)) {
- float pixsize = gp_style->texture_pixsize / 1000000.0f;
tGPspoint *ptb = (tGPspoint *)gpd->runtime.sbuffer + gpd->runtime.sbuffer_used - 1;
bGPDspoint spt, spt2;
@@ -961,11 +822,8 @@ static short gp_stroke_addpoint(
/* reproject previous */
ED_gpencil_tpoint_to_point(p->region, origin, ptb, &spt2);
ED_gp_project_point_to_plane(p->scene, obact, rv3d, origin, p->lock_axis - 1, &spt2);
- p->totpixlen += len_v3v3(&spt.x, &spt2.x) / pixsize;
+ p->totpixlen += len_v3v3(&spt.x, &spt2.x);
pt->uv_fac = p->totpixlen;
- if ((gp_style) && (gp_style->sima)) {
- pt->uv_fac /= gp_style->sima->gen_x;
- }
}
else {
p->totpixlen = 0.0f;
@@ -975,7 +833,7 @@ static short gp_stroke_addpoint(
/* increment counters */
gpd->runtime.sbuffer_used++;
- /* smooth while drawing previous points with a reduction factor for previous */
+ /* Smooth while drawing previous points with a reduction factor for previous. */
if (brush->gpencil_settings->active_smooth > 0.0f) {
for (int s = 0; s < 3; s++) {
gp_smooth_buffer(p,
@@ -984,100 +842,11 @@ static short gp_stroke_addpoint(
}
}
- return GP_STROKEADD_NORMAL;
- }
- else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) {
-
- /* enable special flag for drawing engine */
- gpd->flag |= GP_DATA_STROKE_POLYGON;
-
- bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
- /* get pointer to destination point */
- pt = (tGPspoint *)(gpd->runtime.sbuffer);
-
- /* store settings */
- copy_v2_v2(&pt->x, mval);
- /* T44932 - Pressure vals are unreliable, so ignore for now */
- pt->pressure = 1.0f;
- pt->strength = 1.0f;
- pt->time = (float)(curtime - p->inittime);
-
- /* if there's stroke for this poly line session add (or replace last) point
- * to stroke. This allows to draw lines more interactively (see new segment
- * during mouse slide, e.g.)
- */
- if (gp_stroke_added_check(p)) {
- bGPDstroke *gps = p->gpf->strokes.last;
- bGPDspoint *pts;
- MDeformVert *dvert = NULL;
-
- /* First time point is adding to temporary buffer (need to allocate new point in stroke) */
- if (gpd->runtime.sbuffer_used == 0) {
- gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1));
- if (gps->dvert != NULL) {
- gps->dvert = MEM_reallocN(gps->dvert, sizeof(MDeformVert) * (gps->totpoints + 1));
- }
- gps->totpoints++;
- }
-
- pts = &gps->points[gps->totpoints - 1];
- if (gps->dvert != NULL) {
- dvert = &gps->dvert[gps->totpoints - 1];
- }
- /* special case for poly lines: normally,
- * depth is needed only when creating new stroke from buffer,
- * but poly lines are converting to stroke instantly,
- * so initialize depth buffer before converting coordinates
- */
- if (gpencil_project_check(p)) {
- 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);
- }
-
- /* convert screen-coordinates to appropriate coordinates (and store them) */
- gp_stroke_convertcoords(p, &pt->x, &pts->x, NULL);
- /* reproject to plane (only in 3d space) */
- gp_reproject_toplane(p, gps);
- /* if parented change position relative to parent object */
- gp_apply_parent_point(depsgraph, obact, gpd, gpl, pts);
- /* copy pressure and time */
- pts->pressure = pt->pressure;
- pts->strength = pt->strength;
- pts->time = pt->time;
- pts->uv_fac = pt->uv_fac;
- pts->uv_rot = pt->uv_rot;
-
- if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) {
- BKE_gpencil_dvert_ensure(gps);
- MDeformWeight *dw = BKE_defvert_ensure_index(dvert, def_nr);
- if (dw) {
- dw->weight = ts->vgroup_weight;
- }
- }
- else {
- if (dvert != NULL) {
- dvert->totweight = 0;
- dvert->dw = NULL;
- }
- }
-
- /* force fill recalc */
- gps->flag |= GP_STROKE_RECALC_GEOMETRY;
- /* drawing batch cache is dirty now */
- gp_update_cache(p->gpd);
- }
-
- /* increment counters */
- if (gpd->runtime.sbuffer_used == 0) {
- gpd->runtime.sbuffer_used++;
- }
+ /* Update evaluated data. */
+ ED_gpencil_sbuffer_update_eval(gpd, p->ob_eval);
return GP_STROKEADD_NORMAL;
}
-
/* return invalid state for now... */
return GP_STROKEADD_INVALID;
}
@@ -1143,42 +912,25 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
return;
}
- /* special case for poly line -- for already added stroke during session
- * coordinates are getting added to stroke immediately to allow more
- * interactive behavior
- */
- if (p->paintmode == GP_PAINTMODE_DRAW_POLY) {
- /* be sure to hide any lazy cursor */
- ED_gpencil_toggle_brush_cursor(p->C, true, NULL);
-
- if (gp_stroke_added_check(p)) {
- return;
- }
- }
-
/* allocate memory for a new stroke */
gps = MEM_callocN(sizeof(bGPDstroke), "gp_stroke");
/* copy appropriate settings for stroke */
gps->totpoints = totelem;
gps->thickness = brush->size;
- gps->gradient_f = brush->gpencil_settings->gradient_f;
- copy_v2_v2(gps->gradient_s, brush->gpencil_settings->gradient_s);
+ gps->fill_opacity_fac = 1.0f;
+ gps->hardeness = brush->gpencil_settings->hardeness;
+ copy_v2_v2(gps->aspect_ratio, brush->gpencil_settings->aspect_ratio);
gps->flag = gpd->runtime.sbuffer_sflag;
gps->inittime = p->inittime;
-
- /* enable recalculation flag by default (only used if hq fill) */
- gps->flag |= GP_STROKE_RECALC_GEOMETRY;
+ gps->uv_scale = 1.0f;
/* allocate enough memory for a continuous array for storage points */
const int subdivide = brush->gpencil_settings->draw_subdivide;
gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points");
+ gps->dvert = NULL;
- /* initialize triangle memory to dummy data */
- gps->triangles = NULL;
- gps->flag |= GP_STROKE_RECALC_GEOMETRY;
- gps->tot_triangles = 0;
/* drawing batch cache is dirty now */
gp_update_cache(p->gpd);
/* set pointer to first non-initialized point */
@@ -1187,6 +939,9 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
dvert = gps->dvert + (gps->totpoints - totelem);
}
+ /* Apply the vertex color to fill. */
+ ED_gpencil_fill_vertex_color_set(ts, brush, gps);
+
/* copy points from the buffer to the stroke */
if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) {
/* straight lines only -> only endpoints */
@@ -1201,6 +956,9 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
pt->strength = ptc->strength;
CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f);
pt->time = ptc->time;
+ /* Apply the vertex color to point. */
+ ED_gpencil_point_vertex_color_set(ts, brush, pt);
+
pt++;
if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) {
@@ -1231,6 +989,8 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
pt->strength = ptc->strength;
CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f);
pt->time = ptc->time;
+ /* Apply the vertex color to point. */
+ ED_gpencil_point_vertex_color_set(ts, brush, pt);
if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) {
BKE_gpencil_dvert_ensure(gps);
@@ -1252,7 +1012,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
pt = gps->points;
for (i = 0; i < gps->totpoints; i++, pt++) {
/* if parented change position relative to parent object */
- gp_apply_parent_point(depsgraph, obact, gpd, gpl, pt);
+ gp_apply_parent_point(depsgraph, obact, gpl, pt);
}
/* if camera view, reproject flat to view to avoid perspective effect */
@@ -1260,40 +1020,6 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
ED_gpencil_project_stroke_to_view(p->C, p->gpl, gps);
}
}
- else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) {
- /* first point */
- ptc = gpd->runtime.sbuffer;
-
- /* convert screen-coordinates to appropriate coordinates (and store them) */
- gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL);
- /* reproject to plane (only in 3d space) */
- gp_reproject_toplane(p, gps);
- /* if parented change position relative to parent object */
- gp_apply_parent_point(depsgraph, obact, gpd, gpl, pt);
- /* if camera view, reproject flat to view to avoid perspective effect */
- if (is_camera) {
- ED_gpencil_project_stroke_to_view(p->C, p->gpl, gps);
- }
- /* copy pressure and time */
- pt->pressure = ptc->pressure;
- pt->strength = ptc->strength;
- CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f);
- pt->time = ptc->time;
-
- if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) {
- BKE_gpencil_dvert_ensure(gps);
- MDeformWeight *dw = BKE_defvert_ensure_index(dvert, def_nr);
- if (dw) {
- dw->weight = ts->vgroup_weight;
- }
- }
- else {
- if (dvert != NULL) {
- dvert->totweight = 0;
- dvert->dw = NULL;
- }
- }
- }
else {
float *depth_arr = NULL;
@@ -1370,9 +1096,6 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
}
}
- /* Smooth any point created with fake events when the mouse/pen move very fast. */
- gp_smooth_fake_segments(p);
-
pt = gps->points;
dvert = gps->dvert;
@@ -1389,6 +1112,8 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
pt->time = ptc->time;
pt->uv_fac = ptc->uv_fac;
pt->uv_rot = ptc->uv_rot;
+ /* Apply the vertex color to point. */
+ ED_gpencil_point_vertex_color_set(ts, brush, pt);
if (dvert != NULL) {
dvert->totweight = 0;
@@ -1401,11 +1126,6 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) && (subdivide > 0)) {
gp_subdivide_stroke(gps, subdivide);
}
- /* apply randomness to stroke */
- if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) &&
- (brush->gpencil_settings->draw_random_sub > 0.0f)) {
- gp_randomize_stroke(gps, brush, p->rng);
- }
/* Smooth stroke after subdiv - only if there's something to do for each iteration,
* the factor is reduced to get a better smoothing
@@ -1415,8 +1135,8 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
float reduce = 0.0f;
for (int r = 0; r < brush->gpencil_settings->draw_smoothlvl; r++) {
for (i = 0; i < gps->totpoints - 1; i++) {
- BKE_gpencil_smooth_stroke(gps, i, brush->gpencil_settings->draw_smoothfac - reduce);
- BKE_gpencil_smooth_stroke_strength(gps, i, brush->gpencil_settings->draw_smoothfac);
+ BKE_gpencil_stroke_smooth(gps, i, brush->gpencil_settings->draw_smoothfac - reduce);
+ BKE_gpencil_stroke_smooth_strength(gps, i, brush->gpencil_settings->draw_smoothfac);
}
reduce += 0.25f; /* reduce the factor */
}
@@ -1425,23 +1145,13 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
/* Simplify adaptive */
if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) &&
(brush->gpencil_settings->simplify_f > 0.0f)) {
- BKE_gpencil_simplify_stroke(gps, brush->gpencil_settings->simplify_f);
- }
-
- /* smooth thickness */
- if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) &&
- (brush->gpencil_settings->thick_smoothfac > 0.0f)) {
- for (int r = 0; r < brush->gpencil_settings->thick_smoothlvl * 2; r++) {
- for (i = 0; i < gps->totpoints - 1; i++) {
- BKE_gpencil_smooth_stroke_thickness(gps, i, brush->gpencil_settings->thick_smoothfac);
- }
- }
+ BKE_gpencil_stroke_simplify_adaptive(gps, brush->gpencil_settings->simplify_f);
}
/* reproject to plane (only in 3d space) */
gp_reproject_toplane(p, gps);
/* change position relative to parent object */
- gp_apply_parent(depsgraph, obact, gpd, gpl, gps);
+ gp_apply_parent(depsgraph, obact, gpl, gps);
/* if camera view, reproject flat to view to avoid perspective effect */
if (is_camera) {
ED_gpencil_project_stroke_to_view(p->C, p->gpl, gps);
@@ -1463,15 +1173,11 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
}
}
- /* calculate UVs along the stroke */
- ED_gpencil_calc_stroke_uv(obact, gps);
-
/* add stroke to frame, usually on tail of the listbase, but if on back is enabled the stroke
* is added on listbase head because the drawing order is inverse and the head stroke is the
* first to draw. This is very useful for artist when drawing the background.
*/
- if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) &&
- (p->paintmode != GP_PAINTMODE_DRAW_POLY)) {
+ if (ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) {
BLI_addhead(&p->gpf->strokes, gps);
}
else {
@@ -1492,9 +1198,12 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
/* post process stroke */
if ((p->brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) &&
p->brush->gpencil_settings->flag & GP_BRUSH_TRIM_STROKE) {
- BKE_gpencil_trim_stroke(gps);
+ BKE_gpencil_stroke_trim(gps);
}
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gps);
+
gp_stroke_added_enable(p);
}
@@ -1540,7 +1249,7 @@ static bool gp_stroke_eraser_is_occluded(tGPsdata *p,
float diff_mat[4][4];
/* calculate difference matrix if parent object */
- ED_gpencil_parent_location(p->depsgraph, obact, p->gpd, gpl, diff_mat);
+ BKE_gpencil_parent_matrix_get(p->depsgraph, obact, gpl, diff_mat);
if (ED_view3d_autodist_simple(p->region, mval_i, mval_3d, 0, NULL)) {
const float depth_mval = view3d_point_depth(rv3d, mval_3d);
@@ -1642,9 +1351,7 @@ static void gp_stroke_soft_refine(bGPDstroke *gps)
}
/* eraser tool - evaluation per stroke */
-/* TODO: this could really do with some optimization (KD-Tree/BVH?) */
static void gp_stroke_eraser_dostroke(tGPsdata *p,
- bGPDlayer *gpl,
bGPDframe *gpf,
bGPDstroke *gps,
const float mval[2],
@@ -1652,21 +1359,15 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
const int radius,
const rcti *rect)
{
- Depsgraph *depsgraph = p->depsgraph;
- Object *obact = (Object *)p->ownerPtr.data;
Brush *eraser = p->eraser;
bGPDspoint *pt0, *pt1, *pt2;
int pc0[2] = {0};
int pc1[2] = {0};
int pc2[2] = {0};
int i;
- float diff_mat[4][4];
int mval_i[2];
round_v2i_v2fl(mval_i, mval);
- /* calculate difference matrix */
- ED_gpencil_parent_location(depsgraph, obact, p->gpd, gpl, diff_mat);
-
if (gps->totpoints == 0) {
/* just free stroke */
gp_free_stroke(p->gpd, gpf, gps);
@@ -1675,7 +1376,7 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
/* only process if it hasn't been masked out... */
if (!(p->flags & GP_PAINTFLAG_SELECTMASK) || (gps->points->flag & GP_SPOINT_SELECT)) {
bGPDspoint pt_temp;
- gp_point_to_parent_space(gps->points, diff_mat, &pt_temp);
+ gp_point_to_parent_space(gps->points, p->diff_mat, &pt_temp);
gp_point_to_xy(&p->gsc, gps, &pt_temp, &pc1[0], &pc1[1]);
/* do boundbox check first */
if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) {
@@ -1699,7 +1400,7 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
/* get points to work with */
pt1 = gps->points + i;
bGPDspoint npt;
- gp_point_to_parent_space(pt1, diff_mat, &npt);
+ gp_point_to_parent_space(pt1, p->diff_mat, &npt);
gp_point_to_xy(&p->gsc, gps, &npt, &pc1[0], &pc1[1]);
/* do boundbox check first */
@@ -1751,7 +1452,7 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
bGPDspoint npt;
if (pt0) {
- gp_point_to_parent_space(pt0, diff_mat, &npt);
+ gp_point_to_parent_space(pt0, p->diff_mat, &npt);
gp_point_to_xy(&p->gsc, gps, &npt, &pc0[0], &pc0[1]);
}
else {
@@ -1759,10 +1460,10 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
copy_v2_v2_int(pc0, pc1);
}
- gp_point_to_parent_space(pt1, diff_mat, &npt);
+ gp_point_to_parent_space(pt1, p->diff_mat, &npt);
gp_point_to_xy(&p->gsc, gps, &npt, &pc1[0], &pc1[1]);
- gp_point_to_parent_space(pt2, diff_mat, &npt);
+ gp_point_to_parent_space(pt2, p->diff_mat, &npt);
gp_point_to_xy(&p->gsc, gps, &npt, &pc2[0], &pc2[1]);
/* Check that point segment of the boundbox of the eraser stroke */
@@ -1862,8 +1563,6 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
/* erase strokes which fall under the eraser strokes */
static void gp_stroke_doeraser(tGPsdata *p)
{
- bGPDlayer *gpl;
- bGPDstroke *gps, *gpn;
rcti rect;
Brush *brush = p->brush;
Brush *eraser = p->eraser;
@@ -1903,29 +1602,36 @@ static void gp_stroke_doeraser(tGPsdata *p)
* only a subset of layers, it is harder to perform the same erase operation
* on multiple layers...
*/
- for (gpl = p->gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &p->gpd->layers) {
bGPDframe *gpf = gpl->actframe;
/* only affect layer if it's editable (and visible) */
- if (gpencil_layer_is_editable(gpl) == false) {
+ if (BKE_gpencil_layer_is_editable(gpl) == false) {
continue;
}
else if (gpf == NULL) {
continue;
}
+ /* calculate difference matrix */
+ BKE_gpencil_parent_matrix_get(p->depsgraph, p->ob, gpl, p->diff_mat);
/* loop over strokes, checking segments for intersections */
- for (gps = gpf->strokes.first; gps; gps = gpn) {
- gpn = gps->next;
+ LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
/* check if the color is editable */
if (ED_gpencil_stroke_color_use(p->ob, gpl, gps) == false) {
continue;
}
+
+ /* Check if the stroke collide with mouse. */
+ if (!ED_gpencil_stroke_check_collision(&p->gsc, gps, p->mval, calc_radius, p->diff_mat)) {
+ continue;
+ }
+
/* Not all strokes in the datablock may be valid in the current editor/context
* (e.g. 2D space strokes in the 3D view, if the same datablock is shared)
*/
if (ED_gpencil_stroke_can_use_direct(p->sa, gps)) {
- gp_stroke_eraser_dostroke(p, gpl, gpf, gps, p->mval, p->mvalo, calc_radius, &rect);
+ gp_stroke_eraser_dostroke(p, gpf, gps, p->mval, p->mvalo, calc_radius, &rect);
}
}
}
@@ -1964,7 +1670,7 @@ static Brush *gp_get_default_eraser(Main *bmain, ToolSettings *ts)
{
Brush *brush_dft = NULL;
Paint *paint = &ts->gp_paint->paint;
- Brush *brush_old = paint->brush;
+ Brush *brush_prev = paint->brush;
for (Brush *brush = bmain->brushes.first; brush; brush = brush->id.next) {
if (brush->gpencil_settings == NULL) {
continue;
@@ -1987,15 +1693,15 @@ static Brush *gp_get_default_eraser(Main *bmain, ToolSettings *ts)
}
/* create a new soft eraser brush */
else {
- brush_dft = BKE_brush_add_gpencil(bmain, ts, "Soft Eraser");
+ brush_dft = BKE_brush_add_gpencil(bmain, ts, "Soft Eraser", OB_MODE_PAINT_GPENCIL);
brush_dft->size = 30.0f;
- brush_dft->gpencil_settings->flag |= (GP_BRUSH_ENABLE_CURSOR | GP_BRUSH_DEFAULT_ERASER);
+ brush_dft->gpencil_settings->flag |= GP_BRUSH_DEFAULT_ERASER;
brush_dft->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_SOFT;
brush_dft->gpencil_tool = GPAINT_TOOL_ERASE;
brush_dft->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_SOFT;
/* reset current brush */
- BKE_paint_brush_set(paint, brush_old);
+ BKE_paint_brush_set(paint, brush_prev);
return brush_dft;
}
@@ -2032,7 +1738,7 @@ static void gp_init_drawing_brush(bContext *C, tGPsdata *p)
/* if not exist, create a new one */
if ((paint->brush == NULL) || (paint->brush->gpencil_settings == NULL)) {
/* create new brushes */
- BKE_brush_gpencil_presets(bmain, ts);
+ BKE_brush_gpencil_paint_presets(bmain, ts);
changed = true;
}
/* be sure curves are initializated */
@@ -2054,10 +1760,7 @@ static void gp_init_drawing_brush(bContext *C, tGPsdata *p)
/* use radius of eraser */
p->radius = (short)p->eraser->size;
- /* GPXX: Need this update to synchronize brush with draw manager.
- * Maybe this update can be removed when the new tool system
- * will be in place, but while, we need this to keep drawing working.
- */
+ /* Need this update to synchronize brush with draw manager. */
if (changed) {
DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
}
@@ -2069,33 +1772,11 @@ static void gp_init_colors(tGPsdata *p)
bGPdata *gpd = p->gpd;
Brush *brush = p->brush;
- MaterialGPencilStyle *gp_style = NULL;
-
/* use brush material */
p->material = BKE_gpencil_object_material_ensure_from_active_input_brush(p->bmain, p->ob, brush);
- /* assign color information to temp tGPsdata */
- gp_style = p->material->gp_style;
- if (gp_style) {
-
- /* set colors */
- if (gp_style->flag & GP_STYLE_STROKE_SHOW) {
- copy_v4_v4(gpd->runtime.scolor, gp_style->stroke_rgba);
- }
- else {
- /* if no stroke, use fill */
- copy_v4_v4(gpd->runtime.scolor, gp_style->fill_rgba);
- }
- copy_v4_v4(gpd->runtime.sfill, gp_style->fill_rgba);
- /* add some alpha to make easy the filling without hide strokes */
- if (gpd->runtime.sfill[3] > 0.8f) {
- gpd->runtime.sfill[3] = 0.8f;
- }
-
- gpd->runtime.mode = (short)gp_style->mode;
- gpd->runtime.bstroke_style = gp_style->stroke_style;
- gpd->runtime.bfill_style = gp_style->fill_style;
- }
+ gpd->runtime.matid = BKE_object_material_slot_find_index(p->ob, p->material);
+ gpd->runtime.sbuffer_brush = brush;
}
/* (re)init new painting data */
@@ -2131,9 +1812,6 @@ static bool gp_session_initdata(bContext *C, wmOperator *op, tGPsdata *p)
switch (curarea->spacetype) {
/* supported views first */
case SPACE_VIEW3D: {
- /* View3D *v3d = curarea->spacedata.first; */
- /* RegionView3D *rv3d = region->regiondata; */
-
/* set current area
* - must verify that region data is 3D-view (and not something else)
*/
@@ -2162,10 +1840,11 @@ static bool gp_session_initdata(bContext *C, wmOperator *op, tGPsdata *p)
local_view_bits = v3d->local_view_uuid;
}
/* create new default object */
- obact = ED_gpencil_add_object(C, p->scene, cur, local_view_bits);
+ obact = ED_gpencil_add_object(C, cur, local_view_bits);
}
/* assign object after all checks to be sure we have one active */
p->ob = obact;
+ p->ob_eval = (Object *)DEG_get_evaluated_object(p->depsgraph, p->ob);
break;
}
@@ -2204,21 +1883,14 @@ static bool gp_session_initdata(bContext *C, wmOperator *op, tGPsdata *p)
gp_init_drawing_brush(C, p);
/* setup active color */
- if (curarea->spacetype == SPACE_VIEW3D) {
- /* region where paint was originated */
- p->gpd->runtime.ar = CTX_wm_region(C);
-
- /* NOTE: This is only done for 3D view, as Materials aren't used for
- * annotations in 2D editors
- */
- int totcol = p->ob->totcol;
+ /* region where paint was originated */
+ p->gpd->runtime.ar = CTX_wm_region(C);
+ int totcol = p->ob->totcol;
+ gp_init_colors(p);
- gp_init_colors(p);
-
- /* check whether the material was newly added */
- if (totcol != p->ob->totcol) {
- WM_event_add_notifier(C, NC_SPACE | ND_SPACE_PROPERTIES, NULL);
- }
+ /* check whether the material was newly added */
+ if (totcol != p->ob->totcol) {
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_PROPERTIES, NULL);
}
/* lock axis (in some modes, disable) */
@@ -2274,7 +1946,6 @@ static void gp_session_cleanup(tGPsdata *p)
/* free stroke buffer */
if (gpd->runtime.sbuffer) {
- /* printf("\t\tGP - free sbuffer\n"); */
MEM_SAFE_FREE(gpd->runtime.sbuffer);
gpd->runtime.sbuffer = NULL;
}
@@ -2300,12 +1971,13 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps
{
Scene *scene = p->scene;
ToolSettings *ts = scene->toolsettings;
+ bool changed = false;
/* get active layer (or add a new one if non-existent) */
- p->gpl = BKE_gpencil_layer_getactive(p->gpd);
+ 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);
-
+ changed = true;
if (p->custom_color[3]) {
copy_v3_v3(p->gpl->color, p->custom_color);
}
@@ -2318,46 +1990,28 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps
return;
}
- /* get active frame (add a new one if not matching frame) */
+ /* Eraser mode: If no active strokes, just return. */
if (paintmode == GP_PAINTMODE_ERASER) {
- /* Eraser mode:
- * 1) Add new frames to all frames that we might touch,
- * 2) Ensure that p->gpf refers to the frame used for the active layer
- * (to avoid problems with other tools which expect it to exist)
- */
bool has_layer_to_erase = false;
- for (bGPDlayer *gpl = p->gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &p->gpd->layers) {
/* Skip if layer not editable */
- if (gpencil_layer_is_editable(gpl) == false) {
+ if (BKE_gpencil_layer_is_editable(gpl) == false) {
continue;
}
- /* Add a new frame if needed (and based off the active frame,
- * as we need some existing strokes to erase)
- *
- * Note: We don't add a new frame if there's nothing there now, so
- * -> If there are no frames at all, don't add one
- * -> If there are no strokes in that frame, don't add a new empty frame
- */
if (gpl->actframe && gpl->actframe->strokes.first) {
- gpl->actframe = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_ADD_COPY);
has_layer_to_erase = true;
+ break;
}
-
- /* XXX: we omit GP_FRAME_PAINT here for now,
- * as it is only really useful for doing
- * paintbuffer drawing
- */
}
- /* Ensure this gets set... */
- p->gpf = p->gpl->actframe;
-
if (has_layer_to_erase == false) {
p->status = GP_STATUS_ERROR;
return;
}
+ /* Ensure this gets set... */
+ p->gpf = p->gpl->actframe;
}
else {
/* Drawing Modes - Add a new frame if needed on the active layer */
@@ -2370,7 +2024,7 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps
add_frame_mode = GP_GETFRAME_ADD_NEW;
}
- p->gpf = BKE_gpencil_layer_getframe(p->gpl, CFRA, add_frame_mode);
+ p->gpf = BKE_gpencil_layer_frame_get(p->gpl, CFRA, add_frame_mode);
if (p->gpf == NULL) {
p->status = GP_STATUS_ERROR;
@@ -2397,8 +2051,6 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps
/* set special fill stroke mode */
if (p->disable_fill == true) {
p->gpd->runtime.sbuffer_sflag |= GP_STROKE_NOFILL;
- /* replace stroke color with fill color */
- copy_v4_v4(p->gpd->runtime.scolor, p->gpd->runtime.sfill);
}
/* set 'initial run' flag, which is only used to denote when a new stroke is starting */
@@ -2443,6 +2095,14 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps
}
}
}
+ if (!changed) {
+ /* Copy the brush to avoid a full tag (very slow). */
+ bGPdata *gpd_eval = (bGPdata *)p->ob_eval->data;
+ gpd_eval->runtime.sbuffer_brush = p->gpd->runtime.sbuffer_brush;
+ }
+ else {
+ gp_update_cache(p->gpd);
+ }
}
/* finish off a stroke (clears buffer, but doesn't finish the paint operation) */
@@ -2487,7 +2147,6 @@ static void gp_paint_cleanup(tGPsdata *p)
p->gpf->flag &= ~GP_FRAME_PAINT;
}
}
-
/* ------------------------------- */
/* Helper callback for drawing the cursor itself */
@@ -2591,10 +2250,7 @@ static void gpencil_draw_exit(bContext *C, wmOperator *op)
else {
/* drawing batch cache is dirty now */
bGPdata *gpd = CTX_data_gpencil_data(C);
- if (gpd) {
- gpd->flag &= ~GP_DATA_STROKE_POLYGON;
- gp_update_cache(gpd);
- }
+ gp_update_cache(gpd);
}
/* clear undo stack */
@@ -2665,23 +2321,6 @@ static int gpencil_draw_init(bContext *C, wmOperator *op, const wmEvent *event)
/* ------------------------------- */
-/* ensure that the correct cursor icon is set */
-static void gpencil_draw_cursor_set(tGPsdata *p)
-{
- UNUSED_VARS(p);
- return;
- /* Disable while we get a better cursor handling for direct input devices (Cintiq/Ipad)*/
-#if 0
- Brush *brush = p->brush;
- if ((p->paintmode == GP_PAINTMODE_ERASER) || (brush->gpencil_tool == GPAINT_TOOL_ERASE)) {
- WM_cursor_modal_set(p->win, WM_CURSOR_CROSS); /* XXX need a better cursor */
- }
- else {
- WM_cursor_modal_set(p->win, WM_CURSOR_NONE);
- }
-#endif
-}
-
/* update UI indicators of status, including cursor and header prints */
static void gpencil_draw_status_indicators(bContext *C, tGPsdata *p)
{
@@ -2724,13 +2363,6 @@ static void gpencil_draw_status_indicators(bContext *C, tGPsdata *p)
}
break;
}
- case GP_PAINTMODE_DRAW_POLY: {
- ED_workspace_status_text(
- C,
- TIP_("Grease Pencil Poly Session: LMB click to place next stroke vertex | "
- "Release Shift/ESC/Enter to end (or click outside this area)"));
- break;
- }
default: /* unhandled future cases */
{
ED_workspace_status_text(
@@ -2753,88 +2385,6 @@ static void gpencil_draw_status_indicators(bContext *C, tGPsdata *p)
/* ------------------------------- */
-/* create a new stroke point at the point indicated by the painting context */
-static void gpencil_draw_apply(
- bContext *C, wmOperator *op, tGPsdata *p, Depsgraph *depsgraph, bool is_fake)
-{
- bGPdata *gpd = p->gpd;
- tGPspoint *pt = NULL;
-
- /* handle drawing/erasing -> test for erasing first */
- if (p->paintmode == GP_PAINTMODE_ERASER) {
- /* do 'live' erasing now */
- gp_stroke_doeraser(p);
-
- /* store used values */
- copy_v2_v2(p->mvalo, p->mval);
- p->opressure = p->pressure;
- }
- /* Only add current point to buffer if mouse moved
- * (even though we got an event, it might be just noise). */
- else if (gp_stroke_filtermval(p, p->mval, p->mvalo)) {
-
- /* if lazy mouse, interpolate the last and current mouse positions */
- if (GPENCIL_LAZY_MODE(p->brush, p->shift)) {
- float now_mouse[2];
- float last_mouse[2];
- copy_v2_v2(now_mouse, p->mval);
- copy_v2_v2(last_mouse, p->mvalo);
- interp_v2_v2v2(now_mouse, now_mouse, last_mouse, p->brush->smooth_stroke_factor);
- copy_v2_v2(p->mval, now_mouse);
- }
-
- /* try to add point */
- short ok = gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime, is_fake);
-
- /* handle errors while adding point */
- if ((ok == GP_STROKEADD_FULL) || (ok == GP_STROKEADD_OVERFLOW)) {
- /* finish off old stroke */
- gp_paint_strokeend(p);
- /* And start a new one!!! Else, projection errors! */
- gp_paint_initstroke(p, p->paintmode, depsgraph);
-
- /* start a new stroke, starting from previous point */
- /* XXX Must manually reset inittime... */
- /* XXX We only need to reuse previous point if overflow! */
- if (ok == GP_STROKEADD_OVERFLOW) {
- p->inittime = p->ocurtime;
- gp_stroke_addpoint(p, p->mvalo, p->opressure, p->ocurtime, is_fake);
- }
- else {
- p->inittime = p->curtime;
- }
- gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime, is_fake);
- }
- else if (ok == GP_STROKEADD_INVALID) {
- /* the painting operation cannot continue... */
- BKE_report(op->reports, RPT_ERROR, "Cannot paint stroke");
- p->status = GP_STATUS_ERROR;
-
- if (G.debug & G_DEBUG) {
- printf("Error: Grease-Pencil Paint - Add Point Invalid\n");
- }
- return;
- }
-
- /* store used values */
- copy_v2_v2(p->mvalo, p->mval);
- p->opressure = p->pressure;
- p->ocurtime = p->curtime;
-
- pt = (tGPspoint *)gpd->runtime.sbuffer + gpd->runtime.sbuffer_used - 1;
- if (p->paintmode != GP_PAINTMODE_ERASER) {
- ED_gpencil_toggle_brush_cursor(C, true, &pt->x);
- }
- }
- else if ((p->brush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP) &&
- (gpd->runtime.sbuffer_used > 0)) {
- pt = (tGPspoint *)gpd->runtime.sbuffer + gpd->runtime.sbuffer_used - 1;
- if (p->paintmode != GP_PAINTMODE_ERASER) {
- ED_gpencil_toggle_brush_cursor(C, true, &pt->x);
- }
- }
-}
-
/* Helper to rotate point around origin */
static void gp_rotate_v2_v2v2fl(float v[2],
const float p[2],
@@ -2956,66 +2506,151 @@ static void gpencil_speed_guide_init(tGPsdata *p, GP_Sculpt_Guide *guide)
}
/* apply speed guide */
-static void gpencil_speed_guide(tGPsdata *p, GP_Sculpt_Guide *guide)
+static void gpencil_snap_to_guide(const tGPsdata *p, const GP_Sculpt_Guide *guide, float point[2])
{
switch (guide->type) {
default:
case GP_GUIDE_CIRCULAR: {
- dist_ensure_v2_v2fl(p->mval, p->guide.origin, p->guide.origin_distance);
+ dist_ensure_v2_v2fl(point, p->guide.origin, p->guide.origin_distance);
break;
}
case GP_GUIDE_RADIAL: {
if (guide->use_snapping && (guide->angle_snap > 0.0f)) {
- closest_to_line_v2(p->mval, p->mval, p->guide.rot_point, p->guide.origin);
+ closest_to_line_v2(point, point, p->guide.rot_point, p->guide.origin);
}
else {
- closest_to_line_v2(p->mval, p->mval, p->mvali, p->guide.origin);
+ closest_to_line_v2(point, point, p->mvali, p->guide.origin);
}
break;
}
case GP_GUIDE_PARALLEL: {
- closest_to_line_v2(p->mval, p->mval, p->mvali, p->guide.rot_point);
+ closest_to_line_v2(point, point, p->mvali, p->guide.rot_point);
if (guide->use_snapping && (guide->spacing > 0.0f)) {
- gp_snap_to_rotated_grid_fl(p->mval, p->guide.origin, p->guide.spacing, guide->angle);
+ gp_snap_to_rotated_grid_fl(point, p->guide.origin, p->guide.spacing, guide->angle);
}
break;
}
case GP_GUIDE_ISO: {
- closest_to_line_v2(p->mval, p->mval, p->mvali, p->guide.rot_point);
+ closest_to_line_v2(point, point, p->mvali, p->guide.rot_point);
if (guide->use_snapping && (guide->spacing > 0.0f)) {
- gp_snap_to_rotated_grid_fl(p->mval, p->guide.origin, p->guide.spacing, p->guide.rot_angle);
+ gp_snap_to_rotated_grid_fl(point, p->guide.origin, p->guide.spacing, p->guide.rot_angle);
}
break;
}
case GP_GUIDE_GRID: {
if (guide->use_snapping && (guide->spacing > 0.0f)) {
- closest_to_line_v2(p->mval, p->mval, p->mvali, p->guide.rot_point);
+ closest_to_line_v2(point, point, p->mvali, p->guide.rot_point);
if (p->straight == STROKE_HORIZONTAL) {
- p->mval[1] = gp_snap_to_grid_fl(p->mval[1], p->guide.origin[1], p->guide.spacing);
+ point[1] = gp_snap_to_grid_fl(point[1], p->guide.origin[1], p->guide.spacing);
}
else {
- p->mval[0] = gp_snap_to_grid_fl(p->mval[0], p->guide.origin[0], p->guide.spacing);
+ point[0] = gp_snap_to_grid_fl(point[0], p->guide.origin[0], p->guide.spacing);
}
}
else if (p->straight == STROKE_HORIZONTAL) {
- p->mval[1] = p->mvali[1]; /* replace y */
+ point[1] = p->mvali[1]; /* replace y */
}
else {
- p->mval[0] = p->mvali[0]; /* replace x */
+ point[0] = p->mvali[0]; /* replace x */
}
break;
}
}
}
+/* create a new stroke point at the point indicated by the painting context */
+static void gpencil_draw_apply(bContext *C, wmOperator *op, tGPsdata *p, Depsgraph *depsgraph)
+{
+ bGPdata *gpd = p->gpd;
+ tGPspoint *pt = NULL;
+
+ /* handle drawing/erasing -> test for erasing first */
+ if (p->paintmode == GP_PAINTMODE_ERASER) {
+ /* do 'live' erasing now */
+ gp_stroke_doeraser(p);
+
+ /* store used values */
+ copy_v2_v2(p->mvalo, p->mval);
+ p->opressure = p->pressure;
+ }
+ /* Only add current point to buffer if mouse moved
+ * (even though we got an event, it might be just noise). */
+ else if (gp_stroke_filtermval(p, p->mval, p->mvalo)) {
+
+ /* if lazy mouse, interpolate the last and current mouse positions */
+ if (GPENCIL_LAZY_MODE(p->brush, p->shift)) {
+ float now_mouse[2];
+ float last_mouse[2];
+ copy_v2_v2(now_mouse, p->mval);
+ copy_v2_v2(last_mouse, p->mvalo);
+ interp_v2_v2v2(now_mouse, now_mouse, last_mouse, p->brush->smooth_stroke_factor);
+ copy_v2_v2(p->mval, now_mouse);
+
+ GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide;
+ bool is_speed_guide = ((guide->use_guide) &&
+ (p->brush && (p->brush->gpencil_tool == GPAINT_TOOL_DRAW)));
+ if (is_speed_guide) {
+ gpencil_snap_to_guide(p, guide, p->mval);
+ }
+ }
+
+ /* try to add point */
+ short ok = gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime);
+
+ /* handle errors while adding point */
+ if ((ok == GP_STROKEADD_FULL) || (ok == GP_STROKEADD_OVERFLOW)) {
+ /* finish off old stroke */
+ gp_paint_strokeend(p);
+ /* And start a new one!!! Else, projection errors! */
+ gp_paint_initstroke(p, p->paintmode, depsgraph);
+
+ /* start a new stroke, starting from previous point */
+ /* XXX Must manually reset inittime... */
+ /* XXX We only need to reuse previous point if overflow! */
+ if (ok == GP_STROKEADD_OVERFLOW) {
+ p->inittime = p->ocurtime;
+ gp_stroke_addpoint(p, p->mvalo, p->opressure, p->ocurtime);
+ }
+ else {
+ p->inittime = p->curtime;
+ }
+ gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime);
+ }
+ else if (ok == GP_STROKEADD_INVALID) {
+ /* the painting operation cannot continue... */
+ BKE_report(op->reports, RPT_ERROR, "Cannot paint stroke");
+ p->status = GP_STATUS_ERROR;
+
+ if (G.debug & G_DEBUG) {
+ printf("Error: Grease-Pencil Paint - Add Point Invalid\n");
+ }
+ return;
+ }
+
+ /* store used values */
+ copy_v2_v2(p->mvalo, p->mval);
+ p->opressure = p->pressure;
+ p->ocurtime = p->curtime;
+
+ pt = (tGPspoint *)gpd->runtime.sbuffer + gpd->runtime.sbuffer_used - 1;
+ if (p->paintmode != GP_PAINTMODE_ERASER) {
+ ED_gpencil_toggle_brush_cursor(C, true, &pt->x);
+ }
+ }
+ else if ((p->brush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP) &&
+ (gpd->runtime.sbuffer_used > 0)) {
+ pt = (tGPspoint *)gpd->runtime.sbuffer + gpd->runtime.sbuffer_used - 1;
+ if (p->paintmode != GP_PAINTMODE_ERASER) {
+ ED_gpencil_toggle_brush_cursor(C, true, &pt->x);
+ }
+ }
+}
+
/* handle draw event */
static void gpencil_draw_apply_event(bContext *C,
wmOperator *op,
const wmEvent *event,
- Depsgraph *depsgraph,
- float x,
- float y,
- const bool is_fake)
+ Depsgraph *depsgraph)
{
tGPsdata *p = op->customdata;
GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide;
@@ -3025,13 +2660,12 @@ static void gpencil_draw_apply_event(bContext *C,
(p->brush && (p->brush->gpencil_tool == GPAINT_TOOL_DRAW)));
/* convert from window-space to area-space mouse coordinates
- * add any x,y override position for fake events
+ * add any x,y override position
*/
- p->mval[0] = (float)event->mval[0] - x;
- p->mval[1] = (float)event->mval[1] - y;
+ copy_v2fl_v2i(p->mval, event->mval);
p->shift = event->shift;
- /* verify direction for straight lines */
+ /* verify direction for straight lines and guides */
if ((is_speed_guide) ||
((event->alt > 0) && (RNA_boolean_get(op->ptr, "disable_straight") == false))) {
if (p->straight == 0) {
@@ -3099,12 +2733,12 @@ static void gpencil_draw_apply_event(bContext *C,
p->flags &= ~GP_PAINTFLAG_FIRSTRUN;
/* set values */
- copy_v2_v2(p->mvalo, p->mval);
p->opressure = p->pressure;
p->inittime = p->ocurtime = p->curtime;
p->straight = 0;
/* save initial mouse */
+ copy_v2_v2(p->mvalo, p->mval);
copy_v2_v2(p->mvali, p->mval);
if (is_speed_guide && !ELEM(p->paintmode, GP_PAINTMODE_ERASER, GP_PAINTMODE_SET_CP) &&
@@ -3120,7 +2754,7 @@ static void gpencil_draw_apply_event(bContext *C,
}
/* wait for vector then add initial point */
- if (is_speed_guide && p->flags & GP_PAINTFLAG_REQ_VECTOR) {
+ if (is_speed_guide && (p->flags & GP_PAINTFLAG_REQ_VECTOR)) {
if (p->straight == 0) {
return;
}
@@ -3154,23 +2788,13 @@ static void gpencil_draw_apply_event(bContext *C,
p->mvali,
(p->straight == STROKE_VERTICAL) ? M_PI_2 : 0.0f);
}
-
- /* create fake events */
- float tmp[2];
- copy_v2_v2(tmp, p->mval);
- gpencil_draw_apply_event(C, op, event, depsgraph, pt[0], pt[1], false);
- if (len_v2v2(p->mval, p->mvalo)) {
- sub_v2_v2v2(pt, p->mval, p->mvalo);
- gpencil_draw_apply_event(C, op, event, depsgraph, pt[0], pt[1], false);
- }
- copy_v2_v2(p->mval, tmp);
}
/* check if stroke is straight or guided */
if ((p->paintmode != GP_PAINTMODE_ERASER) && ((p->straight) || (is_speed_guide))) {
/* guided stroke */
if (is_speed_guide) {
- gpencil_speed_guide(p, guide);
+ gpencil_snap_to_guide(p, guide, p->mval);
}
else if (p->straight == STROKE_HORIZONTAL) {
p->mval[1] = p->mvali[1]; /* replace y */
@@ -3192,7 +2816,7 @@ static void gpencil_draw_apply_event(bContext *C,
RNA_float_set(&itemptr, "time", p->curtime - p->inittime);
/* apply the current latest drawing point */
- gpencil_draw_apply(C, op, p, depsgraph, is_fake);
+ gpencil_draw_apply(C, op, p, depsgraph);
/* force refresh */
/* just active area for now, since doing whole screen is too slow */
@@ -3207,28 +2831,21 @@ static int gpencil_draw_exec(bContext *C, wmOperator *op)
tGPsdata *p = NULL;
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- /* printf("GPencil - Starting Re-Drawing\n"); */
-
/* try to initialize context data needed while drawing */
if (!gpencil_draw_init(C, op, NULL)) {
MEM_SAFE_FREE(op->customdata);
- /* printf("\tGP - no valid data\n"); */
return OPERATOR_CANCELLED;
}
else {
p = op->customdata;
}
- /* printf("\tGP - Start redrawing stroke\n"); */
-
/* loop over the stroke RNA elements recorded (i.e. progress of mouse movement),
* setting the relevant values in context at each step, then applying
*/
RNA_BEGIN (op->ptr, itemptr, "stroke") {
float mousef[2];
- /* printf("\t\tGP - stroke elem\n"); */
-
/* get relevant data for this point from stroke */
RNA_float_get_array(&itemptr, "mouse", mousef);
p->mval[0] = mousef[0];
@@ -3258,12 +2875,10 @@ static int gpencil_draw_exec(bContext *C, wmOperator *op)
}
/* apply this data as necessary now (as per usual) */
- gpencil_draw_apply(C, op, p, depsgraph, false);
+ gpencil_draw_apply(C, op, p, depsgraph);
}
RNA_END;
- /* printf("\tGP - done\n"); */
-
/* cleanup */
gpencil_draw_exit(C, op);
@@ -3400,9 +3015,9 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event
else {
/* don't erase empty frames */
bool has_layer_to_erase = false;
- for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
/* Skip if layer not editable */
- if (gpencil_layer_is_editable(gpl)) {
+ if (BKE_gpencil_layer_is_editable(gpl)) {
if (gpl->actframe && gpl->actframe->strokes.first) {
has_layer_to_erase = true;
break;
@@ -3430,35 +3045,25 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event
}
/* TODO: set any additional settings that we can take from the events?
- * TODO? if tablet is erasing, force eraser to be on? */
-
- /* if eraser is on, draw radial aid */
+ * if eraser is on, draw radial aid */
if (p->paintmode == GP_PAINTMODE_ERASER) {
gpencil_draw_toggle_eraser_cursor(C, p, true);
}
else {
ED_gpencil_toggle_brush_cursor(C, true, NULL);
}
- /* set cursor
- * NOTE: This may change later (i.e. intentionally via brush toggle,
- * or unintentionally if the user scrolls outside the area)...
- */
- gpencil_draw_cursor_set(p);
/* only start drawing immediately if we're allowed to do so... */
if (RNA_boolean_get(op->ptr, "wait_for_input") == false) {
/* hotkey invoked - start drawing */
- /* printf("\tGP - set first spot\n"); */
p->status = GP_STATUS_PAINTING;
/* handle the initial drawing - i.e. for just doing a simple dot */
- gpencil_draw_apply_event(
- C, op, event, CTX_data_ensure_evaluated_depsgraph(C), 0.0f, 0.0f, false);
+ gpencil_draw_apply_event(C, op, event, CTX_data_ensure_evaluated_depsgraph(C));
op->flag |= OP_IS_MODAL_CURSOR_REGION;
}
else {
/* toolbar invoked - don't start drawing yet... */
- /* printf("\tGP - hotkey invoked... waiting for click-drag\n"); */
op->flag |= OP_IS_MODAL_CURSOR_REGION;
}
@@ -3510,12 +3115,7 @@ static tGPsdata *gpencil_stroke_begin(bContext *C, wmOperator *op)
p->status = GP_STATUS_ERROR;
}
- /* printf("\t\tGP - start stroke\n"); */
-
/* we may need to set up paint env again if we're resuming */
- /* XXX: watch it with the paintmode! in future,
- * it'd be nice to allow changing paint-mode when in sketching-sessions */
-
if (gp_session_initdata(C, op, p)) {
gp_paint_initstroke(p, p->paintmode, CTX_data_depsgraph_pointer(C));
}
@@ -3528,134 +3128,222 @@ static tGPsdata *gpencil_stroke_begin(bContext *C, wmOperator *op)
return op->customdata;
}
-static void gpencil_stroke_end(wmOperator *op)
+/* Add arc points between two mouse events using the previous segment to determine the vertice of
+ * the arc.
+ * /+ CTL
+ * / |
+ * / |
+ * PtA +...|...+ PtB
+ * /
+ * /
+ * + PtA - 1
+ * /
+ * CTL is the vertice of the triangle created between PtA and PtB */
+static void gpencil_add_arc_points(tGPsdata *p, float mval[2], int segments)
{
- tGPsdata *p = op->customdata;
+ bGPdata *gpd = p->gpd;
+ if (gpd->runtime.sbuffer_used < 3) {
+ return;
+ }
+
+ int idx_prev = gpd->runtime.sbuffer_used;
+
+ /* Add space for new arc points. */
+ gpd->runtime.sbuffer_used += segments - 1;
+
+ /* Check if still room in buffer or add more. */
+ gpd->runtime.sbuffer = ED_gpencil_sbuffer_ensure(
+ gpd->runtime.sbuffer, &gpd->runtime.sbuffer_size, &gpd->runtime.sbuffer_used, false);
+
+ tGPspoint *points = (tGPspoint *)gpd->runtime.sbuffer;
+ tGPspoint *pt = NULL;
+ tGPspoint *pt_before = &points[idx_prev - 1]; /* current - 2 */
+ tGPspoint *pt_prev = &points[idx_prev - 2]; /* previous */
+
+ /* Create two vectors, previous and half way of the actual to get the vertex of the triangle
+ * for arc curve.
+ */
+ float v_prev[2], v_cur[2], v_half[2];
+ sub_v2_v2v2(v_cur, mval, &pt_prev->x);
+
+ sub_v2_v2v2(v_prev, &pt_prev->x, &pt_before->x);
+ interp_v2_v2v2(v_half, &pt_prev->x, mval, 0.5f);
+ sub_v2_v2(v_half, &pt_prev->x);
+
+ /* If angle is too sharp undo all changes and return. */
+ const float min_angle = DEG2RADF(120.0f);
+ float angle = angle_v2v2(v_prev, v_half);
+ if (angle < min_angle) {
+ gpd->runtime.sbuffer_used -= segments - 1;
+ return;
+ }
+
+ /* Project the half vector to the previous vector and calculate the mid projected point. */
+ float dot = dot_v2v2(v_prev, v_half);
+ float l = len_squared_v2(v_prev);
+ if (l > 0.0f) {
+ mul_v2_fl(v_prev, dot / l);
+ }
- gp_paint_cleanup(p);
+ /* Calc the position of the control point. */
+ float ctl[2];
+ add_v2_v2v2(ctl, &pt_prev->x, v_prev);
- gpencil_undo_push(p->gpd);
+ float step = M_PI_2 / (float)(segments + 1);
+ float a = step;
- gp_session_cleanup(p);
+ float midpoint[2], start[2], end[2], cp1[2], corner[2];
+ mid_v2_v2v2(midpoint, &pt_prev->x, mval);
+ copy_v2_v2(start, &pt_prev->x);
+ copy_v2_v2(end, mval);
+ copy_v2_v2(cp1, ctl);
- p->status = GP_STATUS_IDLING;
- op->flag |= OP_IS_MODAL_CURSOR_REGION;
+ corner[0] = midpoint[0] - (cp1[0] - midpoint[0]);
+ corner[1] = midpoint[1] - (cp1[1] - midpoint[1]);
- p->gpd = NULL;
- p->gpl = NULL;
- p->gpf = NULL;
+ for (int i = 0; i < segments; i++) {
+ pt = &points[idx_prev + i - 1];
+ pt->x = corner[0] + (end[0] - corner[0]) * sinf(a) + (start[0] - corner[0]) * cosf(a);
+ pt->y = corner[1] + (end[1] - corner[1]) * sinf(a) + (start[1] - corner[1]) * cosf(a);
+
+ /* Set pressure and strength equals to previous. It will be smoothed later. */
+ pt->pressure = pt_prev->pressure;
+ pt->strength = pt_prev->strength;
+
+ a += step;
+ }
}
-/* Move last stroke in the listbase to the head
- * to be drawn below all previous strokes in the layer. */
-static void gpencil_move_last_stroke_to_back(bContext *C)
+static void gpencil_add_guide_points(const tGPsdata *p,
+ const GP_Sculpt_Guide *guide,
+ const float start[2],
+ const float end[2],
+ int segments)
{
- /* Move last stroke (the polygon) to head of the listbase stroke
- * to draw on back of all previous strokes. */
- bGPdata *gpd = ED_gpencil_data_get_active(C);
- bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
-
- /* sanity checks */
- if (ELEM(NULL, gpd, gpl, gpl->actframe)) {
+ bGPdata *gpd = p->gpd;
+ if ((gpd->runtime.sbuffer_used == 0)) {
return;
}
- bGPDframe *gpf = gpl->actframe;
- bGPDstroke *gps = gpf->strokes.last;
- if (ELEM(NULL, gps)) {
- return;
+ int idx_prev = gpd->runtime.sbuffer_used;
+
+ /* Add space for new points. */
+ gpd->runtime.sbuffer_used += segments - 1;
+
+ /* Check if still room in buffer or add more. */
+ gpd->runtime.sbuffer = ED_gpencil_sbuffer_ensure(
+ gpd->runtime.sbuffer, &gpd->runtime.sbuffer_size, &gpd->runtime.sbuffer_used, false);
+
+ tGPspoint *points = (tGPspoint *)gpd->runtime.sbuffer;
+ tGPspoint *pt = NULL;
+ tGPspoint *pt_before = &points[idx_prev - 1];
+
+ /* Use arc sampling for circular guide */
+ if (guide->type == GP_GUIDE_CIRCULAR) {
+ float cw = cross_tri_v2(start, p->guide.origin, end);
+ float angle = angle_v2v2v2(start, p->guide.origin, end);
+
+ float step = angle / (float)(segments + 1);
+ if (cw < 0.0f) {
+ step = -step;
+ }
+
+ float a = step;
+
+ for (int i = 0; i < segments; i++) {
+ pt = &points[idx_prev + i - 1];
+
+ gp_rotate_v2_v2v2fl(&pt->x, start, p->guide.origin, -a);
+ gpencil_snap_to_guide(p, guide, &pt->x);
+ a += step;
+
+ /* Set pressure and strength equals to previous. It will be smoothed later. */
+ pt->pressure = pt_before->pressure;
+ pt->strength = pt_before->strength;
+ }
}
+ else {
+ float step = 1.0f / (float)(segments + 1);
+ float a = step;
+
+ for (int i = 0; i < segments; i++) {
+ pt = &points[idx_prev + i - 1];
+
+ interp_v2_v2v2(&pt->x, start, end, a);
+ gpencil_snap_to_guide(p, guide, &pt->x);
+ a += step;
- BLI_remlink(&gpf->strokes, gps);
- BLI_insertlinkbefore(&gpf->strokes, gpf->strokes.first, gps);
+ /* Set pressure and strength equals to previous. It will be smoothed later. */
+ pt->pressure = pt_before->pressure;
+ pt->strength = pt_before->strength;
+ }
+ }
}
-/* Add fake events for missing mouse movements when the artist draw very fast */
-static bool gpencil_add_fake_events(bContext *C, wmOperator *op, const wmEvent *event, tGPsdata *p)
+/* Add fake points for missing mouse movements when the artist draw very fast creating an arc
+ * with the vertice in the midle of the segment and using the angle of the previous segment. */
+static void gpencil_add_fake_points(const wmEvent *event, tGPsdata *p)
{
Brush *brush = p->brush;
+ /* Lazy mode do not use fake events. */
+ if (GPENCIL_LAZY_MODE(brush, p->shift)) {
+ return;
+ }
+
GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide;
- Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
int input_samples = brush->gpencil_settings->input_samples;
- bool added_events = false;
- /* ensure sampling when using circular guide */
- if (guide->use_guide && (guide->type == GP_GUIDE_CIRCULAR)) {
+ bool is_speed_guide = ((guide->use_guide) &&
+ (p->brush && (p->brush->gpencil_tool == GPAINT_TOOL_DRAW)));
+
+ /* TODO: ensure sampling enough points when using circular guide,
+ but the arc must be around the center. (see if above to check other guides only)
+ */
+ if (is_speed_guide && (guide->type == GP_GUIDE_CIRCULAR)) {
input_samples = GP_MAX_INPUT_SAMPLES;
}
if (input_samples == 0) {
- return added_events;
+ return;
}
- RegionView3D *rv3d = p->region->regiondata;
- float defaultpixsize = rv3d->pixsize * 1000.0f;
- int samples = (GP_MAX_INPUT_SAMPLES - input_samples + 1);
- float thickness = (float)brush->size;
+ int samples = GP_MAX_INPUT_SAMPLES - input_samples + 1;
- float pt[2], a[2], b[2];
- float vec[3];
- float scale = 1.0f;
+ float mouse_prv[2], mouse_cur[2];
+ float min_dist = 4.0f * samples;
- /* get pixel scale */
- gp_get_3d_reference(p, vec);
- mul_m4_v3(rv3d->persmat, vec);
- if (rv3d->is_persp) {
- scale = vec[2] * defaultpixsize;
- }
- else {
- scale = defaultpixsize;
- }
+ copy_v2_v2(mouse_prv, p->mvalo);
+ copy_v2fl_v2i(mouse_cur, event->mval);
- /* The thickness of the brush is reduced of thickness to get overlap dots */
- float dot_factor = 0.50f;
- if (samples < 2) {
- dot_factor = 0.05f;
- }
- else if (samples < 4) {
- dot_factor = 0.10f;
- }
- else if (samples < 7) {
- dot_factor = 0.3f;
- }
- else if (samples < 10) {
- dot_factor = 0.4f;
+ /* get distance in pixels */
+ float dist = len_v2v2(mouse_prv, mouse_cur);
+
+ /* get distance for circular guide */
+ if (is_speed_guide && (guide->type == GP_GUIDE_CIRCULAR)) {
+ float middle[2];
+ gpencil_snap_to_guide(p, guide, mouse_prv);
+ gpencil_snap_to_guide(p, guide, mouse_cur);
+ mid_v2_v2v2(middle, mouse_cur, mouse_prv);
+ gpencil_snap_to_guide(p, guide, middle);
+ dist = len_v2v2(mouse_prv, middle) + len_v2v2(middle, mouse_cur);
}
- float factor = ((thickness * dot_factor) / scale) * samples;
- copy_v2_v2(a, p->mvalo);
- b[0] = (float)event->mval[0] + 1.0f;
- b[1] = (float)event->mval[1] + 1.0f;
+ if ((dist > 3.0f) && (dist > min_dist)) {
+ int slices = (dist / min_dist) + 1;
- /* get distance in pixels */
- float dist = len_v2v2(a, b);
-
- /* for very small distances, add a half way point */
- if (dist <= 2.0f) {
- interp_v2_v2v2(pt, a, b, 0.5f);
- sub_v2_v2v2(pt, b, pt);
- /* create fake event */
- gpencil_draw_apply_event(C, op, event, depsgraph, pt[0], pt[1], true);
- added_events = true;
- }
- else if (dist >= factor) {
- int slices = 2 + (int)((dist - 1.0) / factor);
- float n = 1.0f / slices;
- for (int i = 1; i < slices; i++) {
- interp_v2_v2v2(pt, a, b, n * i);
- sub_v2_v2v2(pt, b, pt);
- /* create fake event */
- gpencil_draw_apply_event(C, op, event, depsgraph, pt[0], pt[1], true);
- added_events = true;
- }
- }
- return added_events;
+ if (is_speed_guide) {
+ gpencil_add_guide_points(p, guide, mouse_prv, mouse_cur, slices);
+ }
+ else {
+ gpencil_add_arc_points(p, mouse_cur, slices);
+ }
+ }
}
/* events handling during interactive drawing part of operator */
static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
tGPsdata *p = op->customdata;
- ToolSettings *ts = CTX_data_tool_settings(C);
+ // ToolSettings *ts = CTX_data_tool_settings(C);
GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide;
/* default exit state - pass through to support MMB view nav, etc. */
@@ -3754,98 +3442,26 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
}
- // printf("\tGP - handle modal event...\n");
-
/* Exit painting mode (and/or end current stroke).
*
- * NOTE: cannot do RIGHTMOUSE (as is standard for canceling)
- * as that would break polyline T32647.
*/
- /* if polyline and release shift must cancel */
- if ((ELEM(event->type, RETKEY, PADENTER, ESCKEY, SPACEKEY, EKEY)) ||
- ((p->paintmode == GP_PAINTMODE_DRAW_POLY) && (event->shift == 0))) {
- /* exit() ends the current stroke before cleaning up */
- /* printf("\t\tGP - end of paint op + end of stroke\n"); */
- /* if drawing polygon and enable on back, must move stroke */
- if (ts) {
- if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) &&
- (p->paintmode == GP_PAINTMODE_DRAW_POLY)) {
- if (p->flags & GP_PAINTFLAG_STROKEADDED) {
- gpencil_move_last_stroke_to_back(C);
- }
- }
- }
+ if (ELEM(event->type, RETKEY, PADENTER, ESCKEY, SPACEKEY, EKEY)) {
p->status = GP_STATUS_DONE;
estate = OPERATOR_FINISHED;
}
/* toggle painting mode upon mouse-button movement
- * - LEFTMOUSE = standard drawing (all) / straight line drawing (all) / polyline (toolbox
- * only)
- * - RIGHTMOUSE = polyline (hotkey) / eraser (all)
+ * - LEFTMOUSE = standard drawing (all) / straight line drawing (all)
+ * - RIGHTMOUSE = eraser (all)
* (Disabling RIGHTMOUSE case here results in bugs like [#32647])
* also making sure we have a valid event value, to not exit too early
*/
if (ELEM(event->type, LEFTMOUSE, RIGHTMOUSE) && (ELEM(event->val, KM_PRESS, KM_RELEASE))) {
/* if painting, end stroke */
if (p->status == GP_STATUS_PAINTING) {
- int sketch = 0;
-
- /* basically, this should be mouse-button up = end stroke
- * BUT, polyline drawing is an exception -- all knots should be added during one session
- */
- sketch |= (p->paintmode == GP_PAINTMODE_DRAW_POLY);
-
- if (sketch) {
- /* end stroke only, and then wait to resume painting soon */
- /* printf("\t\tGP - end stroke only\n"); */
- gpencil_stroke_end(op);
-
- /* If eraser mode is on, turn it off after the stroke finishes
- * NOTE: This just makes it nicer to work with drawing sessions
- */
- if (p->paintmode == GP_PAINTMODE_ERASER) {
- p->paintmode = RNA_enum_get(op->ptr, "mode");
-
- /* if the original mode was *still* eraser,
- * we'll let it say for now, since this gives
- * users an opportunity to have visual feedback
- * when adjusting eraser size
- */
- if (p->paintmode != GP_PAINTMODE_ERASER) {
- /* turn off cursor...
- * NOTE: this should be enough for now
- * Just hiding this makes it seem like
- * you can paint again...
- */
- gpencil_draw_toggle_eraser_cursor(C, p, false);
- }
- }
-
- /* we've just entered idling state, so this event was processed (but no others yet) */
- estate = OPERATOR_RUNNING_MODAL;
-
- /* stroke could be smoothed, send notifier to refresh screen */
- WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
- }
- else {
- /* printf("\t\tGP - end of stroke + op\n"); */
- /* if drawing polygon and enable on back, must move stroke */
- if (ts) {
- if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) &&
- (p->paintmode == GP_PAINTMODE_DRAW_POLY)) {
- if (p->flags & GP_PAINTFLAG_STROKEADDED) {
- gpencil_move_last_stroke_to_back(C);
- }
- }
- }
- /* drawing batch cache is dirty now */
- gp_update_cache(p->gpd);
-
- p->status = GP_STATUS_DONE;
- estate = OPERATOR_FINISHED;
- }
+ p->status = GP_STATUS_DONE;
+ estate = OPERATOR_FINISHED;
}
else if (event->val == KM_PRESS) {
bool in_bounds = false;
@@ -3929,15 +3545,6 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
* NOTE: Don't enter this case if an error occurred while finding the
* region (as above)
*/
- /* if drawing polygon and enable on back, must move stroke */
- if (ts) {
- if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) &&
- (p->paintmode == GP_PAINTMODE_DRAW_POLY)) {
- if (p->flags & GP_PAINTFLAG_STROKEADDED) {
- gpencil_move_last_stroke_to_back(C);
- }
- }
- }
p->status = GP_STATUS_DONE;
estate = OPERATOR_FINISHED;
}
@@ -3954,25 +3561,24 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* handle painting mouse-movements? */
if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) {
/* handle drawing event */
- /* printf("\t\tGP - add point\n"); */
+ bool is_speed_guide = ((guide->use_guide) &&
+ (p->brush && (p->brush->gpencil_tool == GPAINT_TOOL_DRAW)));
int size_before = p->gpd->runtime.sbuffer_used;
- bool added_events = false;
- if (((p->flags & GP_PAINTFLAG_FIRSTRUN) == 0) && (p->paintmode != GP_PAINTMODE_ERASER)) {
- added_events = gpencil_add_fake_events(C, op, event, p);
+ if (((p->flags & GP_PAINTFLAG_FIRSTRUN) == 0) && (p->paintmode != GP_PAINTMODE_ERASER) &&
+ !(is_speed_guide && (p->flags & GP_PAINTFLAG_REQ_VECTOR))) {
+ gpencil_add_fake_points(event, p);
}
- gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph_pointer(C), 0.0f, 0.0f, false);
+ gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph_pointer(C));
int size_after = p->gpd->runtime.sbuffer_used;
- /* Last point of the event is always real (not fake). */
- tGPspoint *points = (tGPspoint *)p->gpd->runtime.sbuffer;
- tGPspoint *pt = &points[size_after - 1];
- pt->tflag &= ~GP_TPOINT_FAKE;
-
- /* Smooth the fake events to get smoother strokes, specially at ends. */
- if (added_events) {
- gp_smooth_fake_events(p, size_before, size_after);
+ /* Smooth segments if some fake points were added (need loop to get cumulative smooth).
+ * the 0.15 value gets a good result in Windows and Linux. */
+ if (!is_speed_guide && (size_after - size_before > 1)) {
+ for (int r = 0; r < 5; r++) {
+ gp_smooth_segment(p->gpd, 0.15f, size_before - 1, size_after - 1);
+ }
}
/* finish painting operation if anything went wrong just now */
@@ -3982,17 +3588,13 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
else {
/* event handled, so just tag as running modal */
- /* printf("\t\t\t\tGP - add point handled!\n"); */
estate = OPERATOR_RUNNING_MODAL;
}
}
/* eraser size */
else if ((p->paintmode == GP_PAINTMODE_ERASER) &&
ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE, PADPLUSKEY, PADMINUS)) {
- /* just resize the brush (local version)
- * TODO: fix the hardcoded size jumps (set to make a visible difference) and hardcoded keys
- */
- /* printf("\t\tGP - resize eraser\n"); */
+ /* Just resize the brush (local version). */
switch (event->type) {
case WHEELDOWNMOUSE: /* larger */
case PADPLUSKEY:
@@ -4032,7 +3634,6 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
else {
/* update status indicators - cursor, header, etc. */
gpencil_draw_status_indicators(C, p);
- gpencil_draw_cursor_set(p); /* cursor may have changed outside our control - T44084 */
}
/* process last operations before exiting */
@@ -4056,12 +3657,6 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
case OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH:
/* event doesn't need to be handled */
-#if 0
- printf("unhandled event -> %d (mmb? = %d | mmv? = %d)\n",
- event->type,
- event->type == MIDDLEMOUSE,
- event->type == MOUSEMOVE);
-#endif
break;
}
@@ -4078,11 +3673,6 @@ static const EnumPropertyItem prop_gpencil_drawmodes[] = {
0,
"Draw Straight Lines",
"Draw straight line segment(s)"},
- {GP_PAINTMODE_DRAW_POLY,
- "DRAW_POLY",
- 0,
- "Draw Poly Line",
- "Click to place endpoints of straight line segments (connected)"},
{GP_PAINTMODE_ERASER, "ERASER", 0, "Eraser", "Erase Grease Pencil strokes"},
{0, NULL, 0, NULL, NULL},
};
@@ -4094,7 +3684,7 @@ void GPENCIL_OT_draw(wmOperatorType *ot)
/* identifiers */
ot->name = "Grease Pencil Draw";
ot->idname = "GPENCIL_OT_draw";
- ot->description = "Draw a new stroke in the active Grease Pencil Object";
+ ot->description = "Draw mouse_prv new stroke in the active Grease Pencil Object";
/* api callbacks */
ot->exec = gpencil_draw_exec;
diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c
index ad54382dde7..96522d1750c 100644
--- a/source/blender/editors/gpencil/gpencil_primitive.c
+++ b/source/blender/editors/gpencil/gpencil_primitive.c
@@ -122,6 +122,10 @@ static void gp_session_validatebuffer(tGPDprimitive *p)
gpd->runtime.sbuffer_sflag = 0;
gpd->runtime.sbuffer_sflag |= GP_STROKE_3DSPACE;
+ /* Set vertex colors for buffer. */
+ ED_gpencil_sbuffer_vertex_color_set(
+ p->depsgraph, p->ob, p->scene->toolsettings, p->brush, p->material);
+
if (ELEM(p->type, GP_STROKE_BOX, GP_STROKE_CIRCLE)) {
gpd->runtime.sbuffer_sflag |= GP_STROKE_CYCLIC;
}
@@ -132,34 +136,11 @@ static void gp_init_colors(tGPDprimitive *p)
bGPdata *gpd = p->gpd;
Brush *brush = p->brush;
- MaterialGPencilStyle *gp_style = NULL;
-
/* use brush material */
- p->mat = BKE_gpencil_object_material_ensure_from_active_input_brush(p->bmain, p->ob, brush);
-
- /* assign color information to temp data */
- gp_style = p->mat->gp_style;
- if (gp_style) {
-
- /* set colors */
- if (gp_style->flag & GP_STYLE_STROKE_SHOW) {
- copy_v4_v4(gpd->runtime.scolor, gp_style->stroke_rgba);
- }
- else {
- /* if no stroke, use fill */
- copy_v4_v4(gpd->runtime.scolor, gp_style->fill_rgba);
- }
-
- copy_v4_v4(gpd->runtime.sfill, gp_style->fill_rgba);
- /* add some alpha to make easy the filling without hide strokes */
- if (gpd->runtime.sfill[3] > 0.8f) {
- gpd->runtime.sfill[3] = 0.8f;
- }
+ p->material = BKE_gpencil_object_material_ensure_from_active_input_brush(p->bmain, p->ob, brush);
- gpd->runtime.mode = (short)gp_style->mode;
- gpd->runtime.bstroke_style = gp_style->stroke_style;
- gpd->runtime.bfill_style = gp_style->fill_style;
- }
+ gpd->runtime.matid = BKE_object_material_slot_find_index(p->ob, p->material);
+ gpd->runtime.sbuffer_brush = brush;
}
/* Helper to square a primitive */
@@ -254,18 +235,6 @@ static void gp_primitive_update_cps(tGPDprimitive *tgpi)
}
}
-/* Helper to reflect point */
-static void UNUSED_FUNCTION(gp_reflect_point_v2_v2v2v2)(float va[2],
- const float p[2],
- const float a[2],
- const float b[2])
-{
- float point[2];
- closest_to_line_v2(point, p, a, b);
- va[0] = point[0] - (p[0] - point[0]);
- va[1] = point[1] - (p[1] - point[1]);
-}
-
/* Poll callback for primitive operators */
static bool gpencil_primitive_add_poll(bContext *C)
{
@@ -294,7 +263,7 @@ static bool gpencil_primitive_add_poll(bContext *C)
/* don't allow operator to function if the active layer is locked/hidden
* (BUT, if there isn't an active layer, we are free to add new layer when the time comes)
*/
- bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
if ((gpl) && (gpl->flag & (GP_LAYER_LOCKED | GP_LAYER_HIDE))) {
CTX_wm_operator_poll_msg_set(C,
"Primitives cannot be added as active layer is locked or hidden");
@@ -322,6 +291,8 @@ static void gpencil_primitive_allocate_memory(tGPDprimitive *tgpi)
static void gp_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi)
{
Scene *scene = CTX_data_scene(C);
+ ToolSettings *ts = scene->toolsettings;
+ Brush *brush = tgpi->brush;
int cfra = CFRA;
bGPDlayer *gpl = CTX_data_active_gpencil_layer(C);
@@ -339,13 +310,15 @@ static void gp_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi)
/* create new temp stroke */
bGPDstroke *gps = MEM_callocN(sizeof(bGPDstroke), "Temp bGPDstroke");
gps->thickness = 2.0f;
- gps->gradient_f = 1.0f;
- gps->gradient_s[0] = 1.0f;
- gps->gradient_s[1] = 1.0f;
+ gps->fill_opacity_fac = 1.0f;
+ gps->hardeness = 1.0f;
+ copy_v2_fl(gps->aspect_ratio, 1.0f);
+ gps->uv_scale = 1.0f;
gps->inittime = 0.0f;
- /* enable recalculation flag by default */
- gps->flag |= GP_STROKE_RECALC_GEOMETRY;
+ /* Apply the vertex color to fill. */
+ ED_gpencil_fill_vertex_color_set(ts, brush, gps);
+
gps->flag &= ~GP_STROKE_SELECT;
/* the polygon must be closed, so enabled cyclic */
if (ELEM(tgpi->type, GP_STROKE_BOX, GP_STROKE_CIRCLE)) {
@@ -367,10 +340,11 @@ static void gp_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi)
/* allocate memory for storage points, but keep empty */
gps->totpoints = 0;
gps->points = MEM_callocN(sizeof(bGPDspoint), "gp_stroke_points");
+ gps->dvert = NULL;
+
/* initialize triangle memory to dummy data */
gps->tot_triangles = 0;
gps->triangles = NULL;
- gps->flag |= GP_STROKE_RECALC_GEOMETRY;
/* add to strokes */
BLI_addtail(&tgpi->gpf->strokes, gps);
@@ -381,6 +355,8 @@ static void gp_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi)
/* Random generator, only init once. */
uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX);
tgpi->rng = BLI_rng_new(rng_seed);
+
+ DEG_id_tag_update(&tgpi->gpd->id, ID_RECALC_COPY_ON_WRITE);
}
/* add new segment to curve */
@@ -898,7 +874,6 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
if (brush->gpencil_settings->flag & GP_BRUSH_USE_JITTER_PRESSURE) {
jitter = BKE_curvemapping_evaluateF(
brush->gpencil_settings->curve_jitter, 0, curve_pressure);
- jitter *= brush->gpencil_settings->draw_sensitivity;
}
else {
jitter = brush->gpencil_settings->draw_jitter;
@@ -934,10 +909,10 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) &&
(brush->gpencil_settings->draw_random_press > 0.0f)) {
if (p2d->rnd[0] > 0.5f) {
- pressure -= brush->gpencil_settings->draw_random_press * p2d->rnd[1];
+ pressure -= (brush->gpencil_settings->draw_random_press * 2.0f) * p2d->rnd[1];
}
else {
- pressure += brush->gpencil_settings->draw_random_press * p2d->rnd[2];
+ pressure += (brush->gpencil_settings->draw_random_press * 2.0f) * p2d->rnd[2];
}
}
@@ -945,7 +920,7 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
if (brush->gpencil_settings->flag & GP_BRUSH_USE_STENGTH_PRESSURE) {
float curvef = BKE_curvemapping_evaluateF(
brush->gpencil_settings->curve_strength, 0, curve_pressure);
- strength *= curvef * brush->gpencil_settings->draw_sensitivity;
+ strength *= curvef;
strength *= brush->gpencil_settings->draw_strength;
}
@@ -973,14 +948,13 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
/* point uv */
if (gpd->runtime.sbuffer_used > 0) {
- MaterialGPencilStyle *gp_style = tgpi->mat->gp_style;
- const float pixsize = gp_style->texture_pixsize / 1000000.0f;
tGPspoint *tptb = (tGPspoint *)gpd->runtime.sbuffer + gpd->runtime.sbuffer_used - 1;
bGPDspoint spt, spt2;
/* get origin to reproject point */
float origin[3];
- ED_gp_get_drawing_reference(tgpi->scene, tgpi->ob, tgpi->gpl, ts->gpencil_v3d_align, origin);
+ ED_gpencil_drawing_reference_get(
+ tgpi->scene, tgpi->ob, tgpi->gpl, ts->gpencil_v3d_align, origin);
/* reproject current */
ED_gpencil_tpoint_to_point(tgpi->region, origin, tpt, &spt);
ED_gp_project_point_to_plane(
@@ -990,11 +964,8 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
ED_gpencil_tpoint_to_point(tgpi->region, origin, tptb, &spt2);
ED_gp_project_point_to_plane(
tgpi->scene, tgpi->ob, tgpi->rv3d, origin, tgpi->lock_axis - 1, &spt2);
- tgpi->totpixlen += len_v3v3(&spt.x, &spt2.x) / pixsize;
+ tgpi->totpixlen += len_v3v3(&spt.x, &spt2.x);
tpt->uv_fac = tgpi->totpixlen;
- if ((gp_style) && (gp_style->sima)) {
- tpt->uv_fac /= gp_style->sima->gen_x;
- }
}
else {
tgpi->totpixlen = 0.0f;
@@ -1028,6 +999,8 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
pt->time = 0.0f;
pt->flag = 0;
pt->uv_fac = tpt->uv_fac;
+ /* Apply the vertex color to point. */
+ ED_gpencil_point_vertex_color_set(ts, brush, pt);
if (gps->dvert != NULL) {
MDeformVert *dvert = &gps->dvert[i];
@@ -1052,7 +1025,8 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
/* reproject to plane */
if (!is_depth) {
float origin[3];
- ED_gp_get_drawing_reference(tgpi->scene, tgpi->ob, tgpi->gpl, ts->gpencil_v3d_align, origin);
+ ED_gpencil_drawing_reference_get(
+ tgpi->scene, tgpi->ob, tgpi->gpl, ts->gpencil_v3d_align, origin);
ED_gp_project_stroke_to_plane(
tgpi->scene, tgpi->ob, tgpi->rv3d, gps, origin, ts->gp_sculpt.lock_axis - 1);
}
@@ -1060,7 +1034,7 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
/* if parented change position relative to parent object */
for (int i = 0; i < gps->totpoints; i++) {
bGPDspoint *pt = &gps->points[i];
- gp_apply_parent_point(tgpi->depsgraph, tgpi->ob, tgpi->gpd, tgpi->gpl, pt);
+ gp_apply_parent_point(tgpi->depsgraph, tgpi->ob, tgpi->gpl, pt);
}
/* if camera view, reproject flat to view to avoid perspective effect */
@@ -1068,8 +1042,11 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
ED_gpencil_project_stroke_to_view(C, tgpi->gpl, gps);
}
- /* force fill recalc */
- gps->flag |= GP_STROKE_RECALC_GEOMETRY;
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gps);
+
+ /* Update evaluated data. */
+ ED_gpencil_sbuffer_update_eval(tgpi->gpd, tgpi->ob_eval);
MEM_SAFE_FREE(depth_arr);
@@ -1162,13 +1139,14 @@ static void gpencil_primitive_init(bContext *C, wmOperator *op)
/* set current scene and window info */
tgpi->bmain = CTX_data_main(C);
+ tgpi->depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
tgpi->scene = scene;
tgpi->ob = CTX_data_active_object(C);
+ tgpi->ob_eval = (Object *)DEG_get_evaluated_object(tgpi->depsgraph, tgpi->ob);
tgpi->sa = CTX_wm_area(C);
tgpi->region = CTX_wm_region(C);
tgpi->rv3d = tgpi->region->regiondata;
tgpi->v3d = tgpi->sa->spacedata.first;
- tgpi->depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
tgpi->win = CTX_wm_window(C);
/* save original type */
@@ -1184,7 +1162,7 @@ static void gpencil_primitive_init(bContext *C, wmOperator *op)
/* if brush doesn't exist, create a new set (fix damaged files from old versions) */
if ((paint->brush == NULL) || (paint->brush->gpencil_settings == NULL)) {
- BKE_brush_gpencil_presets(bmain, ts);
+ BKE_brush_gpencil_paint_presets(bmain, ts);
}
/* Set Draw brush. */
@@ -1199,7 +1177,7 @@ static void gpencil_primitive_init(bContext *C, wmOperator *op)
tgpi->gpd->runtime.tot_cp_points = 0;
/* getcolor info */
- tgpi->mat = BKE_gpencil_object_material_ensure_from_active_input_toolsettings(
+ tgpi->material = BKE_gpencil_object_material_ensure_from_active_input_toolsettings(
bmain, tgpi->ob, ts);
/* set parameters */
@@ -1309,20 +1287,17 @@ static void gpencil_primitive_interaction_end(bContext *C,
add_frame_mode = GP_GETFRAME_ADD_NEW;
}
- gpf = BKE_gpencil_layer_getframe(tgpi->gpl, tgpi->cframe, add_frame_mode);
+ gpf = BKE_gpencil_layer_frame_get(tgpi->gpl, tgpi->cframe, add_frame_mode);
/* prepare stroke to get transferred */
gps = tgpi->gpf->strokes.first;
if (gps) {
gps->thickness = brush->size;
- gps->gradient_f = brush->gpencil_settings->gradient_f;
- copy_v2_v2(gps->gradient_s, brush->gpencil_settings->gradient_s);
-
- gps->flag |= GP_STROKE_RECALC_GEOMETRY;
- gps->tot_triangles = 0;
+ gps->hardeness = brush->gpencil_settings->hardeness;
+ copy_v2_v2(gps->aspect_ratio, brush->gpencil_settings->aspect_ratio);
- /* calculate UVs along the stroke */
- ED_gpencil_calc_stroke_uv(tgpi->ob, gps);
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gps);
}
/* transfer stroke from temporary buffer to the actual frame */
@@ -1348,7 +1323,7 @@ static void gpencil_primitive_interaction_end(bContext *C,
/* Close stroke with geometry */
if ((tgpi->type == GP_STROKE_BOX) || (tgpi->type == GP_STROKE_CIRCLE)) {
- BKE_gpencil_close_stroke(gps);
+ BKE_gpencil_stroke_close(gps);
}
DEG_id_tag_update(&tgpi->gpd->id, ID_RECALC_COPY_ON_WRITE);
diff --git a/source/blender/editors/gpencil/gpencil_brush.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
index 14354ff38f4..5cf4681c755 100644
--- a/source/blender/editors/gpencil/gpencil_brush.c
+++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
@@ -48,11 +48,13 @@
#include "DNA_gpencil_types.h"
#include "DNA_object_types.h"
+#include "BKE_brush.h"
#include "BKE_colortools.h"
#include "BKE_context.h"
#include "BKE_deform.h"
#include "BKE_gpencil.h"
#include "BKE_gpencil_modifier.h"
+#include "BKE_main.h"
#include "BKE_material.h"
#include "BKE_object_deform.h"
#include "BKE_report.h"
@@ -83,8 +85,8 @@
/* Context for brush operators */
typedef struct tGP_BrushEditData {
/* Current editor/region/etc. */
- /* NOTE: This stuff is mainly needed to handle 3D view projection stuff... */
- struct Main *bmain;
+ Depsgraph *depsgraph;
+ Main *bmain;
Scene *scene;
Object *object;
@@ -96,11 +98,9 @@ typedef struct tGP_BrushEditData {
/* Brush Settings */
GP_Sculpt_Settings *settings;
- GP_Sculpt_Data *gp_brush;
- GP_Sculpt_Data *gp_brush_old;
+ Brush *brush;
+ Brush *brush_prev;
- eGP_Sculpt_Types brush_type;
- eGP_Sculpt_Types brush_type_old;
eGP_Sculpt_Flag flag;
eGP_Sculpt_SelectMaskFlag mask;
@@ -109,7 +109,6 @@ typedef struct tGP_BrushEditData {
/* Is the brush currently painting? */
bool is_painting;
- bool is_weight_mode;
bool is_transformed;
/* Start of new sculpt stroke */
@@ -176,10 +175,6 @@ static void gpsculpt_compute_lock_axis(tGP_BrushEditData *gso,
bGPDspoint *pt,
const float save_pt[3])
{
- if (gso->sa->spacetype != SPACE_VIEW3D) {
- return;
- }
-
const ToolSettings *ts = gso->scene->toolsettings;
const View3DCursor *cursor = &gso->scene->cursor;
const int axis = ts->gp_sculpt.lock_axis;
@@ -230,28 +225,13 @@ static GP_Sculpt_Settings *gpsculpt_get_settings(Scene *scene)
return &scene->toolsettings->gp_sculpt;
}
-/* Get the active brush */
-static GP_Sculpt_Data *gpsculpt_get_brush(Scene *scene, bool is_weight_mode)
-{
- GP_Sculpt_Settings *gset = &scene->toolsettings->gp_sculpt;
- GP_Sculpt_Data *gp_brush = NULL;
- if (is_weight_mode) {
- gp_brush = &gset->brush[gset->weighttype];
- }
- else {
- gp_brush = &gset->brush[gset->brushtype];
- }
-
- return gp_brush;
-}
-
/* Brush Operations ------------------------------- */
/* Invert behavior of brush? */
static bool gp_brush_invert_check(tGP_BrushEditData *gso)
{
/* The basic setting is the brush's setting (from the panel) */
- bool invert = ((gso->gp_brush->flag & GP_SCULPT_FLAG_INVERT) != 0);
+ bool invert = ((gso->brush->gpencil_settings->sculpt_flag & GP_SCULPT_FLAG_INVERT) != 0);
/* During runtime, the user can hold down the Ctrl key to invert the basic behavior */
if (gso->flag & GP_SCULPT_FLAG_INVERT) {
@@ -260,10 +240,10 @@ static bool gp_brush_invert_check(tGP_BrushEditData *gso)
/* set temporary status */
if (invert) {
- gso->gp_brush->flag |= GP_SCULPT_FLAG_TMP_INVERT;
+ gso->brush->gpencil_settings->sculpt_flag |= GP_SCULPT_FLAG_TMP_INVERT;
}
else {
- gso->gp_brush->flag &= ~GP_SCULPT_FLAG_TMP_INVERT;
+ gso->brush->gpencil_settings->sculpt_flag &= ~GP_SCULPT_FLAG_TMP_INVERT;
}
return invert;
@@ -272,28 +252,24 @@ static bool gp_brush_invert_check(tGP_BrushEditData *gso)
/* Compute strength of effect */
static float gp_brush_influence_calc(tGP_BrushEditData *gso, const int radius, const int co[2])
{
- GP_Sculpt_Data *gp_brush = gso->gp_brush;
+ Brush *brush = gso->brush;
/* basic strength factor from brush settings */
- float influence = gp_brush->strength;
+ float influence = brush->alpha;
/* use pressure? */
- if (gp_brush->flag & GP_SCULPT_FLAG_USE_PRESSURE) {
+ if (brush->gpencil_settings->flag & GP_BRUSH_USE_PRESSURE) {
influence *= gso->pressure;
}
/* distance fading */
- if (gp_brush->flag & GP_SCULPT_FLAG_USE_FALLOFF) {
- int mval_i[2];
- round_v2i_v2fl(mval_i, gso->mval);
- float distance = (float)len_v2v2_int(mval_i, co);
- float fac;
+ int mval_i[2];
+ round_v2i_v2fl(mval_i, gso->mval);
+ float distance = (float)len_v2v2_int(mval_i, co);
- CLAMP(distance, 0.0f, (float)radius);
- fac = 1.0f - (distance / (float)radius);
-
- influence *= fac;
- }
+ /* Apply Brush curve. */
+ float brush_fallof = BKE_brush_curve_strength(brush, distance, (float)radius);
+ influence *= brush_fallof;
/* apply multiframe falloff */
influence *= gso->mf_falloff;
@@ -302,18 +278,36 @@ static float gp_brush_influence_calc(tGP_BrushEditData *gso, const int radius, c
return influence;
}
-/* Force recal filling data */
-static void gp_recalc_geometry(bGPDstroke *gps)
+/* Tag stroke to be recalculated. */
+static void gpencil_recalc_geometry_tag(bGPDstroke *gps)
{
- bGPDstroke *gps_orig = gps->runtime.gps_orig;
- if (gps_orig) {
- gps_orig->flag |= GP_STROKE_RECALC_GEOMETRY;
- gps_orig->tot_triangles = 0;
+ bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
+ gps_active->flag |= GP_STROKE_TAG;
+}
+
+/* Recalc any stroke tagged. */
+static void gpencil_update_geometry(bGPdata *gpd)
+{
+ if (gpd == NULL) {
+ return;
}
- else {
- gps->flag |= GP_STROKE_RECALC_GEOMETRY;
- gps->tot_triangles = 0;
+
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ if ((gpl->actframe != gpf) && ((gpf->flag & GP_FRAME_SELECT) == 0)) {
+ continue;
+ }
+
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ if (gps->flag & GP_STROKE_TAG) {
+ BKE_gpencil_stroke_geometry_update(gps);
+ gps->flag &= ~GP_STROKE_TAG;
+ }
+ }
+ }
}
+ DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
+ WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
}
/* ************************************************ */
@@ -334,31 +328,22 @@ static bool gp_brush_smooth_apply(tGP_BrushEditData *gso,
const int radius,
const int co[2])
{
- // GP_Sculpt_Data *gp_brush = gso->brush;
float inf = gp_brush_influence_calc(gso, radius, co);
- /* need one flag enabled by default */
- if ((gso->settings->flag &
- (GP_SCULPT_SETT_FLAG_APPLY_POSITION | GP_SCULPT_SETT_FLAG_APPLY_STRENGTH |
- GP_SCULPT_SETT_FLAG_APPLY_THICKNESS | GP_SCULPT_SETT_FLAG_APPLY_UV)) == 0) {
- gso->settings->flag |= GP_SCULPT_SETT_FLAG_APPLY_POSITION;
- }
/* perform smoothing */
- if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_POSITION) {
- BKE_gpencil_smooth_stroke(gps, pt_index, inf);
+ if (gso->brush->gpencil_settings->sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_POSITION) {
+ BKE_gpencil_stroke_smooth(gps, pt_index, inf);
}
- if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_STRENGTH) {
- BKE_gpencil_smooth_stroke_strength(gps, pt_index, inf);
+ if (gso->brush->gpencil_settings->sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_STRENGTH) {
+ BKE_gpencil_stroke_smooth_strength(gps, pt_index, inf);
}
- if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_THICKNESS) {
- BKE_gpencil_smooth_stroke_thickness(gps, pt_index, inf);
+ if (gso->brush->gpencil_settings->sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_THICKNESS) {
+ BKE_gpencil_stroke_smooth_thickness(gps, pt_index, inf);
}
- if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_UV) {
- BKE_gpencil_smooth_stroke_uv(gps, pt_index, inf);
+ if (gso->brush->gpencil_settings->sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_UV) {
+ BKE_gpencil_stroke_smooth_uv(gps, pt_index, inf);
}
- gp_recalc_geometry(gps);
-
return true;
}
@@ -440,7 +425,7 @@ static bool gp_brush_strength_apply(tGP_BrushEditData *gso,
CLAMP(pt->strength, 0.0f, 1.0f);
/* smooth the strength */
- BKE_gpencil_smooth_stroke_strength(gps, pt_index, inf);
+ BKE_gpencil_stroke_smooth_strength(gps, pt_index, inf);
return true;
}
@@ -534,38 +519,27 @@ static bool gp_brush_grab_store_points(tGP_BrushEditData *gso,
static void gp_brush_grab_calc_dvec(tGP_BrushEditData *gso)
{
/* Convert mouse-movements to movement vector */
- // TODO: incorporate pressure into this?
- // XXX: screen-space strokes in 3D space will suffer!
- if (gso->sa->spacetype == SPACE_VIEW3D) {
- RegionView3D *rv3d = gso->region->regiondata;
- float *rvec = gso->object->loc;
- float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL);
-
- float mval_f[2];
-
- /* convert from 2D screenspace to 3D... */
- mval_f[0] = (float)(gso->mval[0] - gso->mval_prev[0]);
- mval_f[1] = (float)(gso->mval[1] - gso->mval_prev[1]);
-
- /* apply evaluated data transformation */
- if (gso->rot_eval != 0.0f) {
- const float cval = cos(gso->rot_eval);
- const float sval = sin(gso->rot_eval);
- float r[2];
- r[0] = (mval_f[0] * cval) - (mval_f[1] * sval);
- r[1] = (mval_f[0] * sval) + (mval_f[1] * cval);
- copy_v2_v2(mval_f, r);
- }
+ RegionView3D *rv3d = gso->region->regiondata;
+ float *rvec = gso->object->loc;
+ float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL);
- ED_view3d_win_to_delta(gso->region, mval_f, gso->dvec, zfac);
- }
- else {
- /* 2D - just copy */
- // XXX: view2d?
- gso->dvec[0] = (float)(gso->mval[0] - gso->mval_prev[0]);
- gso->dvec[1] = (float)(gso->mval[1] - gso->mval_prev[1]);
- gso->dvec[2] = 0.0f; /* unused */
+ float mval_f[2];
+
+ /* convert from 2D screenspace to 3D... */
+ mval_f[0] = (float)(gso->mval[0] - gso->mval_prev[0]);
+ mval_f[1] = (float)(gso->mval[1] - gso->mval_prev[1]);
+
+ /* apply evaluated data transformation */
+ if (gso->rot_eval != 0.0f) {
+ const float cval = cos(gso->rot_eval);
+ const float sval = sin(gso->rot_eval);
+ float r[2];
+ r[0] = (mval_f[0] * cval) - (mval_f[1] * sval);
+ r[1] = (mval_f[0] * sval) + (mval_f[1] * cval);
+ copy_v2_v2(mval_f, r);
}
+
+ ED_view3d_win_to_delta(gso->region, mval_f, gso->dvec, zfac);
}
/* Apply grab transform to all relevant points of the affected strokes */
@@ -574,7 +548,14 @@ static void gp_brush_grab_apply_cached(tGP_BrushEditData *gso,
const float diff_mat[4][4])
{
tGPSB_Grab_StrokeData *data = BLI_ghash_lookup(gso->stroke_customdata, gps);
+ /* If a new frame is created, could be impossible find the stroke. */
+ if (data == NULL) {
+ return;
+ }
+
int i;
+ float inverse_diff_mat[4][4];
+ invert_m4_m4(inverse_diff_mat, diff_mat);
/* Apply dvec to all of the stored points */
for (i = 0; i < data->size; i++) {
@@ -596,14 +577,11 @@ static void gp_brush_grab_apply_cached(tGP_BrushEditData *gso,
/* apply */
add_v3_v3v3(&pt->x, fpt, delta);
/* undo transformation to the init parent position */
- float inverse_diff_mat[4][4];
- invert_m4_m4(inverse_diff_mat, diff_mat);
mul_m4_v3(inverse_diff_mat, &pt->x);
/* compute lock axis */
gpsculpt_compute_lock_axis(gso, pt, save_pt);
}
- gp_recalc_geometry(gps);
}
/* free customdata used for handling this stroke */
@@ -647,8 +625,6 @@ static bool gp_brush_push_apply(tGP_BrushEditData *gso,
/* compute lock axis */
gpsculpt_compute_lock_axis(gso, pt, save_pt);
- gps->flag |= GP_STROKE_RECALC_GEOMETRY;
-
/* done */
return true;
}
@@ -658,35 +634,26 @@ static bool gp_brush_push_apply(tGP_BrushEditData *gso,
/* Compute reference midpoint for the brush - this is what we'll be moving towards */
static void gp_brush_calc_midpoint(tGP_BrushEditData *gso)
{
- if (gso->sa->spacetype == SPACE_VIEW3D) {
- /* Convert mouse position to 3D space
- * See: gpencil_paint.c :: gp_stroke_convertcoords()
- */
- RegionView3D *rv3d = gso->region->regiondata;
- const float *rvec = gso->object->loc;
- float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL);
-
- float mval_f[2];
- copy_v2_v2(mval_f, gso->mval);
- float mval_prj[2];
- float dvec[3];
-
- if (ED_view3d_project_float_global(gso->region, rvec, mval_prj, V3D_PROJ_TEST_NOP) ==
- V3D_PROJ_RET_OK) {
- sub_v2_v2v2(mval_f, mval_prj, mval_f);
- ED_view3d_win_to_delta(gso->region, mval_f, dvec, zfac);
- sub_v3_v3v3(gso->dvec, rvec, dvec);
- }
- else {
- zero_v3(gso->dvec);
- }
+ /* Convert mouse position to 3D space
+ * See: gpencil_paint.c :: gp_stroke_convertcoords()
+ */
+ RegionView3D *rv3d = gso->region->regiondata;
+ const float *rvec = gso->object->loc;
+ float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL);
+
+ float mval_f[2];
+ copy_v2_v2(mval_f, gso->mval);
+ float mval_prj[2];
+ float dvec[3];
+
+ if (ED_view3d_project_float_global(gso->region, rvec, mval_prj, V3D_PROJ_TEST_NOP) ==
+ V3D_PROJ_RET_OK) {
+ sub_v2_v2v2(mval_f, mval_prj, mval_f);
+ ED_view3d_win_to_delta(gso->region, mval_f, dvec, zfac);
+ sub_v3_v3v3(gso->dvec, rvec, dvec);
}
else {
- /* Just 2D coordinates */
- // XXX: fix View2D offsets later
- gso->dvec[0] = (float)gso->mval[0];
- gso->dvec[1] = (float)gso->mval[1];
- gso->dvec[2] = 0.0f;
+ zero_v3(gso->dvec);
}
}
@@ -738,8 +705,6 @@ static bool gp_brush_pinch_apply(tGP_BrushEditData *gso,
/* compute lock axis */
gpsculpt_compute_lock_axis(gso, pt, save_pt);
- gp_recalc_geometry(gps);
-
/* done */
return true;
}
@@ -826,8 +791,6 @@ static bool gp_brush_twist_apply(tGP_BrushEditData *gso,
}
}
- gp_recalc_geometry(gps);
-
/* done */
return true;
}
@@ -851,15 +814,9 @@ static bool gp_brush_randomize_apply(tGP_BrushEditData *gso,
*/
const float inf = gp_brush_influence_calc(gso, radius, co) / 2.0f;
const float fac = BLI_rng_get_float(gso->rng) * inf;
- /* need one flag enabled by default */
- if ((gso->settings->flag &
- (GP_SCULPT_SETT_FLAG_APPLY_POSITION | GP_SCULPT_SETT_FLAG_APPLY_STRENGTH |
- GP_SCULPT_SETT_FLAG_APPLY_THICKNESS | GP_SCULPT_SETT_FLAG_APPLY_UV)) == 0) {
- gso->settings->flag |= GP_SCULPT_SETT_FLAG_APPLY_POSITION;
- }
/* apply random to position */
- if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_POSITION) {
+ if (gso->brush->gpencil_settings->sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_POSITION) {
/* Jitter is applied perpendicular to the mouse movement vector
* - We compute all effects in screenspace (since it's easier)
* and then project these to get the points/distances in
@@ -886,35 +843,20 @@ static bool gp_brush_randomize_apply(tGP_BrushEditData *gso,
/* convert to dataspace */
if (gps->flag & GP_STROKE_3DSPACE) {
/* 3D: Project to 3D space */
- if (gso->sa->spacetype == SPACE_VIEW3D) {
- bool flip;
- RegionView3D *rv3d = gso->region->regiondata;
- float zfac = ED_view3d_calc_zfac(rv3d, &pt->x, &flip);
- if (flip == false) {
- float dvec[3];
- ED_view3d_win_to_delta(gso->gsc.region, svec, dvec, zfac);
- add_v3_v3(&pt->x, dvec);
- /* compute lock axis */
- gpsculpt_compute_lock_axis(gso, pt, save_pt);
- }
- }
- else {
- /* ERROR */
- BLI_assert(!"3D stroke being sculpted in non-3D view");
+ bool flip;
+ RegionView3D *rv3d = gso->region->regiondata;
+ float zfac = ED_view3d_calc_zfac(rv3d, &pt->x, &flip);
+ if (flip == false) {
+ float dvec[3];
+ ED_view3d_win_to_delta(gso->gsc.region, svec, dvec, zfac);
+ add_v3_v3(&pt->x, dvec);
+ /* compute lock axis */
+ gpsculpt_compute_lock_axis(gso, pt, save_pt);
}
}
- else {
- /* 2D: As-is */
- // XXX: v2d scaling/offset?
- float nco[2];
- nco[0] = (float)co[0] + svec[0];
- nco[1] = (float)co[1] + svec[1];
-
- copy_v2_v2(&pt->x, nco);
- }
}
/* apply random to strength */
- if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_STRENGTH) {
+ if (gso->brush->gpencil_settings->sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_STRENGTH) {
if (BLI_rng_get_float(gso->rng) > 0.5f) {
pt->strength += fac;
}
@@ -925,7 +867,7 @@ static bool gp_brush_randomize_apply(tGP_BrushEditData *gso,
CLAMP_MAX(pt->strength, 1.0f);
}
/* apply random to thickness (use pressure) */
- if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_THICKNESS) {
+ if (gso->brush->gpencil_settings->sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_THICKNESS) {
if (BLI_rng_get_float(gso->rng) > 0.5f) {
pt->pressure += fac;
}
@@ -936,7 +878,7 @@ static bool gp_brush_randomize_apply(tGP_BrushEditData *gso,
CLAMP_MIN(pt->pressure, 0.0f);
}
/* apply random to UV (use pressure) */
- if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_UV) {
+ if (gso->brush->gpencil_settings->sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_UV) {
if (BLI_rng_get_float(gso->rng) > 0.5f) {
pt->uv_rot += fac;
}
@@ -946,69 +888,10 @@ static bool gp_brush_randomize_apply(tGP_BrushEditData *gso,
CLAMP(pt->uv_rot, -M_PI_2, M_PI_2);
}
- gp_recalc_geometry(gps);
-
/* done */
return true;
}
-/* Weight Paint Brush */
-/* Change weight paint for vertex groups */
-static bool gp_brush_weight_apply(tGP_BrushEditData *gso,
- bGPDstroke *gps,
- float UNUSED(rot_eval),
- int pt_index,
- const int radius,
- const int co[2])
-{
- /* create dvert */
- BKE_gpencil_dvert_ensure(gps);
-
- MDeformVert *dvert = gps->dvert + pt_index;
- float inf;
-
- /* Compute strength of effect
- * - We divide the strength by 10, so that users can set "sane" values.
- * Otherwise, good default values are in the range of 0.093
- */
- inf = gp_brush_influence_calc(gso, radius, co) / 10.0f;
-
- /* need a vertex group */
- if (gso->vrgroup == -1) {
- if (gso->object) {
- BKE_object_defgroup_add(gso->object);
- DEG_relations_tag_update(gso->bmain);
- gso->vrgroup = 0;
- }
- }
- else {
- bDeformGroup *defgroup = BLI_findlink(&gso->object->defbase, gso->vrgroup);
- if (defgroup->flag & DG_LOCK_WEIGHT) {
- return false;
- }
- }
- /* get current weight */
- MDeformWeight *dw = BKE_defvert_ensure_index(dvert, gso->vrgroup);
- float curweight = dw ? dw->weight : 0.0f;
-
- if (gp_brush_invert_check(gso)) {
- curweight -= inf;
- }
- else {
- /* increase weight */
- curweight += inf;
- /* verify maximum target weight */
- CLAMP_MAX(curweight, gso->gp_brush->weight);
- }
-
- CLAMP(curweight, 0.0f, 1.0f);
- if (dw) {
- dw->weight = curweight;
- }
-
- return true;
-}
-
/* ************************************************ */
/* Non Callback-Based Brushes */
/* Clone Brush ------------------------------------- */
@@ -1112,9 +995,9 @@ static void gp_brush_clone_add(bContext *C, tGP_BrushEditData *gso)
{
tGPSB_CloneBrushData *data = gso->customdata;
- Object *ob = CTX_data_active_object(C);
+ Object *ob = gso->object;
bGPdata *gpd = (bGPdata *)ob->data;
- Scene *scene = CTX_data_scene(C);
+ Scene *scene = gso->scene;
bGPDstroke *gps;
float delta[3];
@@ -1136,31 +1019,24 @@ static void gp_brush_clone_add(bContext *C, tGP_BrushEditData *gso)
bGPDlayer *gpl = NULL;
/* Try to use original layer. */
if (gps->runtime.tmp_layerinfo != NULL) {
- gpl = BLI_findstring(&gpd->layers, gps->runtime.tmp_layerinfo, offsetof(bGPDlayer, info));
+ gpl = BKE_gpencil_layer_named_get(gpd, gps->runtime.tmp_layerinfo);
}
/* if not available, use active layer. */
if (gpl == NULL) {
gpl = CTX_data_active_gpencil_layer(C);
}
- bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_ADD_NEW);
+ bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_ADD_NEW);
/* Make a new stroke */
- new_stroke = MEM_dupallocN(gps);
-
- new_stroke->points = MEM_dupallocN(gps->points);
- if (gps->dvert != NULL) {
- new_stroke->dvert = MEM_dupallocN(gps->dvert);
- BKE_gpencil_stroke_weights_duplicate(gps, new_stroke);
- }
- new_stroke->triangles = MEM_dupallocN(gps->triangles);
+ new_stroke = BKE_gpencil_stroke_duplicate(gps, true);
new_stroke->next = new_stroke->prev = NULL;
BLI_addtail(&gpf->strokes, new_stroke);
/* Fix color references */
Material *ma = BLI_ghash_lookup(data->new_colors, POINTER_FROM_INT(new_stroke->mat_nr));
- new_stroke->mat_nr = BKE_gpencil_object_material_get_index(ob, ma);
+ new_stroke->mat_nr = BKE_gpencil_object_material_index_get(ob, ma);
if (!ma || new_stroke->mat_nr < 0) {
new_stroke->mat_nr = 0;
}
@@ -1204,26 +1080,20 @@ static void gp_brush_clone_adjust(tGP_BrushEditData *gso)
int i;
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if (gso->gp_brush->flag & GP_SCULPT_FLAG_USE_FALLOFF) {
- /* "Smudge" Effect when falloff is enabled */
- float delta[3] = {0.0f};
- int sco[2] = {0};
- float influence;
+ /* "Smudge" Effect falloff */
+ float delta[3] = {0.0f};
+ int sco[2] = {0};
+ float influence;
- /* compute influence on point */
- gp_point_to_xy(&gso->gsc, gps, pt, &sco[0], &sco[1]);
- influence = gp_brush_influence_calc(gso, gso->gp_brush->size, sco);
+ /* compute influence on point */
+ gp_point_to_xy(&gso->gsc, gps, pt, &sco[0], &sco[1]);
+ influence = gp_brush_influence_calc(gso, gso->brush->size, sco);
- /* adjust the amount of displacement to apply */
- mul_v3_v3fl(delta, gso->dvec, influence);
+ /* adjust the amount of displacement to apply */
+ mul_v3_v3fl(delta, gso->dvec, influence);
- /* apply */
- add_v3_v3(&pt->x, delta);
- }
- else {
- /* Just apply the offset - All points move perfectly in sync with the cursor */
- add_v3_v3(&pt->x, gso->dvec);
- }
+ /* apply */
+ add_v3_v3(&pt->x, delta);
}
}
}
@@ -1258,17 +1128,15 @@ static bool gpsculpt_brush_apply_clone(bContext *C, tGP_BrushEditData *gso)
static void gpsculpt_brush_header_set(bContext *C, tGP_BrushEditData *gso)
{
- const char *brush_name = NULL;
+ Brush *brush = gso->brush;
char str[UI_MAX_DRAW_STR] = "";
- RNA_enum_name(rna_enum_gpencil_sculpt_brush_items, gso->brush_type, &brush_name);
-
BLI_snprintf(str,
sizeof(str),
TIP_("GPencil Sculpt: %s Stroke | LMB to paint | RMB/Escape to Exit"
" | Ctrl to Invert Action | Wheel Up/Down for Size "
" | Shift-Wheel Up/Down for Strength"),
- (brush_name) ? brush_name : "<?>");
+ brush->id.name + 2);
ED_workspace_status_text(C, str);
}
@@ -1281,33 +1149,20 @@ static void gpsculpt_brush_header_set(bContext *C, tGP_BrushEditData *gso)
static bool gpsculpt_brush_init(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
- ToolSettings *ts = CTX_data_tool_settings(C);
+ ToolSettings *ts = scene->toolsettings;
Object *ob = CTX_data_active_object(C);
- const bool is_weight_mode = ob->mode == OB_MODE_WEIGHT_GPENCIL;
/* set the brush using the tool */
-#if 0
- GP_Sculpt_Settings *gset = &ts->gp_sculpt;
- eGP_Sculpt_Types mode = is_weight_mode ? gset->weighttype : gset->brushtype;
-#endif
tGP_BrushEditData *gso;
/* setup operator data */
gso = MEM_callocN(sizeof(tGP_BrushEditData), "tGP_BrushEditData");
op->customdata = gso;
+ gso->depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
gso->bmain = CTX_data_main(C);
/* store state */
gso->settings = gpsculpt_get_settings(scene);
- gso->gp_brush = gpsculpt_get_brush(scene, is_weight_mode);
- gso->is_weight_mode = is_weight_mode;
-
- if (is_weight_mode) {
- gso->brush_type = gso->settings->weighttype;
- }
- else {
- gso->brush_type = gso->settings->brushtype;
- }
/* Random generator, only init once. */
uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX);
@@ -1320,11 +1175,6 @@ static bool gpsculpt_brush_init(bContext *C, wmOperator *op)
gso->gpd = ED_gpencil_data_get_active(C);
gso->cfra = INT_MAX; /* NOTE: So that first stroke will get handled in init_stroke() */
- /* some brushes cannot use pressure for radius */
- if (ELEM(gso->brush_type, GP_SCULPT_TYPE_GRAB, GP_SCULPT_TYPE_CLONE)) {
- gso->gp_brush->flag &= ~GP_SCULPT_FLAG_PRESSURE_RADIUS;
- }
-
gso->scene = scene;
gso->object = ob;
if (ob) {
@@ -1345,6 +1195,10 @@ static bool gpsculpt_brush_init(bContext *C, wmOperator *op)
gso->sa = CTX_wm_area(C);
gso->region = CTX_wm_region(C);
+ Paint *paint = &ts->gp_sculptpaint->paint;
+ gso->brush = paint->brush;
+ BKE_curvemapping_initialize(gso->brush->curve);
+
/* save mask */
gso->mask = ts->gpencil_selectmode_sculpt;
@@ -1359,8 +1213,9 @@ static bool gpsculpt_brush_init(bContext *C, wmOperator *op)
}
/* initialise custom data for brushes */
- switch (gso->brush_type) {
- case GP_SCULPT_TYPE_CLONE: {
+ char tool = gso->brush->gpencil_sculpt_tool;
+ switch (tool) {
+ case GPSCULPT_TOOL_CLONE: {
bGPDstroke *gps;
bool found = false;
@@ -1390,7 +1245,7 @@ static bool gpsculpt_brush_init(bContext *C, wmOperator *op)
break;
}
- case GP_SCULPT_TYPE_GRAB: {
+ case GPSCULPT_TOOL_GRAB: {
/* initialise the cache needed for this brush */
gso->stroke_customdata = BLI_ghash_ptr_new("GP Grab Brush - Strokes Hash");
break;
@@ -1407,11 +1262,6 @@ static bool gpsculpt_brush_init(bContext *C, wmOperator *op)
/* update header */
gpsculpt_brush_header_set(C, gso);
- /* setup cursor drawing */
- // WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_CROSS);
- if (gso->sa->spacetype != SPACE_VIEW3D) {
- ED_gpencil_toggle_brush_cursor(C, true, NULL);
- }
return true;
}
@@ -1419,10 +1269,11 @@ static void gpsculpt_brush_exit(bContext *C, wmOperator *op)
{
tGP_BrushEditData *gso = op->customdata;
wmWindow *win = CTX_wm_window(C);
+ char tool = gso->brush->gpencil_sculpt_tool;
/* free brush-specific data */
- switch (gso->brush_type) {
- case GP_SCULPT_TYPE_GRAB: {
+ switch (tool) {
+ case GPSCULPT_TOOL_GRAB: {
/* Free per-stroke customdata
* - Keys don't need to be freed, as those are the strokes
* - Values assigned to those keys do, as they are custom structs
@@ -1431,7 +1282,7 @@ static void gpsculpt_brush_exit(bContext *C, wmOperator *op)
break;
}
- case GP_SCULPT_TYPE_CLONE: {
+ case GPSCULPT_TOOL_CLONE: {
/* Free customdata */
gp_brush_clone_free(gso);
break;
@@ -1450,15 +1301,14 @@ static void gpsculpt_brush_exit(bContext *C, wmOperator *op)
BLI_rng_free(gso->rng);
}
- /* disable cursor and headerprints */
+ /* Disable headerprints. */
ED_workspace_status_text(C, NULL);
- WM_cursor_modal_restore(win);
- if (gso->sa->spacetype != SPACE_VIEW3D) {
- ED_gpencil_toggle_brush_cursor(C, false, NULL);
- }
/* disable temp invert flag */
- gso->gp_brush->flag &= ~GP_SCULPT_FLAG_TMP_INVERT;
+ gso->brush->gpencil_settings->sculpt_flag &= ~GP_SCULPT_FLAG_TMP_INVERT;
+
+ /* Update geometry data for tagged strokes. */
+ gpencil_update_geometry(gso->gpd);
/* free operator data */
MEM_freeN(gso);
@@ -1468,6 +1318,11 @@ static void gpsculpt_brush_exit(bContext *C, wmOperator *op)
/* poll callback for stroke sculpting operator(s) */
static bool gpsculpt_brush_poll(bContext *C)
{
+ ScrArea *sa = CTX_wm_area(C);
+ if (sa && sa->spacetype != SPACE_VIEW3D) {
+ return false;
+ }
+
/* NOTE: this is a bit slower, but is the most accurate... */
return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0;
}
@@ -1478,7 +1333,6 @@ static void gpsculpt_brush_init_stroke(bContext *C, tGP_BrushEditData *gso)
{
bGPdata *gpd = gso->gpd;
- bGPDlayer *gpl;
Scene *scene = gso->scene;
int cfra = CFRA;
@@ -1488,9 +1342,9 @@ static void gpsculpt_brush_init_stroke(bContext *C, tGP_BrushEditData *gso)
}
/* go through each layer, and ensure that we've got a valid frame to use */
- for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
/* only editable and visible layers are considered */
- if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
+ if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
bGPDframe *gpf = gpl->actframe;
/* Make a new frame to work on if the layer's frame
@@ -1578,12 +1432,11 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso,
{
GP_SpaceConversion *gsc = &gso->gsc;
rcti *rect = &gso->brush_rect;
- GP_Sculpt_Data *gp_brush = gso->gp_brush;
- const int radius = (gp_brush->flag & GP_SCULPT_FLAG_PRESSURE_RADIUS) ?
- gso->gp_brush->size * gso->pressure :
- gso->gp_brush->size;
- const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gso->gpd);
- bGPDstroke *gps_active = (!is_multiedit) ? gps->runtime.gps_orig : gps;
+ Brush *brush = gso->brush;
+ const int radius = (brush->flag & GP_BRUSH_USE_PRESSURE) ? gso->brush->size * gso->pressure :
+ gso->brush->size;
+
+ bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
bGPDspoint *pt_active = NULL;
bGPDspoint *pt1, *pt2;
@@ -1595,13 +1448,19 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso,
bool include_last = false;
bool changed = false;
float rot_eval = 0.0f;
+
+ /* Check if the stroke collide with brush. */
+ if (!ED_gpencil_stroke_check_collision(gsc, gps, gso->mval, radius, diff_mat)) {
+ return false;
+ }
+
if (gps->totpoints == 1) {
bGPDspoint pt_temp;
pt = &gps->points[0];
gp_point_to_parent_space(gps->points, diff_mat, &pt_temp);
gp_point_to_xy(gsc, gps, &pt_temp, &pc1[0], &pc1[1]);
- pt_active = (!is_multiedit) ? pt->runtime.pt_orig : pt;
+ pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
/* do boundbox check first */
if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) {
/* only check if point is inside */
@@ -1627,7 +1486,7 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso,
/* Skip if neither one is selected
* (and we are only allowed to edit/consider selected points) */
- if ((GPENCIL_ANY_SCULPT_MASK(gso->mask)) && (!gso->is_weight_mode)) {
+ if (GPENCIL_ANY_SCULPT_MASK(gso->mask)) {
if (!(pt1->flag & GP_SPOINT_SELECT) && !(pt2->flag & GP_SPOINT_SELECT)) {
include_last = false;
continue;
@@ -1654,8 +1513,8 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso,
/* To each point individually... */
pt = &gps->points[i];
- pt_active = (!is_multiedit) ? pt->runtime.pt_orig : pt;
- index = (!is_multiedit) ? pt->runtime.idx_orig : i;
+ pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
+ index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i;
if (pt_active != NULL) {
rot_eval = gpsculpt_rotation_eval_get(gso, gps, pt, i);
ok = apply(gso, gps_active, rot_eval, index, radius, pc1);
@@ -1671,8 +1530,8 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso,
*/
if (i + 1 == gps->totpoints - 1) {
pt = &gps->points[i + 1];
- pt_active = (!is_multiedit) ? pt->runtime.pt_orig : pt;
- index = (!is_multiedit) ? pt->runtime.idx_orig : i + 1;
+ pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
+ index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i + 1;
if (pt_active != NULL) {
rot_eval = gpsculpt_rotation_eval_get(gso, gps, pt, i + 1);
ok |= apply(gso, gps_active, rot_eval, index, radius, pc2);
@@ -1692,8 +1551,8 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso,
* (but wasn't added then, to avoid double-ups).
*/
pt = &gps->points[i];
- pt_active = (!is_multiedit) ? pt->runtime.pt_orig : pt;
- index = (!is_multiedit) ? pt->runtime.idx_orig : i;
+ pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
+ index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i;
if (pt_active != NULL) {
rot_eval = gpsculpt_rotation_eval_get(gso, gps, pt, i);
changed |= apply(gso, gps_active, rot_eval, index, radius, pc1);
@@ -1715,10 +1574,11 @@ static bool gpsculpt_brush_do_frame(bContext *C,
const float diff_mat[4][4])
{
bool changed = false;
- Object *ob = CTX_data_active_object(C);
- const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gso->gpd);
+ bool redo_geom = false;
+ Object *ob = gso->object;
+ char tool = gso->brush->gpencil_sculpt_tool;
- for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
/* skip strokes that are invalid for current view */
if (ED_gpencil_stroke_can_use(C, gps) == false) {
continue;
@@ -1728,28 +1588,29 @@ static bool gpsculpt_brush_do_frame(bContext *C,
continue;
}
- switch (gso->brush_type) {
- case GP_SCULPT_TYPE_SMOOTH: /* Smooth strokes */
+ switch (tool) {
+ case GPSCULPT_TOOL_SMOOTH: /* Smooth strokes */
{
changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_smooth_apply);
+ redo_geom |= changed;
break;
}
- case GP_SCULPT_TYPE_THICKNESS: /* Adjust stroke thickness */
+ case GPSCULPT_TOOL_THICKNESS: /* Adjust stroke thickness */
{
changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_thickness_apply);
break;
}
- case GP_SCULPT_TYPE_STRENGTH: /* Adjust stroke color strength */
+ case GPSCULPT_TOOL_STRENGTH: /* Adjust stroke color strength */
{
changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_strength_apply);
break;
}
- case GP_SCULPT_TYPE_GRAB: /* Grab points */
+ case GPSCULPT_TOOL_GRAB: /* Grab points */
{
- bGPDstroke *gps_active = (!is_multiedit) ? gps->runtime.gps_orig : gps;
+ bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
if (gps_active != NULL) {
if (gso->first) {
/* First time this brush stroke is being applied:
@@ -1757,7 +1618,8 @@ static bool gpsculpt_brush_do_frame(bContext *C,
* 2) Use the points now under the cursor
*/
gp_brush_grab_stroke_init(gso, gps_active);
- changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_grab_store_points);
+ changed |= gpsculpt_brush_do_stroke(
+ gso, gps_active, diff_mat, gp_brush_grab_store_points);
}
else {
/* Apply effect to the stored points */
@@ -1765,45 +1627,61 @@ static bool gpsculpt_brush_do_frame(bContext *C,
changed |= true;
}
}
+ redo_geom |= changed;
break;
}
- case GP_SCULPT_TYPE_PUSH: /* Push points */
+ case GPSCULPT_TOOL_PUSH: /* Push points */
{
changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_push_apply);
+ redo_geom |= changed;
break;
}
- case GP_SCULPT_TYPE_PINCH: /* Pinch points */
+ case GPSCULPT_TOOL_PINCH: /* Pinch points */
{
changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_pinch_apply);
+ redo_geom |= changed;
break;
}
- case GP_SCULPT_TYPE_TWIST: /* Twist points around midpoint */
+ case GPSCULPT_TOOL_TWIST: /* Twist points around midpoint */
{
changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_twist_apply);
+ redo_geom |= changed;
break;
}
- case GP_SCULPT_TYPE_RANDOMIZE: /* Apply jitter */
+ case GPSCULPT_TOOL_RANDOMIZE: /* Apply jitter */
{
changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_randomize_apply);
- break;
- }
-
- case GP_SCULPT_TYPE_WEIGHT: /* Adjust vertex group weight */
- {
- changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_weight_apply);
+ redo_geom |= changed;
break;
}
default:
- printf("ERROR: Unknown type of GPencil Sculpt brush - %u\n", gso->brush_type);
+ printf("ERROR: Unknown type of GPencil Sculpt brush \n");
break;
}
- /* Triangulation must be calculated if changed */
- gp_recalc_geometry(gps);
+
+ /* Triangulation must be calculated. */
+ if (redo_geom) {
+ bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
+ if (gpl->actframe == gpf) {
+ MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
+ /* Update active frame now, only if material has fill. */
+ if (gp_style->flag & GP_MATERIAL_FILL_SHOW) {
+ BKE_gpencil_stroke_geometry_update(gps_active);
+ }
+ else {
+ gpencil_recalc_geometry_tag(gps_active);
+ }
+ }
+ else {
+ /* Delay a full recalculation for other frames. */
+ gpencil_recalc_geometry_tag(gps_active);
+ }
+ }
}
return changed;
@@ -1812,17 +1690,19 @@ static bool gpsculpt_brush_do_frame(bContext *C,
/* Perform two-pass brushes which modify the existing strokes */
static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso)
{
- ToolSettings *ts = CTX_data_tool_settings(C);
- Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ ToolSettings *ts = gso->scene->toolsettings;
+ Depsgraph *depsgraph = gso->depsgraph;
Object *obact = gso->object;
- Object *ob_eval = DEG_get_evaluated_object(depsgraph, obact);
- bGPdata *gpd = gso->gpd;
bool changed = false;
+ Object *ob_eval = (Object *)DEG_get_evaluated_id(depsgraph, &obact->id);
+ bGPdata *gpd = (bGPdata *)ob_eval->data;
+
/* Calculate brush-specific data which applies equally to all points */
- switch (gso->brush_type) {
- case GP_SCULPT_TYPE_GRAB: /* Grab points */
- case GP_SCULPT_TYPE_PUSH: /* Push points */
+ char tool = gso->brush->gpencil_sculpt_tool;
+ switch (tool) {
+ case GPSCULPT_TOOL_GRAB: /* Grab points */
+ case GPSCULPT_TOOL_PUSH: /* Push points */
{
/* calculate amount of displacement to apply */
gso->rot_eval = 0.0f;
@@ -1830,15 +1710,15 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso)
break;
}
- case GP_SCULPT_TYPE_PINCH: /* Pinch points */
- case GP_SCULPT_TYPE_TWIST: /* Twist points around midpoint */
+ case GPSCULPT_TOOL_PINCH: /* Pinch points */
+ case GPSCULPT_TOOL_TWIST: /* Twist points around midpoint */
{
/* calculate midpoint of the brush (in data space) */
gp_brush_calc_midpoint(gso);
break;
}
- case GP_SCULPT_TYPE_RANDOMIZE: /* Random jitter */
+ case GPSCULPT_TOOL_RANDOMIZE: /* Random jitter */
{
/* compute the displacement vector for the cursor (in data space) */
gso->rot_eval = 0.0f;
@@ -1851,21 +1731,15 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso)
}
/* Find visible strokes, and perform operations on those if hit */
- CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
/* If no active frame, don't do anything... */
- if (gpl->actframe == NULL) {
- continue;
- }
- /* Get evaluated frames array data */
- int idx_eval = BLI_findindex(&gpd->layers, gpl);
- bGPDframe *gpf_eval = &ob_eval->runtime.gpencil_evaluated_frames[idx_eval];
- if (gpf_eval == NULL) {
+ if ((!BKE_gpencil_layer_is_editable(gpl)) || (gpl->actframe == NULL)) {
continue;
}
/* calculate difference matrix */
float diff_mat[4][4];
- ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat);
+ BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat);
/* Active Frame or MultiFrame? */
if (gso->is_multiframe) {
@@ -1874,10 +1748,10 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso)
int f_end = 0;
if (gso->use_multiframe_falloff) {
- BKE_gpencil_get_range_selected(gpl, &f_init, &f_end);
+ BKE_gpencil_frame_range_selected(gpl, &f_init, &f_end);
}
- for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
/* Always do active frame; Otherwise, only include selected frames */
if ((gpf == gpl->actframe) || (gpf->flag & GP_FRAME_SELECT)) {
/* compute multiframe falloff factor */
@@ -1897,12 +1771,13 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso)
}
}
else {
- /* Apply to active frame's strokes */
- gso->mf_falloff = 1.0f;
- changed |= gpsculpt_brush_do_frame(C, gso, gpl, gpf_eval, diff_mat);
+ if (gpl->actframe != NULL) {
+ /* Apply to active frame's strokes */
+ gso->mf_falloff = 1.0f;
+ changed |= gpsculpt_brush_do_frame(C, gso, gpl, gpl->actframe, diff_mat);
+ }
}
}
- CTX_DATA_END;
return changed;
}
@@ -1911,10 +1786,9 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso)
static void gpsculpt_brush_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
{
tGP_BrushEditData *gso = op->customdata;
- GP_Sculpt_Data *gp_brush = gso->gp_brush;
- const int radius = ((gp_brush->flag & GP_SCULPT_FLAG_PRESSURE_RADIUS) ?
- gso->gp_brush->size * gso->pressure :
- gso->gp_brush->size);
+ Brush *brush = gso->brush;
+ const int radius = (brush->flag & GP_BRUSH_USE_PRESSURE) ? gso->brush->size * gso->pressure :
+ gso->brush->size;
float mousef[2];
int mouse[2];
bool changed = false;
@@ -1947,7 +1821,8 @@ static void gpsculpt_brush_apply(bContext *C, wmOperator *op, PointerRNA *itempt
gso->brush_rect.ymax = mouse[1] + radius;
/* Apply brush */
- if (gso->brush_type == GP_SCULPT_TYPE_CLONE) {
+ char tool = gso->brush->gpencil_sculpt_tool;
+ if (tool == GPSCULPT_TOOL_CLONE) {
changed = gpsculpt_brush_apply_clone(C, gso);
}
else {
@@ -1968,13 +1843,18 @@ static void gpsculpt_brush_apply(bContext *C, wmOperator *op, PointerRNA *itempt
}
/* Running --------------------------------------------- */
+static Brush *gpsculpt_get_smooth_brush(tGP_BrushEditData *gso)
+{
+ Main *bmain = gso->bmain;
+ Brush *brush = BLI_findstring(&bmain->brushes, "Smooth Stroke", offsetof(ID, name) + 2);
+
+ return brush;
+}
/* helper - a record stroke, and apply paint event */
static void gpsculpt_brush_apply_event(bContext *C, wmOperator *op, const wmEvent *event)
{
tGP_BrushEditData *gso = op->customdata;
- ToolSettings *ts = CTX_data_tool_settings(C);
- GP_Sculpt_Settings *gset = &ts->gp_sculpt;
PointerRNA itemptr;
float mouse[2];
@@ -1997,19 +1877,17 @@ static void gpsculpt_brush_apply_event(bContext *C, wmOperator *op, const wmEven
}
RNA_float_set(&itemptr, "pressure", pressure);
- if (!gso->is_weight_mode) {
- if (event->shift) {
- gso->gp_brush_old = gso->gp_brush;
- gso->brush_type_old = gso->brush_type;
+ if (event->shift) {
+ gso->brush_prev = gso->brush;
- gso->gp_brush = &gset->brush[GP_SCULPT_TYPE_SMOOTH];
- gso->brush_type = GP_SCULPT_TYPE_SMOOTH;
+ gso->brush = gpsculpt_get_smooth_brush(gso);
+ if (gso->brush == NULL) {
+ gso->brush = gso->brush_prev;
}
- else {
- if (gso->gp_brush_old != NULL) {
- gso->gp_brush = gso->gp_brush_old;
- gso->brush_type = gso->brush_type_old;
- }
+ }
+ else {
+ if (gso->brush_prev != NULL) {
+ gso->brush = gso->brush_prev;
}
}
@@ -2058,25 +1936,26 @@ static int gpsculpt_brush_invoke(bContext *C, wmOperator *op, const wmEvent *eve
gso = op->customdata;
/* initialise type-specific data (used for the entire session) */
- switch (gso->brush_type) {
+ char tool = gso->brush->gpencil_sculpt_tool;
+ switch (tool) {
/* Brushes requiring timer... */
- case GP_SCULPT_TYPE_THICKNESS:
- brush_rate = 0.01f; // XXX: hardcoded
+ case GPSCULPT_TOOL_THICKNESS:
+ brush_rate = 0.01f;
needs_timer = true;
break;
- case GP_SCULPT_TYPE_STRENGTH:
- brush_rate = 0.01f; // XXX: hardcoded
+ case GPSCULPT_TOOL_STRENGTH:
+ brush_rate = 0.01f;
needs_timer = true;
break;
- case GP_SCULPT_TYPE_PINCH:
- brush_rate = 0.001f; // XXX: hardcoded
+ case GPSCULPT_TOOL_PINCH:
+ brush_rate = 0.001f;
needs_timer = true;
break;
- case GP_SCULPT_TYPE_TWIST:
- brush_rate = 0.01f; // XXX: hardcoded
+ case GPSCULPT_TOOL_TWIST:
+ brush_rate = 0.01f;
needs_timer = true;
break;
@@ -2141,42 +2020,6 @@ static int gpsculpt_brush_modal(bContext *C, wmOperator *op, const wmEvent *even
}
break;
- /* Adjust brush settings */
- /* FIXME: Step increments and modifier keys are hardcoded here! */
- case WHEELUPMOUSE:
- case PADPLUSKEY:
- if (event->shift) {
- /* increase strength */
- gso->gp_brush->strength += 0.05f;
- CLAMP_MAX(gso->gp_brush->strength, 1.0f);
- }
- else {
- /* increase brush size */
- gso->gp_brush->size += 3;
- CLAMP_MAX(gso->gp_brush->size, 300);
- }
-
- redraw_region = true;
- redraw_toolsettings = true;
- break;
-
- case WHEELDOWNMOUSE:
- case PADMINUS:
- if (event->shift) {
- /* decrease strength */
- gso->gp_brush->strength -= 0.05f;
- CLAMP_MIN(gso->gp_brush->strength, 0.0f);
- }
- else {
- /* decrease brush size */
- gso->gp_brush->size -= 3;
- CLAMP_MIN(gso->gp_brush->size, 1);
- }
-
- redraw_region = true;
- redraw_toolsettings = true;
- break;
-
/* Painting mbut release = Stop painting (back to idle) */
case LEFTMOUSE:
// BLI_assert(event->val == KM_RELEASE);
@@ -2232,43 +2075,7 @@ static int gpsculpt_brush_modal(bContext *C, wmOperator *op, const wmEvent *even
redraw_region = true;
break;
- /* Adjust brush settings */
- /* FIXME: Step increments and modifier keys are hardcoded here! */
- case WHEELUPMOUSE:
- case PADPLUSKEY:
- if (event->shift) {
- /* increase strength */
- gso->gp_brush->strength += 0.05f;
- CLAMP_MAX(gso->gp_brush->strength, 1.0f);
- }
- else {
- /* increase brush size */
- gso->gp_brush->size += 3;
- CLAMP_MAX(gso->gp_brush->size, 300);
- }
-
- redraw_region = true;
- redraw_toolsettings = true;
- break;
-
- case WHEELDOWNMOUSE:
- case PADMINUS:
- if (event->shift) {
- /* decrease strength */
- gso->gp_brush->strength -= 0.05f;
- CLAMP_MIN(gso->gp_brush->strength, 0.0f);
- }
- else {
- /* decrease brush size */
- gso->gp_brush->size -= 3;
- CLAMP_MIN(gso->gp_brush->size, 1);
- }
-
- redraw_region = true;
- redraw_toolsettings = true;
- break;
-
- /* Change Frame - Allowed */
+ /* Change Frame - Allowed */
case LEFTARROWKEY:
case RIGHTARROWKEY:
case UPARROWKEY:
diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c
index be265ed4bd5..26b68707d55 100644
--- a/source/blender/editors/gpencil/gpencil_select.c
+++ b/source/blender/editors/gpencil/gpencil_select.c
@@ -84,17 +84,40 @@ static int gpencil_select_mode_from_sculpt(eGP_Sculpt_SelectMaskFlag mode)
}
}
+/* Convert vertex mask mode to Select mode */
+static int gpencil_select_mode_from_vertex(eGP_Sculpt_SelectMaskFlag mode)
+{
+ if (mode & GP_VERTEX_MASK_SELECTMODE_POINT) {
+ return GP_SELECTMODE_POINT;
+ }
+ else if (mode & GP_VERTEX_MASK_SELECTMODE_STROKE) {
+ return GP_SELECTMODE_STROKE;
+ }
+ else if (mode & GP_VERTEX_MASK_SELECTMODE_SEGMENT) {
+ return GP_SELECTMODE_SEGMENT;
+ }
+ else {
+ return GP_SELECTMODE_POINT;
+ }
+}
+
static bool gpencil_select_poll(bContext *C)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
+ ToolSettings *ts = CTX_data_tool_settings(C);
if (GPENCIL_SCULPT_MODE(gpd)) {
- ToolSettings *ts = CTX_data_tool_settings(C);
if (!(GPENCIL_ANY_SCULPT_MASK(ts->gpencil_selectmode_sculpt))) {
return false;
}
}
+ if (GPENCIL_VERTEX_MODE(gpd)) {
+ if (!(GPENCIL_ANY_VERTEX_MASK(ts->gpencil_selectmode_vertex))) {
+ return false;
+ }
+ }
+
/* we just need some visible strokes, and to be in editmode or other modes only to catch event */
if (GPENCIL_ANY_MODE(gpd)) {
/* TODO: include a check for visible strokes? */
@@ -350,7 +373,7 @@ static void gp_select_same_layer(bContext *C)
Scene *scene = CTX_data_scene(C);
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
- bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_USE_PREV);
+ bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_USE_PREV);
bGPDstroke *gps;
bool found = false;
@@ -832,7 +855,7 @@ void GPENCIL_OT_select_less(wmOperatorType *ot)
* from gpencil_paint.c #gp_stroke_eraser_dostroke().
* It would be great to de-duplicate the logic here sometime, but that can wait.
*/
-static bool gp_stroke_do_circle_sel(bGPdata *gpd,
+static bool gp_stroke_do_circle_sel(bGPdata *UNUSED(gpd),
bGPDlayer *gpl,
bGPDstroke *gps,
GP_SpaceConversion *gsc,
@@ -845,13 +868,12 @@ static bool gp_stroke_do_circle_sel(bGPdata *gpd,
const int selectmode,
const float scale)
{
- const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
bGPDspoint *pt1 = NULL;
bGPDspoint *pt2 = NULL;
int x0 = 0, y0 = 0, x1 = 0, y1 = 0;
int i;
bool changed = false;
- bGPDstroke *gps_active = (!is_multiedit) ? gps->runtime.gps_orig : gps;
+ bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
bGPDspoint *pt_active = NULL;
if (gps->totpoints == 1) {
@@ -910,22 +932,22 @@ static bool gp_stroke_do_circle_sel(bGPdata *gpd,
*/
hit = true;
if (select) {
- pt_active = (!is_multiedit) ? pt1->runtime.pt_orig : pt1;
+ pt_active = pt1->runtime.pt_orig;
if (pt_active != NULL) {
pt_active->flag |= GP_SPOINT_SELECT;
}
- pt_active = (!is_multiedit) ? pt2->runtime.pt_orig : pt2;
+ pt_active = pt2->runtime.pt_orig;
if (pt_active != NULL) {
pt_active->flag |= GP_SPOINT_SELECT;
}
changed = true;
}
else {
- pt_active = (!is_multiedit) ? pt1->runtime.pt_orig : pt1;
+ pt_active = pt1->runtime.pt_orig;
if (pt_active != NULL) {
pt_active->flag &= ~GP_SPOINT_SELECT;
}
- pt_active = (!is_multiedit) ? pt2->runtime.pt_orig : pt2;
+ pt_active = pt2->runtime.pt_orig;
if (pt_active != NULL) {
pt_active->flag &= ~GP_SPOINT_SELECT;
}
@@ -942,7 +964,7 @@ static bool gp_stroke_do_circle_sel(bGPdata *gpd,
/* if stroke mode expand selection */
if ((hit) && (selectmode == GP_SELECTMODE_STROKE)) {
for (i = 0, pt1 = gps->points; i < gps->totpoints; i++, pt1++) {
- pt_active = (!is_multiedit) ? pt1->runtime.pt_orig : pt1;
+ pt_active = (pt1->runtime.pt_orig) ? pt1->runtime.pt_orig : pt1;
if (pt_active != NULL) {
if (select) {
pt_active->flag |= GP_SPOINT_SELECT;
@@ -955,7 +977,7 @@ static bool gp_stroke_do_circle_sel(bGPdata *gpd,
}
/* expand selection to segment */
- pt_active = (!is_multiedit) ? pt1->runtime.pt_orig : pt1;
+ pt_active = (pt1->runtime.pt_orig) ? pt1->runtime.pt_orig : pt1;
if ((hit) && (selectmode == GP_SELECTMODE_SEGMENT) && (select) && (pt_active != NULL)) {
float r_hita[3], r_hitb[3];
bool hit_select = (bool)(pt1->flag & GP_SPOINT_SELECT);
@@ -976,9 +998,17 @@ static int gpencil_circle_select_exec(bContext *C, wmOperator *op)
ToolSettings *ts = CTX_data_tool_settings(C);
Object *ob = CTX_data_active_object(C);
- const int selectmode = (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) ?
- gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt) :
- ts->gpencil_selectmode_edit;
+ int selectmode;
+ if (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) {
+ selectmode = gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt);
+ }
+ else if (ob && ob->mode == OB_MODE_VERTEX_GPENCIL) {
+ selectmode = gpencil_select_mode_from_vertex(ts->gpencil_selectmode_vertex);
+ }
+ else {
+ selectmode = ts->gpencil_selectmode_edit;
+ }
+
const float scale = ts->gp_sculpt.isect_threshold;
/* if not edit/sculpt mode, the event is catched but not processed */
@@ -1100,9 +1130,16 @@ static int gpencil_generic_select_exec(bContext *C,
ToolSettings *ts = CTX_data_tool_settings(C);
ScrArea *sa = CTX_wm_area(C);
- const short selectmode = (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) ?
- gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt) :
- ts->gpencil_selectmode_edit;
+ int selectmode;
+ if (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) {
+ selectmode = gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt);
+ }
+ else if (ob && ob->mode == OB_MODE_VERTEX_GPENCIL) {
+ selectmode = gpencil_select_mode_from_vertex(ts->gpencil_selectmode_vertex);
+ }
+ else {
+ selectmode = ts->gpencil_selectmode_edit;
+ }
const bool strokemode = ((selectmode == GP_SELECTMODE_STROKE) &&
((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0));
@@ -1145,16 +1182,16 @@ static int gpencil_generic_select_exec(bContext *C,
/* select/deselect points */
GP_EVALUATED_STROKES_BEGIN(gpstroke_iter, C, gpl, gps)
{
- bGPDstroke *gps_active = (!is_multiedit) ? gps->runtime.gps_orig : gps;
+ bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
bGPDspoint *pt;
int i;
bool hit = false;
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if ((!is_multiedit) && (pt->runtime.pt_orig == NULL)) {
+ if (pt->runtime.pt_orig == NULL) {
continue;
}
- bGPDspoint *pt_active = (!is_multiedit) ? pt->runtime.pt_orig : pt;
+ bGPDspoint *pt_active = pt->runtime.pt_orig;
/* convert point coords to screenspace */
const bool is_inside = is_inside_fn(gps, pt, &gsc, gpstroke_iter.diff_mat, user_data);
@@ -1192,7 +1229,7 @@ static int gpencil_generic_select_exec(bContext *C,
if ((!is_multiedit) && (pt->runtime.pt_orig == NULL)) {
continue;
}
- bGPDspoint *pt_active = (!is_multiedit) ? pt->runtime.pt_orig : pt;
+ bGPDspoint *pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
if (sel_op_result) {
pt_active->flag |= GP_SPOINT_SELECT;
@@ -1419,6 +1456,10 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
whole = (bool)(gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt) ==
GP_SELECTMODE_STROKE);
}
+ else if ((ob) && (ob->mode == OB_MODE_VERTEX_GPENCIL)) {
+ whole = (bool)(gpencil_select_mode_from_vertex(ts->gpencil_selectmode_sculpt) ==
+ GP_SELECTMODE_STROKE);
+ }
else {
whole = (bool)(ts->gpencil_selectmode_edit == GP_SELECTMODE_STROKE);
}
@@ -1433,7 +1474,7 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
/* XXX: maybe we should go from the top of the stack down instead... */
GP_EVALUATED_STROKES_BEGIN(gpstroke_iter, C, gpl, gps)
{
- bGPDstroke *gps_active = (!is_multiedit) ? gps->runtime.gps_orig : gps;
+ bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
bGPDspoint *pt;
int i;
@@ -1526,9 +1567,16 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
hit_stroke->flag |= GP_STROKE_SELECT;
/* expand selection to segment */
- const short selectmode = (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) ?
- gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt) :
- ts->gpencil_selectmode_edit;
+ int selectmode;
+ if (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) {
+ selectmode = gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt);
+ }
+ else if (ob && ob->mode == OB_MODE_VERTEX_GPENCIL) {
+ selectmode = gpencil_select_mode_from_vertex(ts->gpencil_selectmode_vertex);
+ }
+ else {
+ selectmode = ts->gpencil_selectmode_edit;
+ }
if (selectmode == GP_SELECTMODE_SEGMENT) {
float r_hita[3], r_hitb[3];
@@ -1606,4 +1654,108 @@ void GPENCIL_OT_select(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_HIDDEN);
}
+/* Select by Vertex Color. */
+static bool gpencil_select_color_poll(bContext *C)
+{
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ Object *ob = CTX_data_active_object(C);
+ if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
+ return false;
+ }
+ bGPdata *gpd = (bGPdata *)ob->data;
+
+ if (GPENCIL_VERTEX_MODE(gpd)) {
+ if (!(GPENCIL_ANY_VERTEX_MASK(ts->gpencil_selectmode_vertex))) {
+ return false;
+ }
+
+ /* Any data to use. */
+ if (gpd->layers.first) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static int gpencil_select_color_exec(bContext *C, wmOperator *op)
+{
+ const float threshold = RNA_float_get(op->ptr, "threshold");
+
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = (bGPdata *)ob->data;
+ if (!GPENCIL_VERTEX_MODE(gpd)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ Paint *paint = &ts->gp_vertexpaint->paint;
+ Brush *brush = paint->brush;
+ bool done = false;
+
+ float hsv_brush[3], hsv_stroke[3];
+ rgb_to_hsv_compat_v(brush->rgb, hsv_brush);
+
+ /* Select any visible stroke that uses this color */
+ CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
+ bGPDspoint *pt;
+ int i;
+ bool gps_selected = false;
+ /* Check all stroke points. */
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if (pt->vert_color[3] < 0.03f) {
+ continue;
+ }
+
+ rgb_to_hsv_compat_v(pt->vert_color, hsv_stroke);
+ /* Only check Hue to get full value and saturation ranges. */
+ if (compare_ff(hsv_stroke[0], hsv_brush[0], threshold)) {
+ pt->flag |= GP_SPOINT_SELECT;
+ gps_selected = true;
+ }
+ }
+
+ if (gps_selected) {
+ gps->flag |= GP_STROKE_SELECT;
+ done = true;
+ }
+ }
+ CTX_DATA_END;
+
+ if (done) {
+ /* updates */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
+
+ /* copy on write tag is needed, or else no refresh happens */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE);
+
+ WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL);
+ WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_select_color(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Select Color";
+ ot->idname = "GPENCIL_OT_select_color";
+ ot->description = "Select all strokes with same color";
+
+ /* callbacks */
+ ot->exec = gpencil_select_color_exec;
+ ot->poll = gpencil_select_color_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ prop = RNA_def_float(ot->srna, "threshold", 0.01f, 0.0f, 1.0f, "Threshold", "", 0.0f, 1.0f);
+ /* avoid re-using last var */
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+}
+
/** \} */
diff --git a/source/blender/editors/gpencil/gpencil_undo.c b/source/blender/editors/gpencil/gpencil_undo.c
index 7b57dacd3e4..f9403fd94b0 100644
--- a/source/blender/editors/gpencil/gpencil_undo.c
+++ b/source/blender/editors/gpencil/gpencil_undo.c
@@ -90,14 +90,14 @@ int ED_undo_gpencil_step(bContext *C, int step, const char *name)
if (gpd_ptr) {
if (*gpd_ptr) {
bGPdata *gpd = *gpd_ptr;
- bGPDlayer *gpl, *gpld;
+ bGPDlayer *gpld;
BKE_gpencil_free_layers(&gpd->layers);
/* copy layers */
BLI_listbase_clear(&gpd->layers);
- for (gpl = new_gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
/* make a copy of source layer and its data */
gpld = BKE_gpencil_layer_duplicate(gpl);
BLI_addtail(&gpd->layers, gpld);
diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c
index 9c2e0f100cc..a975af1c19a 100644
--- a/source/blender/editors/gpencil/gpencil_utils.c
+++ b/source/blender/editors/gpencil/gpencil_utils.c
@@ -328,7 +328,7 @@ bool ED_gpencil_data_owner_is_annotation(PointerRNA *owner_ptr)
bool ED_gpencil_has_keyframe_v3d(Scene *UNUSED(scene), Object *ob, int cfra)
{
if (ob && ob->data && (ob->type == OB_GPENCIL)) {
- bGPDlayer *gpl = BKE_gpencil_layer_getactive(ob->data);
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(ob->data);
if (gpl) {
if (gpl->actframe) {
// XXX: assumes that frame has been fetched already
@@ -336,7 +336,7 @@ bool ED_gpencil_has_keyframe_v3d(Scene *UNUSED(scene), Object *ob, int cfra)
}
else {
/* XXX: disabled as could be too much of a penalty */
- /* return BKE_gpencil_layer_find_frame(gpl, cfra); */
+ /* return BKE_gpencil_layer_frame_find(gpl, cfra); */
}
}
}
@@ -367,7 +367,7 @@ bool gp_active_layer_poll(bContext *C)
return false;
}
bGPdata *gpd = (bGPdata *)ob->data;
- bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
return (gpl != NULL);
}
@@ -553,10 +553,10 @@ bool ED_gpencil_stroke_color_use(Object *ob, const bGPDlayer *gpl, const bGPDstr
MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
if (gp_style != NULL) {
- if (gp_style->flag & GP_STYLE_COLOR_HIDE) {
+ if (gp_style->flag & GP_MATERIAL_HIDE) {
return false;
}
- if (((gpl->flag & GP_LAYER_UNLOCK_COLOR) == 0) && (gp_style->flag & GP_STYLE_COLOR_LOCKED)) {
+ if (((gpl->flag & GP_LAYER_UNLOCK_COLOR) == 0) && (gp_style->flag & GP_MATERIAL_LOCKED)) {
return false;
}
}
@@ -630,8 +630,7 @@ void gp_point_to_parent_space(const bGPDspoint *pt, const float diff_mat[4][4],
/**
* Change position relative to parent object
*/
-void gp_apply_parent(
- Depsgraph *depsgraph, Object *obact, bGPdata *gpd, bGPDlayer *gpl, bGPDstroke *gps)
+void gp_apply_parent(Depsgraph *depsgraph, Object *obact, bGPDlayer *gpl, bGPDstroke *gps)
{
bGPDspoint *pt;
int i;
@@ -641,7 +640,7 @@ void gp_apply_parent(
float inverse_diff_mat[4][4];
float fpt[3];
- ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat);
+ BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat);
invert_m4_m4(inverse_diff_mat, diff_mat);
for (i = 0; i < gps->totpoints; i++) {
@@ -654,15 +653,14 @@ void gp_apply_parent(
/**
* Change point position relative to parent object
*/
-void gp_apply_parent_point(
- Depsgraph *depsgraph, Object *obact, bGPdata *gpd, bGPDlayer *gpl, bGPDspoint *pt)
+void gp_apply_parent_point(Depsgraph *depsgraph, Object *obact, bGPDlayer *gpl, bGPDspoint *pt)
{
/* undo matrix */
float diff_mat[4][4];
float inverse_diff_mat[4][4];
float fpt[3];
- ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat);
+ BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat);
invert_m4_m4(inverse_diff_mat, diff_mat);
mul_v3_m4v3(fpt, inverse_diff_mat, &pt->x);
@@ -870,7 +868,7 @@ bool gp_point_xy_to_3d(const GP_SpaceConversion *gsc,
const RegionView3D *rv3d = gsc->region->regiondata;
float rvec[3];
- ED_gp_get_drawing_reference(
+ ED_gpencil_drawing_reference_get(
scene, gsc->ob, gsc->gpl, scene->toolsettings->gpencil_v3d_align, rvec);
float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL);
@@ -930,7 +928,7 @@ void gp_stroke_convertcoords_tpoint(Scene *scene,
/* Current method just converts each point in screen-coordinates to
* 3D-coordinates using the 3D-cursor as reference.
*/
- ED_gp_get_drawing_reference(scene, ob, gpl, ts->gpencil_v3d_align, rvec);
+ ED_gpencil_drawing_reference_get(scene, ob, gpl, ts->gpencil_v3d_align, rvec);
zfac = ED_view3d_calc_zfac(region->regiondata, rvec, NULL);
if (ED_view3d_project_float_global(region, rvec, mval_prj, V3D_PROJ_TEST_NOP) ==
@@ -949,7 +947,7 @@ void gp_stroke_convertcoords_tpoint(Scene *scene,
* Get drawing reference point for conversion or projection of the stroke
* \param[out] r_vec : Reference point found
*/
-void ED_gp_get_drawing_reference(
+void ED_gpencil_drawing_reference_get(
const Scene *scene, const Object *ob, bGPDlayer *UNUSED(gpl), char align_flag, float r_vec[3])
{
const float *fp = scene->cursor.location;
@@ -979,7 +977,6 @@ void ED_gpencil_project_stroke_to_view(bContext *C, bGPDlayer *gpl, bGPDstroke *
Scene *scene = CTX_data_scene(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Object *ob = CTX_data_active_object(C);
- bGPdata *gpd = (bGPdata *)ob->data;
GP_SpaceConversion gsc = {NULL};
bGPDspoint *pt;
@@ -990,7 +987,7 @@ void ED_gpencil_project_stroke_to_view(bContext *C, bGPDlayer *gpl, bGPDstroke *
/* init space conversion stuff */
gp_point_conversion_init(C, &gsc);
- ED_gpencil_parent_location(depsgraph, ob, gpd, gpl, diff_mat);
+ BKE_gpencil_parent_matrix_get(depsgraph, ob, gpl, diff_mat);
invert_m4_m4(inverse_diff_mat, diff_mat);
/* Adjust each point */
@@ -1182,7 +1179,6 @@ void gp_subdivide_stroke(bGPDstroke *gps, const int subdivide)
if (gps->dvert != NULL) {
gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints);
}
- gps->flag |= GP_STROKE_RECALC_GEOMETRY;
/* move points from last to first to new place */
i2 = gps->totpoints - 1;
@@ -1197,6 +1193,7 @@ void gp_subdivide_stroke(bGPDstroke *gps, const int subdivide)
pt_final->flag = pt->flag;
pt_final->uv_fac = pt->uv_fac;
pt_final->uv_rot = pt->uv_rot;
+ copy_v4_v4(pt_final->vert_color, pt->vert_color);
if (gps->dvert != NULL) {
MDeformVert *dvert = &gps->dvert[i];
@@ -1223,6 +1220,7 @@ void gp_subdivide_stroke(bGPDstroke *gps, const int subdivide)
pt_final->time = interpf(pt->time, next->time, 0.5f);
pt_final->uv_fac = interpf(pt->uv_fac, next->uv_fac, 0.5f);
pt_final->uv_rot = interpf(pt->uv_rot, next->uv_rot, 0.5f);
+ interp_v4_v4v4(pt_final->vert_color, pt->vert_color, next->vert_color, 0.5f);
if (gps->dvert != NULL) {
MDeformVert *dvert_final = &gps->dvert[i2];
@@ -1251,116 +1249,11 @@ void gp_subdivide_stroke(bGPDstroke *gps, const int subdivide)
/* free temp memory */
MEM_SAFE_FREE(temp_points);
}
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gps);
}
-/**
- * Add randomness to stroke
- * \param gps: Stroke data
- * \param brush: Brush data
- */
-void gp_randomize_stroke(bGPDstroke *gps, Brush *brush, RNG *rng)
-{
- bGPDspoint *pt1, *pt2, *pt3;
- float v1[3];
- float v2[3];
- if (gps->totpoints < 3) {
- return;
- }
-
- /* get two vectors using 3 points */
- pt1 = &gps->points[0];
- pt2 = &gps->points[1];
- pt3 = &gps->points[(int)(gps->totpoints * 0.75)];
-
- sub_v3_v3v3(v1, &pt2->x, &pt1->x);
- sub_v3_v3v3(v2, &pt3->x, &pt2->x);
- normalize_v3(v1);
- normalize_v3(v2);
-
- /* get normal vector to plane created by two vectors */
- float normal[3];
- cross_v3_v3v3(normal, v1, v2);
- normalize_v3(normal);
-
- /* get orthogonal vector to plane to rotate random effect */
- float ortho[3];
- cross_v3_v3v3(ortho, v1, normal);
- normalize_v3(ortho);
-
- /* Read all points and apply shift vector (first and last point not modified) */
- for (int i = 1; i < gps->totpoints - 1; i++) {
- bGPDspoint *pt = &gps->points[i];
- /* get vector with shift (apply a division because random is too sensitive */
- const float fac = BLI_rng_get_float(rng) * (brush->gpencil_settings->draw_random_sub / 10.0f);
- float svec[3];
- copy_v3_v3(svec, ortho);
- if (BLI_rng_get_float(rng) > 0.5f) {
- mul_v3_fl(svec, -fac);
- }
- else {
- mul_v3_fl(svec, fac);
- }
-
- /* apply shift */
- add_v3_v3(&pt->x, svec);
- }
-}
-
-/* ******************************************************** */
-/* Layer Parenting - Compute Parent Transforms */
-
-/* calculate difference matrix */
-void ED_gpencil_parent_location(const Depsgraph *depsgraph,
- Object *obact,
- bGPdata *UNUSED(gpd),
- bGPDlayer *gpl,
- float diff_mat[4][4])
-{
- Object *ob_eval = depsgraph != NULL ? DEG_get_evaluated_object(depsgraph, obact) : obact;
- Object *obparent = gpl->parent;
- Object *obparent_eval = depsgraph != NULL ? DEG_get_evaluated_object(depsgraph, obparent) :
- obparent;
-
- /* if not layer parented, try with object parented */
- if (obparent_eval == NULL) {
- if (ob_eval != NULL) {
- if (ob_eval->type == OB_GPENCIL) {
- copy_m4_m4(diff_mat, ob_eval->obmat);
- return;
- }
- }
- /* not gpencil object */
- unit_m4(diff_mat);
- return;
- }
- else {
- if ((gpl->partype == PAROBJECT) || (gpl->partype == PARSKEL)) {
- mul_m4_m4m4(diff_mat, obparent_eval->obmat, gpl->inverse);
- add_v3_v3(diff_mat[3], ob_eval->obmat[3]);
- return;
- }
- else if (gpl->partype == PARBONE) {
- bPoseChannel *pchan = BKE_pose_channel_find_name(obparent_eval->pose, gpl->parsubstr);
- if (pchan) {
- float tmp_mat[4][4];
- mul_m4_m4m4(tmp_mat, obparent_eval->obmat, pchan->pose_mat);
- mul_m4_m4m4(diff_mat, tmp_mat, gpl->inverse);
- add_v3_v3(diff_mat[3], ob_eval->obmat[3]);
- }
- else {
- /* if bone not found use object (armature) */
- mul_m4_m4m4(diff_mat, obparent_eval->obmat, gpl->inverse);
- add_v3_v3(diff_mat[3], ob_eval->obmat[3]);
- }
- return;
- }
- else {
- unit_m4(diff_mat); /* not defined type */
- }
- }
-}
-
-/* reset parent matrix for all layers */
+/* Reset parent matrix for all layers. */
void ED_gpencil_reset_layers_parent(Depsgraph *depsgraph, Object *obact, bGPdata *gpd)
{
bGPDspoint *pt;
@@ -1370,7 +1263,7 @@ void ED_gpencil_reset_layers_parent(Depsgraph *depsgraph, Object *obact, bGPdata
float gpl_loc[3];
zero_v3(gpl_loc);
- for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
if (gpl->parent != NULL) {
/* calculate new matrix */
if ((gpl->partype == PAROBJECT) || (gpl->partype == PARSKEL)) {
@@ -1390,12 +1283,12 @@ void ED_gpencil_reset_layers_parent(Depsgraph *depsgraph, Object *obact, bGPdata
/* only redo if any change */
if (!equals_m4m4(gpl->inverse, cur_mat)) {
/* first apply current transformation to all strokes */
- ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat);
+ BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat);
/* undo local object */
sub_v3_v3(diff_mat[3], gpl_loc);
- for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
- for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
mul_m4_v3(diff_mat, &pt->x);
}
@@ -1411,10 +1304,7 @@ void ED_gpencil_reset_layers_parent(Depsgraph *depsgraph, Object *obact, bGPdata
/* GP Object Stuff */
/* Helper function to create new OB_GPENCIL Object */
-Object *ED_gpencil_add_object(bContext *C,
- Scene *UNUSED(scene),
- const float loc[3],
- ushort local_view_bits)
+Object *ED_gpencil_add_object(bContext *C, const float loc[3], ushort local_view_bits)
{
float rot[3] = {0.0f};
@@ -1437,7 +1327,7 @@ void ED_gpencil_add_defaults(bContext *C, Object *ob)
/* if not exist, create a new one */
if ((paint->brush == NULL) || (paint->brush->gpencil_settings == NULL)) {
/* create new brushes */
- BKE_brush_gpencil_presets(bmain, ts);
+ BKE_brush_gpencil_paint_presets(bmain, ts);
}
/* ensure a color exists and is assigned to object */
@@ -1676,8 +1566,11 @@ static bool gp_check_cursor_region(bContext *C, int mval_i[2])
ScrArea *sa = CTX_wm_area(C);
Object *ob = CTX_data_active_object(C);
- if ((ob == NULL) ||
- (!ELEM(ob->mode, OB_MODE_PAINT_GPENCIL, OB_MODE_SCULPT_GPENCIL, OB_MODE_WEIGHT_GPENCIL))) {
+ if ((ob == NULL) || (!ELEM(ob->mode,
+ OB_MODE_PAINT_GPENCIL,
+ OB_MODE_SCULPT_GPENCIL,
+ OB_MODE_WEIGHT_GPENCIL,
+ OB_MODE_VERTEX_GPENCIL))) {
return false;
}
@@ -1753,22 +1646,14 @@ static void gp_brush_cursor_draw(bContext *C, int x, int y, void *customdata)
Scene *scene = CTX_data_scene(C);
Object *ob = CTX_data_active_object(C);
ARegion *region = CTX_wm_region(C);
+ Paint *paint = BKE_paint_get_active_from_context(C);
- GP_Sculpt_Settings *gset = &scene->toolsettings->gp_sculpt;
bGPdata *gpd = ED_gpencil_data_get_active(C);
- GP_Sculpt_Data *gp_brush = NULL;
Brush *brush = NULL;
Material *ma = NULL;
MaterialGPencilStyle *gp_style = NULL;
float *last_mouse_position = customdata;
- if ((gpd) && (gpd->flag & GP_DATA_STROKE_WEIGHTMODE)) {
- gp_brush = &gset->brush[gset->weighttype];
- }
- else {
- gp_brush = &gset->brush[gset->brushtype];
- }
-
/* default radius and color */
float color[3] = {1.0f, 1.0f, 1.0f};
float darkcolor[3];
@@ -1794,7 +1679,7 @@ static void gp_brush_cursor_draw(bContext *C, int x, int y, void *customdata)
return;
}
- if ((brush->gpencil_settings->flag & GP_BRUSH_ENABLE_CURSOR) == 0) {
+ if ((paint->flags & PAINT_SHOW_BRUSH) == 0) {
return;
}
@@ -1805,7 +1690,7 @@ static void gp_brush_cursor_draw(bContext *C, int x, int y, void *customdata)
}
/* get current drawing color */
- ma = BKE_gpencil_object_material_get_from_brush(ob, brush);
+ ma = BKE_gpencil_object_material_from_brush_get(ob, brush);
if (ma) {
gp_style = ma->gp_style;
@@ -1822,29 +1707,73 @@ static void gp_brush_cursor_draw(bContext *C, int x, int y, void *customdata)
copy_v3_v3(color, gp_style->stroke_rgba);
}
else {
- radius = 5.0f;
- copy_v3_v3(color, brush->add_col);
+ /* Only Tint tool must show big cursor. */
+ if (brush->gpencil_tool == GPAINT_TOOL_TINT) {
+ radius = brush->size;
+ copy_v3_v3(color, brush->rgb);
+ }
+ else {
+ radius = 5.0f;
+ copy_v3_v3(color, brush->add_col);
+ }
}
}
}
- /* for sculpt use sculpt brush size */
- if (GPENCIL_SCULPT_OR_WEIGHT_MODE(gpd)) {
- if (gp_brush) {
- if ((gp_brush->flag & GP_SCULPT_FLAG_ENABLE_CURSOR) == 0) {
- return;
- }
+ /* Sculpt use sculpt brush size */
+ if (GPENCIL_SCULPT_MODE(gpd)) {
+ brush = scene->toolsettings->gp_sculptpaint->paint.brush;
+ if ((brush == NULL) || (brush->gpencil_settings == NULL)) {
+ return;
+ }
+ if ((paint->flags & PAINT_SHOW_BRUSH) == 0) {
+ return;
+ }
- radius = gp_brush->size;
- if (gp_brush->flag & (GP_SCULPT_FLAG_INVERT | GP_SCULPT_FLAG_TMP_INVERT)) {
- copy_v3_v3(color, gp_brush->curcolor_sub);
- }
- else {
- copy_v3_v3(color, gp_brush->curcolor_add);
- }
+ radius = brush->size;
+ if (brush->gpencil_settings->sculpt_flag &
+ (GP_SCULPT_FLAG_INVERT | GP_SCULPT_FLAG_TMP_INVERT)) {
+ copy_v3_v3(color, brush->sub_col);
+ }
+ else {
+ copy_v3_v3(color, brush->add_col);
+ }
+ }
+
+ /* Weight Paint */
+ if (GPENCIL_WEIGHT_MODE(gpd)) {
+ brush = scene->toolsettings->gp_weightpaint->paint.brush;
+ if ((brush == NULL) || (brush->gpencil_settings == NULL)) {
+ return;
+ }
+ if ((paint->flags & PAINT_SHOW_BRUSH) == 0) {
+ return;
+ }
+
+ radius = brush->size;
+ if (brush->gpencil_settings->sculpt_flag &
+ (GP_SCULPT_FLAG_INVERT | GP_SCULPT_FLAG_TMP_INVERT)) {
+ copy_v3_v3(color, brush->sub_col);
+ }
+ else {
+ copy_v3_v3(color, brush->add_col);
}
}
+ /* For Vertex Paint use brush size. */
+ if (GPENCIL_VERTEX_MODE(gpd)) {
+ brush = scene->toolsettings->gp_vertexpaint->paint.brush;
+ if ((brush == NULL) || (brush->gpencil_settings == NULL)) {
+ return;
+ }
+ if ((paint->flags & PAINT_SHOW_BRUSH) == 0) {
+ return;
+ }
+
+ radius = brush->size;
+ copy_v3_v3(color, brush->rgb);
+ }
+
/* draw icon */
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
@@ -1924,30 +1853,6 @@ void ED_gpencil_toggle_brush_cursor(bContext *C, bool enable, void *customdata)
}
}
-/* verify if is using the right brush */
-static void gpencil_verify_brush_type(bContext *C, int newmode)
-{
- ToolSettings *ts = CTX_data_tool_settings(C);
- GP_Sculpt_Settings *gset = &ts->gp_sculpt;
-
- switch (newmode) {
- case OB_MODE_SCULPT_GPENCIL:
- gset->flag &= ~GP_SCULPT_SETT_FLAG_WEIGHT_MODE;
- if ((gset->brushtype < 0) || (gset->brushtype >= GP_SCULPT_TYPE_WEIGHT)) {
- gset->brushtype = GP_SCULPT_TYPE_PUSH;
- }
- break;
- case OB_MODE_WEIGHT_GPENCIL:
- gset->flag |= GP_SCULPT_SETT_FLAG_WEIGHT_MODE;
- if ((gset->weighttype < GP_SCULPT_TYPE_WEIGHT) || (gset->weighttype >= GP_SCULPT_TYPE_MAX)) {
- gset->weighttype = GP_SCULPT_TYPE_WEIGHT;
- }
- break;
- default:
- break;
- }
-}
-
/* set object modes */
void ED_gpencil_setup_modes(bContext *C, bGPdata *gpd, int newmode)
{
@@ -1961,6 +1866,7 @@ void ED_gpencil_setup_modes(bContext *C, bGPdata *gpd, int newmode)
gpd->flag &= ~GP_DATA_STROKE_PAINTMODE;
gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE;
gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE;
+ gpd->flag &= ~GP_DATA_STROKE_VERTEXMODE;
ED_gpencil_toggle_brush_cursor(C, false, NULL);
break;
case OB_MODE_PAINT_GPENCIL:
@@ -1968,6 +1874,7 @@ void ED_gpencil_setup_modes(bContext *C, bGPdata *gpd, int newmode)
gpd->flag |= GP_DATA_STROKE_PAINTMODE;
gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE;
gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE;
+ gpd->flag &= ~GP_DATA_STROKE_VERTEXMODE;
ED_gpencil_toggle_brush_cursor(C, true, NULL);
break;
case OB_MODE_SCULPT_GPENCIL:
@@ -1975,7 +1882,7 @@ void ED_gpencil_setup_modes(bContext *C, bGPdata *gpd, int newmode)
gpd->flag &= ~GP_DATA_STROKE_PAINTMODE;
gpd->flag |= GP_DATA_STROKE_SCULPTMODE;
gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE;
- gpencil_verify_brush_type(C, OB_MODE_SCULPT_GPENCIL);
+ gpd->flag &= ~GP_DATA_STROKE_VERTEXMODE;
ED_gpencil_toggle_brush_cursor(C, true, NULL);
break;
case OB_MODE_WEIGHT_GPENCIL:
@@ -1983,7 +1890,15 @@ void ED_gpencil_setup_modes(bContext *C, bGPdata *gpd, int newmode)
gpd->flag &= ~GP_DATA_STROKE_PAINTMODE;
gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE;
gpd->flag |= GP_DATA_STROKE_WEIGHTMODE;
- gpencil_verify_brush_type(C, OB_MODE_WEIGHT_GPENCIL);
+ gpd->flag &= ~GP_DATA_STROKE_VERTEXMODE;
+ ED_gpencil_toggle_brush_cursor(C, true, NULL);
+ break;
+ case OB_MODE_VERTEX_GPENCIL:
+ gpd->flag &= ~GP_DATA_STROKE_EDITMODE;
+ gpd->flag &= ~GP_DATA_STROKE_PAINTMODE;
+ gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE;
+ gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE;
+ gpd->flag |= GP_DATA_STROKE_VERTEXMODE;
ED_gpencil_toggle_brush_cursor(C, true, NULL);
break;
default:
@@ -1991,6 +1906,7 @@ void ED_gpencil_setup_modes(bContext *C, bGPdata *gpd, int newmode)
gpd->flag &= ~GP_DATA_STROKE_PAINTMODE;
gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE;
gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE;
+ gpd->flag &= ~GP_DATA_STROKE_VERTEXMODE;
ED_gpencil_toggle_brush_cursor(C, false, NULL);
break;
}
@@ -2032,6 +1948,7 @@ void ED_gpencil_tpoint_to_point(ARegion *region,
/* conversion to 3d format */
gpencil_stroke_convertcoords(region, tpt, origin, p3d);
copy_v3_v3(&pt->x, p3d);
+ zero_v4(pt->vert_color);
pt->pressure = tpt->pressure;
pt->strength = tpt->strength;
@@ -2039,63 +1956,6 @@ void ED_gpencil_tpoint_to_point(ARegion *region,
pt->uv_rot = tpt->uv_rot;
}
-/* texture coordinate utilities */
-void ED_gpencil_calc_stroke_uv(Object *ob, bGPDstroke *gps)
-{
- if (gps == NULL) {
- return;
- }
- MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
- float pixsize;
- if (gp_style) {
- pixsize = gp_style->texture_pixsize / 1000000.0f;
- }
- else {
- /* use this value by default */
- pixsize = 0.0001f;
- }
- pixsize = MAX2(pixsize, 0.0000001f);
-
- bGPDspoint *pt = NULL;
- bGPDspoint *ptb = NULL;
- int i;
- float totlen = 0.0f;
-
- /* first read all points and calc distance */
- for (i = 0; i < gps->totpoints; i++) {
- pt = &gps->points[i];
- /* first point */
- if (i == 0) {
- pt->uv_fac = 0.0f;
- continue;
- }
-
- ptb = &gps->points[i - 1];
- totlen += len_v3v3(&pt->x, &ptb->x) / pixsize;
- pt->uv_fac = totlen;
- }
-
- /* normalize the distance using a factor */
- float factor;
-
- /* if image, use texture width */
- if ((gp_style) && (gp_style->stroke_style == GP_STYLE_STROKE_STYLE_TEXTURE) &&
- (gp_style->sima)) {
- factor = gp_style->sima->gen_x;
- }
- else if (totlen == 0) {
- return;
- }
- else {
- factor = totlen;
- }
-
- for (i = 0; i < gps->totpoints; i++) {
- pt = &gps->points[i];
- pt->uv_fac /= factor;
- }
-}
-
/* recalc uv for any stroke using the material */
void ED_gpencil_update_color_uv(Main *bmain, Material *mat)
{
@@ -2108,11 +1968,11 @@ void ED_gpencil_update_color_uv(Main *bmain, Material *mat)
continue;
}
- for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
/* only editable and visible layers are considered */
- if (gpencil_layer_is_editable(gpl)) {
- for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
- for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ if (BKE_gpencil_layer_is_editable(gpl)) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
/* check if it is editable */
if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) {
continue;
@@ -2120,7 +1980,7 @@ void ED_gpencil_update_color_uv(Main *bmain, Material *mat)
gps_ma = BKE_gpencil_material(ob, gps->mat_nr + 1);
/* update */
if ((gps_ma) && (gps_ma == mat)) {
- ED_gpencil_calc_stroke_uv(ob, gps);
+ BKE_gpencil_stroke_uv_update(gps);
}
}
}
@@ -2197,6 +2057,7 @@ static void gp_copy_points(bGPDstroke *gps, bGPDspoint *pt, bGPDspoint *pt_final
pt_final->flag = pt->flag;
pt_final->uv_fac = pt->uv_fac;
pt_final->uv_rot = pt->uv_rot;
+ copy_v4_v4(pt_final->vert_color, pt->vert_color);
if (gps->dvert != NULL) {
MDeformVert *dvert = &gps->dvert[i];
@@ -2251,7 +2112,6 @@ static void gp_insert_point(
if (gps->dvert != NULL) {
gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints);
}
- gps->flag |= GP_STROKE_RECALC_GEOMETRY;
/* copy all points */
int i2 = 0;
@@ -2275,6 +2135,8 @@ static void gp_insert_point(
i2++;
}
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gps);
MEM_SAFE_FREE(temp_points);
}
@@ -2518,10 +2380,9 @@ void ED_gpencil_select_toggle_all(bContext *C, int action)
* nothing should be able to touch it
*/
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
- bGPDframe *gpf;
/* deselect all strokes on all frames */
- for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
bGPDstroke *gps;
for (gps = gpf->strokes.first; gps; gps = gps->next) {
@@ -2618,6 +2479,18 @@ tGPspoint *ED_gpencil_sbuffer_ensure(tGPspoint *buffer_array,
return buffer_array;
}
+void ED_gpencil_sbuffer_update_eval(bGPdata *gpd, Object *ob_eval)
+{
+ bGPdata *gpd_eval = (bGPdata *)ob_eval->data;
+
+ gpd_eval->runtime.sbuffer = gpd->runtime.sbuffer;
+ gpd_eval->runtime.sbuffer_sflag = gpd->runtime.sbuffer_sflag;
+ gpd_eval->runtime.sbuffer_used = gpd->runtime.sbuffer_used;
+ gpd_eval->runtime.sbuffer_size = gpd->runtime.sbuffer_size;
+ gpd_eval->runtime.tot_cp_points = gpd->runtime.tot_cp_points;
+ gpd_eval->runtime.cp_points = gpd->runtime.cp_points;
+}
+
/* Tag all scene grease pencil object to update. */
void ED_gpencil_tag_scene_gpencil(Scene *scene)
{
@@ -2638,3 +2511,107 @@ void ED_gpencil_tag_scene_gpencil(Scene *scene)
WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL);
}
+
+void ED_gpencil_fill_vertex_color_set(ToolSettings *ts, Brush *brush, bGPDstroke *gps)
+{
+ if (GPENCIL_USE_VERTEX_COLOR_FILL(ts, brush)) {
+ copy_v3_v3(gps->vert_color_fill, brush->rgb);
+ gps->vert_color_fill[3] = brush->gpencil_settings->vertex_factor;
+ srgb_to_linearrgb_v4(gps->vert_color_fill, gps->vert_color_fill);
+ }
+ else {
+ zero_v4(gps->vert_color_fill);
+ }
+}
+
+void ED_gpencil_point_vertex_color_set(ToolSettings *ts, Brush *brush, bGPDspoint *pt)
+{
+ if (GPENCIL_USE_VERTEX_COLOR_STROKE(ts, brush)) {
+ copy_v3_v3(pt->vert_color, brush->rgb);
+ pt->vert_color[3] = brush->gpencil_settings->vertex_factor;
+ srgb_to_linearrgb_v4(pt->vert_color, pt->vert_color);
+ }
+ else {
+ zero_v4(pt->vert_color);
+ }
+}
+
+void ED_gpencil_sbuffer_vertex_color_set(
+ Depsgraph *depsgraph, Object *ob, ToolSettings *ts, Brush *brush, Material *material)
+{
+ bGPdata *gpd = (bGPdata *)ob->data;
+ Object *ob_eval = (Object *)DEG_get_evaluated_id(depsgraph, &ob->id);
+ bGPdata *gpd_eval = (bGPdata *)ob_eval->data;
+ MaterialGPencilStyle *gp_style = material->gp_style;
+
+ float vertex_color[4];
+ copy_v3_v3(vertex_color, brush->rgb);
+ vertex_color[3] = brush->gpencil_settings->vertex_factor;
+ srgb_to_linearrgb_v4(vertex_color, vertex_color);
+
+ /* Copy fill vertex color. */
+ if (GPENCIL_USE_VERTEX_COLOR_FILL(ts, brush)) {
+ copy_v4_v4(gpd->runtime.vert_color_fill, vertex_color);
+ }
+ else {
+ copy_v4_v4(gpd->runtime.vert_color_fill, gp_style->fill_rgba);
+ }
+ /* Copy stroke vertex color. */
+ if (GPENCIL_USE_VERTEX_COLOR_STROKE(ts, brush)) {
+ copy_v4_v4(gpd->runtime.vert_color, vertex_color);
+ }
+ else {
+ copy_v4_v4(gpd->runtime.vert_color, gp_style->stroke_rgba);
+ }
+
+ /* Copy to eval data because paint operators don't tag refresh until end for speedup painting. */
+ if (gpd_eval != NULL) {
+ copy_v4_v4(gpd_eval->runtime.vert_color, gpd->runtime.vert_color);
+ copy_v4_v4(gpd_eval->runtime.vert_color_fill, gpd->runtime.vert_color_fill);
+ gpd_eval->runtime.matid = gpd->runtime.matid;
+ }
+}
+
+/* Check if the stroke collides with brush. */
+bool ED_gpencil_stroke_check_collision(GP_SpaceConversion *gsc,
+ bGPDstroke *gps,
+ float mouse[2],
+ const int radius,
+ const float diff_mat[4][4])
+{
+ const int offset = (int)ceil(sqrt((radius * radius) * 2));
+ bGPDspoint pt_dummy, pt_dummy_ps;
+ float boundbox_min[2] = {0.0f};
+ float boundbox_max[2] = {0.0f};
+ float zerov3[3];
+
+ /* Check we have something to use (only for old files). */
+ if (equals_v3v3(zerov3, gps->boundbox_min)) {
+ BKE_gpencil_stroke_boundingbox_calc(gps);
+ }
+
+ /* Convert bound box to 2d */
+ copy_v3_v3(&pt_dummy.x, gps->boundbox_min);
+ gp_point_to_parent_space(&pt_dummy, diff_mat, &pt_dummy_ps);
+ gp_point_to_xy_fl(gsc, gps, &pt_dummy_ps, &boundbox_min[0], &boundbox_min[1]);
+
+ copy_v3_v3(&pt_dummy.x, gps->boundbox_max);
+ gp_point_to_parent_space(&pt_dummy, diff_mat, &pt_dummy_ps);
+ gp_point_to_xy_fl(gsc, gps, &pt_dummy_ps, &boundbox_max[0], &boundbox_max[1]);
+
+ /* Ensure the bounding box is oriented to axis. */
+ if (boundbox_max[0] < boundbox_min[0]) {
+ SWAP(float, boundbox_min[0], boundbox_max[0]);
+ }
+ if (boundbox_max[1] < boundbox_min[1]) {
+ SWAP(float, boundbox_min[1], boundbox_max[1]);
+ }
+
+ rcti rect_stroke = {boundbox_min[0], boundbox_max[0], boundbox_min[1], boundbox_max[1]};
+
+ /* For mouse, add a small offet to avoid false negative in corners. */
+ rcti rect_mouse = {mouse[0] - offset, mouse[0] + offset, mouse[1] - offset, mouse[1] + offset};
+
+ /* Check collision between both rectangles. */
+ return BLI_rcti_isect(&rect_stroke, &rect_mouse, NULL);
+}
diff --git a/source/blender/editors/gpencil/gpencil_uv.c b/source/blender/editors/gpencil/gpencil_uv.c
new file mode 100644
index 00000000000..5e397374437
--- /dev/null
+++ b/source/blender/editors/gpencil/gpencil_uv.c
@@ -0,0 +1,587 @@
+/*
+ * 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.
+ */
+
+/** \file
+ * \ingroup edgpencil
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_gpencil_types.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_string.h"
+#include "BLI_math.h"
+
+#include "BLT_translation.h"
+
+#include "BKE_context.h"
+#include "BKE_gpencil.h"
+#include "BKE_unit.h"
+
+#include "RNA_define.h"
+#include "RNA_access.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "UI_interface.h"
+
+#include "ED_gpencil.h"
+#include "ED_numinput.h"
+#include "ED_screen.h"
+#include "ED_space_api.h"
+#include "ED_view3d.h"
+
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
+
+#include "gpencil_intern.h"
+
+typedef struct GpUvData {
+ Object *ob;
+ bGPdata *gpd;
+ GP_SpaceConversion gsc;
+ float ob_scale;
+
+ float initial_length;
+ float pixel_size; /* use when mouse input is interpreted as spatial distance */
+ bool is_modal;
+
+ /* Arrays of original loc/rot/scale by stroke. */
+ float (*array_loc)[2];
+ float *array_rot;
+ float *array_scale;
+
+ /* modal only */
+ float mcenter[2];
+ float mouse[2];
+
+ /** Vector with the original orientation. */
+ float vinit_rotation[2];
+
+ void *draw_handle_pixel;
+} GpUvData;
+
+enum {
+ GP_UV_ROTATE = 0,
+ GP_UV_TRANSLATE = 1,
+ GP_UV_SCALE = 2,
+ GP_UV_ALL = 3,
+};
+
+#define SMOOTH_FACTOR 0.3f
+
+static void gpencil_uv_transform_update_header(wmOperator *op, bContext *C)
+{
+ const int mode = RNA_enum_get(op->ptr, "mode");
+ const char *str = TIP_("Confirm: Enter/LClick, Cancel: (Esc/RClick) %s");
+
+ char msg[UI_MAX_DRAW_STR];
+ ScrArea *sa = CTX_wm_area(C);
+
+ if (sa) {
+ char flts_str[NUM_STR_REP_LEN * 2];
+ switch (mode) {
+ case GP_UV_TRANSLATE: {
+ float location[2];
+ RNA_float_get_array(op->ptr, "location", location);
+ BLI_snprintf(
+ flts_str, NUM_STR_REP_LEN, ", Translation: (%f, %f)", location[0], location[1]);
+ break;
+ }
+ case GP_UV_ROTATE: {
+ BLI_snprintf(flts_str,
+ NUM_STR_REP_LEN,
+ ", Rotation: %f",
+ RAD2DEG(RNA_float_get(op->ptr, "rotation")));
+ break;
+ }
+ case GP_UV_SCALE: {
+ BLI_snprintf(
+ flts_str, NUM_STR_REP_LEN, ", Scale: %f", RAD2DEG(RNA_float_get(op->ptr, "scale")));
+ break;
+ }
+ default:
+ break;
+ }
+ BLI_snprintf(msg, sizeof(msg), str, flts_str, flts_str + NUM_STR_REP_LEN);
+ ED_area_status_text(sa, msg);
+ }
+}
+
+/* Helper: Get stroke center. */
+static void gpencil_stroke_center(bGPDstroke *gps, float r_center[3])
+{
+ bGPDspoint *pt;
+ int i;
+
+ zero_v3(r_center);
+ if (gps->totpoints > 0) {
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ add_v3_v3(r_center, &pt->x);
+ }
+
+ mul_v3_fl(r_center, 1.0f / gps->totpoints);
+ }
+}
+
+static bool gpencil_uv_transform_init(bContext *C, wmOperator *op, const bool is_modal)
+{
+ GpUvData *opdata;
+ if (is_modal) {
+ float zero[2] = {0.0f};
+ RNA_float_set_array(op->ptr, "location", zero);
+ RNA_float_set(op->ptr, "rotation", 0.0f);
+ RNA_float_set(op->ptr, "scale", 1.0f);
+ }
+
+ op->customdata = opdata = MEM_mallocN(sizeof(GpUvData), __func__);
+
+ opdata->is_modal = is_modal;
+ opdata->ob = CTX_data_active_object(C);
+ opdata->gpd = (bGPdata *)opdata->ob->data;
+ gp_point_conversion_init(C, &opdata->gsc);
+ opdata->array_loc = NULL;
+ opdata->array_rot = NULL;
+ opdata->array_scale = NULL;
+ opdata->ob_scale = mat4_to_scale(opdata->ob->obmat);
+
+ opdata->vinit_rotation[0] = 1.0f;
+ opdata->vinit_rotation[1] = 0.0f;
+
+ if (is_modal) {
+ ARegion *region = CTX_wm_region(C);
+
+ opdata->draw_handle_pixel = ED_region_draw_cb_activate(
+ region->type, ED_region_draw_mouse_line_cb, opdata->mcenter, REGION_DRAW_POST_PIXEL);
+ }
+
+ /* Calc selected strokes center. */
+ zero_v2(opdata->mcenter);
+ float center[3] = {0.0f};
+ int i = 0;
+ /* Need use evaluated to get the viewport final position. */
+ GP_EVALUATED_STROKES_BEGIN(gpstroke_iter, C, gpl, gps)
+ {
+ if (gps->flag & GP_STROKE_SELECT) {
+ float r_center[3];
+ gpencil_stroke_center(gps, r_center);
+ /* Add object location. */
+ add_v3_v3(r_center, opdata->ob->obmat[3]);
+ add_v3_v3(center, r_center);
+ i++;
+ }
+ }
+ GP_EVALUATED_STROKES_END(gpstroke_iter);
+
+ if (i > 0) {
+ mul_v3_fl(center, 1.0f / i);
+ /* Create arrays to save all transformations. */
+ opdata->array_loc = MEM_calloc_arrayN(i, 2 * sizeof(float), __func__);
+ opdata->array_rot = MEM_calloc_arrayN(i, sizeof(float), __func__);
+ opdata->array_scale = MEM_calloc_arrayN(i, sizeof(float), __func__);
+ i = 0;
+ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ if (gps->flag & GP_STROKE_SELECT) {
+ copy_v2_v2(opdata->array_loc[i], gps->uv_translation);
+ opdata->array_rot[i] = gps->uv_rotation;
+ opdata->array_scale[i] = gps->uv_scale;
+ i++;
+ }
+ }
+ GP_EDITABLE_STROKES_END(gpstroke_iter);
+ }
+ /* convert to 2D */
+ gp_point_3d_to_xy(&opdata->gsc, GP_STROKE_3DSPACE, center, opdata->mcenter);
+
+ return true;
+}
+
+static void gpencil_uv_transform_exit(bContext *C, wmOperator *op)
+{
+ GpUvData *opdata;
+ ScrArea *sa = CTX_wm_area(C);
+
+ opdata = op->customdata;
+
+ if (opdata->is_modal) {
+ ARegion *region = CTX_wm_region(C);
+
+ ED_region_draw_cb_exit(region->type, opdata->draw_handle_pixel);
+ }
+
+ WM_cursor_set(CTX_wm_window(C), WM_CURSOR_DEFAULT);
+
+ if (sa) {
+ ED_area_status_text(sa, NULL);
+ }
+ WM_main_add_notifier(NC_GEOM | ND_DATA, NULL);
+
+ MEM_SAFE_FREE(opdata->array_loc);
+ MEM_SAFE_FREE(opdata->array_rot);
+ MEM_SAFE_FREE(opdata->array_scale);
+ MEM_SAFE_FREE(op->customdata);
+}
+
+static void gpencil_transform_fill_cancel(bContext *C, wmOperator *op)
+{
+ GpUvData *opdata = op->customdata;
+ UNUSED_VARS(opdata);
+
+ gpencil_uv_transform_exit(C, op);
+
+ /* need to force redisplay or we may still view the modified result */
+ ED_region_tag_redraw(CTX_wm_region(C));
+}
+
+static bool gpencil_uv_transform_calc(bContext *C, wmOperator *op)
+{
+ const int mode = RNA_enum_get(op->ptr, "mode");
+ GpUvData *opdata = op->customdata;
+ bGPdata *gpd = opdata->gpd;
+ bool changed = false;
+ /* Get actual vector. */
+ float vr[2];
+ sub_v2_v2v2(vr, opdata->mouse, opdata->mcenter);
+ normalize_v2(vr);
+
+ float location[2];
+ RNA_float_get_array(op->ptr, "location", location);
+
+ float uv_rotation = (opdata->is_modal) ? angle_signed_v2v2(opdata->vinit_rotation, vr) :
+ RNA_float_get(op->ptr, "rotation");
+ uv_rotation *= SMOOTH_FACTOR;
+
+ if (opdata->is_modal) {
+ RNA_float_set(op->ptr, "rotation", uv_rotation);
+ }
+
+ int i = 0;
+
+ /* Apply transformations to all strokes. */
+ if ((mode == GP_UV_TRANSLATE) || (!opdata->is_modal)) {
+ float mdiff[2];
+ mdiff[0] = opdata->mcenter[0] - opdata->mouse[0];
+ mdiff[1] = opdata->mcenter[1] - opdata->mouse[1];
+
+ /* Apply a big amount of smooth always for translate to get smooth result. */
+ mul_v2_fl(mdiff, 0.006f);
+
+ /* Apply angle in translation. */
+ mdiff[0] *= cos(uv_rotation);
+ mdiff[1] *= sin(uv_rotation);
+ if (opdata->is_modal) {
+ RNA_float_set_array(op->ptr, "location", mdiff);
+ }
+
+ changed = (bool)((mdiff[0] != 0.0f) || (mdiff[1] != 0.0f));
+ if (changed) {
+ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ if (gps->flag & GP_STROKE_SELECT) {
+ if (opdata->is_modal) {
+ add_v2_v2v2(gps->uv_translation, opdata->array_loc[i], mdiff);
+ }
+ else {
+ copy_v2_v2(gps->uv_translation, location);
+ }
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gps);
+ i++;
+ }
+ }
+ GP_EDITABLE_STROKES_END(gpstroke_iter);
+ }
+ }
+
+ if ((mode == GP_UV_ROTATE) || (!opdata->is_modal)) {
+ changed = (bool)(uv_rotation != 0.0f);
+ if (changed) {
+ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ if (gps->flag & GP_STROKE_SELECT) {
+ gps->uv_rotation = (opdata->is_modal) ? opdata->array_rot[i] + uv_rotation : uv_rotation;
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gps);
+ i++;
+ }
+ }
+ GP_EDITABLE_STROKES_END(gpstroke_iter);
+ }
+ }
+
+ if ((mode == GP_UV_SCALE) || (!opdata->is_modal)) {
+ float mdiff[2];
+ mdiff[0] = opdata->mcenter[0] - opdata->mouse[0];
+ mdiff[1] = opdata->mcenter[1] - opdata->mouse[1];
+ float scale = (opdata->is_modal) ?
+ ((len_v2(mdiff) - opdata->initial_length) * opdata->pixel_size) /
+ opdata->ob_scale :
+ RNA_float_get(op->ptr, "scale");
+ scale *= SMOOTH_FACTOR;
+
+ if (opdata->is_modal) {
+ RNA_float_set(op->ptr, "scale", scale);
+ }
+
+ changed = (bool)(scale != 0.0f);
+ if (changed) {
+ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ if (gps->flag & GP_STROKE_SELECT) {
+ gps->uv_scale = (opdata->is_modal) ? opdata->array_scale[i] + scale : scale;
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gps);
+ i++;
+ }
+ }
+ GP_EDITABLE_STROKES_END(gpstroke_iter);
+ }
+ }
+
+ if ((!opdata->is_modal) || (changed)) {
+ /* Update cursor line. */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
+ WM_main_add_notifier(NC_GEOM | ND_DATA, NULL);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ }
+
+ return changed;
+}
+
+static int gpencil_transform_fill_exec(bContext *C, wmOperator *op)
+{
+ if (!gpencil_uv_transform_init(C, op, false)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ if (!gpencil_uv_transform_calc(C, op)) {
+ gpencil_uv_transform_exit(C, op);
+ return OPERATOR_CANCELLED;
+ }
+
+ gpencil_uv_transform_exit(C, op);
+ return OPERATOR_FINISHED;
+}
+
+static bool gpencil_transform_fill_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);
+
+ if ((gpl == NULL) || (ob->mode != OB_MODE_EDIT_GPENCIL)) {
+ return false;
+ }
+
+ return true;
+}
+
+static int gpencil_transform_fill_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ RegionView3D *rv3d = CTX_wm_region_view3d(C);
+ float mlen[2];
+ float center_3d[3];
+
+ if (!gpencil_uv_transform_init(C, op, true)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ GpUvData *opdata = op->customdata;
+ /* initialize mouse values */
+ opdata->mouse[0] = event->mval[0];
+ opdata->mouse[1] = event->mval[1];
+
+ copy_v3_v3(center_3d, opdata->ob->loc);
+ mlen[0] = opdata->mcenter[0] - event->mval[0];
+ mlen[1] = opdata->mcenter[1] - event->mval[1];
+ opdata->initial_length = len_v2(mlen);
+
+ opdata->pixel_size = rv3d ? ED_view3d_pixel_size(rv3d, center_3d) : 1.0f;
+
+ /* Calc init rotation vector. */
+ float mouse[2] = {event->mval[0], event->mval[1]};
+ sub_v2_v2v2(opdata->vinit_rotation, mouse, opdata->mcenter);
+ normalize_v2(opdata->vinit_rotation);
+
+ gpencil_uv_transform_calc(C, op);
+
+ gpencil_uv_transform_update_header(op, C);
+ WM_cursor_set(CTX_wm_window(C), WM_CURSOR_EW_ARROW);
+
+ WM_event_add_modal_handler(C, op);
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static int gpencil_transform_fill_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ GpUvData *opdata = op->customdata;
+
+ switch (event->type) {
+ case ESCKEY:
+ case RIGHTMOUSE: {
+ gpencil_transform_fill_cancel(C, op);
+ return OPERATOR_CANCELLED;
+ }
+ case MOUSEMOVE: {
+ opdata->mouse[0] = event->mval[0];
+ opdata->mouse[1] = event->mval[1];
+
+ if (gpencil_uv_transform_calc(C, op)) {
+ gpencil_uv_transform_update_header(op, C);
+ }
+ else {
+ gpencil_transform_fill_cancel(C, op);
+ return OPERATOR_CANCELLED;
+ }
+ break;
+ }
+ case LEFTMOUSE:
+ case PADENTER:
+ case RETKEY: {
+ if ((event->val == KM_PRESS) ||
+ ((event->val == KM_RELEASE) && RNA_boolean_get(op->ptr, "release_confirm"))) {
+ gpencil_uv_transform_calc(C, op);
+ gpencil_uv_transform_exit(C, op);
+ return OPERATOR_FINISHED;
+ }
+ break;
+ }
+ }
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+void GPENCIL_OT_transform_fill(wmOperatorType *ot)
+{
+ static const EnumPropertyItem uv_mode[] = {
+ {GP_UV_TRANSLATE, "TRANSLATE", 0, "Translate", ""},
+ {GP_UV_ROTATE, "ROTATE", 0, "Rotate", ""},
+ {GP_UV_SCALE, "SCALE", 0, "Scale", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Transform Stroke Fill";
+ ot->idname = "GPENCIL_OT_transform_fill";
+ ot->description = "Transform Grease Pencil Stroke Fill";
+
+ /* api callbacks */
+ ot->invoke = gpencil_transform_fill_invoke;
+ ot->modal = gpencil_transform_fill_modal;
+ ot->exec = gpencil_transform_fill_exec;
+ ot->cancel = gpencil_transform_fill_cancel;
+ ot->poll = gpencil_transform_fill_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_CURSOR_XY | OPTYPE_BLOCKING;
+
+ /* properties */
+ ot->prop = RNA_def_enum(ot->srna, "mode", uv_mode, GP_UV_ROTATE, "Mode", "");
+
+ prop = RNA_def_float_vector(
+ ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX, "Location", "", -FLT_MAX, FLT_MAX);
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+
+ prop = RNA_def_float_rotation(ot->srna,
+ "rotation",
+ 0,
+ NULL,
+ DEG2RADF(-360.0f),
+ DEG2RADF(360.0f),
+ "Rotation",
+ "",
+ DEG2RADF(-360.0f),
+ DEG2RADF(360.0f));
+ RNA_def_property_float_default(prop, DEG2RADF(0.0f));
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+
+ prop = RNA_def_float(ot->srna, "scale", 1.0f, 0.001f, 100.0f, "Scale", "", 0.001f, 100.0f);
+ RNA_def_property_float_default(prop, 0.0f);
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+
+ prop = RNA_def_boolean(ot->srna, "release_confirm", 0, "Confirm on Release", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+}
+
+/* Clear UV transformations. */
+static int gpencil_reset_transform_fill_exec(bContext *C, wmOperator *op)
+{
+ const int mode = RNA_enum_get(op->ptr, "mode");
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = (bGPdata *)ob->data;
+ bool changed = false;
+
+ /* Loop all selected strokes and reset. */
+ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ if (gps->flag & GP_STROKE_SELECT) {
+ if ((mode == GP_UV_TRANSLATE) || (mode == GP_UV_ALL)) {
+ zero_v2(gps->uv_translation);
+ }
+ if ((mode == GP_UV_ROTATE) || (mode == GP_UV_ALL)) {
+ gps->uv_rotation = 0.0f;
+ }
+ if ((mode == GP_UV_SCALE) || (mode == GP_UV_ALL)) {
+ gps->uv_scale = 1.0f;
+ }
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gps);
+ changed = true;
+ }
+ }
+ GP_EDITABLE_STROKES_END(gpstroke_iter);
+
+ /* notifiers */
+ if (changed) {
+ DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_reset_transform_fill(wmOperatorType *ot)
+{
+ static const EnumPropertyItem uv_clear_mode[] = {
+ {GP_UV_ALL, "ALL", 0, "All", ""},
+ {GP_UV_TRANSLATE, "TRANSLATE", 0, "Translate", ""},
+ {GP_UV_ROTATE, "ROTATE", 0, "Rotate", ""},
+ {GP_UV_SCALE, "SCALE", 0, "Scale", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ /* identifiers */
+ ot->name = "Reset Fill Transformations";
+ ot->idname = "GPENCIL_OT_reset_transform_fill";
+ ot->description = "Reset any UV transformation and back to default values";
+
+ /* callbacks */
+ ot->exec = gpencil_reset_transform_fill_exec;
+ ot->poll = gpencil_transform_fill_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ ot->prop = RNA_def_enum(ot->srna, "mode", uv_clear_mode, GP_UV_ALL, "Mode", "");
+}
diff --git a/source/blender/editors/gpencil/gpencil_vertex_ops.c b/source/blender/editors/gpencil/gpencil_vertex_ops.c
new file mode 100644
index 00000000000..6a3eebf1baf
--- /dev/null
+++ b/source/blender/editors/gpencil/gpencil_vertex_ops.c
@@ -0,0 +1,899 @@
+/*
+ * 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) 2015, Blender Foundation
+ * This is a new part of Blender
+ * Brush based operators for editing Grease Pencil strokes
+ */
+
+/** \file
+ * \ingroup edgpencil
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_ghash.h"
+#include "BLI_math.h"
+
+#include "BLT_translation.h"
+
+#include "DNA_brush_types.h"
+#include "DNA_gpencil_types.h"
+
+#include "BKE_colortools.h"
+#include "BKE_context.h"
+#include "BKE_gpencil.h"
+#include "BKE_gpencil_modifier.h"
+#include "BKE_main.h"
+#include "BKE_material.h"
+#include "BKE_paint.h"
+#include "BKE_report.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+#include "RNA_enum_types.h"
+
+#include "UI_view2d.h"
+
+#include "ED_gpencil.h"
+#include "ED_screen.h"
+#include "ED_view3d.h"
+
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
+
+#include "gpencil_intern.h"
+
+enum {
+ GP_PAINT_VERTEX_STROKE = 0,
+ GP_PAINT_VERTEX_FILL = 1,
+ GP_PAINT_VERTEX_BOTH = 2,
+};
+
+static const EnumPropertyItem gpencil_modesEnumPropertyItem_mode[] = {
+ {GP_PAINT_VERTEX_STROKE, "STROKE", 0, "Stroke", ""},
+ {GP_PAINT_VERTEX_FILL, "FILL", 0, "Fill", ""},
+ {GP_PAINT_VERTEX_BOTH, "BOTH", 0, "Both", ""},
+ {0, NULL, 0, NULL, NULL},
+};
+
+/* Poll callback for stroke vertex paint operator. */
+static bool gp_vertexpaint_mode_poll(bContext *C)
+{
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ Object *ob = CTX_data_active_object(C);
+ if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
+ return false;
+ }
+
+ bGPdata *gpd = (bGPdata *)ob->data;
+ if (GPENCIL_VERTEX_MODE(gpd)) {
+ if (!(GPENCIL_ANY_VERTEX_MASK(ts->gpencil_selectmode_vertex))) {
+ return false;
+ }
+
+ /* Any data to use. */
+ if (gpd->layers.first) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static int gp_vertexpaint_brightness_contrast_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = (bGPdata *)ob->data;
+ bool changed = false;
+ int i;
+ bGPDspoint *pt;
+ const int mode = RNA_enum_get(op->ptr, "mode");
+
+ float gain, offset;
+ {
+ float brightness = RNA_float_get(op->ptr, "brightness");
+ float contrast = RNA_float_get(op->ptr, "contrast");
+ brightness /= 100.0f;
+ float delta = contrast / 200.0f;
+ /*
+ * The algorithm is by Werner D. Streidt
+ * (http://visca.com/ffactory/archives/5-99/msg00021.html)
+ * Extracted of OpenCV demhist.c
+ */
+ if (contrast > 0) {
+ gain = 1.0f - delta * 2.0f;
+ gain = 1.0f / max_ff(gain, FLT_EPSILON);
+ offset = gain * (brightness - delta);
+ }
+ else {
+ delta *= -1;
+ gain = max_ff(1.0f - delta * 2.0f, 0.0f);
+ offset = gain * brightness + delta;
+ }
+ }
+
+ /* Loop all selected strokes. */
+ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ if (gps->flag & GP_STROKE_SELECT) {
+ changed = true;
+ /* Fill color. */
+ if (gps->flag & GP_STROKE_SELECT) {
+ changed = true;
+ if (mode != GP_PAINT_VERTEX_STROKE) {
+ if (gps->vert_color_fill[3] > 0.0f) {
+ for (int i2 = 0; i2 < 3; i2++) {
+ gps->vert_color_fill[i2] = gain * gps->vert_color_fill[i2] + offset;
+ }
+ }
+ }
+ }
+
+ /* Stroke points. */
+ if (mode != GP_PAINT_VERTEX_FILL) {
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if ((pt->flag & GP_SPOINT_SELECT) && (pt->vert_color[3] > 0.0f)) {
+ for (int i2 = 0; i2 < 3; i2++) {
+ pt->vert_color[i2] = gain * pt->vert_color[i2] + offset;
+ }
+ }
+ }
+ }
+ }
+ }
+ GP_EDITABLE_STROKES_END(gpstroke_iter);
+
+ /* notifiers */
+ if (changed) {
+ 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_vertex_color_brightness_contrast(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Vertex Paint Bright/Contrast";
+ ot->idname = "GPENCIL_OT_vertex_color_brightness_contrast";
+ ot->description = "Adjust vertex color brightness/contrast";
+
+ /* api callbacks */
+ ot->exec = gp_vertexpaint_brightness_contrast_exec;
+ ot->poll = gp_vertexpaint_mode_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* params */
+ ot->prop = RNA_def_enum(ot->srna, "mode", gpencil_modesEnumPropertyItem_mode, 0, "Mode", "");
+ const float min = -100, max = +100;
+ prop = RNA_def_float(ot->srna, "brightness", 0.0f, min, max, "Brightness", "", min, max);
+ prop = RNA_def_float(ot->srna, "contrast", 0.0f, min, max, "Contrast", "", min, max);
+ RNA_def_property_ui_range(prop, min, max, 1, 1);
+}
+
+static int gp_vertexpaint_hsv_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = (bGPdata *)ob->data;
+
+ bool changed = false;
+ int i;
+ bGPDspoint *pt;
+ float hsv[3];
+
+ const int mode = RNA_enum_get(op->ptr, "mode");
+ float hue = RNA_float_get(op->ptr, "h");
+ float sat = RNA_float_get(op->ptr, "s");
+ float val = RNA_float_get(op->ptr, "v");
+
+ /* Loop all selected strokes. */
+ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ if (gps->flag & GP_STROKE_SELECT) {
+ changed = true;
+
+ /* Fill color. */
+ if (mode != GP_PAINT_VERTEX_STROKE) {
+ if (gps->vert_color_fill[3] > 0.0f) {
+
+ rgb_to_hsv_v(gps->vert_color_fill, hsv);
+
+ hsv[0] += (hue - 0.5f);
+ if (hsv[0] > 1.0f) {
+ hsv[0] -= 1.0f;
+ }
+ else if (hsv[0] < 0.0f) {
+ hsv[0] += 1.0f;
+ }
+ hsv[1] *= sat;
+ hsv[2] *= val;
+
+ hsv_to_rgb_v(hsv, gps->vert_color_fill);
+ }
+ }
+
+ /* Stroke points. */
+ if (mode != GP_PAINT_VERTEX_FILL) {
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if ((pt->flag & GP_SPOINT_SELECT) && (pt->vert_color[3] > 0.0f)) {
+ rgb_to_hsv_v(pt->vert_color, hsv);
+
+ hsv[0] += (hue - 0.5f);
+ if (hsv[0] > 1.0f) {
+ hsv[0] -= 1.0f;
+ }
+ else if (hsv[0] < 0.0f) {
+ hsv[0] += 1.0f;
+ }
+ hsv[1] *= sat;
+ hsv[2] *= val;
+
+ hsv_to_rgb_v(hsv, pt->vert_color);
+ }
+ }
+ }
+ }
+ }
+ GP_EDITABLE_STROKES_END(gpstroke_iter);
+
+ /* notifiers */
+ if (changed) {
+ 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_vertex_color_hsv(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Vertex Paint Hue Saturation Value";
+ ot->idname = "GPENCIL_OT_vertex_color_hsv";
+ ot->description = "Adjust vertex color HSV values";
+
+ /* api callbacks */
+ ot->exec = gp_vertexpaint_hsv_exec;
+ ot->poll = gp_vertexpaint_mode_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* params */
+ ot->prop = RNA_def_enum(ot->srna, "mode", gpencil_modesEnumPropertyItem_mode, 0, "Mode", "");
+ RNA_def_float(ot->srna, "h", 0.5f, 0.0f, 1.0f, "Hue", "", 0.0f, 1.0f);
+ RNA_def_float(ot->srna, "s", 1.0f, 0.0f, 2.0f, "Saturation", "", 0.0f, 2.0f);
+ RNA_def_float(ot->srna, "v", 1.0f, 0.0f, 2.0f, "Value", "", 0.0f, 2.0f);
+}
+
+static int gp_vertexpaint_invert_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = (bGPdata *)ob->data;
+
+ bool changed = false;
+ int i;
+ bGPDspoint *pt;
+
+ const int mode = RNA_enum_get(op->ptr, "mode");
+
+ /* Loop all selected strokes. */
+ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ if (gps->flag & GP_STROKE_SELECT) {
+ changed = true;
+ /* Fill color. */
+ if (gps->flag & GP_STROKE_SELECT) {
+ changed = true;
+ if (mode != GP_PAINT_VERTEX_STROKE) {
+ if (gps->vert_color_fill[3] > 0.0f) {
+ for (int i2 = 0; i2 < 3; i2++) {
+ gps->vert_color_fill[i2] = 1.0f - gps->vert_color_fill[i2];
+ }
+ }
+ }
+ }
+
+ /* Stroke points. */
+ if (mode != GP_PAINT_VERTEX_FILL) {
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if ((pt->flag & GP_SPOINT_SELECT) && (pt->vert_color[3] > 0.0f)) {
+ for (int i2 = 0; i2 < 3; i2++) {
+ pt->vert_color[i2] = 1.0f - pt->vert_color[i2];
+ }
+ }
+ }
+ }
+ }
+ }
+ GP_EDITABLE_STROKES_END(gpstroke_iter);
+
+ /* notifiers */
+ if (changed) {
+ 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_vertex_color_invert(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Vertex Paint Invert";
+ ot->idname = "GPENCIL_OT_vertex_color_invert";
+ ot->description = "Invert RGB values";
+
+ /* api callbacks */
+ ot->exec = gp_vertexpaint_invert_exec;
+ ot->poll = gp_vertexpaint_mode_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* params */
+ ot->prop = RNA_def_enum(ot->srna, "mode", gpencil_modesEnumPropertyItem_mode, 0, "Mode", "");
+}
+
+static int gp_vertexpaint_levels_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = (bGPdata *)ob->data;
+
+ bool changed = false;
+ int i;
+ bGPDspoint *pt;
+
+ const int mode = RNA_enum_get(op->ptr, "mode");
+ float gain = RNA_float_get(op->ptr, "gain");
+ float offset = RNA_float_get(op->ptr, "offset");
+
+ /* Loop all selected strokes. */
+ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+
+ /* Fill color. */
+ if (gps->flag & GP_STROKE_SELECT) {
+ changed = true;
+ if (mode != GP_PAINT_VERTEX_STROKE) {
+ if (gps->vert_color_fill[3] > 0.0f) {
+ for (int i2 = 0; i2 < 3; i2++) {
+ gps->vert_color_fill[i2] = gain * (gps->vert_color_fill[i2] + offset);
+ }
+ }
+ }
+ }
+
+ /* Stroke points. */
+ if (mode != GP_PAINT_VERTEX_FILL) {
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if ((pt->flag & GP_SPOINT_SELECT) && (pt->vert_color[3] > 0.0f)) {
+ for (int i2 = 0; i < 3; i2++) {
+ pt->vert_color[i2] = gain * (pt->vert_color[i2] + offset);
+ }
+ }
+ }
+ }
+ }
+ GP_EDITABLE_STROKES_END(gpstroke_iter);
+
+ /* notifiers */
+ if (changed) {
+ 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_vertex_color_levels(wmOperatorType *ot)
+{
+
+ /* identifiers */
+ ot->name = "Vertex Paint Levels";
+ ot->idname = "GPENCIL_OT_vertex_color_levels";
+ ot->description = "Adjust levels of vertex colors";
+
+ /* api callbacks */
+ ot->exec = gp_vertexpaint_levels_exec;
+ ot->poll = gp_vertexpaint_mode_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* params */
+ ot->prop = RNA_def_enum(ot->srna, "mode", gpencil_modesEnumPropertyItem_mode, 0, "Mode", "");
+
+ RNA_def_float(
+ ot->srna, "offset", 0.0f, -1.0f, 1.0f, "Offset", "Value to add to colors", -1.0f, 1.0f);
+ RNA_def_float(
+ ot->srna, "gain", 1.0f, 0.0f, FLT_MAX, "Gain", "Value to multiply colors by", 0.0f, 10.0f);
+}
+
+static int gp_vertexpaint_set_exec(bContext *C, wmOperator *op)
+{
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = (bGPdata *)ob->data;
+ Paint *paint = &ts->gp_vertexpaint->paint;
+ Brush *brush = brush = paint->brush;
+
+ bool changed = false;
+ int i;
+ bGPDspoint *pt;
+
+ const int mode = RNA_enum_get(op->ptr, "mode");
+ float factor = RNA_float_get(op->ptr, "factor");
+
+ /* Loop all selected strokes. */
+ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+
+ /* Fill color. */
+ if (gps->flag & GP_STROKE_SELECT) {
+ changed = true;
+ if (mode != GP_PAINT_VERTEX_STROKE) {
+ copy_v3_v3(gps->vert_color_fill, brush->rgb);
+ gps->vert_color_fill[3] = factor;
+ }
+ }
+
+ /* Stroke points. */
+ if (mode != GP_PAINT_VERTEX_FILL) {
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ copy_v3_v3(pt->vert_color, brush->rgb);
+ pt->vert_color[3] = factor;
+ }
+ }
+ }
+ }
+ GP_EDITABLE_STROKES_END(gpstroke_iter);
+
+ /* notifiers */
+ if (changed) {
+ 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_vertex_color_set(wmOperatorType *ot)
+{
+
+ /* identifiers */
+ ot->name = "Vertex Paint Set Color";
+ ot->idname = "GPENCIL_OT_vertex_color_set";
+ ot->description = "Set active color to all selected vertex";
+
+ /* api callbacks */
+ ot->exec = gp_vertexpaint_set_exec;
+ ot->poll = gp_vertexpaint_mode_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* params */
+ ot->prop = RNA_def_enum(ot->srna, "mode", gpencil_modesEnumPropertyItem_mode, 0, "Mode", "");
+ RNA_def_float(ot->srna, "factor", 1.0f, 0.001f, 1.0f, "Factor", "Mix Factor", 0.001f, 1.0f);
+}
+
+/* Helper to extract color from vertex color to create a palette. */
+static bool gp_extract_palette_from_vertex(bContext *C, const bool selected, const int threshold)
+{
+ Main *bmain = CTX_data_main(C);
+ Object *ob = CTX_data_active_object(C);
+ bool done = false;
+ const float range = pow(10.0f, threshold);
+ float col[3];
+
+ GHash *color_table = BLI_ghash_int_new(__func__);
+
+ /* Extract all colors. */
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
+ }
+ if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) {
+ continue;
+ }
+ MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
+ if (gp_style == NULL) {
+ continue;
+ }
+
+ if ((selected) && ((gps->flag & GP_STROKE_SELECT) == 0)) {
+ continue;
+ }
+
+ bool use_stroke = (gp_style->flag & GP_MATERIAL_STROKE_SHOW);
+ bool use_fill = (gp_style->flag & GP_MATERIAL_FILL_SHOW);
+
+ /* Material is disabled. */
+ if ((!use_fill) && (!use_stroke)) {
+ continue;
+ }
+
+ /* Only solid strokes or stencil. */
+ if ((use_stroke) && ((gp_style->stroke_style == GP_MATERIAL_STROKE_STYLE_TEXTURE) &&
+ ((gp_style->flag & GP_MATERIAL_STROKE_PATTERN) == 0))) {
+ continue;
+ }
+
+ /* Only solid fill. */
+ if ((use_fill) && (gp_style->fill_style != GP_MATERIAL_FILL_STYLE_SOLID)) {
+ continue;
+ }
+
+ /* Fill color. */
+ if (gps->vert_color_fill[3] > 0.0f) {
+ col[0] = truncf(gps->vert_color_fill[0] * range) / range;
+ col[1] = truncf(gps->vert_color_fill[1] * range) / range;
+ col[2] = truncf(gps->vert_color_fill[2] * range) / range;
+
+ uint key = rgb_to_cpack(col[0], col[1], col[2]);
+
+ if (!BLI_ghash_haskey(color_table, POINTER_FROM_INT(key))) {
+ BLI_ghash_insert(color_table, POINTER_FROM_INT(key), POINTER_FROM_INT(key));
+ }
+ }
+
+ /* Read all points to get all colors. */
+ bGPDspoint *pt;
+ int i;
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ col[0] = truncf(pt->vert_color[0] * range) / range;
+ col[1] = truncf(pt->vert_color[1] * range) / range;
+ col[2] = truncf(pt->vert_color[2] * range) / range;
+
+ uint key = rgb_to_cpack(col[0], col[1], col[2]);
+ if (!BLI_ghash_haskey(color_table, POINTER_FROM_INT(key))) {
+ BLI_ghash_insert(color_table, POINTER_FROM_INT(key), POINTER_FROM_INT(key));
+ }
+ }
+ }
+ }
+ }
+ CTX_DATA_END;
+
+ /* Create the Palette. */
+ done = BKE_palette_from_hash(bmain, color_table, ob->id.name + 2, true);
+
+ /* Free memory. */
+ BLI_ghash_free(color_table, NULL, NULL);
+
+ return done;
+}
+
+/* Convert Materials to Vertex Color. */
+typedef struct GPMatArray {
+ uint key;
+ Material *ma;
+ int index;
+} GPMatArray;
+
+static uint get_material_type(MaterialGPencilStyle *gp_style,
+ bool use_stroke,
+ bool use_fill,
+ char *name)
+{
+ uint r_i = 0;
+ if ((use_stroke) && (use_fill)) {
+ switch (gp_style->mode) {
+ case GP_MATERIAL_MODE_LINE: {
+ r_i = 1;
+ strcpy(name, "Line Stroke-Fill");
+ break;
+ }
+ case GP_MATERIAL_MODE_DOT: {
+ r_i = 2;
+ strcpy(name, "Dots Stroke-Fill");
+ break;
+ }
+ case GP_MATERIAL_MODE_SQUARE: {
+ r_i = 3;
+ strcpy(name, "Squares Stroke-Fill");
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ else if (use_stroke) {
+ switch (gp_style->mode) {
+ case GP_MATERIAL_MODE_LINE: {
+ r_i = 4;
+ strcpy(name, "Line Stroke");
+ break;
+ }
+ case GP_MATERIAL_MODE_DOT: {
+ r_i = 5;
+ strcpy(name, "Dots Stroke");
+ break;
+ }
+ case GP_MATERIAL_MODE_SQUARE: {
+ r_i = 6;
+ strcpy(name, "Squares Stroke");
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ else {
+ r_i = 7;
+ strcpy(name, "Solid Fill");
+ }
+
+ /* Create key TSSSSFFFF (T: Type S: Stroke Alpha F: Fill Alpha) */
+ r_i *= 1e8;
+ if (use_stroke) {
+ r_i += gp_style->stroke_rgba[3] * 1e7;
+ }
+ if (use_fill) {
+ r_i += gp_style->fill_rgba[3] * 1e3;
+ }
+
+ return r_i;
+}
+
+static bool gp_material_to_vertex_poll(bContext *C)
+{
+ /* only supported with grease pencil objects */
+ Object *ob = CTX_data_active_object(C);
+ if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
+ return false;
+ }
+
+ return true;
+}
+
+static int gp_material_to_vertex_exec(bContext *C, wmOperator *op)
+{
+ Main *bmain = CTX_data_main(C);
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = (bGPdata *)ob->data;
+ const bool remove = RNA_boolean_get(op->ptr, "remove");
+ const bool palette = RNA_boolean_get(op->ptr, "palette");
+ const bool selected = RNA_boolean_get(op->ptr, "selected");
+
+ char name[32] = "";
+ Material *ma = NULL;
+ GPMatArray *mat_elm = NULL;
+ int i;
+
+ bool changed = false;
+
+ short *totcol = BKE_object_material_len_p(ob);
+ if (totcol == 0) {
+ return OPERATOR_CANCELLED;
+ }
+
+ /* These arrays hold all materials and index in the material slots for all combinations. */
+ int totmat = *totcol;
+ GPMatArray *mat_table = MEM_calloc_arrayN(totmat, sizeof(GPMatArray), __func__);
+
+ /* Update stroke material index. */
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
+ }
+ if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) {
+ continue;
+ }
+
+ if ((selected) && ((gps->flag & GP_STROKE_SELECT) == 0)) {
+ continue;
+ }
+
+ MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
+ if (gp_style == NULL) {
+ continue;
+ }
+
+ bool use_stroke = ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) &&
+ (gp_style->stroke_rgba[3] > 0.0f));
+ bool use_fill = ((gp_style->flag & GP_MATERIAL_FILL_SHOW) &&
+ (gp_style->fill_rgba[3] > 0.0f));
+ bool is_stencil = ((gp_style->stroke_style == GP_MATERIAL_STROKE_STYLE_TEXTURE) &&
+ (gp_style->flag & GP_MATERIAL_STROKE_PATTERN));
+ /* Material is disabled. */
+ if ((!use_fill) && (!use_stroke)) {
+ continue;
+ }
+
+ /* Only solid strokes or stencil. */
+ if ((use_stroke) && ((gp_style->stroke_style == GP_MATERIAL_STROKE_STYLE_TEXTURE) &&
+ ((gp_style->flag & GP_MATERIAL_STROKE_PATTERN) == 0))) {
+ continue;
+ }
+
+ /* Only solid fill. */
+ if ((use_fill) && (gp_style->fill_style != GP_MATERIAL_FILL_STYLE_SOLID)) {
+ continue;
+ }
+
+ /* Only for no Stencil materials. */
+ if (!is_stencil) {
+ /* Create material type unique key by type and alpha. */
+ uint key = get_material_type(gp_style, use_stroke, use_fill, name);
+
+ /* Check if material exist. */
+ bool found = false;
+ for (i = 0; i < totmat; i++) {
+ mat_elm = &mat_table[i];
+ if (mat_elm->ma == NULL) {
+ break;
+ }
+ if (key == mat_elm->key) {
+ found = true;
+ break;
+ }
+ }
+
+ /* If not found create a new material. */
+ if (!found) {
+ ma = BKE_gpencil_material_add(bmain, name);
+ if (use_stroke) {
+ ma->gp_style->flag |= GP_MATERIAL_STROKE_SHOW;
+ }
+ else {
+ ma->gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW;
+ }
+
+ if (use_fill) {
+ ma->gp_style->flag |= GP_MATERIAL_FILL_SHOW;
+ }
+ else {
+ ma->gp_style->flag &= ~GP_MATERIAL_FILL_SHOW;
+ }
+
+ ma->gp_style->stroke_rgba[3] = gp_style->stroke_rgba[3];
+ ma->gp_style->fill_rgba[3] = gp_style->fill_rgba[3];
+
+ BKE_object_material_slot_add(bmain, ob);
+ BKE_object_material_assign(bmain, ob, ma, ob->totcol, BKE_MAT_ASSIGN_USERPREF);
+
+ mat_elm->key = key;
+ mat_elm->ma = ma;
+ mat_elm->index = ob->totcol - 1;
+ }
+ else {
+ mat_elm = &mat_table[i];
+ }
+
+ /* Update stroke */
+ gps->mat_nr = mat_elm->index;
+ }
+
+ changed = true;
+ copy_v3_v3(gps->vert_color_fill, gp_style->fill_rgba);
+ gps->vert_color_fill[3] = 1.0f;
+
+ /* Update all points. */
+ bGPDspoint *pt;
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ copy_v3_v3(pt->vert_color, gp_style->stroke_rgba);
+ pt->vert_color[3] = 1.0f;
+ }
+ }
+ }
+ }
+ CTX_DATA_END;
+
+ /* notifiers */
+ if (changed) {
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ }
+
+ /* Free memory. */
+ MEM_SAFE_FREE(mat_table);
+
+ /* Generate a Palette. */
+ if (palette) {
+ gp_extract_palette_from_vertex(C, selected, 1);
+ }
+
+ /* Clean unused materials. */
+ if (remove) {
+ WM_operator_name_call(
+ C, "OBJECT_OT_material_slot_remove_unused", WM_OP_INVOKE_REGION_WIN, NULL);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_material_to_vertex_color(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Convert Stroke Materials to Vertex Color";
+ ot->idname = "GPENCIL_OT_material_to_vertex_color";
+ ot->description = "Replace materials in strokes with Vertex Color";
+
+ /* api callbacks */
+ ot->exec = gp_material_to_vertex_exec;
+ ot->poll = gp_material_to_vertex_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ ot->prop = RNA_def_boolean(ot->srna,
+ "remove",
+ true,
+ "Remove Unused Materiales",
+ "Remove any unused material after the conversion");
+ RNA_def_boolean(ot->srna, "palette", true, "Create Palette", "Create a new palette with colors");
+ RNA_def_boolean(ot->srna, "selected", false, "Only Selected", "Convert only selected strokes");
+ RNA_def_int(ot->srna, "threshold", 3, 1, 4, "Threshold", "", 1, 4);
+}
+
+/* Extract Palette from Vertex Color. */
+static bool gp_extract_palette_vertex_poll(bContext *C)
+{
+ /* only supported with grease pencil objects */
+ Object *ob = CTX_data_active_object(C);
+ if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
+ return false;
+ }
+
+ return true;
+}
+
+static int gp_extract_palette_vertex_exec(bContext *C, wmOperator *op)
+{
+ const bool selected = RNA_boolean_get(op->ptr, "selected");
+ const int threshold = RNA_int_get(op->ptr, "threshold");
+
+ if (gp_extract_palette_from_vertex(C, selected, threshold)) {
+ BKE_reportf(op->reports, RPT_INFO, "Palette created");
+ }
+ else {
+ BKE_reportf(op->reports, RPT_ERROR, "Unable to find Vertex Information to create palette");
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_extract_palette_vertex(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Extract Palette from Vertex Color";
+ ot->idname = "GPENCIL_OT_extract_palette_vertex";
+ ot->description = "Extract all colors used in Grease Pencil Vertex and create a Palette";
+
+ /* api callbacks */
+ ot->exec = gp_extract_palette_vertex_exec;
+ ot->poll = gp_extract_palette_vertex_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ ot->prop = RNA_def_boolean(
+ ot->srna, "selected", false, "Only Selected", "Convert only selected strokes");
+ RNA_def_int(ot->srna, "threshold", 1, 1, 4, "Threshold", "", 1, 4);
+}
diff --git a/source/blender/editors/gpencil/gpencil_vertex_paint.c b/source/blender/editors/gpencil/gpencil_vertex_paint.c
new file mode 100644
index 00000000000..5b5a306aa25
--- /dev/null
+++ b/source/blender/editors/gpencil/gpencil_vertex_paint.c
@@ -0,0 +1,1414 @@
+/*
+ * 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) 2015, Blender Foundation
+ * This is a new part of Blender
+ * Brush based operators for editing Grease Pencil strokes
+ */
+
+/** \file
+ * \ingroup edgpencil
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_math.h"
+
+#include "BLT_translation.h"
+
+#include "DNA_brush_types.h"
+#include "DNA_gpencil_types.h"
+
+#include "BKE_brush.h"
+#include "BKE_colortools.h"
+#include "BKE_context.h"
+#include "BKE_gpencil.h"
+#include "BKE_report.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "UI_view2d.h"
+
+#include "ED_gpencil.h"
+#include "ED_screen.h"
+#include "ED_view3d.h"
+
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
+
+#include "gpencil_intern.h"
+
+/* ************************************************ */
+/* General Brush Editing Context */
+#define GP_SELECT_BUFFER_CHUNK 256
+#define GP_GRID_PIXEL_SIZE 10.0f
+
+/* Temp Flags while Painting. */
+typedef enum eGPDvertex_brush_Flag {
+ /* invert the effect of the brush */
+ GP_VERTEX_FLAG_INVERT = (1 << 0),
+ /* temporary invert action */
+ GP_VERTEX_FLAG_TMP_INVERT = (1 << 1),
+} eGPDvertex_brush_Flag;
+
+/* Grid of Colors for Smear. */
+typedef struct tGP_Grid {
+ /** Lower right corner of rectangle of grid cell. */
+ float bottom[2];
+ /** Upper left corner of rectangle of grid cell. */
+ float top[2];
+ /** Average Color */
+ float color[4];
+ /** Total points included. */
+ int totcol;
+
+} tGP_Grid;
+
+/* List of points affected by brush. */
+typedef struct tGP_Selected {
+ /** Referenced stroke. */
+ bGPDstroke *gps;
+ /** Point index in points array. */
+ int pt_index;
+ /** Position */
+ int pc[2];
+ /** Color */
+ float color[4];
+} tGP_Selected;
+
+/* Context for brush operators */
+typedef struct tGP_BrushVertexpaintData {
+ Scene *scene;
+ Object *object;
+
+ ARegion *region;
+
+ /* Current GPencil datablock */
+ bGPdata *gpd;
+
+ Brush *brush;
+ eGPDvertex_brush_Flag flag;
+ eGP_Vertex_SelectMaskFlag mask;
+
+ /* Space Conversion Data */
+ GP_SpaceConversion gsc;
+
+ /* Is the brush currently painting? */
+ bool is_painting;
+
+ /* Start of new paint */
+ bool first;
+
+ /* Is multiframe editing enabled, and are we using falloff for that? */
+ bool is_multiframe;
+ bool use_multiframe_falloff;
+
+ /* Brush Runtime Data: */
+ /* - position and pressure
+ * - the *_prev variants are the previous values
+ */
+ float mval[2], mval_prev[2];
+ float pressure, pressure_prev;
+
+ /* - Effect 2D vector */
+ float dvec[2];
+
+ /* - multiframe falloff factor */
+ float mf_falloff;
+
+ /* brush geometry (bounding box) */
+ rcti brush_rect;
+
+ /* Temp data to save selected points */
+ /** Stroke buffer. */
+ tGP_Selected *pbuffer;
+ /** Number of elements currently used in cache. */
+ int pbuffer_used;
+ /** Number of total elements available in cache. */
+ int pbuffer_size;
+
+ /** Grid of average colors */
+ tGP_Grid *grid;
+ /** Total number of rows/cols. */
+ int grid_size;
+ /** Total number of cells elments in the grid array. */
+ int grid_len;
+ /** Grid sample position (used to determine distance of falloff) */
+ int grid_sample[2];
+ /** Grid is ready to use */
+ bool grid_ready;
+
+} tGP_BrushVertexpaintData;
+
+/* Ensure the buffer to hold temp selected point size is enough to save all points selected. */
+static tGP_Selected *gpencil_select_buffer_ensure(tGP_Selected *buffer_array,
+ int *buffer_size,
+ int *buffer_used,
+ const bool clear)
+{
+ tGP_Selected *p = NULL;
+
+ /* By default a buffer is created with one block with a predefined number of free slots,
+ * if the size is not enough, the cache is reallocated adding a new block of free slots.
+ * This is done in order to keep cache small and improve speed. */
+ if (*buffer_used + 1 > *buffer_size) {
+ if ((*buffer_size == 0) || (buffer_array == NULL)) {
+ p = MEM_callocN(sizeof(struct tGP_Selected) * GP_SELECT_BUFFER_CHUNK, __func__);
+ *buffer_size = GP_SELECT_BUFFER_CHUNK;
+ }
+ else {
+ *buffer_size += GP_SELECT_BUFFER_CHUNK;
+ p = MEM_recallocN(buffer_array, sizeof(struct tGP_Selected) * *buffer_size);
+ }
+
+ if (p == NULL) {
+ *buffer_size = *buffer_used = 0;
+ }
+
+ buffer_array = p;
+ }
+
+ /* clear old data */
+ if (clear) {
+ *buffer_used = 0;
+ if (buffer_array != NULL) {
+ memset(buffer_array, 0, sizeof(tGP_Selected) * *buffer_size);
+ }
+ }
+
+ return buffer_array;
+}
+
+/* Brush Operations ------------------------------- */
+
+/* Invert behavior of brush? */
+static bool brush_invert_check(tGP_BrushVertexpaintData *gso)
+{
+ /* The basic setting is no inverted */
+ bool invert = false;
+
+ /* During runtime, the user can hold down the Ctrl key to invert the basic behavior */
+ if (gso->flag & GP_VERTEX_FLAG_INVERT) {
+ invert ^= true;
+ }
+
+ return invert;
+}
+
+/* Compute strength of effect. */
+static float brush_influence_calc(tGP_BrushVertexpaintData *gso, const int radius, const int co[2])
+{
+ Brush *brush = gso->brush;
+ float influence = brush->size;
+
+ /* use pressure? */
+ if (brush->gpencil_settings->flag & GP_BRUSH_USE_PRESSURE) {
+ influence *= gso->pressure;
+ }
+
+ /* distance fading */
+ int mval_i[2];
+ round_v2i_v2fl(mval_i, gso->mval);
+ float distance = (float)len_v2v2_int(mval_i, co);
+
+ /* Apply Brush curve. */
+ float brush_fallof = BKE_brush_curve_strength(brush, distance, (float)radius);
+ influence *= brush_fallof;
+
+ /* apply multiframe falloff */
+ influence *= gso->mf_falloff;
+
+ /* return influence */
+ return influence;
+}
+
+/* Compute effect vector for directional brushes. */
+static void brush_calc_dvec_2d(tGP_BrushVertexpaintData *gso)
+{
+ gso->dvec[0] = (float)(gso->mval[0] - gso->mval_prev[0]);
+ gso->dvec[1] = (float)(gso->mval[1] - gso->mval_prev[1]);
+
+ normalize_v2(gso->dvec);
+}
+
+/* Init a grid of cells around mouse position.
+ *
+ * For each Cell.
+ *
+ * *--------* Top
+ * | |
+ * | |
+ * Bottom *--------*
+ *
+ * The number of cells is calculated using the brush size and a predefined
+ * number of pixels (see: GP_GRID_PIXEL_SIZE)
+ */
+
+static void gp_grid_cells_init(tGP_BrushVertexpaintData *gso)
+{
+ tGP_Grid *grid;
+ float bottom[2];
+ float top[2];
+ int grid_index = 0;
+
+ /* The grid center is (0,0). */
+ bottom[0] = gso->brush_rect.xmin - gso->mval[0];
+ bottom[1] = gso->brush_rect.ymax - GP_GRID_PIXEL_SIZE - gso->mval[1];
+
+ /* Calc all cell of the grid from top/left. */
+ for (int y = gso->grid_size - 1; y >= 0; y--) {
+ top[1] = bottom[1] + GP_GRID_PIXEL_SIZE;
+
+ for (int x = 0; x < gso->grid_size; x++) {
+ top[0] = bottom[0] + GP_GRID_PIXEL_SIZE;
+
+ grid = &gso->grid[grid_index];
+
+ copy_v2_v2(grid->bottom, bottom);
+ copy_v2_v2(grid->top, top);
+
+ bottom[0] += GP_GRID_PIXEL_SIZE;
+
+ grid_index++;
+ }
+
+ /* Reset for new row. */
+ bottom[0] = gso->brush_rect.xmin - gso->mval[0];
+ bottom[1] -= GP_GRID_PIXEL_SIZE;
+ }
+}
+
+/* Get the index used in the grid base on dvec. */
+static void gp_grid_cell_average_color_idx_get(tGP_BrushVertexpaintData *gso, int r_idx[2])
+{
+ /* Lower direction. */
+ if (gso->dvec[1] < 0.0f) {
+ if ((gso->dvec[0] >= -1.0f) && (gso->dvec[0] < -0.8f)) {
+ r_idx[0] = 0;
+ r_idx[1] = -1;
+ }
+ else if ((gso->dvec[0] >= -0.8f) && (gso->dvec[0] < -0.6f)) {
+ r_idx[0] = -1;
+ r_idx[1] = -1;
+ }
+ else if ((gso->dvec[0] >= -0.6f) && (gso->dvec[0] < 0.6f)) {
+ r_idx[0] = -1;
+ r_idx[1] = 0;
+ }
+ else if ((gso->dvec[0] >= 0.6f) && (gso->dvec[0] < 0.8f)) {
+ r_idx[0] = -1;
+ r_idx[1] = 1;
+ }
+ else if (gso->dvec[0] >= 0.8f) {
+ r_idx[0] = 0;
+ r_idx[1] = 1;
+ }
+ }
+ /* Upper direction. */
+ else {
+ if ((gso->dvec[0] >= -1.0f) && (gso->dvec[0] < -0.8f)) {
+ r_idx[0] = 0;
+ r_idx[1] = -1;
+ }
+ else if ((gso->dvec[0] >= -0.8f) && (gso->dvec[0] < -0.6f)) {
+ r_idx[0] = 1;
+ r_idx[1] = -1;
+ }
+ else if ((gso->dvec[0] >= -0.6f) && (gso->dvec[0] < 0.6f)) {
+ r_idx[0] = 1;
+ r_idx[1] = 0;
+ }
+ else if ((gso->dvec[0] >= 0.6f) && (gso->dvec[0] < 0.8f)) {
+ r_idx[0] = 1;
+ r_idx[1] = 1;
+ }
+ else if (gso->dvec[0] >= 0.8f) {
+ r_idx[0] = 0;
+ r_idx[1] = 1;
+ }
+ }
+}
+
+static int gp_grid_cell_index_get(tGP_BrushVertexpaintData *gso, int pc[2])
+{
+ float bottom[2], top[2];
+
+ for (int i = 0; i < gso->grid_len; i++) {
+ tGP_Grid *grid = &gso->grid[i];
+ add_v2_v2v2(bottom, grid->bottom, gso->mval);
+ add_v2_v2v2(top, grid->top, gso->mval);
+
+ if (pc[0] >= bottom[0] && pc[0] <= top[0] && pc[1] >= bottom[1] && pc[1] <= top[1]) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/* Fill the grid with the color in each cell and assign point cell index. */
+static void gp_grid_colors_calc(tGP_BrushVertexpaintData *gso)
+{
+ tGP_Selected *selected = NULL;
+ bGPDstroke *gps_selected = NULL;
+ bGPDspoint *pt = NULL;
+ tGP_Grid *grid = NULL;
+
+ /* Don't calculate again. */
+ if (gso->grid_ready) {
+ return;
+ }
+
+ /* Extract colors by cell. */
+ for (int i = 0; i < gso->pbuffer_used; i++) {
+ selected = &gso->pbuffer[i];
+ gps_selected = selected->gps;
+ pt = &gps_selected->points[selected->pt_index];
+ int grid_index = gp_grid_cell_index_get(gso, selected->pc);
+
+ if (grid_index > -1) {
+ grid = &gso->grid[grid_index];
+ /* Add stroke mix color (only if used). */
+ if (pt->vert_color[3] > 0.0f) {
+ add_v3_v3(grid->color, selected->color);
+ grid->color[3] = 1.0f;
+ grid->totcol++;
+ }
+ }
+ }
+
+ /* Average colors. */
+ for (int i = 0; i < gso->grid_len; i++) {
+ grid = &gso->grid[i];
+ if (grid->totcol > 0) {
+ mul_v3_fl(grid->color, (1.0f / (float)grid->totcol));
+ }
+ }
+
+ /* Save sample position. */
+ round_v2i_v2fl(gso->grid_sample, gso->mval);
+
+ gso->grid_ready = true;
+
+ return;
+}
+
+/* ************************************************ */
+/* Brush Callbacks
+ * This section defines the callbacks used by each brush to perform their magic.
+ * These are called on each point within the brush's radius. */
+
+/* Tint Brush */
+static bool brush_tint_apply(tGP_BrushVertexpaintData *gso,
+ bGPDstroke *gps,
+ int pt_index,
+ const int radius,
+ const int co[2])
+{
+ Brush *brush = gso->brush;
+
+ /* Attenuate factor to get a smoother tinting. */
+ float inf = (brush_influence_calc(gso, radius, co) * brush->gpencil_settings->draw_strength) /
+ 100.0f;
+ float inf_fill = (gso->pressure * brush->gpencil_settings->draw_strength) / 1000.0f;
+
+ CLAMP(inf, 0.0f, 1.0f);
+ CLAMP(inf_fill, 0.0f, 1.0f);
+
+ bGPDspoint *pt = &gps->points[pt_index];
+
+ /* Apply color to Stroke point. */
+ if (GPENCIL_TINT_VERTEX_COLOR_STROKE(brush)) {
+ if (brush_invert_check(gso)) {
+ pt->vert_color[3] -= inf;
+ CLAMP_MIN(pt->vert_color[3], 0.0f);
+ }
+ else {
+ /* Premult. */
+ mul_v3_fl(pt->vert_color, pt->vert_color[3]);
+ /* "Alpha over" blending. */
+ interp_v3_v3v3(pt->vert_color, pt->vert_color, brush->rgb, inf);
+ pt->vert_color[3] = pt->vert_color[3] * (1.0 - inf) + inf;
+ /* Un-premult. */
+ if (pt->vert_color[3] > 0.0f) {
+ mul_v3_fl(pt->vert_color, 1.0f / pt->vert_color[3]);
+ }
+ }
+ }
+
+ /* Apply color to Fill area (all with same color and factor). */
+ if (GPENCIL_TINT_VERTEX_COLOR_FILL(brush)) {
+ if (brush_invert_check(gso)) {
+ gps->vert_color_fill[3] -= inf_fill;
+ CLAMP_MIN(gps->vert_color_fill[3], 0.0f);
+ }
+ else {
+ /* Premult. */
+ mul_v3_fl(gps->vert_color_fill, gps->vert_color_fill[3]);
+ /* "Alpha over" blending. */
+ interp_v3_v3v3(gps->vert_color_fill, gps->vert_color_fill, brush->rgb, inf_fill);
+ gps->vert_color_fill[3] = gps->vert_color_fill[3] * (1.0 - inf_fill) + inf_fill;
+ /* Un-premult. */
+ if (gps->vert_color_fill[3] > 0.0f) {
+ mul_v3_fl(gps->vert_color_fill, 1.0f / gps->vert_color_fill[3]);
+ }
+ }
+ }
+
+ return true;
+}
+
+/* Replace Brush (Don't use pressure or invert). */
+static bool brush_replace_apply(tGP_BrushVertexpaintData *gso, bGPDstroke *gps, int pt_index)
+{
+ Brush *brush = gso->brush;
+ bGPDspoint *pt = &gps->points[pt_index];
+
+ /* Apply color to Stroke point. */
+ if (GPENCIL_TINT_VERTEX_COLOR_STROKE(brush)) {
+ copy_v3_v3(pt->vert_color, brush->rgb);
+ /* If not mix color, full replace. */
+ if (pt->vert_color[3] == 0.0f) {
+ pt->vert_color[3] = 1.0f;
+ }
+ }
+
+ /* Apply color to Fill area (all with same color and factor). */
+ if (GPENCIL_TINT_VERTEX_COLOR_FILL(brush)) {
+ copy_v3_v3(gps->vert_color_fill, brush->rgb);
+ /* If not mix color, full replace. */
+ if (gps->vert_color_fill[3] == 0.0f) {
+ gps->vert_color_fill[3] = 1.0f;
+ }
+ }
+
+ return true;
+}
+
+/* Get surrounding color. */
+static bool get_surrounding_color(tGP_BrushVertexpaintData *gso,
+ bGPDstroke *gps,
+ int pt_index,
+ float r_color[3])
+{
+ tGP_Selected *selected = NULL;
+ bGPDstroke *gps_selected = NULL;
+ bGPDspoint *pt = NULL;
+
+ int totcol = 0;
+ zero_v3(r_color);
+
+ /* Average the surrounding points except current one. */
+ for (int i = 0; i < gso->pbuffer_used; i++) {
+ selected = &gso->pbuffer[i];
+ gps_selected = selected->gps;
+ /* current point is not evaluated. */
+ if ((gps_selected == gps) && (selected->pt_index == pt_index)) {
+ continue;
+ }
+
+ pt = &gps_selected->points[selected->pt_index];
+
+ /* Add stroke mix color (only if used). */
+ if (pt->vert_color[3] > 0.0f) {
+ add_v3_v3(r_color, selected->color);
+ totcol++;
+ }
+ }
+ if (totcol > 0) {
+ mul_v3_fl(r_color, (1.0f / (float)totcol));
+ return true;
+ }
+
+ return false;
+}
+
+/* Blur Brush */
+static bool brush_blur_apply(tGP_BrushVertexpaintData *gso,
+ bGPDstroke *gps,
+ int pt_index,
+ const int radius,
+ const int co[2])
+{
+ Brush *brush = gso->brush;
+
+ /* Attenuate factor to get a smoother tinting. */
+ float inf = (brush_influence_calc(gso, radius, co) * brush->gpencil_settings->draw_strength) /
+ 100.0f;
+ float inf_fill = (gso->pressure * brush->gpencil_settings->draw_strength) / 1000.0f;
+
+ bGPDspoint *pt = &gps->points[pt_index];
+
+ /* Get surrounding color. */
+ float blur_color[3];
+ if (get_surrounding_color(gso, gps, pt_index, blur_color)) {
+ /* Apply color to Stroke point. */
+ if (GPENCIL_TINT_VERTEX_COLOR_STROKE(brush)) {
+ interp_v3_v3v3(pt->vert_color, pt->vert_color, blur_color, inf);
+ }
+
+ /* Apply color to Fill area (all with same color and factor). */
+ if (GPENCIL_TINT_VERTEX_COLOR_FILL(brush)) {
+ interp_v3_v3v3(gps->vert_color_fill, gps->vert_color_fill, blur_color, inf_fill);
+ }
+ return true;
+ }
+
+ return false;
+}
+
+/* Average Brush */
+static bool brush_average_apply(tGP_BrushVertexpaintData *gso,
+ bGPDstroke *gps,
+ int pt_index,
+ const int radius,
+ const int co[2],
+ float average_color[3])
+{
+ Brush *brush = gso->brush;
+
+ /* Attenuate factor to get a smoother tinting. */
+ float inf = (brush_influence_calc(gso, radius, co) * brush->gpencil_settings->draw_strength) /
+ 100.0f;
+ float inf_fill = (gso->pressure * brush->gpencil_settings->draw_strength) / 1000.0f;
+
+ bGPDspoint *pt = &gps->points[pt_index];
+
+ float alpha = pt->vert_color[3];
+ float alpha_fill = gps->vert_color_fill[3];
+
+ if (brush_invert_check(gso)) {
+ alpha -= inf;
+ alpha_fill -= inf_fill;
+ }
+ else {
+ alpha += inf;
+ alpha_fill += inf_fill;
+ }
+
+ /* Apply color to Stroke point. */
+ if (GPENCIL_TINT_VERTEX_COLOR_STROKE(brush)) {
+ CLAMP(alpha, 0.0f, 1.0f);
+ interp_v3_v3v3(pt->vert_color, pt->vert_color, average_color, inf);
+ pt->vert_color[3] = alpha;
+ }
+
+ /* Apply color to Fill area (all with same color and factor). */
+ if (GPENCIL_TINT_VERTEX_COLOR_FILL(brush)) {
+ CLAMP(alpha_fill, 0.0f, 1.0f);
+ copy_v3_v3(gps->vert_color_fill, average_color);
+ gps->vert_color_fill[3] = alpha_fill;
+ }
+
+ return true;
+}
+
+/* Smear Brush */
+static bool brush_smear_apply(tGP_BrushVertexpaintData *gso,
+ bGPDstroke *gps,
+ int pt_index,
+ tGP_Selected *selected)
+{
+ Brush *brush = gso->brush;
+ tGP_Grid *grid = NULL;
+ int average_idx[2];
+ ARRAY_SET_ITEMS(average_idx, 0, 0);
+
+ bool changed = false;
+
+ /* Need some movement, so first input is not done. */
+ if (gso->first) {
+ return false;
+ }
+
+ bGPDspoint *pt = &gps->points[pt_index];
+
+ /* Need get average colors in the grid. */
+ if ((!gso->grid_ready) && (gso->pbuffer_used > 0)) {
+ gp_grid_colors_calc(gso);
+ }
+
+ /* The influence is equal to strength and no decay around brush radius. */
+ float inf = brush->gpencil_settings->draw_strength;
+ if (brush->flag & GP_BRUSH_USE_PRESSURE) {
+ inf *= gso->pressure;
+ }
+
+ /* Calc distance from initial sample location and add a fallof effect. */
+ int mval_i[2];
+ round_v2i_v2fl(mval_i, gso->mval);
+ float distance = (float)len_v2v2_int(mval_i, gso->grid_sample);
+ float fac = 1.0f - (distance / (float)(brush->size * 2));
+ CLAMP(fac, 0.0f, 1.0f);
+ inf *= fac;
+
+ /* Retry row and col for average color. */
+ gp_grid_cell_average_color_idx_get(gso, average_idx);
+
+ /* Retry average color cell. */
+ int grid_index = gp_grid_cell_index_get(gso, selected->pc);
+ if (grid_index > -1) {
+ int row = grid_index / gso->grid_size;
+ int col = grid_index - (gso->grid_size * row);
+ row += average_idx[0];
+ col += average_idx[1];
+ CLAMP(row, 0, gso->grid_size);
+ CLAMP(col, 0, gso->grid_size);
+
+ int new_index = (row * gso->grid_size) + col;
+ CLAMP(new_index, 0, gso->grid_len - 1);
+ grid = &gso->grid[new_index];
+ }
+
+ /* Apply color to Stroke point. */
+ if (GPENCIL_TINT_VERTEX_COLOR_STROKE(brush)) {
+ if (grid_index > -1) {
+ if (grid->color[3] > 0.0f) {
+ // copy_v3_v3(pt->vert_color, grid->color);
+ interp_v3_v3v3(pt->vert_color, pt->vert_color, grid->color, inf);
+ changed = true;
+ }
+ }
+ }
+
+ /* Apply color to Fill area (all with same color and factor). */
+ if (GPENCIL_TINT_VERTEX_COLOR_FILL(brush)) {
+ if (grid_index > -1) {
+ if (grid->color[3] > 0.0f) {
+ interp_v3_v3v3(gps->vert_color_fill, gps->vert_color_fill, grid->color, inf);
+ changed = true;
+ }
+ }
+ }
+
+ return changed;
+}
+
+/* ************************************************ */
+/* Header Info */
+static void gp_vertexpaint_brush_header_set(bContext *C)
+{
+ ED_workspace_status_text(C,
+ TIP_("GPencil Vertex Paint: LMB to paint | RMB/Escape to Exit"
+ " | Ctrl to Invert Action"));
+}
+
+/* ************************************************ */
+/* Grease Pencil Vertex Paint Operator */
+
+/* Init/Exit ----------------------------------------------- */
+
+static bool gp_vertexpaint_brush_init(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ Object *ob = CTX_data_active_object(C);
+ Paint *paint = ob->mode == OB_MODE_VERTEX_GPENCIL ? &ts->gp_vertexpaint->paint :
+ &ts->gp_paint->paint;
+
+ /* set the brush using the tool */
+ tGP_BrushVertexpaintData *gso;
+
+ /* setup operator data */
+ gso = MEM_callocN(sizeof(tGP_BrushVertexpaintData), "tGP_BrushVertexpaintData");
+ op->customdata = gso;
+
+ gso->brush = paint->brush;
+ BKE_curvemapping_initialize(gso->brush->curve);
+
+ gso->is_painting = false;
+ gso->first = true;
+
+ gso->pbuffer = NULL;
+ gso->pbuffer_size = 0;
+ gso->pbuffer_used = 0;
+
+ /* Alloc grid array */
+ gso->grid_size = (int)(((gso->brush->size * 2.0f) / GP_GRID_PIXEL_SIZE) + 1.0);
+ /* Square value. */
+ gso->grid_len = gso->grid_size * gso->grid_size;
+ gso->grid = MEM_callocN(sizeof(tGP_Grid) * gso->grid_len, "tGP_Grid");
+ gso->grid_ready = false;
+
+ gso->gpd = ED_gpencil_data_get_active(C);
+ gso->scene = scene;
+ gso->object = ob;
+
+ gso->region = CTX_wm_region(C);
+
+ /* Save mask. */
+ gso->mask = ts->gpencil_selectmode_vertex;
+
+ /* Multiframe settings. */
+ gso->is_multiframe = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gso->gpd);
+ gso->use_multiframe_falloff = (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_FRAME_FALLOFF) != 0;
+
+ /* Init multi-edit falloff curve data before doing anything,
+ * so we won't have to do it again later. */
+ if (gso->is_multiframe) {
+ BKE_curvemapping_initialize(ts->gp_sculpt.cur_falloff);
+ }
+
+ /* Setup space conversions. */
+ gp_point_conversion_init(C, &gso->gsc);
+
+ /* Update header. */
+ gp_vertexpaint_brush_header_set(C);
+
+ return true;
+}
+
+static void gp_vertexpaint_brush_exit(bContext *C, wmOperator *op)
+{
+ tGP_BrushVertexpaintData *gso = op->customdata;
+
+ /* Disable headerprints. */
+ ED_workspace_status_text(C, NULL);
+
+ /* Disable temp invert flag. */
+ gso->brush->flag &= ~GP_VERTEX_FLAG_TMP_INVERT;
+
+ /* Free operator data */
+ MEM_SAFE_FREE(gso->pbuffer);
+ MEM_SAFE_FREE(gso->grid);
+ MEM_SAFE_FREE(gso);
+ op->customdata = NULL;
+}
+
+/* Poll callback for stroke vertex paint operator. */
+static bool gp_vertexpaint_brush_poll(bContext *C)
+{
+ /* NOTE: this is a bit slower, but is the most accurate... */
+ return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0;
+}
+
+/* Helper to save the points selected by the brush. */
+static void gp_save_selected_point(tGP_BrushVertexpaintData *gso,
+ bGPDstroke *gps,
+ int index,
+ int pc[2])
+{
+ tGP_Selected *selected;
+ bGPDspoint *pt = &gps->points[index];
+
+ /* Ensure the array to save the list of selected points is big enough. */
+ gso->pbuffer = gpencil_select_buffer_ensure(
+ gso->pbuffer, &gso->pbuffer_size, &gso->pbuffer_used, false);
+
+ selected = &gso->pbuffer[gso->pbuffer_used];
+ selected->gps = gps;
+ selected->pt_index = index;
+ copy_v2_v2_int(selected->pc, pc);
+ copy_v4_v4(selected->color, pt->vert_color);
+
+ gso->pbuffer_used++;
+}
+
+/* Select points in this stroke and add to an array to be used later. */
+static void gp_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
+ bGPDstroke *gps,
+ const float diff_mat[4][4])
+{
+ GP_SpaceConversion *gsc = &gso->gsc;
+ rcti *rect = &gso->brush_rect;
+ Brush *brush = gso->brush;
+ const int radius = (brush->flag & GP_BRUSH_USE_PRESSURE) ? gso->brush->size * gso->pressure :
+ gso->brush->size;
+ bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
+ bGPDspoint *pt_active = NULL;
+
+ bGPDspoint *pt1, *pt2;
+ bGPDspoint *pt = NULL;
+ int pc1[2] = {0};
+ int pc2[2] = {0};
+ int i;
+ int index;
+ bool include_last = false;
+
+ /* Check if the stroke collide with brush. */
+ if (!ED_gpencil_stroke_check_collision(gsc, gps, gso->mval, radius, diff_mat)) {
+ return;
+ }
+
+ if (gps->totpoints == 1) {
+ bGPDspoint pt_temp;
+ pt = &gps->points[0];
+ gp_point_to_parent_space(gps->points, diff_mat, &pt_temp);
+ gp_point_to_xy(gsc, gps, &pt_temp, &pc1[0], &pc1[1]);
+
+ pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
+ /* do boundbox check first */
+ if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) {
+ /* only check if point is inside */
+ int mval_i[2];
+ round_v2i_v2fl(mval_i, gso->mval);
+ if (len_v2v2_int(mval_i, pc1) <= radius) {
+ /* apply operation to this point */
+ if (pt_active != NULL) {
+ gp_save_selected_point(gso, gps_active, 0, pc1);
+ }
+ }
+ }
+ }
+ else {
+ /* Loop over the points in the stroke, checking for intersections
+ * - an intersection means that we touched the stroke
+ */
+ for (i = 0; (i + 1) < gps->totpoints; i++) {
+ /* Get points to work with */
+ pt1 = gps->points + i;
+ pt2 = gps->points + i + 1;
+
+ /* Skip if neither one is selected
+ * (and we are only allowed to edit/consider selected points) */
+ if ((GPENCIL_ANY_VERTEX_MASK(gso->mask)) && (GPENCIL_VERTEX_MODE(gso->gpd))) {
+ if (!(pt1->flag & GP_SPOINT_SELECT) && !(pt2->flag & GP_SPOINT_SELECT)) {
+ include_last = false;
+ continue;
+ }
+ }
+
+ bGPDspoint npt;
+ gp_point_to_parent_space(pt1, diff_mat, &npt);
+ gp_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]);
+
+ gp_point_to_parent_space(pt2, diff_mat, &npt);
+ gp_point_to_xy(gsc, gps, &npt, &pc2[0], &pc2[1]);
+
+ /* Check that point segment of the boundbox of the selection stroke */
+ if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) ||
+ ((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1]))) {
+ /* Check if point segment of stroke had anything to do with
+ * brush region (either within stroke painted, or on its lines)
+ * - this assumes that linewidth is irrelevant
+ */
+ if (gp_stroke_inside_circle(
+ gso->mval, gso->mval_prev, radius, pc1[0], pc1[1], pc2[0], pc2[1])) {
+
+ /* To each point individually... */
+ pt = &gps->points[i];
+ pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
+ index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i;
+ if (pt_active != NULL) {
+ gp_save_selected_point(gso, gps_active, index, pc1);
+ }
+
+ /* Only do the second point if this is the last segment,
+ * and it is unlikely that the point will get handled
+ * otherwise.
+ *
+ * NOTE: There is a small risk here that the second point wasn't really
+ * actually in-range. In that case, it only got in because
+ * the line linking the points was!
+ */
+ if (i + 1 == gps->totpoints - 1) {
+ pt = &gps->points[i + 1];
+ pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
+ index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i + 1;
+ if (pt_active != NULL) {
+ gp_save_selected_point(gso, gps_active, index, pc2);
+ include_last = false;
+ }
+ }
+ else {
+ include_last = true;
+ }
+ }
+ else if (include_last) {
+ /* This case is for cases where for whatever reason the second vert (1st here)
+ * doesn't get included because the whole edge isn't in bounds,
+ * but it would've qualified since it did with the previous step
+ * (but wasn't added then, to avoid double-ups).
+ */
+ pt = &gps->points[i];
+ pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
+ index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i;
+ if (pt_active != NULL) {
+ gp_save_selected_point(gso, gps_active, index, pc1);
+
+ include_last = false;
+ }
+ }
+ }
+ }
+ }
+}
+
+/* Apply vertex paint brushes to strokes in the given frame. */
+static bool gp_vertexpaint_brush_do_frame(bContext *C,
+ tGP_BrushVertexpaintData *gso,
+ bGPDlayer *gpl,
+ bGPDframe *gpf,
+ const float diff_mat[4][4])
+{
+ Object *ob = CTX_data_active_object(C);
+ char tool = ob->mode == OB_MODE_VERTEX_GPENCIL ? gso->brush->gpencil_vertex_tool :
+ gso->brush->gpencil_tool;
+ const int radius = (gso->brush->flag & GP_BRUSH_USE_PRESSURE) ?
+ gso->brush->size * gso->pressure :
+ gso->brush->size;
+ tGP_Selected *selected = NULL;
+ int i;
+
+ /*---------------------------------------------------------------------
+ * First step: select the points affected. This step is required to have
+ * all selected points before apply the effect, because it could be
+ * required to average data.
+ *--------------------------------------------------------------------- */
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ /* 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_color_use(ob, gpl, gps) == false) {
+ continue;
+ }
+
+ /* Check points below the brush. */
+ gp_vertexpaint_select_stroke(gso, gps, diff_mat);
+ }
+
+ /* For Average tool, need calculate the average resulting color from all colors
+ * under the brush. */
+ float average_color[3] = {0};
+ int totcol = 0;
+ if ((tool == GPVERTEX_TOOL_AVERAGE) && (gso->pbuffer_used > 0)) {
+ for (i = 0; i < gso->pbuffer_used; i++) {
+ selected = &gso->pbuffer[i];
+ bGPDstroke *gps = selected->gps;
+ bGPDspoint *pt = &gps->points[selected->pt_index];
+
+ /* Add stroke mix color (only if used). */
+ if (pt->vert_color[3] > 0.0f) {
+ add_v3_v3(average_color, pt->vert_color);
+ totcol++;
+ }
+
+ /* If Fill color mix, add to average. */
+ if (gps->vert_color_fill[3] > 0.0f) {
+ add_v3_v3(average_color, gps->vert_color_fill);
+ totcol++;
+ }
+ }
+
+ /* Get average. */
+ if (totcol > 0) {
+ mul_v3_fl(average_color, (1.0f / (float)totcol));
+ }
+ }
+
+ /*---------------------------------------------------------------------
+ * Second step: Apply effect.
+ *--------------------------------------------------------------------- */
+ bool changed = false;
+ for (i = 0; i < gso->pbuffer_used; i++) {
+ changed = true;
+ selected = &gso->pbuffer[i];
+
+ switch (tool) {
+ case GPAINT_TOOL_TINT:
+ case GPVERTEX_TOOL_DRAW: {
+ brush_tint_apply(gso, selected->gps, selected->pt_index, radius, selected->pc);
+ changed |= true;
+ break;
+ }
+ case GPVERTEX_TOOL_BLUR: {
+ brush_blur_apply(gso, selected->gps, selected->pt_index, radius, selected->pc);
+ changed |= true;
+ break;
+ }
+ case GPVERTEX_TOOL_AVERAGE: {
+ brush_average_apply(
+ gso, selected->gps, selected->pt_index, radius, selected->pc, average_color);
+ changed |= true;
+ break;
+ }
+ case GPVERTEX_TOOL_SMEAR: {
+ brush_smear_apply(gso, selected->gps, selected->pt_index, selected);
+ changed |= true;
+ break;
+ }
+ case GPVERTEX_TOOL_REPLACE: {
+ brush_replace_apply(gso, selected->gps, selected->pt_index);
+ changed |= true;
+ break;
+ }
+
+ default:
+ printf("ERROR: Unknown type of GPencil Vertex Paint brush\n");
+ break;
+ }
+ }
+ /* Clear the selected array, but keep the memory allocation.*/
+ gso->pbuffer = gpencil_select_buffer_ensure(
+ gso->pbuffer, &gso->pbuffer_size, &gso->pbuffer_used, true);
+
+ return changed;
+}
+
+/* Apply brush effect to all layers. */
+static bool gp_vertexpaint_brush_apply_to_layers(bContext *C, tGP_BrushVertexpaintData *gso)
+{
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Object *obact = gso->object;
+ bool changed = false;
+
+ Object *ob_eval = (Object *)DEG_get_evaluated_id(depsgraph, &obact->id);
+ bGPdata *gpd = (bGPdata *)ob_eval->data;
+
+ /* Find visible strokes, and perform operations on those if hit */
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ /* If locked or no active frame, don't do anything. */
+ if ((!BKE_gpencil_layer_is_editable(gpl)) || (gpl->actframe == NULL)) {
+ continue;
+ }
+
+ /* calculate difference matrix */
+ float diff_mat[4][4];
+ BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat);
+
+ /* Active Frame or MultiFrame? */
+ if (gso->is_multiframe) {
+ /* init multiframe falloff options */
+ int f_init = 0;
+ int f_end = 0;
+
+ if (gso->use_multiframe_falloff) {
+ BKE_gpencil_frame_range_selected(gpl, &f_init, &f_end);
+ }
+
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ /* Always do active frame; Otherwise, only include selected frames */
+ if ((gpf == gpl->actframe) || (gpf->flag & GP_FRAME_SELECT)) {
+ /* compute multiframe falloff factor */
+ if (gso->use_multiframe_falloff) {
+ /* Faloff depends on distance to active frame (relative to the overall frame range)
+ */
+ gso->mf_falloff = BKE_gpencil_multiframe_falloff_calc(
+ gpf, gpl->actframe->framenum, f_init, f_end, ts->gp_sculpt.cur_falloff);
+ }
+ else {
+ /* No falloff */
+ gso->mf_falloff = 1.0f;
+ }
+
+ /* affect strokes in this frame */
+ changed |= gp_vertexpaint_brush_do_frame(C, gso, gpl, gpf, diff_mat);
+ }
+ }
+ }
+ else {
+ /* Apply to active frame's strokes */
+ if (gpl->actframe != NULL) {
+ gso->mf_falloff = 1.0f;
+ changed |= gp_vertexpaint_brush_do_frame(C, gso, gpl, gpl->actframe, diff_mat);
+ }
+ }
+ }
+
+ return changed;
+}
+
+/* Calculate settings for applying brush */
+static void gp_vertexpaint_brush_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
+{
+ tGP_BrushVertexpaintData *gso = op->customdata;
+ Brush *brush = gso->brush;
+ const int radius = ((brush->flag & GP_BRUSH_USE_PRESSURE) ? gso->brush->size * gso->pressure :
+ gso->brush->size);
+ float mousef[2];
+ int mouse[2];
+ bool changed = false;
+
+ /* Get latest mouse coordinates */
+ RNA_float_get_array(itemptr, "mouse", mousef);
+ gso->mval[0] = mouse[0] = (int)(mousef[0]);
+ gso->mval[1] = mouse[1] = (int)(mousef[1]);
+
+ gso->pressure = RNA_float_get(itemptr, "pressure");
+
+ if (RNA_boolean_get(itemptr, "pen_flip")) {
+ gso->flag |= GP_VERTEX_FLAG_INVERT;
+ }
+ else {
+ gso->flag &= ~GP_VERTEX_FLAG_INVERT;
+ }
+
+ /* Store coordinates as reference, if operator just started running */
+ if (gso->first) {
+ gso->mval_prev[0] = gso->mval[0];
+ gso->mval_prev[1] = gso->mval[1];
+ gso->pressure_prev = gso->pressure;
+ }
+
+ /* Update brush_rect, so that it represents the bounding rectangle of brush. */
+ gso->brush_rect.xmin = mouse[0] - radius;
+ gso->brush_rect.ymin = mouse[1] - radius;
+ gso->brush_rect.xmax = mouse[0] + radius;
+ gso->brush_rect.ymax = mouse[1] + radius;
+
+ /* Calc 2D direction vector and relative angle. */
+ brush_calc_dvec_2d(gso);
+
+ /* Calc grid for smear tool. */
+ gp_grid_cells_init(gso);
+
+ changed = gp_vertexpaint_brush_apply_to_layers(C, gso);
+
+ /* Updates */
+ if (changed) {
+ DEG_id_tag_update(&gso->gpd->id, ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ }
+
+ /* Store values for next step */
+ gso->mval_prev[0] = gso->mval[0];
+ gso->mval_prev[1] = gso->mval[1];
+ gso->pressure_prev = gso->pressure;
+ gso->first = false;
+}
+
+/* Running --------------------------------------------- */
+
+/* helper - a record stroke, and apply paint event */
+static void gp_vertexpaint_brush_apply_event(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ tGP_BrushVertexpaintData *gso = op->customdata;
+ PointerRNA itemptr;
+ float mouse[2];
+
+ mouse[0] = event->mval[0] + 1;
+ mouse[1] = event->mval[1] + 1;
+
+ /* fill in stroke */
+ RNA_collection_add(op->ptr, "stroke", &itemptr);
+
+ RNA_float_set_array(&itemptr, "mouse", mouse);
+ RNA_boolean_set(&itemptr, "pen_flip", event->ctrl != false);
+ RNA_boolean_set(&itemptr, "is_start", gso->first);
+
+ /* Handle pressure sensitivity (which is supplied by tablets). */
+ float pressure = event->tablet.pressure;
+ CLAMP(pressure, 0.0f, 1.0f);
+ RNA_float_set(&itemptr, "pressure", pressure);
+
+ /* apply */
+ gp_vertexpaint_brush_apply(C, op, &itemptr);
+}
+
+/* reapply */
+static int gp_vertexpaint_brush_exec(bContext *C, wmOperator *op)
+{
+ if (!gp_vertexpaint_brush_init(C, op)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ RNA_BEGIN (op->ptr, itemptr, "stroke") {
+ gp_vertexpaint_brush_apply(C, op, &itemptr);
+ }
+ RNA_END;
+
+ gp_vertexpaint_brush_exit(C, op);
+
+ return OPERATOR_FINISHED;
+}
+
+/* start modal painting */
+static int gp_vertexpaint_brush_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ tGP_BrushVertexpaintData *gso = NULL;
+ const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input");
+ const bool is_playing = ED_screen_animation_playing(CTX_wm_manager(C)) != NULL;
+
+ /* the operator cannot work while play animation */
+ if (is_playing) {
+ BKE_report(op->reports, RPT_ERROR, "Cannot Paint while play animation");
+
+ return OPERATOR_CANCELLED;
+ }
+
+ /* init painting data */
+ if (!gp_vertexpaint_brush_init(C, op)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ gso = op->customdata;
+
+ /* register modal handler */
+ WM_event_add_modal_handler(C, op);
+
+ /* start drawing immediately? */
+ if (is_modal == false) {
+ ARegion *region = CTX_wm_region(C);
+
+ /* apply first dab... */
+ gso->is_painting = true;
+ gp_vertexpaint_brush_apply_event(C, op, event);
+
+ /* redraw view with feedback */
+ ED_region_tag_redraw(region);
+ }
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+/* painting - handle events */
+static int gp_vertexpaint_brush_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ tGP_BrushVertexpaintData *gso = op->customdata;
+ const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input");
+ bool redraw_region = false;
+ bool redraw_toolsettings = false;
+
+ /* The operator can be in 2 states: Painting and Idling */
+ if (gso->is_painting) {
+ /* Painting */
+ switch (event->type) {
+ /* Mouse Move = Apply somewhere else */
+ case MOUSEMOVE:
+ case INBETWEEN_MOUSEMOVE:
+ /* apply brush effect at new position */
+ gp_vertexpaint_brush_apply_event(C, op, event);
+
+ /* force redraw, so that the cursor will at least be valid */
+ redraw_region = true;
+ break;
+
+ /* Painting mbut release = Stop painting (back to idle) */
+ case LEFTMOUSE:
+ if (is_modal) {
+ /* go back to idling... */
+ gso->is_painting = false;
+ }
+ else {
+ /* end painting, since we're not modal */
+ gso->is_painting = false;
+
+ gp_vertexpaint_brush_exit(C, op);
+ return OPERATOR_FINISHED;
+ }
+ break;
+
+ /* Abort painting if any of the usual things are tried */
+ case MIDDLEMOUSE:
+ case RIGHTMOUSE:
+ case ESCKEY:
+ gp_vertexpaint_brush_exit(C, op);
+ return OPERATOR_FINISHED;
+ }
+ }
+ else {
+ /* Idling */
+ BLI_assert(is_modal == true);
+
+ switch (event->type) {
+ /* Painting mbut press = Start painting (switch to painting state) */
+ case LEFTMOUSE:
+ /* do initial "click" apply */
+ gso->is_painting = true;
+ gso->first = true;
+
+ gp_vertexpaint_brush_apply_event(C, op, event);
+ break;
+
+ /* Exit modal operator, based on the "standard" ops */
+ case RIGHTMOUSE:
+ case ESCKEY:
+ gp_vertexpaint_brush_exit(C, op);
+ return OPERATOR_FINISHED;
+
+ /* MMB is often used for view manipulations */
+ case MIDDLEMOUSE:
+ return OPERATOR_PASS_THROUGH;
+
+ /* Mouse movements should update the brush cursor - Just redraw the active region */
+ case MOUSEMOVE:
+ case INBETWEEN_MOUSEMOVE:
+ redraw_region = true;
+ break;
+
+ /* Change Frame - Allowed */
+ case LEFTARROWKEY:
+ case RIGHTARROWKEY:
+ case UPARROWKEY:
+ case DOWNARROWKEY:
+ return OPERATOR_PASS_THROUGH;
+
+ /* Camera/View Gizmo's - Allowed */
+ /* (See rationale in gpencil_paint.c -> gpencil_draw_modal()) */
+ case PAD0:
+ case PAD1:
+ case PAD2:
+ case PAD3:
+ case PAD4:
+ case PAD5:
+ case PAD6:
+ case PAD7:
+ case PAD8:
+ case PAD9:
+ return OPERATOR_PASS_THROUGH;
+
+ /* Unhandled event */
+ default:
+ break;
+ }
+ }
+
+ /* Redraw region? */
+ if (redraw_region) {
+ ED_region_tag_redraw(CTX_wm_region(C));
+ }
+
+ /* Redraw toolsettings (brush settings)? */
+ if (redraw_toolsettings) {
+ DEG_id_tag_update(&gso->gpd->id, ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL);
+ }
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+void GPENCIL_OT_vertex_paint(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Stroke Vertex Paint";
+ ot->idname = "GPENCIL_OT_vertex_paint";
+ ot->description = "Paint stroke points with a color";
+
+ /* api callbacks */
+ ot->exec = gp_vertexpaint_brush_exec;
+ ot->invoke = gp_vertexpaint_brush_invoke;
+ ot->modal = gp_vertexpaint_brush_modal;
+ ot->cancel = gp_vertexpaint_brush_exit;
+ ot->poll = gp_vertexpaint_brush_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
+
+ /* properties */
+ PropertyRNA *prop;
+ prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+
+ prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+}
diff --git a/source/blender/editors/gpencil/gpencil_weight_paint.c b/source/blender/editors/gpencil/gpencil_weight_paint.c
new file mode 100644
index 00000000000..6b337afa559
--- /dev/null
+++ b/source/blender/editors/gpencil/gpencil_weight_paint.c
@@ -0,0 +1,901 @@
+/*
+ * 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) 2015, Blender Foundation
+ * This is a new part of Blender
+ * Brush based operators for editing Grease Pencil strokes
+ */
+
+/** \file
+ * \ingroup edgpencil
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_math.h"
+
+#include "BLT_translation.h"
+
+#include "DNA_brush_types.h"
+#include "DNA_gpencil_types.h"
+
+#include "BKE_brush.h"
+#include "BKE_colortools.h"
+#include "BKE_context.h"
+#include "BKE_deform.h"
+#include "BKE_gpencil.h"
+#include "BKE_main.h"
+#include "DNA_meshdata_types.h"
+#include "BKE_object_deform.h"
+#include "BKE_report.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "UI_view2d.h"
+
+#include "ED_gpencil.h"
+#include "ED_screen.h"
+#include "ED_view3d.h"
+
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
+
+#include "gpencil_intern.h"
+
+/* ************************************************ */
+/* General Brush Editing Context */
+#define GP_SELECT_BUFFER_CHUNK 256
+
+/* Grid of Colors for Smear. */
+typedef struct tGP_Grid {
+ /** Lower right corner of rectangle of grid cell. */
+ float bottom[2];
+ /** Upper left corner of rectangle of grid cell. */
+ float top[2];
+ /** Average Color */
+ float color[4];
+ /** Total points included. */
+ int totcol;
+
+} tGP_Grid;
+
+/* List of points affected by brush. */
+typedef struct tGP_Selected {
+ /** Referenced stroke. */
+ bGPDstroke *gps;
+ /** Point index in points array. */
+ int pt_index;
+ /** Position */
+ int pc[2];
+ /** Color */
+ float color[4];
+} tGP_Selected;
+
+/* Context for brush operators */
+typedef struct tGP_BrushWeightpaintData {
+ struct Main *bmain;
+ Scene *scene;
+ Object *object;
+
+ ARegion *region;
+
+ /* Current GPencil datablock */
+ bGPdata *gpd;
+
+ Brush *brush;
+
+ /* Space Conversion Data */
+ GP_SpaceConversion gsc;
+
+ /* Is the brush currently painting? */
+ bool is_painting;
+
+ /* Start of new paint */
+ bool first;
+
+ /* Is multiframe editing enabled, and are we using falloff for that? */
+ bool is_multiframe;
+ bool use_multiframe_falloff;
+
+ /* active vertex group */
+ int vrgroup;
+
+ /* Brush Runtime Data: */
+ /* - position and pressure
+ * - the *_prev variants are the previous values
+ */
+ float mval[2], mval_prev[2];
+ float pressure, pressure_prev;
+
+ /* - Effect 2D vector */
+ float dvec[2];
+
+ /* - multiframe falloff factor */
+ float mf_falloff;
+
+ /* brush geometry (bounding box) */
+ rcti brush_rect;
+
+ /* Temp data to save selected points */
+ /** Stroke buffer. */
+ tGP_Selected *pbuffer;
+ /** Number of elements currently used in cache. */
+ int pbuffer_used;
+ /** Number of total elements available in cache. */
+ int pbuffer_size;
+} tGP_BrushWeightpaintData;
+
+/* Ensure the buffer to hold temp selected point size is enough to save all points selected. */
+static tGP_Selected *gpencil_select_buffer_ensure(tGP_Selected *buffer_array,
+ int *buffer_size,
+ int *buffer_used,
+ const bool clear)
+{
+ tGP_Selected *p = NULL;
+
+ /* By default a buffer is created with one block with a predefined number of free slots,
+ * if the size is not enough, the cache is reallocated adding a new block of free slots.
+ * This is done in order to keep cache small and improve speed. */
+ if (*buffer_used + 1 > *buffer_size) {
+ if ((*buffer_size == 0) || (buffer_array == NULL)) {
+ p = MEM_callocN(sizeof(struct tGP_Selected) * GP_SELECT_BUFFER_CHUNK, __func__);
+ *buffer_size = GP_SELECT_BUFFER_CHUNK;
+ }
+ else {
+ *buffer_size += GP_SELECT_BUFFER_CHUNK;
+ p = MEM_recallocN(buffer_array, sizeof(struct tGP_Selected) * *buffer_size);
+ }
+
+ if (p == NULL) {
+ *buffer_size = *buffer_used = 0;
+ }
+
+ buffer_array = p;
+ }
+
+ /* clear old data */
+ if (clear) {
+ *buffer_used = 0;
+ if (buffer_array != NULL) {
+ memset(buffer_array, 0, sizeof(tGP_Selected) * *buffer_size);
+ }
+ }
+
+ return buffer_array;
+}
+
+/* Brush Operations ------------------------------- */
+
+/* Compute strength of effect. */
+static float brush_influence_calc(tGP_BrushWeightpaintData *gso, const int radius, const int co[2])
+{
+ Brush *brush = gso->brush;
+
+ /* basic strength factor from brush settings */
+ float influence = brush->alpha;
+
+ /* use pressure? */
+ if (brush->gpencil_settings->flag & GP_BRUSH_USE_PRESSURE) {
+ influence *= gso->pressure;
+ }
+
+ /* distance fading */
+ int mval_i[2];
+ round_v2i_v2fl(mval_i, gso->mval);
+ float distance = (float)len_v2v2_int(mval_i, co);
+ influence *= 1.0f - (distance / max_ff(radius, 1e-8));
+
+ /* Apply Brush curve. */
+ float brush_fallof = BKE_brush_curve_strength(brush, distance, (float)radius);
+ influence *= brush_fallof;
+
+ /* apply multiframe falloff */
+ influence *= gso->mf_falloff;
+
+ /* return influence */
+ return influence;
+}
+
+/* Compute effect vector for directional brushes. */
+static void brush_calc_dvec_2d(tGP_BrushWeightpaintData *gso)
+{
+ gso->dvec[0] = (float)(gso->mval[0] - gso->mval_prev[0]);
+ gso->dvec[1] = (float)(gso->mval[1] - gso->mval_prev[1]);
+
+ normalize_v2(gso->dvec);
+}
+
+/* ************************************************ */
+/* Brush Callbacks
+ * This section defines the callbacks used by each brush to perform their magic.
+ * These are called on each point within the brush's radius. */
+
+/* Draw Brush */
+static bool brush_draw_apply(tGP_BrushWeightpaintData *gso,
+ bGPDstroke *gps,
+ int pt_index,
+ const int radius,
+ const int co[2])
+{
+ /* create dvert */
+ BKE_gpencil_dvert_ensure(gps);
+
+ MDeformVert *dvert = gps->dvert + pt_index;
+ float inf;
+
+ /* Compute strength of effect */
+ inf = brush_influence_calc(gso, radius, co);
+
+ /* need a vertex group */
+ if (gso->vrgroup == -1) {
+ if (gso->object) {
+ BKE_object_defgroup_add(gso->object);
+ DEG_relations_tag_update(gso->bmain);
+ gso->vrgroup = 0;
+ }
+ }
+ else {
+ bDeformGroup *defgroup = BLI_findlink(&gso->object->defbase, gso->vrgroup);
+ if (defgroup->flag & DG_LOCK_WEIGHT) {
+ return false;
+ }
+ }
+ /* Get current weight and blend. */
+ MDeformWeight *dw = BKE_defvert_ensure_index(dvert, gso->vrgroup);
+ if (dw) {
+ dw->weight = interpf(gso->brush->weight, dw->weight, inf);
+ CLAMP(dw->weight, 0.0f, 1.0f);
+ }
+ return true;
+}
+
+/* ************************************************ */
+/* Header Info */
+static void gp_weightpaint_brush_header_set(bContext *C)
+{
+ ED_workspace_status_text(C, TIP_("GPencil Weight Paint: LMB to paint | RMB/Escape to Exit"));
+}
+
+/* ************************************************ */
+/* Grease Pencil Weight Paint Operator */
+
+/* Init/Exit ----------------------------------------------- */
+
+static bool gp_weightpaint_brush_init(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ Object *ob = CTX_data_active_object(C);
+ Paint *paint = &ts->gp_weightpaint->paint;
+
+ /* set the brush using the tool */
+ tGP_BrushWeightpaintData *gso;
+
+ /* setup operator data */
+ gso = MEM_callocN(sizeof(tGP_BrushWeightpaintData), "tGP_BrushWeightpaintData");
+ op->customdata = gso;
+
+ gso->bmain = CTX_data_main(C);
+
+ gso->brush = paint->brush;
+ BKE_curvemapping_initialize(gso->brush->curve);
+
+ gso->is_painting = false;
+ gso->first = true;
+
+ gso->pbuffer = NULL;
+ gso->pbuffer_size = 0;
+ gso->pbuffer_used = 0;
+
+ gso->gpd = ED_gpencil_data_get_active(C);
+ gso->scene = scene;
+ gso->object = ob;
+ if (ob) {
+ gso->vrgroup = ob->actdef - 1;
+ if (!BLI_findlink(&ob->defbase, gso->vrgroup)) {
+ gso->vrgroup = -1;
+ }
+ }
+ else {
+ gso->vrgroup = -1;
+ }
+
+ gso->region = CTX_wm_region(C);
+
+ /* Multiframe settings. */
+ gso->is_multiframe = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gso->gpd);
+ gso->use_multiframe_falloff = (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_FRAME_FALLOFF) != 0;
+
+ /* Init multi-edit falloff curve data before doing anything,
+ * so we won't have to do it again later. */
+ if (gso->is_multiframe) {
+ BKE_curvemapping_initialize(ts->gp_sculpt.cur_falloff);
+ }
+
+ /* Setup space conversions. */
+ gp_point_conversion_init(C, &gso->gsc);
+
+ /* Update header. */
+ gp_weightpaint_brush_header_set(C);
+
+ return true;
+}
+
+static void gp_weightpaint_brush_exit(bContext *C, wmOperator *op)
+{
+ tGP_BrushWeightpaintData *gso = op->customdata;
+
+ /* Disable headerprints. */
+ ED_workspace_status_text(C, NULL);
+
+ /* Free operator data */
+ MEM_SAFE_FREE(gso->pbuffer);
+ MEM_SAFE_FREE(gso);
+ op->customdata = NULL;
+}
+
+/* Poll callback for stroke weight paint operator. */
+static bool gp_weightpaint_brush_poll(bContext *C)
+{
+ /* NOTE: this is a bit slower, but is the most accurate... */
+ return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0;
+}
+
+/* Helper to save the points selected by the brush. */
+static void gp_save_selected_point(tGP_BrushWeightpaintData *gso,
+ bGPDstroke *gps,
+ int index,
+ int pc[2])
+{
+ tGP_Selected *selected;
+ bGPDspoint *pt = &gps->points[index];
+
+ /* Ensure the array to save the list of selected points is big enough. */
+ gso->pbuffer = gpencil_select_buffer_ensure(
+ gso->pbuffer, &gso->pbuffer_size, &gso->pbuffer_used, false);
+
+ selected = &gso->pbuffer[gso->pbuffer_used];
+ selected->gps = gps;
+ selected->pt_index = index;
+ copy_v2_v2_int(selected->pc, pc);
+ copy_v4_v4(selected->color, pt->vert_color);
+
+ gso->pbuffer_used++;
+}
+
+/* Select points in this stroke and add to an array to be used later. */
+static void gp_weightpaint_select_stroke(tGP_BrushWeightpaintData *gso,
+ bGPDstroke *gps,
+ const float diff_mat[4][4])
+{
+ GP_SpaceConversion *gsc = &gso->gsc;
+ rcti *rect = &gso->brush_rect;
+ Brush *brush = gso->brush;
+ const int radius = (brush->flag & GP_BRUSH_USE_PRESSURE) ? gso->brush->size * gso->pressure :
+ gso->brush->size;
+ bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
+ bGPDspoint *pt_active = NULL;
+
+ bGPDspoint *pt1, *pt2;
+ bGPDspoint *pt = NULL;
+ int pc1[2] = {0};
+ int pc2[2] = {0};
+ int i;
+ int index;
+ bool include_last = false;
+
+ /* Check if the stroke collide with brush. */
+ if (!ED_gpencil_stroke_check_collision(gsc, gps, gso->mval, radius, diff_mat)) {
+ return;
+ }
+
+ if (gps->totpoints == 1) {
+ bGPDspoint pt_temp;
+ pt = &gps->points[0];
+ gp_point_to_parent_space(gps->points, diff_mat, &pt_temp);
+ gp_point_to_xy(gsc, gps, &pt_temp, &pc1[0], &pc1[1]);
+
+ pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
+ /* do boundbox check first */
+ if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) {
+ /* only check if point is inside */
+ int mval_i[2];
+ round_v2i_v2fl(mval_i, gso->mval);
+ if (len_v2v2_int(mval_i, pc1) <= radius) {
+ /* apply operation to this point */
+ if (pt_active != NULL) {
+ gp_save_selected_point(gso, gps_active, 0, pc1);
+ }
+ }
+ }
+ }
+ else {
+ /* Loop over the points in the stroke, checking for intersections
+ * - an intersection means that we touched the stroke
+ */
+ for (i = 0; (i + 1) < gps->totpoints; i++) {
+ /* Get points to work with */
+ pt1 = gps->points + i;
+ pt2 = gps->points + i + 1;
+
+ bGPDspoint npt;
+ gp_point_to_parent_space(pt1, diff_mat, &npt);
+ gp_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]);
+
+ gp_point_to_parent_space(pt2, diff_mat, &npt);
+ gp_point_to_xy(gsc, gps, &npt, &pc2[0], &pc2[1]);
+
+ /* Check that point segment of the boundbox of the selection stroke */
+ if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) ||
+ ((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1]))) {
+ /* Check if point segment of stroke had anything to do with
+ * brush region (either within stroke painted, or on its lines)
+ * - this assumes that linewidth is irrelevant
+ */
+ if (gp_stroke_inside_circle(
+ gso->mval, gso->mval_prev, radius, pc1[0], pc1[1], pc2[0], pc2[1])) {
+
+ /* To each point individually... */
+ pt = &gps->points[i];
+ pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
+ index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i;
+ if (pt_active != NULL) {
+ gp_save_selected_point(gso, gps_active, index, pc1);
+ }
+
+ /* Only do the second point if this is the last segment,
+ * and it is unlikely that the point will get handled
+ * otherwise.
+ *
+ * NOTE: There is a small risk here that the second point wasn't really
+ * actually in-range. In that case, it only got in because
+ * the line linking the points was!
+ */
+ if (i + 1 == gps->totpoints - 1) {
+ pt = &gps->points[i + 1];
+ pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
+ index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i + 1;
+ if (pt_active != NULL) {
+ gp_save_selected_point(gso, gps_active, index, pc2);
+ include_last = false;
+ }
+ }
+ else {
+ include_last = true;
+ }
+ }
+ else if (include_last) {
+ /* This case is for cases where for whatever reason the second vert (1st here)
+ * doesn't get included because the whole edge isn't in bounds,
+ * but it would've qualified since it did with the previous step
+ * (but wasn't added then, to avoid double-ups).
+ */
+ pt = &gps->points[i];
+ pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
+ index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i;
+ if (pt_active != NULL) {
+ gp_save_selected_point(gso, gps_active, index, pc1);
+
+ include_last = false;
+ }
+ }
+ }
+ }
+ }
+}
+
+/* Apply weight paint brushes to strokes in the given frame. */
+static bool gp_weightpaint_brush_do_frame(bContext *C,
+ tGP_BrushWeightpaintData *gso,
+ bGPDlayer *gpl,
+ bGPDframe *gpf,
+ const float diff_mat[4][4])
+{
+ Object *ob = CTX_data_active_object(C);
+ char tool = gso->brush->gpencil_weight_tool;
+ const int radius = (gso->brush->flag & GP_BRUSH_USE_PRESSURE) ?
+ gso->brush->size * gso->pressure :
+ gso->brush->size;
+ tGP_Selected *selected = NULL;
+ int i;
+
+ /*---------------------------------------------------------------------
+ * First step: select the points affected. This step is required to have
+ * all selected points before apply the effect, because it could be
+ * required to do some step. Now is not used, but the operator is ready.
+ *--------------------------------------------------------------------- */
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ /* 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_color_use(ob, gpl, gps) == false) {
+ continue;
+ }
+
+ /* Check points below the brush. */
+ gp_weightpaint_select_stroke(gso, gps, diff_mat);
+ }
+
+ /*---------------------------------------------------------------------
+ * Second step: Apply effect.
+ *--------------------------------------------------------------------- */
+ bool changed = false;
+ for (i = 0; i < gso->pbuffer_used; i++) {
+ changed = true;
+ selected = &gso->pbuffer[i];
+
+ switch (tool) {
+ case GPWEIGHT_TOOL_DRAW: {
+ brush_draw_apply(gso, selected->gps, selected->pt_index, radius, selected->pc);
+ changed |= true;
+ break;
+ }
+ default:
+ printf("ERROR: Unknown type of GPencil Weight Paint brush\n");
+ break;
+ }
+ }
+ /* Clear the selected array, but keep the memory allocation.*/
+ gso->pbuffer = gpencil_select_buffer_ensure(
+ gso->pbuffer, &gso->pbuffer_size, &gso->pbuffer_used, true);
+
+ return changed;
+}
+
+/* Apply brush effect to all layers. */
+static bool gp_weightpaint_brush_apply_to_layers(bContext *C, tGP_BrushWeightpaintData *gso)
+{
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Object *obact = gso->object;
+ bool changed = false;
+
+ Object *ob_eval = (Object *)DEG_get_evaluated_id(depsgraph, &obact->id);
+ bGPdata *gpd = (bGPdata *)ob_eval->data;
+
+ /* Find visible strokes, and perform operations on those if hit */
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ /* If locked or no active frame, don't do anything. */
+ if ((!BKE_gpencil_layer_is_editable(gpl)) || (gpl->actframe == NULL)) {
+ continue;
+ }
+
+ /* calculate difference matrix */
+ float diff_mat[4][4];
+ BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat);
+
+ /* Active Frame or MultiFrame? */
+ if (gso->is_multiframe) {
+ /* init multiframe falloff options */
+ int f_init = 0;
+ int f_end = 0;
+
+ if (gso->use_multiframe_falloff) {
+ BKE_gpencil_frame_range_selected(gpl, &f_init, &f_end);
+ }
+
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ /* Always do active frame; Otherwise, only include selected frames */
+ if ((gpf == gpl->actframe) || (gpf->flag & GP_FRAME_SELECT)) {
+ /* compute multiframe falloff factor */
+ if (gso->use_multiframe_falloff) {
+ /* Faloff depends on distance to active frame (relative to the overall frame range)
+ */
+ gso->mf_falloff = BKE_gpencil_multiframe_falloff_calc(
+ gpf, gpl->actframe->framenum, f_init, f_end, ts->gp_sculpt.cur_falloff);
+ }
+ else {
+ /* No falloff */
+ gso->mf_falloff = 1.0f;
+ }
+
+ /* affect strokes in this frame */
+ changed |= gp_weightpaint_brush_do_frame(C, gso, gpl, gpf, diff_mat);
+ }
+ }
+ }
+ else {
+ if (gpl->actframe != NULL) {
+ /* Apply to active frame's strokes */
+ gso->mf_falloff = 1.0f;
+ changed |= gp_weightpaint_brush_do_frame(C, gso, gpl, gpl->actframe, diff_mat);
+ }
+ }
+ }
+
+ return changed;
+}
+
+/* Calculate settings for applying brush */
+static void gp_weightpaint_brush_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
+{
+ tGP_BrushWeightpaintData *gso = op->customdata;
+ Brush *brush = gso->brush;
+ const int radius = ((brush->flag & GP_BRUSH_USE_PRESSURE) ? gso->brush->size * gso->pressure :
+ gso->brush->size);
+ float mousef[2];
+ int mouse[2];
+ bool changed = false;
+
+ /* Get latest mouse coordinates */
+ RNA_float_get_array(itemptr, "mouse", mousef);
+ gso->mval[0] = mouse[0] = (int)(mousef[0]);
+ gso->mval[1] = mouse[1] = (int)(mousef[1]);
+
+ gso->pressure = RNA_float_get(itemptr, "pressure");
+
+ /* Store coordinates as reference, if operator just started running */
+ if (gso->first) {
+ gso->mval_prev[0] = gso->mval[0];
+ gso->mval_prev[1] = gso->mval[1];
+ gso->pressure_prev = gso->pressure;
+ }
+
+ /* Update brush_rect, so that it represents the bounding rectangle of brush. */
+ gso->brush_rect.xmin = mouse[0] - radius;
+ gso->brush_rect.ymin = mouse[1] - radius;
+ gso->brush_rect.xmax = mouse[0] + radius;
+ gso->brush_rect.ymax = mouse[1] + radius;
+
+ /* Calc 2D direction vector and relative angle. */
+ brush_calc_dvec_2d(gso);
+
+ changed = gp_weightpaint_brush_apply_to_layers(C, gso);
+
+ /* Updates */
+ if (changed) {
+ DEG_id_tag_update(&gso->gpd->id, ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ }
+
+ /* Store values for next step */
+ gso->mval_prev[0] = gso->mval[0];
+ gso->mval_prev[1] = gso->mval[1];
+ gso->pressure_prev = gso->pressure;
+ gso->first = false;
+}
+
+/* Running --------------------------------------------- */
+
+/* helper - a record stroke, and apply paint event */
+static void gp_weightpaint_brush_apply_event(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ tGP_BrushWeightpaintData *gso = op->customdata;
+ PointerRNA itemptr;
+ float mouse[2];
+
+ mouse[0] = event->mval[0] + 1;
+ mouse[1] = event->mval[1] + 1;
+
+ /* fill in stroke */
+ RNA_collection_add(op->ptr, "stroke", &itemptr);
+
+ RNA_float_set_array(&itemptr, "mouse", mouse);
+ RNA_boolean_set(&itemptr, "pen_flip", event->ctrl != false);
+ RNA_boolean_set(&itemptr, "is_start", gso->first);
+
+ /* Handle pressure sensitivity (which is supplied by tablets). */
+ float pressure = event->tablet.pressure;
+ CLAMP(pressure, 0.0f, 1.0f);
+ RNA_float_set(&itemptr, "pressure", pressure);
+
+ /* apply */
+ gp_weightpaint_brush_apply(C, op, &itemptr);
+}
+
+/* reapply */
+static int gp_weightpaint_brush_exec(bContext *C, wmOperator *op)
+{
+ if (!gp_weightpaint_brush_init(C, op)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ RNA_BEGIN (op->ptr, itemptr, "stroke") {
+ gp_weightpaint_brush_apply(C, op, &itemptr);
+ }
+ RNA_END;
+
+ gp_weightpaint_brush_exit(C, op);
+
+ return OPERATOR_FINISHED;
+}
+
+/* start modal painting */
+static int gp_weightpaint_brush_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ tGP_BrushWeightpaintData *gso = NULL;
+ const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input");
+ const bool is_playing = ED_screen_animation_playing(CTX_wm_manager(C)) != NULL;
+
+ /* the operator cannot work while play animation */
+ if (is_playing) {
+ BKE_report(op->reports, RPT_ERROR, "Cannot Paint while play animation");
+
+ return OPERATOR_CANCELLED;
+ }
+
+ /* init painting data */
+ if (!gp_weightpaint_brush_init(C, op)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ gso = op->customdata;
+
+ /* register modal handler */
+ WM_event_add_modal_handler(C, op);
+
+ /* start drawing immediately? */
+ if (is_modal == false) {
+ ARegion *region = CTX_wm_region(C);
+
+ /* apply first dab... */
+ gso->is_painting = true;
+ gp_weightpaint_brush_apply_event(C, op, event);
+
+ /* redraw view with feedback */
+ ED_region_tag_redraw(region);
+ }
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+/* painting - handle events */
+static int gp_weightpaint_brush_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ tGP_BrushWeightpaintData *gso = op->customdata;
+ const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input");
+ bool redraw_region = false;
+ bool redraw_toolsettings = false;
+
+ /* The operator can be in 2 states: Painting and Idling */
+ if (gso->is_painting) {
+ /* Painting */
+ switch (event->type) {
+ /* Mouse Move = Apply somewhere else */
+ case MOUSEMOVE:
+ case INBETWEEN_MOUSEMOVE:
+ /* apply brush effect at new position */
+ gp_weightpaint_brush_apply_event(C, op, event);
+
+ /* force redraw, so that the cursor will at least be valid */
+ redraw_region = true;
+ break;
+
+ /* Painting mbut release = Stop painting (back to idle) */
+ case LEFTMOUSE:
+ if (is_modal) {
+ /* go back to idling... */
+ gso->is_painting = false;
+ }
+ else {
+ /* end painting, since we're not modal */
+ gso->is_painting = false;
+
+ gp_weightpaint_brush_exit(C, op);
+ return OPERATOR_FINISHED;
+ }
+ break;
+
+ /* Abort painting if any of the usual things are tried */
+ case MIDDLEMOUSE:
+ case RIGHTMOUSE:
+ case ESCKEY:
+ gp_weightpaint_brush_exit(C, op);
+ return OPERATOR_FINISHED;
+ }
+ }
+ else {
+ /* Idling */
+ BLI_assert(is_modal == true);
+
+ switch (event->type) {
+ /* Painting mbut press = Start painting (switch to painting state) */
+ case LEFTMOUSE:
+ /* do initial "click" apply */
+ gso->is_painting = true;
+ gso->first = true;
+
+ gp_weightpaint_brush_apply_event(C, op, event);
+ break;
+
+ /* Exit modal operator, based on the "standard" ops */
+ case RIGHTMOUSE:
+ case ESCKEY:
+ gp_weightpaint_brush_exit(C, op);
+ return OPERATOR_FINISHED;
+
+ /* MMB is often used for view manipulations */
+ case MIDDLEMOUSE:
+ return OPERATOR_PASS_THROUGH;
+
+ /* Mouse movements should update the brush cursor - Just redraw the active region */
+ case MOUSEMOVE:
+ case INBETWEEN_MOUSEMOVE:
+ redraw_region = true;
+ break;
+
+ /* Change Frame - Allowed */
+ case LEFTARROWKEY:
+ case RIGHTARROWKEY:
+ case UPARROWKEY:
+ case DOWNARROWKEY:
+ return OPERATOR_PASS_THROUGH;
+
+ /* Camera/View Gizmo's - Allowed */
+ /* (See rationale in gpencil_paint.c -> gpencil_draw_modal()) */
+ case PAD0:
+ case PAD1:
+ case PAD2:
+ case PAD3:
+ case PAD4:
+ case PAD5:
+ case PAD6:
+ case PAD7:
+ case PAD8:
+ case PAD9:
+ return OPERATOR_PASS_THROUGH;
+
+ /* Unhandled event */
+ default:
+ break;
+ }
+ }
+
+ /* Redraw region? */
+ if (redraw_region) {
+ ED_region_tag_redraw(CTX_wm_region(C));
+ }
+
+ /* Redraw toolsettings (brush settings)? */
+ if (redraw_toolsettings) {
+ DEG_id_tag_update(&gso->gpd->id, ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL);
+ }
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+void GPENCIL_OT_weight_paint(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Stroke Weight Paint";
+ ot->idname = "GPENCIL_OT_weight_paint";
+ ot->description = "Paint stroke points with a color";
+
+ /* api callbacks */
+ ot->exec = gp_weightpaint_brush_exec;
+ ot->invoke = gp_weightpaint_brush_invoke;
+ ot->modal = gp_weightpaint_brush_modal;
+ ot->cancel = gp_weightpaint_brush_exit;
+ ot->poll = gp_weightpaint_brush_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
+
+ /* properties */
+ PropertyRNA *prop;
+ prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+
+ prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+}
diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h
index 8d2d9b934d4..8dd162ea538 100644
--- a/source/blender/editors/include/ED_gpencil.h
+++ b/source/blender/editors/include/ED_gpencil.h
@@ -39,6 +39,7 @@ struct bGPDspoint;
struct bGPDstroke;
struct bGPdata;
struct tGPspoint;
+struct GP_SpaceConversion;
struct ARegion;
struct Depsgraph;
@@ -46,6 +47,7 @@ struct Main;
struct RegionView3D;
struct ReportList;
struct Scene;
+struct ToolSettings;
struct ScrArea;
struct View3D;
struct ViewLayer;
@@ -75,23 +77,8 @@ typedef struct tGPspoint {
float uv_rot; /* uv rotation for dor mode */
float rnd[3]; /* rnd value */
bool rnd_dirty; /* rnd flag */
- short tflag; /* Internal flag */
} tGPspoint;
-/* tGPspoint->flag */
-typedef enum etGPspoint_tFlag {
- /* Created by Fake event (used when mouse/pen move very fast while drawing). */
- GP_TPOINT_FAKE = (1 << 0),
-} etGPspoint_tFlag;
-
-/* used to sort by zdepth gpencil objects in viewport */
-/* TODO: this could be a system parameter in userprefs screen */
-#define GP_CACHE_BLOCK_SIZE 16
-typedef struct tGPencilSort {
- struct Base *base;
- float zdepth;
-} tGPencilSort;
-
/* ----------- Grease Pencil Tools/Context ------------- */
/* Context-dependent */
@@ -210,12 +197,6 @@ bool ED_gpencil_add_lattice_modifier(const struct bContext *C,
/* ------------ Transformation Utilities ------------ */
-/* get difference matrix */
-void ED_gpencil_parent_location(const struct Depsgraph *depsgraph,
- struct Object *obact,
- struct bGPdata *gpd,
- struct bGPDlayer *gpl,
- float diff_mat[4][4]);
/* reset parent matrix for all layers */
void ED_gpencil_reset_layers_parent(struct Depsgraph *depsgraph,
struct Object *obact,
@@ -231,7 +212,6 @@ void ED_gpencil_create_stroke(struct bContext *C, struct Object *ob, float mat[4
/* ------------ Object Utilities ------------ */
struct Object *ED_gpencil_add_object(struct bContext *C,
- struct Scene *scene,
const float loc[3],
unsigned short local_view_bits);
void ED_gpencil_add_defaults(struct bContext *C, struct Object *ob);
@@ -251,11 +231,11 @@ void ED_gp_project_point_to_plane(const struct Scene *scene,
const float origin[3],
const int axis,
struct bGPDspoint *pt);
-void ED_gp_get_drawing_reference(const struct Scene *scene,
- const struct Object *ob,
- struct bGPDlayer *gpl,
- char align_flag,
- float vec[3]);
+void ED_gpencil_drawing_reference_get(const struct Scene *scene,
+ const struct Object *ob,
+ struct bGPDlayer *gpl,
+ char align_flag,
+ float vec[3]);
void ED_gpencil_project_stroke_to_view(struct bContext *C,
struct bGPDlayer *gpl,
struct bGPDstroke *gps);
@@ -277,7 +257,6 @@ void ED_gpencil_tpoint_to_point(struct ARegion *region,
float origin[3],
const struct tGPspoint *tpt,
struct bGPDspoint *pt);
-void ED_gpencil_calc_stroke_uv(struct Object *ob, struct bGPDstroke *gps);
void ED_gpencil_update_color_uv(struct Main *bmain, struct Material *mat);
/* extend selection to stroke intersections
@@ -303,9 +282,30 @@ struct tGPspoint *ED_gpencil_sbuffer_ensure(struct tGPspoint *buffer_array,
int *buffer_size,
int *buffer_used,
const bool clear);
+void ED_gpencil_sbuffer_update_eval(struct bGPdata *gpd, struct Object *ob_eval);
+
/* Tag all scene grease pencil object to update. */
void ED_gpencil_tag_scene_gpencil(struct Scene *scene);
+/* Vertex color set. */
+void ED_gpencil_fill_vertex_color_set(struct ToolSettings *ts,
+ struct Brush *brush,
+ struct bGPDstroke *gps);
+void ED_gpencil_point_vertex_color_set(struct ToolSettings *ts,
+ struct Brush *brush,
+ struct bGPDspoint *pt);
+void ED_gpencil_sbuffer_vertex_color_set(struct Depsgraph *depsgraph,
+ struct Object *ob,
+ struct ToolSettings *ts,
+ struct Brush *brush,
+ struct Material *material);
+
+bool ED_gpencil_stroke_check_collision(struct GP_SpaceConversion *gsc,
+ struct bGPDstroke *gps,
+ float mouse[2],
+ const int radius,
+ const float diff_mat[4][4]);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index 165a53203f3..a0a3d0a3b85 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -4115,9 +4115,9 @@ static void ui_def_but_rna__panel_type(bContext *C, uiLayout *layout, void *but_
void ui_but_rna_menu_convert_to_panel_type(uiBut *but, const char *panel_type)
{
- BLI_assert(but->type == UI_BTYPE_MENU);
- BLI_assert(but->menu_create_func == ui_def_but_rna__menu);
- BLI_assert((void *)but->poin == but);
+ BLI_assert(ELEM(but->type, UI_BTYPE_MENU, UI_BTYPE_COLOR));
+ // BLI_assert(but->menu_create_func == ui_def_but_rna__menu);
+ // BLI_assert((void *)but->poin == but);
but->menu_create_func = ui_def_but_rna__panel_type;
but->func_argN = BLI_strdup(panel_type);
}
diff --git a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c b/source/blender/editors/interface/interface_eyedropper_gpencil_color.c
index 790609a17d8..f9f5c745e94 100644
--- a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c
+++ b/source/blender/editors/interface/interface_eyedropper_gpencil_color.c
@@ -39,6 +39,7 @@
#include "BKE_gpencil.h"
#include "BKE_main.h"
#include "BKE_material.h"
+#include "BKE_paint.h"
#include "BKE_report.h"
#include "UI_interface.h"
@@ -65,6 +66,8 @@ typedef struct EyedropperGPencil {
struct ColorManagedDisplay *display;
/** color under cursor RGB */
float color[3];
+ /** Mode */
+ int mode;
} EyedropperGPencil;
/* Helper: Draw status message while the user is running the operator */
@@ -89,6 +92,7 @@ static bool eyedropper_gpencil_init(bContext *C, wmOperator *op)
display_device = scene->display_settings.display_device;
eye->display = IMB_colormanagement_display_get_named(display_device);
+ eye->mode = RNA_enum_get(op->ptr, "mode");
return true;
}
@@ -101,31 +105,15 @@ static void eyedropper_gpencil_exit(bContext *C, wmOperator *op)
MEM_SAFE_FREE(op->customdata);
}
-/* Set the material. */
-static void eyedropper_gpencil_color_set(bContext *C, const wmEvent *event, EyedropperGPencil *eye)
+static void eyedropper_add_material(
+ bContext *C, float col_conv[4], const bool only_stroke, const bool only_fill, const bool both)
{
Main *bmain = CTX_data_main(C);
Object *ob = CTX_data_active_object(C);
Material *ma = NULL;
- const bool only_stroke = ((!event->ctrl) && (!event->shift));
- const bool only_fill = ((!event->ctrl) && (event->shift));
- const bool both = ((event->ctrl) && (event->shift));
-
- float col_conv[4];
bool found = false;
- /* Convert from linear rgb space to display space because grease pencil colors are in display
- * space, and this conversion is needed to undo the conversion to linear performed by
- * eyedropper_color_sample_fl. */
- if (eye->display) {
- copy_v3_v3(col_conv, eye->color);
- IMB_colormanagement_scene_linear_to_display_v3(col_conv, eye->display);
- }
- else {
- copy_v3_v3(col_conv, eye->color);
- }
-
/* Look for a similar material in grease pencil slots. */
short *totcol = BKE_object_material_len_p(ob);
for (short i = 0; i < *totcol; i++) {
@@ -138,15 +126,15 @@ static void eyedropper_gpencil_color_set(bContext *C, const wmEvent *event, Eyed
if (gp_style != NULL) {
/* Check stroke color. */
bool found_stroke = compare_v3v3(gp_style->stroke_rgba, col_conv, 0.01f) &&
- (gp_style->flag & GP_STYLE_STROKE_SHOW);
+ (gp_style->flag & GP_MATERIAL_STROKE_SHOW);
/* Check fill color. */
bool found_fill = compare_v3v3(gp_style->fill_rgba, col_conv, 0.01f) &&
- (gp_style->flag & GP_STYLE_FILL_SHOW);
+ (gp_style->flag & GP_MATERIAL_FILL_SHOW);
- if ((only_stroke) && (found_stroke) && ((gp_style->flag & GP_STYLE_FILL_SHOW) == 0)) {
+ if ((only_stroke) && (found_stroke) && ((gp_style->flag & GP_MATERIAL_FILL_SHOW) == 0)) {
found = true;
}
- else if ((only_fill) && (found_fill) && ((gp_style->flag & GP_STYLE_STROKE_SHOW) == 0)) {
+ else if ((only_fill) && (found_fill) && ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) == 0)) {
found = true;
}
else if ((both) && (found_stroke) && (found_fill)) {
@@ -180,22 +168,22 @@ static void eyedropper_gpencil_color_set(bContext *C, const wmEvent *event, Eyed
/* Only create Stroke (default option). */
if (only_stroke) {
/* Stroke color. */
- gp_style_new->flag |= GP_STYLE_STROKE_SHOW;
- gp_style_new->flag &= ~GP_STYLE_FILL_SHOW;
+ gp_style_new->flag |= GP_MATERIAL_STROKE_SHOW;
+ gp_style_new->flag &= ~GP_MATERIAL_FILL_SHOW;
copy_v3_v3(gp_style_new->stroke_rgba, col_conv);
zero_v4(gp_style_new->fill_rgba);
}
/* Fill Only. */
else if (only_fill) {
/* Fill color. */
- gp_style_new->flag &= ~GP_STYLE_STROKE_SHOW;
- gp_style_new->flag |= GP_STYLE_FILL_SHOW;
+ gp_style_new->flag &= ~GP_MATERIAL_STROKE_SHOW;
+ gp_style_new->flag |= GP_MATERIAL_FILL_SHOW;
zero_v4(gp_style_new->stroke_rgba);
copy_v3_v3(gp_style_new->fill_rgba, col_conv);
}
/* Stroke and Fill. */
else if (both) {
- gp_style_new->flag |= GP_STYLE_STROKE_SHOW | GP_STYLE_FILL_SHOW;
+ gp_style_new->flag |= GP_MATERIAL_STROKE_SHOW | GP_MATERIAL_FILL_SHOW;
copy_v3_v3(gp_style_new->stroke_rgba, col_conv);
copy_v3_v3(gp_style_new->fill_rgba, col_conv);
}
@@ -203,6 +191,69 @@ static void eyedropper_gpencil_color_set(bContext *C, const wmEvent *event, Eyed
ED_undo_push(C, "Add Grease Pencil Material");
}
+/* Create a new palette color and palette if needed. */
+static void eyedropper_add_palette_color(bContext *C, float col_conv[4])
+{
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+ ToolSettings *ts = scene->toolsettings;
+ GpPaint *gp_paint = ts->gp_paint;
+ GpVertexPaint *gp_vertexpaint = ts->gp_vertexpaint;
+ Paint *paint = &gp_paint->paint;
+ Paint *vertexpaint = &gp_vertexpaint->paint;
+
+ /* Check for Palette in Draw and Vertex Paint Mode. */
+ if (paint->palette == NULL) {
+ paint->palette = BKE_palette_add(bmain, "Grease Pencil");
+ if (vertexpaint->palette == NULL) {
+ vertexpaint->palette = paint->palette;
+ }
+ }
+ /* Check if the color exist already. */
+ Palette *palette = paint->palette;
+ for (PaletteColor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
+ if (compare_v3v3(palcolor->rgb, col_conv, 0.01f)) {
+ return;
+ }
+ }
+
+ /* Create Colors. */
+ PaletteColor *palcol = BKE_palette_color_add(palette);
+ if (palcol) {
+ copy_v3_v3(palcol->rgb, col_conv);
+ }
+}
+
+/* Set the material or the palette color. */
+static void eyedropper_gpencil_color_set(bContext *C, const wmEvent *event, EyedropperGPencil *eye)
+{
+
+ const bool only_stroke = ((!event->ctrl) && (!event->shift));
+ const bool only_fill = ((!event->ctrl) && (event->shift));
+ const bool both = ((event->ctrl) && (event->shift));
+
+ float col_conv[4];
+
+ /* Convert from linear rgb space to display space because grease pencil colors are in display
+ * space, and this conversion is needed to undo the conversion to linear performed by
+ * eyedropper_color_sample_fl. */
+ if (eye->display) {
+ copy_v3_v3(col_conv, eye->color);
+ IMB_colormanagement_scene_linear_to_display_v3(col_conv, eye->display);
+ }
+ else {
+ copy_v3_v3(col_conv, eye->color);
+ }
+
+ /* Add material or Palette color*/
+ if (eye->mode == 0) {
+ eyedropper_add_material(C, col_conv, only_stroke, only_fill, both);
+ }
+ else {
+ eyedropper_add_palette_color(C, col_conv);
+ }
+}
+
/* Sample the color below cursor. */
static void eyedropper_gpencil_color_sample(bContext *C, EyedropperGPencil *eye, int mx, int my)
{
@@ -307,6 +358,12 @@ static bool eyedropper_gpencil_poll(bContext *C)
void UI_OT_eyedropper_gpencil_color(wmOperatorType *ot)
{
+ static const EnumPropertyItem items_mode[] = {
+ {0, "MATERIAL", 0, "Material", ""},
+ {1, "PALETTE", 0, "Palette", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
/* identifiers */
ot->name = "Grease Pencil Eyedropper";
ot->idname = "UI_OT_eyedropper_gpencil_color";
@@ -321,4 +378,7 @@ void UI_OT_eyedropper_gpencil_color(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING;
+
+ /* properties */
+ ot->prop = RNA_def_enum(ot->srna, "mode", items_mode, 0, "Mode", "");
}
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 6a9632f54bb..088a904ec78 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -3994,7 +3994,12 @@ static void ui_block_open_begin(bContext *C, uiBut *but, uiHandleButtonData *dat
copy_v3_v3(data->vec, data->origvec);
but->editvec = data->vec;
- handlefunc = ui_block_func_COLOR;
+ if (ui_but_menu_draw_as_popover(but)) {
+ popoverfunc = but->menu_create_func;
+ }
+ else {
+ handlefunc = ui_block_func_COLOR;
+ }
arg = but;
break;
@@ -4063,8 +4068,8 @@ int ui_but_menu_direction(uiBut *but)
}
/**
- * Hack for #uiList #UI_BTYPE_LISTROW buttons to "give" events to overlaying #UI_BTYPE_TEXT buttons
- * (Ctrl-Click rename feature & co).
+ * Hack for #uiList #UI_BTYPE_LISTROW buttons to "give" events to overlaying #UI_BTYPE_TEXT
+ * buttons (Ctrl-Click rename feature & co).
*/
static uiBut *ui_but_list_row_text_activate(bContext *C,
uiBut *but,
@@ -5553,14 +5558,13 @@ static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, co
button_activate_state(C, but, BUTTON_STATE_EXIT);
ui_apply_but(C, but->block, but, data, true);
- /* Button's state need to be changed to EXIT so moving mouse away from this mouse wouldn't
- * lead to cancel changes made to this button, but changing state to EXIT also makes no
- * button active for a while which leads to triggering operator
- * when doing fast scrolling mouse wheel.
- * using post activate stuff from button allows to make button be active again after
- * checking for all all that mouse leave and cancel stuff,
- * so quick scroll wouldn't be an issue anymore.
- * Same goes for scrolling wheel in another direction below (sergey).
+ /* Button's state need to be changed to EXIT so moving mouse away from this mouse
+ * wouldn't lead to cancel changes made to this button, but changing state to EXIT also
+ * makes no button active for a while which leads to triggering operator when doing fast
+ * scrolling mouse wheel. using post activate stuff from button allows to make button be
+ * active again after checking for all all that mouse leave and cancel stuff, so quick
+ * scroll wouldn't be an issue anymore. Same goes for scrolling wheel in another
+ * direction below (sergey).
*/
data->postbut = but;
data->posttype = BUTTON_ACTIVATE_OVER;
@@ -5797,15 +5801,27 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co
}
else {
Scene *scene = CTX_data_scene(C);
+ bool updated = false;
if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) {
RNA_property_float_get_array(&but->rnapoin, but->rnaprop, color);
BKE_brush_color_set(scene, brush, color);
+ updated = true;
}
else if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR) {
RNA_property_float_get_array(&but->rnapoin, but->rnaprop, color);
IMB_colormanagement_scene_linear_to_srgb_v3(color);
BKE_brush_color_set(scene, brush, color);
+ updated = true;
+ }
+
+ if (updated) {
+ PointerRNA brush_ptr;
+ PropertyRNA *brush_color_prop;
+
+ RNA_id_pointer_create(&brush->id, &brush_ptr);
+ brush_color_prop = RNA_struct_find_property(&brush_ptr, "color");
+ RNA_property_update(C, &brush_ptr, brush_color_prop);
}
}
@@ -9519,7 +9535,8 @@ static int ui_handle_menu_event(bContext *C,
/* Closing sub-levels of pull-downs.
*
* The actual event is handled by the button under the cursor.
- * This is done so we can right click on menu items even when they have sub-menus open. */
+ * This is done so we can right click on menu items even when they have sub-menus open.
+ */
case RIGHTMOUSE:
if (inside == false) {
if (event->val == KM_PRESS && (block->flag & UI_BLOCK_LOOP)) {
diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c
index c1018a67fb3..3e07023e52d 100644
--- a/source/blender/editors/interface/interface_icons.c
+++ b/source/blender/editors/interface/interface_icons.c
@@ -2050,7 +2050,10 @@ static int ui_id_brush_get_icon(const bContext *C, ID *id)
}
/* reset the icon */
- if ((ob != NULL) && (ob->mode & OB_MODE_PAINT_GPENCIL) && (br->gpencil_settings != NULL)) {
+ if ((ob != NULL) &&
+ (ob->mode & (OB_MODE_PAINT_GPENCIL | OB_MODE_VERTEX_GPENCIL | OB_MODE_SCULPT_GPENCIL |
+ OB_MODE_WEIGHT_GPENCIL)) &&
+ (br->gpencil_settings != NULL)) {
switch (br->gpencil_settings->icon_id) {
case GP_BRUSH_ICON_PENCIL:
br->id.icon_id = ICON_GPBRUSH_PENCIL;
@@ -2088,6 +2091,54 @@ static int ui_id_brush_get_icon(const bContext *C, ID *id)
case GP_BRUSH_ICON_ERASE_STROKE:
br->id.icon_id = ICON_GPBRUSH_ERASE_STROKE;
break;
+ case GP_BRUSH_ICON_TINT:
+ br->id.icon_id = ICON_BRUSH_TEXDRAW;
+ break;
+ case GP_BRUSH_ICON_VERTEX_DRAW:
+ br->id.icon_id = ICON_BRUSH_MIX;
+ break;
+ case GP_BRUSH_ICON_VERTEX_BLUR:
+ br->id.icon_id = ICON_BRUSH_BLUR;
+ break;
+ case GP_BRUSH_ICON_VERTEX_AVERAGE:
+ br->id.icon_id = ICON_BRUSH_BLUR;
+ break;
+ case GP_BRUSH_ICON_VERTEX_SMEAR:
+ br->id.icon_id = ICON_BRUSH_BLUR;
+ break;
+ case GP_BRUSH_ICON_VERTEX_REPLACE:
+ br->id.icon_id = ICON_BRUSH_MIX;
+ break;
+ case GP_BRUSH_ICON_GPBRUSH_SMOOTH:
+ br->id.icon_id = ICON_GPBRUSH_SMOOTH;
+ break;
+ case GP_BRUSH_ICON_GPBRUSH_THICKNESS:
+ br->id.icon_id = ICON_GPBRUSH_THICKNESS;
+ break;
+ case GP_BRUSH_ICON_GPBRUSH_STRENGTH:
+ br->id.icon_id = ICON_GPBRUSH_STRENGTH;
+ break;
+ case GP_BRUSH_ICON_GPBRUSH_RANDOMIZE:
+ br->id.icon_id = ICON_GPBRUSH_RANDOMIZE;
+ break;
+ case GP_BRUSH_ICON_GPBRUSH_GRAB:
+ br->id.icon_id = ICON_GPBRUSH_GRAB;
+ break;
+ case GP_BRUSH_ICON_GPBRUSH_PUSH:
+ br->id.icon_id = ICON_GPBRUSH_PUSH;
+ break;
+ case GP_BRUSH_ICON_GPBRUSH_TWIST:
+ br->id.icon_id = ICON_GPBRUSH_TWIST;
+ break;
+ case GP_BRUSH_ICON_GPBRUSH_PINCH:
+ br->id.icon_id = ICON_GPBRUSH_PINCH;
+ break;
+ case GP_BRUSH_ICON_GPBRUSH_CLONE:
+ br->id.icon_id = ICON_GPBRUSH_CLONE;
+ break;
+ case GP_BRUSH_ICON_GPBRUSH_WEIGHT:
+ br->id.icon_id = ICON_GPBRUSH_WEIGHT;
+ break;
default:
br->id.icon_id = ICON_GPBRUSH_PEN;
break;
diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c
index 8e65b818314..dd002f4e77f 100644
--- a/source/blender/editors/interface/interface_layout.c
+++ b/source/blender/editors/interface/interface_layout.c
@@ -2325,7 +2325,7 @@ void uiItemFullR_with_popover(uiLayout *layout,
uiItemFullR(layout, ptr, prop, index, value, flag, name, icon);
but = but->next;
while (but) {
- if (but->rnaprop == prop && but->type == UI_BTYPE_MENU) {
+ if (but->rnaprop == prop && ELEM(but->type, UI_BTYPE_MENU, UI_BTYPE_COLOR)) {
ui_but_rna_menu_convert_to_panel_type(but, panel_type);
break;
}
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index c80c5317735..b752a1a1429 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -5385,6 +5385,21 @@ void uiTemplateColorPicker(uiLayout *layout,
}
}
+static void ui_template_palette_menu(bContext *UNUSED(C), uiLayout *layout, void *UNUSED(but_p))
+{
+ uiLayout *row;
+
+ uiItemL(layout, IFACE_("Sort by:"), ICON_NONE);
+ row = uiLayoutRow(layout, false);
+ uiItemEnumO_value(row, IFACE_("Hue"), ICON_NONE, "PALETTE_OT_sort", "type", 1);
+ row = uiLayoutRow(layout, false);
+ uiItemEnumO_value(row, IFACE_("Saturation"), ICON_NONE, "PALETTE_OT_sort", "type", 2);
+ row = uiLayoutRow(layout, false);
+ uiItemEnumO_value(row, IFACE_("Value"), ICON_NONE, "PALETTE_OT_sort", "type", 3);
+ row = uiLayoutRow(layout, false);
+ uiItemEnumO_value(row, IFACE_("Luminance"), ICON_NONE, "PALETTE_OT_sort", "type", 4);
+}
+
void uiTemplatePalette(uiLayout *layout,
PointerRNA *ptr,
const char *propname,
@@ -5396,6 +5411,8 @@ void uiTemplatePalette(uiLayout *layout,
PaletteColor *color;
uiBlock *block;
uiLayout *col;
+ uiBut *but = NULL;
+
int row_cols = 0, col_id = 0;
int cols_per_row = MAX2(uiLayoutGetWidth(layout) / UI_UNIT_X, 1);
@@ -5437,6 +5454,37 @@ void uiTemplatePalette(uiLayout *layout,
UI_UNIT_X,
UI_UNIT_Y,
NULL);
+ if (color) {
+ but = uiDefIconButO(block,
+ UI_BTYPE_BUT,
+ "PALETTE_OT_color_move",
+ WM_OP_INVOKE_DEFAULT,
+ ICON_TRIA_UP,
+ 0,
+ 0,
+ UI_UNIT_X,
+ UI_UNIT_Y,
+ NULL);
+ UI_but_operator_ptr_get(but);
+ RNA_enum_set(but->opptr, "type", -1);
+
+ but = uiDefIconButO(block,
+ UI_BTYPE_BUT,
+ "PALETTE_OT_color_move",
+ WM_OP_INVOKE_DEFAULT,
+ ICON_TRIA_DOWN,
+ 0,
+ 0,
+ UI_UNIT_X,
+ UI_UNIT_Y,
+ NULL);
+ UI_but_operator_ptr_get(but);
+ RNA_enum_set(but->opptr, "type", 1);
+
+ /* Menu. */
+ uiDefIconMenuBut(
+ block, ui_template_palette_menu, NULL, ICON_SORTSIZE, 0, 0, UI_UNIT_X, UI_UNIT_Y, "");
+ }
col = uiLayoutColumn(layout, true);
uiLayoutRow(col, true);
diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c
index 06f532f99e3..17b6bfdb956 100644
--- a/source/blender/editors/object/object_add.c
+++ b/source/blender/editors/object/object_add.c
@@ -1537,7 +1537,7 @@ static int object_delete_exec(bContext *C, wmOperator *op)
* Will also remove parent from grease pencil from other scenes,
* even when use_global is false... */
for (bGPdata *gpd = bmain->gpencils.first; gpd; gpd = gpd->id.next) {
- for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
if (gpl->parent != NULL) {
if (gpl->parent == ob) {
gpl->parent = NULL;
@@ -2394,7 +2394,7 @@ static int convert_exec(bContext *C, wmOperator *op)
* Nurbs Surface are not supported.
*/
ushort local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0;
- gpencil_ob = ED_gpencil_add_object(C, scene, ob->loc, local_view_bits);
+ gpencil_ob = ED_gpencil_add_object(C, ob->loc, local_view_bits);
copy_v3_v3(gpencil_ob->rot, ob->rot);
copy_v3_v3(gpencil_ob->scale, ob->scale);
BKE_gpencil_convert_curve(bmain, scene, gpencil_ob, ob, false, false, true);
diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c
index f9ac0474d44..def93db2537 100644
--- a/source/blender/editors/object/object_edit.c
+++ b/source/blender/editors/object/object_edit.c
@@ -1370,7 +1370,8 @@ static const EnumPropertyItem *object_mode_set_itemsf(bContext *C,
OB_MODE_EDIT_GPENCIL,
OB_MODE_PAINT_GPENCIL,
OB_MODE_SCULPT_GPENCIL,
- OB_MODE_WEIGHT_GPENCIL) &&
+ OB_MODE_WEIGHT_GPENCIL,
+ OB_MODE_VERTEX_GPENCIL) &&
(ob->type == OB_GPENCIL)) ||
(input->value == OB_MODE_OBJECT)) {
RNA_enum_item_add(&item, &totitem, input);
diff --git a/source/blender/editors/object/object_gpencil_modifier.c b/source/blender/editors/object/object_gpencil_modifier.c
index 9138e65dd2f..4543f1a19b3 100644
--- a/source/blender/editors/object/object_gpencil_modifier.c
+++ b/source/blender/editors/object/object_gpencil_modifier.c
@@ -91,6 +91,11 @@ GpencilModifierData *ED_object_gpencil_modifier_add(
/* make sure modifier data has unique name */
BKE_gpencil_modifier_unique_name(&ob->greasepencil_modifiers, new_md);
+ /* Enable edit mode visible by default. */
+ if (mti->flags & eGpencilModifierTypeFlag_SupportsEditmode) {
+ new_md->mode |= eGpencilModifierMode_Editmode;
+ }
+
bGPdata *gpd = ob->data;
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
@@ -362,7 +367,7 @@ void OBJECT_OT_gpencil_modifier_add(wmOperatorType *ot)
PropertyRNA *prop;
/* identifiers */
- ot->name = "Add Grease Pencil Modifier";
+ ot->name = "Add Modifier";
ot->description = "Add a procedural operation/effect to the active grease pencil object";
ot->idname = "OBJECT_OT_gpencil_modifier_add";
diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c
index 80e7e6312aa..edc2f15813c 100644
--- a/source/blender/editors/object/object_modes.c
+++ b/source/blender/editors/object/object_modes.c
@@ -88,6 +88,9 @@ static const char *object_mode_op_string(eObjectMode mode)
if (mode == OB_MODE_WEIGHT_GPENCIL) {
return "GPENCIL_OT_weightmode_toggle";
}
+ if (mode == OB_MODE_VERTEX_GPENCIL) {
+ return "GPENCIL_OT_vertexmode_toggle";
+ }
return NULL;
}
@@ -129,7 +132,7 @@ bool ED_object_mode_compat_test(const Object *ob, eObjectMode mode)
break;
case OB_GPENCIL:
if (mode & (OB_MODE_EDIT | OB_MODE_EDIT_GPENCIL | OB_MODE_PAINT_GPENCIL |
- OB_MODE_SCULPT_GPENCIL | OB_MODE_WEIGHT_GPENCIL)) {
+ OB_MODE_SCULPT_GPENCIL | OB_MODE_WEIGHT_GPENCIL | OB_MODE_VERTEX_GPENCIL)) {
return true;
}
break;
diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c
index 039714ca3ec..3b1fb49c383 100644
--- a/source/blender/editors/object/object_transform.c
+++ b/source/blender/editors/object/object_transform.c
@@ -696,7 +696,7 @@ static int apply_objects_internal(bContext *C,
/* Unsupported configuration */
bool has_unparented_layers = false;
- for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
/* Parented layers aren't supported as we can't easily re-evaluate
* the scene to sample parent movement */
if (gpl->parent == NULL) {
@@ -1394,13 +1394,13 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
/* recalculate all strokes
* (all layers are considered without evaluating lock attributes) */
- for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
/* calculate difference matrix */
- ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat);
+ BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat);
/* undo matrix */
invert_m4_m4(inverse_diff_mat, diff_mat);
- for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
- for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
float mpt[3];
mul_v3_m4v3(mpt, inverse_diff_mat, &pt->x);
diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c
index e72818479c4..444413a757f 100644
--- a/source/blender/editors/screen/area.c
+++ b/source/blender/editors/screen/area.c
@@ -1723,9 +1723,81 @@ static void ed_default_handlers(
wm->defaultconf, "Grease Pencil Stroke Paint (Fill)", 0, 0);
WM_event_add_keymap_handler(handlers, keymap_paint_fill);
+ wmKeyMap *keymap_paint_tint = WM_keymap_ensure(
+ wm->defaultconf, "Grease Pencil Stroke Paint (Tint)", 0, 0);
+ WM_event_add_keymap_handler(handlers, keymap_paint_tint);
+
wmKeyMap *keymap_sculpt = WM_keymap_ensure(
wm->defaultconf, "Grease Pencil Stroke Sculpt Mode", 0, 0);
WM_event_add_keymap_handler(handlers, keymap_sculpt);
+
+ wmKeyMap *keymap_vertex = WM_keymap_ensure(
+ wm->defaultconf, "Grease Pencil Stroke Vertex Mode", 0, 0);
+ WM_event_add_keymap_handler(handlers, keymap_vertex);
+
+ wmKeyMap *keymap_vertex_draw = WM_keymap_ensure(
+ wm->defaultconf, "Grease Pencil Stroke Vertex (Draw)", 0, 0);
+ WM_event_add_keymap_handler(handlers, keymap_vertex_draw);
+
+ wmKeyMap *keymap_vertex_blur = WM_keymap_ensure(
+ wm->defaultconf, "Grease Pencil Stroke Vertex (Blur)", 0, 0);
+ WM_event_add_keymap_handler(handlers, keymap_vertex_blur);
+
+ wmKeyMap *keymap_vertex_average = WM_keymap_ensure(
+ wm->defaultconf, "Grease Pencil Stroke Vertex (Average)", 0, 0);
+ WM_event_add_keymap_handler(handlers, keymap_vertex_average);
+
+ wmKeyMap *keymap_vertex_smear = WM_keymap_ensure(
+ wm->defaultconf, "Grease Pencil Stroke Vertex (Smear)", 0, 0);
+ WM_event_add_keymap_handler(handlers, keymap_vertex_smear);
+
+ wmKeyMap *keymap_vertex_replace = WM_keymap_ensure(
+ wm->defaultconf, "Grease Pencil Stroke Vertex (Replace)", 0, 0);
+ WM_event_add_keymap_handler(handlers, keymap_vertex_replace);
+
+ wmKeyMap *keymap_sculpt_smooth = WM_keymap_ensure(
+ wm->defaultconf, "Grease Pencil Stroke Sculpt (Smooth)", 0, 0);
+ WM_event_add_keymap_handler(handlers, keymap_sculpt_smooth);
+
+ wmKeyMap *keymap_sculpt_thickness = WM_keymap_ensure(
+ wm->defaultconf, "Grease Pencil Stroke Sculpt (Thickness)", 0, 0);
+ WM_event_add_keymap_handler(handlers, keymap_sculpt_thickness);
+
+ wmKeyMap *keymap_sculpt_strength = WM_keymap_ensure(
+ wm->defaultconf, "Grease Pencil Stroke Sculpt (Strength)", 0, 0);
+ WM_event_add_keymap_handler(handlers, keymap_sculpt_strength);
+
+ wmKeyMap *keymap_sculpt_grab = WM_keymap_ensure(
+ wm->defaultconf, "Grease Pencil Stroke Sculpt (Grab)", 0, 0);
+ WM_event_add_keymap_handler(handlers, keymap_sculpt_grab);
+
+ wmKeyMap *keymap_sculpt_push = WM_keymap_ensure(
+ wm->defaultconf, "Grease Pencil Stroke Sculpt (Push)", 0, 0);
+ WM_event_add_keymap_handler(handlers, keymap_sculpt_push);
+
+ wmKeyMap *keymap_sculpt_twist = WM_keymap_ensure(
+ wm->defaultconf, "Grease Pencil Stroke Sculpt (Twist)", 0, 0);
+ WM_event_add_keymap_handler(handlers, keymap_sculpt_twist);
+
+ wmKeyMap *keymap_sculpt_pinch = WM_keymap_ensure(
+ wm->defaultconf, "Grease Pencil Stroke Sculpt (Pinch)", 0, 0);
+ WM_event_add_keymap_handler(handlers, keymap_sculpt_pinch);
+
+ wmKeyMap *keymap_sculpt_randomize = WM_keymap_ensure(
+ wm->defaultconf, "Grease Pencil Stroke Sculpt (Randomize)", 0, 0);
+ WM_event_add_keymap_handler(handlers, keymap_sculpt_randomize);
+
+ wmKeyMap *keymap_sculpt_clone = WM_keymap_ensure(
+ wm->defaultconf, "Grease Pencil Stroke Sculpt (Clone)", 0, 0);
+ WM_event_add_keymap_handler(handlers, keymap_sculpt_clone);
+
+ wmKeyMap *keymap_weight = WM_keymap_ensure(
+ wm->defaultconf, "Grease Pencil Stroke Weight Mode", 0, 0);
+ WM_event_add_keymap_handler(handlers, keymap_weight);
+
+ wmKeyMap *keymap_weight_draw = WM_keymap_ensure(
+ wm->defaultconf, "Grease Pencil Stroke Weight (Draw)", 0, 0);
+ WM_event_add_keymap_handler(handlers, keymap_weight_draw);
}
}
diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c
index a840d199823..540a4c05247 100644
--- a/source/blender/editors/screen/screen_context.c
+++ b/source/blender/editors/screen/screen_context.c
@@ -555,7 +555,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
bGPdata *gpd = ED_gpencil_data_get_active_direct(sa, obact);
if (gpd) {
- bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
if (gpl) {
CTX_data_pointer_set(result, &gpd->id, &RNA_GPencilLayer, gpl);
@@ -567,7 +567,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
bGPdata *gpd = ED_annotation_data_get_active_direct((ID *)sc, sa, scene);
if (gpd) {
- bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
if (gpl) {
CTX_data_pointer_set(result, &gpd->id, &RNA_GPencilLayer, gpl);
@@ -579,7 +579,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
bGPdata *gpd = ED_gpencil_data_get_active_direct(sa, obact);
if (gpd) {
- bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
if (gpl) {
CTX_data_pointer_set(result, &gpd->id, &RNA_GPencilLayer, gpl->actframe);
@@ -609,7 +609,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
bGPDlayer *gpl;
for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
- if (gpencil_layer_is_editable(gpl)) {
+ if (BKE_gpencil_layer_is_editable(gpl)) {
CTX_data_list_add(result, &gpd->id, &RNA_GPencilLayer, gpl);
}
}
@@ -625,7 +625,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
bGPDlayer *gpl;
for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
- if (gpencil_layer_is_editable(gpl) && (gpl->actframe)) {
+ if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe)) {
bGPDframe *gpf;
bGPDstroke *gps;
bGPDframe *init_gpf = gpl->actframe;
diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c
index ee1cb09ae94..6fe1ba0b817 100644
--- a/source/blender/editors/sculpt_paint/paint_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_ops.c
@@ -26,6 +26,9 @@
#include "BLI_math_vector.h"
#include "BLI_string.h"
+#include "IMB_imbuf_types.h"
+#include "IMB_imbuf.h"
+
#include "DNA_customdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
@@ -33,9 +36,11 @@
#include "BKE_brush.h"
#include "BKE_context.h"
+#include "BKE_image.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_paint.h"
+#include "BKE_report.h"
#include "ED_paint.h"
#include "ED_screen.h"
@@ -294,6 +299,315 @@ static void PALETTE_OT_color_delete(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+/* --- Extract Palette from Image. */
+static bool palette_extract_img_poll(bContext *C)
+{
+ SpaceLink *sl = CTX_wm_space_data(C);
+ if (sl->spacetype == SPACE_IMAGE) {
+ return true;
+ }
+
+ return false;
+}
+
+static int palette_extract_img_exec(bContext *C, wmOperator *op)
+{
+ const int threshold = RNA_int_get(op->ptr, "threshold");
+
+ Main *bmain = CTX_data_main(C);
+ bool done = false;
+
+ SpaceImage *sima = CTX_wm_space_image(C);
+ Image *image = sima->image;
+ ImageUser iuser = sima->iuser;
+ void *lock;
+ ImBuf *ibuf;
+ GHash *color_table = BLI_ghash_int_new(__func__);
+
+ ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock);
+
+ if (ibuf->rect) {
+ /* Extract all colors. */
+ for (int row = 0; row < ibuf->y; row++) {
+ for (int col = 0; col < ibuf->x; col++) {
+ float color[4];
+ IMB_sampleImageAtLocation(ibuf, (float)col, (float)row, false, color);
+ const float range = pow(10.0f, threshold);
+ color[0] = truncf(color[0] * range) / range;
+ color[1] = truncf(color[1] * range) / range;
+ color[2] = truncf(color[2] * range) / range;
+
+ uint key = rgb_to_cpack(color[0], color[1], color[2]);
+ if (!BLI_ghash_haskey(color_table, POINTER_FROM_INT(key))) {
+ BLI_ghash_insert(color_table, POINTER_FROM_INT(key), POINTER_FROM_INT(key));
+ }
+ }
+ }
+
+ done = BKE_palette_from_hash(bmain, color_table, image->id.name + 2, false);
+ }
+
+ /* Free memory. */
+ BLI_ghash_free(color_table, NULL, NULL);
+ BKE_image_release_ibuf(image, ibuf, lock);
+
+ if (done) {
+ BKE_reportf(op->reports, RPT_INFO, "Palette created");
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+static void PALETTE_OT_extract_from_image(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Extract Palette from Image";
+ ot->idname = "PALETTE_OT_extract_from_image";
+ ot->description = "Extract all colors used in Image and create a Palette";
+
+ /* api callbacks */
+ ot->exec = palette_extract_img_exec;
+ ot->poll = palette_extract_img_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_int(ot->srna, "threshold", 1, 1, 4, "Threshold", "", 1, 4);
+}
+
+/* Sort Palette color by Hue and Saturation. */
+static bool palette_sort_poll(bContext *C)
+{
+ Paint *paint = BKE_paint_get_active_from_context(C);
+ Palette *palette = paint->palette;
+ if (palette) {
+ return true;
+ }
+
+ return false;
+}
+
+static int palette_sort_exec(bContext *C, wmOperator *op)
+{
+ const int type = RNA_enum_get(op->ptr, "type");
+
+ Paint *paint = BKE_paint_get_active_from_context(C);
+ Palette *palette = paint->palette;
+
+ if (palette == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+
+ tPaletteColorHSV *color_array = NULL;
+ tPaletteColorHSV *col_elm = NULL;
+
+ const int totcol = BLI_listbase_count(&palette->colors);
+
+ if (totcol > 0) {
+ color_array = MEM_calloc_arrayN(totcol, sizeof(tPaletteColorHSV), __func__);
+ /* Put all colors in an array. */
+ int t = 0;
+ for (PaletteColor *color = palette->colors.first; color; color = color->next) {
+ float h, s, v;
+ rgb_to_hsv(color->rgb[0], color->rgb[1], color->rgb[2], &h, &s, &v);
+ col_elm = &color_array[t];
+ copy_v3_v3(col_elm->rgb, color->rgb);
+ col_elm->value = color->value;
+ col_elm->h = h;
+ col_elm->s = s;
+ col_elm->v = v;
+ t++;
+ }
+ /* Sort */
+ if (type == 1) {
+ BKE_palette_sort_hsv(color_array, totcol);
+ }
+ else if (type == 2) {
+ BKE_palette_sort_svh(color_array, totcol);
+ }
+ else if (type == 3) {
+ BKE_palette_sort_vhs(color_array, totcol);
+ }
+ else {
+ BKE_palette_sort_luminance(color_array, totcol);
+ }
+
+ /* Clear old color swatches. */
+ PaletteColor *color_next = NULL;
+ for (PaletteColor *color = palette->colors.first; color; color = color_next) {
+ color_next = color->next;
+ BKE_palette_color_remove(palette, color);
+ }
+
+ /* Recreate swatches sorted. */
+ for (int i = 0; i < totcol; i++) {
+ col_elm = &color_array[i];
+ PaletteColor *palcol = BKE_palette_color_add(palette);
+ if (palcol) {
+ copy_v3_v3(palcol->rgb, col_elm->rgb);
+ }
+ }
+ }
+
+ /* Free memory. */
+ if (totcol > 0) {
+ MEM_SAFE_FREE(color_array);
+ }
+
+ WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+static void PALETTE_OT_sort(wmOperatorType *ot)
+{
+ static const EnumPropertyItem sort_type[] = {
+ {1, "HSV", 0, "Hue, Saturation, Value", ""},
+ {2, "SVH", 0, "Saturation, Value, Hue", ""},
+ {3, "VHS", 0, "Value, Hue, Saturation", ""},
+ {4, "LUMINANCE", 0, "Luminance", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ /* identifiers */
+ ot->name = "Sort Palette";
+ ot->idname = "PALETTE_OT_sort";
+ ot->description = "Sort Palette Colors";
+
+ /* api callbacks */
+ ot->exec = palette_sort_exec;
+ ot->poll = palette_sort_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ ot->prop = RNA_def_enum(ot->srna, "type", sort_type, 1, "Type", "");
+}
+
+/* Move colors in palette. */
+static int palette_color_move_exec(bContext *C, wmOperator *op)
+{
+ Paint *paint = BKE_paint_get_active_from_context(C);
+ Palette *palette = paint->palette;
+ PaletteColor *palcolor = BLI_findlink(&palette->colors, palette->active_color);
+
+ if (palcolor == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+
+ const int direction = RNA_enum_get(op->ptr, "type");
+
+ BLI_assert(ELEM(direction, -1, 0, 1)); /* we use value below */
+ if (BLI_listbase_link_move(&palette->colors, palcolor, direction)) {
+ palette->active_color += direction;
+ WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, NULL);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+static void PALETTE_OT_color_move(wmOperatorType *ot)
+{
+ static const EnumPropertyItem slot_move[] = {
+ {-1, "UP", 0, "Up", ""},
+ {1, "DOWN", 0, "Down", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ /* identifiers */
+ ot->name = "Move Palette Color";
+ ot->idname = "PALETTE_OT_color_move";
+ ot->description = "Move the active Color up/down in the list";
+
+ /* api callbacks */
+ ot->exec = palette_color_move_exec;
+ ot->poll = palette_sort_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ ot->prop = RNA_def_enum(ot->srna, "type", slot_move, 0, "Type", "");
+}
+
+/* Join Palette swatches. */
+static bool palette_join_poll(bContext *C)
+{
+ Paint *paint = BKE_paint_get_active_from_context(C);
+ Palette *palette = paint->palette;
+ if (palette) {
+ return true;
+ }
+
+ return false;
+}
+
+static int palette_join_exec(bContext *C, wmOperator *op)
+{
+ Main *bmain = CTX_data_main(C);
+ Paint *paint = BKE_paint_get_active_from_context(C);
+ Palette *palette = paint->palette;
+ Palette *palette_join = NULL;
+ bool done = false;
+
+ char name[MAX_ID_NAME - 2];
+ RNA_string_get(op->ptr, "palette", name);
+
+ if ((palette == NULL) || (name[0] == '\0')) {
+ return OPERATOR_CANCELLED;
+ }
+
+ palette_join = (Palette *)BKE_libblock_find_name(bmain, ID_PAL, name);
+ if (palette_join == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+
+ const int totcol = BLI_listbase_count(&palette_join->colors);
+
+ if (totcol > 0) {
+ for (PaletteColor *color = palette_join->colors.first; color; color = color->next) {
+ PaletteColor *palcol = BKE_palette_color_add(palette);
+ if (palcol) {
+ copy_v3_v3(palcol->rgb, color->rgb);
+ palcol->value = color->value;
+ done = true;
+ }
+ }
+ }
+
+ if (done) {
+ /* Clear old color swatches. */
+ PaletteColor *color_next = NULL;
+ for (PaletteColor *color = palette_join->colors.first; color; color = color_next) {
+ color_next = color->next;
+ BKE_palette_color_remove(palette_join, color);
+ }
+
+ /* Notifier. */
+ WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, NULL);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+static void PALETTE_OT_join(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Join Palette Swatches";
+ ot->idname = "PALETTE_OT_join";
+ ot->description = "Join Palette Swatches";
+
+ /* api callbacks */
+ ot->exec = palette_join_exec;
+ ot->poll = palette_join_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_string(ot->srna, "palette", NULL, MAX_ID_NAME - 2, "Palette", "Name of the Palette");
+}
+
static int brush_reset_exec(bContext *C, wmOperator *UNUSED(op))
{
Paint *paint = BKE_paint_get_active_from_context(C);
@@ -456,6 +770,9 @@ static const ePaintMode brush_select_paint_modes[] = {
PAINT_MODE_WEIGHT,
PAINT_MODE_TEXTURE_3D,
PAINT_MODE_GPENCIL,
+ PAINT_MODE_VERTEX_GPENCIL,
+ PAINT_MODE_SCULPT_GPENCIL,
+ PAINT_MODE_WEIGHT_GPENCIL,
};
static int brush_select_exec(bContext *C, wmOperator *op)
@@ -965,6 +1282,11 @@ void ED_operatortypes_paint(void)
WM_operatortype_append(PALETTE_OT_color_add);
WM_operatortype_append(PALETTE_OT_color_delete);
+ WM_operatortype_append(PALETTE_OT_extract_from_image);
+ WM_operatortype_append(PALETTE_OT_sort);
+ WM_operatortype_append(PALETTE_OT_color_move);
+ WM_operatortype_append(PALETTE_OT_join);
+
/* paint curve */
WM_operatortype_append(PAINTCURVE_OT_new);
WM_operatortype_append(PAINTCURVE_OT_add_point);
diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c
index e01986181bc..e17bf00106a 100644
--- a/source/blender/editors/space_action/action_edit.c
+++ b/source/blender/editors/space_action/action_edit.c
@@ -803,7 +803,7 @@ static void insert_gpencil_keys(bAnimContext *ac, short mode)
/* insert gp frames */
for (ale = anim_data.first; ale; ale = ale->next) {
bGPDlayer *gpl = (bGPDlayer *)ale->data;
- BKE_gpencil_layer_getframe(gpl, CFRA, add_frame_mode);
+ BKE_gpencil_layer_frame_get(gpl, CFRA, add_frame_mode);
}
ANIM_animdata_update(ac, &anim_data);
diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c
index 458c8690e3c..4322e4277ad 100644
--- a/source/blender/editors/space_action/action_select.c
+++ b/source/blender/editors/space_action/action_select.c
@@ -1759,8 +1759,8 @@ static int mouse_action_keys(bAnimContext *ac,
gpl->flag |= GP_LAYER_SELECT;
/* Update other layer status. */
- if (BKE_gpencil_layer_getactive(gpd) != gpl) {
- BKE_gpencil_layer_setactive(gpd, gpl);
+ if (BKE_gpencil_layer_active_get(gpd) != gpl) {
+ BKE_gpencil_layer_active_set(gpd, gpl);
BKE_gpencil_layer_autolock_set(gpd, false);
WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
}
diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c
index 1c26dc8f834..3e88a7a7b88 100644
--- a/source/blender/editors/space_outliner/outliner_draw.c
+++ b/source/blender/editors/space_outliner/outliner_draw.c
@@ -817,7 +817,7 @@ static void namebutton_cb(bContext *C, void *tsep, char *oldname)
bGPDlayer *gpl = te->directdata;
/* always make layer active */
- BKE_gpencil_layer_setactive(gpd, gpl);
+ BKE_gpencil_layer_active_set(gpd, gpl);
// XXX: name needs translation stuff
BLI_uniquename(
@@ -2252,6 +2252,9 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
case eGpencilModifierType_Armature:
data.icon = ICON_MOD_ARMATURE;
break;
+ case eGpencilModifierType_Vertexcolor:
+ data.icon = ICON_MOD_NORMALEDIT;
+ break;
/* Default */
default:
diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c
index 6d449e98c7e..8ddacfc84c2 100644
--- a/source/blender/editors/space_outliner/outliner_select.c
+++ b/source/blender/editors/space_outliner/outliner_select.c
@@ -561,7 +561,7 @@ static eOLDrawState tree_element_active_gplayer(bContext *C,
*/
if (set != OL_SETSEL_NONE) {
if (gpl) {
- BKE_gpencil_layer_setactive(gpd, gpl);
+ BKE_gpencil_layer_active_set(gpd, gpl);
DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_SELECTED, gpd);
}
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index a8514967748..8c884783913 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -1215,6 +1215,9 @@ void ED_view3d_buttons_region_layout_ex(const bContext *C,
case CTX_MODE_WEIGHT_GPENCIL:
ARRAY_SET_ITEMS(contexts, ".greasepencil_weight");
break;
+ case CTX_MODE_VERTEX_GPENCIL:
+ ARRAY_SET_ITEMS(contexts, ".greasepencil_vertex");
+ break;
default:
break;
}
@@ -1232,6 +1235,9 @@ void ED_view3d_buttons_region_layout_ex(const bContext *C,
case CTX_MODE_EDIT_GPENCIL:
ARRAY_SET_ITEMS(contexts, ".greasepencil_edit");
break;
+ case CTX_MODE_VERTEX_GPENCIL:
+ ARRAY_SET_ITEMS(contexts, ".greasepencil_vertex");
+ break;
default:
break;
}
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
index 6fbae1d8cb1..83539900f36 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
@@ -409,7 +409,7 @@ static bool view3d_ruler_item_mousemove(struct Depsgraph *depsgraph,
/* Helper: Find the layer created as ruler. */
static bGPDlayer *view3d_ruler_layer_get(bGPdata *gpd)
{
- for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
if (gpl->flag & GP_LAYER_IS_RULER) {
return gpl;
}
@@ -445,7 +445,7 @@ static bool view3d_ruler_to_gpencil(bContext *C, wmGizmoGroup *gzgroup)
gpl->flag |= GP_LAYER_HIDE | GP_LAYER_IS_RULER;
}
- gpf = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_ADD_NEW);
+ gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_ADD_NEW);
BKE_gpencil_free_strokes(gpf);
for (ruler_item = gzgroup->gizmos.first; ruler_item;
@@ -477,9 +477,10 @@ static bool view3d_ruler_to_gpencil(bContext *C, wmGizmoGroup *gzgroup)
}
gps->flag = GP_STROKE_3DSPACE;
gps->thickness = 3;
- gps->gradient_f = 1.0f;
- gps->gradient_s[0] = 1.0f;
- gps->gradient_s[1] = 1.0f;
+ gps->hardeness = 1.0f;
+ gps->fill_opacity_fac = 1.0f;
+ copy_v2_fl(gps->aspect_ratio, 1.0f);
+ gps->uv_scale = 1.0f;
BLI_addtail(&gpf->strokes, gps);
changed = true;
@@ -498,7 +499,7 @@ static bool view3d_ruler_from_gpencil(const bContext *C, wmGizmoGroup *gzgroup)
gpl = view3d_ruler_layer_get(scene->gpd);
if (gpl) {
bGPDframe *gpf;
- gpf = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_USE_PREV);
+ gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_USE_PREV);
if (gpf) {
bGPDstroke *gps;
for (gps = gpf->strokes.first; gps; gps = gps->next) {
diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c
index 7649bd45a1a..bd92193206f 100644
--- a/source/blender/editors/space_view3d/view3d_select.c
+++ b/source/blender/editors/space_view3d/view3d_select.c
@@ -2265,7 +2265,8 @@ static bool ed_object_select_pick(bContext *C,
if (ELEM(basact->object->mode,
OB_MODE_PAINT_GPENCIL,
OB_MODE_SCULPT_GPENCIL,
- OB_MODE_WEIGHT_GPENCIL)) {
+ OB_MODE_WEIGHT_GPENCIL,
+ OB_MODE_VERTEX_GPENCIL)) {
ED_gpencil_toggle_brush_cursor(C, true, NULL);
}
else {
diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c
index 009164057ce..f93a3ec260b 100644
--- a/source/blender/editors/transform/transform_convert.c
+++ b/source/blender/editors/transform/transform_convert.c
@@ -887,7 +887,7 @@ static void posttrans_gpd_clean(bGPdata *gpd)
for (gpf = gpl->frames.first; gpf; gpf = gpfn) {
gpfn = gpf->next;
if (gpfn && gpf->framenum == gpfn->framenum) {
- BKE_gpencil_layer_delframe(gpl, gpf);
+ BKE_gpencil_layer_frame_delete(gpl, gpf);
}
}
}
@@ -2740,9 +2740,11 @@ void createTransData(bContext *C, TransInfo *t)
has_transform_context = false;
}
}
- else if ((ob) &&
- (ELEM(
- ob->mode, OB_MODE_PAINT_GPENCIL, OB_MODE_SCULPT_GPENCIL, OB_MODE_WEIGHT_GPENCIL))) {
+ else if ((ob) && (ELEM(ob->mode,
+ OB_MODE_PAINT_GPENCIL,
+ OB_MODE_SCULPT_GPENCIL,
+ OB_MODE_WEIGHT_GPENCIL,
+ OB_MODE_VERTEX_GPENCIL))) {
/* In grease pencil all transformations must be canceled if not Object or Edit. */
has_transform_context = false;
}
diff --git a/source/blender/editors/transform/transform_convert_gpencil.c b/source/blender/editors/transform/transform_convert_gpencil.c
index c61961e46d1..17e69ff38b8 100644
--- a/source/blender/editors/transform/transform_convert_gpencil.c
+++ b/source/blender/editors/transform/transform_convert_gpencil.c
@@ -83,6 +83,8 @@ void createTransGPencil(bContext *C, TransInfo *t)
const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
const bool is_prop_edit_connected = (t->flag & T_PROP_CONNECTED) != 0;
+ const bool is_scale_thickness = ((t->mode == TFM_GPENCIL_SHRINKFATTEN) ||
+ (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_SCALE_THICKNESS));
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
@@ -108,7 +110,7 @@ void createTransGPencil(bContext *C, TransInfo *t)
*/
for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
/* only editable and visible layers are considered */
- if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
+ if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
bGPDframe *gpf;
bGPDstroke *gps;
bGPDframe *init_gpf = gpl->actframe;
@@ -180,7 +182,7 @@ void createTransGPencil(bContext *C, TransInfo *t)
/* Second Pass: Build transdata array */
for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
/* only editable and visible layers are considered */
- if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
+ if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
const int cfra = (gpl->flag & GP_LAYER_FRAMELOCK) ? gpl->actframe->framenum : cfra_scene;
bGPDframe *gpf = gpl->actframe;
bGPDstroke *gps;
@@ -196,11 +198,11 @@ void createTransGPencil(bContext *C, TransInfo *t)
int f_end = 0;
if (use_multiframe_falloff) {
- BKE_gpencil_get_range_selected(gpl, &f_init, &f_end);
+ BKE_gpencil_frame_range_selected(gpl, &f_init, &f_end);
}
/* calculate difference matrix */
- ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat);
+ BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat);
/* undo matrix */
invert_m4_m4(inverse_diff_mat, diff_mat);
@@ -312,9 +314,9 @@ void createTransGPencil(bContext *C, TransInfo *t)
}
/* for other transform modes (e.g. shrink-fatten), need to additional data
- * but never for scale or mirror
+ * but never for mirror
*/
- if ((t->mode != TFM_RESIZE) && (t->mode != TFM_MIRROR)) {
+ if ((t->mode != TFM_MIRROR) && (is_scale_thickness)) {
if (t->mode != TFM_GPENCIL_OPACITY) {
td->val = &pt->pressure;
td->ival = pt->pressure;
diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c
index 3fe1b99adfb..bb4d50fcf54 100644
--- a/source/blender/editors/transform/transform_generics.c
+++ b/source/blender/editors/transform/transform_generics.c
@@ -1149,12 +1149,15 @@ static void recalcData_sequencer(TransInfo *t)
static void recalcData_gpencil_strokes(TransInfo *t)
{
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
+ bGPDstroke *gps_prev = NULL;
TransData *td = tc->data;
for (int i = 0; i < tc->data_len; i++, td++) {
bGPDstroke *gps = td->extra;
- if (gps != NULL) {
- gps->flag |= GP_STROKE_RECALC_GEOMETRY;
+ if ((gps != NULL) && (gps != gps_prev)) {
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gps);
+ gps_prev = gps;
}
}
}
diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c
index 02767156ef4..0548cc4e503 100644
--- a/source/blender/editors/transform/transform_gizmo_3d.c
+++ b/source/blender/editors/transform/transform_gizmo_3d.c
@@ -792,13 +792,13 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
if (is_gp_edit) {
float diff_mat[4][4];
const bool use_mat_local = true;
- for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
/* only editable and visible layers are considered */
- if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
+ if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
/* calculate difference matrix */
- ED_gpencil_parent_location(depsgraph, ob, gpd, gpl, diff_mat);
+ BKE_gpencil_parent_matrix_get(depsgraph, ob, gpl, diff_mat);
for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) {
/* skip strokes that are invalid for current view */
diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c
index 5b5d1338637..cb4f4dfa46c 100644
--- a/source/blender/editors/undo/ed_undo.c
+++ b/source/blender/editors/undo/ed_undo.c
@@ -202,7 +202,8 @@ static int ed_undo_step_impl(
if (ELEM(obact->mode,
OB_MODE_PAINT_GPENCIL,
OB_MODE_SCULPT_GPENCIL,
- OB_MODE_WEIGHT_GPENCIL)) {
+ OB_MODE_WEIGHT_GPENCIL,
+ OB_MODE_VERTEX_GPENCIL)) {
ED_gpencil_toggle_brush_cursor(C, true, NULL);
}
else {