diff options
Diffstat (limited to 'source/blender/editors/sculpt_paint')
23 files changed, 4201 insertions, 1292 deletions
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index 752a5c36010..a5cc262ddcd 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -31,6 +31,7 @@ set(INC ../../render/extern/include ../../windowmanager ../../../../intern/atomic + ../../../../intern/clog ../../../../intern/glew-mx ../../../../intern/guardedalloc ) @@ -47,7 +48,6 @@ set(SRC paint_image.c paint_image_2d.c paint_image_proj.c - paint_image_undo.c paint_mask.c paint_ops.c paint_stroke.c diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index 65e10f98753..774d4ef09b1 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -45,13 +45,17 @@ #include "BKE_node.h" #include "BKE_paint.h" #include "BKE_colortools.h" +#include "BKE_object.h" #include "WM_api.h" +#include "wm_cursors.h" #include "IMB_imbuf_types.h" #include "ED_view3d.h" +#include "DEG_depsgraph.h" + #include "GPU_draw.h" #include "GPU_immediate.h" #include "GPU_immediate_util.h" @@ -87,6 +91,7 @@ typedef struct CursorSnapshot { GLuint overlay_texture; int size; int zoom; + int curve_preset; } CursorSnapshot; static TexSnapshot primary_snap = {0}; @@ -422,7 +427,8 @@ static int load_tex_cursor(Brush *br, ViewContext *vc, float zoom) int size; const bool refresh = !cursor_snap.overlay_texture || - (overlay_flags & PAINT_OVERLAY_INVALID_CURVE) || cursor_snap.zoom != zoom; + (overlay_flags & PAINT_OVERLAY_INVALID_CURVE) || cursor_snap.zoom != zoom || + cursor_snap.curve_preset != br->curve_preset; init = (cursor_snap.overlay_texture != 0); @@ -502,6 +508,7 @@ static int load_tex_cursor(Brush *br, ViewContext *vc, float zoom) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + cursor_snap.curve_preset = br->curve_preset; BKE_paint_reset_overlay_invalid(PAINT_OVERLAY_INVALID_CURVE); return 1; @@ -602,7 +609,7 @@ static bool sculpt_get_brush_geometry(bContext *C, /* Draw an overlay that shows what effect the brush's texture will * have on brush strength */ -static void paint_draw_tex_overlay(UnifiedPaintSettings *ups, +static bool paint_draw_tex_overlay(UnifiedPaintSettings *ups, Brush *brush, ViewContext *vc, int x, @@ -622,7 +629,7 @@ static void paint_draw_tex_overlay(UnifiedPaintSettings *ups, if (!(mtex->tex) || !((mtex->brush_map_mode == MTEX_MAP_MODE_STENCIL) || (valid && ELEM(mtex->brush_map_mode, MTEX_MAP_MODE_VIEW, MTEX_MAP_MODE_TILED)))) { - return; + return false; } if (load_tex(brush, vc, zoom, col, primary)) { @@ -728,18 +735,19 @@ static void paint_draw_tex_overlay(UnifiedPaintSettings *ups, GPU_matrix_pop(); } } + return true; } /* Draw an overlay that shows what effect the brush's texture will * have on brush strength */ -static void paint_draw_cursor_overlay( +static bool paint_draw_cursor_overlay( UnifiedPaintSettings *ups, Brush *brush, ViewContext *vc, int x, int y, float zoom) { rctf quad; /* check for overlay mode */ if (!(brush->overlay_flags & BRUSH_OVERLAY_CURSOR)) { - return; + return false; } if (load_tex_cursor(brush, vc, zoom)) { @@ -811,9 +819,10 @@ static void paint_draw_cursor_overlay( GPU_matrix_pop(); } } + return true; } -static void paint_draw_alpha_overlay(UnifiedPaintSettings *ups, +static bool paint_draw_alpha_overlay(UnifiedPaintSettings *ups, Brush *brush, ViewContext *vc, int x, @@ -824,6 +833,9 @@ static void paint_draw_alpha_overlay(UnifiedPaintSettings *ups, /* Color means that primary brush texture is colored and * secondary is used for alpha/mask control. */ bool col = ELEM(mode, PAINT_MODE_TEXTURE_3D, PAINT_MODE_TEXTURE_2D, PAINT_MODE_VERTEX); + + bool alpha_overlay_active = false; + eOverlayControlFlags flags = BKE_paint_get_overlay_flags(); gpuPushAttr(GPU_DEPTH_BUFFER_BIT | GPU_BLEND_BIT); @@ -836,37 +848,43 @@ static void paint_draw_alpha_overlay(UnifiedPaintSettings *ups, /* Colored overlay should be drawn separately. */ if (col) { if (!(flags & PAINT_OVERLAY_OVERRIDE_PRIMARY)) { - paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, true, true); + alpha_overlay_active = paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, true, true); } if (!(flags & PAINT_OVERLAY_OVERRIDE_SECONDARY)) { - paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, false, false); + alpha_overlay_active = paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, false, false); } if (!(flags & PAINT_OVERLAY_OVERRIDE_CURSOR)) { - paint_draw_cursor_overlay(ups, brush, vc, x, y, zoom); + alpha_overlay_active = paint_draw_cursor_overlay(ups, brush, vc, x, y, zoom); } } else { if (!(flags & PAINT_OVERLAY_OVERRIDE_PRIMARY) && (mode != PAINT_MODE_WEIGHT)) { - paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, false, true); + alpha_overlay_active = paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, false, true); } if (!(flags & PAINT_OVERLAY_OVERRIDE_CURSOR)) { - paint_draw_cursor_overlay(ups, brush, vc, x, y, zoom); + alpha_overlay_active = paint_draw_cursor_overlay(ups, brush, vc, x, y, zoom); } } GPU_matrix_pop(); gpuPopAttr(); + + return alpha_overlay_active; } -BLI_INLINE void draw_tri_point( - unsigned int pos, float sel_col[4], float pivot_col[4], float *co, float width, bool selected) +BLI_INLINE void draw_tri_point(unsigned int pos, + const float sel_col[4], + float pivot_col[4], + float *co, + float width, + bool selected) { immUniformColor4fv(selected ? sel_col : pivot_col); GPU_line_width(3.0f); float w = width / 2.0f; - float tri[3][2] = { + const float tri[3][2] = { {co[0], co[1] + w}, {co[0] - w, co[1] - w}, {co[0] + w, co[1] - w}, @@ -888,8 +906,12 @@ BLI_INLINE void draw_tri_point( immEnd(); } -BLI_INLINE void draw_rect_point( - unsigned int pos, float sel_col[4], float handle_col[4], float *co, float width, bool selected) +BLI_INLINE void draw_rect_point(unsigned int pos, + const float sel_col[4], + float handle_col[4], + float *co, + float width, + bool selected) { immUniformColor4fv(selected ? sel_col : handle_col); @@ -1074,8 +1096,135 @@ static bool ommit_cursor_drawing(Paint *paint, ePaintMode mode, Brush *brush) return true; } +static void cursor_draw_point_screen_space( + const uint gpuattr, const ARegion *ar, float true_location[3], float obmat[4][4], int size) +{ + float translation_vertex_cursor[3], location[3]; + copy_v3_v3(location, true_location); + mul_m4_v3(obmat, location); + ED_view3d_project(ar, location, translation_vertex_cursor); + imm_draw_circle_fill_3d( + gpuattr, translation_vertex_cursor[0], translation_vertex_cursor[1], size, 10); +} + +static void cursor_draw_tiling_preview(const uint gpuattr, + const ARegion *ar, + const float true_location[3], + Sculpt *sd, + Object *ob, + float radius) +{ + BoundBox *bb = BKE_object_boundbox_get(ob); + float orgLoc[3], location[3]; + int dim, tile_pass = 0; + int start[3]; + int end[3]; + int cur[3]; + const float *bbMin = bb->vec[0]; + const float *bbMax = bb->vec[6]; + const float *step = sd->paint.tile_offset; + + copy_v3_v3(orgLoc, true_location); + for (dim = 0; dim < 3; dim++) { + if ((sd->paint.symmetry_flags & (PAINT_TILE_X << dim)) && step[dim] > 0) { + start[dim] = (bbMin[dim] - orgLoc[dim] - radius) / step[dim]; + end[dim] = (bbMax[dim] - orgLoc[dim] + radius) / step[dim]; + } + else { + start[dim] = end[dim] = 0; + } + } + copy_v3_v3_int(cur, start); + for (cur[0] = start[0]; cur[0] <= end[0]; cur[0]++) { + for (cur[1] = start[1]; cur[1] <= end[1]; cur[1]++) { + for (cur[2] = start[2]; cur[2] <= end[2]; cur[2]++) { + if (!cur[0] && !cur[1] && !cur[2]) { + /* skip tile at orgLoc, this was already handled before all others */ + continue; + } + tile_pass++; + for (dim = 0; dim < 3; dim++) { + location[dim] = cur[dim] * step[dim] + orgLoc[dim]; + } + cursor_draw_point_screen_space(gpuattr, ar, location, ob->obmat, 3); + } + } + } +} + +static void cursor_draw_point_with_symmetry(const uint gpuattr, + const ARegion *ar, + const float true_location[3], + Sculpt *sd, + Object *ob, + float radius) +{ + const char symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; + float location[3], symm_rot_mat[4][4]; + + for (int i = 0; i <= symm; i++) { + if (i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) { + + /* Axis Symmetry */ + flip_v3_v3(location, true_location, (char)i); + cursor_draw_point_screen_space(gpuattr, ar, location, ob->obmat, 3); + + /* Tiling */ + cursor_draw_tiling_preview(gpuattr, ar, location, sd, ob, radius); + + /* Radial Symmetry */ + for (char raxis = 0; raxis < 3; raxis++) { + for (int r = 1; r < sd->radial_symm[raxis]; r++) { + float angle = 2 * M_PI * r / sd->radial_symm[(int)raxis]; + flip_v3_v3(location, true_location, (char)i); + unit_m4(symm_rot_mat); + rotate_m4(symm_rot_mat, raxis + 'X', angle); + mul_m4_v3(symm_rot_mat, location); + + cursor_draw_tiling_preview(gpuattr, ar, location, sd, ob, radius); + cursor_draw_point_screen_space(gpuattr, ar, location, ob->obmat, 3); + } + } + } + } +} + +static void sculpt_geometry_preview_lines_draw(const uint gpuattr, SculptSession *ss) +{ + 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(); + if (!depth_test) { + GPU_depth_test(true); + } + + GPU_line_width(1.0f); + if (ss->preview_vert_index_count > 0) { + immBegin(GPU_PRIM_LINES, ss->preview_vert_index_count); + for (int i = 0; i < ss->preview_vert_index_count; i++) { + immVertex3fv(gpuattr, sculpt_vertex_co_get(ss, ss->preview_vert_index_list[i])); + } + immEnd(); + } + + /* Restore depth test value. */ + if (!depth_test) { + GPU_depth_test(false); + } +} + +static bool paint_use_2d_cursor(ePaintMode mode) +{ + if (mode >= PAINT_MODE_TEXTURE_3D) { + return true; + } + return false; +} + static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) { + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Scene *scene = CTX_data_scene(C); ARegion *ar = CTX_wm_region(C); UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; @@ -1083,6 +1232,9 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) Brush *brush = BKE_paint_brush(paint); ePaintMode mode = BKE_paintmode_get_active_from_context(C); + /* 2d or 3d painting? */ + const bool use_2d_cursor = paint_use_2d_cursor(mode); + /* check that brush drawing is enabled */ if (ommit_cursor_drawing(paint, mode, brush)) { return; @@ -1091,7 +1243,7 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) /* can't use stroke vc here because this will be called during * mouse over too, not just during a stroke */ ViewContext vc; - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); if (vc.rv3d && (vc.rv3d->rflag & RV3D_NAVIGATING)) { return; @@ -1109,7 +1261,7 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) /* set various defaults */ const float *outline_col = brush->add_col; - const float outline_alpha = 0.5f; + const float outline_alpha = 0.7f; float translation[2] = {x, y}; float final_radius = (BKE_brush_size_get(scene, brush) * zoomx); @@ -1121,34 +1273,7 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) } /* draw overlay */ - paint_draw_alpha_overlay(ups, brush, &vc, x, y, zoomx, mode); - - /* TODO: as sculpt and other paint modes are unified, this - * special mode of drawing will go away */ - if ((mode == PAINT_MODE_SCULPT) && vc.obact->sculpt) { - float location[3]; - int pixel_radius; - - /* test if brush is over the mesh */ - bool hit = sculpt_get_brush_geometry(C, &vc, x, y, &pixel_radius, location, ups); - - if (BKE_brush_use_locked_size(scene, brush)) { - BKE_brush_size_set(scene, brush, pixel_radius); - } - - /* check if brush is subtracting, use different color then */ - /* TODO: no way currently to know state of pen flip or - * invert key modifier without starting a stroke */ - if (((ups->draw_inverted == 0) ^ ((brush->flag & BRUSH_DIR_IN) == 0)) && - BKE_brush_sculpt_has_secondary_color(brush)) { - outline_col = brush->sub_col; - } - - /* only do if brush is over the mesh */ - if (hit) { - paint_cursor_on_hit(ups, brush, &vc, location); - } - } + bool alpha_overlay_active = paint_draw_alpha_overlay(ups, brush, &vc, x, y, zoomx, mode); if (ups->draw_anchored) { final_radius = ups->anchored_size; @@ -1158,25 +1283,243 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) } /* make lines pretty */ - GPU_line_width(1.0f); + GPU_line_width(2.0f); GPU_blend(true); /* TODO: also set blend mode? */ GPU_line_smooth(true); - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + if (use_2d_cursor) { + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + immUniformColor3fvAlpha(outline_col, outline_alpha); + + /* draw brush outline */ + if (ups->stroke_active && BKE_brush_use_size_pressure(scene, brush)) { + imm_draw_circle_wire_2d( + pos, translation[0], translation[1], final_radius * ups->size_pressure_value, 40); + /* outer at half alpha */ + immUniformColor3fvAlpha(outline_col, outline_alpha * 0.5f); + } + + GPU_line_width(1.0f); + imm_draw_circle_wire_2d(pos, translation[0], translation[1], final_radius, 40); + } + else { /* 3d painting */ + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + + /* TODO: as sculpt and other paint modes are unified, this + * special mode of drawing will go away */ + Object *obact = vc.obact; + SculptSession *ss = obact ? obact->sculpt : NULL; + if ((mode == PAINT_MODE_SCULPT) && ss) { + float location[3]; + int pixel_radius; + + /* test if brush is over the mesh */ + bool hit = sculpt_get_brush_geometry(C, &vc, x, y, &pixel_radius, location, ups); + + if (BKE_brush_use_locked_size(scene, brush)) { + BKE_brush_size_set(scene, brush, pixel_radius); + } + + /* check if brush is subtracting, use different color then */ + /* TODO: no way currently to know state of pen flip or + * invert key modifier without starting a stroke */ + if (((ups->draw_inverted == 0) ^ ((brush->flag & BRUSH_DIR_IN) == 0)) && + BKE_brush_sculpt_has_secondary_color(brush)) { + outline_col = brush->sub_col; + } + + /* only do if brush is over the mesh */ + if (hit) { + paint_cursor_on_hit(ups, brush, &vc, location); + } + } + + immUniformColor3fvAlpha(outline_col, outline_alpha); + + if (ups->stroke_active && BKE_brush_use_size_pressure(scene, brush)) { + imm_draw_circle_wire_3d( + pos, translation[0], translation[1], final_radius * ups->size_pressure_value, 40); + /* outer at half alpha */ + immUniformColor3fvAlpha(outline_col, outline_alpha * 0.5f); + } + + /* Only sculpt mode cursor for now */ + /* Disable for PBVH_GRIDS */ + bool is_multires = ss && ss->pbvh && BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS; - /* set brush color */ - immUniformColor3fvAlpha(outline_col, outline_alpha); + SculptCursorGeometryInfo gi; + float mouse[2] = {x - ar->winrct.xmin, y - ar->winrct.ymin}; + int prev_active_vertex_index = -1; + bool is_cursor_over_mesh = false; - /* draw brush outline */ - if (ups->stroke_active && BKE_brush_use_size_pressure(scene, brush)) { - /* inner at full alpha */ - imm_draw_circle_wire_2d( - pos, translation[0], translation[1], final_radius * ups->size_pressure_value, 40); - /* outer at half alpha */ - immUniformColor3fvAlpha(outline_col, outline_alpha * 0.5f); + /* Update the active vertex */ + if ((mode == PAINT_MODE_SCULPT) && ss && !ups->stroke_active) { + prev_active_vertex_index = ss->active_vertex_index; + is_cursor_over_mesh = sculpt_cursor_geometry_info_update( + C, &gi, mouse, !(brush->falloff_shape & BRUSH_AIRBRUSH)); + } + /* Use special paint crosshair cursor in all paint modes*/ + wmWindow *win = CTX_wm_window(C); + WM_cursor_set(win, WM_CURSOR_PAINT); + + if ((mode == PAINT_MODE_SCULPT) && ss && !(brush->falloff_shape & BRUSH_AIRBRUSH)) { + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + + if (!ups->stroke_active) { + bool update_previews = false; + if (is_cursor_over_mesh && !alpha_overlay_active) { + + if (prev_active_vertex_index != ss->active_vertex_index) { + update_previews = true; + } + + float rds; + if (!BKE_brush_use_locked_size(scene, brush)) { + rds = paint_calc_object_space_radius( + &vc, gi.location, BKE_brush_size_get(scene, brush)); + } + else { + rds = BKE_brush_unprojected_radius_get(scene, brush); + } + + wmViewport(&ar->winrct); + + /* Draw 3D active vertex preview with symmetry*/ + if (len_v3v3(gi.active_vertex_co, gi.location) < rds) { + cursor_draw_point_with_symmetry(pos, ar, gi.active_vertex_co, sd, vc.obact, rds); + } + + /* Draw pose brush origin */ + if (brush->sculpt_tool == SCULPT_TOOL_POSE && !is_multires) { + immUniformColor4f(1.0f, 1.0f, 1.0f, 0.8f); + if (update_previews) { + BKE_sculpt_update_object_for_edit(depsgraph, vc.obact, true, false); + sculpt_pose_calc_pose_data( + sd, vc.obact, ss, gi.location, rds, brush->pose_offset, ss->pose_origin, NULL); + } + cursor_draw_point_screen_space(pos, ar, ss->pose_origin, vc.obact->obmat, 5); + } + + /* Draw 3D brush cursor */ + GPU_matrix_push_projection(); + ED_view3d_draw_setup_view(CTX_wm_window(C), + CTX_data_depsgraph_pointer(C), + CTX_data_scene(C), + ar, + CTX_wm_view3d(C), + NULL, + NULL, + NULL); + + float cursor_trans[4][4], cursor_rot[4][4]; + float z_axis[4] = {0.0f, 0.0f, 1.0f, 0.0f}; + float quat[4]; + + copy_m4_m4(cursor_trans, vc.obact->obmat); + translate_m4(cursor_trans, gi.location[0], gi.location[1], gi.location[2]); + rotation_between_vecs_to_quat(quat, z_axis, gi.normal); + quat_to_mat4(cursor_rot, quat); + + GPU_matrix_push(); + GPU_matrix_mul(cursor_trans); + GPU_matrix_mul(cursor_rot); + immUniformColor3fvAlpha(outline_col, outline_alpha); + GPU_line_width(2.0f); + imm_draw_circle_wire_3d(pos, 0, 0, rds, 80); + GPU_line_width(1.0f); + immUniformColor3fvAlpha(outline_col, outline_alpha * 0.5f); + imm_draw_circle_wire_3d(pos, 0, 0, rds * clamp_f(brush->alpha, 0.0f, 1.0f), 80); + GPU_matrix_pop(); + + /* Update and draw dynamic mesh preview lines */ + GPU_matrix_push(); + GPU_matrix_mul(vc.obact->obmat); + if (brush->sculpt_tool == SCULPT_TOOL_GRAB && (brush->flag & BRUSH_GRAB_ACTIVE_VERTEX) && + !is_multires) { + if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && ss->modifiers_active) { + sculpt_geometry_preview_lines_update(C, ss, rds); + sculpt_geometry_preview_lines_draw(pos, ss); + } + } + + /* Draw pose brush line preview */ + if (brush->sculpt_tool == SCULPT_TOOL_POSE && !is_multires) { + immUniformColor4f(1.0f, 1.0f, 1.0f, 0.8f); + GPU_line_width(2.0f); + immBegin(GPU_PRIM_LINES, 2); + immVertex3fv(pos, ss->pose_origin); + immVertex3fv(pos, gi.location); + immEnd(); + } + + GPU_matrix_pop(); + + GPU_matrix_pop_projection(); + + wmWindowViewport(win); + } + else { + /* Draw default cursor when the mouse is not over the mesh or there are no supported + * overlays active */ + GPU_line_width(1.0f); + /* Reduce alpha to increase the contrast when the cursor is over the mesh */ + immUniformColor3fvAlpha(outline_col, outline_alpha * 0.8); + imm_draw_circle_wire_3d(pos, translation[0], translation[1], final_radius, 80); + immUniformColor3fvAlpha(outline_col, outline_alpha * 0.35f); + imm_draw_circle_wire_3d(pos, + translation[0], + translation[1], + final_radius * clamp_f(brush->alpha, 0.0f, 1.0f), + 80); + } + } + else { + if (vc.obact->sculpt->cache && !vc.obact->sculpt->cache->first_time) { + /* Draw cursor location preview when the stroke is active using the data from StrokeCache + */ + float cursor_location[3]; + wmViewport(&ar->winrct); + copy_v3_v3(cursor_location, ss->cache->true_location); + if (ss->cache->brush->sculpt_tool == SCULPT_TOOL_GRAB) { + add_v3_v3(cursor_location, ss->cache->grab_delta); + } + cursor_draw_point_with_symmetry( + pos, ar, cursor_location, sd, vc.obact, ss->cache->radius); + + /* Draw cached dynamic mesh preview lines */ + if (brush->sculpt_tool == SCULPT_TOOL_GRAB && (brush->flag & BRUSH_GRAB_ACTIVE_VERTEX) && + !is_multires) { + if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && ss->modifiers_active) { + GPU_matrix_push_projection(); + ED_view3d_draw_setup_view(CTX_wm_window(C), + CTX_data_depsgraph_pointer(C), + CTX_data_scene(C), + ar, + CTX_wm_view3d(C), + NULL, + NULL, + NULL); + GPU_matrix_push(); + GPU_matrix_mul(vc.obact->obmat); + sculpt_geometry_preview_lines_draw(pos, ss); + GPU_matrix_pop(); + GPU_matrix_pop_projection(); + } + } + + wmWindowViewport(win); + } + } + } + else { + /* Draw default cursor in unsupported modes */ + GPU_line_width(1.0f); + imm_draw_circle_wire_3d(pos, translation[0], translation[1], final_radius, 40); + } } - imm_draw_circle_wire_2d(pos, translation[0], translation[1], final_radius, 40); immUnbindProgram(); diff --git a/source/blender/editors/sculpt_paint/paint_curve.c b/source/blender/editors/sculpt_paint/paint_curve.c index d9fd194e96f..62c31c91f8d 100644 --- a/source/blender/editors/sculpt_paint/paint_curve.c +++ b/source/blender/editors/sculpt_paint/paint_curve.c @@ -35,8 +35,6 @@ #include "BKE_main.h" #include "BKE_paint.h" -#include "DEG_depsgraph.h" - #include "ED_view3d.h" #include "ED_paint.h" diff --git a/source/blender/editors/sculpt_paint/paint_curve_undo.c b/source/blender/editors/sculpt_paint/paint_curve_undo.c index bd62a59e73f..c14ccd27804 100644 --- a/source/blender/editors/sculpt_paint/paint_curve_undo.c +++ b/source/blender/editors/sculpt_paint/paint_curve_undo.c @@ -35,7 +35,6 @@ #include "ED_undo.h" #include "WM_api.h" -#include "WM_types.h" #include "paint_intern.h" diff --git a/source/blender/editors/sculpt_paint/paint_hide.c b/source/blender/editors/sculpt_paint/paint_hide.c index 5852012891d..026dc39c668 100644 --- a/source/blender/editors/sculpt_paint/paint_hide.c +++ b/source/blender/editors/sculpt_paint/paint_hide.c @@ -37,7 +37,6 @@ #include "BKE_ccg.h" #include "BKE_context.h" #include "BKE_mesh.h" -#include "BKE_mesh_runtime.h" #include "BKE_multires.h" #include "BKE_paint.h" #include "BKE_subsurf.h" @@ -135,7 +134,6 @@ static void partialvis_update_grids(Depsgraph *depsgraph, float planes[4][4]) { CCGElem **grids; - CCGKey key; BLI_bitmap **grid_hidden; int *grid_indices, totgrid, i; bool any_changed = false, any_visible = false; @@ -143,7 +141,7 @@ static void partialvis_update_grids(Depsgraph *depsgraph, /* get PBVH data */ BKE_pbvh_node_get_grids(pbvh, node, &grid_indices, &totgrid, NULL, NULL, &grids); grid_hidden = BKE_pbvh_grid_hidden(pbvh); - BKE_pbvh_get_grid_key(pbvh, &key); + CCGKey key = *BKE_pbvh_get_grid_key(pbvh); sculpt_undo_push_node(ob, node, SCULPT_UNDO_HIDDEN); @@ -299,15 +297,17 @@ static void rect_from_props(rcti *rect, PointerRNA *ptr) rect->ymax = RNA_int_get(ptr, "ymax"); } -static void clip_planes_from_rect(bContext *C, float clip_planes[4][4], const rcti *rect) +static void clip_planes_from_rect(bContext *C, + Depsgraph *depsgraph, + float clip_planes[4][4], + const rcti *rect) { ViewContext vc; BoundBox bb; view3d_operator_needs_opengl(C); - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); ED_view3d_clipping_calc(&bb, clip_planes, vc.ar, vc.obact, rect); - negate_m4(clip_planes); } /* If mode is inside, get all PBVH nodes that lie at least partially @@ -322,17 +322,18 @@ static void get_pbvh_nodes( /* select search callback */ switch (mode) { case PARTIALVIS_INSIDE: - cb = BKE_pbvh_node_planes_contain_AABB; + cb = BKE_pbvh_node_frustum_contain_AABB; break; case PARTIALVIS_OUTSIDE: - cb = BKE_pbvh_node_planes_exclude_AABB; + cb = BKE_pbvh_node_frustum_exclude_AABB; break; case PARTIALVIS_ALL: case PARTIALVIS_MASKED: break; } - BKE_pbvh_search_gather(pbvh, cb, clip_planes, nodes, totnode); + PBVHFrustumPlanes frustum = {.planes = clip_planes, .num_planes = 4}; + BKE_pbvh_search_gather(pbvh, cb, &frustum, nodes, totnode); } static int hide_show_exec(bContext *C, wmOperator *op) @@ -355,7 +356,7 @@ static int hide_show_exec(bContext *C, wmOperator *op) area = RNA_enum_get(op->ptr, "area"); rect_from_props(&rect, op->ptr); - clip_planes_from_rect(C, clip_planes, &rect); + clip_planes_from_rect(C, depsgraph, clip_planes, &rect); pbvh = BKE_sculpt_object_pbvh_ensure(depsgraph, ob); BLI_assert(ob->sculpt->pbvh == pbvh); @@ -363,6 +364,8 @@ static int hide_show_exec(bContext *C, wmOperator *op) get_pbvh_nodes(pbvh, &nodes, &totnode, clip_planes, area); pbvh_type = BKE_pbvh_type(pbvh); + negate_m4(clip_planes); + /* start undo */ switch (action) { case PARTIALVIS_HIDE: diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index f3a6cfa0d5c..24c2dfb6c6b 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -113,10 +113,10 @@ void imapaint_region_tiles( IMB_rectclip(ibuf, NULL, &x, &y, &srcx, &srcy, &w, &h); - *tw = ((x + w - 1) >> IMAPAINT_TILE_BITS); - *th = ((y + h - 1) >> IMAPAINT_TILE_BITS); - *tx = (x >> IMAPAINT_TILE_BITS); - *ty = (y >> IMAPAINT_TILE_BITS); + *tw = ((x + w - 1) >> ED_IMAGE_UNDO_TILE_BITS); + *th = ((y + h - 1) >> ED_IMAGE_UNDO_TILE_BITS); + *tx = (x >> ED_IMAGE_UNDO_TILE_BITS); + *ty = (y >> ED_IMAGE_UNDO_TILE_BITS); } void ED_imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int h, bool find_old) @@ -147,11 +147,12 @@ void ED_imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int imapaint_region_tiles(ibuf, x, y, w, h, &tilex, &tiley, &tilew, &tileh); - ListBase *undo_tiles = ED_image_undo_get_tiles(); + ListBase *undo_tiles = ED_image_paint_tile_list_get(); for (ty = tiley; ty <= tileh; ty++) { for (tx = tilex; tx <= tilew; tx++) { - image_undo_push_tile(undo_tiles, ima, ibuf, &tmpibuf, tx, ty, NULL, NULL, false, find_old); + ED_image_paint_tile_push( + undo_tiles, ima, ibuf, &tmpibuf, tx, ty, NULL, NULL, false, find_old); } } @@ -467,12 +468,13 @@ static void gradient_draw_line(bContext *UNUSED(C), int x, int y, void *customda static PaintOperation *texture_paint_init(bContext *C, wmOperator *op, const float mouse[2]) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Scene *scene = CTX_data_scene(C); ToolSettings *settings = scene->toolsettings; PaintOperation *pop = MEM_callocN(sizeof(PaintOperation), "PaintOperation"); /* caller frees */ Brush *brush = BKE_paint_brush(&settings->imapaint.paint); int mode = RNA_enum_get(op->ptr, "mode"); - ED_view3d_viewcontext_init(C, &pop->vc); + ED_view3d_viewcontext_init(C, &pop->vc, depsgraph); copy_v2_v2(pop->prevmouse, mouse); copy_v2_v2(pop->startmouse, mouse); @@ -699,7 +701,7 @@ static int paint_invoke(bContext *C, wmOperator *op, const wmEvent *event) event->type); if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { - paint_stroke_data_free(op); + paint_stroke_free(C, op); return OPERATOR_FINISHED; } /* add modal handler */ @@ -1022,7 +1024,7 @@ static int sample_color_invoke(bContext *C, wmOperator *op, const wmEvent *event !RNA_boolean_get(op->ptr, "merged"); paint_sample_color(C, ar, event->mval[0], event->mval[1], use_sample_texture, false); - WM_cursor_modal_set(win, BC_EYEDROPPER_CURSOR); + WM_cursor_modal_set(win, WM_CURSOR_EYEDROPPER); WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush); diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index 8f1156295a3..4f1ae10aa62 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -1197,23 +1197,24 @@ static void paint_2d_do_making_brush(ImagePaintState *s, int tileh) { ImBuf tmpbuf; - IMB_initImBuf(&tmpbuf, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, 0); + IMB_initImBuf(&tmpbuf, ED_IMAGE_UNDO_TILE_SIZE, ED_IMAGE_UNDO_TILE_SIZE, 32, 0); - ListBase *undo_tiles = ED_image_undo_get_tiles(); + ListBase *undo_tiles = ED_image_paint_tile_list_get(); for (int ty = tiley; ty <= tileh; ty++) { for (int tx = tilex; tx <= tilew; tx++) { /* retrieve original pixels + mask from undo buffer */ unsigned short *mask; - int origx = region->destx - tx * IMAPAINT_TILE_SIZE; - int origy = region->desty - ty * IMAPAINT_TILE_SIZE; + int origx = region->destx - tx * ED_IMAGE_UNDO_TILE_SIZE; + int origy = region->desty - ty * ED_IMAGE_UNDO_TILE_SIZE; if (s->canvas->rect_float) { - tmpbuf.rect_float = image_undo_find_tile( + tmpbuf.rect_float = ED_image_paint_tile_find( undo_tiles, s->image, s->canvas, tx, ty, &mask, false); } else { - tmpbuf.rect = image_undo_find_tile(undo_tiles, s->image, s->canvas, tx, ty, &mask, false); + tmpbuf.rect = ED_image_paint_tile_find( + undo_tiles, s->image, s->canvas, tx, ty, &mask, false); } IMB_rectblend(s->canvas, @@ -1454,8 +1455,6 @@ static void paint_2d_canvas_free(ImagePaintState *s) paint_delete_blur_kernel(s->blurkernel); MEM_freeN(s->blurkernel); } - - image_undo_remove_masks(); } void paint_2d_stroke(void *ps, diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 397b2981ace..5e004c7d675 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -77,15 +77,11 @@ #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_screen.h" -#include "BKE_texture.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" -#include "UI_interface.h" - #include "ED_object.h" -#include "ED_mesh.h" #include "ED_node.h" #include "ED_paint.h" #include "ED_screen.h" @@ -105,7 +101,6 @@ #include "IMB_colormanagement.h" -#include "bmesh.h" //#include "bmesh_tools.h" #include "paint_intern.h" @@ -1812,31 +1807,31 @@ static int project_paint_undo_subtiles(const TileInfo *tinf, int tx, int ty) } if (generate_tile) { - ListBase *undo_tiles = ED_image_undo_get_tiles(); + ListBase *undo_tiles = ED_image_paint_tile_list_get(); volatile void *undorect; if (tinf->masked) { - undorect = image_undo_push_tile(undo_tiles, - pjIma->ima, - pjIma->ibuf, - tinf->tmpibuf, - tx, - ty, - &pjIma->maskRect[tile_index], - &pjIma->valid[tile_index], - true, - false); + undorect = ED_image_paint_tile_push(undo_tiles, + pjIma->ima, + pjIma->ibuf, + tinf->tmpibuf, + tx, + ty, + &pjIma->maskRect[tile_index], + &pjIma->valid[tile_index], + true, + false); } else { - undorect = image_undo_push_tile(undo_tiles, - pjIma->ima, - pjIma->ibuf, - tinf->tmpibuf, - tx, - ty, - NULL, - &pjIma->valid[tile_index], - true, - false); + undorect = ED_image_paint_tile_push(undo_tiles, + pjIma->ima, + pjIma->ibuf, + tinf->tmpibuf, + tx, + ty, + NULL, + &pjIma->valid[tile_index], + true, + false); } BKE_image_mark_dirty(pjIma->ima, pjIma->ibuf); @@ -1885,14 +1880,14 @@ static ProjPixel *project_paint_uvpixel_init(const ProjPaintState *ps, /* calculate the undo tile offset of the pixel, used to store the original * pixel color and accumulated mask if any */ - x_tile = x_px >> IMAPAINT_TILE_BITS; - y_tile = y_px >> IMAPAINT_TILE_BITS; + x_tile = x_px >> ED_IMAGE_UNDO_TILE_BITS; + y_tile = y_px >> ED_IMAGE_UNDO_TILE_BITS; - x_round = x_tile * IMAPAINT_TILE_SIZE; - y_round = y_tile * IMAPAINT_TILE_SIZE; + x_round = x_tile * ED_IMAGE_UNDO_TILE_SIZE; + y_round = y_tile * ED_IMAGE_UNDO_TILE_SIZE; // memset(projPixel, 0, size); - tile_offset = (x_px - x_round) + (y_px - y_round) * IMAPAINT_TILE_SIZE; + tile_offset = (x_px - x_round) + (y_px - y_round) * ED_IMAGE_UNDO_TILE_SIZE; tile_index = project_paint_undo_subtiles(tinf, x_tile, y_tile); /* other thread may be initializing the tile so wait here */ @@ -1900,8 +1895,9 @@ static ProjPixel *project_paint_uvpixel_init(const ProjPaintState *ps, /* pass */ } - BLI_assert(tile_index < (IMAPAINT_TILE_NUMBER(ibuf->x) * IMAPAINT_TILE_NUMBER(ibuf->y))); - BLI_assert(tile_offset < (IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE)); + BLI_assert(tile_index < + (ED_IMAGE_UNDO_TILE_NUMBER(ibuf->x) * ED_IMAGE_UNDO_TILE_NUMBER(ibuf->y))); + BLI_assert(tile_offset < (ED_IMAGE_UNDO_TILE_SIZE * ED_IMAGE_UNDO_TILE_SIZE)); projPixel->valid = projima->valid[tile_index]; @@ -2979,7 +2975,7 @@ static void project_paint_face_init(const ProjPaintState *ps, TileInfo tinf = { ps->tile_lock, ps->do_masking, - IMAPAINT_TILE_NUMBER(ibuf->x), + ED_IMAGE_UNDO_TILE_NUMBER(ibuf->x), tmpibuf, ps->projImages + image_index, }; @@ -3931,7 +3927,7 @@ static void proj_paint_state_thread_init(ProjPaintState *ps, const bool reset_th BLI_spin_init(ps->tile_lock); } - image_undo_init_locks(); + ED_image_paint_tile_lock_init(); } for (a = 0; a < ps->thread_tot; a++) { @@ -4249,8 +4245,8 @@ static void project_paint_build_proj_ima(ProjPaintState *ps, projIma->ima = node->link; projIma->touch = 0; projIma->ibuf = BKE_image_acquire_ibuf(projIma->ima, NULL, NULL); - size = sizeof(void **) * IMAPAINT_TILE_NUMBER(projIma->ibuf->x) * - IMAPAINT_TILE_NUMBER(projIma->ibuf->y); + size = sizeof(void **) * ED_IMAGE_UNDO_TILE_NUMBER(projIma->ibuf->x) * + ED_IMAGE_UNDO_TILE_NUMBER(projIma->ibuf->y); projIma->partRedrawRect = BLI_memarena_alloc( arena, sizeof(ImagePaintPartialRedraw) * PROJ_BOUNDBOX_SQUARED); partial_redraw_array_init(projIma->partRedrawRect); @@ -4540,8 +4536,6 @@ static void project_paint_end(ProjPaintState *ps) { int a; - image_undo_remove_masks(); - /* dereference used image buffers */ if (ps->is_shared_user == false) { ProjPaintImage *projIma; @@ -4583,7 +4577,7 @@ static void project_paint_end(ProjPaintState *ps) MEM_freeN((void *)ps->tile_lock); } - image_undo_end_locks(); + ED_image_paint_tile_lock_end(); #ifndef PROJ_DEBUG_NOSEAMBLEED if (ps->seam_bleed_px > 0.0f) { @@ -5212,8 +5206,10 @@ static void do_projectpaint_thread(TaskPool *__restrict UNUSED(pool), float line_len_sq_inv, line_len; float f; float color_f[4]; - float p[2] = {projPixel->projCoSS[0] - lastpos[0], - projPixel->projCoSS[1] - lastpos[1]}; + const float p[2] = { + projPixel->projCoSS[0] - lastpos[0], + projPixel->projCoSS[1] - lastpos[1], + }; sub_v2_v2v2(tangent, pos, lastpos); line_len = len_squared_v2(tangent); diff --git a/source/blender/editors/sculpt_paint/paint_image_undo.c b/source/blender/editors/sculpt_paint/paint_image_undo.c deleted file mode 100644 index 93dcd3ad0f6..00000000000 --- a/source/blender/editors/sculpt_paint/paint_image_undo.c +++ /dev/null @@ -1,625 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup edsculpt - */ - -#include "MEM_guardedalloc.h" - -#include "BLI_math.h" -#include "BLI_blenlib.h" -#include "BLI_utildefines.h" -#include "BLI_threads.h" - -#include "DNA_image_types.h" -#include "DNA_windowmanager_types.h" -#include "DNA_object_types.h" -#include "DNA_screen_types.h" -#include "DNA_space_types.h" -#include "DNA_workspace_types.h" - -#include "IMB_imbuf.h" -#include "IMB_imbuf_types.h" - -#include "BKE_context.h" -#include "BKE_image.h" -#include "BKE_paint.h" -#include "BKE_undo_system.h" - -#include "DEG_depsgraph.h" - -#include "ED_paint.h" -#include "ED_undo.h" -#include "ED_util.h" -#include "ED_object.h" - -#include "GPU_draw.h" - -#include "WM_api.h" - -#include "paint_intern.h" - -/* -------------------------------------------------------------------- */ -/** \name Undo Conversion - * \{ */ - -typedef struct UndoImageTile { - struct UndoImageTile *next, *prev; - - char ibufname[IMB_FILENAME_SIZE]; - - union { - float *fp; - unsigned int *uint; - void *pt; - } rect; - - unsigned short *mask; - - int x, y; - - /* TODO(campbell): avoid storing the ID per tile, - * adds unnecessary overhead restoring undo steps when most tiles share the same image. */ - UndoRefID_Image image_ref; - - short source; - bool use_float; - char gen_type; - bool valid; - - size_t undo_size; -} UndoImageTile; - -/* this is a static resource for non-globality, - * Maybe it should be exposed as part of the - * paint operation, but for now just give a public interface */ -static SpinLock undolock; - -void image_undo_init_locks(void) -{ - BLI_spin_init(&undolock); -} - -void image_undo_end_locks(void) -{ - BLI_spin_end(&undolock); -} - -/* UNDO */ -typedef enum { - COPY = 0, - RESTORE = 1, - RESTORE_COPY = 2, -} CopyMode; - -static void undo_copy_tile(UndoImageTile *tile, ImBuf *tmpibuf, ImBuf *ibuf, CopyMode mode) -{ - if (mode == COPY) { - /* copy or swap contents of tile->rect and region in ibuf->rect */ - IMB_rectcpy(tmpibuf, - ibuf, - 0, - 0, - tile->x * IMAPAINT_TILE_SIZE, - tile->y * IMAPAINT_TILE_SIZE, - IMAPAINT_TILE_SIZE, - IMAPAINT_TILE_SIZE); - - if (ibuf->rect_float) { - SWAP(float *, tmpibuf->rect_float, tile->rect.fp); - } - else { - SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint); - } - } - else { - if (mode == RESTORE_COPY) { - IMB_rectcpy(tmpibuf, - ibuf, - 0, - 0, - tile->x * IMAPAINT_TILE_SIZE, - tile->y * IMAPAINT_TILE_SIZE, - IMAPAINT_TILE_SIZE, - IMAPAINT_TILE_SIZE); - } - /* swap to the tmpbuf for easy copying */ - if (ibuf->rect_float) { - SWAP(float *, tmpibuf->rect_float, tile->rect.fp); - } - else { - SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint); - } - - IMB_rectcpy(ibuf, - tmpibuf, - tile->x * IMAPAINT_TILE_SIZE, - tile->y * IMAPAINT_TILE_SIZE, - 0, - 0, - IMAPAINT_TILE_SIZE, - IMAPAINT_TILE_SIZE); - - if (mode == RESTORE) { - if (ibuf->rect_float) { - SWAP(float *, tmpibuf->rect_float, tile->rect.fp); - } - else { - SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint); - } - } - } -} - -void *image_undo_find_tile(ListBase *undo_tiles, - Image *ima, - ImBuf *ibuf, - int x_tile, - int y_tile, - unsigned short **mask, - bool validate) -{ - UndoImageTile *tile; - const bool use_float = (ibuf->rect_float != NULL); - - for (tile = undo_tiles->first; tile; tile = tile->next) { - if (tile->x == x_tile && tile->y == y_tile && ima->gen_type == tile->gen_type && - ima->source == tile->source) { - if (tile->use_float == use_float) { - if (STREQ(tile->ibufname, ibuf->name)) { - if (mask) { - /* allocate mask if requested */ - if (!tile->mask) { - tile->mask = MEM_callocN(sizeof(unsigned short) * IMAPAINT_TILE_SIZE * - IMAPAINT_TILE_SIZE, - "UndoImageTile.mask"); - } - - *mask = tile->mask; - } - if (validate) { - tile->valid = true; - } - return tile->rect.pt; - } - } - } - } - - return NULL; -} - -void *image_undo_push_tile(ListBase *undo_tiles, - Image *ima, - ImBuf *ibuf, - ImBuf **tmpibuf, - int x_tile, - int y_tile, - unsigned short **mask, - bool **valid, - bool proj, - bool find_prev) -{ - UndoImageTile *tile; - int allocsize; - const bool use_float = (ibuf->rect_float != NULL); - void *data; - - /* check if tile is already pushed */ - - /* in projective painting we keep accounting of tiles, so if we need one pushed, just push! */ - if (find_prev) { - data = image_undo_find_tile(undo_tiles, ima, ibuf, x_tile, y_tile, mask, true); - if (data) { - return data; - } - } - - if (*tmpibuf == NULL) { - *tmpibuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, IB_rectfloat | IB_rect); - } - - tile = MEM_callocN(sizeof(UndoImageTile), "UndoImageTile"); - tile->x = x_tile; - tile->y = y_tile; - - /* add mask explicitly here */ - if (mask) { - *mask = tile->mask = MEM_callocN( - sizeof(unsigned short) * IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE, "UndoImageTile.mask"); - } - allocsize = IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE * 4; - allocsize *= (ibuf->rect_float) ? sizeof(float) : sizeof(char); - tile->rect.pt = MEM_mapallocN(allocsize, "UndeImageTile.rect"); - - BLI_strncpy(tile->ibufname, ibuf->name, sizeof(tile->ibufname)); - - tile->gen_type = ima->gen_type; - tile->source = ima->source; - tile->use_float = use_float; - tile->valid = true; - tile->image_ref.ptr = ima; - - if (valid) { - *valid = &tile->valid; - } - undo_copy_tile(tile, *tmpibuf, ibuf, COPY); - - if (proj) { - BLI_spin_lock(&undolock); - } - BLI_addtail(undo_tiles, tile); - - if (proj) { - BLI_spin_unlock(&undolock); - } - return tile->rect.pt; -} - -void image_undo_remove_masks(void) -{ - ListBase *undo_tiles = ED_image_undo_get_tiles(); - UndoImageTile *tile; - - for (tile = undo_tiles->first; tile; tile = tile->next) { - if (tile->mask) { - MEM_freeN(tile->mask); - tile->mask = NULL; - } - } -} - -static void image_undo_restore_runtime(ListBase *lb) -{ - ImBuf *ibuf, *tmpibuf; - UndoImageTile *tile; - - tmpibuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, IB_rectfloat | IB_rect); - - for (tile = lb->first; tile; tile = tile->next) { - Image *ima = tile->image_ref.ptr; - ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); - - undo_copy_tile(tile, tmpibuf, ibuf, RESTORE); - - GPU_free_image(ima); /* force OpenGL reload (maybe partial update will operate better?) */ - if (ibuf->rect_float) { - ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */ - } - if (ibuf->mipmap[0]) { - ibuf->userflags |= IB_MIPMAP_INVALID; /* force mipmap recreatiom */ - } - ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; - - BKE_image_release_ibuf(ima, ibuf, NULL); - } - - IMB_freeImBuf(tmpibuf); -} - -static void image_undo_restore_list(ListBase *lb) -{ - ImBuf *tmpibuf = IMB_allocImBuf( - IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, IB_rectfloat | IB_rect); - - for (UndoImageTile *tile = lb->first; tile; tile = tile->next) { - - Image *ima = tile->image_ref.ptr; - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); - - if (ima && ibuf && !STREQ(tile->ibufname, ibuf->name)) { - /* current ImBuf filename was changed, probably current frame - * was changed when painting on image sequence, rather than storing - * full image user (which isn't so obvious, btw) try to find ImBuf with - * matched file name in list of already loaded images */ - - BKE_image_release_ibuf(ima, ibuf, NULL); - - ibuf = BKE_image_get_ibuf_with_name(ima, tile->ibufname); - } - - if (!ima || !ibuf || !(ibuf->rect || ibuf->rect_float)) { - BKE_image_release_ibuf(ima, ibuf, NULL); - continue; - } - - if (ima->gen_type != tile->gen_type || ima->source != tile->source) { - BKE_image_release_ibuf(ima, ibuf, NULL); - continue; - } - - const bool use_float = (ibuf->rect_float != NULL); - - if (use_float != tile->use_float) { - BKE_image_release_ibuf(ima, ibuf, NULL); - continue; - } - - undo_copy_tile(tile, tmpibuf, ibuf, RESTORE_COPY); - - BKE_image_mark_dirty(ima, ibuf); - GPU_free_image(ima); /* force OpenGL reload */ - - if (ibuf->rect_float) { - ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */ - } - if (ibuf->mipmap[0]) { - ibuf->userflags |= IB_MIPMAP_INVALID; /* force mipmap recreatiom */ - } - ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; - - DEG_id_tag_update(&ima->id, 0); - - BKE_image_release_ibuf(ima, ibuf, NULL); - } - - IMB_freeImBuf(tmpibuf); -} - -static void image_undo_free_list(ListBase *lb) -{ - for (UndoImageTile *tile = lb->first, *tile_next; tile; tile = tile_next) { - tile_next = tile->next; - MEM_freeN(tile->rect.pt); - MEM_freeN(tile); - } -} - -static void image_undo_invalidate(void) -{ - UndoImageTile *tile; - ListBase *lb = ED_image_undo_get_tiles(); - - for (tile = lb->first; tile; tile = tile->next) { - tile->valid = false; - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Implements ED Undo System - * \{ */ - -typedef struct ImageUndoStep { - UndoStep step; - ListBase tiles; - bool is_encode_init; - ePaintMode paint_mode; -} ImageUndoStep; - -static bool image_undosys_poll(bContext *C) -{ - Object *obact = CTX_data_active_object(C); - - ScrArea *sa = CTX_wm_area(C); - if (sa && (sa->spacetype == SPACE_IMAGE)) { - SpaceImage *sima = (SpaceImage *)sa->spacedata.first; - if ((obact && (obact->mode & OB_MODE_TEXTURE_PAINT)) || (sima->mode == SI_MODE_PAINT)) { - return true; - } - } - else { - if (obact && (obact->mode & OB_MODE_TEXTURE_PAINT)) { - return true; - } - } - return false; -} - -static void image_undosys_step_encode_init(struct bContext *UNUSED(C), UndoStep *us_p) -{ - ImageUndoStep *us = (ImageUndoStep *)us_p; - /* dummy, memory is cleared anyway. */ - us->is_encode_init = true; - BLI_listbase_clear(&us->tiles); -} - -static bool image_undosys_step_encode(struct bContext *C, - struct Main *UNUSED(bmain), - UndoStep *us_p) -{ - /* dummy, encoding is done along the way by adding tiles - * to the current 'ImageUndoStep' added by encode_init. */ - ImageUndoStep *us = (ImageUndoStep *)us_p; - - BLI_assert(us->step.data_size == 0); - - int allocsize = IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE * 4; - - if (us->is_encode_init) { - /* first dispose of invalid tiles (may happen due to drag dot for instance) */ - for (UndoImageTile *tile = us->tiles.first; tile;) { - if (!tile->valid) { - UndoImageTile *tmp_tile = tile->next; - MEM_freeN(tile->rect.pt); - BLI_freelinkN(&us->tiles, tile); - tile = tmp_tile; - } - else { - us->step.data_size += allocsize * (tile->use_float ? sizeof(float) : sizeof(char)); - tile = tile->next; - } - } - } - else { - /* Happens when switching modes. */ - ePaintMode paint_mode = BKE_paintmode_get_active_from_context(C); - BLI_assert(ELEM(paint_mode, PAINT_MODE_TEXTURE_2D, PAINT_MODE_TEXTURE_3D)); - us->paint_mode = paint_mode; - } - - us_p->is_applied = true; - - return true; -} - -static void image_undosys_step_decode_undo_impl(ImageUndoStep *us) -{ - BLI_assert(us->step.is_applied == true); - image_undo_restore_list(&us->tiles); - us->step.is_applied = false; -} - -static void image_undosys_step_decode_redo_impl(ImageUndoStep *us) -{ - BLI_assert(us->step.is_applied == false); - image_undo_restore_list(&us->tiles); - us->step.is_applied = true; -} - -static void image_undosys_step_decode_undo(ImageUndoStep *us, bool is_final) -{ - ImageUndoStep *us_iter = us; - while (us_iter->step.next && (us_iter->step.next->type == us_iter->step.type)) { - if (us_iter->step.next->is_applied == false) { - break; - } - us_iter = (ImageUndoStep *)us_iter->step.next; - } - while (us_iter != us || (!is_final && us_iter == us)) { - image_undosys_step_decode_undo_impl(us_iter); - if (us_iter == us) { - break; - } - us_iter = (ImageUndoStep *)us_iter->step.prev; - } -} - -static void image_undosys_step_decode_redo(ImageUndoStep *us) -{ - ImageUndoStep *us_iter = us; - while (us_iter->step.prev && (us_iter->step.prev->type == us_iter->step.type)) { - if (us_iter->step.prev->is_applied == true) { - break; - } - us_iter = (ImageUndoStep *)us_iter->step.prev; - } - while (us_iter && (us_iter->step.is_applied == false)) { - image_undosys_step_decode_redo_impl(us_iter); - if (us_iter == us) { - break; - } - us_iter = (ImageUndoStep *)us_iter->step.next; - } -} - -static void image_undosys_step_decode( - struct bContext *C, struct Main *bmain, UndoStep *us_p, int dir, bool is_final) -{ - ImageUndoStep *us = (ImageUndoStep *)us_p; - if (dir < 0) { - image_undosys_step_decode_undo(us, is_final); - } - else { - image_undosys_step_decode_redo(us); - } - - if (us->paint_mode == PAINT_MODE_TEXTURE_3D) { - ED_object_mode_set(C, OB_MODE_TEXTURE_PAINT); - } - - /* Refresh texture slots. */ - ED_editors_init_for_undo(bmain); -} - -static void image_undosys_step_free(UndoStep *us_p) -{ - ImageUndoStep *us = (ImageUndoStep *)us_p; - image_undo_free_list(&us->tiles); -} - -static void image_undosys_foreach_ID_ref(UndoStep *us_p, - UndoTypeForEachIDRefFn foreach_ID_ref_fn, - void *user_data) -{ - ImageUndoStep *us = (ImageUndoStep *)us_p; - for (UndoImageTile *tile = us->tiles.first; tile; tile = tile->next) { - foreach_ID_ref_fn(user_data, ((UndoRefID *)&tile->image_ref)); - } -} - -/* Export for ED_undo_sys. */ -void ED_image_undosys_type(UndoType *ut) -{ - ut->name = "Image"; - ut->poll = image_undosys_poll; - ut->step_encode_init = image_undosys_step_encode_init; - ut->step_encode = image_undosys_step_encode; - ut->step_decode = image_undosys_step_decode; - ut->step_free = image_undosys_step_free; - - ut->step_foreach_ID_ref = image_undosys_foreach_ID_ref; - - ut->use_context = true; - - ut->step_size = sizeof(ImageUndoStep); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Utilities - * \{ */ - -ListBase *ED_image_undosys_step_get_tiles(UndoStep *us_p) -{ - ImageUndoStep *us = (ImageUndoStep *)us_p; - return &us->tiles; -} - -ListBase *ED_image_undo_get_tiles(void) -{ - UndoStack *ustack = ED_undo_stack_get(); - UndoStep *us_prev = ustack->step_init; - UndoStep *us_p = BKE_undosys_stack_init_or_active_with_type(ustack, BKE_UNDOSYS_TYPE_IMAGE); - ImageUndoStep *us = (ImageUndoStep *)us_p; - /* We should always have an undo push started when accessing tiles, - * not doing this means we won't have paint_mode correctly set. */ - BLI_assert(us_p == us_prev); - if (us_p != us_prev) { - /* Fallback value until we can be sure this never happens. */ - us->paint_mode = PAINT_MODE_TEXTURE_2D; - } - return ED_image_undosys_step_get_tiles(us_p); -} - -/* restore painting image to previous state. Used for anchored and drag-dot style brushes*/ -void ED_image_undo_restore(UndoStep *us) -{ - ListBase *lb = ED_image_undosys_step_get_tiles(us); - image_undo_restore_runtime(lb); - image_undo_invalidate(); -} - -void ED_image_undo_push_begin(const char *name, int paint_mode) -{ - UndoStack *ustack = ED_undo_stack_get(); - bContext *C = NULL; /* special case, we never read from this. */ - UndoStep *us_p = BKE_undosys_step_push_init_with_type(ustack, C, name, BKE_UNDOSYS_TYPE_IMAGE); - ImageUndoStep *us = (ImageUndoStep *)us_p; - BLI_assert(ELEM(paint_mode, PAINT_MODE_TEXTURE_2D, PAINT_MODE_TEXTURE_3D)); - us->paint_mode = paint_mode; -} - -void ED_image_undo_push_end(void) -{ - UndoStack *ustack = ED_undo_stack_get(); - BKE_undosys_step_push(ustack, NULL, NULL); - WM_file_tag_modified(); -} - -/** \} */ diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index 5efedf69fe4..69eed84fe2b 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -70,7 +70,7 @@ struct PaintStroke *paint_stroke_new(struct bContext *C, StrokeRedraw redraw, StrokeDone done, int event_type); -void paint_stroke_data_free(struct wmOperator *op); +void paint_stroke_free(struct bContext *C, struct wmOperator *op); bool paint_space_stroke_enabled(struct Brush *br, enum ePaintMode mode); bool paint_supports_dynamic_size(struct Brush *br, enum ePaintMode mode); @@ -184,10 +184,6 @@ typedef struct ImagePaintPartialRedraw { int enabled; } ImagePaintPartialRedraw; -#define IMAPAINT_TILE_BITS 6 -#define IMAPAINT_TILE_SIZE (1 << IMAPAINT_TILE_BITS) -#define IMAPAINT_TILE_NUMBER(size) (((size) + IMAPAINT_TILE_SIZE - 1) >> IMAPAINT_TILE_BITS) - bool image_texture_paint_poll(struct bContext *C); void imapaint_image_update(struct SpaceImage *sima, struct Image *image, @@ -252,31 +248,6 @@ void PAINT_OT_add_texture_paint_slot(struct wmOperatorType *ot); void PAINT_OT_image_paint(struct wmOperatorType *ot); void PAINT_OT_add_simple_uvs(struct wmOperatorType *ot); -/* paint_image_undo.c */ -void *image_undo_find_tile(ListBase *undo_tiles, - struct Image *ima, - struct ImBuf *ibuf, - int x_tile, - int y_tile, - unsigned short **mask, - bool validate); -void *image_undo_push_tile(ListBase *undo_tiles, - struct Image *ima, - struct ImBuf *ibuf, - struct ImBuf **tmpibuf, - int x_tile, - int y_tile, - unsigned short **, - bool **valid, - bool proj, - bool find_prev); -void image_undo_remove_masks(void); -void image_undo_init_locks(void); -void image_undo_end_locks(void); - -struct ListBase *ED_image_undosys_step_get_tiles(struct UndoStep *us_p); -struct ListBase *ED_image_undo_get_tiles(void); - /* sculpt_uv.c */ void SCULPT_OT_uv_sculpt_stroke(struct wmOperatorType *ot); diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index 74212058fc7..a93e55685d2 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -109,6 +109,7 @@ static void mask_flood_fill_task_cb(void *__restrict userdata, const PaintMaskFloodMode mode = data->mode; const float value = data->value; + bool redraw = false; PBVHVertexIter vi; @@ -116,13 +117,19 @@ static void mask_flood_fill_task_cb(void *__restrict userdata, BKE_pbvh_vertex_iter_begin(data->pbvh, node, vi, PBVH_ITER_UNIQUE) { + float prevmask = *vi.mask; mask_flood_fill_set_elem(vi.mask, mode, value); + if (prevmask != *vi.mask) { + redraw = true; + } } BKE_pbvh_vertex_iter_end; - BKE_pbvh_node_mark_redraw(node); - if (data->multires) { - BKE_pbvh_node_mark_normals_update(node); + if (redraw) { + BKE_pbvh_node_mark_update_mask(node); + if (data->multires) { + BKE_pbvh_node_mark_normals_update(node); + } } } @@ -160,16 +167,15 @@ static int mask_flood_fill_exec(bContext *C, wmOperator *op) }; TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - - 0, totnode, &data, mask_flood_fill_task_cb, &settings); + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BLI_task_parallel_range(0, totnode, &data, mask_flood_fill_task_cb, &settings); if (multires) { multires_mark_as_modified(depsgraph, ob, MULTIRES_COORDS_MODIFIED); } + BKE_pbvh_update_vertex_data(pbvh, PBVH_UpdateMask); + sculpt_undo_push_end(); if (nodes) { @@ -255,24 +261,32 @@ static void mask_box_select_task_cb(void *__restrict userdata, PBVHVertexIter vi; bool any_masked = false; + bool redraw = false; BKE_pbvh_vertex_iter_begin(data->pbvh, node, vi, PBVH_ITER_UNIQUE) { if (is_effected(clip_planes_final, vi.co)) { + float prevmask = *vi.mask; if (!any_masked) { any_masked = true; sculpt_undo_push_node(data->ob, node, SCULPT_UNDO_MASK); - BKE_pbvh_node_mark_redraw(node); 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; + + if (redraw) { + BKE_pbvh_node_mark_update_mask(node); + } } bool ED_sculpt_mask_box_select(struct bContext *C, ViewContext *vc, const rcti *rect, bool select) @@ -297,7 +311,6 @@ bool ED_sculpt_mask_box_select(struct bContext *C, ViewContext *vc, const rcti * /* transform the clip planes in object space */ ED_view3d_clipping_calc(&bb, clip_planes, vc->ar, vc->obact, rect); - negate_m4(clip_planes); BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true); pbvh = ob->sculpt->pbvh; @@ -305,7 +318,7 @@ bool ED_sculpt_mask_box_select(struct bContext *C, ViewContext *vc, const rcti * sculpt_undo_push_begin("Mask box fill"); - for (symmpass = 0; symmpass <= symm; ++symmpass) { + for (symmpass = 0; symmpass <= symm; symmpass++) { if (symmpass == 0 || (symm & symmpass && (symm != 5 || symmpass != 3) && (symm != 6 || (symmpass != 3 && symmpass != 5)))) { int j = 0; @@ -315,8 +328,10 @@ bool ED_sculpt_mask_box_select(struct bContext *C, ViewContext *vc, const rcti * flip_plane(clip_planes_final[j], clip_planes[j], symmpass); } - BKE_pbvh_search_gather( - pbvh, BKE_pbvh_node_planes_contain_AABB, clip_planes_final, &nodes, &totnode); + 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); MaskTaskData data = { .ob = ob, @@ -329,9 +344,7 @@ bool ED_sculpt_mask_box_select(struct bContext *C, ViewContext *vc, const rcti * }; TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && - totnode > SCULPT_THREADED_LIMIT); + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); BLI_task_parallel_range(0, totnode, &data, mask_box_select_task_cb, &settings); if (nodes) { @@ -344,6 +357,8 @@ bool ED_sculpt_mask_box_select(struct bContext *C, ViewContext *vc, const rcti * multires_mark_as_modified(depsgraph, ob, MULTIRES_COORDS_MODIFIED); } + BKE_pbvh_update_vertex_data(pbvh, PBVH_UpdateMask); + sculpt_undo_push_end(); ED_region_tag_redraw(ar); @@ -462,7 +477,7 @@ static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) /* 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); + ED_view3d_viewcontext_init(C, &vc, depsgraph); /* lasso data calculations */ data.vc = &vc; @@ -483,7 +498,6 @@ static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) &data); ED_view3d_clipping_calc(&bb, clip_planes, vc.ar, vc.obact, &data.rect); - negate_m4(clip_planes); BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true); pbvh = ob->sculpt->pbvh; @@ -491,7 +505,7 @@ static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) sculpt_undo_push_begin("Mask lasso fill"); - for (symmpass = 0; symmpass <= symm; ++symmpass) { + for (symmpass = 0; symmpass <= symm; symmpass++) { if ((symmpass == 0) || (symm & symmpass && (symm != 5 || symmpass != 3) && (symm != 6 || (symmpass != 3 && symmpass != 5)))) { int j = 0; @@ -505,8 +519,11 @@ static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) /* 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_planes_contain_AABB, clip_planes_final, &nodes, &totnode); + pbvh, BKE_pbvh_node_frustum_contain_AABB, &frustum, &nodes, &totnode); + + negate_m4(clip_planes_final); data.task_data.ob = ob; data.task_data.pbvh = pbvh; @@ -516,9 +533,7 @@ static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) data.task_data.value = value; TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && - (totnode > SCULPT_THREADED_LIMIT)); + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); BLI_task_parallel_range(0, totnode, &data, mask_gesture_lasso_task_cb, &settings); if (nodes) { @@ -531,6 +546,8 @@ static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) multires_mark_as_modified(depsgraph, ob, MULTIRES_COORDS_MODIFIED); } + BKE_pbvh_update_vertex_data(pbvh, PBVH_UpdateMask); + sculpt_undo_push_end(); ED_region_tag_redraw(vc.ar); diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index f58afcdadc1..97455d479dc 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -29,32 +29,22 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_brush_types.h" -#include "DNA_gpencil_types.h" #include "BKE_brush.h" #include "BKE_context.h" -#include "BKE_gpencil.h" #include "BKE_library.h" #include "BKE_main.h" #include "BKE_paint.h" -#include "BKE_report.h" - -#include "DEG_depsgraph.h" #include "ED_paint.h" #include "ED_screen.h" -#include "ED_select_utils.h" #include "ED_image.h" -#include "ED_gpencil.h" -#include "UI_resources.h" #include "WM_api.h" #include "WM_types.h" -#include "WM_toolsystem.h" #include "RNA_access.h" #include "RNA_define.h" -#include "RNA_enum_types.h" #include "paint_intern.h" #include "sculpt_intern.h" diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index 694dae49d30..d8be345cc84 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -43,7 +43,6 @@ #include "BKE_curve.h" #include "BKE_colortools.h" #include "BKE_image.h" -#include "BKE_mesh.h" #include "WM_api.h" #include "WM_types.h" @@ -57,6 +56,7 @@ #include "IMB_imbuf_types.h" #include "paint_intern.h" +#include "sculpt_intern.h" #include <float.h> #include <math.h> @@ -93,6 +93,8 @@ typedef struct PaintStroke { int cur_sample; float last_mouse_position[2]; + float last_world_space_position[3]; + bool stroke_over_mesh; /* space distance covered so far */ float stroke_distance; @@ -218,6 +220,8 @@ static bool paint_tool_require_location(Brush *brush, ePaintMode mode) case PAINT_MODE_SCULPT: if (ELEM(brush->sculpt_tool, SCULPT_TOOL_GRAB, + SCULPT_TOOL_ELASTIC_DEFORM, + SCULPT_TOOL_POSE, SCULPT_TOOL_ROTATE, SCULPT_TOOL_SNAKE_HOOK, SCULPT_TOOL_THUMB)) { @@ -233,11 +237,27 @@ static bool paint_tool_require_location(Brush *brush, ePaintMode mode) return true; } +static bool paint_stroke_use_scene_spacing(Brush *brush, ePaintMode mode) +{ + switch (mode) { + case PAINT_MODE_SCULPT: + return brush->flag & BRUSH_SCENE_SPACING; + default: + break; + } + return false; +} + static bool paint_tool_require_inbetween_mouse_events(Brush *brush, ePaintMode mode) { switch (mode) { case PAINT_MODE_SCULPT: - if (ELEM(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_THUMB)) { + if (ELEM(brush->sculpt_tool, + SCULPT_TOOL_GRAB, + SCULPT_TOOL_ROTATE, + SCULPT_TOOL_THUMB, + SCULPT_TOOL_ELASTIC_DEFORM, + SCULPT_TOOL_POSE)) { return false; } else { @@ -523,6 +543,11 @@ static void paint_brush_stroke_add_step(bContext *C, copy_v2_v2(stroke->last_mouse_position, mouse_in); stroke->last_pressure = pressure; + if (paint_stroke_use_scene_spacing(brush, mode)) { + sculpt_stroke_get_location(C, stroke->last_world_space_position, stroke->last_mouse_position); + mul_m4_v3(stroke->vc.obact->obmat, stroke->last_world_space_position); + } + if (paint_stroke_use_jitter(mode, brush, stroke->stroke_mode == BRUSH_STROKE_INVERT)) { float delta[2]; float factor = stroke->zoom_2d; @@ -600,14 +625,34 @@ static bool paint_smooth_stroke(PaintStroke *stroke, return true; } -static float paint_space_stroke_spacing(const Scene *scene, +static float paint_space_stroke_spacing(bContext *C, + const Scene *scene, PaintStroke *stroke, float size_pressure, float spacing_pressure) { - /* brushes can have a minimum size of 1.0 but with pressure it can be smaller then a pixel - * causing very high step sizes, hanging blender [#32381] */ - const float size_clamp = max_ff(1.0f, BKE_brush_size_get(scene, stroke->brush) * size_pressure); + Paint *paint = BKE_paint_get_active_from_context(C); + ePaintMode mode = BKE_paintmode_get_active_from_context(C); + Brush *brush = BKE_paint_brush(paint); + float size_clamp = 0.0f; + float size = BKE_brush_size_get(scene, stroke->brush) * size_pressure; + if (paint_stroke_use_scene_spacing(brush, mode)) { + if (!BKE_brush_use_locked_size(scene, brush)) { + float last_object_space_position[3]; + mul_v3_m4v3( + last_object_space_position, stroke->vc.obact->imat, stroke->last_world_space_position); + size_clamp = paint_calc_object_space_radius(&stroke->vc, last_object_space_position, size); + } + else { + size_clamp = BKE_brush_unprojected_radius_get(scene, brush) * size_pressure; + } + } + else { + /* brushes can have a minimum size of 1.0 but with pressure it can be smaller then a pixel + * causing very high step sizes, hanging blender [#32381] */ + size_clamp = max_ff(1.0f, size); + } + float spacing = stroke->brush->spacing; /* apply spacing pressure */ @@ -619,7 +664,12 @@ static float paint_space_stroke_spacing(const Scene *scene, * the fact that brush can be scaled there. */ spacing *= stroke->zoom_2d; - return max_ff(1.0, size_clamp * spacing / 50.0f); + if (paint_stroke_use_scene_spacing(brush, mode)) { + return max_ff(0.001f, size_clamp * spacing / 50.f); + } + else { + return max_ff(1.0, size_clamp * spacing / 50.0f); + } } static float paint_stroke_overlapped_curve(Brush *br, float x, float spacing) @@ -677,14 +727,18 @@ static float paint_stroke_integrate_overlap(Brush *br, float factor) } } -static float paint_space_stroke_spacing_variable( - const Scene *scene, PaintStroke *stroke, float pressure, float dpressure, float length) +static float paint_space_stroke_spacing_variable(bContext *C, + const Scene *scene, + PaintStroke *stroke, + float pressure, + float dpressure, + float length) { if (BKE_brush_use_size_pressure(scene, stroke->brush)) { /* use pressure to modify size. set spacing so that at 100%, the circles * are aligned nicely with no overlap. for this the spacing needs to be * the average of the previous and next size. */ - float s = paint_space_stroke_spacing(scene, stroke, 1.0f, pressure); + float s = paint_space_stroke_spacing(C, scene, stroke, 1.0f, pressure); float q = s * dpressure / (2.0f * length); float pressure_fac = (1.0f + q) / (1.0f - q); @@ -692,14 +746,15 @@ static float paint_space_stroke_spacing_variable( float new_size_pressure = stroke->last_pressure * pressure_fac; /* average spacing */ - float last_spacing = paint_space_stroke_spacing(scene, stroke, last_size_pressure, pressure); - float new_spacing = paint_space_stroke_spacing(scene, stroke, new_size_pressure, pressure); + float last_spacing = paint_space_stroke_spacing( + C, scene, stroke, last_size_pressure, pressure); + float new_spacing = paint_space_stroke_spacing(C, scene, stroke, new_size_pressure, pressure); return 0.5f * (last_spacing + new_spacing); } else { /* no size pressure */ - return paint_space_stroke_spacing(scene, stroke, 1.0f, pressure); + return paint_space_stroke_spacing(C, scene, stroke, 1.0f, pressure); } } @@ -711,29 +766,63 @@ static int paint_space_stroke(bContext *C, float final_pressure) { const Scene *scene = CTX_data_scene(C); + ARegion *ar = CTX_wm_region(C); PaintStroke *stroke = op->customdata; UnifiedPaintSettings *ups = stroke->ups; + Paint *paint = BKE_paint_get_active_from_context(C); + ePaintMode mode = BKE_paintmode_get_active_from_context(C); + Brush *brush = BKE_paint_brush(paint); int cnt = 0; - float pressure, dpressure; - float mouse[2], dmouse[2]; - float length; - float no_pressure_spacing = paint_space_stroke_spacing(scene, stroke, 1.0f, 1.0f); - - sub_v2_v2v2(dmouse, final_mouse, stroke->last_mouse_position); + const bool use_scene_spacing = paint_stroke_use_scene_spacing(brush, mode); + float d_world_space_position[3] = {0.0f}; - pressure = stroke->last_pressure; - dpressure = final_pressure - stroke->last_pressure; + float no_pressure_spacing = paint_space_stroke_spacing(C, scene, stroke, 1.0f, 1.0f); + float pressure = stroke->last_pressure; + float dpressure = final_pressure - stroke->last_pressure; - length = normalize_v2(dmouse); + float dmouse[2]; + sub_v2_v2v2(dmouse, final_mouse, stroke->last_mouse_position); + float length = normalize_v2(dmouse); + + if (use_scene_spacing) { + float world_space_position[3]; + bool hit = sculpt_stroke_get_location(C, world_space_position, final_mouse); + mul_m4_v3(stroke->vc.obact->obmat, world_space_position); + if (hit && stroke->stroke_over_mesh) { + sub_v3_v3v3(d_world_space_position, world_space_position, stroke->last_world_space_position); + length = len_v3(d_world_space_position); + stroke->stroke_over_mesh = true; + } + else { + length = 0.0f; + zero_v3(d_world_space_position); + stroke->stroke_over_mesh = hit; + if (stroke->stroke_over_mesh) { + copy_v3_v3(stroke->last_world_space_position, world_space_position); + } + } + } while (length > 0.0f) { float spacing = paint_space_stroke_spacing_variable( - scene, stroke, pressure, dpressure, length); + C, scene, stroke, pressure, dpressure, length); + float mouse[3]; if (length >= spacing) { - mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing; - mouse[1] = stroke->last_mouse_position[1] + dmouse[1] * spacing; + if (use_scene_spacing) { + float final_world_space_position[3]; + normalize_v3(d_world_space_position); + mul_v3_v3fl(final_world_space_position, d_world_space_position, spacing); + add_v3_v3v3(final_world_space_position, + stroke->last_world_space_position, + final_world_space_position); + ED_view3d_project(ar, final_world_space_position, mouse); + } + else { + mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing; + mouse[1] = stroke->last_mouse_position[1] + dmouse[1] * spacing; + } pressure = stroke->last_pressure + (spacing / length) * dpressure; ups->overlap_factor = paint_stroke_integrate_overlap(stroke->brush, @@ -767,14 +856,16 @@ PaintStroke *paint_stroke_new(bContext *C, StrokeDone done, int event_type) { + struct Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); PaintStroke *stroke = MEM_callocN(sizeof(PaintStroke), "PaintStroke"); ToolSettings *toolsettings = CTX_data_tool_settings(C); UnifiedPaintSettings *ups = &toolsettings->unified_paint_settings; Paint *p = BKE_paint_get_active_from_context(C); Brush *br = stroke->brush = BKE_paint_brush(p); + RegionView3D *rv3d = CTX_wm_region_view3d(C); float zoomx, zoomy; - ED_view3d_viewcontext_init(C, &stroke->vc); + ED_view3d_viewcontext_init(C, &stroke->vc, depsgraph); stroke->get_location = get_location; stroke->test_start = test_start; @@ -797,6 +888,10 @@ PaintStroke *paint_stroke_new(bContext *C, ups->overlap_factor = 1.0; ups->stroke_active = true; + if (rv3d) { + rv3d->rflag |= RV3D_PAINTING; + } + zero_v3(ups->average_stroke_accum); ups->average_stroke_counter = 0; @@ -811,20 +906,42 @@ PaintStroke *paint_stroke_new(bContext *C, return stroke; } -void paint_stroke_data_free(struct wmOperator *op) +void paint_stroke_free(bContext *C, wmOperator *op) { + RegionView3D *rv3d = CTX_wm_region_view3d(C); + PaintStroke *stroke = op->customdata; + UnifiedPaintSettings *ups = stroke->ups; + + ups->draw_anchored = false; + ups->stroke_active = false; + + if (rv3d) { + rv3d->rflag &= ~RV3D_PAINTING; + } + + if (stroke->timer) { + WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), stroke->timer); + } + + if (stroke->rng) { + BLI_rng_free(stroke->rng); + } + + if (stroke->stroke_cursor) { + WM_paint_cursor_end(CTX_wm_manager(C), stroke->stroke_cursor); + } + + BLI_freelistN(&stroke->line); + BKE_paint_set_overlay_override(0); MEM_SAFE_FREE(op->customdata); } -static void stroke_done(struct bContext *C, struct wmOperator *op) +static void stroke_done(bContext *C, wmOperator *op) { - struct PaintStroke *stroke = op->customdata; + PaintStroke *stroke = op->customdata; UnifiedPaintSettings *ups = stroke->ups; - ups->draw_anchored = false; - ups->stroke_active = false; - /* reset rotation here to avoid doing so in cursor display */ if (!(stroke->brush->mtex.brush_angle_mode & MTEX_ANGLE_RAKE)) { ups->brush_rotation = 0.0f; @@ -844,21 +961,7 @@ static void stroke_done(struct bContext *C, struct wmOperator *op) } } - if (stroke->timer) { - WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), stroke->timer); - } - - if (stroke->rng) { - BLI_rng_free(stroke->rng); - } - - if (stroke->stroke_cursor) { - WM_paint_cursor_end(CTX_wm_manager(C), stroke->stroke_cursor); - } - - BLI_freelistN(&stroke->line); - - paint_stroke_data_free(op); + paint_stroke_free(C, op); } /* Returns zero if the stroke dots should not be spaced, non-zero otherwise */ @@ -871,6 +974,8 @@ static bool sculpt_is_grab_tool(Brush *br) { return ELEM(br->sculpt_tool, SCULPT_TOOL_GRAB, + SCULPT_TOOL_ELASTIC_DEFORM, + SCULPT_TOOL_POSE, SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_SNAKE_HOOK); @@ -1079,7 +1184,7 @@ static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *str if (br->flag & BRUSH_CURVE) { UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; const Scene *scene = CTX_data_scene(C); - const float spacing = paint_space_stroke_spacing(scene, stroke, 1.0f, 1.0f); + const float spacing = paint_space_stroke_spacing(C, scene, stroke, 1.0f, 1.0f); PaintCurve *pc = br->paint_curve; PaintCurvePoint *pcp; float length_residue = 0.0f; @@ -1250,6 +1355,11 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) if (!stroke->stroke_started) { stroke->last_pressure = sample_average.pressure; copy_v2_v2(stroke->last_mouse_position, sample_average.mouse); + if (paint_stroke_use_scene_spacing(br, mode)) { + stroke->stroke_over_mesh = sculpt_stroke_get_location( + C, stroke->last_world_space_position, sample_average.mouse); + mul_m4_v3(stroke->vc.obact->obmat, stroke->last_world_space_position); + } stroke->stroke_started = stroke->test_start(C, op, sample_average.mouse); BLI_assert((stroke->stroke_started & ~1) == 0); /* 0/1 */ diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c index 4b9d9a2cc01..a014fe7fdff 100644 --- a/source/blender/editors/sculpt_paint/paint_utils.c +++ b/source/blender/editors/sculpt_paint/paint_utils.c @@ -46,7 +46,6 @@ #include "BKE_image.h" #include "BKE_material.h" #include "BKE_mesh_runtime.h" -#include "BKE_object.h" #include "BKE_paint.h" #include "BKE_report.h" @@ -101,9 +100,9 @@ bool paint_convert_bb_to_rect(rcti *rect, ED_view3d_ob_project_mat_get(rv3d, ob, projection_mat); - for (i = 0; i < 2; ++i) { - for (j = 0; j < 2; ++j) { - for (k = 0; k < 2; ++k) { + for (i = 0; i < 2; i++) { + for (j = 0; j < 2; j++) { + for (k = 0; k < 2; k++) { float vec[3], proj[2]; int proj_i[2]; vec[0] = i ? bb_min[0] : bb_max[0]; @@ -145,7 +144,6 @@ void paint_calc_redraw_planes(float planes[4][4], rect.ymax += 2; ED_view3d_clipping_calc(&bb, planes, ar, ob, &rect); - negate_m4(planes); } float paint_calc_object_space_radius(ViewContext *vc, const float center[3], float pixel_radius) @@ -251,7 +249,7 @@ static void imapaint_project(float matrix[4][4], const float co[3], float pco[4] } static void imapaint_tri_weights(float matrix[4][4], - GLint view[4], + const GLint view[4], const float v1[3], const float v2[3], const float v3[3], @@ -506,7 +504,7 @@ void paint_sample_color( unsigned int totpoly = me->totpoly; if (CustomData_has_layer(&me_eval->ldata, CD_MLOOPUV)) { - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); view3d_operator_needs_opengl(C); diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index 12da8790b91..3554a6cc546 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -34,7 +34,6 @@ #include "BLI_array_utils.h" #include "BLI_task.h" -#include "DNA_armature_types.h" #include "DNA_mesh_types.h" #include "DNA_particle_types.h" #include "DNA_scene_types.h" @@ -42,7 +41,6 @@ #include "DNA_object_types.h" #include "RNA_access.h" -#include "RNA_define.h" #include "BKE_brush.h" #include "BKE_context.h" @@ -195,17 +193,12 @@ static bool vertex_paint_use_fast_update_check(Object *ob) return false; } -static void paint_last_stroke_update(Scene *scene, ARegion *ar, const float mval[2]) +static void paint_last_stroke_update(Scene *scene, const float location[3]) { - const int mval_i[2] = {mval[0], mval[1]}; - float world[3]; - - if (ED_view3d_autodist_simple(ar, mval_i, world, 0, NULL)) { - UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; - ups->average_stroke_counter++; - add_v3_v3(ups->average_stroke_accum, world); - ups->last_stroke_valid = true; - } + UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; + ups->average_stroke_counter++; + add_v3_v3(ups->average_stroke_accum, location); + ups->last_stroke_valid = true; } /* polling - retrieve whether cursor should be set or operator should be done */ @@ -260,7 +253,7 @@ static bool weight_paint_poll_ex(bContext *C, bool check_tool) (BKE_paint_brush(&CTX_data_tool_settings(C)->wpaint->paint) != NULL) && (sa = CTX_wm_area(C)) && (sa->spacetype == SPACE_VIEW3D)) { ARegion *ar = CTX_wm_region(C); - if (ar->regiontype == RGN_TYPE_WINDOW) { + if (ELEM(ar->regiontype, RGN_TYPE_WINDOW, RGN_TYPE_HUD)) { if (!check_tool || WM_toolsystem_active_tool_is_brush(C)) { return 1; } @@ -1610,7 +1603,7 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo /* make mode data storage */ wpd = MEM_callocN(sizeof(struct WPaintData), "WPaintData"); paint_stroke_set_mode_data(stroke, wpd); - ED_view3d_viewcontext_init(C, &wpd->vc); + ED_view3d_viewcontext_init(C, &wpd->vc, depsgraph); view_angle_limits_init(&wpd->normal_angle_precalc, vp->paint.brush->falloff_angle, (vp->paint.brush->flag & BRUSH_FRONTFACE_FALLOFF) != 0); @@ -2080,9 +2073,7 @@ static void calculate_average_weight(SculptThreadedTaskData *data, data->custom_data = accum; TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((data->sd->flags & SCULPT_USE_OPENMP) && - totnode > SCULPT_THREADED_LIMIT); + BKE_pbvh_parallel_range_settings(&settings, (data->sd->flags & SCULPT_USE_OPENMP), totnode); BLI_task_parallel_range(0, totnode, data, do_wpaint_brush_calc_average_weight_cb_ex, &settings); uint accum_len = 0; @@ -2128,10 +2119,9 @@ static void wpaint_paint_leaves(bContext *C, /* Use this so average can modify its weight without touching the brush. */ data.strength = BKE_brush_weight_get(scene, brush); - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); /* NOTE: current mirroring code cannot be run in parallel */ - settings.use_threading = !(me->editflag & ME_EDIT_MIRROR_X); + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, !(me->editflag & ME_EDIT_MIRROR_X), totnode); switch ((eBrushWeightPaintTool)brush->weightpaint_tool) { case WPAINT_TOOL_AVERAGE: @@ -2351,7 +2341,7 @@ static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P /* calculate pivot for rotation around seletion if needed */ /* also needed for "View Selected" on last stroke */ - paint_last_stroke_update(scene, vc->ar, ss->cache->mouse); + paint_last_stroke_update(scene, ss->cache->location); BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); @@ -2446,7 +2436,7 @@ static int wpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event) event->type); if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { - paint_stroke_data_free(op); + paint_stroke_free(C, op); return OPERATOR_FINISHED; } /* add modal handler */ @@ -2655,7 +2645,7 @@ static bool vpaint_stroke_test_start(bContext *C, struct wmOperator *op, const f /* make mode data storage */ vpd = MEM_callocN(sizeof(*vpd), "VPaintData"); paint_stroke_set_mode_data(stroke, vpd); - ED_view3d_viewcontext_init(C, &vpd->vc); + ED_view3d_viewcontext_init(C, &vpd->vc, depsgraph); view_angle_limits_init(&vpd->normal_angle_precalc, vp->paint.brush->falloff_angle, (vp->paint.brush->flag & BRUSH_FRONTFACE_FALLOFF) != 0); @@ -3142,7 +3132,7 @@ static void calculate_average_color(SculptThreadedTaskData *data, data->custom_data = accum; TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); + BKE_pbvh_parallel_range_settings(&settings, true, totnode); BLI_task_parallel_range(0, totnode, data, do_vpaint_brush_calc_average_color_cb_ex, &settings); uint accum_len = 0; @@ -3188,7 +3178,7 @@ static void vpaint_paint_leaves(bContext *C, .me = me, }; TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); + BKE_pbvh_parallel_range_settings(&settings, true, totnode); switch ((eBrushVertexPaintTool)brush->vertexpaint_tool) { case VPAINT_TOOL_AVERAGE: calculate_average_color(&data, nodes, totnode); @@ -3331,7 +3321,7 @@ static void vpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P /* calculate pivot for rotation around seletion if needed */ /* also needed for "View Selected" on last stroke */ - paint_last_stroke_update(scene, vc->ar, ss->cache->mouse); + paint_last_stroke_update(scene, ss->cache->location); ED_region_tag_redraw(vc->ar); @@ -3388,7 +3378,7 @@ static int vpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event) event->type); if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { - paint_stroke_data_free(op); + paint_stroke_free(C, op); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c index 9a6251e2f98..266c130d12a 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c @@ -272,7 +272,6 @@ static bool vertex_color_smooth(Object *ob) { Mesh *me; const MPoly *mp; - int i, j; bool *mlooptag; @@ -282,6 +281,7 @@ static bool vertex_color_smooth(Object *ob) } const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; + const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; mlooptag = MEM_callocN(sizeof(bool) * me->totloop, "VPaintData mlooptag"); @@ -289,15 +289,19 @@ static bool vertex_color_smooth(Object *ob) mp = me->mpoly; for (i = 0; i < me->totpoly; i++, mp++) { const MLoop *ml = me->mloop + mp->loopstart; - int ml_index = mp->loopstart; if (use_face_sel && !(mp->flag & ME_FACE_SEL)) { continue; } - for (j = 0; j < mp->totloop; j++, ml_index++, ml++) { - mlooptag[ml_index] = true; - } + j = 0; + do { + if (!(use_vert_sel && !(me->mvert[ml->v].flag & SELECT))) { + mlooptag[mp->loopstart + j] = true; + } + ml++; + j++; + } while (j < mp->totloop); } /* remove stale me->mcol, will be added later */ diff --git a/source/blender/editors/sculpt_paint/paint_vertex_color_utils.c b/source/blender/editors/sculpt_paint/paint_vertex_color_utils.c index 6511c90f5e1..71865d0de73 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_color_utils.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_color_utils.c @@ -20,13 +20,9 @@ * Intended for use by `paint_vertex.c` & `paint_vertex_color_ops.c`. */ -#include "MEM_guardedalloc.h" - -#include "DNA_brush_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" -#include "DNA_scene_types.h" #include "BLI_math_base.h" #include "BLI_math_color.h" @@ -54,29 +50,37 @@ bool ED_vpaint_color_transform(struct Object *ob, { Mesh *me; const MPoly *mp; + int i, j; if (((me = BKE_mesh_from_object(ob)) == NULL) || (ED_mesh_color_ensure(me, NULL) == false)) { return false; } const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; - mp = me->mpoly; + const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; - for (int i = 0; i < me->totpoly; i++, mp++) { - MLoopCol *lcol = &me->mloopcol[mp->loopstart]; + mp = me->mpoly; + for (i = 0; i < me->totpoly; i++, mp++) { + MLoopCol *lcol = me->mloopcol + mp->loopstart; if (use_face_sel && !(mp->flag & ME_FACE_SEL)) { continue; } - for (int j = 0; j < mp->totloop; j++, lcol++) { - float col_mix[3]; - rgb_uchar_to_float(col_mix, &lcol->r); + j = 0; + do { + uint vidx = me->mloop[mp->loopstart + j].v; + if (!(use_vert_sel && !(me->mvert[vidx].flag & SELECT))) { + float col_mix[3]; + rgb_uchar_to_float(col_mix, &lcol->r); - vpaint_tx_fn(col_mix, user_data, col_mix); + vpaint_tx_fn(col_mix, user_data, col_mix); - rgb_float_to_uchar(&lcol->r, col_mix); - } + rgb_float_to_uchar(&lcol->r, col_mix); + } + lcol++; + j++; + } while (j < mp->totloop); } /* remove stale me->mcol, will be added later */ diff --git a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c index 4aa9dc8a295..f0fe2d4ebdc 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c @@ -24,13 +24,8 @@ #include "BLI_math.h" #include "BLI_bitmap.h" -#include "IMB_imbuf.h" -#include "IMB_imbuf_types.h" -#include "IMB_colormanagement.h" - #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" -#include "DNA_particle_types.h" #include "DNA_brush_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" @@ -44,7 +39,6 @@ #include "BKE_deform.h" #include "BKE_mesh.h" #include "BKE_mesh_iterators.h" -#include "BKE_mesh_mapping.h" #include "BKE_mesh_runtime.h" #include "BKE_modifier.h" #include "BKE_object_deform.h" @@ -178,11 +172,12 @@ void PAINT_OT_weight_from_bones(wmOperatorType *ot) /* note: we cant sample frontbuf, weight colors are interpolated too unpredictable */ static int weight_sample_invoke(bContext *C, wmOperator *op, const wmEvent *event) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewContext vc; Mesh *me; bool changed = false; - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); me = BKE_mesh_from_object(vc.obact); if (me && me->dvert && vc.v3d && vc.rv3d && (vc.obact->actdef != 0)) { @@ -308,10 +303,11 @@ static const EnumPropertyItem *weight_paint_sample_enum_itemf(bContext *C, if (C) { wmWindow *win = CTX_wm_window(C); if (win && win->eventstate) { + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); ViewContext vc; Mesh *me; - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); me = BKE_mesh_from_object(vc.obact); if (me && me->dvert && vc.v3d && vc.rv3d && vc.obact->defbase.first) { @@ -379,8 +375,9 @@ static const EnumPropertyItem *weight_paint_sample_enum_itemf(bContext *C, static int weight_sample_group_exec(bContext *C, wmOperator *op) { int type = RNA_enum_get(op->ptr, "group"); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewContext vc; - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); BLI_assert(type + 1 >= 0); vc.obact->actdef = type + 1; @@ -885,7 +882,7 @@ void PAINT_OT_weight_gradient(wmOperatorType *ot) prop = RNA_def_enum(ot->srna, "type", gradient_types, 0, "Type", ""); RNA_def_property_flag(prop, PROP_SKIP_SAVE); - WM_operator_properties_gesture_straightline(ot, CURSOR_EDIT); + WM_operator_properties_gesture_straightline(ot, WM_CURSOR_EDIT); } /** \} */ diff --git a/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c b/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c index c71315872f6..28699b45add 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c @@ -20,16 +20,12 @@ * Intended for use by `paint_vertex.c` & `paint_vertex_weight_ops.c`. */ -#include "MEM_guardedalloc.h" - #include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_string_utils.h" #include "DNA_armature_types.h" #include "DNA_mesh_types.h" -#include "DNA_scene_types.h" -#include "DNA_brush_types.h" #include "DNA_object_types.h" #include "BKE_action.h" diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 08febfcc470..b9d621fc1fb 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -27,9 +27,11 @@ #include "BLI_math.h" #include "BLI_blenlib.h" #include "BLI_dial_2d.h" +#include "BLI_gsqueue.h" +#include "BLI_ghash.h" +#include "BLI_hash.h" #include "BLI_task.h" #include "BLI_utildefines.h" -#include "BLI_ghash.h" #include "BLT_translation.h" @@ -65,7 +67,6 @@ #include "BKE_subsurf.h" #include "DEG_depsgraph.h" -#include "DEG_depsgraph_query.h" #include "WM_api.h" #include "WM_types.h" @@ -92,22 +93,18 @@ #include <stdlib.h> #include <string.h> -/* Sculpt PBVH abstraction API */ - -/* Do not use these functions while working with PBVH_GRIDS data in SculptSession */ - -/* TODO: why is this kept, should it be removed? */ -#if 0 /* UNUSED */ +/* Sculpt PBVH abstraction API + * + * This is read-only, for writing use PBVH vertex iterators. There vd.index matches + * the indices used here. + * + * For multires, the same vertex in multiple grids is counted multiple times, with + * different index for each grid. */ -static int sculpt_active_vertex_get(SculptSession *ss) +static void sculpt_vertex_random_access_init(SculptSession *ss) { - switch (BKE_pbvh_type(ss->pbvh)) { - case PBVH_FACES: - return ss->active_vertex_index; - case PBVH_BMESH: - return ss->active_vertex_index; - default: - return 0; + if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + BM_mesh_elem_index_ensure(ss->bm, BM_VERT); } } @@ -118,65 +115,48 @@ static int sculpt_vertex_count_get(SculptSession *ss) return ss->totvert; case PBVH_BMESH: return BM_mesh_elem_count(BKE_pbvh_get_bmesh(ss->pbvh), BM_VERT); - default: - return 0; + case PBVH_GRIDS: + return BKE_pbvh_get_grid_num_vertices(ss->pbvh); } -} -static void sculpt_vertex_normal_get(SculptSession *ss, int index, float no[3]) -{ - switch (BKE_pbvh_type(ss->pbvh)) { - case PBVH_FACES: - normal_short_to_float_v3(no, ss->mvert[index].no); - return; - case PBVH_BMESH: - copy_v3_v3(no, BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index)->no); - default: - return; - } + return 0; } -static float *sculpt_vertex_co_get(SculptSession *ss, int index) +const float *sculpt_vertex_co_get(SculptSession *ss, int index) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: return ss->mvert[index].co; case PBVH_BMESH: return BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index)->co; - default: - return NULL; - } -} - -static void sculpt_vertex_co_set(SculptSession *ss, int index, float co[3]) -{ - switch (BKE_pbvh_type(ss->pbvh)) { - case PBVH_FACES: - copy_v3_v3(ss->mvert[index].co, co); - return; - case PBVH_BMESH: - copy_v3_v3(BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index)->co, co); - return; - default: - return; + case PBVH_GRIDS: { + const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); + const int grid_index = index / key->grid_area; + const int vertex_index = index - grid_index * key->grid_area; + CCGElem *elem = BKE_pbvh_get_grids(ss->pbvh)[grid_index]; + return CCG_elem_co(key, CCG_elem_offset(key, elem, vertex_index)); + } } + return NULL; } -static void sculpt_vertex_mask_set(SculptSession *ss, int index, float mask) +static void sculpt_vertex_normal_get(SculptSession *ss, int index, float no[3]) { - BMVert *v; - float *mask_p; switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: - ss->vmask[index] = mask; + normal_short_to_float_v3(no, ss->mvert[index].no); return; case PBVH_BMESH: - v = BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index); - mask_p = BM_ELEM_CD_GET_VOID_P(v, CustomData_get_offset(&ss->bm->vdata, CD_PAINT_MASK)); - *(mask_p) = mask; - return; - default: - return; + copy_v3_v3(no, BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index)->no); + break; + case PBVH_GRIDS: { + const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); + const int grid_index = index / key->grid_area; + const int vertex_index = index - grid_index * key->grid_area; + CCGElem *elem = BKE_pbvh_get_grids(ss->pbvh)[grid_index]; + copy_v3_v3(no, CCG_elem_no(key, CCG_elem_offset(key, elem, vertex_index))); + break; + } } } @@ -191,25 +171,44 @@ static float sculpt_vertex_mask_get(SculptSession *ss, int index) v = BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index); mask = BM_ELEM_CD_GET_VOID_P(v, CustomData_get_offset(&ss->bm->vdata, CD_PAINT_MASK)); return *mask; - default: - return 0; + case PBVH_GRIDS: { + const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); + const int grid_index = index / key->grid_area; + const int vertex_index = index - grid_index * key->grid_area; + CCGElem *elem = BKE_pbvh_get_grids(ss->pbvh)[grid_index]; + return *CCG_elem_mask(key, CCG_elem_offset(key, elem, vertex_index)); + } } + + return 0.0f; } -static void sculpt_vertex_tag_update(SculptSession *ss, int index) +static int sculpt_active_vertex_get(SculptSession *ss) { + BLI_assert(BKE_pbvh_type(ss->pbvh) != PBVH_GRIDS); switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: - ss->mvert[index].flag |= ME_VERT_PBVH_UPDATE; - return; + return ss->active_vertex_index; case PBVH_BMESH: - return; - default: - return; + return ss->active_vertex_index; + case PBVH_GRIDS: + return ss->active_vertex_index; } + + return 0; } -# define SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY 256 +static const float *sculpt_active_vertex_co_get(SculptSession *ss) +{ + return sculpt_vertex_co_get(ss, sculpt_active_vertex_get(ss)); +} + +static void sculpt_active_vertex_normal_get(SculptSession *ss, float normal[3]) +{ + sculpt_vertex_normal_get(ss, sculpt_active_vertex_get(ss), normal); +} + +#define SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY 256 typedef struct SculptVertexNeighborIter { int *neighbors; @@ -286,16 +285,26 @@ static void sculpt_vertex_neighbors_get_faces(SculptSession *ss, if (poly_get_adj_loops_from_vert(p, ss->mloop, (int)index, f_adj_v) != -1) { int j; for (j = 0; j < ARRAY_SIZE(f_adj_v); j += 1) { - if (vert_map->count != 2 || ss->pmap[f_adj_v[j]].count <= 2) { - if (f_adj_v[j] != (int)index) { - sculpt_vertex_neighbor_add(iter, f_adj_v[j]); - } + if (f_adj_v[j] != (int)index) { + sculpt_vertex_neighbor_add(iter, f_adj_v[j]); } } } } } +static void sculpt_vertex_neighbors_get_grids(SculptSession *UNUSED(ss), + int UNUSED(index), + SculptVertexNeighborIter *iter) +{ + /* TODO: implement this for multires. It might also be worth changing this + * iterator to provide a coordinate and mask pointer directly for effiency, + * rather than converting back and forth between CCGElem and global index. */ + iter->size = 0; + iter->capacity = SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY; + iter->neighbors = iter->neighbors_fixed; +} + static void sculpt_vertex_neighbors_get(SculptSession *ss, int index, SculptVertexNeighborIter *iter) @@ -307,24 +316,247 @@ static void sculpt_vertex_neighbors_get(SculptSession *ss, case PBVH_BMESH: sculpt_vertex_neighbors_get_bmesh(ss, index, iter); return; - default: - break; + case PBVH_GRIDS: + sculpt_vertex_neighbors_get_grids(ss, index, iter); + return; + } +} + +#define sculpt_vertex_neighbors_iter_begin(ss, v_index, neighbor_iterator) \ + sculpt_vertex_neighbors_get(ss, v_index, &neighbor_iterator); \ + for (neighbor_iterator.i = 0; neighbor_iterator.i < neighbor_iterator.size; \ + neighbor_iterator.i++) { \ + neighbor_iterator.index = ni.neighbors[ni.i]; + +#define sculpt_vertex_neighbors_iter_end(neighbor_iterator) \ + } \ + if (neighbor_iterator.neighbors != neighbor_iterator.neighbors_fixed) { \ + MEM_freeN(neighbor_iterator.neighbors); \ + } \ + ((void)0) + +/* Utils */ +static bool check_vertex_pivot_symmetry(const float vco[3], const float pco[3], const char symm) +{ + bool is_in_symmetry_area = true; + for (int i = 0; i < 3; i++) { + char symm_it = 1 << i; + if (symm & symm_it) { + if (pco[i] == 0.0f) { + if (vco[i] > 0.0f) { + is_in_symmetry_area = false; + } + } + if (vco[i] * pco[i] < 0.0f) { + is_in_symmetry_area = false; + } + } } + return is_in_symmetry_area; } -# define sculpt_vertex_neighbors_iter_begin(ss, v_index, neighbor_iterator) \ - sculpt_vertex_neighbors_get(ss, v_index, &neighbor_iterator); \ - for (neighbor_iterator.i = 0; neighbor_iterator.i < neighbor_iterator.size; \ - neighbor_iterator.i++) { \ - neighbor_iterator.index = ni.neighbors[ni.i]; +typedef struct NearestVertexTLSData { + int nearest_vertex_index; + float nearest_vertex_distance_squared; +} NearestVertexTLSData; + +static void do_nearest_vertex_get_task_cb(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + NearestVertexTLSData *nvtd = tls->userdata_chunk; + PBVHVertexIter vd; -# define sculpt_vertex_neighbors_iter_end(neighbor_iterator) \ - } \ - if (neighbor_iterator.neighbors != neighbor_iterator.neighbors_fixed) { \ - MEM_freeN(neighbor_iterator.neighbors); \ + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + float distance_squared = len_squared_v3v3(vd.co, data->nearest_vertex_search_co); + if (distance_squared < nvtd->nearest_vertex_distance_squared && + distance_squared < data->max_distance_squared) { + nvtd->nearest_vertex_index = vd.index; + nvtd->nearest_vertex_distance_squared = distance_squared; } + } + BKE_pbvh_vertex_iter_end; +} + +static void nearest_vertex_get_finalize(void *__restrict userdata, void *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + NearestVertexTLSData *nvtd = tls; + if (data->nearest_vertex_index == -1) { + data->nearest_vertex_index = nvtd->nearest_vertex_index; + } + else if (nvtd->nearest_vertex_distance_squared < data->nearest_vertex_distance_squared) { + data->nearest_vertex_index = nvtd->nearest_vertex_index; + data->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared; + } +} + +static int sculpt_nearest_vertex_get( + Sculpt *sd, Object *ob, float co[3], float max_distance, bool use_original) +{ + SculptSession *ss = ob->sculpt; + PBVHNode **nodes = NULL; + int totnode; + SculptSearchSphereData data = { + .ss = ss, + .sd = sd, + .radius_squared = max_distance * max_distance, + .original = use_original, + .center = co, + }; + BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, &totnode); + if (totnode == 0) { + return -1; + } + + SculptThreadedTaskData task_data = { + .sd = sd, + .ob = ob, + .nodes = nodes, + .max_distance_squared = max_distance * max_distance, + .nearest_vertex_index = -1, + }; -#endif /* UNUSED */ + copy_v3_v3(task_data.nearest_vertex_search_co, co); + task_data.nearest_vertex_distance_squared = FLT_MAX; + NearestVertexTLSData nvtd; + nvtd.nearest_vertex_index = -1; + nvtd.nearest_vertex_distance_squared = FLT_MAX; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + settings.func_finalize = nearest_vertex_get_finalize; + settings.userdata_chunk = &nvtd; + settings.userdata_chunk_size = sizeof(NearestVertexTLSData); + BLI_task_parallel_range(0, totnode, &task_data, do_nearest_vertex_get_task_cb, &settings); + + MEM_SAFE_FREE(nodes); + + return task_data.nearest_vertex_index; +} + +static bool is_symmetry_iteration_valid(char i, char symm) +{ + return i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5))); +} + +/* Checks if a vertex is inside the brush radius from any of its mirrored axis */ +static bool sculpt_is_vertex_inside_brush_radius_symm(const float vertex[3], + const float br_co[3], + float radius, + char symm) +{ + for (char i = 0; i <= symm; ++i) { + if (is_symmetry_iteration_valid(i, symm)) { + float location[3]; + flip_v3_v3(location, br_co, (char)i); + if (len_squared_v3v3(location, vertex) < radius * radius) { + return true; + } + } + } + return false; +} + +/* Sculpt Flood Fill API + * + * Iterate over connected vertices, starting from one or more initial vertices. */ + +typedef struct SculptFloodFill { + GSQueue *queue; + char *visited_vertices; +} SculptFloodFill; + +typedef struct SculptFloodFillIterator { + int v; + int it; + float edge_factor; +} SculptFloodFillIterator; + +static void sculpt_floodfill_init(SculptSession *ss, SculptFloodFill *flood) +{ + int vertex_count = sculpt_vertex_count_get(ss); + sculpt_vertex_random_access_init(ss); + + flood->queue = BLI_gsqueue_new(sizeof(SculptFloodFillIterator)); + flood->visited_vertices = MEM_callocN(vertex_count * sizeof(char), "visited vertices"); +} + +static void sculpt_floodfill_add_initial(SculptFloodFill *flood, int index) +{ + SculptFloodFillIterator mevit; + mevit.v = index; + mevit.it = 0; + mevit.edge_factor = 1.0f; + BLI_gsqueue_push(flood->queue, &mevit); +} + +static void sculpt_floodfill_add_active( + Sculpt *sd, Object *ob, SculptSession *ss, SculptFloodFill *flood, float radius) +{ + /* Add active vertex and symmetric vertices to the queue. */ + const char symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; + for (char i = 0; i <= symm; ++i) { + if (is_symmetry_iteration_valid(i, symm)) { + int v = -1; + if (i == 0) { + v = sculpt_active_vertex_get(ss); + } + else if (radius > 0.0f) { + float radius_squared = (radius == FLT_MAX) ? FLT_MAX : radius * radius; + float location[3]; + flip_v3_v3(location, sculpt_active_vertex_co_get(ss), i); + v = sculpt_nearest_vertex_get(sd, ob, location, radius_squared, false); + } + if (v != -1) { + sculpt_floodfill_add_initial(flood, v); + } + } + } +} + +static void sculpt_floodfill_execute(SculptSession *ss, + SculptFloodFill *flood, + bool (*func)(SculptSession *ss, + const SculptFloodFillIterator *from, + SculptFloodFillIterator *to, + void *userdata), + void *userdata) +{ + /* TODO: multires support, taking into account duplicate vertices and + * correctly handling them in the pose, automask and mask expand callbacks. */ + while (!BLI_gsqueue_is_empty(flood->queue)) { + SculptFloodFillIterator from; + BLI_gsqueue_pop(flood->queue, &from); + SculptVertexNeighborIter ni; + sculpt_vertex_neighbors_iter_begin(ss, from.v, ni) + { + if (flood->visited_vertices[ni.index] == 0) { + flood->visited_vertices[ni.index] = 1; + + SculptFloodFillIterator to; + to.v = ni.index; + to.it = from.it + 1; + to.edge_factor = 0.0f; + + if (func(ss, &from, &to, userdata)) { + BLI_gsqueue_push(flood->queue, &to); + } + } + } + sculpt_vertex_neighbors_iter_end(ni); + } +} + +static void sculpt_floodfill_free(SculptFloodFill *flood) +{ + MEM_SAFE_FREE(flood->visited_vertices); + BLI_gsqueue_free(flood->queue); + flood->queue = NULL; +} /** \name Tool Capabilities * @@ -354,13 +586,19 @@ static bool sculpt_has_active_modifiers(Scene *scene, Object *ob) static bool sculpt_tool_needs_original(const char sculpt_tool) { - return ELEM( - sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_THUMB, SCULPT_TOOL_LAYER); + return ELEM(sculpt_tool, + SCULPT_TOOL_GRAB, + SCULPT_TOOL_ROTATE, + SCULPT_TOOL_THUMB, + SCULPT_TOOL_LAYER, + SCULPT_TOOL_DRAW_SHARP, + SCULPT_TOOL_ELASTIC_DEFORM, + SCULPT_TOOL_POSE); } static bool sculpt_tool_is_proxy_used(const char sculpt_tool) { - return ELEM(sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_LAYER); + return ELEM(sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_LAYER, SCULPT_TOOL_POSE); } static bool sculpt_brush_use_topology_rake(const SculptSession *ss, const Brush *brush) @@ -381,9 +619,11 @@ static int sculpt_brush_needs_normal(const SculptSession *ss, const Brush *brush SCULPT_TOOL_BLOB, SCULPT_TOOL_CREASE, SCULPT_TOOL_DRAW, + SCULPT_TOOL_DRAW_SHARP, SCULPT_TOOL_LAYER, SCULPT_TOOL_NUDGE, SCULPT_TOOL_ROTATE, + SCULPT_TOOL_ELASTIC_DEFORM, SCULPT_TOOL_THUMB) || (brush->mtex.brush_map_mode == MTEX_MAP_MODE_AREA)) || @@ -673,14 +913,10 @@ static void paint_mesh_restore_co(Sculpt *sd, Object *ob) }; TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && !ss->bm && - totnode > SCULPT_THREADED_LIMIT); + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP) && !ss->bm, totnode); BLI_task_parallel_range(0, totnode, &data, paint_mesh_restore_co_task_cb, &settings); - if (nodes) { - MEM_freeN(nodes); - } + MEM_SAFE_FREE(nodes); } /*** BVH Tree ***/ @@ -748,17 +984,26 @@ void ED_sculpt_redraw_planes_get(float planes[4][4], ARegion *ar, Object *ob) void sculpt_brush_test_init(SculptSession *ss, SculptBrushTest *test) { - RegionView3D *rv3d = ss->cache->vc->rv3d; + RegionView3D *rv3d = ss->cache ? ss->cache->vc->rv3d : ss->rv3d; + + test->radius_squared = ss->cache ? ss->cache->radius_squared : + ss->cursor_radius * ss->cursor_radius; + if (ss->cache) { + copy_v3_v3(test->location, ss->cache->location); + test->mirror_symmetry_pass = ss->cache->mirror_symmetry_pass; + } + else { + copy_v3_v3(test->location, ss->cursor_location); + test->mirror_symmetry_pass = 0; + } - test->radius_squared = ss->cache->radius_squared; - copy_v3_v3(test->location, ss->cache->location); test->dist = 0.0f; /* just for initialize */ /* Only for 2D projection. */ zero_v4(test->plane_view); zero_v4(test->plane_tool); - test->mirror_symmetry_pass = ss->cache->mirror_symmetry_pass; + test->mirror_symmetry_pass = ss->cache ? ss->cache->mirror_symmetry_pass : 0; if (rv3d->rflag & RV3D_CLIPPING) { test->clip_rv3d = rv3d; @@ -945,6 +1190,123 @@ static bool sculpt_brush_test_cyl(SculptBrushTest *test, #endif +/* Automasking */ + +static bool sculpt_automasking_enabled(SculptSession *ss, const Brush *br) +{ + // REMOVE WITH PBVH_GRIDS + if (ss->pbvh && BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { + return false; + } + + if (sculpt_stroke_is_dynamic_topology(ss, br)) { + return false; + } + if (br->automasking_flags & BRUSH_AUTOMASKING_TOPOLOGY) { + return true; + } + return false; +} + +static float sculpt_automasking_factor_get(SculptSession *ss, int vert) +{ + if (ss->cache->automask) { + return ss->cache->automask[vert]; + } + else { + return 1.0f; + } +} + +static void sculpt_automasking_end(Object *ob) +{ + SculptSession *ss = ob->sculpt; + if (ss->cache && ss->cache->automask) { + MEM_freeN(ss->cache->automask); + } +} + +static bool sculpt_automasking_is_constrained_by_radius(Brush *br) +{ + /* 2D falloff is not constrained by radius */ + if (br->falloff_shape & BRUSH_AIRBRUSH) { + return false; + } + + if (ELEM(br->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB)) { + return true; + } + return false; +} + +typedef struct AutomaskFloodFillData { + float *automask_factor; + float radius; + bool use_radius; + float location[3]; + char symm; +} AutomaskFloodFillData; + +static bool automask_floodfill_cb(SculptSession *ss, + const SculptFloodFillIterator *UNUSED(from), + SculptFloodFillIterator *to, + void *userdata) +{ + AutomaskFloodFillData *data = userdata; + + data->automask_factor[to->v] = 1.0f; + return (!data->use_radius || + sculpt_is_vertex_inside_brush_radius_symm( + sculpt_vertex_co_get(ss, to->v), data->location, data->radius, data->symm)); +} + +static float *sculpt_topology_automasking_init(Sculpt *sd, Object *ob, float *automask_factor) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + if (!sculpt_automasking_enabled(ss, brush)) { + return NULL; + } + + if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) { + BLI_assert(!"Topology masking: pmap missing"); + return NULL; + } + + /* Flood fill automask to connected vertices. Limited to vertices inside + * the brush radius if the tool requires it */ + SculptFloodFill flood; + sculpt_floodfill_init(ss, &flood); + sculpt_floodfill_add_active(sd, ob, ss, &flood, ss->cache->radius); + + AutomaskFloodFillData fdata = { + .automask_factor = automask_factor, + .radius = ss->cache->radius, + .use_radius = sculpt_automasking_is_constrained_by_radius(brush), + .symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL, + }; + copy_v3_v3(fdata.location, sculpt_active_vertex_co_get(ss)); + sculpt_floodfill_execute(ss, &flood, automask_floodfill_cb, &fdata); + sculpt_floodfill_free(&flood); + + return automask_factor; +} + +static void sculpt_automasking_init(Sculpt *sd, Object *ob) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + ss->cache->automask = MEM_callocN(sizeof(float) * sculpt_vertex_count_get(ss), + "automask_factor"); + + if (brush->automasking_flags & BRUSH_AUTOMASKING_TOPOLOGY) { + sculpt_vertex_random_access_init(ss); + sculpt_topology_automasking_init(sd, ob, ss->cache->automask); + } +} + /* ===== Sculpting ===== */ static void flip_v3(float v[3], const char symm) @@ -986,7 +1348,7 @@ static float calc_radial_symmetry_feather(Sculpt *sd, float overlap; overlap = 0; - for (i = 1; i < sd->radial_symm[axis - 'X']; ++i) { + for (i = 1; i < sd->radial_symm[axis - 'X']; i++) { const float angle = 2 * M_PI * i / sd->radial_symm[axis - 'X']; overlap += calc_overlap(cache, symm, axis, angle); } @@ -1033,24 +1395,28 @@ static float calc_symmetry_feather(Sculpt *sd, StrokeCache *cache) * \note These are all _very_ similar, when changing one, check others. * \{ */ +typedef struct AreaNormalCenterTLSData { + float private_co[2][3]; + float private_no[2][3]; + int private_count[2]; +} AreaNormalCenterTLSData; + static void calc_area_normal_and_center_task_cb(void *__restrict userdata, const int n, - const TaskParallelTLS *__restrict UNUSED(tls)) + const TaskParallelTLS *__restrict tls) { SculptThreadedTaskData *data = userdata; SculptSession *ss = data->ob->sculpt; + AreaNormalCenterTLSData *anctd = tls->userdata_chunk; float(*area_nos)[3] = data->area_nos; float(*area_cos)[3] = data->area_cos; PBVHVertexIter vd; SculptUndoNode *unode = NULL; - float private_co[2][3] = {{0.0f}}; - float private_no[2][3] = {{0.0f}}; - int private_count[2] = {0}; bool use_original = false; - if (ss->cache->original) { + if (ss->cache && ss->cache->original) { unode = sculpt_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS); use_original = (unode->co || unode->bm_entry); } @@ -1059,6 +1425,13 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata, SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); + /* Update the test radius to sample the normal using the normal radius of the brush */ + if (data->brush->ob_mode == OB_MODE_SCULPT) { + float test_radius = sqrtf(test.radius_squared); + test_radius *= data->brush->normal_radius_factor; + test.radius_squared = test_radius * test_radius; + } + /* when the mesh is edited we can't rely on original coords * (original mesh may not even have verts in brush radius) */ if (use_original && data->has_bm_orco) { @@ -1087,12 +1460,12 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata, flip_index = (dot_v3v3(ss->cache->view_normal, no) <= 0.0f); if (area_cos) { - add_v3_v3(private_co[flip_index], co); + add_v3_v3(anctd->private_co[flip_index], co); } if (area_nos) { - add_v3_v3(private_no[flip_index], no); + add_v3_v3(anctd->private_no[flip_index], no); } - private_count[flip_index] += 1; + anctd->private_count[flip_index] += 1; } } } @@ -1120,6 +1493,8 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata, const float *no; int flip_index; + data->any_vertex_sampled = true; + if (use_original) { normal_short_to_float_v3(no_buf, no_s); no = no_buf; @@ -1134,38 +1509,42 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata, } } - flip_index = (dot_v3v3(ss->cache->view_normal, no) <= 0.0f); + flip_index = (dot_v3v3(ss->cache ? ss->cache->view_normal : ss->cursor_view_normal, no) <= + 0.0f); if (area_cos) { - add_v3_v3(private_co[flip_index], co); + add_v3_v3(anctd->private_co[flip_index], co); } if (area_nos) { - add_v3_v3(private_no[flip_index], no); + add_v3_v3(anctd->private_no[flip_index], no); } - private_count[flip_index] += 1; + anctd->private_count[flip_index] += 1; } } BKE_pbvh_vertex_iter_end; } +} - BLI_mutex_lock(&data->mutex); - +static void calc_area_normal_and_center_finalize(void *__restrict userdata, void *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + AreaNormalCenterTLSData *anctd = tls; + float(*area_nos)[3] = data->area_nos; + float(*area_cos)[3] = data->area_cos; /* for flatten center */ if (area_cos) { - add_v3_v3(area_cos[0], private_co[0]); - add_v3_v3(area_cos[1], private_co[1]); + add_v3_v3(area_cos[0], anctd->private_co[0]); + add_v3_v3(area_cos[1], anctd->private_co[1]); } /* for area normal */ if (area_nos) { - add_v3_v3(area_nos[0], private_no[0]); - add_v3_v3(area_nos[1], private_no[1]); + add_v3_v3(area_nos[0], anctd->private_no[0]); + add_v3_v3(area_nos[1], anctd->private_no[1]); } /* weights */ - data->count[0] += private_count[0]; - data->count[1] += private_count[1]; - - BLI_mutex_unlock(&data->mutex); + data->count[0] += anctd->private_count[0]; + data->count[1] += anctd->private_count[1]; } static void calc_area_center( @@ -1193,15 +1572,16 @@ static void calc_area_center( .area_nos = NULL, .count = count, }; - BLI_mutex_init(&data.mutex); + + AreaNormalCenterTLSData anctd = {{{0}}}; TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + settings.func_finalize = calc_area_normal_and_center_finalize; + settings.userdata_chunk = &anctd; + settings.userdata_chunk_size = sizeof(AreaNormalCenterTLSData); BLI_task_parallel_range(0, totnode, &data, calc_area_normal_and_center_task_cb, &settings); - BLI_mutex_end(&data.mutex); - /* for flatten center */ for (n = 0; n < ARRAY_SIZE(area_cos); n++) { if (count[n] != 0) { @@ -1218,12 +1598,12 @@ static void calc_area_normal( Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_no[3]) { const Brush *brush = BKE_paint_brush(&sd->paint); - bool use_threading = (sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT; + bool use_threading = (sd->flags & SCULPT_USE_OPENMP); sculpt_pbvh_calc_area_normal(brush, ob, nodes, totnode, use_threading, r_area_no); } /* expose 'calc_area_normal' externally. */ -void sculpt_pbvh_calc_area_normal(const Brush *brush, +bool sculpt_pbvh_calc_area_normal(const Brush *brush, Object *ob, PBVHNode **nodes, int totnode, @@ -1249,22 +1629,26 @@ void sculpt_pbvh_calc_area_normal(const Brush *brush, .area_cos = NULL, .area_nos = area_nos, .count = count, + .any_vertex_sampled = false, }; - BLI_mutex_init(&data.mutex); + + AreaNormalCenterTLSData anctd = {{{0}}}; TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = use_threading; + BKE_pbvh_parallel_range_settings(&settings, use_threading, totnode); + settings.func_finalize = calc_area_normal_and_center_finalize; + settings.userdata_chunk = &anctd; + settings.userdata_chunk_size = sizeof(AreaNormalCenterTLSData); BLI_task_parallel_range(0, totnode, &data, calc_area_normal_and_center_task_cb, &settings); - BLI_mutex_end(&data.mutex); - /* for area normal */ for (int i = 0; i < ARRAY_SIZE(area_nos); i++) { if (normalize_v3_v3(r_area_no, area_nos[i]) != 0.0f) { break; } } + + return data.any_vertex_sampled; } /* this calculates flatten center and area normal together, @@ -1295,15 +1679,16 @@ static void calc_area_normal_and_center( .area_nos = area_nos, .count = count, }; - BLI_mutex_init(&data.mutex); + + AreaNormalCenterTLSData anctd = {{{0}}}; TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + settings.func_finalize = calc_area_normal_and_center_finalize; + settings.userdata_chunk = &anctd; + settings.userdata_chunk_size = sizeof(AreaNormalCenterTLSData); BLI_task_parallel_range(0, totnode, &data, calc_area_normal_and_center_task_cb, &settings); - BLI_mutex_end(&data.mutex); - /* for flatten center */ for (n = 0; n < ARRAY_SIZE(area_cos); n++) { if (count[n] != 0) { @@ -1353,6 +1738,7 @@ static float brush_strength(const Sculpt *sd, case SCULPT_TOOL_CLAY: case SCULPT_TOOL_CLAY_STRIPS: case SCULPT_TOOL_DRAW: + case SCULPT_TOOL_DRAW_SHARP: case SCULPT_TOOL_LAYER: return alpha * flip * pressure * overlap * feather; @@ -1418,6 +1804,10 @@ static float brush_strength(const Sculpt *sd, case SCULPT_TOOL_ROTATE: return alpha * pressure * feather; + case SCULPT_TOOL_ELASTIC_DEFORM: + case SCULPT_TOOL_POSE: + return root_alpha * feather; + default: return 0; } @@ -1431,6 +1821,7 @@ float tex_strength(SculptSession *ss, const short vno[3], const float fno[3], const float mask, + const int vertex_index, const int thread_id) { StrokeCache *cache = ss->cache; @@ -1501,6 +1892,9 @@ float tex_strength(SculptSession *ss, /* Paint mask */ avg *= 1.0f - mask; + /* Automasking */ + avg *= sculpt_automasking_factor_get(ss, vertex_index); + return avg; } @@ -1508,10 +1902,22 @@ float tex_strength(SculptSession *ss, bool sculpt_search_sphere_cb(PBVHNode *node, void *data_v) { SculptSearchSphereData *data = data_v; - float *center = data->ss->cache->location, nearest[3]; + float *center, nearest[3]; + if (data->center) { + center = data->center; + } + else { + center = data->ss->cache ? data->ss->cache->location : data->ss->cursor_location; + } float t[3], bb_min[3], bb_max[3]; int i; + if (data->ignore_fully_masked) { + if (BKE_pbvh_node_fully_masked_get(node)) { + return false; + } + } + if (data->original) { BKE_pbvh_node_get_original_BB(node, bb_min, bb_max); } @@ -1519,7 +1925,7 @@ bool sculpt_search_sphere_cb(PBVHNode *node, void *data_v) BKE_pbvh_node_get_BB(node, bb_min, bb_max); } - for (i = 0; i < 3; ++i) { + for (i = 0; i < 3; i++) { if (bb_min[i] > center[i]) { nearest[i] = bb_min[i]; } @@ -1542,6 +1948,12 @@ bool sculpt_search_circle_cb(PBVHNode *node, void *data_v) SculptSearchCircleData *data = data_v; float bb_min[3], bb_max[3]; + if (data->ignore_fully_masked) { + if (BKE_pbvh_node_fully_masked_get(node)) { + return false; + } + } + if (data->original) { BKE_pbvh_node_get_original_BB(node, bb_min, bb_max); } @@ -1561,7 +1973,7 @@ static void sculpt_clip(Sculpt *sd, SculptSession *ss, float co[3], const float { int i; - for (i = 0; i < 3; ++i) { + for (i = 0; i < 3; i++) { if (sd->flags & (SCULPT_LOCK_X << i)) { continue; } @@ -1575,6 +1987,25 @@ static void sculpt_clip(Sculpt *sd, SculptSession *ss, float co[3], const float } } +static PBVHNode **sculpt_pbvh_gather_cursor_update(Object *ob, + Sculpt *sd, + bool use_original, + int *r_totnode) +{ + SculptSession *ss = ob->sculpt; + PBVHNode **nodes = NULL; + SculptSearchSphereData data = { + .ss = ss, + .sd = sd, + .radius_squared = ss->cursor_radius, + .original = use_original, + .ignore_fully_masked = false, + .center = NULL, + }; + BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, r_totnode); + return nodes; +} + static PBVHNode **sculpt_pbvh_gather_generic(Object *ob, Sculpt *sd, const Brush *brush, @@ -1585,13 +2016,16 @@ static PBVHNode **sculpt_pbvh_gather_generic(Object *ob, SculptSession *ss = ob->sculpt; PBVHNode **nodes = NULL; - /* Build a list of all nodes that are potentially within the brush's area of influence */ + /* Build a list of all nodes that are potentially within the cursor or brush's area of influence + */ if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { SculptSearchSphereData data = { .ss = ss, .sd = sd, .radius_squared = SQUARE(ss->cache->radius * radius_scale), .original = use_original, + .ignore_fully_masked = brush->sculpt_tool != SCULPT_TOOL_MASK, + .center = NULL, }; BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, r_totnode); } @@ -1602,9 +2036,10 @@ static PBVHNode **sculpt_pbvh_gather_generic(Object *ob, SculptSearchCircleData data = { .ss = ss, .sd = sd, - .radius_squared = SQUARE(ss->cache->radius * radius_scale), + .radius_squared = ss->cache ? SQUARE(ss->cache->radius * radius_scale) : ss->cursor_radius, .original = use_original, .dist_ray_to_aabb_precalc = &dist_ray_to_aabb_precalc, + .ignore_fully_masked = brush->sculpt_tool != SCULPT_TOOL_MASK, }; BKE_pbvh_search_gather(ss->pbvh, sculpt_search_circle_cb, &data, &nodes, r_totnode); } @@ -1839,92 +2274,51 @@ static void bmesh_neighbor_average(float avg[3], BMVert *v) copy_v3_v3(avg, v->co); } -/* For bmesh: average only the four most aligned (parallel and perpendicular) edges - * relative to a direction. Naturally converges to a quad-like tessellation. */ +/* For bmesh: Average surrounding verts based on an orthogonality measure. + * Naturally converges to a quad-like structure. */ static void bmesh_four_neighbor_average(float avg[3], float direction[3], BMVert *v) { - /* Logic for 3 or more is identical. */ - const int vfcount = BM_vert_face_count_at_most(v, 3); - - /* Don't modify corner vertices. */ - if (vfcount < 2) { - copy_v3_v3(avg, v->co); - return; - } - - /* Project the direction to the vertex normal and create an additional - * parallel vector. */ - float dir_a[3], dir_b[3]; - cross_v3_v3v3(dir_a, direction, v->no); - cross_v3_v3v3(dir_b, dir_a, v->no); - - /* The four vectors which will be used for smoothing. - * Occasionally less than 4 verts match the requirements in that case - * use 'v' as fallback. */ - BMVert *pos_a = v; - BMVert *neg_a = v; - BMVert *pos_b = v; - BMVert *neg_b = v; - - float pos_score_a = 0.0f; - float neg_score_a = 0.0f; - float pos_score_b = 0.0f; - float neg_score_b = 0.0f; - - BMIter liter; - BMLoop *l; - - BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { - BMVert *adj_v[2] = {l->prev->v, l->next->v}; - for (int i = 0; i < ARRAY_SIZE(adj_v); i++) { - BMVert *v_other = adj_v[i]; + float avg_co[3] = {0, 0, 0}; + float tot_co = 0; - if (vfcount != 2 || BM_vert_face_count_at_most(v_other, 2) <= 2) { - float vec[3]; - sub_v3_v3v3(vec, v_other->co, v->co); - normalize_v3(vec); + BMIter eiter; + BMEdge *e; - /* The score is a measure of how orthogonal the edge is. */ - float score = dot_v3v3(vec, dir_a); - - if (score >= pos_score_a) { - pos_a = v_other; - pos_score_a = score; - } - else if (score < neg_score_a) { - neg_a = v_other; - neg_score_a = score; - } - /* The same scoring but for the perpendicular direction. */ - score = dot_v3v3(vec, dir_b); - - if (score >= pos_score_b) { - pos_b = v_other; - pos_score_b = score; - } - else if (score < neg_score_b) { - neg_b = v_other; - neg_score_b = score; - } - } + BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { + if (BM_edge_is_boundary(e)) { + copy_v3_v3(avg, v->co); + return; } + BMVert *v_other = (e->v1 == v) ? e->v2 : e->v1; + float vec[3]; + sub_v3_v3v3(vec, v_other->co, v->co); + madd_v3_v3fl(vec, v->no, -dot_v3v3(vec, v->no)); + normalize_v3(vec); + + /* fac is a measure of how orthogonal or parallel the edge is + * relative to the direction */ + float fac = dot_v3v3(vec, direction); + fac = fac * fac - 0.5f; + fac *= fac; + madd_v3_v3fl(avg_co, v_other->co, fac); + tot_co += fac; } - /* Average everything together. */ - zero_v3(avg); - add_v3_v3(avg, pos_a->co); - add_v3_v3(avg, neg_a->co); - add_v3_v3(avg, pos_b->co); - add_v3_v3(avg, neg_b->co); - mul_v3_fl(avg, 0.25f); + /* In case vert has no Edge s */ + if (tot_co > 0) { + mul_v3_v3fl(avg, avg_co, 1.0f / tot_co); - /* Preserve volume. */ - float vec[3]; - sub_v3_v3(avg, v->co); - mul_v3_v3fl(vec, v->no, dot_v3v3(avg, v->no)); - sub_v3_v3(avg, vec); - add_v3_v3(avg, v->co); + /* Preserve volume. */ + float vec[3]; + sub_v3_v3(avg, v->co); + mul_v3_v3fl(vec, v->no, dot_v3v3(avg, v->no)); + sub_v3_v3(avg, vec); + add_v3_v3(avg, v->co); + } + else { + zero_v3(avg); + } } /* Same logic as neighbor_average_mask(), but for bmesh rather than mesh */ @@ -1956,6 +2350,48 @@ 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) +{ + float avg[3] = {0.0f, 0.0f, 0.0f}; + int total = 0; + + SculptVertexNeighborIter ni; + sculpt_vertex_neighbors_iter_begin(ss, index, ni) + { + 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 / (float)total); + } + else { + copy_v3_v3(result, sculpt_vertex_co_get(ss, index)); + } +} + +static float grids_neighbor_average_mask(SculptSession *ss, int index) +{ + float avg = 0.0f; + int total = 0; + + SculptVertexNeighborIter ni; + sculpt_vertex_neighbors_iter_begin(ss, index, ni) + { + avg += sculpt_vertex_mask_get(ss, ni.index); + total++; + } + sculpt_vertex_neighbors_iter_end(ni); + + if (total > 0) { + return avg / (float)total; + } + else { + return sculpt_vertex_mask_get(ss, index); + } +} + /* Note: uses after-struct allocated mem to store actual cache... */ typedef struct SculptDoBrushSmoothGridDataChunk { size_t tmpgrid_size; @@ -1964,10 +2400,14 @@ typedef struct SculptDoBrushSmoothGridDataChunk { typedef struct { SculptSession *ss; const float *ray_start; + const float *ray_normal; bool hit; float depth; bool original; + int active_vertex_index; + float *face_normal; + struct IsectRayPrecalc isect_precalc; } SculptRaycastData; @@ -2018,6 +2458,7 @@ static void do_smooth_brush_mesh_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, smooth_mask ? 0.0f : (vd.mask ? *vd.mask : 0.0f), + vd.index, tls->thread_id); if (smooth_mask) { float val = neighbor_average_mask(ss, vd.vert_indices[vd.i]) - *vd.mask; @@ -2073,6 +2514,7 @@ static void do_smooth_brush_bmesh_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, smooth_mask ? 0.0f : *vd.mask, + vd.index, tls->thread_id); if (smooth_mask) { float val = bmesh_neighbor_average_mask(vd.bm_vert, vd.cd_vert_mask_offset) - *vd.mask; @@ -2115,6 +2557,7 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, mul_v3_v3fl( tmp, ss->cache->sculpt_normal_symm, dot_v3v3(ss->cache->sculpt_normal_symm, direction)); sub_v3_v3(direction, tmp); + normalize_v3(direction); /* Cancel if there's no grab data. */ if (is_zero_v3(direction)) { @@ -2132,11 +2575,17 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, 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 * - tex_strength( - ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, *vd.mask, tls->thread_id) * - ss->cache->pressure; + const float fade = bstrength * + tex_strength(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + *vd.mask, + vd.index, + tls->thread_id) * + ss->cache->pressure; float avg[3], val[3]; @@ -2169,7 +2618,6 @@ static void do_smooth_brush_multires_task_cb_ex(void *__restrict userdata, float bstrength = data->strength; CCGElem **griddata, *gddata; - CCGKey key; float(*tmpgrid_co)[3] = NULL; float tmprow_co[2][3]; @@ -2188,7 +2636,7 @@ static void do_smooth_brush_multires_task_cb_ex(void *__restrict userdata, BKE_pbvh_node_get_grids( ss->pbvh, data->nodes[n], &grid_indices, &totgrid, NULL, &gridsize, &griddata); - BKE_pbvh_get_grid_key(ss->pbvh, &key); + CCGKey key = *BKE_pbvh_get_grid_key(ss->pbvh); grid_hidden = BKE_pbvh_grid_hidden(ss->pbvh); @@ -2280,7 +2728,7 @@ static void do_smooth_brush_multires_task_cb_ex(void *__restrict userdata, const float fade = bstrength * tex_strength( - ss, brush, co, sqrtf(test.dist), NULL, fno, strength_mask, tls->thread_id); + ss, brush, co, sqrtf(test.dist), NULL, fno, strength_mask, 0, tls->thread_id); float f = 1.0f / 16.0f; if (x == 0 || x == gridsize - 1) { @@ -2336,7 +2784,7 @@ static void smooth(Sculpt *sd, return; } - for (iteration = 0; iteration <= count; ++iteration) { + for (iteration = 0; iteration <= count; iteration++) { const float strength = (iteration != count) ? 1.0f : last; SculptThreadedTaskData data = { @@ -2349,8 +2797,7 @@ static void smooth(Sculpt *sd, }; TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); switch (type) { case PBVH_GRIDS: { @@ -2399,7 +2846,7 @@ static void bmesh_topology_rake( const int count = iterations * bstrength + 1; const float factor = iterations * bstrength / count; - for (iteration = 0; iteration <= count; ++iteration) { + for (iteration = 0; iteration <= count; iteration++) { SculptThreadedTaskData data = { .sd = sd, @@ -2409,8 +2856,7 @@ static void bmesh_topology_rake( .strength = factor, }; TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); BLI_task_parallel_range(0, totnode, &data, do_topology_rake_bmesh_task_cb_ex, &settings); } @@ -2441,7 +2887,7 @@ static void do_mask_brush_draw_task_cb_ex(void *__restrict userdata, { if (sculpt_brush_test_sq_fn(&test, vd.co)) { const float fade = tex_strength( - ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, tls->thread_id); + ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, vd.index, tls->thread_id); (*vd.mask) += fade * bstrength; CLAMP(*vd.mask, 0, 1); @@ -2467,8 +2913,7 @@ static void do_mask_brush_draw(Sculpt *sd, Object *ob, PBVHNode **nodes, int tot }; TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); BLI_task_parallel_range(0, totnode, &data, do_mask_brush_draw_task_cb_ex, &settings); } @@ -2516,6 +2961,7 @@ static void do_draw_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, + vd.index, tls->thread_id); mul_v3_v3fl(proxy[vd.i], offset, fade); @@ -2554,11 +3000,86 @@ static void do_draw_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) }; TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); BLI_task_parallel_range(0, totnode, &data, do_draw_brush_task_cb_ex, &settings); } +static void do_draw_sharp_brush_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 *offset = data->offset; + + PBVHVertexIter vd; + SculptOrigVertData orig_data; + float(*proxy)[3]; + + sculpt_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + + 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) + { + sculpt_orig_vert_data_update(&orig_data, &vd); + if (sculpt_brush_test_sq_fn(&test, orig_data.co)) { + /* offset vertex */ + const float fade = tex_strength(ss, + brush, + orig_data.co, + sqrtf(test.dist), + orig_data.no, + NULL, + vd.mask ? *vd.mask : 0.0f, + vd.index, + tls->thread_id); + + mul_v3_v3fl(proxy[vd.i], offset, fade); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + } + BKE_pbvh_vertex_iter_end; +} + +static void do_draw_sharp_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + float offset[3]; + const float bstrength = ss->cache->bstrength; + + /* offset with as much as possible factored in already */ + mul_v3_v3fl(offset, ss->cache->sculpt_normal_symm, ss->cache->radius); + mul_v3_v3(offset, ss->cache->scale); + mul_v3_fl(offset, bstrength); + + /* XXX - this shouldn't be necessary, but sculpting crashes in blender2.8 otherwise + * initialize before threads so they can do curve mapping */ + BKE_curvemapping_initialize(brush->curve); + + /* threaded loop over nodes */ + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .offset = offset, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BLI_task_parallel_range(0, totnode, &data, do_draw_sharp_brush_task_cb_ex, &settings); +} + /** * Used for 'SCULPT_TOOL_CREASE' and 'SCULPT_TOOL_BLOB' */ @@ -2593,6 +3114,7 @@ static void do_crease_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, + vd.index, tls->thread_id); float val1[3]; float val2[3]; @@ -2670,8 +3192,7 @@ static void do_crease_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod }; TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); BLI_task_parallel_range(0, totnode, &data, do_crease_brush_task_cb_ex, &settings); } @@ -2703,6 +3224,7 @@ static void do_pinch_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, + vd.index, tls->thread_id); float val[3]; @@ -2732,8 +3254,7 @@ static void do_pinch_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode }; TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); BLI_task_parallel_range(0, totnode, &data, do_pinch_brush_task_cb_ex, &settings); } @@ -2771,6 +3292,7 @@ static void do_grab_brush_task_cb_ex(void *__restrict userdata, orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f, + vd.index, tls->thread_id); mul_v3_v3fl(proxy[vd.i], grab_delta, fade); @@ -2804,11 +3326,613 @@ static void do_grab_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) }; TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); BLI_task_parallel_range(0, totnode, &data, do_grab_brush_task_cb_ex, &settings); } +/* Regularized Kelvinlets: Sculpting Brushes based on Fundamental Solutions of Elasticity + * Pixar Technical Memo #17-03 */ + +typedef struct KelvinletParams { + float f; + float a; + float b; + float c; + float radius_scaled; +} KelvinletParams; + +static int sculpt_kelvinlet_get_scale_iteration_count(eBrushElasticDeformType type) +{ + if (type == BRUSH_ELASTIC_DEFORM_GRAB) { + return 1; + } + if (type == BRUSH_ELASTIC_DEFORM_GRAB_BISCALE) { + return 2; + } + if (type == BRUSH_ELASTIC_DEFORM_GRAB_TRISCALE) { + return 3; + } + return 0; +} + +static void sculpt_kelvinet_integrate(void (*kelvinlet)(float disp[3], + const float vertex_co[3], + const float location[3], + float normal[3], + KelvinletParams *p), + float r_disp[3], + const float vertex_co[3], + const float location[3], + float normal[3], + KelvinletParams *p) +{ + float k[4][3], k_it[4][3]; + kelvinlet(k[0], vertex_co, location, normal, p); + copy_v3_v3(k_it[0], k[0]); + mul_v3_fl(k_it[0], 0.5f); + add_v3_v3v3(k_it[0], vertex_co, k_it[0]); + kelvinlet(k[1], k_it[0], location, normal, p); + copy_v3_v3(k_it[1], k[1]); + mul_v3_fl(k_it[1], 0.5f); + add_v3_v3v3(k_it[1], vertex_co, k_it[1]); + kelvinlet(k[2], k_it[1], location, normal, p); + copy_v3_v3(k_it[2], k[2]); + add_v3_v3v3(k_it[2], vertex_co, k_it[2]); + sub_v3_v3v3(k_it[2], k_it[2], location); + kelvinlet(k[3], k_it[2], location, normal, p); + copy_v3_v3(r_disp, k[0]); + madd_v3_v3fl(r_disp, k[1], 2); + madd_v3_v3fl(r_disp, k[2], 2); + add_v3_v3(r_disp, k[3]); + mul_v3_fl(r_disp, 1.0f / 6.0f); +} + +/* Regularized Kelvinlets: Formula (16) */ +static void sculpt_kelvinlet_scale(float disp[3], + const float vertex_co[3], + const float location[3], + float UNUSED(normal[3]), + KelvinletParams *p) +{ + float r_v[3]; + sub_v3_v3v3(r_v, vertex_co, location); + float r = len_v3(r_v); + float r_e = sqrtf(r * r + p->radius_scaled * p->radius_scaled); + float u = (2.0f * p->b - p->a) * ((1.0f / (r_e * r_e * r_e))) + + ((3.0f * p->radius_scaled * p->radius_scaled) / (2.0f * r_e * r_e * r_e * r_e * r_e)); + float fade = u * p->c; + mul_v3_v3fl(disp, r_v, fade * p->f); +} + +/* Regularized Kelvinlets: Formula (15) */ +static void sculpt_kelvinlet_twist(float disp[3], + const float vertex_co[3], + const float location[3], + float normal[3], + KelvinletParams *p) +{ + float r_v[3], q_r[3]; + sub_v3_v3v3(r_v, vertex_co, location); + float r = len_v3(r_v); + float r_e = sqrtf(r * r + p->radius_scaled * p->radius_scaled); + float u = -p->a * ((1.0f / (r_e * r_e * r_e))) + + ((3.0f * p->radius_scaled * p->radius_scaled) / (2.0f * r_e * r_e * r_e * r_e * r_e)); + float fade = u * p->c; + cross_v3_v3v3(q_r, normal, r_v); + mul_v3_v3fl(disp, q_r, fade * p->f); +} + +static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float *grab_delta = data->grab_delta; + const float *location = ss->cache->location; + + PBVHVertexIter vd; + SculptOrigVertData orig_data; + float(*proxy)[3]; + + const float bstrength = ss->cache->bstrength; + + sculpt_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + + /* Maybe this can be exposed to the user */ + float radius_e[3] = {1.0f, 2.0f, 2.0f}; + float r_e[3]; + float kvl[3]; + float radius_scaled[3]; + + radius_scaled[0] = ss->cache->radius * radius_e[0]; + radius_scaled[1] = radius_scaled[0] * radius_e[1]; + radius_scaled[2] = radius_scaled[1] * radius_e[2]; + + float shear_modulus = 1.0f; + float poisson_ratio = brush->elastic_deform_volume_preservation; + + float a = 1.0f / (4.0f * (float)M_PI * shear_modulus); + float b = a / (4.0f * (1.0f - poisson_ratio)); + float c = 2 * (3.0f * a - 2.0f * b); + + float dir; + if (ss->cache->mouse[0] > ss->cache->initial_mouse[0]) { + dir = 1.0f; + } + else { + dir = -1.0f; + } + + if (brush->elastic_deform_type == BRUSH_ELASTIC_DEFORM_TWIST) { + int symm = ss->cache->mirror_symmetry_pass; + if (symm == 1 || symm == 2 || symm == 4 || symm == 7) { + dir = -dir; + } + } + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + sculpt_orig_vert_data_update(&orig_data, &vd); + float fade, final_disp[3], weights[3]; + float r = len_v3v3(location, orig_data.co); + KelvinletParams params; + params.a = a; + params.b = b; + params.c = c; + params.radius_scaled = radius_scaled[0]; + + int multi_scale_it = sculpt_kelvinlet_get_scale_iteration_count(brush->elastic_deform_type); + for (int it = 0; it < max_ii(1, multi_scale_it); it++) { + r_e[it] = sqrtf(r * r + radius_scaled[it] * radius_scaled[it]); + } + + /* Regularized Kelvinlets: Formula (6) */ + for (int s_it = 0; s_it < multi_scale_it; s_it++) { + kvl[s_it] = ((a - b) / r_e[s_it]) + ((b * r * r) / (r_e[s_it] * r_e[s_it] * r_e[s_it])) + + ((a * radius_scaled[s_it] * radius_scaled[s_it]) / + (2.0f * r_e[s_it] * r_e[s_it] * r_e[s_it])); + } + + switch (brush->elastic_deform_type) { + /* Regularized Kelvinlets: Multi-scale extrapolation. Formula (11) */ + case BRUSH_ELASTIC_DEFORM_GRAB: + fade = kvl[0] * c; + mul_v3_v3fl(final_disp, grab_delta, fade * bstrength * 20.f); + break; + case BRUSH_ELASTIC_DEFORM_GRAB_BISCALE: { + const float u = kvl[0] - kvl[1]; + fade = u * c / ((1.0f / radius_scaled[0]) - (1.0f / radius_scaled[1])); + mul_v3_v3fl(final_disp, grab_delta, fade * bstrength * 20.0f); + break; + } + case BRUSH_ELASTIC_DEFORM_GRAB_TRISCALE: { + weights[0] = 1.0f; + weights[1] = -( + (radius_scaled[2] * radius_scaled[2] - radius_scaled[0] * radius_scaled[0]) / + (radius_scaled[2] * radius_scaled[2] - radius_scaled[1] * radius_scaled[1])); + weights[2] = ((radius_scaled[1] * radius_scaled[1] - radius_scaled[0] * radius_scaled[0]) / + (radius_scaled[2] * radius_scaled[2] - radius_scaled[1] * radius_scaled[1])); + + const float u = weights[0] * kvl[0] + weights[1] * kvl[1] + weights[2] * kvl[2]; + fade = u * c / + (weights[0] / radius_scaled[0] + weights[1] / radius_scaled[1] + + weights[2] / radius_scaled[2]); + mul_v3_v3fl(final_disp, grab_delta, fade * bstrength * 20.0f); + break; + } + case BRUSH_ELASTIC_DEFORM_SCALE: + params.f = len_v3(grab_delta) * dir * bstrength; + sculpt_kelvinet_integrate(sculpt_kelvinlet_scale, + final_disp, + orig_data.co, + location, + ss->cache->sculpt_normal_symm, + ¶ms); + break; + case BRUSH_ELASTIC_DEFORM_TWIST: + params.f = len_v3(grab_delta) * dir * bstrength; + sculpt_kelvinet_integrate(sculpt_kelvinlet_twist, + final_disp, + orig_data.co, + location, + ss->cache->sculpt_normal_symm, + ¶ms); + break; + } + + if (vd.mask) { + mul_v3_fl(final_disp, 1.0f - *vd.mask); + } + + mul_v3_fl(final_disp, sculpt_automasking_factor_get(ss, vd.index)); + + copy_v3_v3(proxy[vd.i], final_disp); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; +} + +static void do_elastic_deform_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + float grab_delta[3]; + + copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .grab_delta = grab_delta, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BLI_task_parallel_range(0, totnode, &data, do_elastic_deform_brush_task_cb_ex, &settings); +} + +static void do_pose_brush_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + + PBVHVertexIter vd; + float disp[3], val[3]; + float final_pos[3]; + + SculptOrigVertData orig_data; + 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 (check_vertex_pivot_symmetry( + orig_data.co, data->pose_initial_co, ss->cache->mirror_symmetry_pass)) { + copy_v3_v3(val, orig_data.co); + mul_m4_v3(data->transform_trans_inv, val); + mul_m4_v3(data->transform_rot, val); + mul_m4_v3(data->transform_trans, val); + sub_v3_v3v3(disp, val, orig_data.co); + + mul_v3_fl(disp, ss->cache->pose_factor[vd.index]); + float mask = vd.mask ? *vd.mask : 0.0f; + mul_v3_fl(disp, 1.0f - mask); + add_v3_v3v3(final_pos, orig_data.co, disp); + copy_v3_v3(vd.co, final_pos); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + } + BKE_pbvh_vertex_iter_end; +} + +static void do_pose_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + float grab_delta[3], rot_quat[4], initial_v[3], current_v[3], temp[3]; + float pose_origin[3]; + float pose_initial_co[3]; + float transform_rot[4][4], transform_trans[4][4], transform_trans_inv[4][4]; + + if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { + return; + } + + copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); + + copy_v3_v3(pose_origin, ss->cache->pose_origin); + flip_v3(pose_origin, (char)ss->cache->mirror_symmetry_pass); + + copy_v3_v3(pose_initial_co, ss->cache->pose_initial_co); + flip_v3(pose_initial_co, (char)ss->cache->mirror_symmetry_pass); + + sub_v3_v3v3(initial_v, pose_initial_co, pose_origin); + normalize_v3(initial_v); + + add_v3_v3v3(temp, pose_initial_co, grab_delta); + sub_v3_v3v3(current_v, temp, pose_origin); + normalize_v3(current_v); + + rotation_between_vecs_to_quat(rot_quat, initial_v, current_v); + unit_m4(transform_rot); + unit_m4(transform_trans); + quat_to_mat4(transform_rot, rot_quat); + translate_m4(transform_trans, pose_origin[0], pose_origin[1], pose_origin[2]); + invert_m4_m4(transform_trans_inv, transform_trans); + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .grab_delta = grab_delta, + .pose_origin = pose_origin, + .pose_initial_co = pose_initial_co, + .transform_rot = transform_rot, + .transform_trans = transform_trans, + .transform_trans_inv = transform_trans_inv, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BLI_task_parallel_range(0, totnode, &data, do_pose_brush_task_cb_ex, &settings); +} + +typedef struct PoseGrowFactorTLSData { + float pos_avg[3]; + int tot_pos_avg; +} PoseGrowFactorTLSData; + +static void pose_brush_grow_factor_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + PoseGrowFactorTLSData *gftd = tls->userdata_chunk; + SculptSession *ss = data->ob->sculpt; + const char symm = data->sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; + const float *active_co = sculpt_active_vertex_co_get(ss); + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + SculptVertexNeighborIter ni; + float max = 0.0f; + sculpt_vertex_neighbors_iter_begin(ss, vd.index, ni) + { + float vmask_f = data->prev_mask[ni.index]; + if (vmask_f > max) { + max = vmask_f; + } + } + sculpt_vertex_neighbors_iter_end(ni); + if (max != data->prev_mask[vd.index]) { + data->pose_factor[vd.index] = max; + if (check_vertex_pivot_symmetry(vd.co, active_co, symm)) { + add_v3_v3(gftd->pos_avg, vd.co); + gftd->tot_pos_avg++; + } + } + } + + BKE_pbvh_vertex_iter_end; +} + +static void pose_brush_grow_factor_finalize(void *__restrict userdata, void *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + PoseGrowFactorTLSData *gftd = tls; + add_v3_v3(data->tot_pos_avg, gftd->pos_avg); + data->tot_pos_count += gftd->tot_pos_avg; +} + +/* Grow the factor until its boundary is near to the offset pose origin */ +static void sculpt_pose_grow_pose_factor( + Sculpt *sd, Object *ob, SculptSession *ss, float pose_origin[3], float *pose_factor) +{ + PBVHNode **nodes; + PBVH *pbvh = ob->sculpt->pbvh; + int totnode; + + BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .nodes = nodes, + .totnode = totnode, + .pose_factor = pose_factor, + }; + TaskParallelSettings settings; + PoseGrowFactorTLSData gftd; + gftd.tot_pos_avg = 0; + zero_v3(gftd.pos_avg); + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + settings.func_finalize = pose_brush_grow_factor_finalize; + settings.userdata_chunk = &gftd; + settings.userdata_chunk_size = sizeof(PoseGrowFactorTLSData); + + bool grow_next_iteration = true; + float prev_len = FLT_MAX; + data.prev_mask = MEM_mallocN(sculpt_vertex_count_get(ss) * sizeof(float), "prev mask"); + while (grow_next_iteration) { + zero_v3(data.tot_pos_avg); + data.tot_pos_count = 0; + zero_v3(gftd.pos_avg); + gftd.tot_pos_avg = 0; + memcpy(data.prev_mask, pose_factor, sculpt_vertex_count_get(ss) * sizeof(float)); + BLI_task_parallel_range(0, totnode, &data, pose_brush_grow_factor_task_cb_ex, &settings); + if (data.tot_pos_count != 0) { + mul_v3_fl(data.tot_pos_avg, 1.0f / (float)data.tot_pos_count); + float len = len_v3v3(data.tot_pos_avg, pose_origin); + if (len < prev_len) { + prev_len = len; + grow_next_iteration = true; + } + else { + grow_next_iteration = false; + memcpy(pose_factor, data.prev_mask, sculpt_vertex_count_get(ss) * sizeof(float)); + } + } + else { + grow_next_iteration = false; + } + } + MEM_freeN(data.prev_mask); + + MEM_SAFE_FREE(nodes); +} + +static bool sculpt_pose_brush_is_vertex_inside_brush_radius(const float vertex[3], + const float br_co[3], + float radius, + char symm) +{ + for (char i = 0; i <= symm; ++i) { + if (is_symmetry_iteration_valid(i, symm)) { + float location[3]; + flip_v3_v3(location, br_co, (char)i); + if (len_v3v3(location, vertex) < radius) { + return true; + } + } + } + return false; +} + +/* Calculate the pose origin and (Optionaly the pose factor) that is used when using the pose brush + * + * r_pose_origin must be a valid pointer. the r_pose_factor is optional. When set to NULL it won't + * be calculated. */ +typedef struct PoseFloodFillData { + float pose_initial_co[3]; + float radius; + int symm; + + float *pose_factor; + float pose_origin[3]; + int tot_co; +} PoseFloodFillData; + +static bool pose_floodfill_cb(SculptSession *ss, + const SculptFloodFillIterator *UNUSED(from), + SculptFloodFillIterator *to, + void *userdata) +{ + PoseFloodFillData *data = userdata; + + if (data->pose_factor) { + data->pose_factor[to->v] = 1.0f; + } + + const float *co = sculpt_vertex_co_get(ss, to->v); + if (sculpt_pose_brush_is_vertex_inside_brush_radius( + co, data->pose_initial_co, data->radius, data->symm)) { + return true; + } + else if (check_vertex_pivot_symmetry(co, data->pose_initial_co, data->symm)) { + add_v3_v3(data->pose_origin, co); + data->tot_co++; + } + + return false; +} + +void sculpt_pose_calc_pose_data(Sculpt *sd, + Object *ob, + SculptSession *ss, + float initial_location[3], + float radius, + float pose_offset, + float *r_pose_origin, + float *r_pose_factor) +{ + sculpt_vertex_random_access_init(ss); + + /* Calculate the pose rotation point based on the boundaries of the brush factor. */ + SculptFloodFill flood; + sculpt_floodfill_init(ss, &flood); + sculpt_floodfill_add_active(sd, ob, ss, &flood, (r_pose_factor) ? radius : 0.0f); + + PoseFloodFillData fdata = { + .radius = radius, + .symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL, + .pose_factor = r_pose_factor, + .tot_co = 0, + }; + zero_v3(fdata.pose_origin); + copy_v3_v3(fdata.pose_initial_co, initial_location); + sculpt_floodfill_execute(ss, &flood, pose_floodfill_cb, &fdata); + sculpt_floodfill_free(&flood); + + if (fdata.tot_co > 0) { + mul_v3_fl(fdata.pose_origin, 1.0f / (float)fdata.tot_co); + } + + /* Offset the pose origin */ + float pose_d[3]; + sub_v3_v3v3(pose_d, fdata.pose_origin, fdata.pose_initial_co); + normalize_v3(pose_d); + madd_v3_v3fl(fdata.pose_origin, pose_d, radius * pose_offset); + copy_v3_v3(r_pose_origin, fdata.pose_origin); + + if (pose_offset != 0.0f && r_pose_factor) { + sculpt_pose_grow_pose_factor(sd, ob, ss, fdata.pose_origin, r_pose_factor); + } +} + +static void pose_brush_init_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + SculptVertexNeighborIter ni; + float avg = 0; + int total = 0; + sculpt_vertex_neighbors_iter_begin(ss, vd.index, ni) + { + avg += ss->cache->pose_factor[ni.index]; + total++; + } + sculpt_vertex_neighbors_iter_end(ni); + + if (total > 0) { + ss->cache->pose_factor[vd.index] = avg / (float)total; + } + } + BKE_pbvh_vertex_iter_end; +} + +static void sculpt_pose_brush_init( + Sculpt *sd, Object *ob, SculptSession *ss, Brush *br, float initial_location[3], float radius) +{ + float *pose_factor = MEM_callocN(sculpt_vertex_count_get(ss) * sizeof(float), "Pose factor"); + + sculpt_pose_calc_pose_data( + sd, ob, ss, initial_location, radius, br->pose_offset, ss->cache->pose_origin, pose_factor); + + copy_v3_v3(ss->cache->pose_initial_co, initial_location); + ss->cache->pose_factor = pose_factor; + + PBVHNode **nodes; + PBVH *pbvh = ob->sculpt->pbvh; + int totnode; + + BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = br, + .nodes = nodes, + }; + + /* Smooth the pose brush factor for cleaner deformation */ + for (int i = 0; i < 4; i++) { + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BLI_task_parallel_range(0, totnode, &data, pose_brush_init_task_cb_ex, &settings); + } + + MEM_SAFE_FREE(nodes); +} + static void do_nudge_brush_task_cb_ex(void *__restrict userdata, const int n, const TaskParallelTLS *__restrict tls) @@ -2838,6 +3962,7 @@ static void do_nudge_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, + vd.index, tls->thread_id); mul_v3_v3fl(proxy[vd.i], cono, fade); @@ -2871,8 +3996,7 @@ static void do_nudge_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode }; TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); BLI_task_parallel_range(0, totnode, &data, do_nudge_brush_task_cb_ex, &settings); } @@ -2911,6 +4035,7 @@ static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, + vd.index, tls->thread_id); mul_v3_v3fl(proxy[vd.i], grab_delta, fade); @@ -2992,8 +4117,7 @@ static void do_snake_hook_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int to }; TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); BLI_task_parallel_range(0, totnode, &data, do_snake_hook_brush_task_cb_ex, &settings); } @@ -3031,6 +4155,7 @@ static void do_thumb_brush_task_cb_ex(void *__restrict userdata, orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f, + vd.index, tls->thread_id); mul_v3_v3fl(proxy[vd.i], cono, fade); @@ -3064,8 +4189,7 @@ static void do_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode }; TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); BLI_task_parallel_range(0, totnode, &data, do_thumb_brush_task_cb_ex, &settings); } @@ -3104,6 +4228,7 @@ static void do_rotate_brush_task_cb_ex(void *__restrict userdata, orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f, + vd.index, tls->thread_id); sub_v3_v3v3(vec, orig_data.co, ss->cache->location); @@ -3137,8 +4262,7 @@ static void do_rotate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod }; TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); BLI_task_parallel_range(0, totnode, &data, do_rotate_brush_task_cb_ex, &settings); } @@ -3183,6 +4307,7 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, + vd.index, tls->thread_id); float *disp = &layer_disp[vd.i]; float val[3]; @@ -3234,8 +4359,7 @@ static void do_layer_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode BLI_mutex_init(&data.mutex); TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); BLI_task_parallel_range(0, totnode, &data, do_layer_brush_task_cb_ex, &settings); BLI_mutex_end(&data.mutex); @@ -3269,6 +4393,7 @@ static void do_inflate_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, + vd.index, tls->thread_id); float val[3]; @@ -3302,8 +4427,7 @@ static void do_inflate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totno }; TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); BLI_task_parallel_range(0, totnode, &data, do_inflate_brush_task_cb_ex, &settings); } @@ -3315,7 +4439,8 @@ static void calc_sculpt_plane( if (ss->cache->mirror_symmetry_pass == 0 && ss->cache->radial_symmetry_pass == 0 && ss->cache->tile_pass == 0 && - (ss->cache->first_time || !(brush->flag & BRUSH_ORIGINAL_NORMAL))) { + (ss->cache->first_time || !(brush->flag & BRUSH_ORIGINAL_PLANE) || + !(brush->flag & BRUSH_ORIGINAL_NORMAL))) { switch (brush->sculpt_plane) { case SCULPT_DISP_DIR_VIEW: copy_v3_v3(r_area_no, ss->cache->true_view_normal); @@ -3352,10 +4477,20 @@ static void calc_sculpt_plane( } /* for area normal */ - copy_v3_v3(ss->cache->sculpt_normal, r_area_no); + if ((!ss->cache->first_time) && (brush->flag & BRUSH_ORIGINAL_NORMAL)) { + copy_v3_v3(r_area_no, ss->cache->sculpt_normal); + } + else { + copy_v3_v3(ss->cache->sculpt_normal, r_area_no); + } /* for flatten center */ - copy_v3_v3(ss->cache->last_center, r_area_co); + if ((!ss->cache->first_time) && (brush->flag & BRUSH_ORIGINAL_PLANE)) { + copy_v3_v3(r_area_co, ss->cache->last_center); + } + else { + copy_v3_v3(ss->cache->last_center, r_area_co); + } } else { /* for area normal */ @@ -3455,6 +4590,7 @@ static void do_flatten_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, + vd.index, tls->thread_id); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -3500,8 +4636,7 @@ static void do_flatten_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totno }; TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); BLI_task_parallel_range(0, totnode, &data, do_flatten_brush_task_cb_ex, &settings); } @@ -3549,6 +4684,7 @@ static void do_clay_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, + vd.index, tls->thread_id); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -3598,8 +4734,7 @@ static void do_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) }; TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); BLI_task_parallel_range(0, totnode, &data, do_clay_brush_task_cb_ex, &settings); } @@ -3646,6 +4781,7 @@ static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, + vd.index, tls->thread_id); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -3728,8 +4864,7 @@ static void do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t }; TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); BLI_task_parallel_range(0, totnode, &data, do_clay_strips_brush_task_cb_ex, &settings); } @@ -3774,6 +4909,7 @@ static void do_fill_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, + vd.index, tls->thread_id); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -3821,8 +4957,7 @@ static void do_fill_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) }; TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); BLI_task_parallel_range(0, totnode, &data, do_fill_brush_task_cb_ex, &settings); } @@ -3866,6 +5001,7 @@ static void do_scrape_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, + vd.index, tls->thread_id); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -3913,8 +5049,7 @@ static void do_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod }; TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); BLI_task_parallel_range(0, totnode, &data, do_scrape_brush_task_cb_ex, &settings); } @@ -3946,6 +5081,7 @@ static void do_gravity_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, + vd.index, tls->thread_id); mul_v3_v3fl(proxy[vd.i], offset, fade); @@ -3982,8 +5118,7 @@ static void do_gravity(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, fl }; TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); BLI_task_parallel_range(0, totnode, &data, do_gravity_task_cb_ex, &settings); } @@ -4085,7 +5220,7 @@ static void sculpt_topology_update(Sculpt *sd, (brush->falloff_shape != PAINT_FALLOFF_SHAPE_SPHERE)); } - MEM_freeN(nodes); + MEM_SAFE_FREE(nodes); /* update average stroke position */ copy_v3_v3(location, ss->cache->true_location); @@ -4103,20 +5238,42 @@ static void do_brush_action_task_cb(void *__restrict userdata, data->nodes[n], data->brush->sculpt_tool == SCULPT_TOOL_MASK ? SCULPT_UNDO_MASK : SCULPT_UNDO_COORDS); - BKE_pbvh_node_mark_update(data->nodes[n]); + if (data->brush->sculpt_tool == SCULPT_TOOL_MASK) { + BKE_pbvh_node_mark_update_mask(data->nodes[n]); + } + else { + BKE_pbvh_node_mark_update(data->nodes[n]); + } } static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings *ups) { SculptSession *ss = ob->sculpt; int totnode; + PBVHNode **nodes; /* Build a list of all nodes that are potentially within the brush's area of influence */ - const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : - ss->cache->original; - const float radius_scale = 1.0f; - PBVHNode **nodes = sculpt_pbvh_gather_generic( - ob, sd, brush, use_original, radius_scale, &totnode); + + /* These brushes need to update all nodes as they are not constrained by the brush radius */ + if (brush->sculpt_tool == SCULPT_TOOL_ELASTIC_DEFORM) { + BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); + } + else if (brush->sculpt_tool == SCULPT_TOOL_POSE) { + float final_radius = ss->cache->radius * (1 + brush->pose_offset); + SculptSearchSphereData data = { + .ss = ss, + .sd = sd, + .radius_squared = final_radius * final_radius, + .original = true, + }; + BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, &totnode); + } + else { + const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : + ss->cache->original; + const float radius_scale = 1.0f; + nodes = sculpt_pbvh_gather_generic(ob, sd, brush, use_original, radius_scale, &totnode); + } /* Only act if some verts are inside the brush area */ if (totnode) { @@ -4130,8 +5287,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe }; TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); BLI_task_parallel_range(0, totnode, &task_data, do_brush_action_task_cb, &settings); if (sculpt_brush_needs_normal(ss, brush)) { @@ -4142,6 +5298,19 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe update_brush_local_mat(sd, ob); } + if (ss->cache->first_time && ss->cache->mirror_symmetry_pass == 0) { + if (sculpt_automasking_enabled(ss, brush)) { + sculpt_automasking_init(sd, ob); + } + } + + if (brush->sculpt_tool == SCULPT_TOOL_POSE && ss->cache->first_time && + ss->cache->mirror_symmetry_pass == 0) { + if (BKE_pbvh_type(ss->pbvh) != PBVH_GRIDS) { + sculpt_pose_brush_init(sd, ob, ss, brush, ss->cache->location, ss->cache->radius); + } + } + /* Apply one type of brush action */ switch (brush->sculpt_tool) { case SCULPT_TOOL_DRAW: @@ -4198,6 +5367,15 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe case SCULPT_TOOL_MASK: do_mask_brush(sd, ob, nodes, totnode); break; + case SCULPT_TOOL_POSE: + do_pose_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_DRAW_SHARP: + do_draw_sharp_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_ELASTIC_DEFORM: + do_elastic_deform_brush(sd, ob, nodes, totnode); + break; } if (!ELEM(brush->sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_MASK) && @@ -4219,7 +5397,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe do_gravity(sd, ob, nodes, totnode, sd->gravity_factor); } - MEM_freeN(nodes); + MEM_SAFE_FREE(nodes); /* update average stroke position */ copy_v3_v3(location, ss->cache->true_location); @@ -4262,8 +5440,12 @@ static void sculpt_combine_proxies_task_cb(void *__restrict userdata, Object *ob = data->ob; /* these brushes start from original coordinates */ - const bool use_orco = ELEM( - data->brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_THUMB); + const bool use_orco = ELEM(data->brush->sculpt_tool, + SCULPT_TOOL_GRAB, + SCULPT_TOOL_ROTATE, + SCULPT_TOOL_THUMB, + SCULPT_TOOL_ELASTIC_DEFORM, + SCULPT_TOOL_POSE); PBVHVertexIter vd; PBVHProxyNode *proxies; @@ -4327,14 +5509,11 @@ static void sculpt_combine_proxies(Sculpt *sd, Object *ob) }; TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); BLI_task_parallel_range(0, totnode, &data, sculpt_combine_proxies_task_cb, &settings); } - if (nodes) { - MEM_freeN(nodes); - } + MEM_SAFE_FREE(nodes); } /* copy the modified vertices from bvh to the active key */ @@ -4385,12 +5564,12 @@ static void sculpt_flush_stroke_deform_task_cb(void *__restrict userdata, } /* flush displacement from deformed PBVH to original layer */ -static void sculpt_flush_stroke_deform(Sculpt *sd, Object *ob) +static void sculpt_flush_stroke_deform(Sculpt *sd, Object *ob, bool is_proxy_used) { SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); - if (sculpt_tool_is_proxy_used(brush->sculpt_tool)) { + if (is_proxy_used) { /* this brushes aren't using proxies, so sculpt_combine_proxies() wouldn't * propagate needed deformation to original base */ @@ -4420,8 +5599,7 @@ static void sculpt_flush_stroke_deform(Sculpt *sd, Object *ob) }; TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); BLI_task_parallel_range(0, totnode, &data, sculpt_flush_stroke_deform_task_cb, &settings); if (vertCos) { @@ -4429,7 +5607,7 @@ static void sculpt_flush_stroke_deform(Sculpt *sd, Object *ob) MEM_freeN(vertCos); } - MEM_freeN(nodes); + MEM_SAFE_FREE(nodes); /* Modifiers could depend on mesh normals, so we should update them/ * Note, then if sculpting happens on locked key, normals should be re-calculated @@ -4513,7 +5691,7 @@ static void do_tiled( float orgLoc[3]; /* position of the "prototype" stroke for tiling */ copy_v3_v3(orgLoc, cache->location); - for (dim = 0; dim < 3; ++dim) { + for (dim = 0; dim < 3; dim++) { if ((sd->paint.symmetry_flags & (PAINT_TILE_X << dim)) && step[dim] > 0) { start[dim] = (bbMin[dim] - orgLoc[dim] - radius) / step[dim]; end[dim] = (bbMax[dim] - orgLoc[dim] + radius) / step[dim]; @@ -4529,16 +5707,16 @@ static void do_tiled( /* now do it for all the tiles */ copy_v3_v3_int(cur, start); - for (cur[0] = start[0]; cur[0] <= end[0]; ++cur[0]) { - for (cur[1] = start[1]; cur[1] <= end[1]; ++cur[1]) { - for (cur[2] = start[2]; cur[2] <= end[2]; ++cur[2]) { + for (cur[0] = start[0]; cur[0] <= end[0]; cur[0]++) { + for (cur[1] = start[1]; cur[1] <= end[1]; cur[1]++) { + for (cur[2] = start[2]; cur[2] <= end[2]; cur[2]++) { if (!cur[0] && !cur[1] && !cur[2]) { continue; /* skip tile at orgLoc, this was already handled before all others */ } ++cache->tile_pass; - for (dim = 0; dim < 3; ++dim) { + for (dim = 0; dim < 3; dim++) { cache->location[dim] = cur[dim] * step[dim] + orgLoc[dim]; cache->plane_offset[dim] = cur[dim] * step[dim]; } @@ -4560,7 +5738,7 @@ static void do_radial_symmetry(Sculpt *sd, SculptSession *ss = ob->sculpt; int i; - for (i = 1; i < sd->radial_symm[axis - 'X']; ++i) { + for (i = 1; i < sd->radial_symm[axis - 'X']; i++) { const float angle = 2 * M_PI * i / sd->radial_symm[axis - 'X']; ss->cache->radial_symmetry_pass = i; sculpt_cache_calc_brushdata_symm(ss->cache, symm, axis, angle); @@ -4600,7 +5778,7 @@ static void do_symmetrical_brush_actions(Sculpt *sd, /* symm is a bit combination of XYZ - * 1 is mirror X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ - for (i = 0; i <= symm; ++i) { + for (i = 0; i <= symm; i++) { if (i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) { cache->mirror_symmetry_pass = i; cache->radial_symmetry_pass = 0; @@ -4703,6 +5881,12 @@ static const char *sculpt_tool_name(Sculpt *sd) return "Mask Brush"; case SCULPT_TOOL_SIMPLIFY: return "Simplify Brush"; + case SCULPT_TOOL_DRAW_SHARP: + return "Draw Sharp Brush"; + case SCULPT_TOOL_ELASTIC_DEFORM: + return "Elastic Deform Brush"; + case SCULPT_TOOL_POSE: + return "Pose Brush"; } return "Sculpting"; @@ -4717,6 +5901,9 @@ void sculpt_cache_free(StrokeCache *cache) if (cache->dial) { MEM_freeN(cache->dial); } + if (cache->pose_factor) { + MEM_freeN(cache->pose_factor); + } MEM_freeN(cache); } @@ -4732,7 +5919,7 @@ static void sculpt_init_mirror_clipping(Object *ob, SculptSession *ss) if (mmd->flag & MOD_MIR_CLIPPING) { /* check each axis for mirroring */ - for (i = 0; i < 3; ++i) { + for (i = 0; i < 3; i++) { if (mmd->flag & (MOD_MIR_AXIS_X << i)) { /* enable sculpt clipping */ ss->cache->flag |= CLIP_X << i; @@ -4894,7 +6081,7 @@ static void sculpt_update_cache_invariants( memcpy(ss->layer_co, ss->deform_cos, ss->totvert); } else { - for (i = 0; i < ss->totvert; ++i) { + for (i = 0; i < ss->totvert; i++) { copy_v3_v3(ss->layer_co[i], ss->mvert[i].co); } } @@ -4910,12 +6097,21 @@ static void sculpt_update_cache_invariants( /* Make copies of the mesh vertex locations and normals for some tools */ if (brush->flag & BRUSH_ANCHORED) { - cache->original = 1; + cache->original = true; + } + + /* Draw sharp does not need the original coordinates to produce the accumulate effect, so it + * should work the opposite way. */ + if (brush->sculpt_tool == SCULPT_TOOL_DRAW_SHARP) { + cache->original = true; } if (SCULPT_TOOL_HAS_ACCUMULATE(brush->sculpt_tool)) { if (!(brush->flag & BRUSH_ACCUMULATE)) { - cache->original = 1; + cache->original = true; + if (brush->sculpt_tool == SCULPT_TOOL_DRAW_SHARP) { + cache->original = false; + } } } @@ -4941,15 +6137,22 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru if (ELEM(tool, SCULPT_TOOL_GRAB, + SCULPT_TOOL_ELASTIC_DEFORM, SCULPT_TOOL_NUDGE, SCULPT_TOOL_CLAY_STRIPS, SCULPT_TOOL_SNAKE_HOOK, + SCULPT_TOOL_POSE, SCULPT_TOOL_THUMB) || sculpt_brush_use_topology_rake(ss, brush)) { float grab_location[3], imat[4][4], delta[3], loc[3]; if (cache->first_time) { - copy_v3_v3(cache->orig_grab_location, cache->true_location); + if (tool == SCULPT_TOOL_GRAB && brush->flag & BRUSH_GRAB_ACTIVE_VERTEX) { + copy_v3_v3(cache->orig_grab_location, sculpt_active_vertex_co_get(ss)); + } + else { + copy_v3_v3(cache->orig_grab_location, cache->true_location); + } } else if (tool == SCULPT_TOOL_SNAKE_HOOK) { add_v3_v3(cache->true_location, cache->grab_delta); @@ -4963,7 +6166,9 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru if (!cache->first_time) { switch (tool) { case SCULPT_TOOL_GRAB: + case SCULPT_TOOL_POSE: case SCULPT_TOOL_THUMB: + case SCULPT_TOOL_ELASTIC_DEFORM: sub_v3_v3v3(delta, grab_location, cache->old_grab_location); invert_m4_m4(imat, ob->obmat); mul_mat3_m4_v3(imat, delta); @@ -5000,13 +6205,25 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru copy_v3_v3(cache->old_grab_location, grab_location); if (tool == SCULPT_TOOL_GRAB) { + if (brush->flag & BRUSH_GRAB_ACTIVE_VERTEX) { + copy_v3_v3(cache->anchored_location, cache->orig_grab_location); + } + else { + copy_v3_v3(cache->anchored_location, cache->true_location); + } + } + else if (tool == SCULPT_TOOL_ELASTIC_DEFORM) { copy_v3_v3(cache->anchored_location, cache->true_location); } else if (tool == SCULPT_TOOL_THUMB) { copy_v3_v3(cache->anchored_location, cache->orig_grab_location); } - if (ELEM(tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB)) { + if (ELEM(tool, + SCULPT_TOOL_GRAB, + SCULPT_TOOL_THUMB, + SCULPT_TOOL_ELASTIC_DEFORM, + SCULPT_TOOL_POSE)) { /* location stays the same for finding vertices in brush radius */ copy_v3_v3(cache->true_location, cache->orig_grab_location); @@ -5144,20 +6361,25 @@ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, Po /* Returns true if any of the smoothing modes are active (currently * one of smooth brush, autosmooth, mask smooth, or shift-key * smooth) */ -static bool sculpt_any_smooth_mode(const Brush *brush, StrokeCache *cache, int stroke_mode) +static bool sculpt_needs_conectivity_info(const Brush *brush, SculptSession *ss, int stroke_mode) { - return ((stroke_mode == BRUSH_STROKE_SMOOTH) || (cache && cache->alt_smooth) || + if (ss && sculpt_automasking_enabled(ss, brush)) { + return true; + } + return ((stroke_mode == BRUSH_STROKE_SMOOTH) || (ss && ss->cache && ss->cache->alt_smooth) || (brush->sculpt_tool == SCULPT_TOOL_SMOOTH) || (brush->autosmooth_factor > 0) || - ((brush->sculpt_tool == SCULPT_TOOL_MASK) && (brush->mask_tool == BRUSH_MASK_SMOOTH))); + ((brush->sculpt_tool == SCULPT_TOOL_MASK) && (brush->mask_tool == BRUSH_MASK_SMOOTH)) || + (brush->sculpt_tool == SCULPT_TOOL_POSE)); } static void sculpt_stroke_modifiers_check(const bContext *C, Object *ob, const Brush *brush) { SculptSession *ss = ob->sculpt; + View3D *v3d = CTX_wm_view3d(C); - if (ss->kb || ss->modifiers_active) { + bool need_pmap = sculpt_needs_conectivity_info(brush, ss, 0); + if (ss->kb || ss->modifiers_active || (!BKE_sculptsession_use_pbvh_draw(ob, v3d) && need_pmap)) { Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); - bool need_pmap = sculpt_any_smooth_mode(brush, ss->cache, 0); BKE_sculpt_update_object_for_edit(depsgraph, ob, need_pmap, false); } } @@ -5186,8 +6408,11 @@ static void sculpt_raycast_cb(PBVHNode *node, void *data_v, float *tmin) origco, use_origco, srd->ray_start, + srd->ray_normal, &srd->isect_precalc, - &srd->depth)) { + &srd->depth, + &srd->active_vertex_index, + srd->face_normal)) { srd->hit = 1; *tmin = srd->depth; } @@ -5275,26 +6500,147 @@ static float sculpt_raycast_init(ViewContext *vc, return dist; } +/* Gets the normal, location and active vertex location of the geometry under the cursor. This also + * updates + * the active vertex and cursor related data of the SculptSession using the mouse position */ +bool sculpt_cursor_geometry_info_update(bContext *C, + SculptCursorGeometryInfo *out, + const float mouse[2], + bool use_sampled_normal) +{ + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + Scene *scene = CTX_data_scene(C); + Sculpt *sd = scene->toolsettings->sculpt; + Object *ob; + SculptSession *ss; + ViewContext vc; + const Brush *brush = BKE_paint_brush(BKE_paint_get_active_from_context(C)); + float ray_start[3], ray_end[3], ray_normal[3], depth, face_normal[3], sampled_normal[3], + mat[3][3]; + float viewDir[3] = {0.0f, 0.0f, 1.0f}; + int totnode; + bool original = false, hit = false; + + ED_view3d_viewcontext_init(C, &vc, depsgraph); + + ob = vc.obact; + ss = ob->sculpt; + + if (!ss->pbvh) { + zero_v3(out->location); + zero_v3(out->normal); + zero_v3(out->active_vertex_co); + return false; + } + + /* PBVH raycast to get active vertex and face normal */ + depth = sculpt_raycast_init(&vc, mouse, ray_start, ray_end, ray_normal, original); + sculpt_stroke_modifiers_check(C, ob, brush); + + SculptRaycastData srd = { + .original = original, + .ss = ob->sculpt, + .hit = 0, + .ray_start = ray_start, + .ray_normal = ray_normal, + .depth = depth, + .face_normal = face_normal, + }; + isect_ray_tri_watertight_v3_precalc(&srd.isect_precalc, ray_normal); + BKE_pbvh_raycast(ss->pbvh, sculpt_raycast_cb, &srd, ray_start, ray_normal, srd.original); + + /* Cursor is not over the mesh, return default values */ + if (!srd.hit) { + zero_v3(out->location); + zero_v3(out->normal); + zero_v3(out->active_vertex_co); + return false; + } + + /* Update the active vertex of the SculptSession */ + ss->active_vertex_index = srd.active_vertex_index; + + if (!ss->multires) { + copy_v3_v3(out->active_vertex_co, sculpt_active_vertex_co_get(ss)); + } + else { + zero_v3(out->active_vertex_co); + } + + copy_v3_v3(out->location, ray_normal); + mul_v3_fl(out->location, srd.depth); + add_v3_v3(out->location, ray_start); + + /* Option to return the face normal directly for performance o accuracy reasons */ + if (!use_sampled_normal) { + copy_v3_v3(out->normal, srd.face_normal); + return hit; + } + + /* Sampled normal calculation */ + float radius; + + /* Update cursor data in SculptSession */ + invert_m4_m4(ob->imat, ob->obmat); + copy_m3_m4(mat, vc.rv3d->viewinv); + mul_m3_v3(mat, viewDir); + copy_m3_m4(mat, ob->imat); + mul_m3_v3(mat, viewDir); + normalize_v3_v3(ss->cursor_view_normal, viewDir); + copy_v3_v3(ss->cursor_normal, srd.face_normal); + copy_v3_v3(ss->cursor_location, out->location); + ss->rv3d = vc.rv3d; + + if (!BKE_brush_use_locked_size(scene, brush)) { + radius = paint_calc_object_space_radius(&vc, out->location, BKE_brush_size_get(scene, brush)); + } + else { + radius = BKE_brush_unprojected_radius_get(scene, brush); + } + ss->cursor_radius = radius; + + PBVHNode **nodes = sculpt_pbvh_gather_cursor_update(ob, sd, original, &totnode); + + /* In case there are no nodes under the cursor, return the face normal */ + if (!totnode) { + MEM_SAFE_FREE(nodes); + copy_v3_v3(out->normal, srd.face_normal); + return true; + } + + /* Calculate the sampled normal */ + if (sculpt_pbvh_calc_area_normal(brush, ob, nodes, totnode, true, sampled_normal)) { + copy_v3_v3(out->normal, sampled_normal); + } + else { + /* Use face normal when there are no vertices to sample inside the cursor radius */ + copy_v3_v3(out->normal, srd.face_normal); + } + MEM_SAFE_FREE(nodes); + return true; +} + /* Do a raycast in the tree to find the 3d brush location * (This allows us to ignore the GL depth buffer) * Returns 0 if the ray doesn't hit the mesh, non-zero otherwise */ bool sculpt_stroke_get_location(bContext *C, float out[3], const float mouse[2]) { + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Object *ob; SculptSession *ss; StrokeCache *cache; - float ray_start[3], ray_end[3], ray_normal[3], depth; + float ray_start[3], ray_end[3], ray_normal[3], depth, face_normal[3]; bool original; ViewContext vc; - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); ob = vc.obact; ss = ob->sculpt; cache = ss->cache; - original = (cache) ? cache->original : 0; + original = (cache) ? cache->original : false; const Brush *brush = BKE_paint_brush(BKE_paint_get_active_from_context(C)); @@ -5302,14 +6648,21 @@ bool sculpt_stroke_get_location(bContext *C, float out[3], const float mouse[2]) depth = sculpt_raycast_init(&vc, mouse, ray_start, ray_end, ray_normal, original); + if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + BM_mesh_elem_table_ensure(ss->bm, BM_VERT); + BM_mesh_elem_index_ensure(ss->bm, BM_VERT); + } + bool hit = false; { SculptRaycastData srd; srd.ss = ob->sculpt; srd.ray_start = ray_start; + srd.ray_normal = ray_normal; srd.hit = 0; srd.depth = depth; srd.original = original; + srd.face_normal = face_normal; isect_ray_tri_watertight_v3_precalc(&srd.isect_precalc, ray_normal); BKE_pbvh_raycast(ss->pbvh, sculpt_raycast_cb, &srd, ray_start, ray_normal, srd.original); @@ -5343,10 +6696,6 @@ bool sculpt_stroke_get_location(bContext *C, float out[3], const float mouse[2]) } } - if (cache && hit) { - copy_v3_v3(cache->true_location, out); - } - return hit; } @@ -5386,7 +6735,7 @@ static void sculpt_brush_stroke_init(bContext *C, wmOperator *op) view3d_operator_needs_opengl(C); sculpt_brush_init_tex(scene, sd, ss); - is_smooth = sculpt_any_smooth_mode(brush, NULL, mode); + is_smooth = sculpt_needs_conectivity_info(brush, ss, mode); BKE_sculpt_update_object_for_edit(depsgraph, ob, is_smooth, need_mask); } @@ -5397,7 +6746,8 @@ static void sculpt_restore_mesh(Sculpt *sd, Object *ob) /* Restore the mesh before continuing with anchored stroke */ if ((brush->flag & BRUSH_ANCHORED) || - (brush->sculpt_tool == SCULPT_TOOL_GRAB && + ((brush->sculpt_tool == SCULPT_TOOL_GRAB || + brush->sculpt_tool == SCULPT_TOOL_ELASTIC_DEFORM) && BKE_brush_use_size_pressure(ss->cache->vc->scene, brush)) || (brush->flag & BRUSH_DRAG_DOT)) { paint_mesh_restore_co(sd, ob); @@ -5415,7 +6765,7 @@ void sculpt_update_object_bounding_box(Object *ob) } } -static void sculpt_flush_update_step(bContext *C) +static void sculpt_flush_update_step(bContext *C, SculptUpdateType update_flags) { Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Object *ob = CTX_data_active_object(C); @@ -5423,6 +6773,12 @@ static void sculpt_flush_update_step(bContext *C) ARegion *ar = CTX_wm_region(C); MultiresModifierData *mmd = ss->multires; View3D *v3d = CTX_wm_view3d(C); + RegionView3D *rv3d = CTX_wm_region_view3d(C); + + if (rv3d) { + /* Mark for faster 3D viewport redraws. */ + rv3d->rflag |= RV3D_PAINTING; + } if (mmd != NULL) { multires_mark_as_modified(depsgraph, ob, MULTIRES_COORDS_MODIFIED); @@ -5443,11 +6799,13 @@ static void sculpt_flush_update_step(bContext *C) * only the part of the 3D viewport where changes happened. */ rcti r; - BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateBB); - /* Update the object's bounding box too so that the object - * doesn't get incorrectly clipped during drawing in - * draw_mesh_object(). [#33790] */ - sculpt_update_object_bounding_box(ob); + if (update_flags & SCULPT_UPDATE_COORDS) { + BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateBB); + /* Update the object's bounding box too so that the object + * doesn't get incorrectly clipped during drawing in + * draw_mesh_object(). [#33790] */ + sculpt_update_object_bounding_box(ob); + } if (sculpt_get_redraw_rect(ar, CTX_wm_region_view3d(C), ob, &r)) { if (ss->cache) { @@ -5467,16 +6825,21 @@ static void sculpt_flush_update_step(bContext *C) } } -static void sculpt_flush_update_done(const bContext *C, Object *ob) +static void sculpt_flush_update_done(const bContext *C, Object *ob, SculptUpdateType update_flags) { /* After we are done drawing the stroke, check if we need to do a more * expensive depsgraph tag to update geometry. */ wmWindowManager *wm = CTX_wm_manager(C); View3D *current_v3d = CTX_wm_view3d(C); + RegionView3D *rv3d = CTX_wm_region_view3d(C); SculptSession *ss = ob->sculpt; Mesh *mesh = ob->data; bool need_tag = (mesh->id.us > 1); /* Always needed for linked duplicates. */ + if (rv3d) { + rv3d->rflag &= ~RV3D_PAINTING; + } + for (wmWindow *win = wm->windows.first; win; win = win->next) { bScreen *screen = WM_window_get_active_screen(win); for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { @@ -5486,11 +6849,26 @@ static void sculpt_flush_update_done(const bContext *C, Object *ob) if (v3d != current_v3d) { need_tag |= !BKE_sculptsession_use_pbvh_draw(ob, v3d); } + + /* Tag all 3D viewports for redraw now that we are done. Others + * viewports did not get a full redraw, and anti-aliasing for the + * current viewport was deactivated. */ + for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) { + if (ar->regiontype == RGN_TYPE_WINDOW) { + ED_region_tag_redraw(ar); + } + } } } } - BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateOriginalBB); + if (update_flags & SCULPT_UPDATE_COORDS) { + BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateOriginalBB); + } + + if (update_flags & SCULPT_UPDATE_MASK) { + BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateMask); + } if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { BKE_pbvh_bmesh_after_stroke(ss->pbvh); @@ -5577,7 +6955,6 @@ static void sculpt_stroke_update_step(bContext *C, } do_symmetrical_brush_actions(sd, ob, do_brush_action, ups); - sculpt_combine_proxies(sd, ob); /* hack to fix noise texture tearing mesh */ @@ -5594,7 +6971,7 @@ static void sculpt_stroke_update_step(bContext *C, * sculpt_flush_update_step(). */ if (ss->modifiers_active) { - sculpt_flush_stroke_deform(sd, ob); + sculpt_flush_stroke_deform(sd, ob, sculpt_tool_is_proxy_used(brush->sculpt_tool)); } else if (ss->kb) { sculpt_update_keyblock(ob); @@ -5603,7 +6980,12 @@ static void sculpt_stroke_update_step(bContext *C, ss->cache->first_time = false; /* Cleanup */ - sculpt_flush_update_step(C); + if (brush->sculpt_tool == SCULPT_TOOL_MASK) { + sculpt_flush_update_step(C, SCULPT_UPDATE_MASK); + } + else { + sculpt_flush_update_step(C, SCULPT_UPDATE_COORDS); + } } static void sculpt_brush_exit_tex(Sculpt *sd) @@ -5647,12 +7029,21 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str } } + if (sculpt_automasking_enabled(ss, brush)) { + sculpt_automasking_end(ob); + } + sculpt_cache_free(ss->cache); ss->cache = NULL; sculpt_undo_push_end(); - sculpt_flush_update_done(C, ob); + if (brush->sculpt_tool == SCULPT_TOOL_MASK) { + sculpt_flush_update_done(C, ob, SCULPT_UPDATE_MASK); + } + else { + sculpt_flush_update_done(C, ob, SCULPT_UPDATE_COORDS); + } WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); } @@ -5683,12 +7074,12 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent ignore_background_click = RNA_boolean_get(op->ptr, "ignore_background_click"); if (ignore_background_click && !over_mesh(C, op, event->x, event->y)) { - paint_stroke_data_free(op); + paint_stroke_free(C, op); return OPERATOR_PASS_THROUGH; } if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { - paint_stroke_data_free(op); + paint_stroke_free(C, op); return OPERATOR_FINISHED; } /* add modal handler */ @@ -5817,8 +7208,19 @@ void sculpt_pbvh_clear(Object *ob) /* Clear out any existing DM and PBVH */ if (ss->pbvh) { BKE_pbvh_free(ss->pbvh); + ss->pbvh = NULL; + } + + if (ss->pmap) { + MEM_freeN(ss->pmap); + ss->pmap = NULL; } - ss->pbvh = NULL; + + if (ss->pmap_mem) { + MEM_freeN(ss->pmap_mem); + ss->pmap_mem = NULL; + } + BKE_object_free_derived_caches(ob); /* Tag to rebuild PBVH in depsgraph. */ @@ -6388,13 +7790,11 @@ void ED_object_sculptmode_exit_ex(Main *bmain, Depsgraph *depsgraph, Scene *scen const int mode_flag = OB_MODE_SCULPT; Mesh *me = BKE_mesh_from_object(ob); - MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob); - if (mmd) { - multires_force_update(ob); - } + multires_flush_sculpt_updates(ob); /* Not needed for now. */ #if 0 + MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob); const int flush_recalc = ed_object_sculptmode_flush_recalc_flag(scene, ob, mmd); #endif @@ -6539,7 +7939,7 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *UNUSED(op)) } } - MEM_freeN(nodes); + MEM_SAFE_FREE(nodes); sculpt_undo_push_end(); /* force rebuild of pbvh for better BB placement */ @@ -6580,8 +7980,9 @@ static void sample_detail(bContext *C, int mx, int my) CTX_wm_area_set(C, sa); CTX_wm_region_set(C, ar); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewContext vc; - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); /* Pick sample detail. */ Sculpt *sd = CTX_data_tool_settings(C)->sculpt; @@ -6624,7 +8025,7 @@ static int sculpt_sample_detail_size_exec(bContext *C, wmOperator *op) static int sculpt_sample_detail_size_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e)) { ED_workspace_status_text(C, TIP_("Click on the mesh to set the detail")); - WM_cursor_modal_set(CTX_wm_window(C), BC_EYEDROPPER_CURSOR); + WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_EYEDROPPER); WM_event_add_modal_handler(C, op); return OPERATOR_RUNNING_MODAL; } @@ -6740,6 +8141,1630 @@ static void SCULPT_OT_set_detail_size(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +static void filter_cache_init_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) + { + if (!vd.mask || (vd.mask && *vd.mask < 1.0f)) { + data->node_mask[i] = 1; + } + } + BKE_pbvh_vertex_iter_end; + + if (data->node_mask[i] == 1) { + sculpt_undo_push_node(data->ob, node, SCULPT_UNDO_COORDS); + } +} + +static void sculpt_filter_cache_init(Object *ob, Sculpt *sd) +{ + SculptSession *ss = ob->sculpt; + PBVH *pbvh = ob->sculpt->pbvh; + PBVHNode **nodes; + int totnode; + + ss->filter_cache = MEM_callocN(sizeof(FilterCache), "filter cache"); + + ss->filter_cache->random_seed = rand(); + + SculptSearchSphereData search_data = { + .original = true, + }; + BKE_pbvh_search_gather(pbvh, NULL, &search_data, &nodes, &totnode); + + int *node_mask = MEM_callocN((unsigned int)totnode * sizeof(int), "node mask"); + + for (int i = 0; i < totnode; i++) { + BKE_pbvh_node_mark_normals_update(nodes[i]); + } + + /* mesh->runtime.subdiv_ccg is not available. Updating of the normals is done during drawing. + * Filters can't use normals in multires. */ + if (BKE_pbvh_type(ss->pbvh) != PBVH_GRIDS) { + BKE_pbvh_update_normals(ss->pbvh, NULL); + } + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .nodes = nodes, + .node_mask = node_mask, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BLI_task_parallel_range(0, totnode, &data, filter_cache_init_task_cb, &settings); + + int tot_active_nodes = 0; + int active_node_index = 0; + PBVHNode **active_nodes; + + /* Count number of PBVH nodes that are not fully masked */ + for (int i = 0; i < totnode; i++) { + if (node_mask[i] == 1) { + tot_active_nodes++; + } + } + + /* Create the final list of nodes that is going to be processed in the filter */ + active_nodes = MEM_callocN(tot_active_nodes * sizeof(PBVHNode *), "active nodes"); + + for (int i = 0; i < totnode; i++) { + if (node_mask[i] == 1) { + active_nodes[active_node_index] = nodes[i]; + active_node_index++; + } + } + + ss->filter_cache->nodes = active_nodes; + ss->filter_cache->totnode = tot_active_nodes; + + MEM_SAFE_FREE(nodes); + MEM_SAFE_FREE(node_mask); +} + +static void sculpt_filter_cache_free(SculptSession *ss) +{ + if (ss->filter_cache->nodes) { + MEM_freeN(ss->filter_cache->nodes); + } + if (ss->filter_cache->mask_update_it) { + MEM_freeN(ss->filter_cache->mask_update_it); + } + if (ss->filter_cache->prev_mask) { + MEM_freeN(ss->filter_cache->prev_mask); + } + if (ss->filter_cache->normal_factor) { + MEM_freeN(ss->filter_cache->normal_factor); + } + MEM_freeN(ss->filter_cache); + ss->filter_cache = NULL; +} + +typedef enum eSculptMeshFilterTypes { + MESH_FILTER_SMOOTH = 0, + MESH_FILTER_SCALE = 1, + MESH_FILTER_INFLATE = 2, + MESH_FILTER_SPHERE = 3, + MESH_FILTER_RANDOM = 4, +} eSculptMeshFilterTypes; + +static EnumPropertyItem prop_mesh_filter_types[] = { + {MESH_FILTER_SMOOTH, "SMOOTH", 0, "Smooth", "Smooth mesh"}, + {MESH_FILTER_SCALE, "SCALE", 0, "Scale", "Scale mesh"}, + {MESH_FILTER_INFLATE, "INFLATE", 0, "Inflate", "Inflate mesh"}, + {MESH_FILTER_SPHERE, "SPHERE", 0, "Sphere", "Morph into sphere"}, + {MESH_FILTER_RANDOM, "RANDOM", 0, "Random", "Randomize vertex positions"}, + {0, NULL, 0, NULL, NULL}, +}; + +typedef enum eMeshFilterDeformAxis { + MESH_FILTER_DEFORM_X = 1 << 0, + MESH_FILTER_DEFORM_Y = 1 << 1, + MESH_FILTER_DEFORM_Z = 1 << 2, +} eMeshFilterDeformAxis; + +static EnumPropertyItem prop_mesh_filter_deform_axis_items[] = { + {MESH_FILTER_DEFORM_X, "X", 0, "X", "Deform in the X axis"}, + {MESH_FILTER_DEFORM_Y, "Y", 0, "Y", "Deform in the Y axis"}, + {MESH_FILTER_DEFORM_Z, "Z", 0, "Z", "Deform in the Z axis"}, + {0, NULL, 0, NULL, NULL}, +}; + +static void mesh_filter_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]; + + const int filter_type = data->filter_type; + + SculptOrigVertData orig_data; + sculpt_orig_vert_data_init(&orig_data, data->ob, data->nodes[i]); + + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) + { + sculpt_orig_vert_data_update(&orig_data, &vd); + float orig_co[3], val[3], avg[3], normal[3], disp[3], disp2[3], transform[3][3], final_pos[3]; + float fade = vd.mask ? *vd.mask : 0.0f; + fade = 1 - fade; + fade *= data->filter_strength; + copy_v3_v3(orig_co, orig_data.co); + switch (filter_type) { + case MESH_FILTER_SMOOTH: + CLAMP(fade, -1.0f, 1.0f); + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: + neighbor_average(ss, avg, vd.index); + break; + case PBVH_BMESH: + bmesh_neighbor_average(avg, vd.bm_vert); + break; + case PBVH_GRIDS: + grids_neighbor_average(ss, avg, vd.index); + break; + } + sub_v3_v3v3(val, avg, orig_co); + madd_v3_v3v3fl(val, orig_co, val, fade); + sub_v3_v3v3(disp, val, orig_co); + break; + case MESH_FILTER_INFLATE: + normal_short_to_float_v3(normal, orig_data.no); + mul_v3_v3fl(disp, normal, fade); + break; + case MESH_FILTER_SCALE: + unit_m3(transform); + scale_m3_fl(transform, 1 + fade); + copy_v3_v3(val, orig_co); + mul_m3_v3(transform, val); + sub_v3_v3v3(disp, val, orig_co); + break; + case MESH_FILTER_SPHERE: + normalize_v3_v3(disp, orig_co); + if (fade > 0) { + mul_v3_v3fl(disp, disp, fade); + } + else { + mul_v3_v3fl(disp, disp, -fade); + } + + unit_m3(transform); + if (fade > 0) { + scale_m3_fl(transform, 1 - fade); + } + else { + scale_m3_fl(transform, 1 + fade); + } + copy_v3_v3(val, orig_co); + mul_m3_v3(transform, val); + sub_v3_v3v3(disp2, val, orig_co); + + mid_v3_v3v3(disp, disp, disp2); + break; + case MESH_FILTER_RANDOM: { + normal_short_to_float_v3(normal, orig_data.no); + /* Index is not unique for multires, so hash by vertex coordinates. */ + const uint *hash_co = (const uint *)orig_co; + const uint hash = BLI_hash_int_2d(hash_co[0], hash_co[1]) ^ + BLI_hash_int_2d(hash_co[2], ss->filter_cache->random_seed); + mul_v3_fl(normal, hash * (1.0f / (float)0xFFFFFFFF) - 0.5f); + mul_v3_v3fl(disp, normal, fade); + break; + } + } + + for (int it = 0; it < 3; it++) { + if (!ss->filter_cache->enabled_axis[it]) { + disp[it] = 0.0f; + } + } + + 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; + } + } + BKE_pbvh_vertex_iter_end; + + BKE_pbvh_node_mark_redraw(node); + BKE_pbvh_node_mark_normals_update(node); +} + +static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + Object *ob = CTX_data_active_object(C); + 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"); + float filter_strength = RNA_float_get(op->ptr, "strength"); + + if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { + sculpt_filter_cache_free(ss); + sculpt_undo_push_end(); + sculpt_flush_update_done(C, ob, SCULPT_UPDATE_COORDS); + return OPERATOR_FINISHED; + } + + if (event->type != MOUSEMOVE) { + return OPERATOR_RUNNING_MODAL; + } + + float len = event->prevclickx - event->mval[0]; + filter_strength = filter_strength * -len * 0.001f * UI_DPI_FAC; + + sculpt_vertex_random_access_init(ss); + + bool needs_pmap = (filter_type == MESH_FILTER_SMOOTH); + BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false); + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .nodes = ss->filter_cache->nodes, + .filter_type = filter_type, + .filter_strength = filter_strength, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings( + &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode); + BLI_task_parallel_range(0, ss->filter_cache->totnode, &data, mesh_filter_task_cb, &settings); + + if (ss->modifiers_active || ss->kb) { + sculpt_flush_stroke_deform(sd, ob, true); + } + + sculpt_flush_update_step(C, SCULPT_UPDATE_COORDS); + + return OPERATOR_RUNNING_MODAL; +} + +static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + 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; + + if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { + return OPERATOR_CANCELLED; + } + + int deform_axis = RNA_enum_get(op->ptr, "deform_axis"); + if (deform_axis == 0) { + return OPERATOR_CANCELLED; + } + + sculpt_vertex_random_access_init(ss); + + bool needs_pmap = (filter_type == MESH_FILTER_SMOOTH); + BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false); + + if (BKE_pbvh_type(pbvh) == PBVH_FACES && needs_pmap && !ob->sculpt->pmap) { + return OPERATOR_CANCELLED; + } + + sculpt_undo_push_begin("Mesh filter"); + + sculpt_filter_cache_init(ob, sd); + + 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; + + WM_event_add_modal_handler(C, op); + return OPERATOR_RUNNING_MODAL; +} + +static void SCULPT_OT_mesh_filter(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Filter mesh"; + ot->idname = "SCULPT_OT_mesh_filter"; + ot->description = "Applies a filter to modify the current mesh"; + + /* api callbacks */ + ot->invoke = sculpt_mesh_filter_invoke; + ot->modal = sculpt_mesh_filter_modal; + ot->poll = sculpt_mode_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* rna */ + RNA_def_enum(ot->srna, + "type", + prop_mesh_filter_types, + MESH_FILTER_INFLATE, + "Filter type", + "Operation that is going to be applied to the mesh"); + RNA_def_float( + ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter Strength", -10.0f, 10.0f); + RNA_def_enum_flag(ot->srna, + "deform_axis", + prop_mesh_filter_deform_axis_items, + MESH_FILTER_DEFORM_X | MESH_FILTER_DEFORM_Y | MESH_FILTER_DEFORM_Z, + "Deform axis", + "Apply the deformation in the selected axis"); +} + +typedef enum eSculptMaskFilterTypes { + MASK_FILTER_SMOOTH = 0, + MASK_FILTER_SHARPEN = 1, + MASK_FILTER_GROW = 2, + MASK_FILTER_SHRINK = 3, + MASK_FILTER_CONTRAST_INCREASE = 5, + MASK_FILTER_CONTRAST_DECREASE = 6, +} eSculptMaskFilterTypes; + +static EnumPropertyItem prop_mask_filter_types[] = { + {MASK_FILTER_SMOOTH, "SMOOTH", 0, "Smooth Mask", "Smooth mask"}, + {MASK_FILTER_SHARPEN, "SHARPEN", 0, "Sharpen Mask", "Sharpen mask"}, + {MASK_FILTER_GROW, "GROW", 0, "Grow Mask", "Grow mask"}, + {MASK_FILTER_SHRINK, "SHRINK", 0, "Shrink Mask", "Shrink mask"}, + {MASK_FILTER_CONTRAST_INCREASE, + "CONTRAST_INCREASE", + 0, + "Increase contrast", + "Increase the contrast of the paint mask"}, + {MASK_FILTER_CONTRAST_DECREASE, + "CONTRAST_DECREASE", + 0, + "Decrease contrast", + "Decrease the contrast of the paint mask"}, + {0, NULL, 0, NULL, NULL}, +}; + +static void mask_filter_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]; + bool update = false; + + const int mode = data->filter_type; + float contrast = 0.0f; + + PBVHVertexIter vd; + + if (mode == MASK_FILTER_CONTRAST_INCREASE) { + contrast = 0.1f; + } + + if (mode == MASK_FILTER_CONTRAST_DECREASE) { + contrast = -0.1f; + } + + BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) + { + float delta, gain, offset, max, min; + float prev_val = *vd.mask; + SculptVertexNeighborIter ni; + switch (mode) { + case MASK_FILTER_SMOOTH: + case MASK_FILTER_SHARPEN: { + float val = 0.0f; + + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: + val = neighbor_average_mask(ss, vd.index); + break; + case PBVH_BMESH: + val = bmesh_neighbor_average_mask(vd.bm_vert, vd.cd_vert_mask_offset); + break; + case PBVH_GRIDS: + val = grids_neighbor_average_mask(ss, vd.index); + break; + } + + val -= *vd.mask; + + if (mode == MASK_FILTER_SMOOTH) { + *vd.mask += val; + } + else if (mode == MASK_FILTER_SHARPEN) { + if (*vd.mask > 0.5f) { + *vd.mask += 0.05f; + } + else { + *vd.mask -= 0.05f; + } + *vd.mask += val / 2; + } + break; + } + case MASK_FILTER_GROW: + max = 0.0f; + sculpt_vertex_neighbors_iter_begin(ss, vd.index, ni) + { + float vmask_f = data->prev_mask[ni.index]; + if (vmask_f > max) { + max = vmask_f; + } + } + sculpt_vertex_neighbors_iter_end(ni); + *vd.mask = max; + break; + case MASK_FILTER_SHRINK: + min = 1.0f; + sculpt_vertex_neighbors_iter_begin(ss, vd.index, ni) + { + float vmask_f = data->prev_mask[ni.index]; + if (vmask_f < min) { + min = vmask_f; + } + } + sculpt_vertex_neighbors_iter_end(ni); + *vd.mask = min; + break; + case MASK_FILTER_CONTRAST_INCREASE: + case MASK_FILTER_CONTRAST_DECREASE: + delta = contrast / 2.0f; + gain = 1.0f - delta * 2.0f; + if (contrast > 0) { + gain = 1.0f / ((gain != 0.0f) ? gain : FLT_EPSILON); + offset = gain * (-delta); + } + else { + delta *= -1; + offset = gain * (delta); + } + *vd.mask = gain * (*vd.mask) + offset; + break; + } + CLAMP(*vd.mask, 0.0f, 1.0f); + if (*vd.mask != prev_val) { + update = true; + } + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; + + if (update) { + BKE_pbvh_node_mark_update_mask(node); + } +} + +static int sculpt_mask_filter_exec(bContext *C, wmOperator *op) +{ + ARegion *ar = CTX_wm_region(C); + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + PBVH *pbvh = ob->sculpt->pbvh; + PBVHNode **nodes; + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + int totnode; + int filter_type = RNA_enum_get(op->ptr, "filter_type"); + + if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { + return OPERATOR_CANCELLED; + } + + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true); + + sculpt_vertex_random_access_init(ss); + + if (!ob->sculpt->pmap) { + return OPERATOR_CANCELLED; + } + + int num_verts = sculpt_vertex_count_get(ss); + + BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); + sculpt_undo_push_begin("Mask filter"); + + for (int i = 0; i < totnode; i++) { + sculpt_undo_push_node(ob, nodes[i], SCULPT_UNDO_MASK); + } + + float *prev_mask = NULL; + int iterations = RNA_int_get(op->ptr, "iterations"); + + /* Auto iteration count calculates the number of iteration based on the vertices of the mesh to + * avoid adding an unnecessary amount of undo steps when using the operator from a shortcut. + * One iteration per 50000 vertices in the mesh should be fine in most cases. + * Maybe we want this to be configurable. */ + if (RNA_boolean_get(op->ptr, "auto_iteration_count")) { + iterations = (int)(num_verts / 50000.0f) + 1; + } + + for (int i = 0; i < iterations; i++) { + if (ELEM(filter_type, MASK_FILTER_GROW, MASK_FILTER_SHRINK)) { + prev_mask = MEM_mallocN(num_verts * sizeof(float), "prevmask"); + for (int j = 0; j < num_verts; j++) { + prev_mask[j] = sculpt_vertex_mask_get(ss, j); + } + } + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .nodes = nodes, + .filter_type = filter_type, + .prev_mask = prev_mask, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BLI_task_parallel_range(0, totnode, &data, mask_filter_task_cb, &settings); + + if (ELEM(filter_type, MASK_FILTER_GROW, MASK_FILTER_SHRINK)) { + MEM_freeN(prev_mask); + } + } + + MEM_SAFE_FREE(nodes); + + sculpt_undo_push_end(); + + ED_region_tag_redraw(ar); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + return OPERATOR_FINISHED; +} + +static void SCULPT_OT_mask_filter(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Mask Filter"; + ot->idname = "SCULPT_OT_mask_filter"; + ot->description = "Applies a filter to modify the current mask"; + + /* api callbacks */ + ot->exec = sculpt_mask_filter_exec; + ot->poll = sculpt_mode_poll; + + ot->flag = OPTYPE_REGISTER; + + /* rna */ + RNA_def_enum(ot->srna, + "filter_type", + prop_mask_filter_types, + MASK_FILTER_SMOOTH, + "Type", + "Filter that is going to be applied to the mask"); + RNA_def_int(ot->srna, + "iterations", + 1, + 1, + 100, + "Iterations", + "Number of times that the filter is going to be applied", + 1, + 100); + RNA_def_boolean( + ot->srna, + "auto_iteration_count", + false, + "Auto Iteration Count", + "Use a automatic number of iterations based on the number of vertices of the sculpt"); +} + +static float neighbor_dirty_mask(SculptSession *ss, PBVHVertexIter *vd) +{ + int total = 0; + float avg[3]; + zero_v3(avg); + + SculptVertexNeighborIter ni; + sculpt_vertex_neighbors_iter_begin(ss, vd->index, ni) + { + float normalized[3]; + sub_v3_v3v3(normalized, sculpt_vertex_co_get(ss, ni.index), vd->co); + normalize_v3(normalized); + add_v3_v3(avg, normalized); + total++; + } + sculpt_vertex_neighbors_iter_end(ni); + + if (total > 0) { + mul_v3_fl(avg, 1.0f / total); + float normal[3]; + if (vd->no) { + normal_short_to_float_v3(normal, vd->no); + } + else { + copy_v3_v3(normal, vd->fno); + } + float dot = dot_v3v3(avg, normal); + float angle = max_ff(saacosf(dot), 0.0f); + return angle; + } + return 0; +} + +typedef struct DirtyMaskRangeData { + float min, max; +} DirtyMaskRangeData; + +static void dirty_mask_compute_range_task_cb(void *__restrict userdata, + const int i, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + PBVHNode *node = data->nodes[i]; + DirtyMaskRangeData *range = tls->userdata_chunk; + PBVHVertexIter vd; + + BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) + { + float dirty_mask = neighbor_dirty_mask(ss, &vd); + range->min = min_ff(dirty_mask, range->min); + range->max = max_ff(dirty_mask, range->max); + } + BKE_pbvh_vertex_iter_end; +} + +static void dirty_mask_compute_range_finalize(void *__restrict userdata, void *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + DirtyMaskRangeData *range = tls; + + data->dirty_mask_min = min_ff(range->min, data->dirty_mask_min); + data->dirty_mask_max = max_ff(range->max, data->dirty_mask_max); +} + +static void dirty_mask_apply_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; + + const bool dirty_only = data->dirty_mask_dirty_only; + const float min = data->dirty_mask_min; + const float max = data->dirty_mask_max; + + float range = max - min; + if (range < 0.0001f) { + range = 0; + } + else { + range = 1.0f / range; + } + + BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) + { + float dirty_mask = neighbor_dirty_mask(ss, &vd); + float mask = *vd.mask + (1 - ((dirty_mask - min) * range)); + if (dirty_only) { + mask = fminf(mask, 0.5f) * 2.0f; + } + *vd.mask = CLAMPIS(mask, 0.0f, 1.0f); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; + BKE_pbvh_node_mark_update_mask(node); +} + +static int sculpt_dirty_mask_exec(bContext *C, wmOperator *op) +{ + ARegion *ar = CTX_wm_region(C); + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + PBVH *pbvh = ob->sculpt->pbvh; + PBVHNode **nodes; + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + int totnode; + + if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { + return OPERATOR_CANCELLED; + } + + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true); + + sculpt_vertex_random_access_init(ss); + + if (!ob->sculpt->pmap) { + return OPERATOR_CANCELLED; + } + + BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); + sculpt_undo_push_begin("Dirty Mask"); + + for (int i = 0; i < totnode; i++) { + sculpt_undo_push_node(ob, nodes[i], SCULPT_UNDO_MASK); + } + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .nodes = nodes, + .dirty_mask_min = FLT_MAX, + .dirty_mask_max = -FLT_MAX, + .dirty_mask_dirty_only = RNA_boolean_get(op->ptr, "dirty_only"), + }; + DirtyMaskRangeData range = { + .min = FLT_MAX, + .max = -FLT_MAX, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + + settings.func_finalize = dirty_mask_compute_range_finalize; + settings.userdata_chunk = ⦥ + settings.userdata_chunk_size = sizeof(DirtyMaskRangeData); + + BLI_task_parallel_range(0, totnode, &data, dirty_mask_compute_range_task_cb, &settings); + BLI_task_parallel_range(0, totnode, &data, dirty_mask_apply_task_cb, &settings); + + MEM_SAFE_FREE(nodes); + + BKE_pbvh_update_vertex_data(pbvh, SCULPT_UPDATE_MASK); + + sculpt_undo_push_end(); + + ED_region_tag_redraw(ar); + + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + + return OPERATOR_FINISHED; +} + +static void SCULPT_OT_dirty_mask(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Dirty Mask"; + ot->idname = "SCULPT_OT_dirty_mask"; + ot->description = "Generates a mask based on the geometry cavity and pointiness"; + + /* api callbacks */ + ot->exec = sculpt_dirty_mask_exec; + ot->poll = sculpt_mode_poll; + + ot->flag = OPTYPE_REGISTER; + + /* rna */ + RNA_def_boolean( + ot->srna, "dirty_only", false, "Dirty Only", "Don't calculate cleans for convex areas"); +} + +static void sculpt_mask_expand_cancel(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + + MEM_freeN(op->customdata); + + for (int n = 0; n < ss->filter_cache->totnode; n++) { + PBVHNode *node = ss->filter_cache->nodes[n]; + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) + { + *vd.mask = ss->filter_cache->prev_mask[vd.index]; + } + BKE_pbvh_vertex_iter_end; + + BKE_pbvh_node_mark_redraw(node); + } + + sculpt_flush_update_step(C, SCULPT_UPDATE_MASK); + sculpt_filter_cache_free(ss); + sculpt_undo_push_end(); + sculpt_flush_update_done(C, ob, SCULPT_UPDATE_MASK); + ED_workspace_status_text(C, NULL); +} + +static void sculpt_expand_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; + int update_it = data->mask_expand_update_it; + + BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_ALL) + { + int vi = vd.index; + float final_mask = *vd.mask; + if (data->mask_expand_use_normals) { + if (ss->filter_cache->normal_factor[sculpt_active_vertex_get(ss)] < + ss->filter_cache->normal_factor[vd.index]) { + final_mask = 1.0f; + } + else { + final_mask = 0.0f; + } + } + else { + if (ss->filter_cache->mask_update_it[vi] <= update_it && + ss->filter_cache->mask_update_it[vi] != 0) { + final_mask = 1.0f; + } + else { + final_mask = 0.0f; + } + } + + if (data->mask_expand_keep_prev_mask) { + final_mask = MAX2(ss->filter_cache->prev_mask[vd.index], final_mask); + } + + if (data->mask_expand_invert_mask) { + final_mask = 1.0f - final_mask; + } + + if (*vd.mask != final_mask) { + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + *vd.mask = final_mask; + BKE_pbvh_node_mark_update_mask(node); + } + } + BKE_pbvh_vertex_iter_end; +} + +static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + float prevclick_f[2]; + copy_v2_v2(prevclick_f, op->customdata); + int prevclick[2] = {(int)prevclick_f[0], (int)prevclick_f[1]}; + int len = (int)len_v2v2_int(prevclick, event->mval); + len = ABS(len); + int mask_speed = RNA_int_get(op->ptr, "mask_speed"); + int mask_expand_update_it = len / mask_speed; + mask_expand_update_it = mask_expand_update_it + 1; + + if (RNA_boolean_get(op->ptr, "use_cursor")) { + SculptCursorGeometryInfo sgi; + float mouse[2]; + mouse[0] = event->mval[0]; + mouse[1] = event->mval[1]; + sculpt_cursor_geometry_info_update(C, &sgi, mouse, false); + mask_expand_update_it = ss->filter_cache->mask_update_it[(int)sculpt_active_vertex_get(ss)]; + } + + if ((event->type == ESCKEY && event->val == KM_PRESS) || + (event->type == RIGHTMOUSE && event->val == KM_PRESS)) { + /* Returning OPERATOR_CANCELLED will leak memory due to not finishing + * undo. Better solution could be to make paint_mesh_restore_co work + * for this case. */ + sculpt_mask_expand_cancel(C, op); + return OPERATOR_FINISHED; + } + + if ((event->type == LEFTMOUSE && event->val == KM_RELEASE) || + (event->type == RETKEY && event->val == KM_PRESS) || + (event->type == PADENTER && event->val == KM_PRESS)) { + + /* Smooth iterations */ + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .nodes = ss->filter_cache->nodes, + .filter_type = MASK_FILTER_SMOOTH, + }; + + int smooth_iterations = RNA_int_get(op->ptr, "smooth_iterations"); + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false); + for (int i = 0; i < smooth_iterations; i++) { + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings( + &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode); + BLI_task_parallel_range(0, ss->filter_cache->totnode, &data, mask_filter_task_cb, &settings); + } + + /* Pivot position */ + if (RNA_boolean_get(op->ptr, "update_pivot")) { + const char symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; + const float threshold = 0.2f; + float avg[3]; + int total = 0; + zero_v3(avg); + + for (int n = 0; n < ss->filter_cache->totnode; n++) { + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, ss->filter_cache->nodes[n], vd, PBVH_ITER_UNIQUE) + { + const float mask = (vd.mask) ? *vd.mask : 0.0f; + if (mask < (0.5f + threshold) && mask > (0.5f - threshold)) { + if (check_vertex_pivot_symmetry( + vd.co, ss->filter_cache->mask_expand_initial_co, symm)) { + add_v3_v3(avg, vd.co); + total++; + } + } + } + BKE_pbvh_vertex_iter_end; + } + + if (total > 0) { + mul_v3_fl(avg, 1.0f / total); + copy_v3_v3(ss->pivot_pos, avg); + } + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data); + } + + MEM_freeN(op->customdata); + + for (int i = 0; i < ss->filter_cache->totnode; i++) { + BKE_pbvh_node_mark_redraw(ss->filter_cache->nodes[i]); + } + + sculpt_filter_cache_free(ss); + + sculpt_undo_push_end(); + sculpt_flush_update_done(C, ob, SCULPT_UPDATE_MASK); + ED_workspace_status_text(C, NULL); + return OPERATOR_FINISHED; + } + + if (event->type != MOUSEMOVE) { + return OPERATOR_RUNNING_MODAL; + } + + if (mask_expand_update_it == ss->filter_cache->mask_update_current_it) { + return OPERATOR_RUNNING_MODAL; + } + + if (mask_expand_update_it < ss->filter_cache->mask_update_last_it) { + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .nodes = ss->filter_cache->nodes, + .mask_expand_update_it = mask_expand_update_it, + .mask_expand_use_normals = RNA_boolean_get(op->ptr, "use_normals"), + .mask_expand_invert_mask = RNA_boolean_get(op->ptr, "invert"), + .mask_expand_keep_prev_mask = RNA_boolean_get(op->ptr, "keep_previous_mask"), + }; + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings( + &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode); + BLI_task_parallel_range(0, ss->filter_cache->totnode, &data, sculpt_expand_task_cb, &settings); + ss->filter_cache->mask_update_current_it = mask_expand_update_it; + } + + sculpt_flush_update_step(C, SCULPT_UPDATE_MASK); + + return OPERATOR_RUNNING_MODAL; +} + +typedef struct MaskExpandFloodFillData { + float original_normal[3]; + float edge_sensitivity; + bool use_normals; +} MaskExpandFloodFillData; + +static bool mask_expand_floodfill_cb(SculptSession *ss, + const SculptFloodFillIterator *from, + SculptFloodFillIterator *to, + void *userdata) +{ + MaskExpandFloodFillData *data = userdata; + + ss->filter_cache->mask_update_it[to->v] = to->it; + if (to->it > ss->filter_cache->mask_update_last_it) { + ss->filter_cache->mask_update_last_it = to->it; + } + + if (data->use_normals) { + float current_normal[3], prev_normal[3]; + sculpt_vertex_normal_get(ss, to->v, current_normal); + sculpt_vertex_normal_get(ss, from->v, prev_normal); + to->edge_factor = dot_v3v3(current_normal, prev_normal) * from->edge_factor; + ss->filter_cache->normal_factor[to->v] = dot_v3v3(data->original_normal, current_normal) * + powf(from->edge_factor, data->edge_sensitivity); + CLAMP(ss->filter_cache->normal_factor[to->v], 0.0f, 1.0f); + } + + return true; +} + +static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + PBVH *pbvh = ob->sculpt->pbvh; + + bool use_normals = RNA_boolean_get(op->ptr, "use_normals"); + + SculptCursorGeometryInfo sgi; + float mouse[2]; + mouse[0] = event->mval[0]; + mouse[1] = event->mval[1]; + + if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { + return OPERATOR_CANCELLED; + } + + sculpt_vertex_random_access_init(ss); + + op->customdata = MEM_mallocN(2 * sizeof(float), "initial mouse position"); + copy_v2_v2(op->customdata, mouse); + + sculpt_cursor_geometry_info_update(C, &sgi, mouse, false); + + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true); + + int vertex_count = sculpt_vertex_count_get(ss); + + ss->filter_cache = MEM_callocN(sizeof(FilterCache), "filter cache"); + + BKE_pbvh_search_gather(pbvh, NULL, NULL, &ss->filter_cache->nodes, &ss->filter_cache->totnode); + + sculpt_undo_push_begin("Mask Expand"); + + for (int i = 0; i < ss->filter_cache->totnode; i++) { + sculpt_undo_push_node(ob, ss->filter_cache->nodes[i], SCULPT_UNDO_MASK); + BKE_pbvh_node_mark_redraw(ss->filter_cache->nodes[i]); + } + + ss->filter_cache->mask_update_it = MEM_callocN(sizeof(int) * vertex_count, + "mask update iteration"); + if (use_normals) { + ss->filter_cache->normal_factor = MEM_callocN(sizeof(float) * vertex_count, + "mask update normal factor"); + } + + ss->filter_cache->prev_mask = MEM_callocN(sizeof(float) * vertex_count, "prev mask"); + for (int i = 0; i < vertex_count; i++) { + ss->filter_cache->prev_mask[i] = sculpt_vertex_mask_get(ss, i); + } + + ss->filter_cache->mask_update_last_it = 1; + ss->filter_cache->mask_update_current_it = 1; + ss->filter_cache->mask_update_it[sculpt_active_vertex_get(ss)] = 1; + + copy_v3_v3(ss->filter_cache->mask_expand_initial_co, sculpt_active_vertex_co_get(ss)); + + SculptFloodFill flood; + sculpt_floodfill_init(ss, &flood); + sculpt_floodfill_add_active(sd, ob, ss, &flood, FLT_MAX); + + MaskExpandFloodFillData fdata = { + .use_normals = use_normals, + .edge_sensitivity = RNA_int_get(op->ptr, "edge_sensitivity"), + }; + sculpt_active_vertex_normal_get(ss, fdata.original_normal); + sculpt_floodfill_execute(ss, &flood, mask_expand_floodfill_cb, &fdata); + sculpt_floodfill_free(&flood); + + if (use_normals) { + for (int repeat = 0; repeat < 2; repeat++) { + for (int i = 0; i < vertex_count; i++) { + float avg = 0; + SculptVertexNeighborIter ni; + sculpt_vertex_neighbors_iter_begin(ss, i, ni) + { + avg += ss->filter_cache->normal_factor[ni.index]; + } + sculpt_vertex_neighbors_iter_end(ni); + ss->filter_cache->normal_factor[i] = avg / ni.size; + } + } + } + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .nodes = ss->filter_cache->nodes, + .mask_expand_update_it = 0, + .mask_expand_use_normals = RNA_boolean_get(op->ptr, "use_normals"), + .mask_expand_invert_mask = RNA_boolean_get(op->ptr, "invert"), + .mask_expand_keep_prev_mask = RNA_boolean_get(op->ptr, "keep_previous_mask"), + }; + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings( + &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode); + BLI_task_parallel_range(0, ss->filter_cache->totnode, &data, sculpt_expand_task_cb, &settings); + + const char *status_str = TIP_( + "Move the mouse to expand the mask from the active vertex. LBM: confirm mask, ESC/RMB: " + "cancel"); + ED_workspace_status_text(C, status_str); + + sculpt_flush_update_step(C, SCULPT_UPDATE_MASK); + WM_event_add_modal_handler(C, op); + return OPERATOR_RUNNING_MODAL; +} + +static void SCULPT_OT_mask_expand(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Mask Expand"; + ot->idname = "SCULPT_OT_mask_expand"; + ot->description = "Expands a mask from the initial active vertex under the cursor"; + + /* api callbacks */ + ot->invoke = sculpt_mask_expand_invoke; + ot->modal = sculpt_mask_expand_modal; + ot->cancel = sculpt_mask_expand_cancel; + ot->poll = sculpt_mode_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->prop = RNA_def_boolean(ot->srna, "invert", true, "Invert", "Invert the new mask"); + ot->prop = RNA_def_boolean( + ot->srna, "use_cursor", true, "Use Cursor", "Expand the mask to the cursor position"); + ot->prop = RNA_def_boolean(ot->srna, + "update_pivot", + true, + "Update Pivot Position", + "Set the pivot position to the mask border after creating the mask"); + ot->prop = RNA_def_int(ot->srna, "smooth_iterations", 2, 0, 10, "Smooth iterations", "", 0, 10); + ot->prop = RNA_def_int(ot->srna, "mask_speed", 5, 1, 10, "Mask speed", "", 1, 10); + + ot->prop = RNA_def_boolean(ot->srna, + "use_normals", + true, + "Use Normals", + "Generate the mask using the normals and curvature of the model"); + ot->prop = RNA_def_boolean(ot->srna, + "keep_previous_mask", + false, + "Keep Previous Mask", + "Generate the new mask on top of the current one"); + ot->prop = RNA_def_int(ot->srna, + "edge_sensitivity", + 300, + 0, + 2000, + "Edge Detection Sensitivity", + "Sensitivity for expanding the mask across sculpted sharp edges when " + "using normals to generate the mask", + 0, + 2000); +} + +void sculpt_geometry_preview_lines_update(bContext *C, SculptSession *ss, float radius) +{ + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + Object *ob = CTX_data_active_object(C); + + ss->preview_vert_index_count = 0; + int totpoints = 0; + + /* This function is called from the cursor drawing code, so the PBVH may not be build yet */ + if (!ss->pbvh) { + return; + } + + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true); + + if (!ss->pmap) { + return; + } + + float brush_co[3]; + copy_v3_v3(brush_co, sculpt_active_vertex_co_get(ss)); + + char *visited_vertices = MEM_callocN(sculpt_vertex_count_get(ss) * sizeof(char), + "visited vertices"); + + /* Assuming an average of 6 edges per vertex in a triangulated mesh */ + const int max_preview_vertices = sculpt_vertex_count_get(ss) * 3 * 2; + + if (ss->preview_vert_index_list == NULL) { + ss->preview_vert_index_list = MEM_callocN(max_preview_vertices * sizeof(int), "preview lines"); + } + + GSQueue *not_visited_vertices = BLI_gsqueue_new(sizeof(int)); + int active_v = sculpt_active_vertex_get(ss); + BLI_gsqueue_push(not_visited_vertices, &active_v); + + while (!BLI_gsqueue_is_empty(not_visited_vertices)) { + int from_v; + BLI_gsqueue_pop(not_visited_vertices, &from_v); + SculptVertexNeighborIter ni; + sculpt_vertex_neighbors_iter_begin(ss, from_v, ni) + { + if (totpoints + (ni.size * 2) < max_preview_vertices) { + int to_v = ni.index; + ss->preview_vert_index_list[totpoints] = from_v; + totpoints++; + ss->preview_vert_index_list[totpoints] = to_v; + totpoints++; + if (visited_vertices[to_v] == 0) { + visited_vertices[to_v] = 1; + const float *co = sculpt_vertex_co_get(ss, to_v); + if (len_squared_v3v3(brush_co, co) < radius * radius) { + BLI_gsqueue_push(not_visited_vertices, &to_v); + } + } + } + } + sculpt_vertex_neighbors_iter_end(ni); + } + + BLI_gsqueue_free(not_visited_vertices); + + MEM_freeN(visited_vertices); + + ss->preview_vert_index_count = totpoints; +} +void ED_sculpt_init_transform(struct bContext *C) +{ + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + + copy_v3_v3(ss->init_pivot_pos, ss->pivot_pos); + copy_v4_v4(ss->init_pivot_rot, ss->pivot_rot); + + sculpt_undo_push_begin("Transform"); + BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false); + + ss->pivot_rot[3] = 1.0f; + + sculpt_vertex_random_access_init(ss); + sculpt_filter_cache_init(ob, sd); +} + +typedef enum PaintSymmetryAreas { + AREA_SYMM_X = (1 << 0), + AREA_SYMM_Y = (1 << 1), + AREA_SYMM_Z = (1 << 2), +} PaintSymmetryAreas; + +static char sculpt_get_vertex_symm_area(float co[3]) +{ + float vco[3]; + char symm_area = 0; + copy_v3_v3(vco, co); + if (vco[0] < 0) { + symm_area |= AREA_SYMM_X; + } + if (vco[1] < 0) { + symm_area |= AREA_SYMM_Y; + } + if (vco[2] < 0) { + symm_area |= AREA_SYMM_Z; + } + return symm_area; +} + +static void flip_qt(float qt[4], char symm) +{ + float euler[3]; + if (symm & PAINT_SYMM_X) { + quat_to_eul(euler, qt); + euler[1] = -euler[1]; + euler[2] = -euler[2]; + eul_to_quat(qt, euler); + } + if (symm & PAINT_SYMM_Y) { + quat_to_eul(euler, qt); + euler[0] = -euler[0]; + euler[2] = -euler[2]; + eul_to_quat(qt, euler); + } + if (symm & PAINT_SYMM_Z) { + quat_to_eul(euler, qt); + euler[0] = -euler[0]; + euler[1] = -euler[1]; + eul_to_quat(qt, euler); + } +} + +static void sculpt_flip_transform_by_symm_area( + float disp[3], float rot[4], char symm, char symmarea, float pivot[3]) +{ + + for (char i = 0; i < 3; i++) { + char symm_it = 1 << i; + if (symm & symm_it) { + if (symmarea & symm_it) { + if (disp) { + flip_v3(disp, symm_it); + } + if (rot) { + flip_qt(rot, symm_it); + } + } + if (pivot[0] < 0) { + if (disp) { + flip_v3(disp, symm_it); + } + if (rot) { + flip_qt(rot, symm_it); + } + } + } + } +} + +static void sculpt_transform_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]; + + SculptOrigVertData orig_data; + sculpt_orig_vert_data_init(&orig_data, data->ob, data->nodes[i]); + + PBVHVertexIter vd; + + sculpt_undo_push_node(data->ob, node, SCULPT_UNDO_COORDS); + BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_ALL) + { + sculpt_orig_vert_data_update(&orig_data, &vd); + float transformed_co[3], orig_co[3], disp[3]; + float fade = vd.mask ? *vd.mask : 0.0f; + copy_v3_v3(orig_co, orig_data.co); + char symm_area = sculpt_get_vertex_symm_area(orig_co); + + copy_v3_v3(transformed_co, orig_co); + mul_m4_v3(data->transform_mats[(int)symm_area], transformed_co); + sub_v3_v3v3(disp, transformed_co, orig_co); + mul_v3_fl(disp, 1.0f - fade); + + add_v3_v3v3(vd.co, orig_co, disp); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; + + BKE_pbvh_node_mark_redraw(node); + BKE_pbvh_node_mark_normals_update(node); +} + +void ED_sculpt_update_modal_transform(struct bContext *C) +{ + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + const char symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; + + sculpt_vertex_random_access_init(ss); + BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false); + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .nodes = ss->filter_cache->nodes, + }; + + float final_pivot_pos[3], d_t[3], d_r[4]; + float t_mat[4][4], r_mat[4][4], s_mat[4][4], pivot_mat[4][4], pivot_imat[4][4], + transform_mat[4][4]; + + copy_v3_v3(final_pivot_pos, ss->pivot_pos); + for (int i = 0; i < 8; i++) { + copy_v3_v3(final_pivot_pos, ss->pivot_pos); + + unit_m4(pivot_mat); + + unit_m4(t_mat); + unit_m4(r_mat); + unit_m4(s_mat); + + /* Translation matrix */ + sub_v3_v3v3(d_t, ss->pivot_pos, ss->init_pivot_pos); + sculpt_flip_transform_by_symm_area(d_t, NULL, symm, (char)i, ss->init_pivot_pos); + translate_m4(t_mat, d_t[0], d_t[1], d_t[2]); + + /* Rotation matrix */ + sub_qt_qtqt(d_r, ss->pivot_rot, ss->init_pivot_rot); + normalize_qt(d_r); + sculpt_flip_transform_by_symm_area(NULL, d_r, symm, (char)i, ss->init_pivot_pos); + quat_to_mat4(r_mat, d_r); + + /* Scale matrix */ + size_to_mat4(s_mat, ss->pivot_scale); + + /* Pivot matrix */ + sculpt_flip_transform_by_symm_area(final_pivot_pos, NULL, symm, (char)i, ss->init_pivot_pos); + translate_m4(pivot_mat, final_pivot_pos[0], final_pivot_pos[1], final_pivot_pos[2]); + invert_m4_m4(pivot_imat, pivot_mat); + + /* Final transform matrix */ + mul_m4_m4m4(transform_mat, r_mat, t_mat); + mul_m4_m4m4(transform_mat, transform_mat, s_mat); + mul_m4_m4m4(data.transform_mats[i], transform_mat, pivot_imat); + mul_m4_m4m4(data.transform_mats[i], pivot_mat, data.transform_mats[i]); + } + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings( + &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode); + BLI_task_parallel_range( + 0, ss->filter_cache->totnode, &data, sculpt_transform_task_cb, &settings); + + if (ss->modifiers_active || ss->kb) { + sculpt_flush_stroke_deform(sd, ob, true); + } + + sculpt_flush_update_step(C, SCULPT_UPDATE_COORDS); +} + +void ED_sculpt_end_transform(struct bContext *C) +{ + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + if (ss->filter_cache) { + sculpt_filter_cache_free(ss); + } + sculpt_undo_push_end(); + sculpt_flush_update_done(C, ob, SCULPT_UPDATE_COORDS); +} + +typedef enum eSculptPivotPositionModes { + SCULPT_PIVOT_POSITION_ORIGIN = 0, + SCULPT_PIVOT_POSITION_UNMASKED = 1, + SCULPT_PIVOT_POSITION_MASK_BORDER = 2, + SCULPT_PIVOT_POSITION_ACTIVE_VERTEX = 3, + SCULPT_PIVOT_POSITION_CURSOR_SURFACE = 4, +} eSculptPivotPositionModes; + +static EnumPropertyItem prop_sculpt_pivot_position_types[] = { + {SCULPT_PIVOT_POSITION_ORIGIN, + "ORIGIN", + 0, + "Origin", + "Sets the pivot to the origin of the sculpt"}, + {SCULPT_PIVOT_POSITION_UNMASKED, + "UNMASKED", + 0, + "Unmasked", + "Sets the pivot position to the average position of the unmasked vertices"}, + {SCULPT_PIVOT_POSITION_MASK_BORDER, + "BORDER", + 0, + "Mask border", + "Sets the pivot position to the center of the border of the mask"}, + {SCULPT_PIVOT_POSITION_ACTIVE_VERTEX, + "ACTIVE", + 0, + "Active vertex", + "Sets the pivot position to the active vertex position"}, + {SCULPT_PIVOT_POSITION_CURSOR_SURFACE, + "SURFACE", + 0, + "Surface", + "Sets the pivot position to the surface under the cursor"}, + {0, NULL, 0, NULL, NULL}, +}; + +static int sculpt_set_pivot_position_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + ARegion *ar = CTX_wm_region(C); + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + const char symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; + + int mode = RNA_enum_get(op->ptr, "mode"); + + BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true); + + /* Pivot to center */ + if (mode == SCULPT_PIVOT_POSITION_ORIGIN) { + zero_v3(ss->pivot_pos); + } + /* Pivot to active vertex */ + else if (mode == SCULPT_PIVOT_POSITION_ACTIVE_VERTEX) { + copy_v3_v3(ss->pivot_pos, sculpt_active_vertex_co_get(ss)); + } + /* Pivot to raycast surface */ + else if (mode == SCULPT_PIVOT_POSITION_CURSOR_SURFACE) { + float stroke_location[3]; + float mouse[2]; + mouse[0] = event->mval[0]; + mouse[1] = event->mval[1]; + if (sculpt_stroke_get_location(C, stroke_location, mouse)) { + copy_v3_v3(ss->pivot_pos, stroke_location); + } + } + else { + PBVHNode **nodes; + int totnode; + BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); + + float avg[3]; + int total = 0; + zero_v3(avg); + + /* Pivot to unmasked */ + if (mode == SCULPT_PIVOT_POSITION_UNMASKED) { + for (int n = 0; n < totnode; n++) { + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) + { + const float mask = (vd.mask) ? *vd.mask : 0.0f; + if (mask < 1.0f) { + if (check_vertex_pivot_symmetry(vd.co, ss->pivot_pos, symm)) { + add_v3_v3(avg, vd.co); + total++; + } + } + } + BKE_pbvh_vertex_iter_end; + } + } + /* Pivot to mask border */ + else if (mode == SCULPT_PIVOT_POSITION_MASK_BORDER) { + const float threshold = 0.2f; + + for (int n = 0; n < totnode; n++) { + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) + { + const float mask = (vd.mask) ? *vd.mask : 0.0f; + if (mask < (0.5f + threshold) && mask > (0.5f - threshold)) { + if (check_vertex_pivot_symmetry(vd.co, ss->pivot_pos, symm)) { + add_v3_v3(avg, vd.co); + total++; + } + } + } + BKE_pbvh_vertex_iter_end; + } + } + + if (total > 0) { + mul_v3_fl(avg, 1.0f / total); + copy_v3_v3(ss->pivot_pos, avg); + } + + MEM_SAFE_FREE(nodes); + } + + ED_region_tag_redraw(ar); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data); + + return OPERATOR_FINISHED; +} + +static void SCULPT_OT_set_pivot_position(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Set Pivot Position"; + ot->idname = "SCULPT_OT_set_pivot_position"; + ot->description = "Sets the sculpt transform pivot position"; + + /* api callbacks */ + ot->invoke = sculpt_set_pivot_position_invoke; + ot->poll = sculpt_mode_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + RNA_def_enum(ot->srna, + "mode", + prop_sculpt_pivot_position_types, + SCULPT_PIVOT_POSITION_UNMASKED, + "Mode", + ""); +} void ED_operatortypes_sculpt(void) { @@ -6752,4 +9777,9 @@ void ED_operatortypes_sculpt(void) WM_operatortype_append(SCULPT_OT_detail_flood_fill); WM_operatortype_append(SCULPT_OT_sample_detail_size); WM_operatortype_append(SCULPT_OT_set_detail_size); + WM_operatortype_append(SCULPT_OT_mesh_filter); + WM_operatortype_append(SCULPT_OT_mask_filter); + WM_operatortype_append(SCULPT_OT_dirty_mask); + WM_operatortype_append(SCULPT_OT_mask_expand); + WM_operatortype_append(SCULPT_OT_set_pivot_position); } diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index bc9a4c1d85b..e9af49a0b5a 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -44,8 +44,38 @@ bool sculpt_mode_poll_view3d(struct bContext *C); bool sculpt_poll(struct bContext *C); bool sculpt_poll_view3d(struct bContext *C); +/* Updates */ + +typedef enum SculptUpdateType { + SCULPT_UPDATE_COORDS = 1 << 0, + SCULPT_UPDATE_MASK = 1 << 1, +} SculptUpdateType; + /* Stroke */ + +typedef struct SculptCursorGeometryInfo { + float location[3]; + float normal[3]; + float active_vertex_co[3]; +} SculptCursorGeometryInfo; + bool sculpt_stroke_get_location(struct bContext *C, float out[3], const float mouse[2]); +bool sculpt_cursor_geometry_info_update(bContext *C, + SculptCursorGeometryInfo *out, + const float mouse[2], + bool use_sampled_normal); +void sculpt_geometry_preview_lines_update(bContext *C, struct SculptSession *ss, float radius); +void sculpt_pose_calc_pose_data(struct Sculpt *sd, + struct Object *ob, + struct SculptSession *ss, + float initial_location[3], + float radius, + float pose_offset, + float *r_pose_origin, + float *r_pose_factor); + +/* Sculpt PBVH abstraction API */ +const float *sculpt_vertex_co_get(struct SculptSession *ss, int index); /* Dynamic topology */ void sculpt_pbvh_clear(Object *ob); @@ -107,6 +137,10 @@ typedef struct SculptUndoNode { int geom_totloop; int geom_totpoly; + /* pivot */ + float pivot_pos[3]; + float pivot_rot[4]; + size_t undo_size; } SculptUndoNode; @@ -158,10 +192,41 @@ typedef struct SculptThreadedTaskData { float (*mat)[4]; float (*vertCos)[3]; + int filter_type; + float filter_strength; + int *node_mask; + /* 0=towards view, 1=flipped */ float (*area_cos)[3]; float (*area_nos)[3]; int *count; + bool any_vertex_sampled; + + float *prev_mask; + + float *pose_origin; + float *pose_initial_co; + float *pose_factor; + float (*transform_rot)[4], (*transform_trans)[4], (*transform_trans_inv)[4]; + + float tot_pos_avg[3]; + int tot_pos_count; + + float max_distance_squared; + float nearest_vertex_search_co[3]; + int nearest_vertex_index; + float nearest_vertex_distance_squared; + + int mask_expand_update_it; + bool mask_expand_invert_mask; + bool mask_expand_use_normals; + bool mask_expand_keep_prev_mask; + + float transform_mats[8][4][4]; + + float dirty_mask_min; + float dirty_mask_max; + bool dirty_mask_dirty_only; ThreadMutex mutex; @@ -190,7 +255,9 @@ typedef struct { struct Sculpt *sd; struct SculptSession *ss; float radius_squared; + float *center; bool original; + bool ignore_fully_masked; } SculptSearchSphereData; typedef struct { @@ -198,6 +265,7 @@ typedef struct { struct SculptSession *ss; float radius_squared; bool original; + bool ignore_fully_masked; struct DistRayAABB_Precalc *dist_ray_to_aabb_precalc; } SculptSearchCircleData; @@ -223,10 +291,11 @@ float tex_strength(struct SculptSession *ss, const short vno[3], const float fno[3], const float mask, + const int vertex_index, const int thread_id); /* just for vertex paint. */ -void sculpt_pbvh_calc_area_normal(const struct Brush *brush, +bool sculpt_pbvh_calc_area_normal(const struct Brush *brush, Object *ob, PBVHNode **nodes, int totnode, @@ -310,6 +379,11 @@ typedef struct StrokeCache { bool original; float anchored_location[3]; + /* Pose brush */ + float *pose_factor; + float pose_initial_co[3]; + float pose_origin[3]; + float vertex_rotation; /* amount to rotate the vertices when using rotate brush */ struct Dial *dial; @@ -324,11 +398,30 @@ typedef struct StrokeCache { float true_gravity_direction[3]; float gravity_direction[3]; + float *automask; + rcti previous_r; /* previous redraw rectangle */ rcti current_r; /* current redraw rectangle */ } StrokeCache; +typedef struct FilterCache { + bool enabled_axis[3]; + int random_seed; + + /* unmasked nodes */ + PBVHNode **nodes; + int totnode; + + /* mask expand iteration caches */ + int mask_update_current_it; + int mask_update_last_it; + int *mask_update_it; + float *normal_factor; + float *prev_mask; + float mask_expand_initial_co[3]; +} FilterCache; + void sculpt_cache_calc_brushdata_symm(StrokeCache *cache, const char symm, const char axis, @@ -346,6 +439,4 @@ void sculpt_update_object_bounding_box(struct Object *ob); bool sculpt_get_redraw_rect(struct ARegion *ar, struct RegionView3D *rv3d, Object *ob, rcti *rect); -#define SCULPT_THREADED_LIMIT 4 - #endif diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index cb8afd5d7fa..788d07f6e78 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -39,8 +39,6 @@ #include "DNA_scene_types.h" #include "DNA_mesh_types.h" #include "DNA_screen_types.h" -#include "DNA_space_types.h" -#include "DNA_workspace_types.h" #include "BKE_ccg.h" #include "BKE_context.h" @@ -49,7 +47,6 @@ #include "BKE_paint.h" #include "BKE_key.h" #include "BKE_mesh.h" -#include "BKE_mesh_runtime.h" #include "BKE_scene.h" #include "BKE_subsurf.h" #include "BKE_subdiv_ccg.h" @@ -62,13 +59,11 @@ #include "WM_api.h" #include "WM_types.h" -#include "ED_paint.h" #include "ED_object.h" #include "ED_sculpt.h" #include "ED_undo.h" #include "bmesh.h" -#include "paint_intern.h" #include "sculpt_intern.h" typedef struct UndoSculpt { @@ -352,8 +347,7 @@ static void sculpt_undo_bmesh_restore_generic(bContext *C, BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); BLI_task_parallel_range( 0, totnode, nodes, sculpt_undo_bmesh_restore_generic_task_cb, &settings); @@ -494,6 +488,9 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase bool partial_update = true; for (unode = lb->first; unode; unode = unode->next) { + /* restore pivot */ + copy_v3_v3(ss->pivot_pos, unode->pivot_pos); + copy_v3_v3(ss->pivot_rot, unode->pivot_rot); if (STREQ(unode->idname, ob->id.name)) { if (unode->type == SCULPT_UNDO_MASK) { /* is possible that we can't do the mask undo (below) @@ -1055,6 +1052,10 @@ SculptUndoNode *sculpt_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType break; } + /* store sculpt pivot */ + copy_v3_v3(unode->pivot_pos, ss->pivot_pos); + copy_v3_v3(unode->pivot_rot, ss->pivot_rot); + /* store active shape key */ if (ss->kb) { BLI_strncpy(unode->shapeName, ss->kb->name, sizeof(ss->kb->name)); diff --git a/source/blender/editors/sculpt_paint/sculpt_uv.c b/source/blender/editors/sculpt_paint/sculpt_uv.c index 91ed9057667..8fbaf3396bd 100644 --- a/source/blender/editors/sculpt_paint/sculpt_uv.c +++ b/source/blender/editors/sculpt_paint/sculpt_uv.c @@ -38,7 +38,6 @@ #include "BKE_context.h" #include "BKE_customdata.h" #include "BKE_editmesh.h" -#include "BKE_main.h" #include "BKE_mesh_mapping.h" #include "BKE_paint.h" @@ -48,10 +47,6 @@ #include "ED_image.h" #include "ED_mesh.h" -#include "GPU_immediate.h" -#include "GPU_immediate_util.h" -#include "GPU_state.h" - #include "WM_api.h" #include "WM_types.h" @@ -151,7 +146,7 @@ typedef struct Temp_UvData { static void HC_relaxation_iteration_uv(BMEditMesh *em, UvSculptData *sculptdata, - float mouse_coord[2], + const float mouse_coord[2], float alpha, float radius, float aspectRatio) @@ -239,7 +234,7 @@ static void HC_relaxation_iteration_uv(BMEditMesh *em, static void laplacian_relaxation_iteration_uv(BMEditMesh *em, UvSculptData *sculptdata, - float mouse_coord[2], + const float mouse_coord[2], float alpha, float radius, float aspectRatio) |