[Bf-blender-cvs] [fced604acfc] master: Curves: improve Snake Hook brush

Jacques Lucke noreply at git.blender.org
Tue Mar 29 14:46:25 CEST 2022


Commit: fced604acfc90ae2bdca068ce1898cdc39838b5f
Author: Jacques Lucke
Date:   Tue Mar 29 14:46:14 2022 +0200
Branches: master
https://developer.blender.org/rBfced604acfc90ae2bdca068ce1898cdc39838b5f

Curves: improve Snake Hook brush

This implements the spherical brush and different falloff
modes for the Snake Hook brush.

Differential Revision: https://developer.blender.org/D14408

===================================================================

M	release/scripts/startup/bl_ui/space_view3d.py
M	source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc

===================================================================

diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index f40d96e30a0..1c42bc85fae 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -514,6 +514,10 @@ class _draw_tool_settings_context_mode:
             layout.prop(tool_settings.curves_sculpt, "interpolate_length")
             layout.prop(tool_settings.curves_sculpt, "interpolate_shape")
 
+        if brush.curves_sculpt_tool == 'SNAKE_HOOK':
+            layout.prop(brush, "falloff_shape", expand=True)
+            layout.prop(brush, "curve_preset")
+
         if brush.curves_sculpt_tool == 'TEST1':
             layout.prop(tool_settings.curves_sculpt, "distance")
 
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 682cd3b47ca..117b2265015 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc
@@ -36,6 +36,14 @@
 #include "ED_screen.h"
 #include "ED_view3d.h"
 
+/**
+ * The code below uses a prefix naming convention to indicate the coordinate space:
+ * cu: Local space of the curves object that is being edited.
+ * su: Local space of the surface object.
+ * wo: World space.
+ * re: 2D coordinates within the region.
+ */
+
 namespace blender::ed::sculpt_paint {
 
 using blender::bke::CurvesGeometry;
@@ -45,116 +53,231 @@ using blender::bke::CurvesGeometry;
  */
 class SnakeHookOperation : public CurvesSculptStrokeOperation {
  private:
-  float2 last_mouse_position_;
+  float2 last_mouse_position_re_;
+
+  CurvesBrush3D brush_3d_;
+
+  friend struct SnakeHookOperatorExecutor;
 
  public:
-  void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override
+  void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override;
+};
+
+/**
+ * Utility class that actually executes the update when the stroke is updated. That's useful
+ * because it avoids passing a very large number of parameters between functions.
+ */
+struct SnakeHookOperatorExecutor {
+  SnakeHookOperation *self_ = nullptr;
+  bContext *C_ = nullptr;
+  Scene *scene_ = nullptr;
+  Object *object_ = nullptr;
+  ARegion *region_ = nullptr;
+  View3D *v3d_ = nullptr;
+  RegionView3D *rv3d_ = nullptr;
+
+  CurvesSculpt *curves_sculpt_ = nullptr;
+  Brush *brush_ = nullptr;
+  float brush_radius_re_;
+  float brush_strength_;
+  eBrushFalloffShape falloff_shape_;
+
+  Curves *curves_id_ = nullptr;
+  CurvesGeometry *curves_ = nullptr;
+
+  float4x4 curves_to_world_mat_;
+  float4x4 world_to_curves_mat_;
+
+  float2 brush_pos_prev_re_;
+  float2 brush_pos_re_;
+  float2 brush_pos_diff_re_;
+
+  void execute(SnakeHookOperation &self, bContext *C, const StrokeExtension &stroke_extension)
   {
-    BLI_SCOPED_DEFER([&]() { last_mouse_position_ = stroke_extension.mouse_position; });
+    BLI_SCOPED_DEFER([&]() { self.last_mouse_position_re_ = stroke_extension.mouse_position; });
+
+    self_ = &self;
+    C_ = 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_sculpt_ = scene_->toolsettings->curves_sculpt;
+    brush_ = BKE_paint_brush(&curves_sculpt_->paint);
+    brush_radius_re_ = BKE_brush_size_get(scene_, brush_);
+    brush_strength_ = BKE_brush_alpha_get(scene_, brush_);
+    falloff_shape_ = static_cast<eBrushFalloffShape>(brush_->falloff_shape);
+
+    curves_to_world_mat_ = object_->obmat;
+    world_to_curves_mat_ = curves_to_world_mat_.inverted();
+
+    curves_id_ = static_cast<Curves *>(object_->data);
+    curves_ = &CurvesGeometry::wrap(curves_id_->geometry);
+
+    brush_pos_prev_re_ = self.last_mouse_position_re_;
+    brush_pos_re_ = stroke_extension.mouse_position;
+    brush_pos_diff_re_ = brush_pos_re_ - brush_pos_prev_re_;
 
     if (stroke_extension.is_first) {
+      if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) {
+        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;
+        }
+      }
       return;
     }
 
-    Scene &scene = *CTX_data_scene(C);
-    Object &object = *CTX_data_active_object(C);
-    ARegion *region = CTX_wm_region(C);
-    View3D *v3d = CTX_wm_view3d(C);
-    RegionView3D *rv3d = CTX_wm_region_view3d(C);
+    if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) {
+      this->spherical_snake_hook();
+    }
+    else if (falloff_shape_ == PAINT_FALLOFF_SHAPE_TUBE) {
+      this->projected_snake_hook();
+    }
+    else {
+      BLI_assert_unreachable();
+    }
 
-    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);
-    const float brush_strength = BKE_brush_alpha_get(&scene, &brush);
+    curves_->tag_positions_changed();
+    DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY);
+    ED_region_tag_redraw(region_);
+  }
 
-    const float4x4 ob_mat = object.obmat;
-    const float4x4 ob_imat = ob_mat.inverted();
+  void projected_snake_hook()
+  {
+    MutableSpan<float3> positions_cu = curves_->positions();
 
     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);
-    MutableSpan<float3> positions = curves.positions();
+    threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) {
+      for (const int curve_i : curves_range) {
+        const IndexRange points = curves_->points_for_curve(curve_i);
+        const int last_point_i = points.last();
+        const float3 old_pos_cu = positions_cu[last_point_i];
 
-    const float2 mouse_prev = last_mouse_position_;
-    const float2 mouse_cur = stroke_extension.mouse_position;
-    const float2 mouse_diff = mouse_cur - mouse_prev;
+        float2 old_pos_re;
+        ED_view3d_project_float_v2_m4(region_, old_pos_cu, old_pos_re, projection.values);
 
-    threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) {
-      for (const int curve_i : curves_range) {
-        const IndexRange curve_points = curves.points_for_curve(curve_i);
-        const int last_point_i = curve_points.last();
+        const float distance_to_brush_re = math::distance(old_pos_re, brush_pos_prev_re_);
+        if (distance_to_brush_re > brush_radius_re_) {
+          continue;
+        }
 
-        const float3 old_position = positions[last_point_i];
+        const float radius_falloff = BKE_brush_curve_strength(
+            brush_, distance_to_brush_re, brush_radius_re_);
+        const float weight = brush_strength_ * radius_falloff;
 
-        float2 old_position_screen;
-        ED_view3d_project_float_v2_m4(
-            region, old_position, old_position_screen, projection.values);
+        const float2 new_position_re = old_pos_re + brush_pos_diff_re_ * weight;
+        float3 new_position_wo;
+        ED_view3d_win_to_3d(
+            v3d_, region_, curves_to_world_mat_ * old_pos_cu, new_position_re, new_position_wo);
+        const float3 new_position_cu = world_to_curves_mat_ * new_position_wo;
 
-        const float distance_screen = math::distance(old_position_screen, mouse_prev);
-        if (distance_screen > brush_radius) {
+        this->move_last_point_and_resample(positions_cu.slice(points), new_position_cu);
+      }
+    });
+  }
+
+  void spherical_snake_hook()
+  {
+    MutableSpan<float3> positions_cu = curves_->positions();
+
+    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 float3 brush_diff_cu = brush_end_cu - brush_start_cu;
+
+    const float brush_radius_cu = self_->brush_3d_.radius_cu;
+    const float brush_radius_sq_cu = pow2f(brush_radius_cu);
+
+    threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) {
+      for (const int curve_i : curves_range) {
+        const IndexRange points = curves_->points_for_curve(curve_i);
+        const int last_point_i = points.last();
+        const float3 old_pos_cu = positions_cu[last_point_i];
+
+        const float distance_to_brush_sq_cu = dist_squared_to_line_segment_v3(
+            old_pos_cu, brush_start_cu, brush_end_cu);
+        if (distance_to_brush_sq_cu > brush_radius_sq_cu) {
           continue;
         }
 
-        const float radius_falloff = pow2f(1.0f - distance_screen / brush_radius);
-        const float weight = brush_strength * radius_falloff;
+        const float distance_to_brush_cu = std::sqrt(distance_to_brush_sq_cu);
+
+        const float radius_falloff = BKE_brush_curve_strength(
+            brush_, distance_to_brush_cu, brush_radius_cu);
+        const float weight = brush_strength_ * radius_falloff;
 
-        const float2 new_position_screen = old_position_screen + mouse_diff * weight;
-        float3 new_position;
-        ED_view3d_win_to_3d(v3d, region, ob_mat * old_position, new_position_screen, new_position);
-        new_position = ob_imat * new_position;
+        const float3 new_pos_cu = old_pos_cu + weight * brush_diff_cu;
 
-        this->move_last

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list