diff options
-rw-r--r-- | release/scripts/startup/bl_ui/properties_paint_common.py | 8 | ||||
-rw-r--r-- | release/scripts/startup/bl_ui/space_toolsystem_toolbar.py | 3 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/brush.c | 3 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt.c | 258 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_intern.h | 9 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_brush_types.h | 11 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_brush.c | 42 |
7 files changed, 326 insertions, 8 deletions
diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index 34afcc09407..3d2474d006a 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -665,6 +665,14 @@ def brush_settings(layout, context, brush, popover=False): col.prop(brush, "use_multiplane_scrape_dynamic") col.prop(brush, "show_multiplane_scrape_planes_preview") + if brush.sculpt_tool == 'SMOOTH': + col = layout.column() + col.prop(brush, "smooth_deform_type") + if brush.smooth_deform_type == 'SURFACE': + col.prop(brush, "surface_smooth_shape_preservation") + col.prop(brush, "surface_smooth_current_vertex") + col.prop(brush, "surface_smooth_iterations") + if brush.sculpt_tool == 'MASK': layout.row().prop(brush, "mask_tool", expand=True) diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index c501c4f66a1..9a7e42bdbce 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -1031,6 +1031,9 @@ class _defs_sculpt: layout.prop(props, "strength") layout.prop(props, "deform_axis") layout.prop(props, "use_face_sets") + if (props.type == "SURFACE_SMOOTH"): + layout.prop(props, "surface_smooth_shape_preservation", expand=False) + layout.prop(props, "surface_smooth_current_vertex", expand=False) return dict( idname="builtin.mesh_filter", diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index e6045c45dc8..2306b046026 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -1348,6 +1348,9 @@ void BKE_brush_sculpt_reset(Brush *br) br->flag &= ~BRUSH_SPACE_ATTEN; br->spacing = 5; br->alpha = 0.7f; + br->surface_smooth_shape_preservation = 0.5f; + br->surface_smooth_current_vertex = 0.5f; + br->surface_smooth_iterations = 4; break; case SCULPT_TOOL_SNAKE_HOOK: br->alpha = 1.0f; diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index ea3fb5624dd..a99b6e44d18 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -914,6 +914,7 @@ static bool sculpt_tool_needs_original(const char sculpt_tool) SCULPT_TOOL_LAYER, SCULPT_TOOL_DRAW_SHARP, SCULPT_TOOL_ELASTIC_DEFORM, + SCULPT_TOOL_SMOOTH, SCULPT_TOOL_POSE); } @@ -2859,7 +2860,7 @@ static float bmesh_neighbor_average_mask(BMVert *v, const int cd_vert_mask_offse } } -static void grids_neighbor_average(SculptSession *ss, float result[3], int index) +static void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], int index) { float avg[3] = {0.0f, 0.0f, 0.0f}; int total = 0; @@ -3155,7 +3156,7 @@ static void do_smooth_brush_multires_task_cb_ex(void *__restrict userdata, } else { float avg[3], val[3]; - grids_neighbor_average(ss, avg, vd.index); + SCULPT_neighbor_coords_average(ss, avg, vd.index); sub_v3_v3v3(val, avg, vd.co); madd_v3_v3v3fl(val, vd.co, val, fade); sculpt_clip(sd, ss, vd.co, val); @@ -3254,6 +3255,153 @@ static void do_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod SculptSession *ss = ob->sculpt; smooth(sd, ob, nodes, totnode, ss->cache->bstrength, false); } +/* HC Smooth Algorithm. */ +/* From: Improved Laplacian Smoothing of Noisy Surface Meshes */ + +static void surface_smooth_laplacian_step(SculptSession *ss, + float *disp, + const float co[3], + float (*laplacian_disp)[3], + const int v_index, + const float origco[3], + const float alpha) +{ + float laplacian_smooth_co[3]; + float weigthed_o[3], weigthed_q[3], d[3]; + SCULPT_neighbor_coords_average(ss, laplacian_smooth_co, v_index); + + mul_v3_v3fl(weigthed_o, origco, alpha); + mul_v3_v3fl(weigthed_q, co, 1.0f - alpha); + add_v3_v3v3(d, weigthed_o, weigthed_q); + sub_v3_v3v3(laplacian_disp[v_index], laplacian_smooth_co, d); + + sub_v3_v3v3(disp, laplacian_smooth_co, co); +} + +static void surface_smooth_displace_step(SculptSession *ss, + float *co, + float (*laplacian_disp)[3], + const int v_index, + const float beta, + const float fade) +{ + float b_avg[3] = {0.0f, 0.0f, 0.0f}; + float b_current_vertex[3]; + int total = 0; + SculptVertexNeighborIter ni; + sculpt_vertex_neighbors_iter_begin(ss, v_index, ni) + { + add_v3_v3(b_avg, laplacian_disp[ni.index]); + total++; + } + sculpt_vertex_neighbors_iter_end(ni); + if (total > 0) { + mul_v3_v3fl(b_current_vertex, b_avg, (1.0f - beta) / (float)total); + madd_v3_v3fl(b_current_vertex, laplacian_disp[v_index], beta); + mul_v3_fl(b_current_vertex, clamp_f(fade, 0.0f, 1.0f)); + sub_v3_v3(co, b_current_vertex); + } +} + +static void do_surface_smooth_brush_laplacian_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float bstrength = ss->cache->bstrength; + float alpha = brush->surface_smooth_shape_preservation; + + PBVHVertexIter vd; + SculptOrigVertData orig_data; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + SCULPT_orig_vert_data_update(&orig_data, &vd); + 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, 0.0f, vd.index, tls->thread_id); + + float disp[3]; + surface_smooth_laplacian_step(ss, + disp, + vd.co, + ss->cache->surface_smooth_laplacian_disp, + vd.index, + orig_data.co, + alpha); + madd_v3_v3fl(vd.co, disp, clamp_f(fade, 0.0f, 1.0f)); + } + BKE_pbvh_vertex_iter_end; + } +} + +static void do_surface_smooth_brush_displace_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float bstrength = ss->cache->bstrength; + const float beta = brush->surface_smooth_current_vertex; + + PBVHVertexIter vd; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + + 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, 0.0f, vd.index, tls->thread_id); + surface_smooth_displace_step( + ss, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.index, beta, fade); + } + } + BKE_pbvh_vertex_iter_end; +} + +static void do_surface_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; + + if (ss->cache->mirror_symmetry_pass == 0 && ss->cache->radial_symmetry_pass == 0) { + ss->cache->surface_smooth_laplacian_disp = MEM_callocN( + SCULPT_vertex_count_get(ss) * 3 * sizeof(float), "HC smooth laplacian b"); + } + + /* Threaded loop over nodes. */ + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + }; + + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + for (int i = 0; i < brush->surface_smooth_iterations; i++) { + BKE_pbvh_parallel_range( + 0, totnode, &data, do_surface_smooth_brush_laplacian_task_cb_ex, &settings); + BKE_pbvh_parallel_range( + 0, totnode, &data, do_surface_smooth_brush_displace_task_cb_ex, &settings); + } +} static void do_mask_brush_draw_task_cb_ex(void *__restrict userdata, const int n, @@ -6010,7 +6158,12 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe do_draw_brush(sd, ob, nodes, totnode); break; case SCULPT_TOOL_SMOOTH: - do_smooth_brush(sd, ob, nodes, totnode); + if (brush->smooth_deform_type == BRUSH_SMOOTH_DEFORM_LAPLACIAN) { + do_smooth_brush(sd, ob, nodes, totnode); + } + else if (brush->smooth_deform_type == BRUSH_SMOOTH_DEFORM_SURFACE) { + do_surface_smooth_brush(sd, ob, nodes, totnode); + } break; case SCULPT_TOOL_CREASE: do_crease_brush(sd, ob, nodes, totnode); @@ -9178,7 +9331,9 @@ static void sculpt_filter_cache_free(SculptSession *ss) if (ss->filter_cache->automask) { MEM_freeN(ss->filter_cache->automask); } - + if (ss->filter_cache->surface_smooth_laplacian_disp) { + MEM_freeN(ss->filter_cache->surface_smooth_laplacian_disp); + } MEM_freeN(ss->filter_cache); ss->filter_cache = NULL; } @@ -9191,6 +9346,7 @@ typedef enum eSculptMeshFilterTypes { MESH_FILTER_RANDOM = 4, MESH_FILTER_RELAX = 5, MESH_FILTER_RELAX_FACE_SETS = 6, + MESH_FILTER_SURFACE_SMOOTH = 7, } eSculptMeshFilterTypes; static EnumPropertyItem prop_mesh_filter_types[] = { @@ -9205,6 +9361,11 @@ static EnumPropertyItem prop_mesh_filter_types[] = { 0, "Relax Face Sets", "Smooth the edges of all the Face Sets"}, + {MESH_FILTER_SURFACE_SMOOTH, + "SURFACE_SMOOTH", + 0, + "Surface Smooth", + "Smooth the surface of the mesh, preserving the volume"}, {0, NULL, 0, NULL, NULL}, }; @@ -9223,8 +9384,11 @@ static EnumPropertyItem prop_mesh_filter_deform_axis_items[] = { static bool sculpt_mesh_filter_needs_pmap(int filter_type, bool use_face_sets) { - return use_face_sets || - ELEM(filter_type, MESH_FILTER_SMOOTH, MESH_FILTER_RELAX, MESH_FILTER_RELAX_FACE_SETS); + return use_face_sets || ELEM(filter_type, + MESH_FILTER_SMOOTH, + MESH_FILTER_RELAX, + MESH_FILTER_RELAX_FACE_SETS, + MESH_FILTER_SURFACE_SMOOTH); } static void mesh_filter_task_cb(void *__restrict userdata, @@ -9296,7 +9460,7 @@ static void mesh_filter_task_cb(void *__restrict userdata, bmesh_neighbor_average(avg, vd.bm_vert); break; case PBVH_GRIDS: - grids_neighbor_average(ss, avg, vd.index); + SCULPT_neighbor_coords_average(ss, avg, vd.index); break; } sub_v3_v3v3(val, avg, orig_co); @@ -9357,6 +9521,16 @@ static void mesh_filter_task_cb(void *__restrict userdata, sub_v3_v3v3(disp, val, vd.co); break; } + case MESH_FILTER_SURFACE_SMOOTH: { + surface_smooth_laplacian_step(ss, + disp, + vd.co, + ss->filter_cache->surface_smooth_laplacian_disp, + vd.index, + orig_data.co, + ss->filter_cache->surface_smooth_shape_preservation); + break; + } } for (int it = 0; it < 3; it++) { @@ -9365,7 +9539,12 @@ static void mesh_filter_task_cb(void *__restrict userdata, } } - add_v3_v3v3(final_pos, orig_co, disp); + if (filter_type == MESH_FILTER_SURFACE_SMOOTH) { + madd_v3_v3v3fl(final_pos, vd.co, disp, clamp_f(fade, 0.0f, 1.0f)); + } + else { + add_v3_v3v3(final_pos, orig_co, disp); + } copy_v3_v3(vd.co, final_pos); if (vd.mvert) { vd.mvert->flag |= ME_VERT_PBVH_UPDATE; @@ -9376,6 +9555,32 @@ static void mesh_filter_task_cb(void *__restrict userdata, BKE_pbvh_node_mark_update(node); } +static void mesh_filter_surface_smooth_displace_task_cb( + void *__restrict userdata, const int i, const TaskParallelTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + PBVHNode *node = data->nodes[i]; + PBVHVertexIter vd; + + BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) + { + float fade = vd.mask ? *vd.mask : 0.0f; + fade = 1.0f - fade; + fade *= data->filter_strength; + if (fade == 0.0f) { + continue; + } + surface_smooth_displace_step(ss, + vd.co, + ss->filter_cache->surface_smooth_laplacian_disp, + vd.index, + ss->filter_cache->surface_smooth_current_vertex, + clamp_f(fade, 0.0f, 1.0f)); + } + BKE_pbvh_vertex_iter_end; +} + static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent *event) { Object *ob = CTX_data_active_object(C); @@ -9418,6 +9623,14 @@ static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent * &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode); BKE_pbvh_parallel_range(0, ss->filter_cache->totnode, &data, mesh_filter_task_cb, &settings); + if (filter_type == MESH_FILTER_SURFACE_SMOOTH) { + BKE_pbvh_parallel_range(0, + ss->filter_cache->totnode, + &data, + mesh_filter_surface_smooth_displace_task_cb, + &settings); + } + ss->filter_cache->iteration_count++; if (ss->deform_modifiers_active || ss->shapekey_active) { @@ -9480,6 +9693,15 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent ss->filter_cache->active_face_set = SCULPT_FACE_SET_NONE; } + if (RNA_enum_get(op->ptr, "type") == MESH_FILTER_SURFACE_SMOOTH) { + ss->filter_cache->surface_smooth_laplacian_disp = MEM_mallocN( + 3 * sizeof(float) * SCULPT_vertex_count_get(ss), "surface smooth disp"); + ss->filter_cache->surface_smooth_shape_preservation = RNA_float_get( + op->ptr, "surface_smooth_shape_preservation"); + ss->filter_cache->surface_smooth_current_vertex = RNA_float_get( + op->ptr, "surface_smooth_current_vertex"); + } + ss->filter_cache->enabled_axis[0] = deform_axis & MESH_FILTER_DEFORM_X; ss->filter_cache->enabled_axis[1] = deform_axis & MESH_FILTER_DEFORM_Y; ss->filter_cache->enabled_axis[2] = deform_axis & MESH_FILTER_DEFORM_Z; @@ -9532,6 +9754,26 @@ static void SCULPT_OT_mesh_filter(struct wmOperatorType *ot) false, "Use Face Sets", "Apply the filter only to the Face Mask under the cursor"); + + /* Surface Smooth Mesh Filter properties. */ + RNA_def_float(ot->srna, + "surface_smooth_shape_preservation", + 0.5f, + 0.0f, + 1.0f, + "Shape Preservation", + "How much of the original shape is preserved when smoothing", + 0.0f, + 1.0f); + RNA_def_float(ot->srna, + "surface_smooth_current_vertex", + 0.5f, + 0.0f, + 1.0f, + "Per Vertex Displacement", + "How much the position of each individual vertex influences the final result", + 0.0f, + 1.0f); } typedef enum eSculptMaskFilterTypes { diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index b8235dc5c95..d8e29d0e773 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -618,6 +618,10 @@ typedef struct StrokeCache { float initial_normal[3]; float true_initial_normal[3]; + /* Surface Smooth Brush */ + /* Stores the displacement produced by the laplacian step of HC smooth. */ + float (*surface_smooth_laplacian_disp)[3]; + float vertex_rotation; /* amount to rotate the vertices when using rotate brush */ struct Dial *dial; @@ -650,6 +654,11 @@ typedef struct FilterCache { * achieve certain effects. */ int iteration_count; + /* Stores the displacement produced by the laplacian step of HC smooth. */ + float (*surface_smooth_laplacian_disp)[3]; + float surface_smooth_shape_preservation; + float surface_smooth_current_vertex; + /* unmasked nodes */ PBVHNode **nodes; int totnode; diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 1fad828e887..59e9d3be58d 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -281,6 +281,11 @@ typedef enum eBrushClothDeformType { BRUSH_CLOTH_DEFORM_EXPAND = 6, } eBrushClothDeformType; +typedef enum eBrushSmoothDeformType { + BRUSH_SMOOTH_DEFORM_LAPLACIAN = 0, + BRUSH_SMOOTH_DEFORM_SURFACE = 1, +} eBrushSmoothDeformType; + typedef enum eBrushClothForceFalloffType { BRUSH_CLOTH_FORCE_FALLOFF_RADIAL = 0, BRUSH_CLOTH_FORCE_FALLOFF_PLANE = 1, @@ -473,6 +478,12 @@ typedef struct Brush { float cloth_sim_limit; float cloth_sim_falloff; + /* smooth */ + int smooth_deform_type; + float surface_smooth_shape_preservation; + float surface_smooth_current_vertex; + int surface_smooth_iterations; + /* multiplane scrape */ float multiplane_scrape_angle; diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index a4091718487..1a163c9e2eb 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -1794,6 +1794,20 @@ static void rna_def_brush(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem brush_smooth_deform_type_items[] = { + {BRUSH_SMOOTH_DEFORM_LAPLACIAN, + "LAPLACIAN", + 0, + "Laplacian", + "Smooths the surface and the volume"}, + {BRUSH_SMOOTH_DEFORM_SURFACE, + "SURFACE", + 0, + "Surface", + "Smooths the surface of the mesh, preserving the volue"}, + {0, NULL, 0, NULL, NULL}, + }; + srna = RNA_def_struct(brna, "Brush", "ID"); RNA_def_struct_ui_text( srna, "Brush", "Brush data-block for storing brush settings for painting and sculpting"); @@ -1909,6 +1923,11 @@ static void rna_def_brush(BlenderRNA *brna) prop, "Force Falloff", "Shape used in the brush to apply force to the cloth"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "smooth_deform_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, brush_smooth_deform_type_items); + RNA_def_property_ui_text(prop, "Deformation", "Deformation type that is used in the brush"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "jitter_unit", PROP_ENUM, PROP_NONE); /* as an enum */ RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); RNA_def_property_enum_items(prop, brush_jitter_unit_items); @@ -2101,6 +2120,29 @@ static void rna_def_brush(BlenderRNA *brna) prop, "Pose Origin Offset", "Offset of the pose origin in relation to the brush radius"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "surface_smooth_shape_preservation", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "surface_smooth_shape_preservation"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text( + prop, "Shape Preservation", "How much of the original shape is preserved when smoothing"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "surface_smooth_current_vertex", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "surface_smooth_current_vertex"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text( + prop, + "Per Vertex Displacement", + "How much the position of each individual vertex influences the final result"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "surface_smooth_iterations", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_sdna(prop, NULL, "surface_smooth_iterations"); + RNA_def_property_range(prop, 1, 10); + RNA_def_property_ui_range(prop, 1, 10, 1, 3); + RNA_def_property_ui_text(prop, "Iterations", "Number of smoothing iterations per brush step"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "multiplane_scrape_angle", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "multiplane_scrape_angle"); RNA_def_property_range(prop, 0.0f, 160.0f); |