GPencil: Changes in Fill and new 3D Cursor View Plane

This commit groups several options that were tested in grease pencil branch:

- Changes to fill algorithms and improves, specially in small areas and stroke corners.
  New options has been added in order to define how the fill is working and internally there are optimizations in detect the small areas in the extremes.

  Kudos to @charlie for coding this fill improvements.

- New 3D cursor view plane option.

  Now it's possible to lock the drawing plane to the 3D cursor and use the 3D cursor orientation. This allows more flexibility when you are drawing and reduce the need to create geometry to draw over surfaces.

- Canvas Grid now can be locked to 3D cursor.
- New option to reproject stroke using 3D cursor.
- Small tweaks and fixes.

Changes reviewed by @pepeland and @mendio
This commit is contained in:
Antonioya 2019-03-17 19:47:31 +01:00
parent 887d052e56
commit 6577618d5b
21 changed files with 485 additions and 136 deletions

View File

@ -393,7 +393,7 @@ def brush_basic_sculpt_settings(layout, context, brush, *, compact=False):
layout.row().prop(brush, "direction", expand=True, **({"text": ""} if compact else {}))
def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False):
def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=True):
gp_settings = brush.gpencil_settings
# Brush details
@ -412,24 +412,15 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False)
row = layout.row(align=True)
row.prop(gp_settings, "eraser_thickness_factor")
elif brush.gpencil_tool == 'FILL':
row = layout.column(align=True)
row.prop(gp_settings, "fill_leak", text="Leak Size")
row.separator()
row = layout.column(align=True)
row.prop(brush, "size", text="Thickness")
row = layout.column(align=True)
row.prop(gp_settings, "fill_simplify_level", text="Simplify")
row = layout.row(align=True)
row.prop(gp_settings, "fill_draw_mode", text="Boundary Draw Mode")
row.prop(gp_settings, "fill_leak", text="Leak Size")
row = layout.row(align=True)
row.prop(brush, "size", text="Thickness")
row = layout.row(align=True)
row.prop(gp_settings, "fill_simplify_level", text="Simplify")
row = layout.row(align=True)
row.prop(gp_settings, "fill_draw_mode", text="Boundary")
row.prop(gp_settings, "show_fill_boundary", text="", icon='GRID')
row = layout.column(align=True)
row.enabled = gp_settings.fill_draw_mode != 'STROKE'
row.prop(gp_settings, "show_fill", text="Ignore Transparent Strokes")
sub = layout.row(align=True)
sub.enabled = not gp_settings.show_fill
sub.prop(gp_settings, "fill_threshold", text="Threshold")
else: # brush.gpencil_tool == 'DRAW':
row = layout.row(align=True)
row.prop(brush, "size", text="Radius")

View File

@ -362,6 +362,15 @@ class _draw_left_context_mode:
panel="TOPBAR_PT_gpencil_primitive",
text="Thickness Profile"
)
if brush.gpencil_tool == 'FILL':
settings = context.tool_settings.gpencil_sculpt
row = layout.row(align=True)
sub = row.row(align=True)
sub.popover(
panel="TOPBAR_PT_gpencil_fill",
text="Fill Options"
)
@staticmethod
def SCULPT_GPENCIL(context, layout, tool):
@ -1036,6 +1045,28 @@ class TOPBAR_PT_gpencil_primitive(Panel):
# Curve
layout.template_curve_mapping(settings, "thickness_primitive_curve", brush=True)
# Grease Pencil Fill
class TOPBAR_PT_gpencil_fill(Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'HEADER'
bl_label = "Advanced"
@staticmethod
def draw(self, context):
paint = context.tool_settings.gpencil_paint
brush = paint.brush
gp_settings = brush.gpencil_settings
layout = self.layout
# Fill
row = layout.row(align=True)
row.prop(gp_settings, "fill_factor", text="Resolution")
if gp_settings.fill_draw_mode != 'STROKE':
row = layout.row(align=True)
row.prop(gp_settings, "show_fill", text="Ignore Transparent Strokes")
row = layout.row(align=True)
row.prop(gp_settings, "fill_threshold", text="Threshold")
classes = (
TOPBAR_HT_upper_bar,
@ -1058,6 +1089,7 @@ classes = (
TOPBAR_PT_active_tool,
TOPBAR_PT_gpencil_layers,
TOPBAR_PT_gpencil_primitive,
TOPBAR_PT_gpencil_fill,
)
if __name__ == "__main__": # only for live edit.

View File

@ -480,6 +480,7 @@ void BKE_brush_gpencil_presets(bContext *C)
brush->gpencil_settings->fill_leak = 3;
brush->gpencil_settings->fill_threshold = 0.1f;
brush->gpencil_settings->fill_simplylvl = 1;
brush->gpencil_settings->fill_factor = 1;
brush->gpencil_settings->icon_id = GP_BRUSH_ICON_FILL;
brush->gpencil_tool = GPAINT_TOOL_FILL;

View File

@ -270,7 +270,7 @@ GPUBatch *DRW_gpencil_get_buffer_stroke_geom(bGPdata *gpd, short thickness)
for (int i = 0; i < totpoints; i++, tpt++) {
ED_gpencil_tpoint_to_point(ar, origin, tpt, &pt);
ED_gp_project_point_to_plane(ob, rv3d, origin, ts->gp_sculpt.lock_axis - 1, &pt);
ED_gp_project_point_to_plane(scene, ob, rv3d, origin, ts->gp_sculpt.lock_axis - 1, &pt);
/* first point for adjacency (not drawn) */
if (i == 0) {
@ -361,7 +361,7 @@ GPUBatch *DRW_gpencil_get_buffer_point_geom(bGPdata *gpd, short thickness)
for (int i = 0; i < totpoints; i++, tpt++) {
ED_gpencil_tpoint_to_point(ar, origin, tpt, &pt);
ED_gp_project_point_to_plane(ob, rv3d, origin, ts->gp_sculpt.lock_axis - 1, &pt);
ED_gp_project_point_to_plane(scene, ob, rv3d, origin, ts->gp_sculpt.lock_axis - 1, &pt);
/* set point */
gpencil_set_stroke_point(

View File

@ -506,7 +506,7 @@ DRWShadingGroup *DRW_gpencil_shgroup_stroke_create(
DRW_shgroup_uniform_float(grp, "pixsize", stl->storage->pixsize, 1);
/* avoid wrong values */
if ((gpd) && (gpd->pixfactor == 0)) {
if ((gpd) && (gpd->pixfactor == 0.0f)) {
gpd->pixfactor = GP_DEFAULT_PIX_FACTOR;
}
@ -627,7 +627,7 @@ static DRWShadingGroup *DRW_gpencil_shgroup_point_create(
DRW_shgroup_uniform_float(grp, "pixsize", stl->storage->pixsize, 1);
/* avoid wrong values */
if ((gpd) && (gpd->pixfactor == 0)) {
if ((gpd) && (gpd->pixfactor == 0.0f)) {
gpd->pixfactor = GP_DEFAULT_PIX_FACTOR;
}

View File

@ -582,6 +582,7 @@ void GPENCIL_cache_populate(void *vedata, Object *ob)
Scene *scene = draw_ctx->scene;
ToolSettings *ts = scene->toolsettings;
View3D *v3d = draw_ctx->v3d;
const View3DCursor *cursor = &scene->cursor;
if (ob->type == OB_GPENCIL && ob->data) {
bGPdata *gpd = (bGPdata *)ob->data;
@ -626,7 +627,9 @@ void GPENCIL_cache_populate(void *vedata, Object *ob)
if ((v3d) &&
((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) &&
(v3d->gp_flag & V3D_GP_SHOW_GRID) &&
(ob->type == OB_GPENCIL) && (ob == draw_ctx->obact))
(ob->type == OB_GPENCIL) && (ob == draw_ctx->obact) &&
((ts->gpencil_v3d_align & GP_PROJECT_DEPTH_VIEW) == 0) &&
((ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) == 0))
{
GPU_BATCH_DISCARD_SAFE(e_data.batch_grid);
MEM_SAFE_FREE(e_data.batch_grid);
@ -634,13 +637,36 @@ void GPENCIL_cache_populate(void *vedata, Object *ob)
e_data.batch_grid = DRW_gpencil_get_grid(ob);
/* define grid orientation */
if (ts->gp_sculpt.lock_axis != GP_LOCKAXIS_VIEW) {
copy_m4_m4(stl->storage->grid_matrix, ob->obmat);
switch (ts->gp_sculpt.lock_axis) {
case GP_LOCKAXIS_VIEW:
{
/* align always to view */
invert_m4_m4(stl->storage->grid_matrix, draw_ctx->rv3d->viewmat);
/* copy ob location */
copy_v3_v3(stl->storage->grid_matrix[3], ob->obmat[3]);
break;
}
case GP_LOCKAXIS_CURSOR:
{
float scale[3] = { 1.0f, 1.0f, 1.0f };
loc_eul_size_to_mat4(stl->storage->grid_matrix,
cursor->location,
cursor->rotation_euler,
scale);
break;
}
default:
{
copy_m4_m4(stl->storage->grid_matrix, ob->obmat);
break;
}
}
/* Move the origin to Object or Cursor */
if (ts->gpencil_v3d_align & GP_PROJECT_CURSOR) {
copy_v3_v3(stl->storage->grid_matrix[3], cursor->location);
}
else {
/* align always to view */
invert_m4_m4(stl->storage->grid_matrix, draw_ctx->rv3d->viewmat);
/* copy ob location */
copy_v3_v3(stl->storage->grid_matrix[3], ob->obmat[3]);
}

View File

@ -720,9 +720,12 @@ static void gp_draw_stroke_3d(tGPDdraw *tgpw, short thickness, const float ink[4
immUniform1f("objscale", obj_scale);
int keep_size = (int)((tgpw->gpd) && (tgpw->gpd->flag & GP_DATA_STROKE_KEEPTHICKNESS));
immUniform1i("keep_size", keep_size);
immUniform1i("pixfactor", tgpw->gpd->pixfactor);
immUniform1f("pixfactor", tgpw->gpd->pixfactor);
/* xray mode always to 3D space to avoid wrong zdepth calculation (T60051) */
immUniform1i("xraymode", GP_XRAY_3DSPACE);
immUniform1i("caps_start", (int)tgpw->gps->caps[0]);
immUniform1i("caps_end", (int)tgpw->gps->caps[1]);
immUniform1i("fill_stroke", (int)tgpw->is_fill_stroke);
/* draw stroke curve */
GPU_line_width(max_ff(curpressure * thickness, 1.0f));
@ -733,23 +736,22 @@ static void gp_draw_stroke_3d(tGPDdraw *tgpw, short thickness, const float ink[4
/* first point for adjacency (not drawn) */
if (i == 0) {
gp_set_point_varying_color(points, ink, attr_id.color);
immAttr1f(attr_id.thickness, max_ff(curpressure * thickness, 1.0f));
if ((cyclic) && (totpoints > 2)) {
immAttr1f(attr_id.thickness, max_ff((points + totpoints - 1)->pressure * thickness, 1.0f));
mul_v3_m4v3(fpt, tgpw->diff_mat, &(points + totpoints - 1)->x);
}
else {
immAttr1f(attr_id.thickness, max_ff((points + 1)->pressure * thickness, 1.0f));
mul_v3_m4v3(fpt, tgpw->diff_mat, &(points + 1)->x);
}
mul_v3_fl(fpt, -1.0f);
immVertex3fv(attr_id.pos, fpt);
}
/* set point */
gp_set_point_varying_color(pt, ink, attr_id.color);
immAttr1f(attr_id.thickness, max_ff(curpressure * thickness, 1.0f));
immAttr1f(attr_id.thickness, max_ff(pt->pressure * thickness, 1.0f));
mul_v3_m4v3(fpt, tgpw->diff_mat, &pt->x);
immVertex3fv(attr_id.pos, fpt);
curpressure = pt->pressure;
}
if (cyclic && totpoints > 2) {
@ -765,10 +767,9 @@ static void gp_draw_stroke_3d(tGPDdraw *tgpw, short thickness, const float ink[4
}
/* last adjacency point (not drawn) */
else {
gp_set_point_varying_color(points + totpoints - 1, ink, attr_id.color);
immAttr1f(attr_id.thickness, max_ff(curpressure * thickness, 1.0f));
gp_set_point_varying_color(points + totpoints - 2, ink, attr_id.color);
immAttr1f(attr_id.thickness, max_ff((points + totpoints - 2)->pressure * thickness, 1.0f));
mul_v3_m4v3(fpt, tgpw->diff_mat, &(points + totpoints - 2)->x);
mul_v3_fl(fpt, -1.0f);
immVertex3fv(attr_id.pos, fpt);
}
@ -1029,6 +1030,10 @@ static void gp_draw_strokes(tGPDdraw *tgpw)
/* calculate thickness */
sthickness = gps->thickness + tgpw->lthick;
if (tgpw->is_fill_stroke) {
sthickness = (short)max_ii(1, sthickness / 2);
}
if (sthickness <= 0) {
continue;
}
@ -1427,6 +1432,7 @@ static void gp_draw_data_layers(RegionView3D *rv3d,
tgpw.winx = winx;
tgpw.winy = winy;
tgpw.dflag = dflag;
tgpw.is_fill_stroke = false;
for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
/* calculate parent position */

View File

@ -176,18 +176,54 @@ static void gpsculpt_compute_lock_axis(tGP_BrushEditData *gso, bGPDspoint *pt, c
return;
}
ToolSettings *ts = gso->scene->toolsettings;
int axis = ts->gp_sculpt.lock_axis;
const ToolSettings *ts = gso->scene->toolsettings;
const View3DCursor *cursor = &gso->scene->cursor;
const int axis = ts->gp_sculpt.lock_axis;
/* lock axis control */
if (axis == 1) {
pt->x = save_pt[0];
}
if (axis == 2) {
pt->y = save_pt[1];
}
if (axis == 3) {
pt->z = save_pt[2];
switch (axis) {
case GP_LOCKAXIS_X:
{
pt->x = save_pt[0];
break;
}
case GP_LOCKAXIS_Y:
{
pt->y = save_pt[1];
break;
}
case GP_LOCKAXIS_Z:
{
pt->z = save_pt[2];
break;
}
case GP_LOCKAXIS_CURSOR:
{
/* compute a plane with cursor normal and position of the point
before do the sculpt */
const float scale[3] = { 1.0f, 1.0f, 1.0f };
float plane_normal[3] = { 0.0f, 0.0f, 1.0f };
float plane[4];
float mat[4][4];
float r_close[3];
loc_eul_size_to_mat4(mat,
cursor->location,
cursor->rotation_euler,
scale);
mul_mat3_m4_v3(mat, plane_normal);
plane_from_point_normal_v3(plane, save_pt, plane_normal);
/* find closest point to the plane with the new position */
closest_to_plane_v3(r_close, plane, &pt->x);
copy_v3_v3(&pt->x, r_close);
break;
}
default:
{
break;
}
}
}

View File

@ -3280,6 +3280,8 @@ typedef enum eGP_ReprojectModes {
GP_REPROJECT_VIEW,
/* Reprojected on to the scene geometry */
GP_REPROJECT_SURFACE,
/* Reprojected on 3D cursor orientation */
GP_REPROJECT_CURSOR,
} eGP_ReprojectModes;
static int gp_strokes_reproject_exec(bContext *C, wmOperator *op)
@ -3334,10 +3336,17 @@ static int gp_strokes_reproject_exec(bContext *C, wmOperator *op)
gp_point_to_xy_fl(&gsc, gps, &pt2, &xy[0], &xy[1]);
/* Project stroke in one axis */
if (ELEM(mode, GP_REPROJECT_FRONT, GP_REPROJECT_SIDE, GP_REPROJECT_TOP)) {
ED_gp_get_drawing_reference(
scene, ob, gpl,
ts->gpencil_v3d_align, origin);
if (ELEM(mode, GP_REPROJECT_FRONT, GP_REPROJECT_SIDE,
GP_REPROJECT_TOP, GP_REPROJECT_CURSOR))
{
if (mode != GP_REPROJECT_CURSOR) {
ED_gp_get_drawing_reference(
scene, ob, gpl,
ts->gpencil_v3d_align, origin);
}
else {
copy_v3_v3(origin, scene->cursor.location);
}
int axis = 0;
switch (mode) {
@ -3356,6 +3365,11 @@ static int gp_strokes_reproject_exec(bContext *C, wmOperator *op)
axis = 2;
break;
}
case GP_REPROJECT_CURSOR:
{
axis = 3;
break;
}
default:
{
axis = 1;
@ -3364,8 +3378,8 @@ static int gp_strokes_reproject_exec(bContext *C, wmOperator *op)
}
ED_gp_project_point_to_plane(
ob, rv3d, origin,
axis, &pt2);
scene, ob, rv3d, origin,
axis, &pt2);
copy_v3_v3(&pt->x, &pt2.x);
@ -3427,6 +3441,8 @@ void GPENCIL_OT_reproject(wmOperatorType *ot)
"using 'Cursor' Stroke Placement"},
{GP_REPROJECT_SURFACE, "SURFACE", 0, "Surface",
"Reproject the strokes on to the scene geometry, as if drawn using 'Surface' placement"},
{GP_REPROJECT_CURSOR, "CURSOR", 0, "Cursor",
"Reproject the strokes using the orienation of 3D cursor"},
{0, NULL, 0, NULL, NULL},
};

View File

@ -132,6 +132,8 @@ typedef struct tGPDfill {
int fill_simplylvl;
/** boundary limits drawing mode */
int fill_draw_mode;
/* scaling factor */
short fill_factor;
/** number of elements currently in cache */
short sbuffer_size;
@ -146,6 +148,13 @@ typedef struct tGPDfill {
BLI_Stack *stack;
/** handle for drawing strokes while operator is running 3d stuff */
void *draw_handle_3d;
/* tmp size x */
int bwinx;
/* tmp size y */
int bwiny;
rcti brect;
} tGPDfill;
@ -231,6 +240,7 @@ static void gp_draw_datablock(tGPDfill *tgpf, const float ink[4])
tgpw.dflag = 0;
tgpw.disable_fill = 1;
tgpw.dflag |= (GP_DRAWFILLS_ONLY3D | GP_DRAWFILLS_NOSTATUS);
glEnable(GL_BLEND);
@ -271,23 +281,27 @@ static void gp_draw_datablock(tGPDfill *tgpf, const float ink[4])
tgpw.t_gpf = gpf;
/* reduce thickness to avoid gaps */
tgpw.lthick = gpl->line_change - 4;
tgpw.is_fill_stroke = (tgpf->fill_draw_mode == GP_FILL_DMODE_CONTROL) ? false : true ;
tgpw.lthick = gpl->line_change;
tgpw.opacity = 1.0;
copy_v4_v4(tgpw.tintcolor, ink);
tgpw.onion = true;
tgpw.custonion = true;
bool textured_stroke = (gp_style->stroke_style == GP_STYLE_STROKE_STYLE_TEXTURE);
/* normal strokes */
if ((tgpf->fill_draw_mode == GP_FILL_DMODE_STROKE) ||
(tgpf->fill_draw_mode == GP_FILL_DMODE_BOTH))
if (((tgpf->fill_draw_mode == GP_FILL_DMODE_STROKE) ||
(tgpf->fill_draw_mode == GP_FILL_DMODE_BOTH)) &&
!textured_stroke)
{
ED_gp_draw_fill(&tgpw);
}
/* 3D Lines with basic shapes and invisible lines */
if ((tgpf->fill_draw_mode == GP_FILL_DMODE_CONTROL) ||
(tgpf->fill_draw_mode == GP_FILL_DMODE_BOTH))
(tgpf->fill_draw_mode == GP_FILL_DMODE_BOTH) ||
textured_stroke)
{
gp_draw_basic_stroke(
tgpf, gps, tgpw.diff_mat, gps->flag & GP_STROKE_CYCLIC, ink,
@ -300,17 +314,45 @@ static void gp_draw_datablock(tGPDfill *tgpf, const float ink[4])
}
/* draw strokes in offscreen buffer */
static void gp_render_offscreen(tGPDfill *tgpf)
static bool gp_render_offscreen(tGPDfill *tgpf)
{
bool is_ortho = false;
float winmat[4][4];
if (!tgpf->gpd) {
return;
return false;
}
/* set temporary new size */
tgpf->bwinx = tgpf->ar->winx;
tgpf->bwiny = tgpf->ar->winy;
tgpf->brect = tgpf->ar->winrct;
/* resize ar */
tgpf->ar->winrct.xmin = 0;
tgpf->ar->winrct.ymin = 0;
tgpf->ar->winrct.xmax = (int)tgpf->ar->winx * tgpf->fill_factor;
tgpf->ar->winrct.ymax = (int)tgpf->ar->winy * tgpf->fill_factor;
tgpf->ar->winx = (short)abs(tgpf->ar->winrct.xmax - tgpf->ar->winrct.xmin);
tgpf->ar->winy = (short)abs(tgpf->ar->winrct.ymax - tgpf->ar->winrct.ymin);
/* save new size */
tgpf->sizex = (int)tgpf->ar->winx;
tgpf->sizey = (int)tgpf->ar->winy;
/* adjust center */
float center[2];
center[0] = (float)tgpf->center[0] * ((float)tgpf->ar->winx / (float)tgpf->bwinx);
center[1] = (float)tgpf->center[1] * ((float)tgpf->ar->winy / (float)tgpf->bwiny);
round_v2i_v2fl(tgpf->center, center);
char err_out[256] = "unknown";
GPUOffScreen *offscreen = GPU_offscreen_create(tgpf->sizex, tgpf->sizey, 0, true, false, err_out);
if (offscreen == NULL) {
printf("GPencil - Fill - Unable to create fill buffer\n");
return false;
}
GPU_offscreen_bind(offscreen, true);
uint flag = IB_rect | IB_rectfloat;
ImBuf *ibuf = IMB_allocImBuf(tgpf->sizex, tgpf->sizey, 32, flag);
@ -328,18 +370,6 @@ static void gp_render_offscreen(tGPDfill *tgpf)
perspective_m4(winmat, viewplane.xmin, viewplane.xmax, viewplane.ymin, viewplane.ymax, clip_start, clip_end);
}
/* set temporary new size */
int bwinx = tgpf->ar->winx;
int bwiny = tgpf->ar->winy;
rcti brect = tgpf->ar->winrct;
tgpf->ar->winx = (short)tgpf->sizex;
tgpf->ar->winy = (short)tgpf->sizey;
tgpf->ar->winrct.xmin = 0;
tgpf->ar->winrct.ymin = 0;
tgpf->ar->winrct.xmax = tgpf->sizex;
tgpf->ar->winrct.ymax = tgpf->sizey;
GPU_matrix_push_projection();
GPU_matrix_identity_set();
GPU_matrix_push();
@ -359,11 +389,6 @@ static void gp_render_offscreen(tGPDfill *tgpf)
float ink[4] = {1.0f, 0.0f, 0.0f, 1.0f};
gp_draw_datablock(tgpf, ink);
/* restore size */
tgpf->ar->winx = (short)bwinx;
tgpf->ar->winy = (short)bwiny;
tgpf->ar->winrct = brect;
GPU_matrix_pop_projection();
GPU_matrix_pop();
@ -386,6 +411,8 @@ static void gp_render_offscreen(tGPDfill *tgpf)
/* switch back to window-system-provided framebuffer */
GPU_offscreen_unbind(offscreen, true);
GPU_offscreen_free(offscreen);
return true;
}
/* return pixel data (rgba) at index */
@ -459,7 +486,8 @@ static bool is_leak_narrow(ImBuf *ibuf, const int maxpixel, int limit, int index
}
}
else {
t_a = true; /* edge of image*/
/* edge of image*/
t_a = true;
break;
}
}
@ -474,7 +502,8 @@ static bool is_leak_narrow(ImBuf *ibuf, const int maxpixel, int limit, int index
}
}
else {
t_b = true; /* edge of image*/
/* edge of image*/
t_b = true;
break;
}
}
@ -543,7 +572,7 @@ static void gpencil_boundaryfill_area(tGPDfill *tgpf)
/* calculate index of the seed point using the position of the mouse */
int index = (tgpf->sizex * tgpf->center[1]) + tgpf->center[0];
if ((index >= 0) && (index < maxpixel)) {
if ((index >= 0) && (index <= maxpixel)) {
BLI_stack_push(stack, &index);
}
@ -561,6 +590,7 @@ static void gpencil_boundaryfill_area(tGPDfill *tgpf)
*/
while (!BLI_stack_is_empty(stack)) {
int v;
BLI_stack_pop(stack, &v);
get_pixel(ibuf, v, rgba);
@ -568,7 +598,7 @@ static void gpencil_boundaryfill_area(tGPDfill *tgpf)
if (true) { /* Was: 'rgba' */
/* check if no border(red) or already filled color(green) */
if ((rgba[0] != 1.0f) && (rgba[1] != 1.0f)) {
/* fill current pixel */
/* fill current pixel with green */
set_pixel(ibuf, v, fill_col);
/* add contact pixels */
@ -580,22 +610,22 @@ static void gpencil_boundaryfill_area(tGPDfill *tgpf)
}
}
/* pixel right */
if (v + 1 < maxpixel) {
if (v + 1 <= maxpixel) {
index = v + 1;
if (!is_leak_narrow(ibuf, maxpixel, tgpf->fill_leak, v, LEAK_HORZ)) {
BLI_stack_push(stack, &index);
}
}
/* pixel top */
if (v + tgpf->sizex < maxpixel) {
index = v + tgpf->sizex;
if (v + ibuf->x <= maxpixel) {
index = v + ibuf->x;
if (!is_leak_narrow(ibuf, maxpixel, tgpf->fill_leak, v, LEAK_VERT)) {
BLI_stack_push(stack, &index);
}
}
/* pixel bottom */
if (v - tgpf->sizex >= 0) {
index = v - tgpf->sizex;
if (v - ibuf->x >= 0) {
index = v - ibuf->x;
if (!is_leak_narrow(ibuf, maxpixel, tgpf->fill_leak, v, LEAK_VERT)) {
BLI_stack_push(stack, &index);
}
@ -625,7 +655,7 @@ static void gpencil_clean_borders(tGPDfill *tgpf)
int pixel = 0;
/* horizontal lines */
for (idx = 0; idx < ibuf->x - 1; idx++) {
for (idx = 0; idx < ibuf->x; idx++) {
/* bottom line */
set_pixel(ibuf, idx, fill_col);
/* top line */
@ -649,12 +679,117 @@ static void gpencil_clean_borders(tGPDfill *tgpf)
tgpf->ima->id.tag |= LIB_TAG_DOIT;
}
/* Naive dilate
*
* Expand green areas into enclosing red areas.
* Using stack prevents creep when replacing colors directly.
* -----------
* XXXXXXX
* XoooooX
* XXooXXX
* XXXX
* -----------
*/
static void dilate(ImBuf *ibuf)
{
BLI_Stack *stack = BLI_stack_new(sizeof(int), __func__);
const float green[4] = { 0.0f, 1.0f, 0.0f, 1.0f };
const int maxpixel = (ibuf->x * ibuf->y) - 1;
/* detect pixels and expand into red areas */
for (int v = maxpixel; v != 0; v--) {
float color[4];
int index;
int tp = 0;
int bm = 0;
int lt = 0;
int rt = 0;
get_pixel(ibuf, v, color);
if (color[1] == 1.0f) {
/* pixel left */
if (v - 1 >= 0) {
index = v - 1;
get_pixel(ibuf, index, color);
if (color[0] == 1.0f) {
BLI_stack_push(stack, &index);
lt = index;
}
}
/* pixel right */
if (v + 1 <= maxpixel) {
index = v + 1;
get_pixel(ibuf, index, color);
if (color[0] == 1.0f) {
BLI_stack_push(stack, &index);
rt = index;
}
}
/* pixel top */
if (v + ibuf->x <= maxpixel) {
index = v + ibuf->x;
get_pixel(ibuf, index, color);
if (color[0] == 1.0f) {
BLI_stack_push(stack, &index);
tp = index;
}
}
/* pixel bottom */
if (v - ibuf->x >= 0) {
index = v - ibuf->x;
get_pixel(ibuf, index, color);
if (color[0] == 1.0f) {
BLI_stack_push(stack, &index);
bm = index;
}
}
/* pixel top-left */
if (tp && lt) {
index = tp - 1;
get_pixel(ibuf, index, color);
if (color[0] == 1.0f) {
BLI_stack_push(stack, &index);
}
}
/* pixel top-right */
if (tp && rt) {
index = tp + 1;
get_pixel(ibuf, index, color);
if (color[0] == 1.0f) {
BLI_stack_push(stack, &index);
}
}
/* pixel bottom-left */
if (bm && lt) {
index = bm - 1;
get_pixel(ibuf, index, color);
if (color[0] == 1.0f) {
BLI_stack_push(stack, &index);
}
}
/* pixel bottom-right */
if (bm && rt) {
index = bm + 1;
get_pixel(ibuf, index, color);
if (color[0] == 1.0f) {
BLI_stack_push(stack, &index);
}
}
}
}
/* set dilated pixels */
while (!BLI_stack_is_empty(stack)) {
int v;
BLI_stack_pop(stack, &v);
set_pixel(ibuf, v, green);
}
BLI_stack_free(stack);
}
/* Get the outline points of a shape using Moore Neighborhood algorithm
*
* This is a Blender customized version of the general algorithm described
* in https://en.wikipedia.org/wiki/Moore_neighborhood
*/
static void gpencil_get_outline_points(tGPDfill *tgpf)
static void gpencil_get_outline_points(tGPDfill *tgpf)
{
ImBuf *ibuf;
float rgba[4];
@ -686,6 +821,9 @@ static void gpencil_get_outline_points(tGPDfill *tgpf)
ibuf = BKE_image_acquire_ibuf(tgpf->ima, NULL, &lock);
int imagesize = ibuf->x * ibuf->y;
/* dilate */
dilate(ibuf);
/* find the initial point to start outline analysis */
for (int idx = imagesize - 1; idx != 0; idx--) {
get_pixel(ibuf, idx, rgba);
@ -835,9 +973,9 @@ static void gpencil_points_from_stack(tGPDfill *tgpf)
while (!BLI_stack_is_empty(tgpf->stack)) {
int v[2];
BLI_stack_pop(tgpf->stack, &v);
point2D->x = v[0];
point2D->y = v[1];
copy_v2fl_v2i(&point2D->x, v);
/* shift points to center of pixel */
add_v2_fl(&point2D->x, 0.5f);
point2D->pressure = 1.0f;
point2D->strength = 1.0f;
point2D->time = 0.0f;
@ -962,7 +1100,7 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf)
tgpf->scene, tgpf->ob, tgpf->gpl,
ts->gpencil_v3d_align, origin);
ED_gp_project_stroke_to_plane(
tgpf->ob, tgpf->rv3d, gps, origin,
tgpf->scene, tgpf->ob, tgpf->rv3d, gps, origin,
tgpf->lock_axis - 1);
}
@ -1076,7 +1214,8 @@ static tGPDfill *gp_session_init_fill(bContext *C, wmOperator *UNUSED(op))
tgpf->fill_threshold = brush->gpencil_settings->fill_threshold;
tgpf->fill_simplylvl = brush->gpencil_settings->fill_simplylvl;
tgpf->fill_draw_mode = brush->gpencil_settings->fill_draw_mode;
tgpf->fill_factor = (short)max_ii(1, min_ii((int)brush->gpencil_settings->fill_factor,8));
/* get color info */
Material *ma = BKE_gpencil_get_material_from_brush(brush);
/* if no brush defaults, get material and color info */
@ -1238,34 +1377,38 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event)
in_bounds = BLI_rcti_isect_pt(&ar->winrct, event->x, event->y);
if ((in_bounds) && (ar->regiontype == RGN_TYPE_WINDOW)) {
/* TODO GPXX: Verify the mouse click is right for any window size */
tgpf->center[0] = event->mval[0];
tgpf->center[1] = event->mval[1];
/* save size */
tgpf->sizex = ar->winx;
tgpf->sizey = ar->winy;
/* render screen to temp image */
gp_render_offscreen(tgpf);
if ( gp_render_offscreen(tgpf) ) {
/* apply boundary fill */
gpencil_boundaryfill_area(tgpf);
/* apply boundary fill */
gpencil_boundaryfill_area(tgpf);
/* clean borders to avoid infinite loops */
gpencil_clean_borders(tgpf);
/* clean borders to avoid infinite loops */
gpencil_clean_borders(tgpf);
/* analyze outline */
gpencil_get_outline_points(tgpf);
/* analyze outline */
gpencil_get_outline_points(tgpf);
/* create array of points from stack */
gpencil_points_from_stack(tgpf);
/* create array of points from stack */
gpencil_points_from_stack(tgpf);
/* create z-depth array for reproject */
gpencil_get_depth_array(tgpf);
/* create z-depth array for reproject */
gpencil_get_depth_array(tgpf);
/* create stroke and reproject */
gpencil_stroke_from_buffer(tgpf);
/* create stroke and reproject */
gpencil_stroke_from_buffer(tgpf);
}
/* restore size */
tgpf->ar->winx = (short)tgpf->bwinx;
tgpf->ar->winy = (short)tgpf->bwiny;
tgpf->ar->winrct = tgpf->brect;
/* free temp stack data */
if (tgpf->stack) {

View File

@ -92,6 +92,7 @@ typedef struct tGPDdraw {
float tintcolor[4]; /* tint color */
bool onion; /* onion flag */
bool custonion; /* use custom onion colors */
bool is_fill_stroke; /* use fill tool */
float diff_mat[4][4]; /* matrix */
} tGPDdraw;

View File

@ -398,7 +398,9 @@ static void gp_reproject_toplane(tGPsdata *p, bGPDstroke *gps)
/* get drawing origin */
gp_get_3d_reference(p, origin);
ED_gp_project_stroke_to_plane(obact, rv3d, gps, origin, p->lock_axis - 1);
ED_gp_project_stroke_to_plane(
p->scene, obact, rv3d, gps,
origin, p->lock_axis - 1);
}
/* convert screen-coordinates to buffer-coordinates */
@ -752,11 +754,11 @@ static short gp_stroke_addpoint(
gp_get_3d_reference(p, origin);
/* reproject current */
ED_gpencil_tpoint_to_point(p->ar, origin, pt, &spt);
ED_gp_project_point_to_plane(obact, rv3d, origin, p->lock_axis - 1, &spt);
ED_gp_project_point_to_plane(p->scene, obact, rv3d, origin, p->lock_axis - 1, &spt);
/* reproject previous */
ED_gpencil_tpoint_to_point(p->ar, origin, ptb, &spt2);
ED_gp_project_point_to_plane(obact, rv3d, origin, p->lock_axis - 1, &spt2);
ED_gp_project_point_to_plane(p->scene, obact, rv3d, origin, p->lock_axis - 1, &spt2);
p->totpixlen += len_v3v3(&spt.x, &spt2.x) / pixsize;
pt->uv_fac = p->totpixlen;
if ((gp_style) && (gp_style->sima)) {

View File

@ -930,11 +930,13 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
ts->gpencil_v3d_align, origin);
/* reproject current */
ED_gpencil_tpoint_to_point(tgpi->ar, origin, tpt, &spt);
ED_gp_project_point_to_plane(tgpi->ob, tgpi->rv3d, origin, tgpi->lock_axis - 1, &spt);
ED_gp_project_point_to_plane(tgpi->scene, tgpi->ob, tgpi->rv3d,
origin, tgpi->lock_axis - 1, &spt);
/* reproject previous */
ED_gpencil_tpoint_to_point(tgpi->ar, origin, tptb, &spt2);
ED_gp_project_point_to_plane(tgpi->ob, tgpi->rv3d, origin, tgpi->lock_axis - 1, &spt2);
ED_gp_project_point_to_plane(tgpi->scene, tgpi->ob, tgpi->rv3d,
origin, tgpi->lock_axis - 1, &spt2);
tgpi->totpixlen += len_v3v3(&spt.x, &spt2.x) / pixsize;
tpt->uv_fac = tgpi->totpixlen;
if ((gp_style) && (gp_style->sima)) {
@ -993,7 +995,8 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
tgpi->scene, tgpi->ob, tgpi->gpl,
ts->gpencil_v3d_align, origin);
ED_gp_project_stroke_to_plane(
tgpi->ob, tgpi->rv3d, gps, origin, ts->gp_sculpt.lock_axis - 1);
tgpi->scene, tgpi->ob, tgpi->rv3d, gps,
origin, ts->gp_sculpt.lock_axis - 1);
}
/* if parented change position relative to parent object */

View File

@ -937,8 +937,12 @@ void ED_gpencil_project_stroke_to_view(bContext *C, bGPDlayer *gpl, bGPDstroke *
* Reproject all points of the stroke to a plane locked to axis to avoid stroke offset
*/
void ED_gp_project_stroke_to_plane(
const Object *ob, const RegionView3D *rv3d, bGPDstroke *gps, const float origin[3], const int axis)
const Scene *scene, const Object *ob,
const RegionView3D *rv3d, bGPDstroke *gps,
const float origin[3], const int axis)
{
const ToolSettings *ts = scene->toolsettings;
const View3DCursor *cursor = &scene->cursor;
float plane_normal[3];
float vn[3];
@ -953,13 +957,37 @@ void ED_gp_project_stroke_to_plane(
*/
ED_view3d_global_to_vector(rv3d, origin, plane_normal);
}
else {
else if (axis < 3) {
plane_normal[axis] = 1.0f;
/* if object, apply object rotation */
if (ob && (ob->type == OB_GPENCIL)) {
mul_mat3_m4_v3(ob->obmat, plane_normal);
float mat[4][4];
copy_m4_m4(mat, ob->obmat);
/* move origin to cursor */
if (ts->gpencil_v3d_align & GP_PROJECT_CURSOR) {
copy_v3_v3(mat[3], cursor->location);
}
mul_mat3_m4_v3(mat, plane_normal);
}
}
else {
float scale[3] = { 1.0f, 1.0f, 1.0f };
plane_normal[2] = 1.0f;
float mat[4][4];
loc_eul_size_to_mat4(mat,
cursor->location,
cursor->rotation_euler,
scale);
/* move origin to object */
if ((ts->gpencil_v3d_align & GP_PROJECT_CURSOR) == 0) {
copy_v3_v3(mat[3], ob->obmat[3]);
}
mul_mat3_m4_v3(mat, plane_normal);
}
/* Reproject the points in the plane */
for (int i = 0; i < gps->totpoints; i++) {
@ -984,8 +1012,12 @@ void ED_gp_project_stroke_to_plane(
* \param[in, out] pt : Point to affect
*/
void ED_gp_project_point_to_plane(
const Object *ob, const RegionView3D *rv3d, const float origin[3], const int axis, bGPDspoint *pt)
const Scene *scene, const Object *ob,
const RegionView3D *rv3d, const float origin[3],
const int axis, bGPDspoint *pt)
{
const ToolSettings *ts = scene->toolsettings;
const View3DCursor *cursor = &scene->cursor;
float plane_normal[3];
float vn[3];
@ -1000,14 +1032,37 @@ void ED_gp_project_point_to_plane(
*/
ED_view3d_global_to_vector(rv3d, origin, plane_normal);
}
else {
else if (axis < 3) {
plane_normal[axis] = 1.0f;
/* if object, apply object rotation */
if (ob && (ob->type == OB_GPENCIL)) {
mul_mat3_m4_v3(ob->obmat, plane_normal);
float mat[4][4];
copy_m4_m4(mat, ob->obmat);
/* move origin to cursor */
if (ts->gpencil_v3d_align & GP_PROJECT_CURSOR) {
copy_v3_v3(mat[3], cursor->location);
}
mul_mat3_m4_v3(mat, plane_normal);
}
}
else {
float scale[3] = { 1.0f, 1.0f, 1.0f };
plane_normal[2] = 1.0f;
float mat[4][4];
loc_eul_size_to_mat4(mat,
cursor->location,
cursor->rotation_euler,
scale);
/* move origin to object */
if ((ts->gpencil_v3d_align & GP_PROJECT_CURSOR) == 0) {
copy_v3_v3(mat[3], ob->obmat[3]);
}
mul_mat3_m4_v3(mat, plane_normal);
}
/* Reproject the points in the plane */
/* get a vector from the point with the current view direction of the viewport */

View File

@ -225,9 +225,11 @@ void ED_gpencil_add_defaults(struct bContext *C, struct Object *ob);
void ED_gpencil_setup_modes(struct bContext *C, struct bGPdata *gpd, int newmode);
void ED_gp_project_stroke_to_plane(
const struct Scene *scene,
const struct Object *ob, const struct RegionView3D *rv3d,
struct bGPDstroke *gps, const float origin[3], const int axis);
void ED_gp_project_point_to_plane(
const struct Scene *scene,
const struct Object *ob, const struct RegionView3D *rv3d,
const float origin[3], const int axis, struct bGPDspoint *pt);
void ED_gp_get_drawing_reference(

View File

@ -1,6 +1,9 @@
uniform mat4 ModelViewProjectionMatrix;
uniform vec2 Viewport;
uniform int xraymode;
uniform int caps_start;
uniform int caps_end;
uniform int fill_stroke;
layout(lines_adjacency) in;
layout(triangle_strip, max_vertices = 13) out;
@ -15,6 +18,8 @@ out vec2 mTexCoord;
#define GP_XRAY_3DSPACE 1
#define GP_XRAY_BACK 2
#define GPENCIL_FLATCAP 1
/* project 3d point to 2d on screen space */
vec2 toScreenSpace(vec4 vertex)
{
@ -37,6 +42,22 @@ float getZdepth(vec4 point)
/* in front by default */
return 0.0;
}
/* check equality but with a small tolerance */
bool is_equal(vec4 p1, vec4 p2)
{
float limit = 0.0001;
float x = abs(p1.x - p2.x);
float y = abs(p1.y - p2.y);
float z = abs(p1.z - p2.z);
if ((x < limit) && (y < limit) && (z < limit)) {
return true;
}
return false;
}
void main(void)
{
float MiterLimit = 0.75;
@ -134,10 +155,11 @@ void main(void)
}
/* generate the start endcap (alpha < 0 used as endcap flag)*/
if (P0 == P2) {
float extend = (fill_stroke > 0) ? 2 : 1 ;
if ((caps_start != GPENCIL_FLATCAP) && is_equal(P0,P2)) {
mTexCoord = vec2(1, 0.5);
mColor = vec4(finalColor[1].rgb, finalColor[1].a * -1.0) ;
vec2 svn1 = normalize(sp1 - sp2) * length_a * 4.0;
vec2 svn1 = normalize(sp1 - sp2) * length_a * 4.0 * extend;
gl_Position = vec4((sp1 + svn1) / Viewport, getZdepth(P1), 1.0);
EmitVertex();
@ -174,7 +196,7 @@ void main(void)
EmitVertex();
/* generate the end endcap (alpha < 0 used as endcap flag)*/
if (P1 == P3) {
if ((caps_end != GPENCIL_FLATCAP) && is_equal(P1,P3)) {
mTexCoord = vec2(0, 1);
mColor = vec4(finalColor[2].rgb, finalColor[2].a * -1.0) ;
gl_Position = vec4((sp2 + (length_b * 2.0) * miter_b) / Viewport, getZdepth(P2), 1.0);
@ -187,7 +209,7 @@ void main(void)
mTexCoord = vec2(1, 0.5);
mColor = vec4(finalColor[2].rgb, finalColor[2].a * -1.0) ;
vec2 svn2 = normalize(sp2 - sp1) * length_b * 4.0;
vec2 svn2 = normalize(sp2 - sp1) * length_b * 4.0 * extend;
gl_Position = vec4((sp2 + svn2) / Viewport, getZdepth(P2), 1.0);
EmitVertex();
}

View File

@ -4,7 +4,7 @@ uniform mat4 ProjectionMatrix;
uniform float pixsize; /* rv3d->pixsize */
uniform int keep_size;
uniform float objscale;
uniform int pixfactor;
uniform float pixfactor;
in vec3 pos;
in vec4 color;

View File

@ -83,23 +83,25 @@ typedef struct BrushGpencilSettings {
float fill_threshold;
/** Number of pixel to consider the leak is too small (x 2). */
short fill_leak;
char _pad1[6];
/** Fill zoom factor */
short fill_factor;
char _pad_1[4];
/** Number of simplify steps. */
int fill_simplylvl;
int fill_simplylvl;
/** Type of control lines drawing mode. */
int fill_draw_mode;
int fill_draw_mode;
/** Icon identifier. */
int icon_id;
int icon_id;
/** Maximum distance before generate new point for very fast mouse movements. */
int input_samples;
int input_samples;
/** Random factor for UV rotation. */
float uv_random;
/** Moved to 'Brush.gpencil_tool'. */
int brush_type DNA_DEPRECATED;
int brush_type DNA_DEPRECATED;
/** Soft, hard or stroke. */
int eraser_mode;
int eraser_mode;
/** Smooth while drawing factor. */
float active_smooth;
/** Factor to apply to strength for soft eraser. */

View File

@ -1053,6 +1053,7 @@ typedef enum eGP_Lockaxis_Types {
GP_LOCKAXIS_X = 1,
GP_LOCKAXIS_Y = 2,
GP_LOCKAXIS_Z = 3,
GP_LOCKAXIS_CURSOR = 4
} eGP_Lockaxis_Types;
/* Settings for a GPencil Stroke Sculpting Brush */

View File

@ -126,10 +126,10 @@ static EnumPropertyItem rna_enum_gpencil_brush_eraser_modes_items[] = {
};
static EnumPropertyItem rna_enum_gpencil_fill_draw_modes_items[] = {
{GP_FILL_DMODE_BOTH, "BOTH", 0, "Default", "Use both visible strokes and edit lines as fill boundary limits"},
{GP_FILL_DMODE_STROKE, "STROKE", 0, "Strokes", "Use visible strokes as fill boundary limits"},
{GP_FILL_DMODE_CONTROL, "CONTROL", 0, "Control", "Use internal control lines as fill boundary limits"},
{GP_FILL_DMODE_BOTH, "BOTH", 0, "Both", "Use visible strokes and control lines as fill boundary limits"},
{0, NULL, 0, NULL, NULL},
{GP_FILL_DMODE_CONTROL, "CONTROL", 0, "Edit Lines", "Use edit lines as fill boundary limits"},
{0, NULL, 0, NULL, NULL}
};
static EnumPropertyItem rna_enum_gpencil_brush_icons_items[] = {
@ -1160,6 +1160,15 @@ static void rna_def_gpencil_options(BlenderRNA *brna)
RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0);
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
/* fill factor size */
prop = RNA_def_property(srna, "fill_factor", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "fill_factor");
RNA_def_property_range(prop, 1, 8);
RNA_def_property_ui_text(prop, "Resolution",
"Multiplier for fill resolution, higher resolution is more accurate but slower");
RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0);
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
/* fill simplify steps */
prop = RNA_def_property(srna, "fill_simplify_level", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "fill_simplylvl");

View File

@ -82,6 +82,7 @@ static const EnumPropertyItem rna_enum_gpencil_lock_axis_items[] = {
{GP_LOCKAXIS_Y, "AXIS_Y", ICON_AXIS_FRONT, "Front (X-Z)", "Project strokes to plane locked to Y"},
{GP_LOCKAXIS_X, "AXIS_X", ICON_AXIS_SIDE, "Side (Y-Z)", "Project strokes to plane locked to X"},
{GP_LOCKAXIS_Z, "AXIS_Z", ICON_AXIS_TOP, "Top (X-Y)", "Project strokes to plane locked to Z"},
{GP_LOCKAXIS_CURSOR, "CURSOR", ICON_PIVOT_CURSOR, "Cursor", "Align strokes to current 3D cursor orientation"},
{0, NULL, 0, NULL, NULL},
};
#endif