[Bf-blender-cvs] [ac45540a348] master: Curves: add brush to add curves on surface

Jacques Lucke noreply at git.blender.org
Wed Mar 2 17:20:53 CET 2022


Commit: ac45540a348ec8662e4e27002c64176c402fe549
Author: Jacques Lucke
Date:   Wed Mar 2 17:15:44 2022 +0100
Branches: master
https://developer.blender.org/rBac45540a348ec8662e4e27002c64176c402fe549

Curves: add brush to add curves on surface

This adds a prototype for the first brush that can add new curves by
painting on a surface. Note that this can only be used when the curves
object has a surface object set in the properties panel.

The brush can take minimum distance into account. This allows
distributing curves with a somewhat consistent density.

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

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

M	release/scripts/startup/bl_ui/space_view3d.py
M	source/blender/blenlib/BLI_hash.hh
M	source/blender/editors/sculpt_paint/curves_sculpt_ops.cc
M	source/blender/makesdna/DNA_brush_enums.h
M	source/blender/makesdna/DNA_scene_types.h
M	source/blender/makesrna/intern/rna_brush.c
M	source/blender/makesrna/intern/rna_sculpt_paint.c

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

diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index cd0306d31fd..d6aea8e2d89 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -473,7 +473,8 @@ class _draw_tool_settings_context_mode:
         if (tool is None) or (not tool.has_datablock):
             return False
 
-        paint = context.tool_settings.curves_sculpt
+        tool_settings = context.tool_settings
+        paint = tool_settings.curves_sculpt
         layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
 
         brush = paint.brush
@@ -500,6 +501,10 @@ class _draw_tool_settings_context_mode:
             header=True
         )
 
+        if brush.curves_sculpt_tool == "TEST3":
+            layout.prop(tool_settings.curves_sculpt, "distance")
+
+
 
 class VIEW3D_HT_header(Header):
     bl_space_type = 'VIEW_3D'
diff --git a/source/blender/blenlib/BLI_hash.hh b/source/blender/blenlib/BLI_hash.hh
index 25841180fcf..25d7cd6aaf8 100644
--- a/source/blender/blenlib/BLI_hash.hh
+++ b/source/blender/blenlib/BLI_hash.hh
@@ -148,6 +148,13 @@ template<> struct DefaultHash<float> {
   }
 };
 
+template<> struct DefaultHash<double> {
+  uint64_t operator()(double value) const
+  {
+    return *reinterpret_cast<uint64_t *>(&value);
+  }
+};
+
 template<> struct DefaultHash<bool> {
   uint64_t operator()(bool value) const
   {
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc
index 936226a03ed..12c03804981 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc
@@ -2,9 +2,14 @@
 
 #include "BLI_utildefines.h"
 
+#include "BKE_attribute_math.hh"
 #include "BKE_brush.h"
+#include "BKE_bvhutils.h"
 #include "BKE_context.h"
 #include "BKE_curves.hh"
+#include "BKE_lib_id.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_runtime.h"
 #include "BKE_paint.h"
 
 #include "WM_api.h"
@@ -19,12 +24,18 @@
 
 #include "DNA_brush_types.h"
 #include "DNA_curves_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
 #include "DNA_screen_types.h"
 
 #include "RNA_access.h"
 
 #include "BLI_index_mask_ops.hh"
+#include "BLI_kdtree.h"
 #include "BLI_math_vector.hh"
+#include "BLI_rand.hh"
+
+#include "PIL_time.h"
 
 #include "curves_sculpt_intern.h"
 #include "paint_intern.h"
@@ -79,7 +90,7 @@ class DeleteOperation : public CurvesSculptStrokeOperation {
   float2 last_mouse_position_;
 
  public:
-  void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension)
+  void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override
   {
     Scene &scene = *CTX_data_scene(C);
     Object &object = *CTX_data_active_object(C);
@@ -146,7 +157,7 @@ class MoveOperation : public CurvesSculptStrokeOperation {
   float2 last_mouse_position_;
 
  public:
-  void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension)
+  void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override
   {
     Scene &scene = *CTX_data_scene(C);
     Object &object = *CTX_data_active_object(C);
@@ -201,6 +212,438 @@ class MoveOperation : public CurvesSculptStrokeOperation {
   }
 };
 
+class AddOperation : public CurvesSculptStrokeOperation {
+ private:
+  /** Contains the root points of the curves that existed before this operation started. */
+  KDTree_3d *old_kdtree_ = nullptr;
+  /** Number of points in the kdtree above. */
+  int old_kdtree_size_ = 0;
+
+  /**
+   * Indicates that the corresponding curve has already been created and can't be changed by this
+   * operation anymore.
+   */
+  static constexpr int ExistsAlreadyIndex = INT32_MAX;
+
+  struct NewPointsData {
+    Vector<float3> bary_coords;
+    Vector<int> looptri_indices;
+    Vector<float3> positions;
+    Vector<float3> normals;
+  };
+
+ public:
+  ~AddOperation()
+  {
+    if (old_kdtree_ != nullptr) {
+      BLI_kdtree_3d_free(old_kdtree_);
+    }
+  }
+
+  void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override
+  {
+    Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C);
+    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);
+
+    Curves &curves_id = *static_cast<Curves *>(object.data);
+    CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
+
+    if (curves_id.surface == nullptr || curves_id.surface->type != OB_MESH) {
+      return;
+    }
+
+    const Object &surface_ob = *curves_id.surface;
+    const Mesh &surface = *static_cast<const Mesh *>(surface_ob.data);
+    const float4x4 surface_ob_mat = surface_ob.obmat;
+    const float4x4 surface_ob_imat = surface_ob_mat.inverted();
+
+    ToolSettings &tool_settings = *scene.toolsettings;
+    CurvesSculpt &curves_sculpt = *tool_settings.curves_sculpt;
+    Brush &brush = *BKE_paint_brush(&curves_sculpt.paint);
+    const float brush_radius_screen = BKE_brush_size_get(&scene, &brush);
+    const float strength = BKE_brush_alpha_get(&scene, &brush);
+    const float minimum_distance = curves_sculpt.distance;
+
+    /* This is the main ray that is used to determine the brush position in 3D space. */
+    float3 ray_start, ray_end;
+    ED_view3d_win_to_segment_clipped(
+        &depsgraph, region, v3d, stroke_extension.mouse_position, ray_start, ray_end, true);
+    ray_start = surface_ob_imat * ray_start;
+    ray_end = surface_ob_imat * ray_end;
+    const float3 ray_direction = math::normalize(ray_end - ray_start);
+
+    /* This ray is used to determine the brush radius in 3d space. */
+    float3 offset_ray_start, offset_ray_end;
+    ED_view3d_win_to_segment_clipped(&depsgraph,
+                                     region,
+                                     v3d,
+                                     stroke_extension.mouse_position +
+                                         float2(0, brush_radius_screen),
+                                     offset_ray_start,
+                                     offset_ray_end,
+                                     true);
+    offset_ray_start = surface_ob_imat * offset_ray_start;
+    offset_ray_end = surface_ob_imat * offset_ray_end;
+
+    float4x4 ob_imat;
+    invert_m4_m4(ob_imat.values, object.obmat);
+
+    const float4x4 transform = ob_imat * surface_ob_mat;
+
+    BVHTreeFromMesh bvhtree;
+    BKE_bvhtree_from_mesh_get(&bvhtree, &surface, BVHTREE_FROM_LOOPTRI, 2);
+
+    /* Do a raycast against the surface object to find the brush position. */
+    BVHTreeRayHit ray_hit;
+    ray_hit.dist = FLT_MAX;
+    ray_hit.index = -1;
+    BLI_bvhtree_ray_cast(bvhtree.tree,
+                         ray_start,
+                         ray_direction,
+                         0.0f,
+                         &ray_hit,
+                         bvhtree.raycast_callback,
+                         &bvhtree);
+
+    if (ray_hit.index == -1) {
+      /* The ray did not hit the surface. */
+      free_bvhtree_from_mesh(&bvhtree);
+      return;
+    }
+    /* Brush position in the space of the surface object. */
+    const float3 brush_pos_3d_surface = ray_hit.co;
+    const float brush_radius_3d_surface = dist_to_line_v3(
+        brush_pos_3d_surface, offset_ray_start, offset_ray_end);
+
+    /* Brush position in the space of the curves object. */
+    const float3 brush_pos_3d_curves = transform * brush_pos_3d_surface;
+    const float brush_radius_3d_curves = dist_to_line_v3(
+        brush_pos_3d_curves, transform * offset_ray_start, transform * offset_ray_end);
+
+    Vector<int> looptri_indices = this->find_looptri_indices_to_consider(
+        bvhtree, brush_pos_3d_surface, brush_radius_3d_surface);
+
+    free_bvhtree_from_mesh(&bvhtree);
+
+    if (old_kdtree_ == nullptr && minimum_distance > 0.0f) {
+      old_kdtree_ = this->kdtree_from_curve_roots_and_positions(curves, curves.curves_range(), {});
+      old_kdtree_size_ = curves.curves_size();
+    }
+
+    float density;
+    if (minimum_distance > 0.0f) {
+      /* Estimate the sampling density based on the target minimum distance. */
+      density = strength * pow2f(1.0f / minimum_distance);
+    }
+    else {
+      /* Sample a somewhat constant amount of points based on the strength. */
+      const float brush_circle_area_3d = M_PI * pow2f(brush_radius_3d_curves);
+      density = strength * 100.0f / brush_circle_area_3d;
+    }
+
+    NewPointsData new_points = this->sample_new_points(density,
+                                                       minimum_distance,
+                                                       brush_radius_3d_curves,
+                                                       brush_pos_3d_curves,
+                                                       looptri_indices,
+                                                       transform,
+                                                       surface);
+    if (minimum_distance > 0.0f) {
+      this->eliminate_too_close_points(new_points, curves, minimum_distance);
+    }
+    this->insert_new_curves(new_points, curves);
+
+    DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY);
+    ED_region_tag_redraw(region);
+  }
+
+ private:
+  Vector<int> find_looptri_indices_to_consider(BVHTreeFromMesh &bvhtree,
+                                               const float3 &brush_pos,
+                                               const float brush_radius_3d)
+  {
+    Vector<int> looptri_indices;
+
+    struct RangeQueryUserData {
+      Vector<int> &indices;
+    } range_query_user_data = {looptri_indices};
+
+    BLI_bvhtree_range_query(
+        bvhtree.tree,
+        brush_pos,
+        brush_radius_3d,
+        [](void *userdata, int index, const float co[3], float dist_sq) {
+          UNUSED_VARS(co, dist_sq);
+          RangeQueryUserData &data = *static_cast<RangeQueryUserData *>(userdata);
+          data.indices.append(index);
+        },
+        &range_query_user_data);
+
+    return looptri_indices;
+  }
+
+  KDTree_3d *kdtree_from_curve_roots_and_positions(const CurvesGeometry &cu

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list