Add Preserve Face Sets Shape option to smooth brush #107471
|
@ -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()
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue