diff options
-rw-r--r-- | source/blender/blenloader/intern/readfile.c | 4 | ||||
-rw-r--r-- | source/blender/editors/gpencil/drawgpencil.c | 134 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_gpencil_types.h | 7 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_gpencil.c | 4 |
4 files changed, 84 insertions, 65 deletions
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 9c3091f186b..678fd84de94 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -6159,9 +6159,11 @@ 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; + gps->triangles = NULL; + gps->tot_triangles = 0; } } } diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c index 3f7805fa2c4..c885842e756 100644 --- a/source/blender/editors/gpencil/drawgpencil.c +++ b/source/blender/editors/gpencil/drawgpencil.c @@ -328,128 +328,134 @@ 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 - */ + +/* 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; + bGPDspoint *pt3 = &points[(int)(totpoints * 0.75)]; + 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++) { - + for (int i = 0; i < totpoints; i++) { + bGPDspoint *pt = &points[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]; + + points2d[i][0] = dot_v3v3(loc, locx); + points2d[i][1] = dot_v3v3(loc, locy); } - + *r_direction = (int)locy[2]; } -/* triangulate stroke for high quality fill (this is done only if cache is null or stroke was modified) */ +/* 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"); - + 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++) { + for (int 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; + gps->tot_triangles++; } } - + /* save triangulation data in stroke cache */ - if (gps->triangles == NULL) { - gps->triangles = MEM_callocN(sizeof(bGPDtriangle) * gps->tot_triangles, "GP Stroke triangulation"); + if (gps->tot_triangles > 0) { + if (gps->triangles == NULL) { + gps->triangles = MEM_callocN(sizeof(bGPDtriangle) * gps->tot_triangles, "GP Stroke triangulation"); + } + else { + gps->triangles = MEM_recallocN(gps->triangles, sizeof(bGPDtriangle) * gps->tot_triangles); + } + + for (int 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)) + { + bGPDtriangle *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]; + } + } } 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]; - } + /* No triangles needed - Free anything allocated previously */ + if (gps->triangles) + MEM_freeN(gps->triangles); + + gps->triangles = NULL; } - /* disable recalculation flag (False)*/ + + /* disable recalculation flag */ if (gps->flag & GP_STROKE_RECALC_CACHES) { - 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(bGPDstroke *gps, short UNUSED(thickness), short dflag, int offsx, int offsy, int winx, int winy) { - int i; - BLI_assert(gps->totpoints >= 3); + /* Triangulation fill if high quality flag is enabled */ if (dflag & GP_DRAWDATA_HQ_FILL) { bGPDtriangle *stroke_triangle; bGPDspoint *pt; - + int i; + /* 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); } + /* Draw all triangles for filling the polygon (cache must be calculated before) */ BLI_assert(gps->tot_triangles >= 1); glBegin(GL_TRIANGLES); @@ -458,23 +464,28 @@ static void gp_draw_stroke_fill(bGPDstroke *gps, short UNUSED(thickness), short /* 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); @@ -486,11 +497,17 @@ static void gp_draw_stroke_fill(bGPDstroke *gps, short UNUSED(thickness), short 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. + * come with limitations (notably for concave shapes), though it works well + * enough for many simple situations. + * + * We keep this legacy implementation around despite now having the high quality + * fills, as this is necessary for keeping everything working nicely for files + * created using old versions of Blender which may have depended on the artifacts + * the old fills created. */ bGPDspoint *pt; - + int i; + glBegin(GL_POLYGON); for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { if (gps->flag & GP_STROKE_3DSPACE) { @@ -498,12 +515,11 @@ static void gp_draw_stroke_fill(bGPDstroke *gps, short UNUSED(thickness), short } else { float co[2]; - + gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co); glVertex2fv(co); } } - glEnd(); } } diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index e1f9a99c610..ab0fcb81379 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -59,7 +59,7 @@ typedef enum eGPDspoint_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 + * 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 */ @@ -77,6 +77,7 @@ 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 */ @@ -94,7 +95,7 @@ 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) */ + /* Recalculate triangulation for high quality fill (when 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) @@ -173,7 +174,7 @@ 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 */ + /* Use high quality fill (instead of buggy legacy OpenGL Fill) */ GP_LAYER_HQ_FILL = (1 << 11) } eGPDlayer_Flag; diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index 6f67a4c992f..10d7efe9374 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -721,10 +721,10 @@ 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 */ + /* 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_ui_text(prop, "High Quality Fill", "Fill strokes using high quality method to avoid glitches (slower fps during animation playback)"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); /* Stroke Drawing Color */ |