diff options
author | Antonio Vazquez <blendergit@gmail.com> | 2016-04-28 16:10:33 +0300 |
---|---|---|
committer | Joshua Leung <aligorith@gmail.com> | 2016-05-08 15:53:47 +0300 |
commit | 1d5c71bca74fe3bef40446259d424380f237c27f (patch) | |
tree | 7621cb32fdc803838494d48bfa167c4ce3f7ae3c | |
parent | 0411cfea9d8bfcad6271ccec51bc9a9c641c798c (diff) |
D1705: Fix Grease Pencil Fill for Concave Shapes
Improve filling for concave shapes using a triangulation of the stroke.
The triangulation information is saved in an internal cache and only is
recalculated if the stroke changes.
The triangulation is not saved in .blend file.
Reviewers: aligorith
Maniphest Tasks: T47102
Differential Revision: https://developer.blender.org/D1705
-rw-r--r-- | release/scripts/startup/bl_ui/properties_grease_pencil_common.py | 1 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/gpencil.c | 9 | ||||
-rw-r--r-- | source/blender/blenloader/intern/readfile.c | 3 | ||||
-rw-r--r-- | source/blender/editors/gpencil/drawgpencil.c | 208 | ||||
-rw-r--r-- | source/blender/editors/gpencil/editaction_gpencil.c | 3 | ||||
-rw-r--r-- | source/blender/editors/gpencil/gpencil_brush.c | 7 | ||||
-rw-r--r-- | source/blender/editors/gpencil/gpencil_edit.c | 35 | ||||
-rw-r--r-- | source/blender/editors/gpencil/gpencil_paint.c | 12 | ||||
-rw-r--r-- | source/blender/editors/transform/transform_conversions.c | 4 | ||||
-rw-r--r-- | source/blender/editors/transform/transform_generics.c | 13 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_gpencil_types.h | 15 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_gpencil.c | 6 |
12 files changed, 279 insertions, 37 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 013f4e64854..542676a66ee 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -576,6 +576,7 @@ class GreasePencilDataPanel: col = split.column(align=True) col.prop(gpl, "line_width", slider=True) col.prop(gpl, "use_volumetric_strokes") + col.prop(gpl, "use_hq_fill") col = split.column(align=True) col.prop(gpl, "show_x_ray") diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index 485c4f5b29f..c23429d86b7 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -68,6 +68,7 @@ bool free_gpencil_strokes(bGPDframe *gpf) /* free stroke memory arrays, then stroke itself */ if (gps->points) MEM_freeN(gps->points); + if (gps->triangles) MEM_freeN(gps->triangles); BLI_freelinkN(&gpf->strokes, gps); } @@ -261,7 +262,9 @@ bGPDlayer *gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setactive) ARRAY_SET_ITEMS(gpl->gcolor_prev, 0.145098f, 0.419608f, 0.137255f); /* green */ ARRAY_SET_ITEMS(gpl->gcolor_next, 0.125490f, 0.082353f, 0.529412f); /* blue */ - + /* HQ fill by default */ + gpl->flag |= GP_LAYER_HQ_FILL; + /* auto-name */ BLI_strncpy(gpl->info, name, sizeof(gpl->info)); BLI_uniquename(&gpd->layers, gpl, DATA_("GP_Layer"), '.', offsetof(bGPDlayer, info), sizeof(gpl->info)); @@ -315,7 +318,8 @@ bGPDframe *gpencil_frame_duplicate(bGPDframe *src) /* make copy of source stroke, then adjust pointer to points too */ gpsd = MEM_dupallocN(gps); gpsd->points = MEM_dupallocN(gps->points); - + gpsd->triangles = MEM_dupallocN(gps->triangles); + gpsd->flag |= GP_STROKE_RECALC_CACHES; BLI_addtail(&dst->strokes, gpsd); } @@ -424,6 +428,7 @@ void gpencil_frame_delete_laststroke(bGPDlayer *gpl, bGPDframe *gpf) /* free the stroke and its data */ MEM_freeN(gps->points); + MEM_freeN(gps->triangles); BLI_freelinkN(&gpf->strokes, gps); /* if frame has no strokes after this, delete it */ diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 1b0dfb7e431..9c3091f186b 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -6159,6 +6159,9 @@ static void direct_link_gpencil(FileData *fd, bGPdata *gpd) for (gps = gpf->strokes.first; gps; gps = gps->next) { gps->points = newdataadr(fd, gps->points); + /* the triangulation is not saved, so need to be recalculated */ + gps->triangles = NULL; + gps->flag |= GP_STROKE_RECALC_CACHES; } } } diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c index b5d9283f6c6..3f7805fa2c4 100644 --- a/source/blender/editors/gpencil/drawgpencil.c +++ b/source/blender/editors/gpencil/drawgpencil.c @@ -35,10 +35,13 @@ #include <math.h> #include <float.h> +#include "MEM_guardedalloc.h" + #include "BLI_sys_types.h" #include "BLI_math.h" #include "BLI_utildefines.h" +#include "BLI_polyfill2d.h" #include "BLF_api.h" #include "BLT_translation.h" @@ -82,6 +85,7 @@ typedef enum eDrawStrokeFlags { GP_DRAWDATA_NO_ONIONS = (1 << 6), /* no onionskins should be drawn (for animation playback) */ GP_DRAWDATA_VOLUMETRIC = (1 << 7), /* draw strokes as "volumetric" circular billboards */ GP_DRAWDATA_FILL = (1 << 8), /* fill insides/bounded-regions of strokes */ + GP_DRAWDATA_HQ_FILL = (1 << 9) /* Use high quality fill */ } eDrawStrokeFlags; @@ -324,37 +328,184 @@ static void gp_draw_stroke_volumetric_3d(bGPDspoint *points, int totpoints, shor /* --------------- Stroke Fills ----------------- */ +/* get points of stroke always flat to view not affected by camera view or view position + */ +static void gp_stroke_2d_flat(bGPDspoint *points, int totpoints, float(*points2d)[2], int *r_direction) +{ + bGPDspoint *pt0 = &points[0]; + bGPDspoint *pt1 = &points[1]; + bGPDspoint *pt3 = &points[(int) (totpoints * 0.75)]; + bGPDspoint *pt; + float locx[3]; + float locy[3]; + float loc3[3]; + float normal[3]; + + /* local X axis (p0-p1) */ + sub_v3_v3v3(locx, &pt1->x, &pt0->x); + + /* point vector at 3/4 */ + sub_v3_v3v3(loc3, &pt3->x, &pt0->x); + + /* vector orthogonal to polygon plane */ + cross_v3_v3v3(normal, locx, loc3); + + /* local Y axis (cross to normal/x axis) */ + cross_v3_v3v3(locy, normal, locx); + + /* Normalize vectors */ + normalize_v3(locx); + normalize_v3(locy); + + /* Get all points in local space */ + for (int i = 0; i < totpoints; i++) { + + float loc[3]; + /* Get local space using first point as origin */ + pt = &points[i]; + sub_v3_v3v3(loc, &pt->x, &pt0->x); + + float co[2]; + co[0] = dot_v3v3(loc, locx); + co[1] = dot_v3v3(loc, locy); + points2d[i][0] = co[0]; + points2d[i][1] = co[1]; + } + + *r_direction = (int)locy[2]; +} + + +/* triangulate stroke for high quality fill (this is done only if cache is null or stroke was modified) */ +static void gp_triangulate_stroke_fill(bGPDstroke *gps) +{ + BLI_assert(gps->totpoints >= 3); + + bGPDtriangle *stroke_triangle; + int i; + + /* allocate memory for temporary areas */ + unsigned int(*tmp_triangles)[3] = MEM_mallocN(sizeof(*tmp_triangles) * gps->totpoints, "GP Stroke temp triangulation"); + float(*points2d)[2] = MEM_mallocN(sizeof(*points2d) * gps->totpoints, "GP Stroke temp 2d points"); + + int direction; + + /* convert to 2d and triangulate */ + gp_stroke_2d_flat(gps->points, gps->totpoints, points2d, &direction); + BLI_polyfill_calc((const float(*)[2])points2d, (unsigned int)gps->totpoints, direction, (unsigned int(*)[3])tmp_triangles); + + /* count number of valid triangles */ + gps->tot_triangles = 0; + for (i = 0; i < gps->totpoints; i++) { + if ((tmp_triangles[i][0] >= 0) && (tmp_triangles[i][0] < gps->totpoints) && + (tmp_triangles[i][1] >= 0) && (tmp_triangles[i][1] < gps->totpoints) && + (tmp_triangles[i][2] >= 0) && (tmp_triangles[i][2] < gps->totpoints)) + { + gps->tot_triangles += 1; + } + } + + /* save triangulation data in stroke cache */ + if (gps->triangles == NULL) { + gps->triangles = MEM_callocN(sizeof(bGPDtriangle) * gps->tot_triangles, "GP Stroke triangulation"); + } + else { + gps->triangles = MEM_recallocN(gps->triangles, sizeof(*gps->triangles) * gps->tot_triangles); + } + + for (i = 0; i < gps->tot_triangles; i++) { + if ((tmp_triangles[i][0] >= 0) && (tmp_triangles[i][0] < gps->totpoints) && + (tmp_triangles[i][1] >= 0) && (tmp_triangles[i][1] < gps->totpoints) && + (tmp_triangles[i][2] >= 0) && (tmp_triangles[i][2] < gps->totpoints)) + { + stroke_triangle = &gps->triangles[i]; + stroke_triangle->v1 = tmp_triangles[i][0]; + stroke_triangle->v2 = tmp_triangles[i][1]; + stroke_triangle->v3 = tmp_triangles[i][2]; + } + } + /* disable recalculation flag (False)*/ + if (gps->flag & GP_STROKE_RECALC_CACHES) { + gps->flag ^= GP_STROKE_RECALC_CACHES; + } + /* clear memory */ + if (tmp_triangles) MEM_freeN(tmp_triangles); + if (points2d) MEM_freeN(points2d); + +} + /* draw fills for shapes */ -static void gp_draw_stroke_fill(bGPDspoint *points, int totpoints, short UNUSED(thickness), - short UNUSED(dflag), short sflag, - int offsx, int offsy, int winx, int winy) +static void gp_draw_stroke_fill(bGPDstroke *gps, short UNUSED(thickness), short dflag, int offsx, int offsy, int winx, int winy) { - bGPDspoint *pt; int i; - - BLI_assert(totpoints >= 3); - - /* As an initial implementation, we use the OpenGL filled polygon drawing - * here since it's the easiest option to implement for this case. It does - * come with limitations (notably for concave shapes), though it shouldn't - * be much of an issue in most cases. - */ - glBegin(GL_POLYGON); - - for (i = 0, pt = points; i < totpoints; i++, pt++) { - if (sflag & GP_STROKE_3DSPACE) { - glVertex3fv(&pt->x); + + BLI_assert(gps->totpoints >= 3); + /* Triangulation fill if high quality flag is enabled */ + if (dflag & GP_DRAWDATA_HQ_FILL) { + bGPDtriangle *stroke_triangle; + bGPDspoint *pt; + + /* Calculate triangles cache for filling area (must be done only after changes) */ + if ((gps->flag & GP_STROKE_RECALC_CACHES) || (gps->tot_triangles == 0) || (gps->triangles == NULL)) { + gp_triangulate_stroke_fill(gps); } - else { - float co[2]; - - gp_calc_2d_stroke_xy(pt, sflag, offsx, offsy, winx, winy, co); - glVertex2fv(co); + /* Draw all triangles for filling the polygon (cache must be calculated before) */ + BLI_assert(gps->tot_triangles >= 1); + glBegin(GL_TRIANGLES); + for (i = 0, stroke_triangle = gps->triangles; i < gps->tot_triangles; i++, stroke_triangle++) { + if (gps->flag & GP_STROKE_3DSPACE) { + /* vertex 1 */ + pt = &gps->points[stroke_triangle->v1]; + glVertex3fv(&pt->x); + /* vertex 2 */ + pt = &gps->points[stroke_triangle->v2]; + glVertex3fv(&pt->x); + /* vertex 3 */ + pt = &gps->points[stroke_triangle->v3]; + glVertex3fv(&pt->x); + } + else { + float co[2]; + /* vertex 1 */ + pt = &gps->points[stroke_triangle->v1]; + gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co); + glVertex2fv(co); + /* vertex 2 */ + pt = &gps->points[stroke_triangle->v2]; + gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co); + glVertex2fv(co); + /* vertex 3 */ + pt = &gps->points[stroke_triangle->v3]; + gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co); + glVertex2fv(co); + } } + glEnd(); + } + else { + /* As an initial implementation, we use the OpenGL filled polygon drawing + * here since it's the easiest option to implement for this case. It does + * come with limitations (notably for concave shapes), though it shouldn't + * be much of an issue in most cases. + */ + bGPDspoint *pt; + + glBegin(GL_POLYGON); + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (gps->flag & GP_STROKE_3DSPACE) { + glVertex3fv(&pt->x); + } + else { + float co[2]; + + gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co); + glVertex2fv(co); + } + } + + glEnd(); } - - glEnd(); } /* ----- Existing Strokes Drawing (3D and Point) ------ */ @@ -695,7 +846,7 @@ static void gp_draw_strokes(bGPDframe *gpf, int offsx, int offsy, int winx, int /* 3D Fill */ if ((dflag & GP_DRAWDATA_FILL) && (gps->totpoints >= 3)) { glColor4fv(fill_color); - gp_draw_stroke_fill(gps->points, gps->totpoints, lthick, dflag, gps->flag, offsx, offsy, winx, winy); + gp_draw_stroke_fill(gps, lthick, dflag, offsx, offsy, winx, winy); } /* 3D Stroke */ @@ -730,7 +881,7 @@ static void gp_draw_strokes(bGPDframe *gpf, int offsx, int offsy, int winx, int /* 2D - Fill */ if ((dflag & GP_DRAWDATA_FILL) && (gps->totpoints >= 3)) { glColor4fv(fill_color); - gp_draw_stroke_fill(gps->points, gps->totpoints, lthick, dflag, gps->flag, offsx, offsy, winx, winy); + gp_draw_stroke_fill(gps, lthick, dflag, offsx, offsy, winx, winy); } /* 2D Strokes... */ @@ -990,7 +1141,10 @@ static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, in /* volumetric strokes... */ GP_DRAWFLAG_APPLY((gpl->flag & GP_LAYER_VOLUMETRIC), GP_DRAWDATA_VOLUMETRIC); - + + /* HQ fills... */ + GP_DRAWFLAG_APPLY((gpl->flag & GP_LAYER_HQ_FILL), GP_DRAWDATA_HQ_FILL); + /* fill strokes... */ // XXX: this is not a very good limit GP_DRAWFLAG_APPLY((gpl->fill[3] > GPENCIL_ALPHA_OPACITY_THRESH), GP_DRAWDATA_FILL); diff --git a/source/blender/editors/gpencil/editaction_gpencil.c b/source/blender/editors/gpencil/editaction_gpencil.c index 09a72c10457..a49b3362155 100644 --- a/source/blender/editors/gpencil/editaction_gpencil.c +++ b/source/blender/editors/gpencil/editaction_gpencil.c @@ -460,7 +460,8 @@ bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode) /* make a copy of stroke, then of its points array */ gpsn = MEM_dupallocN(gps); gpsn->points = MEM_dupallocN(gps->points); - + /* duplicate triangle information */ + gpsn->triangles = MEM_dupallocN(gps->triangles); /* append stroke to frame */ BLI_addtail(&gpf->strokes, gpsn); } diff --git a/source/blender/editors/gpencil/gpencil_brush.c b/source/blender/editors/gpencil/gpencil_brush.c index 43751dbadb9..0bf5a259d5a 100644 --- a/source/blender/editors/gpencil/gpencil_brush.c +++ b/source/blender/editors/gpencil/gpencil_brush.c @@ -766,6 +766,8 @@ static void gp_brush_clone_add(bContext *C, tGP_BrushEditData *gso) new_stroke = MEM_dupallocN(gps); new_stroke->points = MEM_dupallocN(gps->points); + /* duplicate triangle information */ + new_stroke->triangles = MEM_dupallocN(gps->triangles); new_stroke->next = new_stroke->prev = NULL; BLI_addtail(&gpf->strokes, new_stroke); @@ -1284,6 +1286,11 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso) printf("ERROR: Unknown type of GPencil Sculpt brush - %u\n", gso->brush_type); break; } + /* Triangulation must be calculated if changed */ + if (changed) { + gps->flag |= GP_STROKE_RECALC_CACHES; + gps->tot_triangles = 0; + } } CTX_DATA_END; diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 03d5ed3e24f..e3501dfa987 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -169,7 +169,10 @@ static void gp_duplicate_points(const bGPDstroke *gps, ListBase *new_strokes) /* make a stupid copy first of the entire stroke (to get the flags too) */ gpsd = MEM_dupallocN(gps); - + /* initialize triangle memory */ + gpsd->triangles = NULL; + gpsd->flag |= GP_STROKE_RECALC_CACHES; + gpsd->tot_triangles = 0; /* now, make a new points array, and copy of the relevant parts */ gpsd->points = MEM_callocN(sizeof(bGPDspoint) * len, "gps stroke points copy"); memcpy(gpsd->points, gps->points + start_idx, sizeof(bGPDspoint) * len); @@ -222,7 +225,10 @@ static int gp_duplicate_exec(bContext *C, wmOperator *op) /* make direct copies of the stroke and its points */ gpsd = MEM_dupallocN(gps); gpsd->points = MEM_dupallocN(gps->points); - + /* triangle information */ + gpsd->triangles = MEM_dupallocN(gps->triangles); + gpsd->flag |= GP_STROKE_RECALC_CACHES; + /* add to temp buffer */ gpsd->next = gpsd->prev = NULL; BLI_addtail(&new_strokes, gpsd); @@ -288,6 +294,7 @@ void ED_gpencil_strokes_copybuf_free(void) gpsn = gps->next; MEM_freeN(gps->points); + MEM_freeN(gps->triangles); BLI_freelinkN(&gp_strokes_copypastebuf, gps); } @@ -335,7 +342,10 @@ static int gp_strokes_copy_exec(bContext *C, wmOperator *op) /* make direct copies of the stroke and its points */ gpsd = MEM_dupallocN(gps); gpsd->points = MEM_dupallocN(gps->points); - + /* duplicate triangle information */ + gpsd->triangles = MEM_dupallocN(gps->triangles); + gpsd->flag |= GP_STROKE_RECALC_CACHES; + gpsd->tot_triangles = 0; /* add to temp buffer */ gpsd->next = gpsd->prev = NULL; BLI_addtail(&gp_strokes_copypastebuf, gpsd); @@ -450,6 +460,9 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) bGPDstroke *new_stroke = MEM_dupallocN(gps); new_stroke->points = MEM_dupallocN(gps->points); + /* triangle information */ + new_stroke->triangles = MEM_dupallocN(gps->triangles); + new_stroke->flag |= GP_STROKE_RECALC_CACHES; new_stroke->next = new_stroke->prev = NULL; BLI_addtail(&gpf->strokes, new_stroke); @@ -672,6 +685,7 @@ static int gp_delete_selected_strokes(bContext *C) if (gps->flag & GP_STROKE_SELECT) { /* free stroke memory arrays, then stroke itself */ if (gps->points) MEM_freeN(gps->points); + if (gps->triangles) MEM_freeN(gps->triangles); BLI_freelinkN(&gpf->strokes, gps); changed = true; @@ -732,6 +746,9 @@ static int gp_dissolve_selected_points(bContext *C) if (tot <= 0) { /* remove the entire stroke */ MEM_freeN(gps->points); + if (gps->triangles) { + MEM_freeN(gps->triangles); + } BLI_freelinkN(&gpf->strokes, gps); } else { @@ -752,7 +769,9 @@ static int gp_dissolve_selected_points(bContext *C) /* save the new buffer */ gps->points = new_points; gps->totpoints = tot; - + /* recalculate cache */ + gps->flag |= GP_STROKE_RECALC_CACHES; + gps->tot_triangles = 0; /* deselect the stroke, since none of its selected points will still be selected */ gps->flag &= ~GP_STROKE_SELECT; } @@ -842,6 +861,11 @@ void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke tGPDeleteIsland *island = &islands[idx]; bGPDstroke *new_stroke = MEM_dupallocN(gps); + /* initialize triangle memory */ + new_stroke->triangles = NULL; + new_stroke->flag |= GP_STROKE_RECALC_CACHES; + new_stroke->tot_triangles = 0; + /* Compute new buffer size (+ 1 needed as the endpoint index is "inclusive") */ new_stroke->totpoints = island->end_idx - island->start_idx + 1; new_stroke->points = MEM_callocN(sizeof(bGPDspoint) * new_stroke->totpoints, "gp delete stroke fragment"); @@ -886,6 +910,9 @@ void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke /* Delete the old stroke */ MEM_freeN(gps->points); + if (gps->triangles) { + MEM_freeN(gps->triangles); + } BLI_freelinkN(&gpf->strokes, gps); } diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 2a81b481ed1..06829cc92be 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -572,6 +572,9 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) gps->flag = gpd->sbuffer_sflag; gps->inittime = p->inittime; + /* enable recalculation flag by default (only used if hq fill) */ + gps->flag |= GP_STROKE_RECALC_CACHES; + /* allocate enough memory for a continuous array for storage points */ int sublevel = gpl->sublevel; int new_totpoints = gps->totpoints; @@ -580,7 +583,10 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) new_totpoints += new_totpoints - 1; } gps->points = MEM_callocN(sizeof(bGPDspoint) * new_totpoints, "gp_stroke_points"); - + /* initialize triangle memory to dummy data */ + gps->triangles = MEM_callocN(sizeof(bGPDtriangle), "GP Stroke triangulation"); + gps->flag |= GP_STROKE_RECALC_CACHES; + gps->tot_triangles = 0; /* set pointer to first non-initialized point */ pt = gps->points + (gps->totpoints - totelem); @@ -795,6 +801,8 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, /* just free stroke */ if (gps->points) MEM_freeN(gps->points); + if (gps->triangles) + MEM_freeN(gps->triangles); BLI_freelinkN(&gpf->strokes, gps); } else if (gps->totpoints == 1) { @@ -807,6 +815,8 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, /* free stroke */ // XXX: pressure sensitive eraser should apply here too? MEM_freeN(gps->points); + if (gps->triangles) + MEM_freeN(gps->triangles); BLI_freelinkN(&gpf->strokes, gps); } } diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c index 5d3e71c5a3c..17f23e9fe21 100644 --- a/source/blender/editors/transform/transform_conversions.c +++ b/source/blender/editors/transform/transform_conversions.c @@ -7813,7 +7813,9 @@ static void createTransGPencil(bContext *C, TransInfo *t) copy_m3_m3(td->mtx, mtx); unit_m3(td->axismtx); // XXX? } - + /* Triangulation must be calculated again, so save the stroke for recalc function */ + td->extra = gps; + td++; tail++; } diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index e23dd99fbcc..ed6477392d8 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -969,6 +969,16 @@ static void recalcData_sequencer(TransInfo *t) flushTransSeq(t); } +/* force recalculation of triangles during transformation */ +static void recalcData_gpencil_strokes(TransInfo *t) + { + TransData *td = t->data; + for (int i = 0; i < t->total; i++, td++) { + bGPDstroke *gps = td->extra; + gps->flag |= GP_STROKE_RECALC_CACHES; + } +} + /* called for updating while transform acts, once per redraw */ void recalcData(TransInfo *t) { @@ -983,7 +993,8 @@ void recalcData(TransInfo *t) flushTransPaintCurve(t); } else if (t->options & CTX_GPENCIL_STROKES) { - /* pass? */ + /* set recalc triangle cache flag */ + recalcData_gpencil_strokes(t); } else if (t->spacetype == SPACE_IMAGE) { recalcData_image(t); diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index e5e193d479b..e1f9a99c610 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -57,6 +57,14 @@ typedef enum eGPDspoint_Flag { GP_SPOINT_TAG = (1 << 1), } eGPSPoint_Flag; +/* Grease-Pencil Annotations - 'Triangle' + * -> A triangle contains the index of three vertices for filling the stroke + * this is only used if high quality fill is enabled + */ +typedef struct bGPDtriangle { + int v1, v2, v3; /* indices for tesselated triangle used for GP Fill */ +} bGPDtriangle; + /* Grease-Pencil Annotations - 'Stroke' * -> A stroke represents a (simplified version) of the curve * drawn by the user in one 'mousedown'->'mouseup' operation @@ -69,6 +77,9 @@ typedef struct bGPDstroke { short thickness; /* thickness of stroke (currently not used) */ short flag; /* various settings about this stroke */ + bGPDtriangle *triangles;/* tesselated triangles for GP Fill */ + int tot_triangles; /* number of triangles in array */ + short pad2[2]; /* avoid compiler align error */ double inittime; /* Init time of stroke */ } bGPDstroke; @@ -83,6 +94,8 @@ typedef enum eGPDstroke_Flag { GP_STROKE_2DIMAGE = (1 << 2), /* stroke is selected */ GP_STROKE_SELECT = (1 << 3), + /* Recalculate triangulation for high quality fill (true force a new recalc) */ + GP_STROKE_RECALC_CACHES = (1 << 4), /* only for use with stroke-buffer (while drawing eraser) */ GP_STROKE_ERASER = (1 << 15) } eGPDstroke_Flag; @@ -160,6 +173,8 @@ typedef enum eGPDlayer_Flag { GP_LAYER_GHOST_NEXTCOL = (1 << 9), /* "volumetric" strokes (i.e. GLU Quadric discs in 3D) */ GP_LAYER_VOLUMETRIC = (1 << 10), + /* Use High quality fill using stencil */ + GP_LAYER_HQ_FILL = (1 << 11) } eGPDlayer_Flag; /* Grease-Pencil Annotations - 'DataBlock' */ diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index 3c16f086325..6f67a4c992f 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -721,6 +721,12 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Volumetric Strokes", "Draw strokes as a series of circular blobs, resulting in a volumetric effect"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + /* Use High quality fill */ + prop = RNA_def_property(srna, "use_hq_fill", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_HQ_FILL); + RNA_def_property_ui_text(prop, "High Quality Fill", "Fill strokes using high quality to avoid glitches (slower fps during animation play)"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + /* Stroke Drawing Color */ prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR_GAMMA); RNA_def_property_array(prop, 3); |