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
This commit is contained in:
Antonio Vazquez
2016-04-29 01:10:33 +12:00
committed by Joshua Leung
parent 0411cfea9d
commit 1d5c71bca7
12 changed files with 281 additions and 39 deletions

View File

@@ -576,6 +576,7 @@ class GreasePencilDataPanel:
col = split.column(align=True) col = split.column(align=True)
col.prop(gpl, "line_width", slider=True) col.prop(gpl, "line_width", slider=True)
col.prop(gpl, "use_volumetric_strokes") col.prop(gpl, "use_volumetric_strokes")
col.prop(gpl, "use_hq_fill")
col = split.column(align=True) col = split.column(align=True)
col.prop(gpl, "show_x_ray") col.prop(gpl, "show_x_ray")

View File

@@ -68,6 +68,7 @@ bool free_gpencil_strokes(bGPDframe *gpf)
/* free stroke memory arrays, then stroke itself */ /* free stroke memory arrays, then stroke itself */
if (gps->points) MEM_freeN(gps->points); if (gps->points) MEM_freeN(gps->points);
if (gps->triangles) MEM_freeN(gps->triangles);
BLI_freelinkN(&gpf->strokes, gps); 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_prev, 0.145098f, 0.419608f, 0.137255f); /* green */
ARRAY_SET_ITEMS(gpl->gcolor_next, 0.125490f, 0.082353f, 0.529412f); /* blue */ 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 */ /* auto-name */
BLI_strncpy(gpl->info, name, sizeof(gpl->info)); BLI_strncpy(gpl->info, name, sizeof(gpl->info));
BLI_uniquename(&gpd->layers, gpl, DATA_("GP_Layer"), '.', offsetof(bGPDlayer, info), 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 */ /* make copy of source stroke, then adjust pointer to points too */
gpsd = MEM_dupallocN(gps); gpsd = MEM_dupallocN(gps);
gpsd->points = MEM_dupallocN(gps->points); gpsd->points = MEM_dupallocN(gps->points);
gpsd->triangles = MEM_dupallocN(gps->triangles);
gpsd->flag |= GP_STROKE_RECALC_CACHES;
BLI_addtail(&dst->strokes, gpsd); BLI_addtail(&dst->strokes, gpsd);
} }
@@ -424,6 +428,7 @@ void gpencil_frame_delete_laststroke(bGPDlayer *gpl, bGPDframe *gpf)
/* free the stroke and its data */ /* free the stroke and its data */
MEM_freeN(gps->points); MEM_freeN(gps->points);
MEM_freeN(gps->triangles);
BLI_freelinkN(&gpf->strokes, gps); BLI_freelinkN(&gpf->strokes, gps);
/* if frame has no strokes after this, delete it */ /* if frame has no strokes after this, delete it */

View File

@@ -6159,6 +6159,9 @@ static void direct_link_gpencil(FileData *fd, bGPdata *gpd)
for (gps = gpf->strokes.first; gps; gps = gps->next) { for (gps = gpf->strokes.first; gps; gps = gps->next) {
gps->points = newdataadr(fd, gps->points); 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;
} }
} }
} }

View File

@@ -35,10 +35,13 @@
#include <math.h> #include <math.h>
#include <float.h> #include <float.h>
#include "MEM_guardedalloc.h"
#include "BLI_sys_types.h" #include "BLI_sys_types.h"
#include "BLI_math.h" #include "BLI_math.h"
#include "BLI_utildefines.h" #include "BLI_utildefines.h"
#include "BLI_polyfill2d.h"
#include "BLF_api.h" #include "BLF_api.h"
#include "BLT_translation.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_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_VOLUMETRIC = (1 << 7), /* draw strokes as "volumetric" circular billboards */
GP_DRAWDATA_FILL = (1 << 8), /* fill insides/bounded-regions of strokes */ GP_DRAWDATA_FILL = (1 << 8), /* fill insides/bounded-regions of strokes */
GP_DRAWDATA_HQ_FILL = (1 << 9) /* Use high quality fill */
} eDrawStrokeFlags; } eDrawStrokeFlags;
@@ -324,37 +328,184 @@ static void gp_draw_stroke_volumetric_3d(bGPDspoint *points, int totpoints, shor
/* --------------- Stroke Fills ----------------- */ /* --------------- Stroke Fills ----------------- */
/* get points of stroke always flat to view not affected by camera view or view position
/* draw fills for shapes */ */
static void gp_draw_stroke_fill(bGPDspoint *points, int totpoints, short UNUSED(thickness), static void gp_stroke_2d_flat(bGPDspoint *points, int totpoints, float(*points2d)[2], int *r_direction)
short UNUSED(dflag), short sflag,
int offsx, int offsy, int winx, int winy)
{ {
bGPDspoint *pt0 = &points[0];
bGPDspoint *pt1 = &points[1];
bGPDspoint *pt3 = &points[(int) (totpoints * 0.75)];
bGPDspoint *pt; 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; int i;
BLI_assert(totpoints >= 3); /* allocate memory for temporary areas */
unsigned int(*tmp_triangles)[3] = MEM_mallocN(sizeof(*tmp_triangles) * gps->totpoints, "GP Stroke temp triangulation");
/* As an initial implementation, we use the OpenGL filled polygon drawing float(*points2d)[2] = MEM_mallocN(sizeof(*points2d) * gps->totpoints, "GP Stroke temp 2d points");
* 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 int direction;
* be much of an issue in most cases.
*/ /* convert to 2d and triangulate */
glBegin(GL_POLYGON); 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);
for (i = 0, pt = points; i < totpoints; i++, pt++) {
if (sflag & GP_STROKE_3DSPACE) { /* count number of valid triangles */
glVertex3fv(&pt->x); gps->tot_triangles = 0;
} for (i = 0; i < gps->totpoints; i++) {
else { if ((tmp_triangles[i][0] >= 0) && (tmp_triangles[i][0] < gps->totpoints) &&
float co[2]; (tmp_triangles[i][1] >= 0) && (tmp_triangles[i][1] < gps->totpoints) &&
(tmp_triangles[i][2] >= 0) && (tmp_triangles[i][2] < gps->totpoints))
gp_calc_2d_stroke_xy(pt, sflag, offsx, offsy, winx, winy, co); {
glVertex2fv(co); gps->tot_triangles += 1;
} }
} }
glEnd(); /* 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(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;
/* 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);
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();
}
} }
/* ----- Existing Strokes Drawing (3D and Point) ------ */ /* ----- 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 */ /* 3D Fill */
if ((dflag & GP_DRAWDATA_FILL) && (gps->totpoints >= 3)) { if ((dflag & GP_DRAWDATA_FILL) && (gps->totpoints >= 3)) {
glColor4fv(fill_color); 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 */ /* 3D Stroke */
@@ -730,7 +881,7 @@ static void gp_draw_strokes(bGPDframe *gpf, int offsx, int offsy, int winx, int
/* 2D - Fill */ /* 2D - Fill */
if ((dflag & GP_DRAWDATA_FILL) && (gps->totpoints >= 3)) { if ((dflag & GP_DRAWDATA_FILL) && (gps->totpoints >= 3)) {
glColor4fv(fill_color); 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... */ /* 2D Strokes... */
@@ -990,7 +1141,10 @@ static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, in
/* volumetric strokes... */ /* volumetric strokes... */
GP_DRAWFLAG_APPLY((gpl->flag & GP_LAYER_VOLUMETRIC), GP_DRAWDATA_VOLUMETRIC); 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... */ /* fill strokes... */
// XXX: this is not a very good limit // XXX: this is not a very good limit
GP_DRAWFLAG_APPLY((gpl->fill[3] > GPENCIL_ALPHA_OPACITY_THRESH), GP_DRAWDATA_FILL); GP_DRAWFLAG_APPLY((gpl->fill[3] > GPENCIL_ALPHA_OPACITY_THRESH), GP_DRAWDATA_FILL);

View 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 */ /* make a copy of stroke, then of its points array */
gpsn = MEM_dupallocN(gps); gpsn = MEM_dupallocN(gps);
gpsn->points = MEM_dupallocN(gps->points); gpsn->points = MEM_dupallocN(gps->points);
/* duplicate triangle information */
gpsn->triangles = MEM_dupallocN(gps->triangles);
/* append stroke to frame */ /* append stroke to frame */
BLI_addtail(&gpf->strokes, gpsn); BLI_addtail(&gpf->strokes, gpsn);
} }

View File

@@ -766,6 +766,8 @@ static void gp_brush_clone_add(bContext *C, tGP_BrushEditData *gso)
new_stroke = MEM_dupallocN(gps); new_stroke = MEM_dupallocN(gps);
new_stroke->points = MEM_dupallocN(gps->points); new_stroke->points = MEM_dupallocN(gps->points);
/* duplicate triangle information */
new_stroke->triangles = MEM_dupallocN(gps->triangles);
new_stroke->next = new_stroke->prev = NULL; new_stroke->next = new_stroke->prev = NULL;
BLI_addtail(&gpf->strokes, new_stroke); 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); printf("ERROR: Unknown type of GPencil Sculpt brush - %u\n", gso->brush_type);
break; break;
} }
/* Triangulation must be calculated if changed */
if (changed) {
gps->flag |= GP_STROKE_RECALC_CACHES;
gps->tot_triangles = 0;
}
} }
CTX_DATA_END; CTX_DATA_END;

View 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) */ /* make a stupid copy first of the entire stroke (to get the flags too) */
gpsd = MEM_dupallocN(gps); 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 */ /* now, make a new points array, and copy of the relevant parts */
gpsd->points = MEM_callocN(sizeof(bGPDspoint) * len, "gps stroke points copy"); gpsd->points = MEM_callocN(sizeof(bGPDspoint) * len, "gps stroke points copy");
memcpy(gpsd->points, gps->points + start_idx, sizeof(bGPDspoint) * len); 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 */ /* make direct copies of the stroke and its points */
gpsd = MEM_dupallocN(gps); gpsd = MEM_dupallocN(gps);
gpsd->points = MEM_dupallocN(gps->points); gpsd->points = MEM_dupallocN(gps->points);
/* triangle information */
gpsd->triangles = MEM_dupallocN(gps->triangles);
gpsd->flag |= GP_STROKE_RECALC_CACHES;
/* add to temp buffer */ /* add to temp buffer */
gpsd->next = gpsd->prev = NULL; gpsd->next = gpsd->prev = NULL;
BLI_addtail(&new_strokes, gpsd); BLI_addtail(&new_strokes, gpsd);
@@ -288,6 +294,7 @@ void ED_gpencil_strokes_copybuf_free(void)
gpsn = gps->next; gpsn = gps->next;
MEM_freeN(gps->points); MEM_freeN(gps->points);
MEM_freeN(gps->triangles);
BLI_freelinkN(&gp_strokes_copypastebuf, gps); 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 */ /* make direct copies of the stroke and its points */
gpsd = MEM_dupallocN(gps); gpsd = MEM_dupallocN(gps);
gpsd->points = MEM_dupallocN(gps->points); 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 */ /* add to temp buffer */
gpsd->next = gpsd->prev = NULL; gpsd->next = gpsd->prev = NULL;
BLI_addtail(&gp_strokes_copypastebuf, gpsd); 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); bGPDstroke *new_stroke = MEM_dupallocN(gps);
new_stroke->points = MEM_dupallocN(gps->points); 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; new_stroke->next = new_stroke->prev = NULL;
BLI_addtail(&gpf->strokes, new_stroke); BLI_addtail(&gpf->strokes, new_stroke);
@@ -672,6 +685,7 @@ static int gp_delete_selected_strokes(bContext *C)
if (gps->flag & GP_STROKE_SELECT) { if (gps->flag & GP_STROKE_SELECT) {
/* free stroke memory arrays, then stroke itself */ /* free stroke memory arrays, then stroke itself */
if (gps->points) MEM_freeN(gps->points); if (gps->points) MEM_freeN(gps->points);
if (gps->triangles) MEM_freeN(gps->triangles);
BLI_freelinkN(&gpf->strokes, gps); BLI_freelinkN(&gpf->strokes, gps);
changed = true; changed = true;
@@ -732,6 +746,9 @@ static int gp_dissolve_selected_points(bContext *C)
if (tot <= 0) { if (tot <= 0) {
/* remove the entire stroke */ /* remove the entire stroke */
MEM_freeN(gps->points); MEM_freeN(gps->points);
if (gps->triangles) {
MEM_freeN(gps->triangles);
}
BLI_freelinkN(&gpf->strokes, gps); BLI_freelinkN(&gpf->strokes, gps);
} }
else { else {
@@ -752,7 +769,9 @@ static int gp_dissolve_selected_points(bContext *C)
/* save the new buffer */ /* save the new buffer */
gps->points = new_points; gps->points = new_points;
gps->totpoints = tot; 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 */ /* deselect the stroke, since none of its selected points will still be selected */
gps->flag &= ~GP_STROKE_SELECT; gps->flag &= ~GP_STROKE_SELECT;
} }
@@ -842,6 +861,11 @@ void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke
tGPDeleteIsland *island = &islands[idx]; tGPDeleteIsland *island = &islands[idx];
bGPDstroke *new_stroke = MEM_dupallocN(gps); 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") */ /* Compute new buffer size (+ 1 needed as the endpoint index is "inclusive") */
new_stroke->totpoints = island->end_idx - island->start_idx + 1; new_stroke->totpoints = island->end_idx - island->start_idx + 1;
new_stroke->points = MEM_callocN(sizeof(bGPDspoint) * new_stroke->totpoints, "gp delete stroke fragment"); 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 */ /* Delete the old stroke */
MEM_freeN(gps->points); MEM_freeN(gps->points);
if (gps->triangles) {
MEM_freeN(gps->triangles);
}
BLI_freelinkN(&gpf->strokes, gps); BLI_freelinkN(&gpf->strokes, gps);
} }

View File

@@ -572,6 +572,9 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
gps->flag = gpd->sbuffer_sflag; gps->flag = gpd->sbuffer_sflag;
gps->inittime = p->inittime; 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 */ /* allocate enough memory for a continuous array for storage points */
int sublevel = gpl->sublevel; int sublevel = gpl->sublevel;
int new_totpoints = gps->totpoints; int new_totpoints = gps->totpoints;
@@ -580,7 +583,10 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
new_totpoints += new_totpoints - 1; new_totpoints += new_totpoints - 1;
} }
gps->points = MEM_callocN(sizeof(bGPDspoint) * new_totpoints, "gp_stroke_points"); 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 */ /* set pointer to first non-initialized point */
pt = gps->points + (gps->totpoints - totelem); pt = gps->points + (gps->totpoints - totelem);
@@ -795,6 +801,8 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
/* just free stroke */ /* just free stroke */
if (gps->points) if (gps->points)
MEM_freeN(gps->points); MEM_freeN(gps->points);
if (gps->triangles)
MEM_freeN(gps->triangles);
BLI_freelinkN(&gpf->strokes, gps); BLI_freelinkN(&gpf->strokes, gps);
} }
else if (gps->totpoints == 1) { else if (gps->totpoints == 1) {
@@ -807,6 +815,8 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
/* free stroke */ /* free stroke */
// XXX: pressure sensitive eraser should apply here too? // XXX: pressure sensitive eraser should apply here too?
MEM_freeN(gps->points); MEM_freeN(gps->points);
if (gps->triangles)
MEM_freeN(gps->triangles);
BLI_freelinkN(&gpf->strokes, gps); BLI_freelinkN(&gpf->strokes, gps);
} }
} }

View File

@@ -7813,7 +7813,9 @@ static void createTransGPencil(bContext *C, TransInfo *t)
copy_m3_m3(td->mtx, mtx); copy_m3_m3(td->mtx, mtx);
unit_m3(td->axismtx); // XXX? unit_m3(td->axismtx); // XXX?
} }
/* Triangulation must be calculated again, so save the stroke for recalc function */
td->extra = gps;
td++; td++;
tail++; tail++;
} }

View File

@@ -969,6 +969,16 @@ static void recalcData_sequencer(TransInfo *t)
flushTransSeq(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 */ /* called for updating while transform acts, once per redraw */
void recalcData(TransInfo *t) void recalcData(TransInfo *t)
{ {
@@ -983,7 +993,8 @@ void recalcData(TransInfo *t)
flushTransPaintCurve(t); flushTransPaintCurve(t);
} }
else if (t->options & CTX_GPENCIL_STROKES) { else if (t->options & CTX_GPENCIL_STROKES) {
/* pass? */ /* set recalc triangle cache flag */
recalcData_gpencil_strokes(t);
} }
else if (t->spacetype == SPACE_IMAGE) { else if (t->spacetype == SPACE_IMAGE) {
recalcData_image(t); recalcData_image(t);

View File

@@ -57,6 +57,14 @@ typedef enum eGPDspoint_Flag {
GP_SPOINT_TAG = (1 << 1), GP_SPOINT_TAG = (1 << 1),
} eGPSPoint_Flag; } 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' /* Grease-Pencil Annotations - 'Stroke'
* -> A stroke represents a (simplified version) of the curve * -> A stroke represents a (simplified version) of the curve
* drawn by the user in one 'mousedown'->'mouseup' operation * 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 thickness; /* thickness of stroke (currently not used) */
short flag; /* various settings about this stroke */ 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 */ double inittime; /* Init time of stroke */
} bGPDstroke; } bGPDstroke;
@@ -83,6 +94,8 @@ typedef enum eGPDstroke_Flag {
GP_STROKE_2DIMAGE = (1 << 2), GP_STROKE_2DIMAGE = (1 << 2),
/* stroke is selected */ /* stroke is selected */
GP_STROKE_SELECT = (1 << 3), 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) */ /* only for use with stroke-buffer (while drawing eraser) */
GP_STROKE_ERASER = (1 << 15) GP_STROKE_ERASER = (1 << 15)
} eGPDstroke_Flag; } eGPDstroke_Flag;
@@ -160,6 +173,8 @@ typedef enum eGPDlayer_Flag {
GP_LAYER_GHOST_NEXTCOL = (1 << 9), GP_LAYER_GHOST_NEXTCOL = (1 << 9),
/* "volumetric" strokes (i.e. GLU Quadric discs in 3D) */ /* "volumetric" strokes (i.e. GLU Quadric discs in 3D) */
GP_LAYER_VOLUMETRIC = (1 << 10), GP_LAYER_VOLUMETRIC = (1 << 10),
/* Use High quality fill using stencil */
GP_LAYER_HQ_FILL = (1 << 11)
} eGPDlayer_Flag; } eGPDlayer_Flag;
/* Grease-Pencil Annotations - 'DataBlock' */ /* Grease-Pencil Annotations - 'DataBlock' */

View 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_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"); 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 */ /* Stroke Drawing Color */
prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR_GAMMA); prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR_GAMMA);
RNA_def_property_array(prop, 3); RNA_def_property_array(prop, 3);