diff --git a/source/blender/editors/curves/intern/curves_ops.cc b/source/blender/editors/curves/intern/curves_ops.cc index 6d4f86fbc7c..e303fabfcc9 100644 --- a/source/blender/editors/curves/intern/curves_ops.cc +++ b/source/blender/editors/curves/intern/curves_ops.cc @@ -24,6 +24,7 @@ #include "WM_api.h" #include "BKE_attribute_math.hh" +#include "BKE_brush.h" #include "BKE_bvhutils.h" #include "BKE_context.h" #include "BKE_curves.hh" @@ -1447,7 +1448,7 @@ static int select_grow_invoke(bContext *C, wmOperator *op, const wmEvent *event) op_data->per_curve.append(std::move(curve_op_data)); } - op_data->initial_mouse_x = event->mval[0]; + op_data->initial_mouse_x = event->xy[0]; WM_event_add_modal_handler(C, op); return OPERATOR_RUNNING_MODAL; @@ -1456,7 +1457,7 @@ static int select_grow_invoke(bContext *C, wmOperator *op, const wmEvent *event) static int select_grow_modal(bContext *C, wmOperator *op, const wmEvent *event) { GrowOperatorData &op_data = *static_cast(op->customdata); - const int mouse_x = event->mval[0]; + const int mouse_x = event->xy[0]; const int mouse_diff_x = mouse_x - op_data.initial_mouse_x; switch (event->type) { case MOUSEMOVE: { @@ -1558,16 +1559,20 @@ struct MinDistanceEditData { float4x4 curves_to_world_mat; float3 pos_cu; float3 normal_cu; - int initial_mouse_x; + int2 initial_mouse; float initial_minimum_distance; void *draw_handle; + ListBase orig_paintcursors; + void *cursor; }; -static void min_distance_edit_draw(const bContext *UNUSED(C), ARegion *UNUSED(ar), void *arg) +static int calculate_points_per_side(bContext *C, MinDistanceEditData &op_data) { - MinDistanceEditData &op_data = *static_cast(arg); + Scene *scene = CTX_data_scene(C); + ARegion *region = CTX_wm_region(C); const float min_distance = op_data.brush->curves_sculpt_settings->minimum_distance; + float brush_radius = BKE_brush_size_get(scene, op_data.brush); float3 tangent_x_cu = math::cross(op_data.normal_cu, float3{0, 0, 1}); if (math::is_zero(tangent_x_cu)) { @@ -1576,36 +1581,139 @@ static void min_distance_edit_draw(const bContext *UNUSED(C), ARegion *UNUSED(ar tangent_x_cu = math::normalize(tangent_x_cu); const float3 tangent_y_cu = math::normalize(math::cross(op_data.normal_cu, tangent_x_cu)); - const int points_per_side = 4; + /* Sample a few points to get a good estimate of how large the grid has to be. */ + Vector points_wo; + points_wo.append(op_data.pos_cu + min_distance * tangent_x_cu); + points_wo.append(op_data.pos_cu + min_distance * tangent_y_cu); + points_wo.append(op_data.pos_cu - min_distance * tangent_x_cu); + points_wo.append(op_data.pos_cu - min_distance * tangent_y_cu); + + Vector points_screen_space; + for (const float3 &pos_wo : points_wo) { + float2 pos_screen_space; + ED_view3d_project_v2(region, pos_wo, pos_screen_space); + points_screen_space.append(pos_screen_space); + } + + float2 origin_screen_space; + ED_view3d_project_v2(region, op_data.pos_cu, origin_screen_space); + + int needed_points = 0; + for (const float2 &pos_screen_space : points_screen_space) { + float distance = math::length(pos_screen_space - origin_screen_space); + int needed_points_iter = (brush_radius * 2.0f) / distance; + + if (needed_points_iter > needed_points) { + needed_points = needed_points_iter; + } + } + + /* Limit to a harcoded number since it only adds noise at some point. */ + return min_ff(300, needed_points); +} + +static void min_distance_edit_draw(bContext *C, int UNUSED(x), int UNUSED(y), void *customdata) +{ + Scene *scene = CTX_data_scene(C); + MinDistanceEditData &op_data = *static_cast(customdata); + + const float min_distance = op_data.brush->curves_sculpt_settings->minimum_distance; + + float3 tangent_x_cu = math::cross(op_data.normal_cu, float3{0, 0, 1}); + if (math::is_zero(tangent_x_cu)) { + tangent_x_cu = math::cross(op_data.normal_cu, float3{0, 1, 0}); + } + tangent_x_cu = math::normalize(tangent_x_cu); + const float3 tangent_y_cu = math::normalize(math::cross(op_data.normal_cu, tangent_x_cu)); + + const int points_per_side = calculate_points_per_side(C, op_data); const int points_per_axis_num = 2 * points_per_side + 1; Vector points_wo; for (const int x_i : IndexRange(points_per_axis_num)) { for (const int y_i : IndexRange(points_per_axis_num)) { - const float x = min_distance * (x_i - (points_per_axis_num - 1) / 2.0f); - const float y = min_distance * (y_i - (points_per_axis_num - 1) / 2.0f); + const float x_iter = min_distance * (x_i - (points_per_axis_num - 1) / 2.0f); + const float y_iter = min_distance * (y_i - (points_per_axis_num - 1) / 2.0f); - const float3 point_pos_cu = op_data.pos_cu + op_data.normal_cu * 0.0001f + x * tangent_x_cu + - y * tangent_y_cu; + const float3 point_pos_cu = op_data.pos_cu + op_data.normal_cu * 0.0001f + + x_iter * tangent_x_cu + y_iter * tangent_y_cu; const float3 point_pos_wo = op_data.curves_to_world_mat * point_pos_cu; points_wo.append(point_pos_wo); } } - const uint pos3d = GPU_vertformat_attr_add( - immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + float4 circle_col = float4(op_data.brush->add_col); + float circle_alpha = op_data.brush->cursor_overlay_alpha; + float brush_radius = BKE_brush_size_get(scene, op_data.brush); + + /* Draw the grid. */ + GPU_matrix_push(); + GPU_matrix_push_projection(); + GPU_blend(GPU_BLEND_ALPHA); + + RegionView3D *rv3d = CTX_wm_region_view3d(C); + ARegion *region = CTX_wm_region(C); + wmWindow *win = CTX_wm_window(C); + + /* It does the same as: `view3d_operator_needs_opengl(C);`. */ + wmViewport(®ion->winrct); + GPU_matrix_projection_set(rv3d->winmat); + GPU_matrix_set(rv3d->viewmat); + + GPUVertFormat *format3d = immVertexFormat(); + + const uint pos3d = GPU_vertformat_attr_add(format3d, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + const uint col3d = GPU_vertformat_attr_add(format3d, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_3D_POINT_FIXED_SIZE_VARYING_COLOR); - immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); GPU_point_size(3); - immUniformColor4f(0.9f, 0.9f, 0.9f, 1.0f); immBegin(GPU_PRIM_POINTS, points_wo.size()); + + float3 brush_origin = op_data.curves_to_world_mat * op_data.pos_cu; + float2 brush_origin_2d; + ED_view3d_project_v2(region, brush_origin, brush_origin_2d); + + /* Smooth alpha transition until the brush edge. */ + const int alpha_border = 20; + const float dist_to_inner_border = brush_radius - alpha_border; + for (const float3 &pos_wo : points_wo) { + float2 pos_screen_space; + ED_view3d_project_v2(region, pos_wo, pos_screen_space); + + const float dist_to_point = math::length(pos_screen_space - brush_origin_2d); + float alpha = 1.0f - ((dist_to_point - dist_to_inner_border) / alpha_border); + + immAttr4f(col3d, 0.9f, 0.9f, 0.9f, alpha); immVertex3fv(pos3d, pos_wo); } immEnd(); immUnbindProgram(); + /* Reset the drawing settings. */ GPU_point_size(1); + GPU_matrix_pop_projection(); + GPU_matrix_pop(); + + int scissor[4] = {0}; + GPU_scissor_get(scissor); + wmWindowViewport(win); + GPU_scissor(scissor[0], scissor[1], scissor[2], scissor[3]); + + /* Draw the brush circle. */ + GPU_matrix_translate_2f((float)op_data.initial_mouse.x, (float)op_data.initial_mouse.y); + + GPUVertFormat *format = immVertexFormat(); + uint pos2d = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + immUniformColor3fvAlpha(circle_col, circle_alpha); + imm_draw_circle_wire_2d(pos2d, 0.0f, 0.0f, brush_radius, 80); + + immUnbindProgram(); + GPU_blend(GPU_BLEND_NONE); } static int min_distance_edit_invoke(bContext *C, wmOperator *op, const wmEvent *event) @@ -1652,23 +1760,19 @@ static int min_distance_edit_invoke(bContext *C, wmOperator *op, const wmEvent * return OPERATOR_CANCELLED; } - const float3 hit_pos_su = ray_hit.co; const float3 hit_normal_su = ray_hit.no; const float4x4 curves_to_world_mat = curves_ob.obmat; const float4x4 world_to_curves_mat = curves_to_world_mat.inverted(); const float4x4 surface_to_curves_mat = world_to_curves_mat * surface_to_world_mat; const float4x4 surface_to_curves_normal_mat = surface_to_curves_mat.inverted().transposed(); - const float3 hit_pos_cu = surface_to_curves_mat * hit_pos_su; const float3 hit_normal_cu = math::normalize(surface_to_curves_normal_mat * hit_normal_su); MinDistanceEditData *op_data = MEM_new(__func__); op_data->curves_to_world_mat = curves_to_world_mat; op_data->normal_cu = hit_normal_cu; - op_data->pos_cu = hit_pos_cu; - op_data->initial_mouse_x = mouse_pos_int_re.x; - op_data->draw_handle = ED_region_draw_cb_activate( - region->type, min_distance_edit_draw, op_data, REGION_DRAW_POST_VIEW); + op_data->pos_cu = ray_hit.co; + op_data->initial_mouse = event->xy; op_data->brush = BKE_paint_brush(&scene->toolsettings->curves_sculpt->paint); op_data->initial_minimum_distance = op_data->brush->curves_sculpt_settings->minimum_distance; @@ -1677,6 +1781,16 @@ static int min_distance_edit_invoke(bContext *C, wmOperator *op, const wmEvent * } op->customdata = op_data; + + /* Temporarily disable other paint cursors. */ + wmWindowManager *wm = CTX_wm_manager(C); + op_data->orig_paintcursors = wm->paintcursors; + BLI_listbase_clear(&wm->paintcursors); + + /* Add minimum distance paint cursor. */ + op_data->cursor = WM_paint_cursor_activate( + SPACE_TYPE_ANY, RGN_TYPE_ANY, op->type->poll, min_distance_edit_draw, op_data); + WM_event_add_modal_handler(C, op); ED_region_tag_redraw(region); return OPERATOR_RUNNING_MODAL; @@ -1688,6 +1802,13 @@ static int min_distance_edit_modal(bContext *C, wmOperator *op, const wmEvent *e MinDistanceEditData &op_data = *static_cast(op->customdata); auto finish = [&]() { + wmWindowManager *wm = CTX_wm_manager(C); + + /* Restore original paint cursors. */ + wm->paintcursors = op_data.orig_paintcursors; + WM_paint_cursor_end(static_cast(op_data.cursor)); + MEM_SAFE_FREE(op_data.cursor); + ED_region_tag_redraw(region); ED_region_draw_cb_exit(region->type, op_data.draw_handle); MEM_freeN(&op_data); @@ -1695,10 +1816,10 @@ static int min_distance_edit_modal(bContext *C, wmOperator *op, const wmEvent *e switch (event->type) { case MOUSEMOVE: { - const int2 mouse_pos_int_re{event->mval}; + const int2 mouse_pos_int_re{event->xy}; const float2 mouse_pos_re{mouse_pos_int_re}; - const float mouse_diff_x = mouse_pos_int_re.x - op_data.initial_mouse_x; + const float mouse_diff_x = mouse_pos_int_re.x - op_data.initial_mouse.x; const float factor = powf(2, mouse_diff_x / UI_UNIT_X / 10.0f); op_data.brush->curves_sculpt_settings->minimum_distance = op_data.initial_minimum_distance * factor; diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index e953cf1141c..50ef743c2c1 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -1958,7 +1958,7 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) static const EnumPropertyItem density_mode_items[] = { {BRUSH_CURVES_SCULPT_DENSITY_MODE_AUTO, "AUTO", - ICON_OUTLINER_OB_POINTCLOUD, + ICON_AUTO, "Auto", "Either add or remove curves depending on the minimum distance of the curves under the " "cursor"},