From 97fe1387aef921246136b4fa057ae96a48b1bf22 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 17 Dec 2024 15:29:40 +0100 Subject: [PATCH] Fix #101766: support orbit-around-selection in curves sculpt mode How the new navigation pivot is determined depends a bit on the kind of brush: * Brushes that deform or remove curves use the 3d-brush position at the start of the brush. * Brushes that add new curves set the pivot to the bounding box center of the new curves. Finding a good pivot point is not super trivial for curves, but the existing 3d brush functionality seems to work well. This also has the benefit that almost no additional computation is needed when the user is using the spherical brush mode. However, if the projected mode is used, and orbit-around-selection is on, then we have to compute the spherical brush center now anyway. Pull Request: https://projects.blender.org/blender/blender/pulls/131907 --- source/blender/editors/sculpt_paint/curves_sculpt_add.cc | 9 +++++++++ .../blender/editors/sculpt_paint/curves_sculpt_brush.cc | 8 ++++++++ .../blender/editors/sculpt_paint/curves_sculpt_comb.cc | 5 ++++- .../blender/editors/sculpt_paint/curves_sculpt_delete.cc | 5 ++++- .../editors/sculpt_paint/curves_sculpt_density.cc | 9 +++++++++ .../editors/sculpt_paint/curves_sculpt_grow_shrink.cc | 5 ++++- .../blender/editors/sculpt_paint/curves_sculpt_intern.hh | 8 +++++++- .../blender/editors/sculpt_paint/curves_sculpt_pinch.cc | 5 ++++- .../blender/editors/sculpt_paint/curves_sculpt_puff.cc | 5 ++++- .../sculpt_paint/curves_sculpt_selection_paint.cc | 5 ++++- .../blender/editors/sculpt_paint/curves_sculpt_slide.cc | 3 +++ .../blender/editors/sculpt_paint/curves_sculpt_smooth.cc | 5 ++++- .../editors/sculpt_paint/curves_sculpt_snake_hook.cc | 5 ++++- source/blender/editors/space_view3d/view3d_navigate.cc | 4 ++++ 14 files changed, 72 insertions(+), 9 deletions(-) diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc index 6386328326a..a7f4ff724fe 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc @@ -6,6 +6,7 @@ #include "curves_sculpt_intern.hh" +#include "BLI_bounds.hh" #include "BLI_kdtree.h" #include "BLI_math_geom.h" #include "BLI_math_matrix.hh" @@ -239,6 +240,14 @@ struct AddOperationExecutor { add_outputs.new_curves_range)); selection.finish(); } + if (U.uiflag & USER_ORBIT_SELECTION) { + if (const std::optional> center_cu = bounds::min_max( + curves_orig_->positions().slice(add_outputs.new_points_range))) + { + remember_stroke_position( + *ctx_.scene, math::transform_point(transforms_.curves_to_world, center_cu->center())); + } + } if (add_outputs.uv_error) { report_invalid_uv_map(stroke_extension.reports); diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc b/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc index 323b4f59f09..2c7ebfc840b 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc @@ -346,6 +346,14 @@ Vector get_symmetry_brush_transforms(const eCurvesSymmetryType symmetr return matrices; } +void remember_stroke_position(Scene &scene, const float3 &brush_position_wo) +{ + UnifiedPaintSettings &ups = scene.toolsettings->unified_paint_settings; + copy_v3_v3(ups.average_stroke_accum, brush_position_wo); + ups.average_stroke_counter = 1; + ups.last_stroke_valid = true; +} + float transform_brush_radius(const float4x4 &transform, const float3 &brush_position, const float old_radius) diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc b/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc index b10096e2b5f..2b7956bf7c3 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc @@ -137,7 +137,7 @@ struct CombOperationExecutor { brush_pos_diff_re_ = brush_pos_re_ - brush_pos_prev_re_; if (stroke_extension.is_first) { - if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE || (U.uiflag & USER_ORBIT_SELECTION)) { this->initialize_spherical_brush_reference_point(); } self_->constraint_solver_.initialize( @@ -392,6 +392,9 @@ struct CombOperationExecutor { brush_radius_base_re_); if (brush_3d.has_value()) { self_->brush_3d_ = *brush_3d; + remember_stroke_position( + *ctx_.scene, + math::transform_point(transforms_.curves_to_world, self_->brush_3d_.position_cu)); } } }; diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc b/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc index 6108462df2c..855d4ac077f 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc @@ -106,7 +106,7 @@ struct DeleteOperationExecutor { const eBrushFalloffShape falloff_shape = eBrushFalloffShape(brush_->falloff_shape); if (stroke_extension.is_first) { - if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE || (U.uiflag & USER_ORBIT_SELECTION)) { this->initialize_spherical_brush_reference_point(); } const bke::crazyspace::GeometryDeformation deformation = @@ -261,6 +261,9 @@ struct DeleteOperationExecutor { brush_radius_base_re_); if (brush_3d.has_value()) { self_->brush_3d_ = *brush_3d; + remember_stroke_position( + *ctx_.scene, + math::transform_point(transforms_.curves_to_world, self_->brush_3d_.position_cu)); } } }; diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_density.cc b/source/blender/editors/sculpt_paint/curves_sculpt_density.cc index 42e8ac27382..791c51b1129 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_density.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_density.cc @@ -16,6 +16,7 @@ #include "BKE_object.hh" #include "BKE_paint.hh" #include "BKE_report.hh" +#include "BLI_bounds.hh" #include "ED_screen.hh" #include "ED_view3d.hh" @@ -287,6 +288,14 @@ struct DensityAddOperationExecutor { add_outputs.new_curves_range)); selection.finish(); } + if (U.uiflag & USER_ORBIT_SELECTION) { + if (const std::optional> center_cu = bounds::min_max( + curves_orig_->positions().slice(add_outputs.new_points_range))) + { + remember_stroke_position( + *ctx_.scene, math::transform_point(transforms_.curves_to_world, center_cu->center())); + } + } if (add_outputs.uv_error) { report_invalid_uv_map(stroke_extension.reports); diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc index f82dd7611d9..6f31e7ce98a 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc @@ -290,7 +290,7 @@ struct CurvesEffectOperationExecutor { brush_pos_end_re_ = stroke_extension.mouse_position; if (stroke_extension.is_first) { - if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) { + if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE || (U.flag & USER_ORBIT_SELECTION)) { if (std::optional brush_3d = sample_curves_3d_brush( *ctx_.depsgraph, *ctx_.region, @@ -301,6 +301,9 @@ struct CurvesEffectOperationExecutor { brush_radius_base_re_)) { self.brush_3d_ = *brush_3d; + remember_stroke_position( + *ctx_.scene, + math::transform_point(transforms_.curves_to_world, self_->brush_3d_.position_cu)); } } diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh index bc9f82e3426..49c715e5384 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh @@ -92,6 +92,12 @@ std::optional sample_curves_3d_brush(const Depsgraph &depsgraph, const float2 &brush_pos_re, const float brush_radius_re); +/** + * Updates the position of the stroke so that it can be used by the orbit-around-selection + * navigation method. + */ +void remember_stroke_position(Scene &scene, const float3 &brush_position_wo); + Vector get_symmetry_brush_transforms(eCurvesSymmetryType symmetry); bke::SpanAttributeWriter float_selection_ensure(Curves &curves_id); @@ -117,7 +123,7 @@ void move_last_point_and_resample(MoveAndResampleBuffers &buffer, class CurvesSculptCommonContext { public: const Depsgraph *depsgraph = nullptr; - const Scene *scene = nullptr; + Scene *scene = nullptr; ARegion *region = nullptr; const View3D *v3d = nullptr; RegionView3D *rv3d = nullptr; diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_pinch.cc b/source/blender/editors/sculpt_paint/curves_sculpt_pinch.cc index 2328dcd01a1..29cafacdf58 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_pinch.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_pinch.cc @@ -112,7 +112,7 @@ struct PinchOperationExecutor { const eBrushFalloffShape falloff_shape = eBrushFalloffShape(brush_->falloff_shape); if (stroke_extension.is_first) { - if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE || (U.uiflag & USER_ORBIT_SELECTION)) { self_->brush_3d_ = *sample_curves_3d_brush(*ctx_.depsgraph, *ctx_.region, *ctx_.v3d, @@ -120,6 +120,9 @@ struct PinchOperationExecutor { *object_, brush_pos_re_, brush_radius_base_re_); + remember_stroke_position( + *ctx_.scene, + math::transform_point(transforms_.curves_to_world, self_->brush_3d_.position_cu)); } self_->constraint_solver_.initialize( diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_puff.cc b/source/blender/editors/sculpt_paint/curves_sculpt_puff.cc index cfc4e8cc286..04b5b623c6a 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_puff.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_puff.cc @@ -121,7 +121,7 @@ struct PuffOperationExecutor { surface_bvh_ = surface_->bvh_corner_tris(); if (stroke_extension.is_first) { - if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE || (U.uiflag & USER_ORBIT_SELECTION)) { self.brush_3d_ = *sample_curves_3d_brush(*ctx_.depsgraph, *ctx_.region, *ctx_.v3d, @@ -129,6 +129,9 @@ struct PuffOperationExecutor { *object_, brush_pos_re_, brush_radius_base_re_); + remember_stroke_position( + *ctx_.scene, + math::transform_point(transforms_.curves_to_world, self_->brush_3d_.position_cu)); } self_->constraint_solver_.initialize( diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc b/source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc index 8590cd34489..39b64daa1b5 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc @@ -115,7 +115,7 @@ struct SelectionPaintOperationExecutor { selection_goal_ = self_->use_select_ ? 1.0f : 0.0f; if (stroke_extension.is_first) { - if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE || (U.uiflag & USER_ORBIT_SELECTION)) { this->initialize_spherical_brush_reference_point(); } } @@ -377,6 +377,9 @@ struct SelectionPaintOperationExecutor { brush_radius_base_re_); if (brush_3d.has_value()) { self_->brush_3d_ = *brush_3d; + remember_stroke_position( + *ctx_.scene, + math::transform_point(transforms_.curves_to_world, self_->brush_3d_.position_cu)); } } }; diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_slide.cc b/source/blender/editors/sculpt_paint/curves_sculpt_slide.cc index 4af181177c4..e654015bfe0 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_slide.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_slide.cc @@ -239,6 +239,9 @@ struct SlideOperationExecutor { if (!brush_3d.has_value()) { return; } + remember_stroke_position( + *ctx_.scene, math::transform_point(transforms_.curves_to_world, brush_3d->position_cu)); + const ReverseUVSampler reverse_uv_sampler_orig{surface_uv_map_orig_, surface_corner_tris_orig_}; for (const float4x4 &brush_transform : brush_transforms) { diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_smooth.cc b/source/blender/editors/sculpt_paint/curves_sculpt_smooth.cc index 711c155311d..4a2f1f956c4 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_smooth.cc @@ -87,7 +87,7 @@ struct SmoothOperationExecutor { const eBrushFalloffShape falloff_shape = eBrushFalloffShape(brush_->falloff_shape); if (stroke_extension.is_first) { - if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE || (U.uiflag & USER_ORBIT_SELECTION)) { self.brush_3d_ = *sample_curves_3d_brush(*ctx_.depsgraph, *ctx_.region, *ctx_.v3d, @@ -95,6 +95,9 @@ struct SmoothOperationExecutor { *object_, brush_pos_re_, brush_radius_base_re_); + remember_stroke_position( + *ctx_.scene, + math::transform_point(transforms_.curves_to_world, self_->brush_3d_.position_cu)); } } diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc b/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc index 54dbe60f064..556df6148c6 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc @@ -129,7 +129,7 @@ struct SnakeHookOperatorExecutor { brush_pos_diff_re_ = brush_pos_re_ - brush_pos_prev_re_; if (stroke_extension.is_first) { - if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE || (U.uiflag & USER_ORBIT_SELECTION)) { std::optional brush_3d = sample_curves_3d_brush(*ctx_.depsgraph, *ctx_.region, *ctx_.v3d, @@ -139,6 +139,9 @@ struct SnakeHookOperatorExecutor { brush_radius_base_re_); if (brush_3d.has_value()) { self_->brush_3d_ = *brush_3d; + remember_stroke_position( + *ctx_.scene, + math::transform_point(transforms_.curves_to_world, self_->brush_3d_.position_cu)); } } return; diff --git a/source/blender/editors/space_view3d/view3d_navigate.cc b/source/blender/editors/space_view3d/view3d_navigate.cc index 4bba2d970e1..1efe14b2885 100644 --- a/source/blender/editors/space_view3d/view3d_navigate.cc +++ b/source/blender/editors/space_view3d/view3d_navigate.cc @@ -801,6 +801,10 @@ bool view3d_orbit_calc_center(bContext *C, float r_dyn_ofs[3]) BKE_paint_stroke_get_average(scene, ob_act_eval, lastofs); is_set = true; } + else if (ob_act && (ob_act->mode & OB_MODE_SCULPT_CURVES)) { + BKE_paint_stroke_get_average(scene, ob_act_eval, lastofs); + is_set = true; + } else if (ob_act && (ob_act->mode & OB_MODE_EDIT) && (ob_act->type == OB_FONT)) { Curve *cu = static_cast(ob_act_eval->data); EditFont *ef = cu->editfont;