GPencil: New conversion to outline in draw mode

This new option converts the stroke to outline perimeter as soon as is drawn.

If no alternative material is set, the actual material is used.

The algorithm is similar to the new operator in D15664

Reviewed By: pepeland

Differential Revision: https://developer.blender.org/D15738
This commit is contained in:
Antonio Vazquez 2022-08-29 09:47:08 +02:00
parent 377b81ac07
commit 613b6ad9e5
6 changed files with 104 additions and 11 deletions

View File

@ -1742,6 +1742,12 @@ class VIEW3D_PT_tools_grease_pencil_brush_post_processing(View3DPanel, Panel):
col1 = col.column(align=True)
col1.prop(gp_settings, "use_trim")
row = col.row(heading="Outline", align=True)
row.prop(gp_settings, "use_settings_outline", text="")
row2 = row.row(align=True)
row2.enabled = gp_settings.use_settings_outline
row2.prop(gp_settings, "material_alt", text="")
class VIEW3D_PT_tools_grease_pencil_brush_random(View3DPanel, Panel):
bl_context = ".greasepencil_paint"

View File

@ -186,6 +186,7 @@ static void brush_foreach_id(ID *id, LibraryForeachIDData *data)
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, brush->paint_curve, IDWALK_CB_USER);
if (brush->gpencil_settings) {
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, brush->gpencil_settings->material, IDWALK_CB_USER);
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, brush->gpencil_settings->material_alt, IDWALK_CB_USER);
}
BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, BKE_texture_mtex_foreach_id(data, &brush->mtex));
BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data,
@ -346,6 +347,7 @@ static void brush_blend_read_lib(BlendLibReader *reader, ID *id)
else {
brush->gpencil_settings->material = nullptr;
}
BLO_read_id_address(reader, brush->id.lib, &brush->gpencil_settings->material_alt);
}
}
@ -358,6 +360,7 @@ static void brush_blend_read_expand(BlendExpander *expander, ID *id)
BLO_expand(expander, brush->paint_curve);
if (brush->gpencil_settings != nullptr) {
BLO_expand(expander, brush->gpencil_settings->material);
BLO_expand(expander, brush->gpencil_settings->material_alt);
}
}
@ -704,6 +707,7 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type)
/* Set vertex mix factor. */
brush->gpencil_settings->vertex_mode = GPPAINT_MODE_BOTH;
brush->gpencil_settings->vertex_factor = 1.0f;
brush->gpencil_settings->material_alt = nullptr;
switch (type) {
case GP_BRUSH_PRESET_AIRBRUSH: {

View File

@ -918,6 +918,64 @@ static void gpencil_stroke_unselect(bGPdata *gpd, bGPDstroke *gps)
}
}
static bGPDstroke *gpencil_stroke_to_outline(tGPsdata *p, bGPDstroke *gps)
{
bGPDlayer *gpl = p->gpl;
RegionView3D *rv3d = p->region->regiondata;
Brush *brush = p->brush;
BrushGpencilSettings *gpencil_settings = brush->gpencil_settings;
MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(p->ob, gps->mat_nr + 1);
const bool is_stroke = ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0);
if (!is_stroke) {
return gps;
}
/* Duplicate the stroke to apply any layer thickness change. */
bGPDstroke *gps_duplicate = BKE_gpencil_stroke_duplicate(gps, true, false);
/* Apply layer thickness change. */
gps_duplicate->thickness += gpl->line_change;
/* Apply object scale to thickness. */
gps_duplicate->thickness *= mat4_to_scale(p->ob->obmat);
CLAMP_MIN(gps_duplicate->thickness, 1.0f);
/* Stroke. */
float diff_mat[4][4];
unit_m4(diff_mat);
bGPDstroke *gps_perimeter = BKE_gpencil_stroke_perimeter_from_view(
rv3d, p->gpd, gpl, gps_duplicate, 3, diff_mat);
/* Assign material. */
if (gpencil_settings->material_alt == NULL) {
gps_perimeter->mat_nr = gps->mat_nr;
}
else {
Material *ma = gpencil_settings->material_alt;
int mat_idx = BKE_gpencil_material_find_index_by_name_prefix(p->ob, ma->id.name + 2);
if (mat_idx > -1) {
gps_perimeter->mat_nr = mat_idx;
}
else {
gps_perimeter->mat_nr = gps->mat_nr;
}
}
/* Set pressure constant. */
bGPDspoint *pt;
for (int i = 0; i < gps_perimeter->totpoints; i++) {
pt = &gps_perimeter->points[i];
pt->pressure = 1.0f;
}
/* Remove original stroke. */
BKE_gpencil_free_stroke(gps);
/* Free Temp stroke. */
BKE_gpencil_free_stroke(gps_duplicate);
return gps_perimeter;
}
/* make a new stroke from the buffer data */
static void gpencil_stroke_newfrombuffer(tGPsdata *p)
{
@ -1221,6 +1279,23 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p)
BKE_gpencil_stroke_simplify_adaptive(gpd, gps, brush->gpencil_settings->simplify_f);
}
/* Set material index. */
gps->mat_nr = BKE_gpencil_object_material_get_index_from_brush(p->ob, p->brush);
if (gps->mat_nr < 0) {
if (p->ob->actcol - 1 < 0) {
gps->mat_nr = 0;
}
else {
gps->mat_nr = p->ob->actcol - 1;
}
}
/* Convert to Outline. */
if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) &&
(brush->gpencil_settings->flag & GP_BRUSH_OUTLINE_STROKE)) {
gps = gpencil_stroke_to_outline(p, gps);
}
/* reproject to plane (only in 3d space) */
gpencil_reproject_toplane(p, gps);
/* change position relative to parent object */
@ -1235,17 +1310,6 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p)
}
}
/* Save material index */
gps->mat_nr = BKE_gpencil_object_material_get_index_from_brush(p->ob, p->brush);
if (gps->mat_nr < 0) {
if (p->ob->actcol - 1 < 0) {
gps->mat_nr = 0;
}
else {
gps->mat_nr = p->ob->actcol - 1;
}
}
/* add stroke to frame, usually on tail of the listbase, but if on back is enabled the stroke
* is added on listbase head because the drawing order is inverse and the head stroke is the
* first to draw. This is very useful for artist when drawing the background.

View File

@ -87,6 +87,8 @@ typedef enum eGPDbrush_Flag {
GP_BRUSH_OCCLUDE_ERASER = (1 << 15),
/* Post process trim stroke */
GP_BRUSH_TRIM_STROKE = (1 << 16),
/* Post process convert to outline stroke */
GP_BRUSH_OUTLINE_STROKE = (1 << 17),
} eGPDbrush_Flag;
typedef enum eGPDbrush_Flag2 {

View File

@ -135,6 +135,8 @@ typedef struct BrushGpencilSettings {
/* optional link of material to replace default in context */
/** Material. */
struct Material *material;
/** Material Alternative for secondary operations. */
struct Material *material_alt;
} BrushGpencilSettings;
typedef struct BrushCurvesSculptSettings {

View File

@ -1820,6 +1820,12 @@ static void rna_def_gpencil_options(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Trim Stroke Ends", "Trim intersecting stroke ends");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
prop = RNA_def_property(srna, "use_settings_outline", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_OUTLINE_STROKE);
RNA_def_property_boolean_default(prop, false);
RNA_def_property_ui_text(prop, "Outline", "Convert stroke to perimeter");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
prop = RNA_def_property(srna, "direction", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, NULL, "sculpt_flag");
RNA_def_property_enum_items(prop, prop_direction_items);
@ -1883,6 +1889,15 @@ static void rna_def_gpencil_options(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_Brush_material_update");
/* Secondary Material */
prop = RNA_def_property(srna, "material_alt", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "Material");
RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_BrushGpencilSettings_material_poll");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK | PROP_CONTEXT_UPDATE);
RNA_def_property_ui_text(prop, "Material", "Material used for secondary uses for this brush");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_Brush_material_update");
prop = RNA_def_property(srna, "show_fill_boundary", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_FILL_SHOW_HELPLINES);
RNA_def_property_boolean_default(prop, true);