diff options
-rw-r--r-- | release/scripts/startup/bl_ui/space_view3d.py | 3 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/curves_sculpt_delete.cc | 198 |
2 files changed, 169 insertions, 32 deletions
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 4f1d001b2f3..69477c80c07 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -529,6 +529,9 @@ class _draw_tool_settings_context_mode: layout.prop(brush, "falloff_shape", expand=True) layout.prop(brush, "curve_preset") + if brush.curves_sculpt_tool == 'DELETE': + layout.prop(brush, "falloff_shape", expand=True) + class VIEW3D_HT_header(Header): bl_space_type = 'VIEW_3D' diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc b/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc index 018c8c2a34f..2841a19d677 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc @@ -35,68 +35,202 @@ #include "ED_screen.h" #include "ED_view3d.h" +/** + * The code below uses a suffix naming convention to indicate the coordinate space: + * cu: Local space of the curves object that is being edited. + * wo: World space. + * re: 2D coordinates within the region. + */ + namespace blender::ed::sculpt_paint { using blender::bke::CurvesGeometry; class DeleteOperation : public CurvesSculptStrokeOperation { private: - float2 last_mouse_position_; + float2 brush_pos_prev_re_; + + CurvesBrush3D brush_3d_; + + friend struct DeleteOperationExecutor; public: - void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override - { - Scene &scene = *CTX_data_scene(C); - Object &object = *CTX_data_active_object(C); - ARegion *region = CTX_wm_region(C); - RegionView3D *rv3d = CTX_wm_region_view3d(C); + void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override; +}; - CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt; - Brush &brush = *BKE_paint_brush(&curves_sculpt.paint); - const float brush_radius = BKE_brush_size_get(&scene, &brush); +struct DeleteOperationExecutor { + DeleteOperation *self_ = nullptr; + bContext *C_ = nullptr; + Depsgraph *depsgraph_ = nullptr; + Scene *scene_ = nullptr; + Object *object_ = nullptr; + ARegion *region_ = nullptr; + View3D *v3d_ = nullptr; + RegionView3D *rv3d_ = nullptr; - float4x4 projection; - ED_view3d_ob_project_mat_get(rv3d, &object, projection.values); + Curves *curves_id_ = nullptr; + CurvesGeometry *curves_ = nullptr; + + CurvesSculpt *curves_sculpt_ = nullptr; + Brush *brush_ = nullptr; + float brush_radius_re_; + + float2 brush_pos_re_; + float2 brush_pos_prev_re_; - Curves &curves_id = *static_cast<Curves *>(object.data); - CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); - Span<float3> positions = curves.positions(); + float4x4 curves_to_world_mat_; + float4x4 world_to_curves_mat_; - const float2 mouse_start = stroke_extension.is_first ? stroke_extension.mouse_position : - last_mouse_position_; - const float2 mouse_end = stroke_extension.mouse_position; + void execute(DeleteOperation &self, bContext *C, const StrokeExtension &stroke_extension) + { + BLI_SCOPED_DEFER([&]() { self.brush_pos_prev_re_ = stroke_extension.mouse_position; }); + + self_ = &self; + C_ = C; + depsgraph_ = CTX_data_depsgraph_pointer(C); + scene_ = CTX_data_scene(C); + object_ = CTX_data_active_object(C); + region_ = CTX_wm_region(C); + v3d_ = CTX_wm_view3d(C); + rv3d_ = CTX_wm_region_view3d(C); + + curves_id_ = static_cast<Curves *>(object_->data); + curves_ = &CurvesGeometry::wrap(curves_id_->geometry); + + curves_sculpt_ = scene_->toolsettings->curves_sculpt; + brush_ = BKE_paint_brush(&curves_sculpt_->paint); + brush_radius_re_ = BKE_brush_size_get(scene_, brush_); + + brush_pos_re_ = stroke_extension.mouse_position; + brush_pos_prev_re_ = stroke_extension.is_first ? stroke_extension.mouse_position : + self.brush_pos_prev_re_; + + curves_to_world_mat_ = object_->obmat; + world_to_curves_mat_ = curves_to_world_mat_.inverted(); + + const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>( + brush_->falloff_shape); + + if (stroke_extension.is_first) { + if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + this->initialize_spherical_brush_reference_point(); + } + } + + if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { + this->delete_projected(); + } + else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + this->delete_spherical(); + } + else { + BLI_assert_unreachable(); + } + + DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); + ED_region_tag_redraw(region_); + } + + void delete_projected() + { + float4x4 projection; + ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); + + Span<float3> positions_cu = curves_->positions(); /* Find indices of curves that have to be removed. */ Vector<int64_t> indices; const IndexMask curves_to_remove = index_mask_ops::find_indices_based_on_predicate( - curves.curves_range(), 512, indices, [&](const int curve_i) { - const IndexRange point_range = curves.points_for_curve(curve_i); + curves_->curves_range(), 512, indices, [&](const int curve_i) { + const IndexRange point_range = curves_->points_for_curve(curve_i); for (const int segment_i : IndexRange(point_range.size() - 1)) { - const float3 pos1 = positions[point_range[segment_i]]; - const float3 pos2 = positions[point_range[segment_i + 1]]; + const float3 pos1_cu = positions_cu[point_range[segment_i]]; + const float3 pos2_cu = positions_cu[point_range[segment_i + 1]]; - float2 pos1_proj, pos2_proj; - ED_view3d_project_float_v2_m4(region, pos1, pos1_proj, projection.values); - ED_view3d_project_float_v2_m4(region, pos2, pos2_proj, projection.values); + float2 pos1_re, pos2_re; + ED_view3d_project_float_v2_m4(region_, pos1_cu, pos1_re, projection.values); + ED_view3d_project_float_v2_m4(region_, pos2_cu, pos2_re, projection.values); - const float dist = dist_seg_seg_v2(pos1_proj, pos2_proj, mouse_start, mouse_end); - if (dist <= brush_radius) { + const float dist = dist_seg_seg_v2( + pos1_re, pos2_re, brush_pos_prev_re_, brush_pos_re_); + if (dist <= brush_radius_re_) { return true; } } return false; }); - curves.remove_curves(curves_to_remove); + curves_->remove_curves(curves_to_remove); + } - curves.tag_positions_changed(); - DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY); - ED_region_tag_redraw(region); + void delete_spherical() + { + Span<float3> positions_cu = curves_->positions(); - last_mouse_position_ = stroke_extension.mouse_position; + float4x4 projection; + ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); + + float3 brush_start_wo, brush_end_wo; + ED_view3d_win_to_3d(v3d_, + region_, + curves_to_world_mat_ * self_->brush_3d_.position_cu, + brush_pos_prev_re_, + brush_start_wo); + ED_view3d_win_to_3d(v3d_, + region_, + curves_to_world_mat_ * self_->brush_3d_.position_cu, + brush_pos_re_, + brush_end_wo); + const float3 brush_start_cu = world_to_curves_mat_ * brush_start_wo; + const float3 brush_end_cu = world_to_curves_mat_ * brush_end_wo; + + const float brush_radius_cu = self_->brush_3d_.radius_cu; + const float brush_radius_sq_cu = pow2f(brush_radius_cu); + + Vector<int64_t> indices; + const IndexMask curves_to_remove = index_mask_ops::find_indices_based_on_predicate( + curves_->curves_range(), 512, indices, [&](const int curve_i) { + const IndexRange points = curves_->points_for_curve(curve_i); + for (const int segment_i : IndexRange(points.size() - 1)) { + const float3 pos1_cu = positions_cu[points[segment_i]]; + const float3 pos2_cu = positions_cu[points[segment_i] + 1]; + + float3 closest_segment_cu, closest_brush_cu; + isect_seg_seg_v3(pos1_cu, + pos2_cu, + brush_start_cu, + brush_end_cu, + closest_segment_cu, + closest_brush_cu); + const float distance_to_brush_sq_cu = math::distance_squared(closest_segment_cu, + closest_brush_cu); + if (distance_to_brush_sq_cu > brush_radius_sq_cu) { + continue; + } + return true; + } + return false; + }); + + curves_->remove_curves(curves_to_remove); + } + + void initialize_spherical_brush_reference_point() + { + std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush( + *C_, *object_, brush_pos_re_, brush_radius_re_); + if (brush_3d.has_value()) { + self_->brush_3d_ = *brush_3d; + } } }; +void DeleteOperation::on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) +{ + DeleteOperationExecutor executor; + executor.execute(*this, C, stroke_extension); +} + std::unique_ptr<CurvesSculptStrokeOperation> new_delete_operation() { return std::make_unique<DeleteOperation>(); |