D1705: Fix Grease Pencil Fill for Concave Shapes
authorAntonio Vazquez <blendergit@gmail.com>
Thu, 28 Apr 2016 13:10:33 +0000 (01:10 +1200)
committerJoshua Leung <aligorith@gmail.com>
Sun, 8 May 2016 12:53:47 +0000 (00:53 +1200)
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

12 files changed:
release/scripts/startup/bl_ui/properties_grease_pencil_common.py
source/blender/blenkernel/intern/gpencil.c
source/blender/blenloader/intern/readfile.c
source/blender/editors/gpencil/drawgpencil.c
source/blender/editors/gpencil/editaction_gpencil.c
source/blender/editors/gpencil/gpencil_brush.c
source/blender/editors/gpencil/gpencil_edit.c
source/blender/editors/gpencil/gpencil_paint.c
source/blender/editors/transform/transform_conversions.c
source/blender/editors/transform/transform_generics.c
source/blender/makesdna/DNA_gpencil_types.h
source/blender/makesrna/intern/rna_gpencil.c

index 013f4e648547f5157d5047ca0d2e43cb0116cfaf..542676a66ee025ee4961f999d65882b4d0038d45 100644 (file)
@@ -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")
index 485c4f5b29f05a5743d5105b31b268abfe5b7900..c23429d86b7e80f5ab54b4b20ef9bffa60835b5f 100644 (file)
@@ -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 */
index 1b0dfb7e431548b9fd05fe16ac1cafef2ae40408..9c3091f186bb8aed1af82b5d22e18a445243a154 100644 (file)
@@ -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;
                        }
                }
        }
index b5d9283f6c67227c0b75f600f5c2497c9d233c0f..3f7805fa2c42f0b8d0e0a7c1d92859d2ad47d05e 100644 (file)
 #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);
index 09a72c1045767f319578af24e4ff0b803a7fc566..a49b33621558bb2c118a52cf3dbcc81cebc03b56 100644 (file)
@@ -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);                               
                                }
index 43751dbadb97eab544ad14c2d89e55c4803ead28..0bf5a259d5a6b17eef9fb1bceac108c44e210f60 100644 (file)
@@ -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;
        
index 03d5ed3e24f87531f629f9e6d8abe4d07023db57..e3501dfa9877745e41f41c31568f5efdf1ca4eb6 100644 (file)
@@ -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);
 }
 
index 2a81b481ed17535f900e97916e52486edfb470a3..06829cc92be33e9f33043a1c21296dcc2ed650b9 100644 (file)
@@ -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);
                        }
                }
index 5d3e71c5a3cd660469ea0512381ea710369f83fe..17f23e9fe21b9f9974337418d88859ab11cf7db2 100644 (file)
@@ -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++;
                                                }
index e23dd99fbcc5830deaec16fba61d0b937a282f05..ed6477392d8cbd2e7e9afd0fc85482967e4b930a 100644 (file)
@@ -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);
index e5e193d479bac0bf6775aec5b158f310822f40d8..e1f9a99c61098de8bdefe746807acc981f4d7307 100644 (file)
@@ -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' */
index 3c16f086325061ca75b23087730641fef2d3d8f0..6f67a4c992f231ee3eb7c98bf6a242281fcd2595 100644 (file)
@@ -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);