Curves: support spherical delete brush

Differential Revision: https://developer.blender.org/D14797
This commit is contained in:
Jacques Lucke 2022-05-02 09:18:57 +02:00
parent a5644f9a28
commit f0f44fd92f
Notes: blender-bot 2023-02-13 16:00:27 +01:00
Referenced by issue #96446, Delete brush - curve sculpting
2 changed files with 168 additions and 31 deletions

View File

@ -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'

View File

@ -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
void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override;
};
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;
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_;
float4x4 curves_to_world_mat_;
float4x4 world_to_curves_mat_;
void execute(DeleteOperation &self, bContext *C, const StrokeExtension &stroke_extension)
{
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);
BLI_SCOPED_DEFER([&]() { self.brush_pos_prev_re_ = stroke_extension.mouse_position; });
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);
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);
ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values);
Curves &curves_id = *static_cast<Curves *>(object.data);
CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
Span<float3> positions = curves.positions();
const float2 mouse_start = stroke_extension.is_first ? stroke_extension.mouse_position :
last_mouse_position_;
const float2 mouse_end = stroke_extension.mouse_position;
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>();