Add Preserve Face Sets Shape option to smooth brush #107471

Closed
Toby Yang wants to merge 1 commits from tianer2820-2/blender:main into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
6 changed files with 127 additions and 1 deletions

View File

@ -759,6 +759,8 @@ def brush_settings(layout, context, brush, popover=False):
col.prop(brush, "surface_smooth_shape_preservation")
col.prop(brush, "surface_smooth_current_vertex")
col.prop(brush, "surface_smooth_iterations")
elif brush.smooth_deform_type == 'LAPLACIAN':
col.prop(brush, "use_preserve_face_sets_shape")
elif sculpt_tool == 'DISPLACEMENT_SMEAR':
col = layout.column()

View File

@ -615,6 +615,29 @@ int SCULPT_vertex_face_set_get(SculptSession *ss, PBVHVertRef vertex)
return 0;
}
bool SCULPT_vertices_are_same_face_set_boundary(SculptSession *ss, PBVHVertRef vertex1, PBVHVertRef vertex2)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
MeshElemMap *vert_map = &ss->pmap[vertex1.i];
for (int i = 0; i < ss->pmap[vertex1.i].count; i++) {
if (!SCULPT_vertex_has_face_set(ss, vertex2, ss->face_sets[vert_map->indices[i]])) {
return false;
}
}
return true;
}
case PBVH_BMESH:
return true;
case PBVH_GRIDS:
/* There is no need check this in Multires. If two vertices are connected and both
* are in a boundary, they will be always in the same boundary. */
return true;
}
return true;
}
bool SCULPT_vertex_has_face_set(SculptSession *ss, PBVHVertRef vertex, int face_set)
{
switch (BKE_pbvh_type(ss->pbvh)) {

View File

@ -1093,6 +1093,10 @@ void SCULPT_face_set_set(SculptSession *ss, PBVHFaceRef face, int fset);
bool SCULPT_vertex_has_face_set(SculptSession *ss, PBVHVertRef vertex, int face_set);
bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, PBVHVertRef vertex);
/* Given two neighbor vertices in both in a Face Set boundary, checks if they are in the same
* boundary. */
bool SCULPT_vertices_are_same_face_set_boundary(SculptSession *ss, PBVHVertRef vertex1, PBVHVertRef vertex2);
int SCULPT_face_set_next_available_get(SculptSession *ss);
void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visible);

View File

@ -261,6 +261,84 @@ static void SCULPT_enhance_details_brush(Sculpt *sd, Object *ob, Span<PBVHNode *
BLI_task_parallel_range(0, nodes.size(), &data, do_enhance_details_brush_task_cb_ex, &settings);
}
static void do_smooth_brush_preserve_face_sets_shape_task_cb_ex(
void *__restrict userdata, const int n, const TaskParallelTLS *__restrict tls)
{
SculptThreadedTaskData *data = (SculptThreadedTaskData*)userdata;
SculptSession *ss = data->ob->sculpt;
Sculpt *sd = data->sd;
const Brush *brush = data->brush;
const bool smooth_mask = data->smooth_mask;
float bstrength = data->strength;
PBVHVertexIter vd;
CLAMP(bstrength, 0.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)) {
float fade = bstrength *
SCULPT_brush_strength_factor(ss,
brush,
vd.co,
sqrtf(test.dist),
vd.no,
vd.fno,
smooth_mask ? 0.0f : (vd.mask ? *vd.mask : 0.0f),
vd.vertex,
thread_id,
nullptr);
float smooth_co[3];
if (SCULPT_vertex_has_unique_face_set(ss, vd.vertex)) {
SCULPT_neighbor_coords_average_interior(ss, smooth_co, vd.vertex);
}
else if (!SCULPT_vertex_is_boundary(ss, vd.vertex)) {
int neighbor_count = 0;
float co_accum[3] = {0.0f, 0.0f, 0.0f};
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
if (!SCULPT_vertex_has_unique_face_set(ss, ni.vertex) &&
SCULPT_vertices_are_same_face_set_boundary(ss, vd.vertex, ni.vertex)) {
add_v3_v3(co_accum, SCULPT_vertex_co_get(ss, ni.vertex));
neighbor_count++;
}
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
if (neighbor_count > 0) {
mul_v3_v3fl(smooth_co, co_accum, 1.0f / neighbor_count);
/* When smoothing the Face Set boundary using only boundary neighbors the mesh will
* shrink much faster per iteration comparted to other vertices. Reducing the fade by a
* factor helps compensating this undesired effect. */
fade *= 0.1f;
}
else {
copy_v3_v3(smooth_co, vd.co);
}
}
else {
copy_v3_v3(smooth_co, vd.co);
}
float disp[3];
sub_v3_v3v3(disp, smooth_co, vd.co);
madd_v3_v3v3fl(disp, vd.co, disp, 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 do_smooth_brush_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
@ -361,7 +439,15 @@ void SCULPT_smooth(
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_smooth_brush_task_cb_ex, &settings);
if (brush->flag2 & BRUSH_PRESERVE_FACE_SETS_SHAPE && !smooth_mask) {
/* Smooth mask never uses preserve Face Sets shape version of smooth as it does not move the
* vertices. */
BLI_task_parallel_range(
0, nodes.size(), &data, do_smooth_brush_preserve_face_sets_shape_task_cb_ex, &settings);
}
else {
BLI_task_parallel_range(0, nodes.size(), &data, do_smooth_brush_task_cb_ex, &settings);
}
}
}

View File

@ -419,6 +419,8 @@ typedef enum eBrushFlags2 {
BRUSH_AREA_RADIUS_PRESSURE = (1 << 7),
BRUSH_GRAB_SILHOUETTE = (1 << 8),
BRUSH_USE_COLOR_AS_DISPLACEMENT = (1 << 9),
BRUSH_PRESERVE_FACE_SETS_SHAPE = (1 << 10),
} eBrushFlags2;
typedef enum {

View File

@ -3447,6 +3447,15 @@ static void rna_def_brush(BlenderRNA *brna)
prop, "Area Radius Pressure", "Enable tablet pressure sensitivity for area radius");
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "use_preserve_face_sets_shape", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_PRESERVE_FACE_SETS_SHAPE);
RNA_def_property_ui_text(
prop,
"Preserve Face Sets Shape",
"Smooths Face Sets boundary vertices independently and uses regular smoothing for the rest "
"of the mesh.This preserves the general shape of the Face Sets");
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "use_pressure_size", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_SIZE_PRESSURE);
RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0);