diff options
Diffstat (limited to 'source/blender/editors/sculpt_paint')
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_cursor.c | 12 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_mask.c | 604 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_stroke.c | 10 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt.c | 28 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_cloth.c | 91 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_filter_mesh.c | 170 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_intern.h | 5 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_smooth.c | 37 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_transform.c | 6 |
9 files changed, 529 insertions, 434 deletions
diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index 0e38340d3bc..ee514fa745c 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -566,7 +566,7 @@ static bool paint_draw_tex_overlay(UnifiedPaintSettings *ups, if (load_tex(brush, vc, zoom, col, primary)) { GPU_color_mask(true, true, true, true); - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); if (mtex->brush_map_mode == MTEX_MAP_MODE_VIEW) { GPU_matrix_push(); @@ -693,7 +693,7 @@ static bool paint_draw_cursor_overlay( float center[2]; GPU_color_mask(true, true, true, true); - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); if (ups->draw_anchored) { copy_v2_v2(center, ups->anchored_initial_mouse); @@ -776,7 +776,7 @@ static bool paint_draw_alpha_overlay(UnifiedPaintSettings *ups, ePaintOverlayControlFlags flags = BKE_paint_get_overlay_flags(); eGPUBlend blend_state = GPU_blend_get(); - bool depth_test = GPU_depth_test_enabled(); + eGPUDepthTest depth_test = GPU_depth_test_get(); /* Translate to region. */ GPU_matrix_push(); @@ -1147,9 +1147,9 @@ static void sculpt_geometry_preview_lines_draw(const uint gpuattr, immUniformColor4f(1.0f, 1.0f, 1.0f, 0.6f); /* Cursor normally draws on top, but for this part we need depth tests. */ - const bool depth_test = GPU_depth_test_enabled(); + const eGPUDepthTest depth_test = GPU_depth_test_get(); if (!depth_test) { - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } GPU_line_width(1.0f); @@ -1163,7 +1163,7 @@ static void sculpt_geometry_preview_lines_draw(const uint gpuattr, /* Restore depth test value. */ if (!depth_test) { - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); } } diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index 05ffb80d8a1..ab8b81a8155 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -31,6 +31,7 @@ #include "BLI_lasso_2d.h" #include "BLI_math_geom.h" #include "BLI_math_matrix.h" +#include "BLI_rect.h" #include "BLI_task.h" #include "BLI_utildefines.h" @@ -221,392 +222,383 @@ void PAINT_OT_mask_flood_fill(struct wmOperatorType *ot) 1.0f); } -/* Box select, operator is VIEW3D_OT_select_box, defined in view3d_select.c. */ +/* Sculpt Gesture Operators. */ -static bool is_effected(float planes[4][4], const float co[3]) -{ - return isect_point_planes_v3(planes, 4, co); -} +typedef enum eSculptGestureShapeType { + SCULPT_GESTURE_SHAPE_BOX, + SCULPT_GESTURE_SHAPE_LASSO, +} eMaskGesturesShapeType; -static void flip_plane(float out[4], const float in[4], const char symm) -{ - if (symm & PAINT_SYMM_X) { - out[0] = -in[0]; - } - else { - out[0] = in[0]; - } - if (symm & PAINT_SYMM_Y) { - out[1] = -in[1]; - } - else { - out[1] = in[1]; - } - if (symm & PAINT_SYMM_Z) { - out[2] = -in[2]; - } - else { - out[2] = in[2]; - } +typedef struct LassoGestureData { + float projviewobjmat[4][4]; - out[3] = in[3]; -} + rcti boundbox; + int width; -static void mask_box_select_task_cb(void *__restrict userdata, - const int i, - const TaskParallelTLS *__restrict UNUSED(tls)) -{ - MaskTaskData *data = userdata; + /* 2D bitmap to test if a vertex is affected by the lasso shape. */ + BLI_bitmap *mask_px; +} LassoGestureData; - PBVHNode *node = data->nodes[i]; +typedef struct SculptGestureContext { + SculptSession *ss; + ViewContext vc; - const PaintMaskFloodMode mode = data->mode; - const float value = data->value; - float(*clip_planes_final)[4] = data->clip_planes_final; + /* Enabled and currently active symmetry. */ + ePaintSymmetryFlags symm; + ePaintSymmetryFlags symmpass; - PBVHVertexIter vi; - bool any_masked = false; - bool redraw = false; + /* Operation parameters. */ + eMaskGesturesShapeType shape_type; + bool front_faces_only; - float vertex_normal[3]; + /* Mask operation parameters. */ + PaintMaskFloodMode mask_mode; + float mask_value; - BKE_pbvh_vertex_iter_begin(data->pbvh, node, vi, PBVH_ITER_UNIQUE) - { - SCULPT_vertex_normal_get(data->ob->sculpt, vi.index, vertex_normal); - float dot = dot_v3v3(data->view_normal, vertex_normal); - const bool is_effected_front_face = !(data->front_faces_only && dot < 0.0f); + /* View parameters. */ + float true_view_normal[3]; + float view_normal[3]; - if (is_effected_front_face && is_effected(clip_planes_final, vi.co)) { - float prevmask = *vi.mask; - if (!any_masked) { - any_masked = true; + float true_clip_planes[4][4]; + float clip_planes[4][4]; - SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_MASK); + /* Lasso Gesture. */ + LassoGestureData lasso; - if (data->multires) { - BKE_pbvh_node_mark_normals_update(node); - } - } - mask_flood_fill_set_elem(vi.mask, mode, value); - if (prevmask != *vi.mask) { - redraw = true; - } - } - } - BKE_pbvh_vertex_iter_end; + /* Task Callback Data. */ + PBVHNode **nodes; + int totnode; +} SculptGestureContext; - if (redraw) { - BKE_pbvh_node_mark_update_mask(node); - } +static void sculpt_gesture_operator_properties(wmOperatorType *ot) +{ + RNA_def_boolean(ot->srna, + "use_front_faces_only", + false, + "Front Faces Only", + "Affect only faces facing towards the view"); } -static int paint_mask_gesture_box_exec(bContext *C, wmOperator *op) +static void sculpt_gesture_context_init_common(bContext *C, + wmOperator *op, + SculptGestureContext *sgcontext) { - ViewContext vc; Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - ED_view3d_viewcontext_init(C, &vc, depsgraph); - - Sculpt *sd = vc.scene->toolsettings->sculpt; - BoundBox bb; - float clip_planes[4][4]; - float clip_planes_final[4][4]; - ARegion *region = vc.region; - Object *ob = vc.obact; - bool multires; - PBVH *pbvh; - PBVHNode **nodes; - int totnode; - int symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; - - const PaintMaskFloodMode mode = RNA_enum_get(op->ptr, "mode"); - const float value = RNA_float_get(op->ptr, "value"); - const bool front_faces_only = RNA_boolean_get(op->ptr, "use_front_faces_only"); + ED_view3d_viewcontext_init(C, &sgcontext->vc, depsgraph); - rcti rect; - WM_operator_properties_border_to_rcti(op, &rect); + Sculpt *sd = sgcontext->vc.scene->toolsettings->sculpt; + Object *ob = sgcontext->vc.obact; - /* Transform the clip planes in object space. */ - ED_view3d_clipping_calc(&bb, clip_planes, vc.region, vc.obact, &rect); + /* Operator properties. */ + sgcontext->front_faces_only = RNA_boolean_get(op->ptr, "use_front_faces_only"); - BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false); - pbvh = ob->sculpt->pbvh; - multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS); + /* SculptSession */ + sgcontext->ss = ob->sculpt; - SCULPT_undo_push_begin("Mask box fill"); + /* Symmetry. */ + sgcontext->symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; - /* Calculate the view normal in object space. */ + /* View Normal. */ float mat[3][3]; float view_dir[3] = {0.0f, 0.0f, 1.0f}; - float true_view_normal[3]; - copy_m3_m4(mat, vc.rv3d->viewinv); + copy_m3_m4(mat, sgcontext->vc.rv3d->viewinv); mul_m3_v3(mat, view_dir); copy_m3_m4(mat, ob->imat); mul_m3_v3(mat, view_dir); - normalize_v3_v3(true_view_normal, view_dir); + normalize_v3_v3(sgcontext->true_view_normal, view_dir); +} - for (int symmpass = 0; symmpass <= symm; symmpass++) { - if (symmpass == 0 || (symm & symmpass && (symm != 5 || symmpass != 3) && - (symm != 6 || (symmpass != 3 && symmpass != 5)))) { +static void sculpt_gesture_lasso_px_cb(int x, int x_end, int y, void *user_data) +{ + SculptGestureContext *mcontext = user_data; + LassoGestureData *lasso = &mcontext->lasso; + int index = (y * lasso->width) + x; + int index_end = (y * lasso->width) + x_end; + do { + BLI_BITMAP_ENABLE(lasso->mask_px, index); + } while (++index != index_end); +} - /* Flip the planes symmetrically as needed. */ - for (int j = 0; j < 4; j++) { - flip_plane(clip_planes_final[j], clip_planes[j], symmpass); - } +static SculptGestureContext *sculpt_gesture_init_from_lasso(bContext *C, wmOperator *op) +{ + SculptGestureContext *sgcontext = MEM_callocN(sizeof(SculptGestureContext), + "sculpt gesture context lasso"); + sgcontext->shape_type = SCULPT_GESTURE_SHAPE_LASSO; + + sculpt_gesture_context_init_common(C, op, sgcontext); - PBVHFrustumPlanes frustum = {.planes = clip_planes_final, .num_planes = 4}; - BKE_pbvh_search_gather(pbvh, BKE_pbvh_node_frustum_contain_AABB, &frustum, &nodes, &totnode); + int mcoords_len; + const int(*mcoords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcoords_len); - negate_m4(clip_planes_final); + if (!mcoords) { + return NULL; + } - MaskTaskData data = { - .ob = ob, - .pbvh = pbvh, - .nodes = nodes, - .multires = multires, - .mode = mode, - .value = value, - .clip_planes_final = clip_planes_final, - .front_faces_only = front_faces_only, - }; + ED_view3d_ob_project_mat_get( + sgcontext->vc.rv3d, sgcontext->vc.obact, sgcontext->lasso.projviewobjmat); + BLI_lasso_boundbox(&sgcontext->lasso.boundbox, mcoords, mcoords_len); + sgcontext->lasso.width = sgcontext->lasso.boundbox.xmax - sgcontext->lasso.boundbox.xmin; + sgcontext->lasso.mask_px = BLI_BITMAP_NEW( + sgcontext->lasso.width * (sgcontext->lasso.boundbox.ymax - sgcontext->lasso.boundbox.ymin), + __func__); + + BLI_bitmap_draw_2d_poly_v2i_n(sgcontext->lasso.boundbox.xmin, + sgcontext->lasso.boundbox.ymin, + sgcontext->lasso.boundbox.xmax, + sgcontext->lasso.boundbox.ymax, + mcoords, + mcoords_len, + sculpt_gesture_lasso_px_cb, + sgcontext); - flip_v3_v3(data.view_normal, true_view_normal, symmpass); + BoundBox bb; + ED_view3d_clipping_calc(&bb, + sgcontext->true_clip_planes, + sgcontext->vc.region, + sgcontext->vc.obact, + &sgcontext->lasso.boundbox); + MEM_freeN((void *)mcoords); + + return sgcontext; +} - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, mask_box_select_task_cb, &settings); +static SculptGestureContext *sculpt_gesture_init_from_box(bContext *C, wmOperator *op) +{ + SculptGestureContext *sgcontext = MEM_callocN(sizeof(SculptGestureContext), + "sculpt gesture context box"); + sgcontext->shape_type = SCULPT_GESTURE_SHAPE_BOX; - if (nodes) { - MEM_freeN(nodes); - } - } - } + sculpt_gesture_context_init_common(C, op, sgcontext); - if (multires) { - multires_mark_as_modified(depsgraph, ob, MULTIRES_COORDS_MODIFIED); - } + rcti rect; + WM_operator_properties_border_to_rcti(op, &rect); - BKE_pbvh_update_vertex_data(pbvh, PBVH_UpdateMask); + BoundBox bb; + ED_view3d_clipping_calc( + &bb, sgcontext->true_clip_planes, sgcontext->vc.region, sgcontext->vc.obact, &rect); - SCULPT_undo_push_end(); + return sgcontext; +} - ED_region_tag_redraw(region); +static void sculpt_gesture_context_free(SculptGestureContext *sgcontext) +{ + MEM_SAFE_FREE(sgcontext->lasso.mask_px); + MEM_SAFE_FREE(sgcontext->nodes); + MEM_SAFE_FREE(sgcontext); +} - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); +static void flip_plane(float out[4], const float in[4], const char symm) +{ + if (symm & PAINT_SYMM_X) { + out[0] = -in[0]; + } + else { + out[0] = in[0]; + } + if (symm & PAINT_SYMM_Y) { + out[1] = -in[1]; + } + else { + out[1] = in[1]; + } + if (symm & PAINT_SYMM_Z) { + out[2] = -in[2]; + } + else { + out[2] = in[2]; + } - return true; + out[3] = in[3]; } -typedef struct LassoMaskData { - struct ViewContext *vc; - float projviewobjmat[4][4]; - BLI_bitmap *px; - int width; - /* Bounding box for scanfilling. */ - rcti rect; - int symmpass; +static void sculpt_gesture_flip_for_symmetry_pass(SculptGestureContext *sgcontext, + const ePaintSymmetryFlags symmpass) +{ + sgcontext->symmpass = symmpass; + for (int j = 0; j < 4; j++) { + flip_plane(sgcontext->clip_planes[j], sgcontext->true_clip_planes[j], symmpass); + } + negate_m4(sgcontext->clip_planes); + flip_v3_v3(sgcontext->view_normal, sgcontext->true_view_normal, symmpass); +} - MaskTaskData task_data; -} LassoMaskData; +static void sculpt_gesture_update_effected_nodes(SculptGestureContext *sgcontext) +{ + SculptSession *ss = sgcontext->ss; + float clip_planes[4][4]; + copy_m4_m4(clip_planes, sgcontext->clip_planes); + negate_m4(clip_planes); + PBVHFrustumPlanes frustum = {.planes = clip_planes, .num_planes = 4}; + BKE_pbvh_search_gather(ss->pbvh, + BKE_pbvh_node_frustum_contain_AABB, + &frustum, + &sgcontext->nodes, + &sgcontext->totnode); +} -/** - * Lasso select. This could be defined as part of #VIEW3D_OT_select_lasso, - * still the shortcuts conflict, so we will use a separate operator. - */ -static bool is_effected_lasso(LassoMaskData *data, const float co[3]) +static bool sculpt_gesture_is_effected_lasso(SculptGestureContext *sgcontext, const float co[3]) { float scr_co_f[2]; int scr_co_s[2]; float co_final[3]; - flip_v3_v3(co_final, co, data->symmpass); + flip_v3_v3(co_final, co, sgcontext->symmpass); + /* First project point to 2d space. */ - ED_view3d_project_float_v2_m4(data->vc->region, co_final, scr_co_f, data->projviewobjmat); + ED_view3d_project_float_v2_m4( + sgcontext->vc.region, co_final, scr_co_f, sgcontext->lasso.projviewobjmat); scr_co_s[0] = scr_co_f[0]; scr_co_s[1] = scr_co_f[1]; - /* Clip against screen, because lasso is limited to screen only. */ - if ((scr_co_s[0] < data->rect.xmin) || (scr_co_s[1] < data->rect.ymin) || - (scr_co_s[0] >= data->rect.xmax) || (scr_co_s[1] >= data->rect.ymax)) { + /* Clip against lasso boundbox. */ + LassoGestureData *lasso = &sgcontext->lasso; + if (!BLI_rcti_isect_pt(&lasso->boundbox, scr_co_s[0], scr_co_s[1])) { return false; } - scr_co_s[0] -= data->rect.xmin; - scr_co_s[1] -= data->rect.ymin; + scr_co_s[0] -= lasso->boundbox.xmin; + scr_co_s[1] -= lasso->boundbox.ymin; - return BLI_BITMAP_TEST_BOOL(data->px, scr_co_s[1] * data->width + scr_co_s[0]); + return BLI_BITMAP_TEST_BOOL(lasso->mask_px, scr_co_s[1] * lasso->width + scr_co_s[0]); } -static void mask_lasso_px_cb(int x, int x_end, int y, void *user_data) +static bool sculpt_gesture_is_vertex_effected(SculptGestureContext *sgcontext, PBVHVertexIter *vd) { - LassoMaskData *data = user_data; - int index = (y * data->width) + x; - int index_end = (y * data->width) + x_end; - do { - BLI_BITMAP_ENABLE(data->px, index); - } while (++index != index_end); + float vertex_normal[3]; + SCULPT_vertex_normal_get(sgcontext->ss, vd->index, vertex_normal); + float dot = dot_v3v3(sgcontext->view_normal, vertex_normal); + const bool is_effected_front_face = !(sgcontext->front_faces_only && dot < 0.0f); + + if (!is_effected_front_face) { + return false; + } + + switch (sgcontext->shape_type) { + case SCULPT_GESTURE_SHAPE_BOX: + return isect_point_planes_v3(sgcontext->clip_planes, 4, vd->co); + case SCULPT_GESTURE_SHAPE_LASSO: + return sculpt_gesture_is_effected_lasso(sgcontext, vd->co); + } + return false; } -static void mask_gesture_lasso_task_cb(void *__restrict userdata, +static void mask_gesture_apply_task_cb(void *__restrict userdata, const int i, const TaskParallelTLS *__restrict UNUSED(tls)) { - LassoMaskData *lasso_data = userdata; - MaskTaskData *data = &lasso_data->task_data; - - PBVHNode *node = data->nodes[i]; + SculptGestureContext *sgcontext = userdata; + Object *ob = sgcontext->vc.obact; + PBVHNode *node = sgcontext->nodes[i]; - const PaintMaskFloodMode mode = data->mode; - const float value = data->value; + const bool is_multires = BKE_pbvh_type(sgcontext->ss->pbvh) == PBVH_GRIDS; - PBVHVertexIter vi; + PBVHVertexIter vd; bool any_masked = false; + bool redraw = false; - float vertex_normal[3]; - - BKE_pbvh_vertex_iter_begin(data->pbvh, node, vi, PBVH_ITER_UNIQUE) + BKE_pbvh_vertex_iter_begin(sgcontext->ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - SCULPT_vertex_normal_get(data->ob->sculpt, vi.index, vertex_normal); - float dot = dot_v3v3(lasso_data->task_data.view_normal, vertex_normal); - const bool is_effected_front_face = !(data->front_faces_only && dot < 0.0f); - - if (is_effected_front_face && is_effected_lasso(lasso_data, vi.co)) { + if (sculpt_gesture_is_vertex_effected(sgcontext, &vd)) { + float prevmask = *vd.mask; if (!any_masked) { any_masked = true; - SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_MASK); + SCULPT_undo_push_node(ob, node, SCULPT_UNDO_MASK); - BKE_pbvh_node_mark_redraw(node); - if (data->multires) { + if (is_multires) { BKE_pbvh_node_mark_normals_update(node); } } - - mask_flood_fill_set_elem(vi.mask, mode, value); + mask_flood_fill_set_elem(vd.mask, sgcontext->mask_mode, sgcontext->mask_value); + if (prevmask != *vd.mask) { + redraw = true; + } } } BKE_pbvh_vertex_iter_end; + + if (redraw) { + BKE_pbvh_node_mark_update_mask(node); + } } -static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) +static void sculpt_gesture_apply(bContext *C, SculptGestureContext *mcontext) { - int mcoords_len; - const int(*mcoords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcoords_len); + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + BKE_sculpt_update_object_for_edit(depsgraph, mcontext->vc.obact, false, true, false); - if (mcoords) { - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - float clip_planes[4][4], clip_planes_final[4][4]; - BoundBox bb; - Object *ob; - ViewContext vc; - LassoMaskData data; - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - int symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; - PBVH *pbvh; - PBVHNode **nodes; - int totnode; - bool multires; - PaintMaskFloodMode mode = RNA_enum_get(op->ptr, "mode"); - float value = RNA_float_get(op->ptr, "value"); - const bool front_faces_only = RNA_boolean_get(op->ptr, "use_front_faces_only"); - - /* Calculations of individual vertices are done in 2D screen space to diminish the amount of - * calculations done. Bounding box PBVH collision is not computed against enclosing rectangle - * of lasso. */ - ED_view3d_viewcontext_init(C, &vc, depsgraph); - - /* Lasso data calculations. */ - data.vc = &vc; - ob = vc.obact; - ED_view3d_ob_project_mat_get(vc.rv3d, ob, data.projviewobjmat); - - BLI_lasso_boundbox(&data.rect, mcoords, mcoords_len); - data.width = data.rect.xmax - data.rect.xmin; - data.px = BLI_BITMAP_NEW(data.width * (data.rect.ymax - data.rect.ymin), __func__); - - BLI_bitmap_draw_2d_poly_v2i_n(data.rect.xmin, - data.rect.ymin, - data.rect.xmax, - data.rect.ymax, - mcoords, - mcoords_len, - mask_lasso_px_cb, - &data); - - ED_view3d_clipping_calc(&bb, clip_planes, vc.region, vc.obact, &data.rect); - - BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false); - pbvh = ob->sculpt->pbvh; - multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS); - - SCULPT_undo_push_begin("Mask lasso fill"); - - /* Calculate the view normal in object space. */ - float mat[3][3]; - float view_dir[3] = {0.0f, 0.0f, 1.0f}; - float true_view_normal[3]; - copy_m3_m4(mat, vc.rv3d->viewinv); - mul_m3_v3(mat, view_dir); - copy_m3_m4(mat, ob->imat); - mul_m3_v3(mat, view_dir); - normalize_v3_v3(true_view_normal, view_dir); - - for (int symmpass = 0; symmpass <= symm; symmpass++) { - if ((symmpass == 0) || (symm & symmpass && (symm != 5 || symmpass != 3) && - (symm != 6 || (symmpass != 3 && symmpass != 5)))) { - - /* Flip the planes symmetrically as needed. */ - for (int j = 0; j < 4; j++) { - flip_plane(clip_planes_final[j], clip_planes[j], symmpass); - } + SCULPT_undo_push_begin("Sculpt Gesture Apply"); - flip_v3_v3(data.task_data.view_normal, true_view_normal, symmpass); + for (ePaintSymmetryFlags symmpass = 0; symmpass <= mcontext->symm; symmpass++) { + if (SCULPT_is_symmetry_iteration_valid(symmpass, mcontext->symm)) { + sculpt_gesture_flip_for_symmetry_pass(mcontext, symmpass); + sculpt_gesture_update_effected_nodes(mcontext); - data.symmpass = symmpass; - - /* Gather nodes inside lasso's enclosing rectangle - * (should greatly help with bigger meshes). */ - PBVHFrustumPlanes frustum = {.planes = clip_planes_final, .num_planes = 4}; - BKE_pbvh_search_gather( - pbvh, BKE_pbvh_node_frustum_contain_AABB, &frustum, &nodes, &totnode); - - negate_m4(clip_planes_final); + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, mcontext->totnode); + BLI_task_parallel_range( + 0, mcontext->totnode, mcontext, mask_gesture_apply_task_cb, &settings); - data.task_data.ob = ob; - data.task_data.pbvh = pbvh; - data.task_data.nodes = nodes; - data.task_data.multires = multires; - data.task_data.mode = mode; - data.task_data.value = value; - data.task_data.front_faces_only = front_faces_only; + MEM_SAFE_FREE(mcontext->nodes); + } + } - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, mask_gesture_lasso_task_cb, &settings); + if (BKE_pbvh_type(mcontext->ss->pbvh) == PBVH_GRIDS) { + multires_mark_as_modified(depsgraph, mcontext->vc.obact, MULTIRES_COORDS_MODIFIED); + } - if (nodes) { - MEM_freeN(nodes); - } - } - } + BKE_pbvh_update_vertex_data(mcontext->ss->pbvh, PBVH_UpdateMask); - if (multires) { - multires_mark_as_modified(depsgraph, ob, MULTIRES_COORDS_MODIFIED); - } + SCULPT_undo_push_end(); - BKE_pbvh_update_vertex_data(pbvh, PBVH_UpdateMask); + ED_region_tag_redraw(mcontext->vc.region); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, mcontext->vc.obact); +} - SCULPT_undo_push_end(); +static void sculpt_gesture_init_mask_properties(SculptGestureContext *sgcontext, wmOperator *op) +{ + sgcontext->mask_mode = RNA_enum_get(op->ptr, "mode"); + sgcontext->mask_value = RNA_float_get(op->ptr, "value"); +} - ED_region_tag_redraw(vc.region); - MEM_freeN((void *)mcoords); - MEM_freeN(data.px); +static void paint_mask_gesture_operator_properties(wmOperatorType *ot) +{ + RNA_def_enum(ot->srna, "mode", mode_items, PAINT_MASK_FLOOD_VALUE, "Mode", NULL); + RNA_def_float( + ot->srna, + "value", + 1.0f, + 0.0f, + 1.0f, + "Value", + "Mask level to use when mode is 'Value'; zero means no masking and one is fully masked", + 0.0f, + 1.0f); +} - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); +static int paint_mask_gesture_box_exec(bContext *C, wmOperator *op) +{ + SculptGestureContext *sgcontext = sculpt_gesture_init_from_box(C, op); + if (!sgcontext) { + return OPERATOR_CANCELLED; + } + sculpt_gesture_init_mask_properties(sgcontext, op); + sculpt_gesture_apply(C, sgcontext); + sculpt_gesture_context_free(sgcontext); + return OPERATOR_FINISHED; +} - return OPERATOR_FINISHED; +static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) +{ + SculptGestureContext *sgcontext = sculpt_gesture_init_from_lasso(C, op); + if (!sgcontext) { + return OPERATOR_CANCELLED; } - return OPERATOR_PASS_THROUGH; + sculpt_gesture_init_mask_properties(sgcontext, op); + sculpt_gesture_apply(C, sgcontext); + sculpt_gesture_context_free(sgcontext); + return OPERATOR_FINISHED; } void PAINT_OT_mask_lasso_gesture(wmOperatorType *ot) @@ -625,23 +617,9 @@ void PAINT_OT_mask_lasso_gesture(wmOperatorType *ot) /* Properties. */ WM_operator_properties_gesture_lasso(ot); + sculpt_gesture_operator_properties(ot); - RNA_def_enum(ot->srna, "mode", mode_items, PAINT_MASK_FLOOD_VALUE, "Mode", NULL); - RNA_def_float( - ot->srna, - "value", - 1.0f, - 0.0f, - 1.0f, - "Value", - "Mask level to use when mode is 'Value'; zero means no masking and one is fully masked", - 0.0f, - 1.0f); - RNA_def_boolean(ot->srna, - "use_front_faces_only", - false, - "Front Faces Only", - "Affect only faces facing towards the view"); + paint_mask_gesture_operator_properties(ot); } void PAINT_OT_mask_box_gesture(wmOperatorType *ot) @@ -660,21 +638,7 @@ void PAINT_OT_mask_box_gesture(wmOperatorType *ot) /* Properties. */ WM_operator_properties_border(ot); + sculpt_gesture_operator_properties(ot); - RNA_def_enum(ot->srna, "mode", mode_items, PAINT_MASK_FLOOD_VALUE, "Mode", NULL); - RNA_def_float( - ot->srna, - "value", - 1.0f, - 0.0f, - 1.0f, - "Value", - "Mask level to use when mode is 'Value'; zero means no masking and one is fully masked", - 0.0f, - 1.0f); - RNA_def_boolean(ot->srna, - "use_front_faces_only", - false, - "Front Faces Only", - "Affect only faces facing towards the view"); + paint_mask_gesture_operator_properties(ot); } diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index 52cdebf3fd5..e709224f370 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -691,6 +691,14 @@ static float paint_space_stroke_spacing(bContext *C, spacing = spacing * (1.5f - spacing_pressure); } + if (SCULPT_is_cloth_deform_brush(brush)) { + /* The spacing in tools that use the cloth solver should not be affected by the brush radius to + * avoid affecting the simulation update rate when changing the radius of the brush. + With a value of 100 and the brush default of 10 for spacing, a simulation step runs every 2 + pixels movement of the cursor. */ + size_clamp = 100.0f; + } + /* stroke system is used for 2d paint too, so we need to account for * the fact that brush can be scaled there. */ spacing *= stroke->zoom_2d; @@ -1001,7 +1009,7 @@ bool paint_space_stroke_enabled(Brush *br, ePaintMode mode) return false; } - if (br->sculpt_tool == SCULPT_TOOL_CLOTH) { + if (br->sculpt_tool == SCULPT_TOOL_CLOTH || SCULPT_is_cloth_deform_brush(br)) { /* The Cloth Brush is a special case for stroke spacing. Even if it has grab modes which do * not support dynamic size, stroke spacing needs to be enabled so it is possible to control * whether the simulation runs constantly or only when the brush moves when using the cloth diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index e1c1b8ee5fb..7a066f35f23 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -2273,7 +2273,7 @@ static float brush_strength(const Sculpt *sd, case SCULPT_TOOL_DISPLACEMENT_ERASER: return alpha * pressure * overlap * feather; case SCULPT_TOOL_CLOTH: - if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB) { + if (ELEM(brush->cloth_deform_type, BRUSH_CLOTH_DEFORM_GRAB, BRUSH_CLOTH_DEFORM_SNAKE_HOOK)) { /* Grab deform uses the same falloff as a regular grab brush. */ return root_alpha * feather; } @@ -6634,13 +6634,19 @@ static float sculpt_brush_dynamic_size_get(Brush *brush, StrokeCache *cache, flo * generally used to create grab deformations. */ static bool sculpt_needs_delta_from_anchored_origin(Brush *brush) { - return ELEM(brush->sculpt_tool, - SCULPT_TOOL_GRAB, - SCULPT_TOOL_POSE, - SCULPT_TOOL_BOUNDARY, - SCULPT_TOOL_THUMB, - SCULPT_TOOL_ELASTIC_DEFORM) || - SCULPT_is_cloth_deform_brush(brush); + if (ELEM(brush->sculpt_tool, + SCULPT_TOOL_GRAB, + SCULPT_TOOL_POSE, + SCULPT_TOOL_BOUNDARY, + SCULPT_TOOL_THUMB, + SCULPT_TOOL_ELASTIC_DEFORM)) { + return true; + } + if (brush->sculpt_tool == SCULPT_TOOL_CLOTH && + brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB) { + return true; + } + return false; } /* In these brushes the grab delta is calculated from the previous stroke location, which is used @@ -6648,7 +6654,7 @@ static bool sculpt_needs_delta_from_anchored_origin(Brush *brush) static bool sculpt_needs_delta_for_tip_orientation(Brush *brush) { if (brush->sculpt_tool == SCULPT_TOOL_CLOTH) { - return !SCULPT_is_cloth_deform_brush(brush); + return brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_SNAKE_HOOK; } return ELEM(brush->sculpt_tool, SCULPT_TOOL_CLAY_STRIPS, @@ -6694,7 +6700,9 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru copy_v3_v3(cache->orig_grab_location, cache->true_location); } } - else if (tool == SCULPT_TOOL_SNAKE_HOOK) { + else if (tool == SCULPT_TOOL_SNAKE_HOOK || + (tool == SCULPT_TOOL_CLOTH && + brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_SNAKE_HOOK)) { add_v3_v3(cache->true_location, cache->grab_delta); } diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c index 9a3fbe474b8..c3666c8aaad 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.c +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c @@ -169,6 +169,8 @@ static void cloth_brush_add_length_constraint(SculptSession *ss, length_constraint->elem_position_a = cloth_sim->pos[v1]; length_constraint->elem_position_b = cloth_sim->pos[v2]; + length_constraint->type = SCULPT_CLOTH_CONSTRAINT_STRUCTURAL; + if (use_persistent) { length_constraint->length = len_v3v3(SCULPT_vertex_persistent_co_get(ss, v1), SCULPT_vertex_persistent_co_get(ss, v2)); @@ -201,6 +203,8 @@ static void cloth_brush_add_softbody_constraint(SculptClothSimulation *cloth_sim length_constraint->elem_position_a = cloth_sim->pos[v]; length_constraint->elem_position_b = cloth_sim->init_pos[v]; + length_constraint->type = SCULPT_CLOTH_CONSTRAINT_SOFTBODY; + length_constraint->length = 0.0f; length_constraint->strength = strength; @@ -220,6 +224,8 @@ static void cloth_brush_add_deformation_constraint(SculptClothSimulation *cloth_ length_constraint->elem_index_a = v; length_constraint->elem_index_b = v; + length_constraint->type = SCULPT_CLOTH_CONSTRAINT_DEFORMATION; + length_constraint->elem_position_a = cloth_sim->pos[v]; length_constraint->elem_position_b = cloth_sim->deformation_pos[v]; @@ -301,12 +307,18 @@ static void do_cloth_brush_build_constraints_task_cb_ex( if (brush && brush->sculpt_tool == SCULPT_TOOL_CLOTH) { /* The cloth brush works by applying forces in most of its modes, but some of them require * deformation coordinates to make the simulation stable. */ - if (cloth_is_deform_brush && len_squared < radius_squared) { - /* When a deform brush is used as part of the cloth brush, deformation constraints are - * created with different strengths and only inside the radius of the brush. */ + if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB && len_squared < radius_squared) { + /* When the grab brush brush is used as part of the cloth brush, deformation constraints + * are created with different strengths and only inside the radius of the brush. */ const float fade = BKE_brush_curve_strength(brush, sqrtf(len_squared), ss->cache->radius); cloth_brush_add_deformation_constraint(data->cloth_sim, vd.index, fade); } + else if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_SNAKE_HOOK) { + /* Cloth Snake Hook creates deformation constraint with fixed strength because the strength + * is controlled per iteration using cloth_sim->deformation_strength. */ + cloth_brush_add_deformation_constraint( + data->cloth_sim, vd.index, CLOTH_DEFORMATION_TARGET_STRENGTH); + } } else if (data->cloth_sim->deformation_pos) { /* Any other tool that target the cloth simulation handle the falloff in @@ -397,7 +409,7 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata, brush, ss->cache->radius, ss->cache->initial_location, cloth_sim->init_pos[vd.index]); float current_vertex_location[3]; - if (SCULPT_is_cloth_deform_brush(brush)) { + if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB) { SCULPT_orig_vert_data_update(&orig_data, &vd); copy_v3_v3(current_vertex_location, orig_data.co); } @@ -456,6 +468,12 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata, fade); zero_v3(force); break; + case BRUSH_CLOTH_DEFORM_SNAKE_HOOK: + copy_v3_v3(cloth_sim->deformation_pos[vd.index], cloth_sim->pos[vd.index]); + madd_v3_v3fl(cloth_sim->deformation_pos[vd.index], ss->cache->grab_delta_symmetry, fade); + cloth_sim->deformation_strength[vd.index] = fade; + zero_v3(force); + break; case BRUSH_CLOTH_DEFORM_PINCH_POINT: if (use_falloff_plane) { float distance = dist_signed_to_plane_v3(vd.co, deform_plane); @@ -718,13 +736,21 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss, cloth_sim->init_pos[v2]) : 1.0f; + float deformation_strength = 1.0f; + if (constraint->type == SCULPT_CLOTH_CONSTRAINT_DEFORMATION) { + deformation_strength = (cloth_sim->deformation_strength[v1] + + cloth_sim->deformation_strength[v2]) * + 0.5f; + } + madd_v3_v3fl(cloth_sim->pos[v1], correction_vector_half, - 1.0f * mask_v1 * sim_factor_v1 * constraint->strength); + 1.0f * mask_v1 * sim_factor_v1 * constraint->strength * deformation_strength); if (v1 != v2) { madd_v3_v3fl(cloth_sim->pos[v2], correction_vector_half, - -1.0f * mask_v2 * sim_factor_v2 * constraint->strength); + -1.0f * mask_v2 * sim_factor_v2 * constraint->strength * + deformation_strength); } } } @@ -824,6 +850,15 @@ static void cloth_brush_apply_brush_foces(Sculpt *sd, Object *ob, PBVHNode **nod } } + if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_SNAKE_HOOK) { + /* Set the deformation strength to 0. Snake hook will initialize the strength in the required + * area. */ + const int totverts = SCULPT_vertex_count_get(ss); + for (int i = 0; i < totverts; i++) { + ss->cache->cloth_sim->deformation_strength[i] = 0.0f; + } + } + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); BLI_task_parallel_range( @@ -862,6 +897,8 @@ SculptClothSimulation *SCULPT_cloth_brush_simulation_create(SculptSession *ss, if (brush && SCULPT_is_cloth_deform_brush(brush)) { cloth_sim->deformation_pos = MEM_calloc_arrayN( totverts, sizeof(float[3]), "cloth sim deformation positions"); + cloth_sim->deformation_strength = MEM_calloc_arrayN( + totverts, sizeof(float), "cloth sim deformation strength"); } cloth_sim->mass = cloth_mass; @@ -921,6 +958,7 @@ void SCULPT_cloth_brush_simulation_init(SculptSession *ss, SculptClothSimulation copy_v3_v3(cloth_sim->prev_pos[i], SCULPT_vertex_co_get(ss, i)); if (has_deformation_pos) { copy_v3_v3(cloth_sim->deformation_pos[i], SCULPT_vertex_co_get(ss, i)); + cloth_sim->deformation_strength[i] = 1.0f; } } } @@ -986,6 +1024,7 @@ void SCULPT_cloth_simulation_free(struct SculptClothSimulation *cloth_sim) MEM_SAFE_FREE(cloth_sim->length_constraint_tweak); MEM_SAFE_FREE(cloth_sim->deformation_pos); MEM_SAFE_FREE(cloth_sim->init_pos); + MEM_SAFE_FREE(cloth_sim->deformation_strength); if (cloth_sim->collider_list) { BKE_collider_cache_free(&cloth_sim->collider_list); } @@ -1071,6 +1110,25 @@ static EnumPropertyItem prop_cloth_filter_type[] = { {0, NULL, 0, NULL, NULL}, }; +static EnumPropertyItem prop_cloth_filter_orientation_items[] = { + {SCULPT_FILTER_ORIENTATION_LOCAL, + "LOCAL", + 0, + "Local", + "Use the local axis to limit the force and set the gravity direction"}, + {SCULPT_FILTER_ORIENTATION_WORLD, + "WORLD", + 0, + "World", + "Use the global axis to limit the force and set the gravity direction"}, + {SCULPT_FILTER_ORIENTATION_VIEW, + "VIEW", + 0, + "View", + "Use the view axis to limit the force and set the gravity direction"}, + {0, NULL, 0, NULL, NULL}, +}; + typedef enum eClothFilterForceAxis { CLOTH_FILTER_FORCE_X = 1 << 0, CLOTH_FILTER_FORCE_Y = 1 << 1, @@ -1120,7 +1178,15 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata, switch (filter_type) { case CLOTH_FILTER_GRAVITY: - force[2] = -data->filter_strength * fade; + if (ss->filter_cache->orientation == SCULPT_FILTER_ORIENTATION_VIEW) { + /* When using the view orientation apply gravity in the -Y axis, this way objects will + * fall down instead of backwards. */ + force[1] = -data->filter_strength * fade; + } + else { + force[2] = -data->filter_strength * fade; + } + SCULPT_filter_to_object_space(force, ss->filter_cache); break; case CLOTH_FILTER_INFLATE: { float normal[3]; @@ -1138,11 +1204,13 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata, break; } + SCULPT_filter_to_orientation_space(force, ss->filter_cache); for (int axis = 0; axis < 3; axis++) { if (!ss->filter_cache->enabled_force_axis[axis]) { force[axis] = 0.0f; } } + SCULPT_filter_to_object_space(force, ss->filter_cache); add_v3_v3(force, sculpt_gravity); @@ -1264,6 +1332,9 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent ss->filter_cache->enabled_force_axis[1] = force_axis & CLOTH_FILTER_FORCE_Y; ss->filter_cache->enabled_force_axis[2] = force_axis & CLOTH_FILTER_FORCE_Z; + SculptFilterOrientation orientation = RNA_enum_get(op->ptr, "orientation"); + ss->filter_cache->orientation = orientation; + WM_event_add_modal_handler(C, op); return OPERATOR_RUNNING_MODAL; } @@ -1297,6 +1368,12 @@ void SCULPT_OT_cloth_filter(struct wmOperatorType *ot) CLOTH_FILTER_FORCE_X | CLOTH_FILTER_FORCE_Y | CLOTH_FILTER_FORCE_Z, "Force axis", "Apply the force in the selected axis"); + RNA_def_enum(ot->srna, + "orientation", + prop_cloth_filter_orientation_items, + SCULPT_FILTER_ORIENTATION_LOCAL, + "Orientation", + "Orientation of the axis to limit the filter force"); RNA_def_float(ot->srna, "cloth_mass", 1.0f, diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c index 56e70a8d47a..619a1b975b6 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c @@ -181,7 +181,7 @@ void SCULPT_filter_cache_free(SculptSession *ss) MEM_SAFE_FREE(ss->filter_cache); } -typedef enum eSculptMeshFilterTypes { +typedef enum eSculptMeshFilterType { MESH_FILTER_SMOOTH = 0, MESH_FILTER_SCALE = 1, MESH_FILTER_INFLATE = 2, @@ -193,7 +193,7 @@ typedef enum eSculptMeshFilterTypes { MESH_FILTER_SHARPEN = 8, MESH_FILTER_ENHANCE_DETAILS = 9, MESH_FILTER_ERASE_DISPLACEMENT = 10, -} eSculptMeshFilterTypes; +} eSculptMeshFilterType; static EnumPropertyItem prop_mesh_filter_types[] = { {MESH_FILTER_SMOOTH, "SMOOTH", 0, "Smooth", "Smooth mesh"}, @@ -258,7 +258,7 @@ static EnumPropertyItem prop_mesh_filter_orientation_items[] = { {0, NULL, 0, NULL, NULL}, }; -static bool sculpt_mesh_filter_needs_pmap(int filter_type, bool use_face_sets) +static bool sculpt_mesh_filter_needs_pmap(eSculptMeshFilterType filter_type, bool use_face_sets) { return use_face_sets || ELEM(filter_type, MESH_FILTER_SMOOTH, @@ -277,7 +277,7 @@ static void mesh_filter_task_cb(void *__restrict userdata, SculptSession *ss = data->ob->sculpt; PBVHNode *node = data->nodes[i]; - const int filter_type = data->filter_type; + const eSculptMeshFilterType filter_type = data->filter_type; SculptOrigVertData orig_data; SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i]); @@ -486,49 +486,80 @@ static void mesh_filter_task_cb(void *__restrict userdata, static void mesh_filter_enhance_details_init_directions(SculptSession *ss) { const int totvert = SCULPT_vertex_count_get(ss); + FilterCache *filter_cache = ss->filter_cache; + + filter_cache->detail_directions = MEM_malloc_arrayN( + totvert, sizeof(float[3]), "detail directions"); for (int i = 0; i < totvert; i++) { float avg[3]; SCULPT_neighbor_coords_average(ss, avg, i); - sub_v3_v3v3(ss->filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i)); + sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i)); } } +static void mesh_filter_surface_smooth_init(SculptSession *ss, + const float shape_preservation, + const float current_vertex_displacement) +{ + const int totvert = SCULPT_vertex_count_get(ss); + FilterCache *filter_cache = ss->filter_cache; + + filter_cache->surface_smooth_laplacian_disp = MEM_malloc_arrayN( + totvert, sizeof(float[3]), "surface smooth displacement"); + filter_cache->surface_smooth_shape_preservation = shape_preservation; + filter_cache->surface_smooth_current_vertex = current_vertex_displacement; +} + static void mesh_filter_init_limit_surface_co(SculptSession *ss) { const int totvert = SCULPT_vertex_count_get(ss); - ss->filter_cache->limit_surface_co = MEM_malloc_arrayN( - 3 * sizeof(float), totvert, "limit surface co"); + FilterCache *filter_cache = ss->filter_cache; + + filter_cache->limit_surface_co = MEM_malloc_arrayN( + sizeof(float[3]), totvert, "limit surface co"); for (int i = 0; i < totvert; i++) { - SCULPT_vertex_limit_surface_get(ss, i, ss->filter_cache->limit_surface_co[i]); + SCULPT_vertex_limit_surface_get(ss, i, filter_cache->limit_surface_co[i]); } } -static void mesh_filter_sharpen_init_factors(SculptSession *ss) +static void mesh_filter_sharpen_init(SculptSession *ss, + const float smooth_ratio, + const float intensify_detail_strength, + const int curvature_smooth_iterations) { const int totvert = SCULPT_vertex_count_get(ss); + FilterCache *filter_cache = ss->filter_cache; + + filter_cache->sharpen_smooth_ratio = smooth_ratio; + filter_cache->sharpen_intensify_detail_strength = intensify_detail_strength; + filter_cache->sharpen_curvature_smooth_iterations = curvature_smooth_iterations; + filter_cache->sharpen_factor = MEM_malloc_arrayN(sizeof(float), totvert, "sharpen factor"); + filter_cache->detail_directions = MEM_malloc_arrayN( + totvert, sizeof(float[3]), "sharpen detail direction"); + for (int i = 0; i < totvert; i++) { float avg[3]; SCULPT_neighbor_coords_average(ss, avg, i); - sub_v3_v3v3(ss->filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i)); - ss->filter_cache->sharpen_factor[i] = len_v3(ss->filter_cache->detail_directions[i]); + sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i)); + filter_cache->sharpen_factor[i] = len_v3(filter_cache->detail_directions[i]); } float max_factor = 0.0f; for (int i = 0; i < totvert; i++) { - if (ss->filter_cache->sharpen_factor[i] > max_factor) { - max_factor = ss->filter_cache->sharpen_factor[i]; + if (filter_cache->sharpen_factor[i] > max_factor) { + max_factor = filter_cache->sharpen_factor[i]; } } max_factor = 1.0f / max_factor; for (int i = 0; i < totvert; i++) { - ss->filter_cache->sharpen_factor[i] *= max_factor; - ss->filter_cache->sharpen_factor[i] = 1.0f - pow2f(1.0f - ss->filter_cache->sharpen_factor[i]); + filter_cache->sharpen_factor[i] *= max_factor; + filter_cache->sharpen_factor[i] = 1.0f - pow2f(1.0f - filter_cache->sharpen_factor[i]); } /* Smooth the calculated factors and directions to remove high frecuency detail. */ for (int smooth_iterations = 0; - smooth_iterations < ss->filter_cache->sharpen_curvature_smooth_iterations; + smooth_iterations < filter_cache->sharpen_curvature_smooth_iterations; smooth_iterations++) { for (int i = 0; i < totvert; i++) { float direction_avg[3] = {0.0f, 0.0f, 0.0f}; @@ -537,15 +568,15 @@ static void mesh_filter_sharpen_init_factors(SculptSession *ss) SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) { - add_v3_v3(direction_avg, ss->filter_cache->detail_directions[ni.index]); - sharpen_avg += ss->filter_cache->sharpen_factor[ni.index]; + add_v3_v3(direction_avg, filter_cache->detail_directions[ni.index]); + sharpen_avg += filter_cache->sharpen_factor[ni.index]; total++; } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); if (total > 0) { - mul_v3_v3fl(ss->filter_cache->detail_directions[i], direction_avg, 1.0f / total); - ss->filter_cache->sharpen_factor[i] = sharpen_avg / total; + mul_v3_v3fl(filter_cache->detail_directions[i], direction_avg, 1.0f / total); + filter_cache->sharpen_factor[i] = sharpen_avg / total; } } } @@ -590,7 +621,7 @@ static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent * Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); SculptSession *ss = ob->sculpt; Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - int filter_type = RNA_enum_get(op->ptr, "type"); + eSculptMeshFilterType filter_type = RNA_enum_get(op->ptr, "type"); float filter_strength = RNA_float_get(op->ptr, "strength"); const bool use_face_sets = RNA_boolean_get(op->ptr, "use_face_sets"); @@ -654,17 +685,21 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent Object *ob = CTX_data_active_object(C); Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - int filter_type = RNA_enum_get(op->ptr, "type"); SculptSession *ss = ob->sculpt; - PBVH *pbvh = ob->sculpt->pbvh; - int deform_axis = RNA_enum_get(op->ptr, "deform_axis"); + const eMeshFilterDeformAxis deform_axis = RNA_enum_get(op->ptr, "deform_axis"); + const eSculptMeshFilterType filter_type = RNA_enum_get(op->ptr, "type"); + const bool use_face_sets = RNA_boolean_get(op->ptr, "use_face_sets"); + const bool needs_topology_info = sculpt_mesh_filter_needs_pmap(filter_type, use_face_sets); + if (deform_axis == 0) { + /* All axis are disabled, so the filter is not going to produce any deformation. */ return OPERATOR_CANCELLED; } - if (RNA_boolean_get(op->ptr, "use_face_sets")) { - /* Update the active vertex */ + if (use_face_sets) { + /* Update the active face set manually as the paint cursor is not enabled when using the Mesh + * Filter Tool. */ float mouse[2]; SculptCursorGeometryInfo sgi; mouse[0] = event->mval[0]; @@ -672,67 +707,48 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); } - const bool use_face_sets = RNA_boolean_get(op->ptr, "use_face_sets"); - SCULPT_vertex_random_access_ensure(ss); - - const bool needs_topology_info = sculpt_mesh_filter_needs_pmap(filter_type, use_face_sets); BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_topology_info, false, false); if (needs_topology_info) { SCULPT_boundary_info_ensure(ob); } - const int totvert = SCULPT_vertex_count_get(ss); - if (BKE_pbvh_type(pbvh) == PBVH_FACES && needs_topology_info && !ob->sculpt->pmap) { - return OPERATOR_CANCELLED; - } - - SCULPT_undo_push_begin("Mesh filter"); - - if (ELEM(filter_type, MESH_FILTER_RELAX, MESH_FILTER_RELAX_FACE_SETS)) { - SCULPT_boundary_info_ensure(ob); - } + SCULPT_undo_push_begin("Mesh Filter"); SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS); - if (use_face_sets) { - ss->filter_cache->active_face_set = SCULPT_active_face_set_get(ss); - } - else { - 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(sizeof(float[3]) * totvert, - "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"); - } - - if (RNA_enum_get(op->ptr, "type") == MESH_FILTER_SHARPEN) { - ss->filter_cache->sharpen_smooth_ratio = RNA_float_get(op->ptr, "sharpen_smooth_ratio"); - ss->filter_cache->sharpen_intensify_detail_strength = RNA_float_get( - op->ptr, "sharpen_intensify_detail_strength"); - ss->filter_cache->sharpen_curvature_smooth_iterations = RNA_int_get( - op->ptr, "sharpen_curvature_smooth_iterations"); - - ss->filter_cache->sharpen_factor = MEM_mallocN(sizeof(float) * totvert, "sharpen factor"); - ss->filter_cache->detail_directions = MEM_malloc_arrayN( - totvert, sizeof(float[3]), "sharpen detail direction"); - - mesh_filter_sharpen_init_factors(ss); - } - - if (RNA_enum_get(op->ptr, "type") == MESH_FILTER_ENHANCE_DETAILS) { - ss->filter_cache->detail_directions = MEM_malloc_arrayN( - totvert, sizeof(float[3]), "detail direction"); - mesh_filter_enhance_details_init_directions(ss); - } + FilterCache *filter_cache = ss->filter_cache; + filter_cache->active_face_set = use_face_sets ? SCULPT_active_face_set_get(ss) : + SCULPT_FACE_SET_NONE; - if (RNA_enum_get(op->ptr, "type") == MESH_FILTER_ERASE_DISPLACEMENT) { - mesh_filter_init_limit_surface_co(ss); + switch (filter_type) { + case MESH_FILTER_SURFACE_SMOOTH: { + const float shape_preservation = RNA_float_get(op->ptr, "surface_smooth_shape_preservation"); + const float current_vertex_displacement = RNA_float_get(op->ptr, + "surface_smooth_current_vertex"); + mesh_filter_surface_smooth_init(ss, shape_preservation, current_vertex_displacement); + break; + } + case MESH_FILTER_SHARPEN: { + const float smooth_ratio = RNA_float_get(op->ptr, "sharpen_smooth_ratio"); + const float intensify_detail_strength = RNA_float_get(op->ptr, + "sharpen_intensify_detail_strength"); + const int curvature_smooth_iterations = RNA_int_get(op->ptr, + "sharpen_curvature_smooth_iterations"); + mesh_filter_sharpen_init( + ss, smooth_ratio, intensify_detail_strength, curvature_smooth_iterations); + break; + } + case MESH_FILTER_ENHANCE_DETAILS: { + mesh_filter_enhance_details_init_directions(ss); + break; + } + case MESH_FILTER_ERASE_DISPLACEMENT: { + mesh_filter_init_limit_surface_co(ss); + break; + } + default: + break; } ss->filter_cache->enabled_axis[0] = deform_axis & MESH_FILTER_DEFORM_X; diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 548ef00ad87..47a375a2318 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -398,8 +398,9 @@ void SCULPT_cloth_plane_falloff_preview_draw(const uint gpuattr, BLI_INLINE bool SCULPT_is_cloth_deform_brush(const Brush *brush) { - return (brush->sculpt_tool == SCULPT_TOOL_CLOTH && - brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB) || + return (brush->sculpt_tool == SCULPT_TOOL_CLOTH && ELEM(brush->cloth_deform_type, + BRUSH_CLOTH_DEFORM_GRAB, + BRUSH_CLOTH_DEFORM_SNAKE_HOOK)) || /* All brushes that are not the cloth brush deform the simulation using softbody constriants instead of applying forces. */ (brush->sculpt_tool != SCULPT_TOOL_CLOTH && diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c index 63fe8643628..87ee7480c92 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.c +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c @@ -66,25 +66,40 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3], { float avg[3] = {0.0f, 0.0f, 0.0f}; int total = 0; - - if (SCULPT_vertex_is_boundary(ss, index)) { - copy_v3_v3(result, SCULPT_vertex_co_get(ss, index)); - return; - } + int neighbor_count = 0; + const bool is_boundary = SCULPT_vertex_is_boundary(ss, index); SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) { - add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index)); - total++; + neighbor_count++; + if (is_boundary) { + /* Boundary vertices use only other boundary vertices. */ + if (SCULPT_vertex_is_boundary(ss, ni.index)) { + add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index)); + total++; + } + } + else { + /* Interior vertices use all neighbors. */ + add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index)); + total++; + } } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); - if (total > 0) { - mul_v3_v3fl(result, avg, 1.0f / total); + /* Do not modify corner vertices. */ + if (neighbor_count <= 2) { + copy_v3_v3(result, SCULPT_vertex_co_get(ss, index)); + return; } - else { + + /* Avoid division by 0 when there are no neighbors. */ + if (total == 0) { copy_v3_v3(result, SCULPT_vertex_co_get(ss, index)); + return; } + + mul_v3_v3fl(result, avg, 1.0f / total); } /* For bmesh: Average surrounding verts based on an orthogonality measure. @@ -316,7 +331,7 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, } else { float avg[3], val[3]; - SCULPT_neighbor_coords_average(ss, avg, vd.index); + SCULPT_neighbor_coords_average_interior(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); diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c index bdada4d2565..b52b04eba3a 100644 --- a/source/blender/editors/sculpt_paint/sculpt_transform.c +++ b/source/blender/editors/sculpt_paint/sculpt_transform.c @@ -326,6 +326,12 @@ static int sculpt_set_pivot_position_exec(bContext *C, wmOperator *op) MEM_SAFE_FREE(nodes); } + /* Update the viewport navigation rotation origin. */ + UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; + copy_v3_v3(ups->average_stroke_accum, ss->pivot_pos); + ups->average_stroke_counter = 1; + ups->last_stroke_valid = true; + ED_region_tag_redraw(region); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data); |