Sculpt: fix broken jitter and smooth

stabilize brush settings.
This commit is contained in:
Joseph Eagar 2021-10-07 19:44:38 -07:00
parent a1202d3ce1
commit f91161a707
4 changed files with 135 additions and 40 deletions

View File

@ -320,6 +320,12 @@ class UnifiedPaintPanel:
@staticmethod
def get_channel_value(context, brush, prop_name, toolsettings_only=False):
if context.mode != "SCULPT":
if prop_name in channel_name_map:
prop_name = channel_name_map[prop_name]
return getattr(brush, prop_name)
ch = brush.channels[prop_name]
if ch.inherit or toolsettings_only:
@ -864,7 +870,7 @@ class StrokePanel(BrushPanel):
col.prop(brush, "dash_ratio", text="Dash Ratio")
col.prop(brush, "dash_samples", text="Dash Length")
if (mode == 'SCULPT' and brush.sculpt_capabilities.has_jitter) or mode != 'SCULPT':
if mode != 'SCULPT':
col.separator()
row = col.row(align=True)
if brush.jitter_unit == 'BRUSH':
@ -873,6 +879,26 @@ class StrokePanel(BrushPanel):
row.prop(brush, "jitter_absolute")
row.prop(brush, "use_pressure_jitter", toggle=True, text="")
col.row().prop(brush, "jitter_unit", expand=True)
elif mode == 'SCULPT' and brush.sculpt_capabilities.has_jitter:
col.separator()
row = col.row(align=True)
if UnifiedPaintPanel.get_channel_value(context, brush, "jitter_unit") == 'BRUSH':
UnifiedPaintPanel.channel_unified(row,
context,
brush,
"jitter", slider=True)
else:
UnifiedPaintPanel.channel_unified(row,
context,
brush,
"jitter_absolute")
#row.prop(brush, "use_pressure_jitter", toggle=True, text="")
UnifiedPaintPanel.channel_unified(col.row(),
context,
brush,
"jitter_unit", expand=True)
#col.row().prop(brush, "jitter_unit", expand=True)
col.separator()
col.prop(settings, "input_samples")
@ -896,20 +922,41 @@ class SmoothStrokePanel(BrushPanel):
settings = self.paint_settings(context)
brush = settings.brush
self.layout.prop(brush, "use_smooth_stroke", text="")
if context.mode == "SCULPT":
self.layout.prop(brush.channels["use_smooth_stroke"], "value", text="")
else:
self.layout.prop(brush, "use_smooth_stroke", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
ui_editing = context.tool_settings.unified_paint_settings.brush_editor_mode
settings = self.paint_settings(context)
brush = settings.brush
if ui_editing:
UnifiedPaintPanel.channel_unified(self.layout,
context,
brush,
"use_smooth_stroke", ui_editing=True, text="Stabilize Stroke")
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
col = layout.column()
col.active = brush.use_smooth_stroke
col.prop(brush, "smooth_stroke_radius", text="Radius", slider=True)
col.prop(brush, "smooth_stroke_factor", text="Factor", slider=True)
col.active = UnifiedPaintPanel.get_channel_value(context, brush, "use_smooth_stroke")
#col.prop(brush, "smooth_stroke_radius", text="Radius", slider=True)
#col.prop(brush, "smooth_stroke_factor", text="Factor", slider=True)
UnifiedPaintPanel.channel_unified(col,
context,
brush,
"smooth_stroke_radius", text="Radius", ui_editing=ui_editing, slider=True)
UnifiedPaintPanel.channel_unified(col,
context,
brush,
"smooth_stroke_factor", text="Factor", ui_editing=ui_editing, slider=True)
class FalloffPanel(BrushPanel):

View File

@ -288,8 +288,15 @@ MAKE_FLOAT(normal_weight, "Normal Weight", "", 0.0f, 0.0f, 1.0f)
MAKE_FLOAT(weight, "Weight", "", 0.5f, 0.0f, 1.0f)
MAKE_FLOAT(jitter, "Jitter", "Jitter the position of the brush while painting", 0.0f, 0.0f, 1.0f)
MAKE_INT(jitter_absolute, "Absolute Jitter", "", 0, 0.0f, 1000.0f)
MAKE_FLOAT(smooth_stroke_radius, "Smooth Stroke Radius", "Minimum distance from last point before stroke continues", 75.0f, 10.0f, 200.0f)
MAKE_FLOAT(smooth_stroke_factor, "Smooth Stroke Factor", "", 0.9f, 0.5f, 0.99f)
MAKE_ENUM_EX(jitter_unit, "Jitter Unit", "Jitter in screen space or relative to brush size", 0, 0, {
{BRUSH_ABSOLUTE_JITTER, "VIEW", "NONE", "View", "Jittering happens in screen space, in pixels"},
{0, "BRUSH", "NONE", "Brush", "Jittering happens relative to the brush size"},
{-1}
})
MAKE_BOOL_EX(use_smooth_stroke, "Smooth Stroke", "Brush lags behind mouse and follows a smoother path", false, 0)
MAKE_FLOAT_EX_EX(smooth_stroke_radius, "Smooth Stroke Radius", "Minimum distance from last point before stroke continues", 75.0f, 10.0f, 200.0f, 10.0f, 200.0f, false, false, 0)
MAKE_FLOAT_EX_EX(smooth_stroke_factor, "Smooth Stroke Factor", "", 0.9f, 0.5f, 0.99f, 0.5f, 0.99f, false, false, 0)
MAKE_FLOAT_EX(rate, "Rate", "", 0.1f, 0.0001f, 10000.0f, 0.01f, 1.0f, false)
MAKE_FLOAT(flow, "Flow", "Amount of paint that is applied per stroke sample", 1.0f, 0.0f, 1.0f)
MAKE_FLOAT(wet_mix, "Wet Mix", "Amount of paint that is picked from the surface into the brush color", 0.0f, 0.0f, 1.0f)

View File

@ -248,6 +248,10 @@ static bool check_builtin_init()
// BKE_brush_channeltype_rna_check(brush_builtin_channels + i);
//}
SUBTYPE_SET(smooth_stroke_radius, BRUSH_CHANNEL_PIXEL);
SUBTYPE_SET(jitter_absolute, BRUSH_CHANNEL_PIXEL);
SUBTYPE_SET(radius, BRUSH_CHANNEL_PIXEL);
SUBTYPE_SET(spacing, BRUSH_CHANNEL_PERCENT);
SUBTYPE_SET(autosmooth_spacing, BRUSH_CHANNEL_PERCENT);
@ -286,6 +290,12 @@ static bool check_builtin_init()
SETCAT(spacing, "Stroke");
SETCAT(use_space_attenuation, "Stroke");
SETCAT(use_smooth_stroke, "Stroke");
SETCAT(smooth_stroke_factor, "Stroke");
SETCAT(smooth_stroke_radius, "Stroke");
SETCAT(jitter_absolute, "Stroke");
SETCAT(jitter_unit, "Stroke");
SETCAT(jitter, "Stroke");
SETCAT(autosmooth, "Smoothing");
SETCAT(autosmooth_projection, "Smoothing");
@ -416,7 +426,7 @@ static BrushSettingsMap brush_settings_map[] = {
DEF(weight, weight, FLOAT, FLOAT)
DEF(multiplane_scrape_angle, multiplane_scrape_angle, FLOAT, FLOAT)
DEF(jitter, jitter, FLOAT, FLOAT)
DEF(jitter_absolute, JITTER_ABSOLITE, INT, INT)
DEF(jitter_absolute, jitter_absolute, INT, INT)
DEF(smooth_stroke_radius, smooth_stroke_radius, INT, FLOAT)
DEF(smooth_stroke_factor, smooth_stroke_factor, FLOAT, FLOAT)
DEF(rate, rate, FLOAT, FLOAT)
@ -502,6 +512,7 @@ typedef struct BrushFlagMap {
char *channel_name;
int flag;
int member_size;
int bitmask_bit;
} BrushFlagMap;
/* clang-format off */
@ -510,7 +521,10 @@ typedef struct BrushFlagMap {
#endif
#define DEF(member, channel, flag)\
{offsetof(Brush, member), #channel, flag, sizeof(((Brush){0}).member)},
{offsetof(Brush, member), #channel, flag, sizeof(((Brush){0}).member), 0},
#define DEFBIT(member, channel, flag, bit)\
{offsetof(Brush, member), #channel, flag, sizeof(((Brush){0}).member), bit},
/* This lookup table is like brush_settings_map except it converts
individual bitflags instead of whole struct members.*/
@ -539,6 +553,8 @@ BrushFlagMap brush_flags_map[] = {
DEF(flag2, use_surface_falloff, BRUSH_USE_SURFACE_FALLOFF)
DEF(flag2, use_grab_active_vertex, BRUSH_GRAB_ACTIVE_VERTEX)
DEF(flag, accumulate, BRUSH_ACCUMULATE)
DEF(flag, use_smooth_stroke, BRUSH_SMOOTH_STROKE)
DEFBIT(flag, jitter_unit, BRUSH_ABSOLUTE_JITTER, BRUSH_ABSOLUTE_JITTER)
};
int brush_flags_map_len = ARRAY_SIZE(brush_flags_map);
@ -637,6 +653,38 @@ void *get_channel_value_pointer(BrushChannel *ch, int *r_data_size)
return NULL;
}
static int brushflag_from_channel(BrushFlagMap *mf, int flag, int val)
{
if (mf->bitmask_bit == 0) {
return val ? flag | mf->flag : flag & ~mf->flag;
}
if (val & mf->bitmask_bit) {
flag |= mf->flag;
}
else {
flag &= ~mf->flag;
}
return flag;
}
static int brushflag_to_channel(BrushFlagMap *mf, int chvalue, int val)
{
if (mf->bitmask_bit == 0) {
return val & mf->flag ? 1 : 0;
}
if (val & mf->flag) {
chvalue |= mf->bitmask_bit;
}
else {
chvalue &= ~mf->bitmask_bit;
}
return chvalue;
}
static void brush_flags_from_channels(BrushChannelSet *chset, Brush *brush)
{
for (int i = 0; i < brush_flags_map_len; i++) {
@ -653,42 +701,22 @@ static void brush_flags_from_channels(BrushChannelSet *chset, Brush *brush)
switch (mf->member_size) {
case 1: {
char *f = (char *)ptr;
if (ch->ivalue) {
*f |= mf->flag;
}
else {
*f &= ~mf->flag;
}
*f = (char)brushflag_from_channel(mf, *f, ch->ivalue);
break;
}
case 2: {
ushort *f = (ushort *)ptr;
if (ch->ivalue) {
*f |= mf->flag;
}
else {
*f &= ~mf->flag;
}
*f = (ushort)brushflag_from_channel(mf, *f, ch->ivalue);
break;
}
case 4: {
uint *f = (uint *)ptr;
if (ch->ivalue) {
*f |= mf->flag;
}
else {
*f &= ~mf->flag;
}
*f = (uint)brushflag_from_channel(mf, *f, ch->ivalue);
break;
}
case 8: {
uint64_t *f = (uint64_t *)ptr;
if (ch->ivalue) {
*f |= mf->flag;
}
else {
*f &= ~mf->flag;
}
*f = (uint64_t)brushflag_from_channel(mf, *f, ch->ivalue);
break;
}
}
@ -711,22 +739,22 @@ static void brush_flags_to_channels(BrushChannelSet *chset, Brush *brush)
switch (mf->member_size) {
case 1: {
char *f = (char *)ptr;
ch->ivalue = (*f & mf->flag) ? 1 : 0;
ch->ivalue = brushflag_to_channel(mf, ch->ivalue, *f);
break;
}
case 2: {
ushort *f = (ushort *)ptr;
ch->ivalue = (*f & mf->flag) ? 1 : 0;
ch->ivalue = brushflag_to_channel(mf, ch->ivalue, *f);
break;
}
case 4: {
uint *f = (uint *)ptr;
ch->ivalue = (*f & mf->flag) ? 1 : 0;
ch->ivalue = brushflag_to_channel(mf, ch->ivalue, *f);
break;
}
case 8: {
uint64_t *f = (uint64_t *)ptr;
ch->ivalue = (*f & mf->flag) ? 1 : 0;
ch->ivalue = brushflag_to_channel(mf, ch->ivalue, *f);
break;
}
}
@ -1053,6 +1081,7 @@ void BKE_brush_builtin_patch(Brush *brush, int tool)
ADDCH(original_plane);
ADDCH(jitter);
ADDCH(jitter_absolute);
ADDCH(jitter_unit);
ADDCH(use_weighted_smooth);
ADDCH(preserve_faceset_boundary);
ADDCH(hard_edge_mode);
@ -1068,6 +1097,7 @@ void BKE_brush_builtin_patch(Brush *brush, int tool)
ADDCH(direction);
ADDCH(dash_ratio);
ADDCH(use_smooth_stroke);
ADDCH(smooth_stroke_factor);
ADDCH(smooth_stroke_radius);
ADDCH(smooth_deform_type);

View File

@ -11892,6 +11892,17 @@ static bool over_mesh(bContext *C, struct wmOperator *UNUSED(op), float x, float
static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const float mouse[2])
{
if (BKE_paintmode_get_active_from_context(C) == PAINT_MODE_SCULPT) {
/* load brush settings into old Brush fields so the
paint API can get at then */
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
if (brush && brush->channels) {
BKE_brush_channelset_compat_load(brush->channels, brush, false);
}
}
/* Don't start the stroke until mouse goes over the mesh.
* NOTE: mouse will only be null when re-executing the saved stroke.
* We have exception for 'exec' strokes since they may not set 'mouse',