Sculpt: Invert Smooth to Enhance Details

This enables the invert mode in the smooth brush as Enhance Details.
The operation is similar to the Sharpen Filter intensify details parameter,
which consist in applying the laplacian smooth displacement in the opposite
direction calculated using the original coordinates.

Reviewed By: sergey

Differential Revision: https://developer.blender.org/D8509
This commit is contained in:
Pablo Dobarro 2020-08-18 16:13:43 +02:00
parent 872efd8d73
commit 3e5431fdf4
Notes: blender-bot 2023-02-14 05:37:19 +01:00
Referenced by commit b80ed8396d, Fix T89164: Sculpt "Smooth" brush crash with zero pressure
Referenced by issue #89164, Crash in sculpt mode using "Smooth" brush and tab with tablet stylus
4 changed files with 105 additions and 3 deletions

View File

@ -2331,7 +2331,7 @@ static float brush_strength(const Sculpt *sd,
}
case SCULPT_TOOL_SMOOTH:
return alpha * pressure * feather;
return flip * alpha * pressure * feather;
case SCULPT_TOOL_PINCH:
if (flip > 0.0f) {
@ -6365,6 +6365,7 @@ void SCULPT_cache_free(StrokeCache *cache)
MEM_SAFE_FREE(cache->surface_smooth_laplacian_disp);
MEM_SAFE_FREE(cache->layer_displacement_factor);
MEM_SAFE_FREE(cache->prev_colors);
MEM_SAFE_FREE(cache->detail_directions);
if (cache->pose_ik_chain) {
SCULPT_pose_ik_chain_free(cache->pose_ik_chain);

View File

@ -889,6 +889,9 @@ typedef struct StrokeCache {
/* Pose brush */
struct SculptPoseIKChain *pose_ik_chain;
/* Enhance Details. */
float (*detail_directions)[3];
/* Clay Thumb brush */
/* Angle of the front tilting plane of the brush to simulate clay accumulation. */
float clay_thumb_front_angle;

View File

@ -195,6 +195,85 @@ void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index
}
}
static void do_enhance_details_brush_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
{
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
Sculpt *sd = data->sd;
const Brush *brush = data->brush;
PBVHVertexIter vd;
float bstrength = ss->cache->bstrength;
CLAMP(bstrength, -1.0f, 1.0f);
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
vd.co,
sqrtf(test.dist),
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.index,
thread_id);
float disp[3];
madd_v3_v3v3fl(disp, vd.co, ss->cache->detail_directions[vd.index], fade);
SCULPT_clip(sd, ss, vd.co, disp);
if (vd.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
}
BKE_pbvh_vertex_iter_end;
}
static void SCULPT_enhance_details_brush(Sculpt *sd,
Object *ob,
PBVHNode **nodes,
const int totnode)
{
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
SCULPT_vertex_random_access_ensure(ss);
SCULPT_boundary_info_ensure(ob);
if (SCULPT_stroke_is_first_brush_step(ss->cache)) {
const int totvert = SCULPT_vertex_count_get(ss);
ss->cache->detail_directions = MEM_malloc_arrayN(
totvert, 3 * sizeof(float), "details directions");
for (int i = 0; i < totvert; i++) {
float avg[3];
SCULPT_neighbor_coords_average(ss, avg, i);
sub_v3_v3v3(ss->cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i));
}
}
SculptThreadedTaskData data = {
.sd = sd,
.ob = ob,
.brush = brush,
.nodes = nodes,
};
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_enhance_details_brush_task_cb_ex, &settings);
}
static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
@ -300,7 +379,14 @@ void SCULPT_smooth(Sculpt *sd,
void SCULPT_do_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
SCULPT_smooth(sd, ob, nodes, totnode, ss->cache->bstrength, false);
if (ss->cache->bstrength <= 0.0f) {
/* Invert mode, intensify details. */
SCULPT_enhance_details_brush(sd, ob, nodes, totnode);
}
else {
/* Regular mode, smooth. */
SCULPT_smooth(sd, ob, nodes, totnode, ss->cache->bstrength, false);
}
}
/* HC Smooth Algorithm. */

View File

@ -45,6 +45,16 @@ static const EnumPropertyItem prop_direction_items[] = {
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem prop_smooth_direction_items[] = {
{0, "SMOOTH", ICON_ADD, "Smooth", "Smooth the surfae"},
{BRUSH_DIR_IN,
"ENHANCE_DETAILS",
ICON_REMOVE,
"Enhance Details",
"Enhance the surface detail"},
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem sculpt_stroke_method_items[] = {
{0, "DOTS", 0, "Dots", "Apply paint on each mouse move step"},
{BRUSH_DRAG_DOT, "DRAG_DOT", 0, "Drag Dot", "Allows a single dot to be carefully positioned"},
@ -527,6 +537,7 @@ static bool rna_BrushCapabilitiesSculpt_has_direction_get(PointerRNA *ptr)
SCULPT_TOOL_DRAW_SHARP,
SCULPT_TOOL_CLAY,
SCULPT_TOOL_CLAY_STRIPS,
SCULPT_TOOL_SMOOTH,
SCULPT_TOOL_LAYER,
SCULPT_TOOL_INFLATE,
SCULPT_TOOL_BLOB,
@ -795,7 +806,8 @@ static const EnumPropertyItem *rna_Brush_direction_itemf(bContext *C,
case SCULPT_TOOL_CLAY:
case SCULPT_TOOL_CLAY_STRIPS:
return prop_direction_items;
case SCULPT_TOOL_SMOOTH:
return prop_smooth_direction_items;
case SCULPT_TOOL_MASK:
switch ((BrushMaskTool)me->mask_tool) {
case BRUSH_MASK_DRAW: