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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAntonio Vazquez <blendergit@gmail.com>2016-03-27 13:24:14 +0300
committerJoshua Leung <aligorith@gmail.com>2016-03-27 17:21:28 +0300
commitbfbbc8ec4001243ad5e9954665ddf9621329319f (patch)
tree95ec19c1cb7714e8d978a517676f4680cf1f8d88
parent71107208ddb2d70cc69d65f03088a8fa348ab030 (diff)
Improve grease pencil stroke quality
Improve the quality of current grease pencil strokes adding a new dynamic smooth and subdivision. The level of smooth and subdivide can be adjusted using UI parameters. These options are disabled by default in order to keep the grease pencil stroke compatible with any existing add-on. Both parameters are defined at layer level. Reviewers: aligorith Differential Revision: https://developer.blender.org/D1866
-rw-r--r--release/scripts/startup/bl_ui/properties_grease_pencil_common.py8
-rw-r--r--source/blender/editors/gpencil/gpencil_brush.c65
-rw-r--r--source/blender/editors/gpencil/gpencil_intern.h15
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c39
-rw-r--r--source/blender/editors/gpencil/gpencil_utils.c98
-rw-r--r--source/blender/makesdna/DNA_gpencil_types.h4
-rw-r--r--source/blender/makesrna/intern/rna_gpencil.c16
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);