diff options
Diffstat (limited to 'source/blender/editors/gpencil')
-rw-r--r-- | source/blender/editors/gpencil/annotate_draw.c | 277 | ||||
-rw-r--r-- | source/blender/editors/gpencil/annotate_paint.c | 500 | ||||
-rw-r--r-- | source/blender/editors/gpencil/gpencil_armature.c | 6 | ||||
-rw-r--r-- | source/blender/editors/gpencil/gpencil_convert.c | 2 | ||||
-rw-r--r-- | source/blender/editors/gpencil/gpencil_data.c | 56 | ||||
-rw-r--r-- | source/blender/editors/gpencil/gpencil_edit.c | 277 | ||||
-rw-r--r-- | source/blender/editors/gpencil/gpencil_fill.c | 2 | ||||
-rw-r--r-- | source/blender/editors/gpencil/gpencil_intern.h | 22 | ||||
-rw-r--r-- | source/blender/editors/gpencil/gpencil_ops.c | 3 | ||||
-rw-r--r-- | source/blender/editors/gpencil/gpencil_paint.c | 169 | ||||
-rw-r--r-- | source/blender/editors/gpencil/gpencil_primitive.c | 129 | ||||
-rw-r--r-- | source/blender/editors/gpencil/gpencil_select.c | 14 | ||||
-rw-r--r-- | source/blender/editors/gpencil/gpencil_utils.c | 209 |
13 files changed, 1253 insertions, 413 deletions
diff --git a/source/blender/editors/gpencil/annotate_draw.c b/source/blender/editors/gpencil/annotate_draw.c index adaf4ab2459..22df7bbbf31 100644 --- a/source/blender/editors/gpencil/annotate_draw.c +++ b/source/blender/editors/gpencil/annotate_draw.c @@ -90,14 +90,54 @@ typedef enum eDrawStrokeFlags { /* ----- Tool Buffer Drawing ------ */ +static void annotation_draw_stroke_arrow_buffer(uint pos, + const float *corner_point, + const float *arrow_coords, + const int arrow_style) +{ + immBeginAtMost(GPU_PRIM_LINE_STRIP, arrow_style); + + switch (arrow_style) { + case GP_STROKE_ARROWSTYLE_SEGMENT: + immVertex2f(pos, arrow_coords[0], arrow_coords[1]); + immVertex2f(pos, arrow_coords[2], arrow_coords[3]); + break; + case GP_STROKE_ARROWSTYLE_CLOSED: + immVertex2f(pos, arrow_coords[0], arrow_coords[1]); + immVertex2f(pos, arrow_coords[2], arrow_coords[3]); + immVertex2f(pos, arrow_coords[4], arrow_coords[5]); + immVertex2f(pos, arrow_coords[0], arrow_coords[1]); + break; + case GP_STROKE_ARROWSTYLE_OPEN: + immVertex2f(pos, arrow_coords[0], arrow_coords[1]); + immVertex2f(pos, corner_point[0], corner_point[1]); + immVertex2f(pos, arrow_coords[2], arrow_coords[3]); + break; + case GP_STROKE_ARROWSTYLE_SQUARE: + immVertex2f(pos, corner_point[0], corner_point[1]); + immVertex2f(pos, arrow_coords[0], arrow_coords[1]); + immVertex2f(pos, arrow_coords[4], arrow_coords[5]); + immVertex2f(pos, arrow_coords[6], arrow_coords[7]); + immVertex2f(pos, arrow_coords[2], arrow_coords[3]); + immVertex2f(pos, corner_point[0], corner_point[1]); + break; + default: + break; + } + immEnd(); +} + /* draw stroke defined in buffer (simple ogl lines/points for now, as dotted lines) */ -static void annotation_draw_stroke_buffer(const tGPspoint *points, - int totpoints, +static void annotation_draw_stroke_buffer(bGPdata *gps, short thickness, short dflag, - short sflag, const float ink[4]) { + bGPdata_Runtime runtime = gps->runtime; + const tGPspoint *points = runtime.sbuffer; + int totpoints = runtime.sbuffer_used; + short sflag = runtime.sbuffer_sflag; + int draw_points = 0; /* error checking */ @@ -176,6 +216,26 @@ static void annotation_draw_stroke_buffer(const tGPspoint *points, } immEnd(); + + /* Draw arrow stroke. */ + if (totpoints > 1) { + /* Draw ending arrow stroke. */ + if ((sflag & GP_STROKE_USE_ARROW_END) && + (runtime.arrow_end_style != GP_STROKE_ARROWSTYLE_NONE)) { + float end[2]; + copy_v2_fl2(end, points[1].x, points[1].y); + annotation_draw_stroke_arrow_buffer(pos, end, runtime.arrow_end, runtime.arrow_end_style); + } + /* Draw starting arrow stroke. */ + if ((sflag & GP_STROKE_USE_ARROW_START) && + (runtime.arrow_start_style != GP_STROKE_ARROWSTYLE_NONE)) { + float start[2]; + copy_v2_fl2(start, points[0].x, points[0].y); + annotation_draw_stroke_arrow_buffer( + pos, start, runtime.arrow_start, runtime.arrow_start_style); + } + } + immUnbindProgram(); } @@ -524,131 +584,6 @@ static void annotation_draw_strokes(const bGPDframe *gpf, GPU_program_point_size(false); } -/* Draw selected verts for strokes being edited */ -static void annotation_draw_strokes_edit(bGPDlayer *gpl, - const bGPDframe *gpf, - int offsx, - int offsy, - int winx, - int winy, - short dflag, - float alpha) -{ - /* if alpha 0 do not draw */ - if (alpha == 0.0f) { - return; - } - - const bool no_xray = (dflag & GP_DRAWDATA_NO_XRAY) != 0; - int mask_orig = 0; - - /* set up depth masks... */ - if (dflag & GP_DRAWDATA_ONLY3D) { - if (no_xray) { - glGetIntegerv(GL_DEPTH_WRITEMASK, &mask_orig); - glDepthMask(0); - GPU_depth_test(true); - - /* first arg is normally rv3d->dist, but this isn't - * available here and seems to work quite well without */ - bglPolygonOffset(1.0f, 1.0f); - } - } - - GPU_program_point_size(true); - - /* draw stroke verts */ - LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { - /* check if stroke can be drawn */ - if (annotation_can_draw_stroke(gps, dflag) == false) { - continue; - } - - /* Optimization: only draw points for selected strokes - * We assume that selected points can only occur in - * strokes that are selected too. - */ - if ((gps->flag & GP_STROKE_SELECT) == 0) { - continue; - } - - /* Get size of verts: - * - The selected state needs to be larger than the unselected state so that - * they stand out more. - * - We use the theme setting for size of the unselected verts - */ - float bsize = UI_GetThemeValuef(TH_GP_VERTEX_SIZE); - float vsize; - if ((int)bsize > 8) { - vsize = 10.0f; - bsize = 8.0f; - } - else { - vsize = bsize + 2; - } - - /* Why? */ - UNUSED_VARS(vsize); - - float selectColor[4]; - UI_GetThemeColor3fv(TH_GP_VERTEX_SELECT, selectColor); - selectColor[3] = alpha; - - GPUVertFormat *format = immVertexFormat(); - uint pos; /* specified later */ - uint size = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - - if (gps->flag & GP_STROKE_3DSPACE) { - pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_3D_POINT_VARYING_SIZE_VARYING_COLOR); - } - else { - pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_POINT_VARYING_SIZE_VARYING_COLOR); - } - - immBegin(GPU_PRIM_POINTS, gps->totpoints); - - /* 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 */ - immAttr3fv(color, gpl->color); - immAttr1f(size, bsize); - - /* then position */ - if (gps->flag & GP_STROKE_3DSPACE) { - immVertex3fv(pos, &pt->x); - } - else { - float co[2]; - annotation_calc_2d_stroke_fxy(&pt->x, gps->flag, offsx, offsy, winx, winy, co); - immVertex2fv(pos, co); - } - } - - immEnd(); - immUnbindProgram(); - } - - GPU_program_point_size(false); - - /* clear depth mask */ - if (dflag & GP_DRAWDATA_ONLY3D) { - if (no_xray) { - glDepthMask(mask_orig); - GPU_depth_test(false); - - bglPolygonOffset(0.0, 0.0); -#if 0 - glDisable(GL_POLYGON_OFFSET_LINE); - glPolygonOffset(0, 0); -#endif - } - } -} - /* ----- General Drawing ------ */ /* draw onion-skinning for a layer */ static void annotation_draw_onionskins( @@ -724,7 +659,7 @@ static void annotation_draw_onionskins( /* loop over gpencil data layers, drawing them */ static void annotation_draw_data_layers( - bGPdata *gpd, int offsx, int offsy, int winx, int winy, int cfra, int dflag, float alpha) + bGPdata *gpd, int offsx, int offsy, int winx, int winy, int cfra, int dflag) { float ink[4]; @@ -767,21 +702,6 @@ static void annotation_draw_data_layers( /* draw the strokes already in active frame */ annotation_draw_strokes(gpf, offsx, offsy, winx, winy, dflag, lthick, ink); - /* Draw verts of selected strokes: - * - when doing OpenGL renders, we don't want to be showing these, as that ends up - * flickering - * - locked layers can't be edited, so there's no point showing these verts - * as they will have no bearings on what gets edited - * - only show when in editmode, since operators shouldn't work otherwise - * (NOTE: doing it this way means that the toggling editmode - * shows visible change immediately). - */ - /* XXX: perhaps we don't want to show these when users are drawing... */ - if ((G.f & G_FLAG_RENDER_VIEWPORT) == 0 && (gpl->flag & GP_LAYER_LOCKED) == 0 && - (gpd->flag & GP_DATA_STROKE_EDITMODE)) { - annotation_draw_strokes_edit(gpl, gpf, offsx, offsy, winx, winy, dflag, alpha); - } - /* Check if may need to draw the active stroke cache, only if this layer is the active layer * that is being edited. (Stroke buffer is currently stored in gp-data) */ @@ -793,67 +713,14 @@ static void annotation_draw_data_layers( * It should also be noted that sbuffer contains temporary point types * i.e. tGPspoints NOT bGPDspoints */ - annotation_draw_stroke_buffer(gpd->runtime.sbuffer, - gpd->runtime.sbuffer_used, - lthick, - dflag, - gpd->runtime.sbuffer_sflag, - ink); + annotation_draw_stroke_buffer(gpd, lthick, dflag, ink); } } } -/* draw a short status message in the top-right corner */ -static void annotation_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); - } -} - /* draw grease-pencil datablock */ static void annotation_draw_data( - bGPdata *gpd, int offsx, int offsy, int winx, int winy, int cfra, int dflag, float alpha) + bGPdata *gpd, int offsx, int offsy, int winx, int winy, int cfra, int dflag) { /* turn on smooth lines (i.e. anti-aliasing) */ GPU_line_smooth(true); @@ -864,7 +731,7 @@ static void annotation_draw_data( GPU_blend(true); /* draw! */ - annotation_draw_data_layers(gpd, offsx, offsy, winx, winy, cfra, dflag, alpha); + annotation_draw_data_layers(gpd, offsx, offsy, winx, winy, cfra, dflag); /* turn off alpha blending, then smooth lines */ GPU_blend(false); // alpha blending @@ -884,7 +751,6 @@ static void annotation_draw_data_all(Scene *scene, const char spacetype) { bGPdata *gpd_source = NULL; - float alpha = 1.0f; if (scene) { if (spacetype == SPACE_VIEW3D) { @@ -897,14 +763,14 @@ static void annotation_draw_data_all(Scene *scene, } if (gpd_source) { - annotation_draw_data(gpd_source, offsx, offsy, winx, winy, cfra, dflag, alpha); + annotation_draw_data(gpd_source, offsx, offsy, winx, winy, cfra, dflag); } } /* scene/clip data has already been drawn, only object/track data is drawn here * if gpd_source == gpd, we don't have any object/track data and we can skip */ if (gpd_source == NULL || (gpd_source && gpd_source != gpd)) { - annotation_draw_data(gpd, offsx, offsy, winx, winy, cfra, dflag, alpha); + annotation_draw_data(gpd, offsx, offsy, winx, winy, cfra, dflag); } } @@ -1026,11 +892,6 @@ void ED_annotation_draw_view2d(const bContext *C, bool onlyv2d) annotation_draw_data_all( scene, gpd, 0, 0, region->winx, region->winy, CFRA, dflag, area->spacetype); - - /* draw status text (if in screen/pixel-space) */ - if (!onlyv2d) { - annotation_draw_status_text(gpd, region); - } } /* draw annotations sketches to specified 3d-view assuming that matrices are already set diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index 8d50e24b7f0..c53b90fbfee 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -100,6 +100,9 @@ typedef enum eGPencil_PaintFlags { GP_PAINTFLAG_STROKEADDED = (1 << 1), GP_PAINTFLAG_V3D_ERASER_DEPTH = (1 << 2), GP_PAINTFLAG_SELECTMASK = (1 << 3), + /* Flags used to indicate if stabilization is being used. */ + GP_PAINTFLAG_USE_STABILIZER = (1 << 7), + GP_PAINTFLAG_USE_STABILIZER_TEMP = (1 << 8), } eGPencil_PaintFlags; /* Temporary 'Stroke' Operation data @@ -148,6 +151,11 @@ typedef struct tGPsdata { /** radius of influence for eraser. */ short radius; + /* Stabilizer. */ + float stabilizer_factor; + char stabilizer_radius; + void *stabilizer_cursor; + /** current mouse-position. */ float mval[2]; /** previous recorded mouse-position. */ @@ -278,6 +286,18 @@ static bool gp_stroke_filtermval(tGPsdata *p, const float mval[2], float pmval[2 * - aims to eliminate some jitter-noise from input when trying to draw straight lines freehand */ } + /* If lazy mouse, check minimum distance. */ + else if (p->flags & GP_PAINTFLAG_USE_STABILIZER_TEMP) { + if ((dx * dx + dy * dy) > (p->stabilizer_radius * p->stabilizer_radius)) { + return true; + } + else { + /* If the mouse is moving within the radius of the last move, + * don't update the mouse position. This allows sharp turns. */ + copy_v2_v2(p->mval, p->mvalo); + return false; + } + } else if ((dx > MIN_MANHATTEN_PX) && (dy > MIN_MANHATTEN_PX)) { return true; @@ -418,6 +438,85 @@ static void gp_smooth_buffer(tGPsdata *p, float inf, int idx) copy_v2_v2(&ptc->x, c); } +static void gp_stroke_arrow_calc_points_segment(float stroke_points[8], + const float ref_point[2], + const float dir_cw[2], + const float dir_ccw[2], + const float lenght, + const float sign) +{ + stroke_points[0] = ref_point[0] + dir_cw[0] * lenght * sign; + stroke_points[1] = ref_point[1] + dir_cw[1] * lenght * sign; + stroke_points[2] = ref_point[0] + dir_ccw[0] * lenght * sign; + stroke_points[3] = ref_point[1] + dir_ccw[1] * lenght * sign; +} + +static void gp_stroke_arrow_calc_points(tGPspoint *point, + const float stroke_dir[2], + float corner[2], + float stroke_points[8], + const int arrow_style) +{ + const int arrow_lenght = 8; + float norm_dir[2]; + copy_v2_v2(norm_dir, stroke_dir); + normalize_v2(norm_dir); + const float inv_norm_dir_clockwise[2] = {norm_dir[1], -norm_dir[0]}; + const float inv_norm_dir_counterclockwise[2] = {-norm_dir[1], norm_dir[0]}; + + switch (arrow_style) { + case GP_STROKE_ARROWSTYLE_OPEN: + mul_v2_fl(norm_dir, arrow_lenght); + stroke_points[0] = corner[0] + inv_norm_dir_clockwise[0] * arrow_lenght + norm_dir[0]; + stroke_points[1] = corner[1] + inv_norm_dir_clockwise[1] * arrow_lenght + norm_dir[1]; + stroke_points[2] = corner[0] + inv_norm_dir_counterclockwise[0] * arrow_lenght + norm_dir[0]; + stroke_points[3] = corner[1] + inv_norm_dir_counterclockwise[1] * arrow_lenght + norm_dir[1]; + break; + case GP_STROKE_ARROWSTYLE_SEGMENT: + gp_stroke_arrow_calc_points_segment(stroke_points, + corner, + inv_norm_dir_clockwise, + inv_norm_dir_counterclockwise, + arrow_lenght, + 1.0f); + break; + case GP_STROKE_ARROWSTYLE_CLOSED: + mul_v2_fl(norm_dir, arrow_lenght); + if (point != NULL) { + add_v2_v2(&point->x, norm_dir); + copy_v2_v2(corner, &point->x); + } + gp_stroke_arrow_calc_points_segment(stroke_points, + corner, + inv_norm_dir_clockwise, + inv_norm_dir_counterclockwise, + arrow_lenght, + -1.0f); + stroke_points[4] = corner[0] - norm_dir[0]; + stroke_points[5] = corner[1] - norm_dir[1]; + break; + case GP_STROKE_ARROWSTYLE_SQUARE: + mul_v2_fl(norm_dir, arrow_lenght * 1.5f); + if (point != NULL) { + add_v2_v2(&point->x, norm_dir); + copy_v2_v2(corner, &point->x); + } + gp_stroke_arrow_calc_points_segment(stroke_points, + corner, + inv_norm_dir_clockwise, + inv_norm_dir_counterclockwise, + arrow_lenght * 0.75f, + -1.0f); + stroke_points[4] = stroke_points[0] - norm_dir[0]; + stroke_points[5] = stroke_points[1] - norm_dir[1]; + stroke_points[6] = stroke_points[2] - norm_dir[0]; + stroke_points[7] = stroke_points[3] - norm_dir[1]; + break; + default: + break; + } +} + /* 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) { @@ -457,6 +556,32 @@ static short gp_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure /* now the buffer has 2 points (and shouldn't be allowed to get any larger) */ gpd->runtime.sbuffer_used = 2; + + /* Arrows. */ + if (gpd->runtime.sbuffer_sflag & (GP_STROKE_USE_ARROW_START | GP_STROKE_USE_ARROW_END)) { + /* Store start and end point coords for arrows. */ + float end[2]; + copy_v2_v2(end, &pt->x); + pt = ((tGPspoint *)(gpd->runtime.sbuffer)); + float start[2]; + copy_v2_v2(start, &pt->x); + + /* Arrow end corner. */ + if (gpd->runtime.sbuffer_sflag & GP_STROKE_USE_ARROW_END) { + pt++; + float e_heading[2] = {start[0] - end[0], start[1] - end[1]}; + /* Calculate points for ending arrow. */ + gp_stroke_arrow_calc_points( + pt, e_heading, end, gpd->runtime.arrow_end, gpd->runtime.arrow_end_style); + } + /* Arrow start corner. */ + if (gpd->runtime.sbuffer_sflag & GP_STROKE_USE_ARROW_START) { + float s_heading[2] = {end[0] - start[0], end[1] - start[1]}; + /* Calculate points for starting arrow. */ + gp_stroke_arrow_calc_points( + NULL, s_heading, start, gpd->runtime.arrow_start, gpd->runtime.arrow_start_style); + } + } } /* can keep carrying on this way :) */ @@ -481,16 +606,20 @@ static short gp_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure /* increment counters */ gpd->runtime.sbuffer_used++; - /* smooth while drawing previous points with a reduction factor for previous */ - for (int s = 0; s < 3; s++) { - gp_smooth_buffer(p, 0.5f * ((3.0f - s) / 3.0f), gpd->runtime.sbuffer_used - s); + + /* Don't smooth if stabilizer is on. */ + if ((p->flags & GP_PAINTFLAG_USE_STABILIZER_TEMP) == 0) { + /* smooth while drawing previous points with a reduction factor for previous */ + for (int s = 0; s < 3; s++) { + gp_smooth_buffer(p, 0.5f * ((3.0f - s) / 3.0f), gpd->runtime.sbuffer_used - s); + } } return GP_STROKEADD_NORMAL; } else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { /* get pointer to destination point */ - pt = (tGPspoint *)(gpd->runtime.sbuffer); + pt = (tGPspoint *)gpd->runtime.sbuffer; /* store settings */ copy_v2_v2(&pt->x, mval); @@ -552,6 +681,123 @@ static short gp_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure return GP_STROKEADD_INVALID; } +static void gp_stroke_arrow_init_point_default(bGPDspoint *pt) +{ + pt->pressure = 1.0f; + pt->strength = 1.0f; + pt->time = 1.0f; +} + +static void gp_stroke_arrow_init_conv_point(bGPDspoint *pt, const float point[3]) +{ + copy_v3_v3(&pt->x, point); + gp_stroke_arrow_init_point_default(pt); +} + +static void gp_stroke_arrow_init_point( + tGPsdata *p, tGPspoint *ptc, bGPDspoint *pt, const float co[8], const int co_idx) +{ + /* Note: provided co_idx should be always pair number as it's [x1, y1, x2, y2, x3, y3]. */ + float real_co[2] = {co[co_idx], co[co_idx + 1]}; + copy_v2_v2(&ptc->x, real_co); + gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); + gp_stroke_arrow_init_point_default(pt); +} + +static void gp_stroke_arrow_allocate(bGPDstroke *gps, const int totpoints) +{ + /* Copy appropriate settings for stroke. */ + gps->totpoints = totpoints; + /* Allocate enough memory for a continuous array for storage points. */ + gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); +} + +static void gp_arrow_create_open(tGPsdata *p, + tGPspoint *ptc, + bGPDspoint *pt, + const float corner_point[3], + const float arrow_points[8]) +{ + gp_stroke_arrow_init_point(p, ptc, pt, arrow_points, 0); + pt++; + gp_stroke_arrow_init_conv_point(pt, corner_point); + pt++; + gp_stroke_arrow_init_point(p, ptc, pt, arrow_points, 2); +} + +static void gp_arrow_create_segm(tGPsdata *p, + tGPspoint *ptc, + bGPDspoint *pt, + const float arrow_points[8]) +{ + gp_stroke_arrow_init_point(p, ptc, pt, arrow_points, 0); + pt++; + gp_stroke_arrow_init_point(p, ptc, pt, arrow_points, 2); +} + +static void gp_arrow_create_closed(tGPsdata *p, + tGPspoint *ptc, + bGPDspoint *pt, + const float arrow_points[8]) +{ + gp_stroke_arrow_init_point(p, ptc, pt, arrow_points, 0); + pt++; + gp_stroke_arrow_init_point(p, ptc, pt, arrow_points, 2); + pt++; + gp_stroke_arrow_init_point(p, ptc, pt, arrow_points, 4); + pt++; + gp_stroke_arrow_init_point(p, ptc, pt, arrow_points, 0); +} + +static void gp_arrow_create_square(tGPsdata *p, + tGPspoint *ptc, + bGPDspoint *pt, + const float corner_point[3], + const float arrow_points[8]) +{ + gp_stroke_arrow_init_conv_point(pt, corner_point); + pt++; + gp_stroke_arrow_init_point(p, ptc, pt, arrow_points, 0); + pt++; + gp_stroke_arrow_init_point(p, ptc, pt, arrow_points, 4); + pt++; + gp_stroke_arrow_init_point(p, ptc, pt, arrow_points, 6); + pt++; + gp_stroke_arrow_init_point(p, ptc, pt, arrow_points, 2); + pt++; + gp_stroke_arrow_init_conv_point(pt, corner_point); +} + +static void gp_arrow_create(tGPsdata *p, + tGPspoint *ptc, + bGPDspoint *pt, + bGPDstroke *arrow_stroke, + const float arrow_points[8], + const int style) +{ + float corner_conv[3]; + copy_v3_v3(corner_conv, &pt->x); + + switch (style) { + case GP_STROKE_ARROWSTYLE_SEGMENT: + gp_arrow_create_segm(p, ptc, pt, arrow_points); + break; + case GP_STROKE_ARROWSTYLE_CLOSED: + gp_arrow_create_closed(p, ptc, pt, arrow_points); + break; + case GP_STROKE_ARROWSTYLE_OPEN: + gp_arrow_create_open(p, ptc, pt, corner_conv, arrow_points); + break; + case GP_STROKE_ARROWSTYLE_SQUARE: + gp_arrow_create_square(p, ptc, pt, corner_conv, arrow_points); + break; + default: + break; + } + /* Link stroke to frame. */ + BLI_addtail(&p->gpf->strokes, arrow_stroke); +} + /* make a new stroke from the buffer data */ static void gp_stroke_newfrombuffer(tGPsdata *p) { @@ -637,17 +883,61 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) } if (totelem == 2) { - /* last point if applicable */ - ptc = ((tGPspoint *)gpd->runtime.sbuffer) + (gpd->runtime.sbuffer_used - 1); + bGPdata_Runtime runtime = gpd->runtime; - /* convert screen-coordinates to appropriate coordinates (and store them) */ + /* Last point if applicable. */ + ptc = ((tGPspoint *)runtime.sbuffer) + (runtime.sbuffer_used - 1); + + /* Convert screen-coordinates to appropriate coordinates (and store them). */ gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); - /* copy pressure and time */ + /* Copy pressure and time. */ pt->pressure = ptc->pressure; pt->strength = ptc->strength; CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); pt->time = ptc->time; + + /** Create arrow strokes. **/ + /* End arrow stroke. */ + if ((runtime.sbuffer_sflag & GP_STROKE_USE_ARROW_END) && + (runtime.arrow_end_style != GP_STROKE_ARROWSTYLE_NONE)) { + int totarrowpoints = runtime.arrow_end_style; + + /* Setting up arrow stroke. */ + bGPDstroke *e_arrow_gps = BKE_gpencil_stroke_duplicate(gps, false); + gp_stroke_arrow_allocate(e_arrow_gps, totarrowpoints); + + /* Set pointer to first non-initialized point. */ + pt = e_arrow_gps->points + (e_arrow_gps->totpoints - totarrowpoints); + + /* End point. */ + ptc = ((tGPspoint *)runtime.sbuffer) + (runtime.sbuffer_used - 1); + gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); + gp_stroke_arrow_init_point_default(pt); + + /* Fill and convert arrow points to create arrow shape. */ + gp_arrow_create(p, ptc, pt, e_arrow_gps, runtime.arrow_end, runtime.arrow_end_style); + } + /* Start arrow stroke. */ + if ((runtime.sbuffer_sflag & GP_STROKE_USE_ARROW_START) && + (runtime.arrow_start_style != GP_STROKE_ARROWSTYLE_NONE)) { + int totarrowpoints = runtime.arrow_start_style; + + /* Setting up arrow stroke. */ + bGPDstroke *s_arrow_gps = BKE_gpencil_stroke_duplicate(gps, false); + gp_stroke_arrow_allocate(s_arrow_gps, totarrowpoints); + + /* Set pointer to first non-initialized point. */ + pt = s_arrow_gps->points + (s_arrow_gps->totpoints - totarrowpoints); + + /* Start point. */ + ptc = runtime.sbuffer; + gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); + gp_stroke_arrow_init_point_default(pt); + + /* Fill and convert arrow points to create arrow shape. */ + gp_arrow_create(p, ptc, pt, s_arrow_gps, runtime.arrow_start, runtime.arrow_start_style); + } } } else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { @@ -1456,6 +1746,67 @@ static void gpencil_draw_toggle_eraser_cursor(bContext *C, tGPsdata *p, short en p); } } +static void gpencil_draw_stabilizer(bContext *C, int x, int y, void *p_ptr) +{ + ARegion *region = CTX_wm_region(C); + tGPsdata *p = (tGPsdata *)p_ptr; + bGPdata_Runtime runtime = p->gpd->runtime; + const tGPspoint *points = runtime.sbuffer; + int totpoints = runtime.sbuffer_used; + if (totpoints < 2) { + return; + } + const tGPspoint *pt = &points[totpoints - 1]; + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + GPU_line_smooth(true); + GPU_blend(true); + GPU_line_width(1.25f); + const float color[3] = {1.0f, 0.39f, 0.39f}; + + /* default radius and color */ + float darkcolor[3]; + const float radius = 4.0f; + + /* Inner Ring: Color from UI panel */ + immUniformColor4f(color[0], color[1], color[2], 0.8f); + imm_draw_circle_wire_2d(pos, x, y, radius, 40); + + /* Outer Ring: Dark color for contrast on light backgrounds (e.g. gray on white) */ + mul_v3_v3fl(darkcolor, color, 0.40f); + immUniformColor4f(darkcolor[0], darkcolor[1], darkcolor[2], 0.8f); + imm_draw_circle_wire_2d(pos, x, y, radius + 1, 40); + + /* Rope Simple. */ + immUniformColor4f(color[0], color[1], color[2], 0.8f); + immBegin(GPU_PRIM_LINES, 2); + immVertex2f(pos, pt->x + region->winrct.xmin, pt->y + region->winrct.ymin); + immVertex2f(pos, x, y); + immEnd(); + + /* Returns back all GPU settings */ + GPU_blend(false); + GPU_line_smooth(false); + + immUnbindProgram(); +} + +/* Turn *stabilizer* brush cursor in 3D view on/off */ +static void gpencil_draw_toggle_stabilizer_cursor(bContext *C, tGPsdata *p, short enable) +{ + if (p->stabilizer_cursor && !enable) { + /* clear cursor */ + WM_paint_cursor_end(CTX_wm_manager(C), p->stabilizer_cursor); + p->stabilizer_cursor = NULL; + } + else if (enable && !p->stabilizer_cursor) { + /* enable cursor */ + p->stabilizer_cursor = WM_paint_cursor_activate( + CTX_wm_manager(C), SPACE_TYPE_ANY, RGN_TYPE_ANY, NULL, gpencil_draw_stabilizer, p); + } +} /* Check if tablet eraser is being used (when processing events) */ static bool gpencil_is_tablet_eraser_active(const wmEvent *event) @@ -1479,6 +1830,9 @@ static void gpencil_draw_exit(bContext *C, wmOperator *op) /* turn off radial brush cursor */ gpencil_draw_toggle_eraser_cursor(C, p, false); } + else if (p->paintmode == GP_PAINTMODE_DRAW) { + gpencil_draw_toggle_stabilizer_cursor(C, p, false); + } /* always store the new eraser size to be used again next time * NOTE: Do this even when not in eraser mode, as eraser may @@ -1566,8 +1920,8 @@ static void gpencil_draw_status_indicators(bContext *C, tGPsdata *p) "ESC/Enter to end (or click outside this area)")); break; default: - /* Do nothing - the others are self explanatory, exit quickly once the mouse is released - * Showing any text would just be annoying as it would flicker. + /* Do nothing - the others are self explanatory, exit quickly once the mouse is + * released Showing any text would just be annoying as it would flicker. */ break; } @@ -1632,6 +1986,16 @@ static void gpencil_draw_apply(wmOperator *op, tGPsdata *p, Depsgraph *depsgraph /* 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 (p->flags & GP_PAINTFLAG_USE_STABILIZER_TEMP) { + 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, min_ff(p->stabilizer_factor, .995f)); + copy_v2_v2(p->mval, now_mouse); + } + /* try to add point */ short ok = gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime); @@ -1675,7 +2039,7 @@ static void gpencil_draw_apply(wmOperator *op, tGPsdata *p, Depsgraph *depsgraph /* handle draw event */ static void annotation_draw_apply_event( - wmOperator *op, const wmEvent *event, Depsgraph *depsgraph, float x, float y) + bContext *C, wmOperator *op, const wmEvent *event, Depsgraph *depsgraph, float x, float y) { tGPsdata *p = op->customdata; PointerRNA itemptr; @@ -1687,8 +2051,23 @@ static void annotation_draw_apply_event( p->mval[0] = (float)event->mval[0] - x; p->mval[1] = (float)event->mval[1] - y; + /* Key to toggle stabilization. */ + if (event->shift > 0 && p->paintmode == GP_PAINTMODE_DRAW) { + /* Using permanent stabilization, shift will deactivate the flag. */ + if (p->flags & (GP_PAINTFLAG_USE_STABILIZER)) { + if (p->flags & GP_PAINTFLAG_USE_STABILIZER_TEMP) { + gpencil_draw_toggle_stabilizer_cursor(C, p, false); + p->flags &= ~GP_PAINTFLAG_USE_STABILIZER_TEMP; + } + } + /* Not using any stabilization flag. Activate temporal one. */ + else if ((p->flags & GP_PAINTFLAG_USE_STABILIZER_TEMP) == 0) { + p->flags |= GP_PAINTFLAG_USE_STABILIZER_TEMP; + gpencil_draw_toggle_stabilizer_cursor(C, p, true); + } + } /* verify key status for straight lines */ - if ((event->ctrl > 0) || (event->alt > 0)) { + else if ((event->ctrl > 0) || (event->alt > 0)) { if (p->straight[0] == 0) { int dx = abs((int)(p->mval[0] - p->mvalo[0])); int dy = abs((int)(p->mval[1] - p->mvalo[1])); @@ -1709,6 +2088,22 @@ static void annotation_draw_apply_event( } else { p->straight[0] = 0; + /* We were using shift while having permanent stabilization actived, + so activate the temp flag back again. */ + if (p->flags & GP_PAINTFLAG_USE_STABILIZER) { + if ((p->flags & GP_PAINTFLAG_USE_STABILIZER_TEMP) == 0) { + gpencil_draw_toggle_stabilizer_cursor(C, p, true); + p->flags |= GP_PAINTFLAG_USE_STABILIZER_TEMP; + } + } + /* We are using the temporal stabilizer flag atm, + but shift is not pressed as well as the permanent flag is not used, + so we don't need the cursor anymore. */ + else if (p->flags & GP_PAINTFLAG_USE_STABILIZER_TEMP) { + /* Reset temporal stabilizer flag and remove cursor. */ + p->flags &= ~GP_PAINTFLAG_USE_STABILIZER_TEMP; + gpencil_draw_toggle_stabilizer_cursor(C, p, false); + } } p->curtime = PIL_check_seconds_timer(); @@ -1896,12 +2291,35 @@ 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? */ - /* TODO: move cursor setting stuff to stroke-start so that paintmode can be changed midway... */ + /* TODO: move cursor setting stuff to stroke-start so that paintmode can be changed midway... + */ /* if eraser is on, draw radial aid */ if (p->paintmode == GP_PAINTMODE_ERASER) { gpencil_draw_toggle_eraser_cursor(C, p, true); } + else if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) { + if (RNA_enum_get(op->ptr, "arrowstyle_start") != GP_STROKE_ARROWSTYLE_NONE) { + p->gpd->runtime.sbuffer_sflag |= GP_STROKE_USE_ARROW_START; + p->gpd->runtime.arrow_start_style = RNA_enum_get(op->ptr, "arrowstyle_start"); + } + if (RNA_enum_get(op->ptr, "arrowstyle_end") != GP_STROKE_ARROWSTYLE_NONE) { + p->gpd->runtime.sbuffer_sflag |= GP_STROKE_USE_ARROW_END; + p->gpd->runtime.arrow_end_style = RNA_enum_get(op->ptr, "arrowstyle_end"); + } + } + else if (p->paintmode == GP_PAINTMODE_DRAW) { + p->stabilizer_factor = RNA_float_get(op->ptr, "stabilizer_factor"); + p->stabilizer_radius = RNA_int_get(op->ptr, "stabilizer_radius"); + if (RNA_boolean_get(op->ptr, "use_stabilizer")) { + p->flags |= GP_PAINTFLAG_USE_STABILIZER | GP_PAINTFLAG_USE_STABILIZER_TEMP; + gpencil_draw_toggle_stabilizer_cursor(C, p, true); + } + else if (event->shift > 0) { + p->flags |= GP_PAINTFLAG_USE_STABILIZER_TEMP; + gpencil_draw_toggle_stabilizer_cursor(C, p, true); + } + } /* set cursor * NOTE: This may change later (i.e. intentionally via brush toggle, * or unintentionally if the user scrolls outside the area)... @@ -1915,7 +2333,7 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event p->status = GP_STATUS_PAINTING; /* handle the initial drawing - i.e. for just doing a simple dot */ - annotation_draw_apply_event(op, event, CTX_data_ensure_evaluated_depsgraph(C), 0.0f, 0.0f); + annotation_draw_apply_event(C, op, event, CTX_data_ensure_evaluated_depsgraph(C), 0.0f, 0.0f); op->flag |= OP_IS_MODAL_CURSOR_REGION; } else { @@ -2007,7 +2425,7 @@ static void annotation_add_missing_events(bContext *C, interp_v2_v2v2(pt, a, b, 0.5f); sub_v2_v2v2(pt, b, pt); /* create fake event */ - annotation_draw_apply_event(op, event, depsgraph, pt[0], pt[1]); + annotation_draw_apply_event(C, op, event, depsgraph, pt[0], pt[1]); } else if (dist >= factor) { int slices = 2 + (int)((dist - 1.0) / factor); @@ -2016,7 +2434,7 @@ static void annotation_add_missing_events(bContext *C, interp_v2_v2v2(pt, a, b, n * i); sub_v2_v2v2(pt, b, pt); /* create fake event */ - annotation_draw_apply_event(op, event, depsgraph, pt[0], pt[1]); + annotation_draw_apply_event(C, op, event, depsgraph, pt[0], pt[1]); } } } @@ -2106,7 +2524,8 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) } /* toggle painting mode upon mouse-button movement - * - LEFTMOUSE = standard drawing (all) / straight line drawing (all) / polyline (toolbox only) + * - LEFTMOUSE = standard drawing (all) / straight line drawing (all) / polyline (toolbox + * only) * - RIGHTMOUSE = polyline (hotkey) / 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 @@ -2262,7 +2681,8 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) } /* TODO(sergey): Possibly evaluating dependency graph from modal operator? */ - annotation_draw_apply_event(op, event, CTX_data_ensure_evaluated_depsgraph(C), 0.0f, 0.0f); + annotation_draw_apply_event( + C, op, event, CTX_data_ensure_evaluated_depsgraph(C), 0.0f, 0.0f); /* finish painting operation if anything went wrong just now */ if (p->status == GP_STATUS_ERROR) { @@ -2370,6 +2790,19 @@ static const EnumPropertyItem prop_gpencil_drawmodes[] = { {0, NULL, 0, NULL, NULL}, }; +static const EnumPropertyItem arrow_types[] = { + {GP_STROKE_ARROWSTYLE_NONE, "NONE", 0, "None", "Don't use any arrow/style in corner"}, + {GP_STROKE_ARROWSTYLE_CLOSED, "ARROW", 0, "Arrow", "Use closed arrow style"}, + {GP_STROKE_ARROWSTYLE_OPEN, "ARROW_OPEN", 0, "Open Arrow", "Use open arrow style"}, + {GP_STROKE_ARROWSTYLE_SEGMENT, + "ARROW_OPEN_INVERTED", + 0, + "Segment", + "Use perpendicular segment style"}, + {GP_STROKE_ARROWSTYLE_SQUARE, "DIAMOND", 0, "Square", "Use square style"}, + {0, NULL, 0, NULL, NULL}, +}; + void GPENCIL_OT_annotate(wmOperatorType *ot) { PropertyRNA *prop; @@ -2393,6 +2826,37 @@ void GPENCIL_OT_annotate(wmOperatorType *ot) ot->prop = RNA_def_enum( ot->srna, "mode", prop_gpencil_drawmodes, 0, "Mode", "Way to interpret mouse movements"); + /* properties */ + prop = RNA_def_enum( + ot->srna, "arrowstyle_start", arrow_types, 0, "Start Arrow Style", "Stroke start style"); + prop = RNA_def_enum( + ot->srna, "arrowstyle_end", arrow_types, 0, "End Arrow Style", "Stroke end style"); + prop = RNA_def_boolean(ot->srna, + "use_stabilizer", + false, + "Stabilize Stroke", + "Helper to draw smooth and clean lines. Press Shift for an invert effect " + "(even if this option is not active)"); + prop = RNA_def_float(ot->srna, + "stabilizer_factor", + 0.75f, + 0.0f, + 1.0f, + "Stabilizer Stroke Factor", + "Higher values gives a smoother stroke", + 0.0f, + 1.0f); + prop = RNA_def_int(ot->srna, + "stabilizer_radius", + 35, + 0, + 200, + "Stabilizer Stroke Radius", + "Minimun distance from last point before stroke continues", + 1, + 100); + RNA_def_property_subtype(prop, PROP_PIXEL); + prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); diff --git a/source/blender/editors/gpencil/gpencil_armature.c b/source/blender/editors/gpencil/gpencil_armature.c index ab8b1a9719b..962a74d9e6f 100644 --- a/source/blender/editors/gpencil/gpencil_armature.c +++ b/source/blender/editors/gpencil/gpencil_armature.c @@ -491,7 +491,7 @@ bool ED_gpencil_add_armature(const bContext *C, ReportList *reports, Object *ob, } /* if no armature modifier, add a new one */ - GpencilModifierData *md = BKE_gpencil_modifiers_findByType(ob, eGpencilModifierType_Armature); + GpencilModifierData *md = BKE_gpencil_modifiers_findby_type(ob, eGpencilModifierType_Armature); if (md == NULL) { md = ED_object_gpencil_modifier_add( reports, bmain, scene, ob, "Armature", eGpencilModifierType_Armature); @@ -590,8 +590,8 @@ static int gpencil_generate_weights_exec(bContext *C, wmOperator *op) } else { /* get armature from modifier */ - GpencilModifierData *md = BKE_gpencil_modifiers_findByType(ob_eval, - eGpencilModifierType_Armature); + GpencilModifierData *md = BKE_gpencil_modifiers_findby_type(ob_eval, + eGpencilModifierType_Armature); if (md == NULL) { BKE_report(op->reports, RPT_ERROR, "The grease pencil object need an Armature modifier"); return OPERATOR_CANCELLED; diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c index e89903adf5f..5441d4e24a6 100644 --- a/source/blender/editors/gpencil/gpencil_convert.c +++ b/source/blender/editors/gpencil/gpencil_convert.c @@ -162,7 +162,7 @@ static void gp_strokepoint_convertcoords(bContext *C, View3D *v3d = CTX_wm_view3d(C); ARegion *region = CTX_wm_region(C); /* TODO(sergey): This function might be called from a loop, but no tagging is happening in it, - * so it's not that expensive to ensure evaluated depsgraph here. However, ideally all the + * so it's not that expensive to ensure evaluated depsgraph here. However, ideally all the * parameters are to wrapped into a context style struct and queried from Context once.*/ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Object *obact = CTX_data_active_object(C); diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index c7b46858df9..8c80334bf8a 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -55,7 +55,7 @@ #include "BKE_brush.h" #include "BKE_context.h" #include "BKE_deform.h" -#include "BKE_fcurve.h" +#include "BKE_fcurve_driver.h" #include "BKE_gpencil.h" #include "BKE_gpencil_modifier.h" #include "BKE_lib_id.h" @@ -2619,7 +2619,7 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op) /* Apply all GP modifiers before */ LISTBASE_FOREACH (GpencilModifierData *, md, &ob_iter->greasepencil_modifiers) { - const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type); + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(md->type); if (mti->bakeModifier) { mti->bakeModifier(bmain, depsgraph, md, ob_iter); } @@ -3193,7 +3193,7 @@ void GPENCIL_OT_material_unlock_all(wmOperatorType *ot) /* ***************** Select all strokes using color ************************ */ -static int gpencil_select_material_exec(bContext *C, wmOperator *op) +static int gpencil_material_select_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); Object *ob = CTX_data_active_object(C); @@ -3263,15 +3263,15 @@ static int gpencil_select_material_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -void GPENCIL_OT_select_material(wmOperatorType *ot) +void GPENCIL_OT_material_select(wmOperatorType *ot) { /* identifiers */ ot->name = "Select Material"; - ot->idname = "GPENCIL_OT_select_material"; + ot->idname = "GPENCIL_OT_material_select"; ot->description = "Select/Deselect all Grease Pencil strokes using current material"; /* callbacks */ - ot->exec = gpencil_select_material_exec; + ot->exec = gpencil_material_select_exec; ot->poll = gpencil_active_material_poll; /* flags */ @@ -3282,6 +3282,48 @@ void GPENCIL_OT_select_material(wmOperatorType *ot) RNA_def_property_flag(ot->prop, PROP_HIDDEN | PROP_SKIP_SAVE); } +/* ***************** Set active material ************************* */ +static int gpencil_material_set_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + int slot = RNA_enum_get(op->ptr, "slot"); + + /* Try to get material */ + if ((slot < 1) || (slot > ob->totcol)) { + BKE_reportf( + op->reports, RPT_ERROR, "Cannot change to non-existent material (index = %d)", slot); + return OPERATOR_CANCELLED; + } + + /* Set active material. */ + ob->actcol = slot; + + /* updates */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_SELECTED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_material_set(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Set Material"; + ot->idname = "GPENCIL_OT_material_set"; + ot->description = "Set active material"; + + /* callbacks */ + ot->exec = gpencil_material_set_exec; + ot->poll = gpencil_active_material_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* Material to use (dynamic enum) */ + ot->prop = RNA_def_enum(ot->srna, "slot", DummyRNA_DEFAULT_items, 0, "Material Slot", ""); + RNA_def_enum_funcs(ot->prop, ED_gpencil_material_enum_itemf); +} + /* ***************** Set selected stroke material the active material ************************ */ static int gpencil_set_active_material_exec(bContext *C, wmOperator *op) @@ -3344,7 +3386,7 @@ bool ED_gpencil_add_lattice_modifier(const bContext *C, } /* if no lattice modifier, add a new one */ - GpencilModifierData *md = BKE_gpencil_modifiers_findByType(ob, eGpencilModifierType_Lattice); + GpencilModifierData *md = BKE_gpencil_modifiers_findby_type(ob, eGpencilModifierType_Lattice); if (md == NULL) { md = ED_object_gpencil_modifier_add( reports, bmain, scene, ob, "Lattice", eGpencilModifierType_Lattice); diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 81596e5715c..4444396558b 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -90,8 +90,9 @@ #include "gpencil_intern.h" -/* ************************************************ */ -/* Stroke Edit Mode Management */ +/* -------------------------------------------------------------------- */ +/** \name Stroke Edit Mode Management + * \{ */ /* poll callback for all stroke editing operators */ static bool gp_stroke_edit_poll(bContext *C) @@ -138,6 +139,12 @@ static bool gpencil_editmode_toggle_poll(bContext *C) return ED_gpencil_data_get_active(C) != NULL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Toggle Edit Mode Operator + * \{ */ + static int gpencil_editmode_toggle_exec(bContext *C, wmOperator *op) { const int back = RNA_boolean_get(op->ptr, "back"); @@ -223,6 +230,12 @@ void GPENCIL_OT_editmode_toggle(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Toggle Select Mode Operator + * \{ */ + /* set select mode */ static bool gpencil_selectmode_toggle_poll(bContext *C) { @@ -298,7 +311,11 @@ void GPENCIL_OT_selectmode_toggle(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } -/* Stroke Paint Mode Management */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Toggle Stroke Paint Mode Operator + * \{ */ static bool gpencil_paintmode_toggle_poll(bContext *C) { @@ -406,7 +423,11 @@ void GPENCIL_OT_paintmode_toggle(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } -/* Stroke Sculpt Mode Management */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Toggle Stroke Sculpt Mode Operator + * \{ */ static bool gpencil_sculptmode_toggle_poll(bContext *C) { @@ -484,6 +505,12 @@ static int gpencil_sculptmode_toggle_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Toggle Weight Paint Mode Operator + * \{ */ + void GPENCIL_OT_sculptmode_toggle(wmOperatorType *ot) { PropertyRNA *prop; @@ -606,7 +633,11 @@ void GPENCIL_OT_weightmode_toggle(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } -/* Vertex Paint Mode Management */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Toggle Vertex Paint Mode Operator + * \{ */ static bool gpencil_vertexmode_toggle_poll(bContext *C) { @@ -708,10 +739,11 @@ void GPENCIL_OT_vertexmode_toggle(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } -/* ************************************************ */ -/* Stroke Editing Operators */ +/** \} */ -/* ************ Stroke Hide selection Toggle ************** */ +/* -------------------------------------------------------------------- */ +/** \name Stroke Hide Selection Toggle Operator + * \{ */ static int gpencil_hideselect_toggle_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -750,7 +782,11 @@ void GPENCIL_OT_selection_opacity_toggle(wmOperatorType *ot) ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; } -/* ************** Duplicate Selected Strokes **************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Duplicate Selected Strokes Operator + * \{ */ /* Make copies of selected point segments in a selected stroke */ static void gp_duplicate_points(const bGPDstroke *gps, @@ -927,7 +963,11 @@ void GPENCIL_OT_duplicate(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ************** Extrude Selected Strokes **************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Extrude Selected Strokes Operator + * \{ */ /* helper to copy a point to temp area */ static void copy_move_point(bGPDstroke *gps, @@ -1148,14 +1188,18 @@ void GPENCIL_OT_extrude(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ******************* Copy/Paste Strokes ************************* */ -/* Grease Pencil stroke data copy/paste buffer: +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Copy/Paste Strokes Utilities + * + * Grease Pencil stroke data copy/paste buffer: * - The copy operation collects all segments of selected strokes, * dumping "ready to be copied" copies of the strokes into the buffer. * - The paste operation makes a copy of those elements, and adds them * to the active layer. This effectively flattens down the strokes * from several different layers into a single layer. - */ + * \{ */ /* list of bGPDstroke instances */ /* NOTE: is exposed within the editors/gpencil module so that other tools can use it too */ @@ -1267,8 +1311,11 @@ GHash *gp_copybuf_validate_colormap(bContext *C) return new_colors; } -/* --------------------- */ -/* Copy selected strokes */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Copy Selected Strokes Operator + * \{ */ static int gp_strokes_copy_exec(bContext *C, wmOperator *op) { @@ -1385,8 +1432,11 @@ void GPENCIL_OT_copy(wmOperatorType *ot) // ot->flag = OPTYPE_REGISTER; } -/* --------------------- */ -/* Paste selected strokes */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Paste Selected Strokes Operator + * \{ */ static bool gp_strokes_paste_poll(bContext *C) { @@ -1557,7 +1607,11 @@ void GPENCIL_OT_paste(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } -/* ******************* Move To Layer ****************************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Move To Layer Operator + * \{ */ static int gp_move_to_layer_exec(bContext *C, wmOperator *op) { @@ -1677,7 +1731,11 @@ void GPENCIL_OT_move_to_layer(wmOperatorType *ot) RNA_def_property_flag(ot->prop, PROP_HIDDEN | PROP_SKIP_SAVE); } -/* ********************* Add Blank Frame *************************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Add Blank Frame Operator + * \{ */ static int gp_blank_frame_add_exec(bContext *C, wmOperator *op) { @@ -1751,7 +1809,11 @@ void GPENCIL_OT_blank_frame_add(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } -/* ******************* Delete Active Frame ************************ */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Delete Active Frame Operator + * \{ */ static bool gp_actframe_delete_poll(bContext *C) { @@ -1832,7 +1894,12 @@ void GPENCIL_OT_annotation_active_frame_delete(wmOperatorType *ot) ot->exec = gp_actframe_delete_exec; ot->poll = gp_annotation_actframe_delete_poll; } -/* **************** Delete All Active Frames ****************** */ + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Delete All Active Frames + * \{ */ static bool gp_actframe_delete_all_poll(bContext *C) { @@ -1893,7 +1960,11 @@ void GPENCIL_OT_active_frames_delete_all(wmOperatorType *ot) ot->poll = gp_actframe_delete_all_poll; } -/* ******************* Delete Operator ************************ */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Delete/Dissolve Utilities + * \{ */ typedef enum eGP_DeleteMode { /* delete selected stroke points */ @@ -1913,8 +1984,6 @@ typedef enum eGP_DissolveMode { GP_DISSOLVE_UNSELECT = 2, } eGP_DissolveMode; -/* ----------------------------------- */ - /* Delete selected strokes */ static int gp_delete_selected_strokes(bContext *C) { @@ -2508,7 +2577,11 @@ int gp_delete_selected_point_wrap(bContext *C) return gp_delete_selected_points(C); } -/* ----------------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Delete Operator + * \{ */ static int gp_delete_exec(bContext *C, wmOperator *op) { @@ -2567,6 +2640,12 @@ void GPENCIL_OT_delete(wmOperatorType *ot) "Method used for deleting Grease Pencil data"); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Dissolve Operator + * \{ */ + static int gp_dissolve_exec(bContext *C, wmOperator *op) { eGP_DissolveMode mode = RNA_enum_get(op->ptr, "type"); @@ -2609,7 +2688,11 @@ void GPENCIL_OT_dissolve(wmOperatorType *ot) "Method used for dissolving Stroke points"); } -/* ****************** Snapping - Strokes <-> Cursor ************************ */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Snapping Selection to Grid Operator + * \{ */ /* Poll callback for snap operators */ /* NOTE: For now, we only allow these in the 3D view, as other editors do not @@ -2624,8 +2707,6 @@ static bool gp_snap_poll(bContext *C) ((area != NULL) && (area->spacetype == SPACE_VIEW3D)); } -/* --------------------------------- */ - static int gp_snap_to_grid(bContext *C, wmOperator *UNUSED(op)) { bGPdata *gpd = ED_gpencil_data_get_active(C); @@ -2700,7 +2781,11 @@ void GPENCIL_OT_snap_to_grid(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ------------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Snapping Selection to Cursor Operator + * \{ */ static int gp_snap_to_cursor(bContext *C, wmOperator *op) { @@ -2792,7 +2877,11 @@ void GPENCIL_OT_snap_to_cursor(wmOperatorType *ot) "Offset the entire stroke instead of selected points only"); } -/* ------------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Snapping Cursor to Selection Operator + * \{ */ static int gp_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op)) { @@ -2883,7 +2972,11 @@ void GPENCIL_OT_snap_cursor_to_selected(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ******************* Apply layer thickness change to strokes ************************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Apply Layer Thickness Change to Strokes Operator + * \{ */ static int gp_stroke_apply_thickness_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -2930,7 +3023,11 @@ void GPENCIL_OT_stroke_apply_thickness(wmOperatorType *ot) ot->poll = gp_active_layer_poll; } -/* ******************* Close Strokes ************************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Stroke Toggle Cyclic Operator + * \{ */ enum { GP_STROKE_CYCLIC_CLOSE = 1, @@ -3050,7 +3147,11 @@ void GPENCIL_OT_stroke_cyclical_set(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } -/* ******************* Flat Stroke Caps ************************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Stroke Toggle Flat Caps Operator + * \{ */ enum { GP_STROKE_CAPS_TOGGLE_BOTH = 0, @@ -3146,7 +3247,11 @@ void GPENCIL_OT_stroke_caps_set(wmOperatorType *ot) ot->prop = RNA_def_enum(ot->srna, "type", toggle_type, GP_STROKE_CAPS_TOGGLE_BOTH, "Type", ""); } -/* ******************* Stroke join ************************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Stroke Join Operator + * \{ */ /* Helper: flip stroke */ static void gpencil_flip_stroke(bGPDstroke *gps) @@ -3196,8 +3301,8 @@ static void gpencil_flip_stroke(bGPDstroke *gps) /* Helper: copy point between strokes */ static void gpencil_stroke_copy_point(bGPDstroke *gps, + MDeformVert *dvert, bGPDspoint *point, - int idx, const float delta[3], float pressure, float strength, @@ -3209,6 +3314,13 @@ static void gpencil_stroke_copy_point(bGPDstroke *gps, if (gps->dvert != NULL) { gps->dvert = MEM_reallocN(gps->dvert, sizeof(MDeformVert) * (gps->totpoints + 1)); } + else { + /* If destination has weight add weight to origin. */ + if (dvert != NULL) { + gps->dvert = MEM_callocN(sizeof(MDeformVert) * (gps->totpoints + 1), __func__); + } + } + gps->totpoints++; newpoint = &gps->points[gps->totpoints - 1]; @@ -3222,11 +3334,16 @@ static void gpencil_stroke_copy_point(bGPDstroke *gps, copy_v4_v4(newpoint->vert_color, point->vert_color); if (gps->dvert != NULL) { - MDeformVert *dvert = &gps->dvert[idx]; MDeformVert *newdvert = &gps->dvert[gps->totpoints - 1]; - newdvert->totweight = dvert->totweight; - newdvert->dw = MEM_dupallocN(dvert->dw); + if (dvert != NULL) { + newdvert->totweight = dvert->totweight; + newdvert->dw = MEM_dupallocN(dvert->dw); + } + else { + newdvert->totweight = 0; + newdvert->dw = NULL; + } } } @@ -3277,16 +3394,18 @@ static void gpencil_stroke_join_strokes(bGPDstroke *gps_a, /* 1st: add one tail point to start invisible area */ point = gps_a->points[gps_a->totpoints - 1]; deltatime = point.time; - gpencil_stroke_copy_point(gps_a, &point, gps_a->totpoints - 1, delta, 0.0f, 0.0f, 0.0f); + + gpencil_stroke_copy_point(gps_a, NULL, &point, delta, 0.0f, 0.0f, 0.0f); /* 2nd: add one head point to finish invisible area */ point = gps_b->points[0]; - gpencil_stroke_copy_point(gps_a, &point, 0, delta, 0.0f, 0.0f, deltatime); + gpencil_stroke_copy_point(gps_a, NULL, &point, delta, 0.0f, 0.0f, deltatime); } /* 3rd: add all points */ for (i = 0, pt = gps_b->points; i < gps_b->totpoints && pt; i++, pt++) { - gpencil_stroke_copy_point(gps_a, pt, i, delta, pt->pressure, pt->strength, deltatime); + MDeformVert *dvert = (gps_b->dvert) ? &gps_b->dvert[i] : NULL; + gpencil_stroke_copy_point(gps_a, dvert, pt, delta, pt->pressure, pt->strength, deltatime); } } @@ -3432,7 +3551,11 @@ void GPENCIL_OT_stroke_join(wmOperatorType *ot) "Leave gaps between joined strokes instead of linking them"); } -/* ******************* Stroke flip ************************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Stroke Flip Operator + * \{ */ static int gp_stroke_flip_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -3491,7 +3614,11 @@ void GPENCIL_OT_stroke_flip(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ***************** Reproject Strokes ********************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Stroke Re-project Operator + * \{ */ typedef enum eGP_ReprojectModes { /* Axis */ @@ -3729,7 +3856,6 @@ static int gp_recalc_geometry_exec(bContext *C, wmOperator *UNUSED(op)) void GPENCIL_OT_recalc_geometry(wmOperatorType *ot) { - /* identifiers */ ot->name = "Recalculate internal geometry"; ot->idname = "GPENCIL_OT_recalc_geometry"; @@ -3743,7 +3869,12 @@ void GPENCIL_OT_recalc_geometry(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ******************* Stroke subdivide ************************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Stroke Subdivide Operator + * \{ */ + /* helper to smooth */ static void gp_smooth_stroke(bContext *C, wmOperator *op) { @@ -4124,7 +4255,12 @@ void GPENCIL_OT_stroke_sample(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } -/* ******************* Stroke trim ************************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Stroke Trim Operator + * \{ */ + static int gp_stroke_trim_exec(bContext *C, wmOperator *UNUSED(op)) { bGPdata *gpd = ED_gpencil_data_get_active(C); @@ -4189,7 +4325,12 @@ void GPENCIL_OT_stroke_trim(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ***************** Separate Strokes ********************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Stroke Separate Operator + * \{ */ + typedef enum eGP_SeparateModes { /* Points */ GP_SEPARATE_POINT = 0, @@ -4407,7 +4548,12 @@ void GPENCIL_OT_stroke_separate(wmOperatorType *ot) ot->prop = RNA_def_enum(ot->srna, "mode", separate_type, GP_SEPARATE_POINT, "Mode", ""); } -/* ***************** Split Strokes ********************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Stroke Split Operator + * \{ */ + static int gp_stroke_split_exec(bContext *C, wmOperator *UNUSED(op)) { Object *ob = CTX_data_active_object(C); @@ -4504,6 +4650,12 @@ void GPENCIL_OT_stroke_split(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Stroke Smooth Operator + * \{ */ + static int gp_stroke_smooth_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); @@ -4554,11 +4706,17 @@ void GPENCIL_OT_stroke_smooth(wmOperatorType *ot) RNA_def_boolean(ot->srna, "smooth_uv", false, "UV", ""); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Stroke Cutter Operator + * \{ */ + /* smart stroke cutter for trimming stroke ends */ struct GP_SelectLassoUserData { rcti rect; - const int (*mcords)[2]; - int mcords_len; + const int (*mcoords)[2]; + int mcoords_len; }; static bool gpencil_test_lasso(bGPDstroke *gps, @@ -4574,7 +4732,7 @@ static bool gpencil_test_lasso(bGPDstroke *gps, gp_point_to_xy(gsc, gps, &pt2, &x0, &y0); /* test if in lasso */ return ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(&data->rect, x0, y0) && - BLI_lasso_is_point_inside(data->mcords, data->mcords_len, x0, y0, INT_MAX)); + BLI_lasso_is_point_inside(data->mcoords, data->mcoords_len, x0, y0, INT_MAX)); } typedef bool (*GPencilTestFn)(bGPDstroke *gps, @@ -4752,19 +4910,19 @@ static int gpencil_cutter_exec(bContext *C, wmOperator *op) } struct GP_SelectLassoUserData data = {0}; - data.mcords = WM_gesture_lasso_path_to_array(C, op, &data.mcords_len); + data.mcoords = WM_gesture_lasso_path_to_array(C, op, &data.mcoords_len); /* Sanity check. */ - if (data.mcords == NULL) { + if (data.mcoords == NULL) { return OPERATOR_PASS_THROUGH; } /* Compute boundbox of lasso (for faster testing later). */ - BLI_lasso_boundbox(&data.rect, data.mcords, data.mcords_len); + BLI_lasso_boundbox(&data.rect, data.mcoords, data.mcoords_len); gpencil_cutter_lasso_select(C, op, gpencil_test_lasso, &data); - MEM_freeN((void *)data.mcords); + MEM_freeN((void *)data.mcoords); return OPERATOR_FINISHED; } @@ -4810,7 +4968,12 @@ bool ED_object_gpencil_exit(struct Main *bmain, Object *ob) return ok; } -/* ** merge by distance *** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Merge By Distance Operator + * \{ */ + static bool gp_merge_by_distance_poll(bContext *C) { Object *ob = CTX_data_active_object(C); @@ -4879,3 +5042,5 @@ void GPENCIL_OT_stroke_merge_by_distance(wmOperatorType *ot) ot->srna, "use_unselected", 0, "Unselected", "Use whole stroke, not only selected points"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); } + +/** \} */ diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index 7e753726564..d23a914fc49 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -1149,7 +1149,7 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf) pt->time = 0.0f; /* Apply the vertex color to point. */ - ED_gpencil_point_vertex_color_set(ts, brush, pt); + ED_gpencil_point_vertex_color_set(ts, brush, pt, NULL); if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) { MDeformWeight *dw = BKE_defvert_ensure_index(dvert, def_nr); diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index c5e5a0b79ef..a98ccb1cba6 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -68,6 +68,17 @@ struct PropertyRNA; /* Internal Operator-State Data ------------------------ */ +/** Random settings by stroke */ +typedef struct GpRandomSettings { + /** Pressure used for evaluated curves. */ + float pen_press; + + float hsv[3]; + float pressure; + float strength; + float uv; +} GpRandomSettings; + /* Temporary draw data (no draw manager mode) */ typedef struct tGPDdraw { struct RegionView3D *rv3d; /* region to draw */ @@ -230,6 +241,10 @@ typedef struct tGPDprimitive { /** size in pixels for uv calculation */ float totpixlen; + + /** Random settings by stroke */ + GpRandomSettings random_settings; + } tGPDprimitive; /* Modal Operator Drawing Callbacks ------------------------ */ @@ -345,6 +360,10 @@ const struct EnumPropertyItem *ED_gpencil_layers_with_new_enum_itemf(struct bCon struct PointerRNA *ptr, struct PropertyRNA *prop, bool *r_free); +const struct EnumPropertyItem *ED_gpencil_material_enum_itemf(struct bContext *C, + struct PointerRNA *ptr, + struct PropertyRNA *prop, + bool *r_free); /* ***************************************************** */ /* Operator Defines */ @@ -550,7 +569,8 @@ void GPENCIL_OT_material_reveal(struct wmOperatorType *ot); void GPENCIL_OT_material_lock_all(struct wmOperatorType *ot); void GPENCIL_OT_material_unlock_all(struct wmOperatorType *ot); void GPENCIL_OT_material_lock_unused(struct wmOperatorType *ot); -void GPENCIL_OT_select_material(struct wmOperatorType *ot); +void GPENCIL_OT_material_select(struct wmOperatorType *ot); +void GPENCIL_OT_material_set(struct wmOperatorType *ot); void GPENCIL_OT_set_active_material(struct wmOperatorType *ot); /* convert old 2.7 files to 2.8 */ diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c index 0171a81f5eb..94c86572fd3 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -653,7 +653,8 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_material_reveal); WM_operatortype_append(GPENCIL_OT_material_lock_all); WM_operatortype_append(GPENCIL_OT_material_unlock_all); - WM_operatortype_append(GPENCIL_OT_select_material); + WM_operatortype_append(GPENCIL_OT_material_select); + WM_operatortype_append(GPENCIL_OT_material_set); /* Editing (Time) --------------- */ diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 139e52254f4..309329e4649 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -255,6 +255,10 @@ typedef struct tGPsdata { tGPguide guide; ReportList *reports; + + /** Random settings by stroke */ + GpRandomSettings random_settings; + } tGPsdata; /* ------ */ @@ -687,6 +691,78 @@ static void gp_smooth_segment(bGPdata *gpd, const float inf, int from_idx, int t } } +static void gp_apply_randomness(tGPsdata *p, + BrushGpencilSettings *brush_settings, + tGPspoint *pt, + const bool press, + const bool strength, + const bool uv) +{ + bGPdata *gpd = p->gpd; + GpRandomSettings random_settings = p->random_settings; + float value = 0.0f; + /* Apply randomness to pressure. */ + if ((brush_settings->draw_random_press > 0.0f) && (press)) { + if ((brush_settings->flag2 & GP_BRUSH_USE_PRESS_AT_STROKE) == 0) { + float rand = BLI_rng_get_float(p->rng) * 2.0f - 1.0f; + value = 1.0 + rand * 2.0 * brush_settings->draw_random_press; + } + else { + value = 1.0 + random_settings.pressure * brush_settings->draw_random_press; + } + + /* Apply random curve. */ + if (brush_settings->flag2 & GP_BRUSH_USE_PRESSURE_RAND_PRESS) { + value *= BKE_curvemapping_evaluateF( + brush_settings->curve_rand_pressure, 0, random_settings.pen_press); + } + + pt->pressure *= value; + CLAMP(pt->pressure, 0.1f, 1.0f); + } + + /* Apply randomness to color strength. */ + if ((brush_settings->draw_random_strength) && (strength)) { + if ((brush_settings->flag2 & GP_BRUSH_USE_STRENGTH_AT_STROKE) == 0) { + float rand = BLI_rng_get_float(p->rng) * 2.0f - 1.0f; + value = 1.0 + rand * brush_settings->draw_random_strength; + } + else { + value = 1.0 + random_settings.strength * brush_settings->draw_random_strength; + } + + /* Apply random curve. */ + if (brush_settings->flag2 & GP_BRUSH_USE_STRENGTH_RAND_PRESS) { + value *= BKE_curvemapping_evaluateF( + brush_settings->curve_rand_pressure, 0, random_settings.pen_press); + } + + pt->strength *= value; + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); + } + + /* Apply randomness to uv texture rotation. */ + if ((brush_settings->uv_random > 0.0f) && (uv)) { + if ((brush_settings->flag2 & GP_BRUSH_USE_UV_AT_STROKE) == 0) { + float rand = BLI_hash_int_01(BLI_hash_int_2d((int)pt->x, gpd->runtime.sbuffer_used)) * 2.0f - + 1.0f; + value = rand * M_PI_2 * brush_settings->uv_random; + } + else { + value = random_settings.uv * M_PI_2 * brush_settings->uv_random; + } + + /* Apply random curve. */ + if (brush_settings->flag2 & GP_BRUSH_USE_UV_RAND_PRESS) { + value *= BKE_curvemapping_evaluateF( + brush_settings->curve_rand_uv, 0, random_settings.pen_press); + } + + pt->uv_rot += value; + CLAMP(pt->uv_rot, -M_PI_2, M_PI_2); + } +} + /* 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) { @@ -744,10 +820,6 @@ static short gp_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure 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); @@ -768,6 +840,15 @@ static short gp_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); } + /* Set vertex colors for buffer. */ + ED_gpencil_sbuffer_vertex_color_set(p->depsgraph, + p->ob, + p->scene->toolsettings, + p->brush, + p->material, + p->random_settings.hsv, + p->random_settings.pen_press); + if (brush_settings->flag & GP_BRUSH_GROUP_RANDOM) { /* Apply jitter to position */ if (brush_settings->draw_jitter > 0.0f) { @@ -781,26 +862,9 @@ static short gp_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure const float fac = rand * square_f(exp_factor) * jitpress; gp_brush_jitter(gpd, pt, fac); } - /* 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); - } - /* apply randomness to uv texture rotation */ - if (brush_settings->uv_random > 0.0f) { - float rand = BLI_hash_int_01(BLI_hash_int_2d((int)pt->x, gpd->runtime.sbuffer_used)) * - 2.0f - - 1.0f; - pt->uv_rot += rand * M_PI_2 * brush_settings->uv_random; - CLAMP(pt->uv_rot, -M_PI_2, M_PI_2); - } - /* 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); - } + + /* Apply other randomness. */ + gp_apply_randomness(p, brush_settings, pt, true, true, true); } /* apply angle of stroke to brush size */ @@ -959,9 +1023,10 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) pt->pressure = ptc->pressure; pt->strength = ptc->strength; CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); + copy_v4_v4(pt->vert_color, ptc->vert_color); pt->time = ptc->time; /* Apply the vertex color to point. */ - ED_gpencil_point_vertex_color_set(ts, brush, pt); + ED_gpencil_point_vertex_color_set(ts, brush, pt, ptc); pt++; @@ -994,7 +1059,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) 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); + ED_gpencil_point_vertex_color_set(ts, brush, pt, ptc); if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) { BKE_gpencil_dvert_ensure(gps); @@ -1113,11 +1178,12 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) pt->pressure = ptc->pressure; pt->strength = ptc->strength; CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); + copy_v4_v4(pt->vert_color, ptc->vert_color); 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); + ED_gpencil_point_vertex_color_set(ts, brush, pt, ptc); if (dvert != NULL) { dvert->totweight = 0; @@ -1751,10 +1817,16 @@ static void gp_init_drawing_brush(bContext *C, tGPsdata *p) BKE_brush_gpencil_paint_presets(bmain, ts, true); changed = true; } - /* be sure curves are initializated */ + /* Be sure curves are initializated. */ BKE_curvemapping_initialize(paint->brush->gpencil_settings->curve_sensitivity); BKE_curvemapping_initialize(paint->brush->gpencil_settings->curve_strength); BKE_curvemapping_initialize(paint->brush->gpencil_settings->curve_jitter); + BKE_curvemapping_initialize(paint->brush->gpencil_settings->curve_rand_pressure); + BKE_curvemapping_initialize(paint->brush->gpencil_settings->curve_rand_strength); + BKE_curvemapping_initialize(paint->brush->gpencil_settings->curve_rand_uv); + BKE_curvemapping_initialize(paint->brush->gpencil_settings->curve_rand_hue); + BKE_curvemapping_initialize(paint->brush->gpencil_settings->curve_rand_saturation); + BKE_curvemapping_initialize(paint->brush->gpencil_settings->curve_rand_value); /* assign to temp tGPsdata */ p->brush = paint->brush; @@ -2700,6 +2772,8 @@ static void gpencil_draw_apply_event(bContext *C, /* handle pressure sensitivity (which is supplied by tablets or otherwise 1.0) */ p->pressure = event->tablet.pressure; + /* By default use pen pressure for random curves but attenuated. */ + p->random_settings.pen_press = pow(p->pressure, 3.0f); /* Hack for pressure sensitive eraser on D+RMB when using a tablet: * The pen has to float over the tablet surface, resulting in @@ -3052,6 +3126,8 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event else { p = op->customdata; } + /* Init random settings. */ + ED_gpencil_init_random_settings(p->brush, event->mval, &p->random_settings); /* TODO: set any additional settings that we can take from the events? * if eraser is on, draw radial aid */ @@ -3175,10 +3251,17 @@ static void gp_brush_angle_segment(tGPsdata *p, tGPspoint *pt_prev, tGPspoint *p static void gpencil_add_arc_points(tGPsdata *p, float mval[2], int segments) { bGPdata *gpd = p->gpd; + BrushGpencilSettings *brush_settings = p->brush->gpencil_settings; + if (gpd->runtime.sbuffer_used < 3) { + tGPspoint *points = (tGPspoint *)gpd->runtime.sbuffer; + /* Apply other randomness to first points. */ + for (int i = 0; i < gpd->runtime.sbuffer_used; i++) { + tGPspoint *pt = &points[i]; + gp_apply_randomness(p, brush_settings, pt, false, false, true); + } return; } - BrushGpencilSettings *brush_settings = p->brush->gpencil_settings; int idx_prev = gpd->runtime.sbuffer_used; /* Add space for new arc points. */ @@ -3233,6 +3316,7 @@ static void gpencil_add_arc_points(tGPsdata *p, float mval[2], int segments) corner[0] = midpoint[0] - (cp1[0] - midpoint[0]); corner[1] = midpoint[1] - (cp1[1] - midpoint[1]); + float stepcolor = 1.0f / segments; tGPspoint *pt_step = pt_prev; for (int i = 0; i < segments; i++) { @@ -3243,6 +3327,9 @@ static void gpencil_add_arc_points(tGPsdata *p, float mval[2], int segments) /* Set pressure and strength equals to previous. It will be smoothed later. */ pt->pressure = pt_prev->pressure; pt->strength = pt_prev->strength; + /* Interpolate vertex color. */ + interp_v4_v4v4( + pt->vert_color, pt_before->vert_color, pt_prev->vert_color, stepcolor * (i + 1)); /* Apply angle of stroke to brush size to interpolated points but slightly attenuated.. */ if (brush_settings->draw_angle_factor != 0.0f) { @@ -3252,26 +3339,8 @@ static void gpencil_add_arc_points(tGPsdata *p, float mval[2], int segments) pt_step = pt; } - /* 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); - } - /* 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); - } - /* Apply randomness to uv texture rotation. */ - if (brush_settings->uv_random > 0.0f) { - float rand = BLI_hash_int_01(BLI_hash_int_2d((int)pt->x, gpd->runtime.sbuffer_used + i)) * - 2.0f - - 1.0f; - pt->uv_rot += rand * M_PI_2 * brush_settings->uv_random; - CLAMP(pt->uv_rot, -M_PI_2, M_PI_2); - } + /* Apply other randomness. */ + gp_apply_randomness(p, brush_settings, pt, false, false, true); a += step; } @@ -3323,6 +3392,7 @@ static void gpencil_add_guide_points(const tGPsdata *p, /* Set pressure and strength equals to previous. It will be smoothed later. */ pt->pressure = pt_before->pressure; pt->strength = pt_before->strength; + copy_v4_v4(pt->vert_color, pt_before->vert_color); } } else { @@ -3339,6 +3409,7 @@ static void gpencil_add_guide_points(const tGPsdata *p, /* Set pressure and strength equals to previous. It will be smoothed later. */ pt->pressure = pt_before->pressure; pt->strength = pt_before->strength; + copy_v4_v4(pt->vert_color, pt_before->vert_color); } } } diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index 9e97a936be8..875a6265497 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -124,8 +124,13 @@ static void gp_session_validatebuffer(tGPDprimitive *p) 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); + ED_gpencil_sbuffer_vertex_color_set(p->depsgraph, + p->ob, + p->scene->toolsettings, + p->brush, + p->material, + p->random_settings.hsv, + 1.0f); if (ELEM(p->type, GP_STROKE_BOX, GP_STROKE_CIRCLE)) { gpd->runtime.sbuffer_sflag |= GP_STROKE_CYCLIC; @@ -681,6 +686,8 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) ToolSettings *ts = tgpi->scene->toolsettings; bGPdata *gpd = tgpi->gpd; Brush *brush = tgpi->brush; + BrushGpencilSettings *brush_settings = brush->gpencil_settings; + GpRandomSettings random_settings = tgpi->random_settings; bGPDstroke *gps = tgpi->gpf->strokes.first; GP_Sculpt_Settings *gset = &ts->gp_sculpt; int depth_margin = (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 4 : 0; @@ -735,11 +742,11 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) if (gset->flag & GP_SCULPT_SETT_FLAG_PRIMITIVE_CURVE) { BKE_curvemapping_initialize(ts->gp_sculpt.cur_primitive); } - if (tgpi->brush->gpencil_settings->flag & GP_BRUSH_USE_JITTER_PRESSURE) { - BKE_curvemapping_initialize(tgpi->brush->gpencil_settings->curve_jitter); + if (brush_settings->flag & GP_BRUSH_USE_JITTER_PRESSURE) { + BKE_curvemapping_initialize(brush_settings->curve_jitter); } - if (tgpi->brush->gpencil_settings->flag & GP_BRUSH_USE_STENGTH_PRESSURE) { - BKE_curvemapping_initialize(tgpi->brush->gpencil_settings->curve_strength); + if (brush_settings->flag & GP_BRUSH_USE_STENGTH_PRESSURE) { + BKE_curvemapping_initialize(brush_settings->curve_strength); } /* get an array of depths, far depths are blended */ @@ -841,10 +848,9 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) tGPspoint *p2d = &points2D[i]; /* set rnd value for reuse */ - if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && (p2d->rnd_dirty != true)) { + if ((brush_settings->flag & GP_BRUSH_GROUP_RANDOM) && (p2d->rnd_dirty != true)) { p2d->rnd[0] = BLI_rng_get_float(tgpi->rng); p2d->rnd[1] = BLI_rng_get_float(tgpi->rng); - p2d->rnd[2] = BLI_rng_get_float(tgpi->rng); p2d->rnd_dirty = true; } @@ -858,7 +864,7 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) /* calc pressure */ float curve_pressure = 1.0; float pressure = 1.0; - float strength = brush->gpencil_settings->draw_strength; + float strength = brush_settings->draw_strength; /* normalize value to evaluate curve */ if (gset->flag & GP_SCULPT_SETT_FLAG_PRIMITIVE_CURVE) { @@ -868,20 +874,18 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) } /* apply jitter to position */ - if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && - (brush->gpencil_settings->draw_jitter > 0.0f)) { + if ((brush_settings->flag & GP_BRUSH_GROUP_RANDOM) && (brush_settings->draw_jitter > 0.0f)) { float jitter; - if (brush->gpencil_settings->flag & GP_BRUSH_USE_JITTER_PRESSURE) { - jitter = BKE_curvemapping_evaluateF( - brush->gpencil_settings->curve_jitter, 0, curve_pressure); + if (brush_settings->flag & GP_BRUSH_USE_JITTER_PRESSURE) { + jitter = BKE_curvemapping_evaluateF(brush_settings->curve_jitter, 0, curve_pressure); } else { - jitter = brush->gpencil_settings->draw_jitter; + jitter = brush_settings->draw_jitter; } /* exponential value */ - const float exfactor = square_f(brush->gpencil_settings->draw_jitter + 2.0f); + const float exfactor = square_f(brush_settings->draw_jitter + 2.0f); const float fac = p2d->rnd[0] * exfactor * jitter; /* vector */ @@ -906,47 +910,68 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) add_v2_v2(&p2d->x, svec); } - /* apply randomness to pressure */ - 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 * 2.0f) * p2d->rnd[1]; - } - else { - pressure += (brush->gpencil_settings->draw_random_press * 2.0f) * p2d->rnd[2]; - } - } - /* color strength */ - if (brush->gpencil_settings->flag & GP_BRUSH_USE_STENGTH_PRESSURE) { - float curvef = BKE_curvemapping_evaluateF( - brush->gpencil_settings->curve_strength, 0, curve_pressure); + if (brush_settings->flag & GP_BRUSH_USE_STENGTH_PRESSURE) { + float curvef = BKE_curvemapping_evaluateF(brush_settings->curve_strength, 0, curve_pressure); strength *= curvef; - strength *= brush->gpencil_settings->draw_strength; + strength *= brush_settings->draw_strength; } CLAMP(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 (p2d->rnd[2] > 0.5f) { - strength -= strength * brush->gpencil_settings->draw_random_strength * p2d->rnd[0]; + if (brush_settings->flag & GP_BRUSH_GROUP_RANDOM) { + /* Apply randomness to pressure. */ + if (brush_settings->draw_random_press > 0.0f) { + if ((brush_settings->flag2 & GP_BRUSH_USE_PRESS_AT_STROKE) == 0) { + float rand = BLI_rng_get_float(tgpi->rng) * 2.0f - 1.0f; + pressure *= 1.0 + rand * 2.0 * brush_settings->draw_random_press; + } + else { + pressure *= 1.0 + random_settings.pressure * brush_settings->draw_random_press; + } + + /* Apply random curve. */ + if (brush_settings->flag2 & GP_BRUSH_USE_PRESSURE_RAND_PRESS) { + pressure *= BKE_curvemapping_evaluateF(brush_settings->curve_rand_pressure, 0, pressure); + } + + CLAMP(pressure, 0.1f, 1.0f); } - else { - strength += strength * brush->gpencil_settings->draw_random_strength * p2d->rnd[1]; + + /* Apply randomness to color strength. */ + if (brush_settings->draw_random_strength) { + if ((brush_settings->flag2 & GP_BRUSH_USE_STRENGTH_AT_STROKE) == 0) { + float rand = BLI_rng_get_float(tgpi->rng) * 2.0f - 1.0f; + strength *= 1.0 + rand * brush_settings->draw_random_strength; + } + else { + strength *= 1.0 + random_settings.strength * brush_settings->draw_random_strength; + } + + /* Apply random curve. */ + if (brush_settings->flag2 & GP_BRUSH_USE_STRENGTH_RAND_PRESS) { + strength *= BKE_curvemapping_evaluateF(brush_settings->curve_rand_strength, 0, pressure); + } + + CLAMP(strength, GPENCIL_STRENGTH_MIN, 1.0f); } - CLAMP(strength, GPENCIL_STRENGTH_MIN, 1.0f); } copy_v2_v2(&tpt->x, &p2d->x); - CLAMP_MIN(pressure, 0.1f); - tpt->pressure = pressure; tpt->strength = strength; tpt->time = p2d->time; + /* Set vertex colors for buffer. */ + ED_gpencil_sbuffer_vertex_color_set(tgpi->depsgraph, + tgpi->ob, + tgpi->scene->toolsettings, + tgpi->brush, + tgpi->material, + tgpi->random_settings.hsv, + strength); + /* point uv */ if (gpd->runtime.sbuffer_used > 0) { tGPspoint *tptb = (tGPspoint *)gpd->runtime.sbuffer + gpd->runtime.sbuffer_used - 1; @@ -994,8 +1019,7 @@ 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); + copy_v4_v4(pt->vert_color, tpt->vert_color); if (gps->dvert != NULL) { MDeformVert *dvert = &gps->dvert[i]; @@ -1159,6 +1183,7 @@ static void gpencil_primitive_init(bContext *C, wmOperator *op) /* Set Draw brush. */ Brush *brush = BKE_paint_toolslots_brush_get(paint, 0); + BKE_brush_tool_set(brush, paint, 0); BKE_paint_brush_set(paint, brush); tgpi->brush = brush; @@ -1226,6 +1251,9 @@ static int gpencil_primitive_invoke(bContext *C, wmOperator *op, const wmEvent * gpencil_primitive_init(C, op); tgpi = op->customdata; + /* Init random settings. */ + ED_gpencil_init_random_settings(tgpi->brush, event->mval, &tgpi->random_settings); + const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input"); if (!is_modal) { tgpi->flag = IN_PROGRESS; @@ -1262,6 +1290,7 @@ static void gpencil_primitive_interaction_end(bContext *C, ToolSettings *ts = tgpi->scene->toolsettings; Brush *brush = tgpi->brush; + BrushGpencilSettings *brush_settings = brush->gpencil_settings; const int def_nr = tgpi->ob->actdef - 1; const bool have_weight = (bool)BLI_findlink(&tgpi->ob->defbase, def_nr); @@ -1285,8 +1314,8 @@ static void gpencil_primitive_interaction_end(bContext *C, gps = tgpi->gpf->strokes.first; if (gps) { gps->thickness = brush->size; - gps->hardeness = brush->gpencil_settings->hardeness; - copy_v2_v2(gps->aspect_ratio, brush->gpencil_settings->aspect_ratio); + gps->hardeness = brush_settings->hardeness; + copy_v2_v2(gps->aspect_ratio, brush_settings->aspect_ratio); /* Calc geometry data. */ BKE_gpencil_stroke_geometry_update(gps); @@ -1449,23 +1478,25 @@ static void gpencil_primitive_edit_event_handling( static void gpencil_primitive_strength(tGPDprimitive *tgpi, bool reset) { Brush *brush = tgpi->brush; + BrushGpencilSettings *brush_settings = brush->gpencil_settings; + if (brush) { if (reset) { - brush->gpencil_settings->draw_strength = tgpi->brush_strength; + brush_settings->draw_strength = tgpi->brush_strength; tgpi->brush_strength = 0.0f; } else { if (tgpi->brush_strength == 0.0f) { - tgpi->brush_strength = brush->gpencil_settings->draw_strength; + tgpi->brush_strength = brush_settings->draw_strength; } float move[2]; sub_v2_v2v2(move, tgpi->mval, tgpi->mvalo); float adjust = (move[1] > 0.0f) ? 0.01f : -0.01f; - brush->gpencil_settings->draw_strength += adjust * fabsf(len_manhattan_v2(move)); + brush_settings->draw_strength += adjust * fabsf(len_manhattan_v2(move)); } /* limit low limit because below 0.2f the stroke is invisible */ - CLAMP(brush->gpencil_settings->draw_strength, 0.2f, 1.0f); + CLAMP(brush_settings->draw_strength, 0.2f, 1.0f); } } diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index e25576f32aa..69d22b52ded 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -1327,8 +1327,8 @@ void GPENCIL_OT_select_box(wmOperatorType *ot) struct GP_SelectLassoUserData { rcti rect; - const int (*mcords)[2]; - int mcords_len; + const int (*mcoords)[2]; + int mcoords_len; }; static bool gpencil_test_lasso(bGPDstroke *gps, @@ -1344,25 +1344,25 @@ static bool gpencil_test_lasso(bGPDstroke *gps, gp_point_to_xy(gsc, gps, &pt2, &x0, &y0); /* test if in lasso boundbox + within the lasso noose */ return ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(&data->rect, x0, y0) && - BLI_lasso_is_point_inside(data->mcords, data->mcords_len, x0, y0, INT_MAX)); + BLI_lasso_is_point_inside(data->mcoords, data->mcoords_len, x0, y0, INT_MAX)); } static int gpencil_lasso_select_exec(bContext *C, wmOperator *op) { struct GP_SelectLassoUserData data = {0}; - data.mcords = WM_gesture_lasso_path_to_array(C, op, &data.mcords_len); + data.mcoords = WM_gesture_lasso_path_to_array(C, op, &data.mcoords_len); /* Sanity check. */ - if (data.mcords == NULL) { + if (data.mcoords == NULL) { return OPERATOR_PASS_THROUGH; } /* Compute boundbox of lasso (for faster testing later). */ - BLI_lasso_boundbox(&data.rect, data.mcords, data.mcords_len); + BLI_lasso_boundbox(&data.rect, data.mcoords, data.mcoords_len); int ret = gpencil_generic_select_exec(C, op, gpencil_test_lasso, &data); - MEM_freeN((void *)data.mcords); + MEM_freeN((void *)data.mcoords); return ret; } diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index c1843b1c138..3cab26eab44 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -30,11 +30,14 @@ #include "BLI_blenlib.h" #include "BLI_ghash.h" +#include "BLI_hash.h" #include "BLI_math.h" #include "BLI_rand.h" #include "BLI_utildefines.h" #include "BLT_translation.h" +#include "PIL_time.h" + #include "DNA_brush_types.h" #include "DNA_gpencil_types.h" #include "DNA_meshdata_types.h" @@ -74,6 +77,7 @@ #include "ED_object.h" #include "ED_screen.h" #include "ED_select_utils.h" +#include "ED_transform_snap_object_context.h" #include "ED_view3d.h" #include "GPU_immediate.h" @@ -481,6 +485,40 @@ const EnumPropertyItem *ED_gpencil_layers_with_new_enum_itemf(bContext *C, return item; } +/* Just existing Materials */ +const EnumPropertyItem *ED_gpencil_material_enum_itemf(bContext *C, + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + bool *r_free) +{ + Object *ob = CTX_data_active_object(C); + EnumPropertyItem *item = NULL, item_tmp = {0}; + int totitem = 0; + int i = 0; + + if (ELEM(NULL, C, ob)) { + return DummyRNA_DEFAULT_items; + } + + /* Existing materials */ + for (i = 1; i <= ob->totcol; i++) { + Material *ma = BKE_object_material_get(ob, i); + if (ma) { + item_tmp.identifier = ma->id.name + 2; + item_tmp.name = ma->id.name + 2; + item_tmp.value = i; + item_tmp.icon = ma->preview ? ma->preview->icon_id : ICON_NONE; + + RNA_enum_item_add(&item, &totitem, &item_tmp); + } + } + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + + return item; +} + /* ******************************************************** */ /* Brush Tool Core */ @@ -670,8 +708,8 @@ void gp_apply_parent_point(Depsgraph *depsgraph, Object *obact, bGPDlayer *gpl, /** * Convert a Grease Pencil coordinate (i.e. can be 2D or 3D) to screenspace (2D) * - * \param[out] r_x The screen-space x-coordinate of the point - * \param[out] r_y The screen-space y-coordinate of the point + * \param[out] r_x: The screen-space x-coordinate of the point + * \param[out] r_y: The screen-space y-coordinate of the point * * \warning This assumes that the caller has already checked * whether the stroke in question can be drawn. @@ -2525,26 +2563,170 @@ void ED_gpencil_fill_vertex_color_set(ToolSettings *ts, Brush *brush, bGPDstroke } } -void ED_gpencil_point_vertex_color_set(ToolSettings *ts, Brush *brush, bGPDspoint *pt) +void ED_gpencil_point_vertex_color_set(ToolSettings *ts, + Brush *brush, + bGPDspoint *pt, + tGPspoint *tpt) { 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); + if (tpt == NULL) { + 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 { + copy_v3_v3(pt->vert_color, tpt->vert_color); + pt->vert_color[3] = brush->gpencil_settings->vertex_factor; + } } else { zero_v4(pt->vert_color); } } -void ED_gpencil_sbuffer_vertex_color_set( - Depsgraph *depsgraph, Object *ob, ToolSettings *ts, Brush *brush, Material *material) +void ED_gpencil_init_random_settings(Brush *brush, + const int mval[2], + GpRandomSettings *random_settings) +{ + int seed = ((uint)(ceil(PIL_check_seconds_timer())) + 1) % 128; + /* Use mouse position to get randomness. */ + int ix = mval[0] * seed; + int iy = mval[1] * seed; + int iz = ix + iy * seed; + zero_v3(random_settings->hsv); + + BrushGpencilSettings *brush_settings = brush->gpencil_settings; + /* Random to Hue. */ + if (brush_settings->random_hue > 0.0f) { + float rand = BLI_hash_int_01(BLI_hash_int_2d(ix, iy)) * 2.0f - 1.0f; + random_settings->hsv[0] = rand * brush_settings->random_hue * 0.5f; + } + /* Random to Saturation. */ + if (brush_settings->random_saturation > 0.0f) { + float rand = BLI_hash_int_01(BLI_hash_int_2d(iy, ix)) * 2.0f - 1.0f; + random_settings->hsv[1] = rand * brush_settings->random_saturation; + } + /* Random to Value. */ + if (brush_settings->random_value > 0.0f) { + float rand = BLI_hash_int_01(BLI_hash_int_2d(ix * iz, iy * iz)) * 2.0f - 1.0f; + random_settings->hsv[2] = rand * brush_settings->random_value; + } + + /* Random to pressure. */ + if (brush_settings->draw_random_press > 0.0f) { + random_settings->pressure = BLI_hash_int_01(BLI_hash_int_2d(ix + iz, iy + iz)) * 2.0f - 1.0f; + } + + /* Randomn to color strength. */ + if (brush_settings->draw_random_strength) { + random_settings->strength = BLI_hash_int_01(BLI_hash_int_2d(ix + iy, iy + iz + ix)) * 2.0f - + 1.0f; + } + + /* Random to uv texture rotation. */ + if (brush_settings->uv_random > 0.0f) { + random_settings->uv = BLI_hash_int_01(BLI_hash_int_2d(iy + iz, ix * iz)) * 2.0f - 1.0f; + } +} + +static void gpencil_sbuffer_vertex_color_random( + bGPdata *gpd, Brush *brush, tGPspoint *tpt, float random_color[3], float pen_pressure) +{ + BrushGpencilSettings *brush_settings = brush->gpencil_settings; + if (brush_settings->flag & GP_BRUSH_GROUP_RANDOM) { + int seed = ((uint)(ceil(PIL_check_seconds_timer())) + 1) % 128; + + int ix = (int)(tpt->x * seed); + int iy = (int)(tpt->y * seed); + int iz = ix + iy * seed; + float hsv[3]; + float factor_value[3]; + zero_v3(factor_value); + + /* Apply randomness to Hue. */ + if (brush_settings->random_hue > 0.0f) { + if ((brush_settings->flag2 & GP_BRUSH_USE_HUE_AT_STROKE) == 0) { + + float rand = BLI_hash_int_01(BLI_hash_int_2d(ix, gpd->runtime.sbuffer_used)) * 2.0f - 1.0f; + factor_value[0] = rand * brush_settings->random_hue * 0.5f; + } + else { + factor_value[0] = random_color[0]; + } + + /* Apply random curve. */ + if (brush_settings->flag2 & GP_BRUSH_USE_HUE_RAND_PRESS) { + factor_value[0] *= BKE_curvemapping_evaluateF( + brush_settings->curve_rand_hue, 0, pen_pressure); + } + } + + /* Apply randomness to Saturation. */ + if (brush_settings->random_saturation > 0.0f) { + if ((brush_settings->flag2 & GP_BRUSH_USE_SAT_AT_STROKE) == 0) { + float rand = BLI_hash_int_01(BLI_hash_int_2d(iy, gpd->runtime.sbuffer_used)) * 2.0f - 1.0f; + factor_value[1] = rand * brush_settings->random_saturation; + } + else { + factor_value[1] = random_color[1]; + } + + /* Apply random curve. */ + if (brush_settings->flag2 & GP_BRUSH_USE_SAT_RAND_PRESS) { + factor_value[1] *= BKE_curvemapping_evaluateF( + brush_settings->curve_rand_saturation, 0, pen_pressure); + } + } + + /* Apply randomness to Value. */ + if (brush_settings->random_value > 0.0f) { + if ((brush_settings->flag2 & GP_BRUSH_USE_VAL_AT_STROKE) == 0) { + float rand = BLI_hash_int_01(BLI_hash_int_2d(iz, gpd->runtime.sbuffer_used)) * 2.0f - 1.0f; + factor_value[2] = rand * brush_settings->random_value; + } + else { + factor_value[2] = random_color[2]; + } + + /* Apply random curve. */ + if (brush_settings->flag2 & GP_BRUSH_USE_VAL_RAND_PRESS) { + factor_value[2] *= BKE_curvemapping_evaluateF( + brush_settings->curve_rand_value, 0, pen_pressure); + } + } + + rgb_to_hsv_v(tpt->vert_color, hsv); + add_v3_v3(hsv, factor_value); + /* For Hue need to cover all range, but for Saturation and Value + * is not logic because the effect is too hard, so the value is just clamped. */ + if (hsv[0] < 0.0f) { + hsv[0] += 1.0f; + } + else if (hsv[0] > 1.0f) { + hsv[0] -= 1.0f; + } + + CLAMP3(hsv, 0.0f, 1.0f); + hsv_to_rgb_v(hsv, tpt->vert_color); + } +} + +void ED_gpencil_sbuffer_vertex_color_set(Depsgraph *depsgraph, + Object *ob, + ToolSettings *ts, + Brush *brush, + Material *material, + float random_color[3], + float pen_pressure) { 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; + int idx = gpd->runtime.sbuffer_used; + tGPspoint *tpt = (tGPspoint *)gpd->runtime.sbuffer + idx; + float vertex_color[4]; copy_v3_v3(vertex_color, brush->rgb); vertex_color[3] = brush->gpencil_settings->vertex_factor; @@ -2559,15 +2741,18 @@ void ED_gpencil_sbuffer_vertex_color_set( } /* Copy stroke vertex color. */ if (GPENCIL_USE_VERTEX_COLOR_STROKE(ts, brush)) { - copy_v4_v4(gpd->runtime.vert_color, vertex_color); + copy_v4_v4(tpt->vert_color, vertex_color); } else { - copy_v4_v4(gpd->runtime.vert_color, gp_style->stroke_rgba); + copy_v4_v4(tpt->vert_color, gp_style->stroke_rgba); } - /* Copy to eval data because paint operators don't tag refresh until end for speedup painting. */ + /* Random Color. */ + gpencil_sbuffer_vertex_color_random(gpd, brush, tpt, random_color, pen_pressure); + + /* 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; } |