[Bf-blender-cvs] [d67f9820b8f] master: Curves: improve Comb brush

Jacques Lucke noreply at git.blender.org
Wed Mar 23 18:20:16 CET 2022


Commit: d67f9820b8f8376084adf5ad964c580c0944027f
Author: Jacques Lucke
Date:   Wed Mar 23 18:19:38 2022 +0100
Branches: master
https://developer.blender.org/rBd67f9820b8f8376084adf5ad964c580c0944027f

Curves: improve Comb brush

New supported features:
* 3D/spherical brush that samples a good position on the curves.
* Falloff.

The custom falloff curve mapping is not yet available in the ui because that
requires some more ui reorganization. This is better done when we have
a better understanding of what settings we need exactly.

Currently, the depth of the 3d brush is only sampled once per stroke, when
first pressing LMB. Sometimes it is expected that the depth of the brush can
change within a single brush. However, implementing that in a good way
is not straight forward and might need additional options. Therefore that
will be handled separately. Some experimentation results are in D14376.

Ref T96445.

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

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

M	release/scripts/startup/bl_ui/space_view3d.py
M	source/blender/blenlib/BLI_math_geom.h
M	source/blender/blenlib/intern/math_geom.c
M	source/blender/editors/sculpt_paint/CMakeLists.txt
A	source/blender/editors/sculpt_paint/curves_sculpt_3d_brush.cc
M	source/blender/editors/sculpt_paint/curves_sculpt_comb.cc
M	source/blender/editors/sculpt_paint/curves_sculpt_intern.hh

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

diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index 70bc14dbc2b..b9415d00871 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -502,6 +502,10 @@ class _draw_tool_settings_context_mode:
                 header=True
             )
 
+        if brush.curves_sculpt_tool == 'COMB':
+            layout.prop(brush, "falloff_shape", expand=True)
+            layout.prop(brush, "curve_preset")
+
         if brush.curves_sculpt_tool == 'ADD':
             layout.prop(brush, "use_frontface")
             layout.prop(brush, "falloff_shape", expand=True)
diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h
index 2f3fbd59b5f..c31e3045c97 100644
--- a/source/blender/blenlib/BLI_math_geom.h
+++ b/source/blender/blenlib/BLI_math_geom.h
@@ -322,18 +322,22 @@ double closest_to_line_v2_db(double r_close[2],
 float closest_to_line_v3(float r_close[3], const float p[3], const float l1[3], const float l2[3]);
 /**
  * Point closest to v1 on line v2-v3 in 2D.
+ *
+ * \return A value in [0, 1] that corresponds to the position of #r_close on the line segment.
  */
-void closest_to_line_segment_v2(float r_close[2],
-                                const float p[2],
-                                const float l1[2],
-                                const float l2[2]);
+float closest_to_line_segment_v2(float r_close[2],
+                                 const float p[2],
+                                 const float l1[2],
+                                 const float l2[2]);
 /**
  * Point closest to v1 on line v2-v3 in 3D.
+ *
+ * \return A value in [0, 1] that corresponds to the position of #r_close on the line segment.
  */
-void closest_to_line_segment_v3(float r_close[3],
-                                const float p[3],
-                                const float l1[3],
-                                const float l2[3]);
+float closest_to_line_segment_v3(float r_close[3],
+                                 const float p[3],
+                                 const float l1[3],
+                                 const float l2[3]);
 void closest_to_plane_normalized_v3(float r_close[3], const float plane[4], const float pt[3]);
 /**
  * Find the closest point on a plane.
diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c
index bc3ed099fd5..e1ec22063e0 100644
--- a/source/blender/blenlib/intern/math_geom.c
+++ b/source/blender/blenlib/intern/math_geom.c
@@ -294,46 +294,48 @@ float dist_to_line_segment_v2(const float p[2], const float l1[2], const float l
   return sqrtf(dist_squared_to_line_segment_v2(p, l1, l2));
 }
 
-void closest_to_line_segment_v2(float r_close[2],
-                                const float p[2],
-                                const float l1[2],
-                                const float l2[2])
+float closest_to_line_segment_v2(float r_close[2],
+                                 const float p[2],
+                                 const float l1[2],
+                                 const float l2[2])
 {
   float lambda, cp[2];
 
   lambda = closest_to_line_v2(cp, p, l1, l2);
 
   /* flip checks for !finite case (when segment is a point) */
-  if (!(lambda > 0.0f)) {
+  if (lambda <= 0.0f) {
     copy_v2_v2(r_close, l1);
+    return 0.0f;
   }
-  else if (!(lambda < 1.0f)) {
+  if (lambda >= 1.0f) {
     copy_v2_v2(r_close, l2);
+    return 1.0f;
   }
-  else {
-    copy_v2_v2(r_close, cp);
-  }
+  copy_v2_v2(r_close, cp);
+  return lambda;
 }
 
-void closest_to_line_segment_v3(float r_close[3],
-                                const float p[3],
-                                const float l1[3],
-                                const float l2[3])
+float closest_to_line_segment_v3(float r_close[3],
+                                 const float p[3],
+                                 const float l1[3],
+                                 const float l2[3])
 {
   float lambda, cp[3];
 
   lambda = closest_to_line_v3(cp, p, l1, l2);
 
   /* flip checks for !finite case (when segment is a point) */
-  if (!(lambda > 0.0f)) {
+  if (lambda <= 0.0f) {
     copy_v3_v3(r_close, l1);
+    return 0.0f;
   }
-  else if (!(lambda < 1.0f)) {
+  if (lambda >= 1.0f) {
     copy_v3_v3(r_close, l2);
+    return 1.0f;
   }
-  else {
-    copy_v3_v3(r_close, cp);
-  }
+  copy_v3_v3(r_close, cp);
+  return lambda;
 }
 
 void closest_to_plane_v3(float r_close[3], const float plane[4], const float pt[3])
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt
index a08ec49d9c4..eee6e680d70 100644
--- a/source/blender/editors/sculpt_paint/CMakeLists.txt
+++ b/source/blender/editors/sculpt_paint/CMakeLists.txt
@@ -27,6 +27,7 @@ set(INC
 )
 
 set(SRC
+  curves_sculpt_3d_brush.cc
   curves_sculpt_add.cc
   curves_sculpt_comb.cc
   curves_sculpt_delete.cc
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_3d_brush.cc b/source/blender/editors/sculpt_paint/curves_sculpt_3d_brush.cc
new file mode 100644
index 00000000000..94c0f5536b7
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_3d_brush.cc
@@ -0,0 +1,232 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <algorithm>
+
+#include "curves_sculpt_intern.hh"
+
+#include "BKE_bvhutils.h"
+#include "BKE_context.h"
+#include "BKE_curves.hh"
+
+#include "ED_view3d.h"
+
+#include "UI_interface.h"
+
+#include "BLI_enumerable_thread_specific.hh"
+#include "BLI_task.hh"
+
+/**
+ * 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 {
+
+struct BrushPositionCandidate {
+  /** 3D position of the brush. */
+  float3 position_cu;
+  /** Squared distance from the mouse position in screen space. */
+  float distance_sq_re = FLT_MAX;
+  /** Measure for how far away the candidate is from the camera. */
+  float depth_sq_cu = FLT_MAX;
+};
+
+/**
+ * Determine the 3D position of a brush based on curve segments under a screen position.
+ */
+static std::optional<float3> find_curves_brush_position(const CurvesGeometry &curves,
+                                                        const float3 &ray_start_cu,
+                                                        const float3 &ray_end_cu,
+                                                        const float brush_radius_re,
+                                                        ARegion &region,
+                                                        RegionView3D &rv3d,
+                                                        Object &object)
+{
+  /* This value might have to be adjusted based on user feedback. */
+  const float brush_inner_radius_re = std::min<float>(brush_radius_re, (float)UI_UNIT_X / 3.0f);
+  const float brush_inner_radius_sq_re = pow2f(brush_inner_radius_re);
+
+  float4x4 projection;
+  ED_view3d_ob_project_mat_get(&rv3d, &object, projection.values);
+
+  float2 brush_pos_re;
+  ED_view3d_project_float_v2_m4(&region, ray_start_cu, brush_pos_re, projection.values);
+
+  const float max_depth_sq_cu = math::distance_squared(ray_start_cu, ray_end_cu);
+
+  /* Contains the logic that checks if `b` is a better candidate than `a`. */
+  auto is_better_candidate = [&](const BrushPositionCandidate &a,
+                                 const BrushPositionCandidate &b) {
+    if (b.distance_sq_re <= brush_inner_radius_sq_re) {
+      if (a.distance_sq_re > brush_inner_radius_sq_re) {
+        /* New candidate is in inner radius while old one is not. */
+        return true;
+      }
+      else if (b.depth_sq_cu < a.depth_sq_cu) {
+        /* Both candidates are in inner radius, but new one is closer to the camera. */
+        return true;
+      }
+    }
+    else if (b.distance_sq_re < a.distance_sq_re) {
+      /* Both candidates are outside of inner radius, but new on is closer to the brush center. */
+      return true;
+    }
+    return false;
+  };
+
+  auto update_if_better = [&](BrushPositionCandidate &a, const BrushPositionCandidate &b) {
+    if (is_better_candidate(a, b)) {
+      a = b;
+    }
+  };
+
+  const Span<float3> positions = curves.positions();
+
+  BrushPositionCandidate best_candidate = threading::parallel_reduce(
+      curves.curves_range(),
+      128,
+      BrushPositionCandidate(),
+      [&](IndexRange curves_range, const BrushPositionCandidate &init) {
+        BrushPositionCandidate best_candidate = init;
+
+        for (const int curve_i : curves_range) {
+          const IndexRange points = curves.range_for_curve(curve_i);
+          const int tot_segments = points.size() - 1;
+
+          for (const int segment_i : IndexRange(tot_segments)) {
+            const float3 &p1_cu = positions[points[segment_i]];
+            const float3 &p2_cu = positions[points[segment_i] + 1];
+
+            float2 p1_re, p2_re;
+            ED_view3d_project_float_v2_m4(&region, p1_cu, p1_re, projection.values);
+            ED_view3d_project_float_v2_m4(&region, p2_cu, p2_re, projection.values);
+
+            float2 closest_re;
+            const float lambda = closest_to_line_segment_v2(
+                closest_re, brush_pos_re, p1_re, p2_re);
+
+            const float3 closest_cu = math::interpolate(p1_cu, p2_cu, lambda);
+            const float depth_sq_cu = math::distance_squared(ray_start_cu, closest_cu);
+            if (depth_sq_cu > max_depth_sq_cu) {
+              continue;
+            }
+
+            const float distance_sq_re = math::distance_squared(brush_pos_re, closest_re);
+
+            BrushPositionCandidate candidate;
+            candidate.position_cu = closest_cu;
+            candidate.depth_sq_cu = depth_sq_cu;
+            candidate.distance_sq_re = distance_sq_re;
+
+            update_if_better(best_candidate, candidate);
+          }
+        }
+        return best_candidate;
+      },
+      [&](const BrushPositionCandidate &a, const BrushPositionCandidate &b) {
+        ret

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list