Sculpt: Support pen pressure modulation in Paint Brush properties

This allows to use pen pressure modulation in hardness, wet mix, wet
persistence, flow and density, as well as inverting the modulation (more
pressure, less density...). With this, it is possible to create brushes
that mix paint or apply a new color based on the pressure.

Reviewed By: sergey, campbellbarton

Differential Revision: https://developer.blender.org/D8267
This commit is contained in:
Pablo Dobarro 2020-07-20 00:37:41 +02:00
parent 4aa2a5481c
commit 69afdf6970
6 changed files with 189 additions and 17 deletions

View File

@ -544,7 +544,11 @@ def brush_settings(layout, context, brush, popover=False):
# normal_radius_factor
layout.prop(brush, "normal_radius_factor", slider=True)
layout.prop(brush, "hardness", slider=True)
row = layout.row(align=True)
row.prop(brush, "hardness", slider=True)
row.prop(brush, "invert_hardness_pressure", text = "")
row.prop(brush, "use_hardness_pressure", text = "")
# auto_smooth_factor and use_inverse_smooth_pressure
if capabilities.has_auto_smooth:
@ -674,13 +678,31 @@ def brush_settings(layout, context, brush, popover=False):
layout.prop(brush, "use_grab_active_vertex")
if brush.sculpt_tool == 'PAINT':
col = layout.column()
col.prop(brush, "flow")
col.prop(brush, "wet_mix")
col.prop(brush, "wet_persistence")
col.prop(brush, "density")
col.prop(brush, "tip_roundness")
col.prop(brush, "tip_scale_x")
row = layout.row(align=True)
row.prop(brush, "flow")
row.prop(brush, "invert_flow_pressure", text = "")
row.prop(brush, "use_flow_pressure", text= "")
row = layout.row(align=True)
row.prop(brush, "wet_mix")
row.prop(brush, "invert_wet_mix_pressure", text = "")
row.prop(brush, "use_wet_mix_pressure", text = "")
row = layout.row(align=True)
row.prop(brush, "wet_persistence")
row.prop(brush, "invert_wet_persistence_pressure", text ="")
row.prop(brush, "use_wet_persistence_pressure", text= "")
row = layout.row(align=True)
row.prop(brush, "density")
row.prop(brush, "invert_density_pressure", text = "")
row.prop(brush, "use_density_pressure", text = "")
row = layout.row()
row.prop(brush, "tip_roundness")
row = layout.row()
row.prop(brush, "tip_scale_x")
if brush.sculpt_tool == 'SMEAR':
col = layout.column()

View File

@ -2418,7 +2418,7 @@ float SCULPT_brush_strength_factor(SculptSession *ss,
/* Hardness. */
float final_len = len;
const float hardness = br->hardness;
const float hardness = cache->paint_brush.hardness;
float p = len / cache->radius;
if (p < hardness) {
final_len = 0.0f;
@ -6618,6 +6618,50 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru
}
}
static void sculpt_update_cache_paint_variants(StrokeCache *cache, const Brush *brush)
{
cache->paint_brush.hardness = brush->hardness;
if (brush->paint_flags & BRUSH_PAINT_HARDNESS_PRESSURE) {
cache->paint_brush.hardness *= brush->paint_flags & BRUSH_PAINT_HARDNESS_PRESSURE_INVERT ?
1.0f - cache->pressure :
cache->pressure;
}
cache->paint_brush.flow = brush->flow;
if (brush->paint_flags & BRUSH_PAINT_FLOW_PRESSURE) {
cache->paint_brush.flow *= brush->paint_flags & BRUSH_PAINT_FLOW_PRESSURE_INVERT ?
1.0f - cache->pressure :
cache->pressure;
}
cache->paint_brush.wet_mix = brush->wet_mix;
if (brush->paint_flags & BRUSH_PAINT_WET_MIX_PRESSURE) {
cache->paint_brush.wet_mix *= brush->paint_flags & BRUSH_PAINT_WET_MIX_PRESSURE_INVERT ?
1.0f - cache->pressure :
cache->pressure;
/* This makes wet mix more sensible in higher values, which allows to create brushes that have
* a wider pressure range were they only blend colors without applying too much of the brush
* color. */
cache->paint_brush.wet_mix = 1.0f - pow2f(1.0f - cache->paint_brush.wet_mix);
}
cache->paint_brush.wet_persistence = brush->wet_persistence;
if (brush->paint_flags & BRUSH_PAINT_WET_PERSISTENCE_PRESSURE) {
cache->paint_brush.wet_persistence = brush->paint_flags &
BRUSH_PAINT_WET_PERSISTENCE_PRESSURE_INVERT ?
1.0f - cache->pressure :
cache->pressure;
}
cache->paint_brush.density = brush->density;
if (brush->paint_flags & BRUSH_PAINT_DENSITY_PRESSURE) {
cache->paint_brush.density = brush->paint_flags & BRUSH_PAINT_DENSITY_PRESSURE_INVERT ?
1.0f - cache->pressure :
cache->pressure;
}
}
/* Initialize the stroke cache variants from operator properties. */
static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, PointerRNA *ptr)
{
@ -6684,6 +6728,8 @@ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, Po
cache->dyntopo_pixel_radius = ups->initial_pixel_radius;
}
sculpt_update_cache_paint_variants(cache, brush);
cache->radius_squared = cache->radius * cache->radius;
if (brush->flag & BRUSH_ANCHORED) {

View File

@ -827,6 +827,15 @@ typedef struct StrokeCache {
bool original;
float anchored_location[3];
/* Paint Brush. */
struct {
float hardness;
float flow;
float wet_mix;
float wet_persistence;
float density;
} paint_brush;
/* Pose brush */
struct SculptPoseIKChain *pose_ik_chain;

View File

@ -164,7 +164,7 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata,
/* Density. */
float noise = 1.0f;
const float density = brush->density;
const float density = ss->cache->paint_brush.density;
if (density < 1.0f) {
const float hash_noise = BLI_hash_int_01(ss->cache->density_seed * 1000 * vd.index);
if (hash_noise > density) {
@ -178,11 +178,12 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata,
float wet_mix_color[4];
float buffer_color[4];
mul_v4_v4fl(paint_color, brush_color, fade * brush->flow);
mul_v4_v4fl(wet_mix_color, data->wet_mix_sampled_color, fade * brush->flow);
mul_v4_v4fl(paint_color, brush_color, fade * ss->cache->paint_brush.flow);
mul_v4_v4fl(wet_mix_color, data->wet_mix_sampled_color, fade * ss->cache->paint_brush.flow);
/* Interpolate with the wet_mix color for wet paint mixing. */
blend_color_interpolate_float(paint_color, paint_color, wet_mix_color, brush->wet_mix);
blend_color_interpolate_float(
paint_color, paint_color, wet_mix_color, ss->cache->paint_brush.wet_mix);
blend_color_mix_float(color_buffer->color[vd.i], color_buffer->color[vd.i], paint_color);
/* Final mix over the original color using brush alpha. */
@ -305,7 +306,7 @@ void SCULPT_do_paint_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
/* Wet paint color sampling. */
float wet_color[4] = {0.0f};
if (brush->wet_mix > 0.0f) {
if (ss->cache->paint_brush.wet_mix > 0.0f) {
SculptThreadedTaskData task_data = {
.sd = sd,
.ob = ob,
@ -332,8 +333,10 @@ void SCULPT_do_paint_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
if (ss->cache->first_time) {
copy_v4_v4(ss->cache->wet_mix_prev_color, wet_color);
}
blend_color_interpolate_float(
wet_color, wet_color, ss->cache->wet_mix_prev_color, brush->wet_persistence);
blend_color_interpolate_float(wet_color,
wet_color,
ss->cache->wet_mix_prev_color,
ss->cache->paint_brush.wet_persistence);
copy_v4_v4(ss->cache->wet_mix_prev_color, wet_color);
CLAMP4(ss->cache->wet_mix_prev_color, 0.0f, 1.0f);
}

View File

@ -388,6 +388,19 @@ typedef enum eAutomasking_flag {
BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS = (1 << 3),
} eAutomasking_flag;
typedef enum ePaintBrush_flag {
BRUSH_PAINT_HARDNESS_PRESSURE = (1 << 0),
BRUSH_PAINT_HARDNESS_PRESSURE_INVERT = (1 << 1),
BRUSH_PAINT_FLOW_PRESSURE = (1 << 2),
BRUSH_PAINT_FLOW_PRESSURE_INVERT = (1 << 3),
BRUSH_PAINT_WET_MIX_PRESSURE = (1 << 4),
BRUSH_PAINT_WET_MIX_PRESSURE_INVERT = (1 << 5),
BRUSH_PAINT_WET_PERSISTENCE_PRESSURE = (1 << 6),
BRUSH_PAINT_WET_PERSISTENCE_PRESSURE_INVERT = (1 << 7),
BRUSH_PAINT_DENSITY_PRESSURE = (1 << 8),
BRUSH_PAINT_DENSITY_PRESSURE_INVERT = (1 << 9),
} ePaintBrush_flag;
typedef struct Brush {
ID id;
@ -454,6 +467,7 @@ typedef struct Brush {
float wet_persistence;
/** Density */
float density;
int paint_flags;
/** Tip Shape */
/* Factor that controls the shape of the brush tip by rounding the corners of a square. */
@ -480,7 +494,7 @@ typedef struct Brush {
/** Source for fill tool color gradient application. */
char gradient_fill_mode;
char _pad0[1];
char _pad0[5];
/** Projection shape (sphere, circle). */
char falloff_shape;

View File

@ -2283,6 +2283,84 @@ static void rna_def_brush(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Tip Scale X", "Scale of the brush tip in the X axis");
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "use_hardness_pressure", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "paint_flags", BRUSH_PAINT_HARDNESS_PRESSURE);
RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0);
RNA_def_property_ui_text(prop, "Use Pressure for Hardness", "Use pressure to modulate hardness");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "invert_hardness_pressure", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "paint_flags", BRUSH_PAINT_HARDNESS_PRESSURE_INVERT);
RNA_def_property_ui_icon(prop, ICON_ARROW_LEFTRIGHT, 0);
RNA_def_property_ui_text(
prop, "Invert Pressure for Hardness", "Invert the modulation of pressure in hardness");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "use_flow_pressure", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "paint_flags", BRUSH_PAINT_FLOW_PRESSURE);
RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0);
RNA_def_property_ui_text(prop, "Use Pressure for Flow", "Use pressure to modulate flow");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "invert_flow_pressure", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "paint_flags", BRUSH_PAINT_FLOW_PRESSURE_INVERT);
RNA_def_property_ui_icon(prop, ICON_ARROW_LEFTRIGHT, 0);
RNA_def_property_ui_text(
prop, "Invert Pressure for Flow", "Invert the modulation of pressure in flow");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "use_wet_mix_pressure", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "paint_flags", BRUSH_PAINT_WET_MIX_PRESSURE);
RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0);
RNA_def_property_ui_text(prop, "Use Pressure for Wet Mix", "Use pressure to modulate wet mix");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "invert_wet_mix_pressure", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "paint_flags", BRUSH_PAINT_WET_MIX_PRESSURE_INVERT);
RNA_def_property_ui_icon(prop, ICON_ARROW_LEFTRIGHT, 0);
RNA_def_property_ui_text(
prop, "Invert Pressure for Wet Mix", "Invert the modulation of pressure in wet mix");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "use_wet_persistence_pressure", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "paint_flags", BRUSH_PAINT_WET_PERSISTENCE_PRESSURE);
RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0);
RNA_def_property_ui_text(
prop, "Use Pressure for Wet Persistence", "Use pressure to modulate wet persistence");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "invert_wet_persistence_pressure", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(
prop, NULL, "paint_flags", BRUSH_PAINT_WET_PERSISTENCE_PRESSURE_INVERT);
RNA_def_property_ui_icon(prop, ICON_ARROW_LEFTRIGHT, 0);
RNA_def_property_ui_text(prop,
"Invert Pressure for Wet Persistence",
"Invert the modulation of pressure in wet persistence");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "use_density_pressure", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "paint_flags", BRUSH_PAINT_DENSITY_PRESSURE);
RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0);
RNA_def_property_ui_text(prop, "Use Pressure for Density", "Use pressure to modulate density");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "invert_density_pressure", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "paint_flags", BRUSH_PAINT_DENSITY_PRESSURE_INVERT);
RNA_def_property_ui_icon(prop, ICON_ARROW_LEFTRIGHT, 0);
RNA_def_property_ui_text(
prop, "Invert Pressure for Density", "Invert the modulation of pressure in density");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "dash_ratio", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "dash_ratio");
RNA_def_property_range(prop, 0.0f, 1.0f);