diff options
-rw-r--r-- | release/scripts/startup/bl_ui/properties_grease_pencil_common.py | 8 | ||||
-rw-r--r-- | source/blender/editors/gpencil/gpencil_brush.c | 65 | ||||
-rw-r--r-- | source/blender/editors/gpencil/gpencil_intern.h | 15 | ||||
-rw-r--r-- | source/blender/editors/gpencil/gpencil_paint.c | 39 | ||||
-rw-r--r-- | source/blender/editors/gpencil/gpencil_utils.c | 98 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_gpencil_types.h | 4 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_gpencil.c | 16 |
7 files changed, 178 insertions, 67 deletions
diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py index b93869b26ba..5d09dc8d942 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -622,6 +622,14 @@ class GreasePencilDataPanel: row.prop(gpl, "after_color", text="") sub.prop(gpl, "ghost_after_range", text="After") + # Smooth and subdivide new strokes + layout.separator() + col = layout.column(align=True) + col.label(text="New Stroke Quality:") + split = col.split() + split.prop(gpl, "smooth_drawfac") + split.prop(gpl, "subdivision") + class GreasePencilToolsPanel: # subclass must set diff --git a/source/blender/editors/gpencil/gpencil_brush.c b/source/blender/editors/gpencil/gpencil_brush.c index 95314445045..ab6217c20e3 100644 --- a/source/blender/editors/gpencil/gpencil_brush.c +++ b/source/blender/editors/gpencil/gpencil_brush.c @@ -223,70 +223,9 @@ static bool gp_brush_smooth_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i GP_EditBrush_Data *brush = gso->brush; bGPDspoint *pt = &gps->points[i]; float inf = gp_brush_influence_calc(gso, radius, co); - float pressure = 0.0f; - float sco[3] = {0.0f}; - /* Do nothing if not enough points to smooth out */ - if (gps->totpoints <= 2) { - return false; - } - - /* Only affect endpoints by a fraction of the normal strength, - * to prevent the stroke from shrinking too much - */ - if ((i == 0) || (i == gps->totpoints - 1)) { - inf *= 0.1f; - } - - /* Compute smoothed coordinate by taking the ones nearby */ - /* XXX: This is potentially slow, and suffers from accumulation error as earlier points are handled before later ones */ - { - // XXX: this is hardcoded to look at 2 points on either side of the current one (i.e. 5 items total) - const int steps = 2; - const float average_fac = 1.0f / (float)(steps * 2 + 1); - int step; - - /* add the point itself */ - madd_v3_v3fl(sco, &pt->x, average_fac); - - if (brush->flag & GP_EDITBRUSH_FLAG_SMOOTH_PRESSURE) { - pressure += pt->pressure * average_fac; - } - - /* n-steps before/after current point */ - // XXX: review how the endpoints are treated by this algorithm - // XXX: falloff measures should also introduce some weighting variations, so that further-out points get less weight - for (step = 1; step <= steps; step++) { - bGPDspoint *pt1, *pt2; - int before = i - step; - int after = i + step; - - CLAMP_MIN(before, 0); - CLAMP_MAX(after, gps->totpoints - 1); - - pt1 = &gps->points[before]; - pt2 = &gps->points[after]; - - /* add both these points to the average-sum (s += p[i]/n) */ - madd_v3_v3fl(sco, &pt1->x, average_fac); - madd_v3_v3fl(sco, &pt2->x, average_fac); - - /* do pressure too? */ - if (brush->flag & GP_EDITBRUSH_FLAG_SMOOTH_PRESSURE) { - pressure += pt1->pressure * average_fac; - pressure += pt2->pressure * average_fac; - } - } - } - - /* Based on influence factor, blend between original and optimal smoothed coordinate */ - interp_v3_v3v3(&pt->x, &pt->x, sco, inf); - - if (brush->flag & GP_EDITBRUSH_FLAG_SMOOTH_PRESSURE) { - pt->pressure = pressure; - } - - return true; + /* perform smoothing */ + return gp_smooth_stroke(gps, i, inf); } /* ----------------------------------------------- */ diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index 45dfb36b9d9..5dd491c66d3 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -208,6 +208,21 @@ void gpencil_undo_init(struct bGPdata *gpd); void gpencil_undo_push(struct bGPdata *gpd); void gpencil_undo_finish(void); +/** +* Apply smooth to stroke +* +* gps Stroke to smooth +* i Point index +* inf Smooth factor +*/ +bool gp_smooth_stroke(bGPDstroke *gps, int i, float inf); + +/* subdivide a stroke +* gps Stroke data +* new_totpoints Total number of points +*/ +void gp_subdivide_stroke(bGPDstroke *gps, const int new_totpoints); + /******************************************************* */ /* FILTERED ACTION DATA - TYPES ---> XXX DEPRECEATED OLD ANIM SYSTEM CODE! */ diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index b76ed90a0f1..3784d11908f 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -571,6 +571,8 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) bGPDstroke *gps; bGPDspoint *pt; tGPspoint *ptc; + bGPDlayer *layer = gpencil_layer_getactive(p->gpd); + int i, totelem; /* since strokes are so fine, when using their depth we need a margin otherwise they might get missed */ int depth_margin = (p->gpd->flag & GP_DATA_DEPTH_STROKE) ? 4 : 0; @@ -610,8 +612,20 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) gps->inittime = p->inittime; /* allocate enough memory for a continuous array for storage points */ - gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); - + int sublevel = layer->sublevel; + int new_totpoints = gps->totpoints; + for (i = 0; i < sublevel; ++i) + { + // Avoid error if subdivide is too big (assume totpoints is right) + if (new_totpoints + (new_totpoints - 1) > GP_STROKE_BUFFER_MAX) + { + sublevel = i; // reduce sublevel + break; + } + new_totpoints += new_totpoints - 1; + } + gps->points = MEM_callocN(sizeof(bGPDspoint) * new_totpoints, "gp_stroke_points"); + /* set pointer to first non-initialized point */ pt = gps->points + (gps->totpoints - totelem); @@ -730,10 +744,29 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) pt->time = ptc->time; } + /* subdivide the stroke */ + if (sublevel > 0) + { + int sub = gps->totpoints; + for (i = 0; i < sublevel; ++i) + { + sub += sub - 1; + gp_subdivide_stroke(gps, sub); + } + } + /* smooth stroke */ + if (layer->smooth_drawfac > 0.0f) // only if something to do + { + for (i = 0; i < gps->totpoints; i++) + { + gp_smooth_stroke(gps, i, layer->smooth_drawfac); + } + } + if (depth_arr) MEM_freeN(depth_arr); } - + /* add stroke to frame */ BLI_addtail(&p->gpf->strokes, gps); gp_stroke_added_enable(p); diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index a23628e6507..8e56cf8199e 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -533,4 +533,102 @@ bool gp_point_xy_to_3d(GP_SpaceConversion *gsc, Scene *scene, const float screen } } +/* Apply smooth to stroke point +* gps Stroke to smooth +* i Point index +* inf Smooth factor +*/ +bool gp_smooth_stroke(bGPDstroke *gps, int i, float inf) +{ + bGPDspoint *pt = &gps->points[i]; + float sco[3] = { 0.0f }; + + /* Do nothing if not enough points to smooth out */ + if (gps->totpoints <= 2) { + return false; + } + + /* Only affect endpoints by a fraction of the normal strength, + * to prevent the stroke from shrinking too much + */ + if ((i == 0) || (i == gps->totpoints - 1)) { + inf *= 0.1f; + } + + /* Compute smoothed coordinate by taking the ones nearby */ + /* XXX: This is potentially slow, and suffers from accumulation error as earlier points are handled before later ones */ + { + // XXX: this is hardcoded to look at 2 points on either side of the current one (i.e. 5 items total) + const int steps = 2; + const float average_fac = 1.0f / (float)(steps * 2 + 1); + int step; + + /* add the point itself */ + madd_v3_v3fl(sco, &pt->x, average_fac); + + /* n-steps before/after current point */ + // XXX: review how the endpoints are treated by this algorithm + // XXX: falloff measures should also introduce some weighting variations, so that further-out points get less weight + for (step = 1; step <= steps; step++) { + bGPDspoint *pt1, *pt2; + int before = i - step; + int after = i + step; + + CLAMP_MIN(before, 0); + CLAMP_MAX(after, gps->totpoints - 1); + + pt1 = &gps->points[before]; + pt2 = &gps->points[after]; + + /* add both these points to the average-sum (s += p[i]/n) */ + madd_v3_v3fl(sco, &pt1->x, average_fac); + madd_v3_v3fl(sco, &pt2->x, average_fac); + + } + } + + /* Based on influence factor, blend between original and optimal smoothed coordinate */ + interp_v3_v3v3(&pt->x, &pt->x, sco, inf); + + return true; +} + +/* subdivide a stroke +* gps Stroke data +* new_totpoints Total number of points +*/ +void gp_subdivide_stroke(bGPDstroke *gps, const int new_totpoints) +{ + int i; + // Subdivide stroke adding a point half way existing points + bGPDspoint *pt_a; + bGPDspoint *pt_b; + bGPDspoint *pt_n; + + /* Move points to insert subdivision */ + int y = 1; + for (i = gps->totpoints - 1; i > 0; --i) + { + pt_n = &gps->points[i]; + gps->points[new_totpoints - y] = *pt_n; + y = y + 2; + } + /* Create interpolated points */ + for (i = 0; i < new_totpoints - 1; ++i) + { + pt_a = &gps->points[i]; + pt_n = &gps->points[i + 1]; + pt_b = &gps->points[i + 2]; + // Interpolate all values + interp_v3_v3v3(&pt_n->x, &pt_a->x, &pt_b->x, 0.5f); + pt_n->pressure = interpf(pt_a->pressure, pt_b->pressure, 0.5f); + pt_n->time = interpf(pt_a->time, pt_b->time, 0.5f); + + ++i; // add to loop to jump next pair + } + + gps->totpoints = new_totpoints; // Increase number of points + +} + /* ******************************************************** */ diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index aa98ddb11ae..0ef213235b6 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -130,6 +130,10 @@ typedef struct bGPDlayer { char info[128]; /* optional reference info about this layer (i.e. "director's comments, 12/3") * this is used for the name of the layer too and kept unique. */ + + float smooth_drawfac; /* factor used for dynamic smooth of strokes */ + short sublevel; /* subdivision level */ + short pad[5]; /* padding for compiler error */ } bGPDlayer; /* bGPDlayer->flag */ diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index 291456a591b..7d5e7c1b49a 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -796,7 +796,21 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_ui_text(prop, "After Color", "Base color for ghosts after the active frame"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - + + /* dynamic smooth factor */ + prop = RNA_def_property(srna, "smooth_drawfac", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "smooth_drawfac"); + RNA_def_property_range(prop, 0.0, 2.0f); + RNA_def_property_ui_text(prop, "Smooth", "Amount of smoothing to apply to newly created strokes, to reduce jitter/noise"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + /* Subdivision level */ + prop = RNA_def_property(srna, "subdivision", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "sublevel"); + RNA_def_property_range(prop, 0, 3); + RNA_def_property_ui_text(prop, "Subdivide", "Number of times to subdivide newly created strokes, for less jagged strokes"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + /* Flags */ prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_HIDE); |