[Bf-blender-cvs] [133095fff43] master: Curves: refactor Add brush

Jacques Lucke noreply at git.blender.org
Fri Jun 17 15:31:40 CEST 2022


Commit: 133095fff4347cb4ff74ab5425b6d6581f524218
Author: Jacques Lucke
Date:   Fri Jun 17 15:31:07 2022 +0200
Branches: master
https://developer.blender.org/rB133095fff4347cb4ff74ab5425b6d6581f524218

Curves: refactor Add brush

This splits out the code that samples points on a surface and the
code that initializes new curves. This code will be reused by D15134.

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

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

M	source/blender/blenkernel/BKE_mesh_sample.hh
M	source/blender/blenkernel/intern/mesh_sample.cc
M	source/blender/editors/sculpt_paint/curves_sculpt_add.cc
M	source/blender/editors/sculpt_paint/curves_sculpt_brush.cc
M	source/blender/editors/sculpt_paint/curves_sculpt_intern.hh
M	source/blender/geometry/CMakeLists.txt
A	source/blender/geometry/GEO_add_curves_on_mesh.hh
A	source/blender/geometry/intern/add_curves_on_mesh.cc

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

diff --git a/source/blender/blenkernel/BKE_mesh_sample.hh b/source/blender/blenkernel/BKE_mesh_sample.hh
index 37c2ad7d3cb..f47201e89cc 100644
--- a/source/blender/blenkernel/BKE_mesh_sample.hh
+++ b/source/blender/blenkernel/BKE_mesh_sample.hh
@@ -6,12 +6,20 @@
  * \ingroup bke
  */
 
+#include "BLI_function_ref.hh"
 #include "BLI_generic_virtual_array.hh"
 #include "BLI_math_vec_types.hh"
 
+#include "DNA_meshdata_types.h"
+
 #include "BKE_attribute.h"
 
 struct Mesh;
+struct BVHTreeFromMesh;
+
+namespace blender {
+struct RandomNumberGenerator;
+}
 
 namespace blender::bke {
 struct ReadAttributeLookup;
@@ -82,4 +90,55 @@ class MeshAttributeInterpolator {
   Span<float3> ensure_nearest_weights();
 };
 
+/**
+ * Find randomly distributed points on the surface of a mesh within a 3D sphere. This does not
+ * sample an exact number of points because it comes with extra overhead to avoid bias that is only
+ * required in some cases. If an exact number of points is required, that has to be implemented at
+ * a higher level.
+ *
+ * \param approximate_density: Roughly the number of points per unit of area.
+ * \return The number of added points.
+ */
+int sample_surface_points_spherical(RandomNumberGenerator &rng,
+                                    const Mesh &mesh,
+                                    Span<int> looptri_indices_to_sample,
+                                    const float3 &sample_pos,
+                                    float sample_radius,
+                                    float approximate_density,
+                                    Vector<float3> &r_bary_coords,
+                                    Vector<int> &r_looptri_indices,
+                                    Vector<float3> &r_positions);
+
+/**
+ * Find randomly distributed points on the surface of a mesh within a circle that is projected on
+ * the mesh. This does not result in an exact number of points because that would come with extra
+ * overhead and is not always possible. If an exact number of points is required, that has to be
+ * implemented at a higher level.
+ *
+ * \param region_position_to_ray: Function that converts a 2D position into a 3D ray that is used
+ *   to find positions on the mesh.
+ * \param mesh_bvhtree: BVH tree of the triangles in the mesh. Passed in so that it does not have
+ *   to be retrieved again.
+ * \param tries_num: Number of 2d positions that are sampled. The maximum
+ *   number of new samples.
+ * \return The number of added points.
+ */
+int sample_surface_points_projected(
+    RandomNumberGenerator &rng,
+    const Mesh &mesh,
+    BVHTreeFromMesh &mesh_bvhtree,
+    const float2 &sample_pos_re,
+    float sample_radius_re,
+    FunctionRef<void(const float2 &pos_re, float3 &r_start, float3 &r_end)> region_position_to_ray,
+    bool front_face_only,
+    int tries_num,
+    int max_points,
+    Vector<float3> &r_bary_coords,
+    Vector<int> &r_looptri_indices,
+    Vector<float3> &r_positions);
+
+float3 compute_bary_coord_in_triangle(const Mesh &mesh,
+                                      const MLoopTri &looptri,
+                                      const float3 &position);
+
 }  // namespace blender::bke::mesh_surface_sample
diff --git a/source/blender/blenkernel/intern/mesh_sample.cc b/source/blender/blenkernel/intern/mesh_sample.cc
index 7595c08a208..106c4c610ba 100644
--- a/source/blender/blenkernel/intern/mesh_sample.cc
+++ b/source/blender/blenkernel/intern/mesh_sample.cc
@@ -2,12 +2,15 @@
 
 #include "BKE_attribute_access.hh"
 #include "BKE_attribute_math.hh"
+#include "BKE_bvhutils.h"
 #include "BKE_mesh_runtime.h"
 #include "BKE_mesh_sample.hh"
 
 #include "DNA_mesh_types.h"
 #include "DNA_meshdata_types.h"
 
+#include "BLI_rand.hh"
+
 namespace blender::bke::mesh_surface_sample {
 
 template<typename T>
@@ -259,4 +262,173 @@ void MeshAttributeInterpolator::sample_attribute(const ReadAttributeLookup &src_
   }
 }
 
+int sample_surface_points_spherical(RandomNumberGenerator &rng,
+                                    const Mesh &mesh,
+                                    const Span<int> looptri_indices_to_sample,
+                                    const float3 &sample_pos,
+                                    const float sample_radius,
+                                    const float approximate_density,
+                                    Vector<float3> &r_bary_coords,
+                                    Vector<int> &r_looptri_indices,
+                                    Vector<float3> &r_positions)
+{
+  const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh),
+                                BKE_mesh_runtime_looptri_len(&mesh)};
+
+  const float sample_radius_sq = pow2f(sample_radius);
+  const float sample_plane_area = M_PI * sample_radius_sq;
+  /* Used for switching between two triangle sampling strategies. */
+  const float area_threshold = sample_plane_area;
+
+  const int old_num = r_bary_coords.size();
+
+  for (const int looptri_index : looptri_indices_to_sample) {
+    const MLoopTri &looptri = looptris[looptri_index];
+
+    const float3 &v0 = mesh.mvert[mesh.mloop[looptri.tri[0]].v].co;
+    const float3 &v1 = mesh.mvert[mesh.mloop[looptri.tri[1]].v].co;
+    const float3 &v2 = mesh.mvert[mesh.mloop[looptri.tri[2]].v].co;
+
+    const float looptri_area = area_tri_v3(v0, v1, v2);
+
+    if (looptri_area < area_threshold) {
+      /* The triangle is small compared to the sample radius. Sample by generating random
+       * barycentric coordinates. */
+      const int amount = rng.round_probabilistic(approximate_density * looptri_area);
+      for ([[maybe_unused]] const int i : IndexRange(amount)) {
+        const float3 bary_coord = rng.get_barycentric_coordinates();
+        const float3 point_pos = attribute_math::mix3(bary_coord, v0, v1, v2);
+        const float dist_to_sample_sq = math::distance_squared(point_pos, sample_pos);
+        if (dist_to_sample_sq > sample_radius_sq) {
+          continue;
+        }
+
+        r_bary_coords.append(bary_coord);
+        r_looptri_indices.append(looptri_index);
+        r_positions.append(point_pos);
+      }
+    }
+    else {
+      /* The triangle is large compared to the sample radius. Sample by generating random points
+       * on the triangle plane within the sample radius. */
+      float3 normal;
+      normal_tri_v3(normal, v0, v1, v2);
+
+      float3 sample_pos_proj = sample_pos;
+      project_v3_plane(sample_pos_proj, normal, v0);
+
+      const float proj_distance_sq = math::distance_squared(sample_pos_proj, sample_pos);
+      const float sample_radius_factor_sq = 1.0f -
+                                            std::min(1.0f, proj_distance_sq / sample_radius_sq);
+      const float radius_proj_sq = sample_radius_sq * sample_radius_factor_sq;
+      const float radius_proj = std::sqrt(radius_proj_sq);
+      const float circle_area = M_PI * radius_proj;
+
+      const int amount = rng.round_probabilistic(approximate_density * circle_area);
+
+      const float3 axis_1 = math::normalize(v1 - v0) * radius_proj;
+      const float3 axis_2 = math::normalize(math::cross(axis_1, math::cross(axis_1, v2 - v0))) *
+                            radius_proj;
+
+      for ([[maybe_unused]] const int i : IndexRange(amount)) {
+        const float r = std::sqrt(rng.get_float());
+        const float angle = rng.get_float() * 2.0f * M_PI;
+        const float x = r * std::cos(angle);
+        const float y = r * std::sin(angle);
+        const float3 point_pos = sample_pos_proj + axis_1 * x + axis_2 * y;
+        if (!isect_point_tri_prism_v3(point_pos, v0, v1, v2)) {
+          /* Sampled point is not in the triangle. */
+          continue;
+        }
+
+        float3 bary_coord;
+        interp_weights_tri_v3(bary_coord, v0, v1, v2, point_pos);
+
+        r_bary_coords.append(bary_coord);
+        r_looptri_indices.append(looptri_index);
+        r_positions.append(point_pos);
+      }
+    }
+  }
+  return r_bary_coords.size() - old_num;
+}
+
+int sample_surface_points_projected(
+    RandomNumberGenerator &rng,
+    const Mesh &mesh,
+    BVHTreeFromMesh &mesh_bvhtree,
+    const float2 &sample_pos_re,
+    const float sample_radius_re,
+    const FunctionRef<void(const float2 &pos_re, float3 &r_start, float3 &r_end)>
+        region_position_to_ray,
+    const bool front_face_only,
+    const int tries_num,
+    const int max_points,
+    Vector<float3> &r_bary_coords,
+    Vector<int> &r_looptri_indices,
+    Vector<float3> &r_positions)
+{
+  const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh),
+                                BKE_mesh_runtime_looptri_len(&mesh)};
+
+  int point_count = 0;
+  for ([[maybe_unused]] const int _ : IndexRange(tries_num)) {
+    if (point_count == max_points) {
+      break;
+    }
+
+    const float r = sample_radius_re * std::sqrt(rng.get_float());
+    const float angle = rng.get_float() * 2.0f * M_PI;
+    float3 ray_start, ray_end;
+    const float2 pos_re = sample_pos_re + r * float2(std::cos(angle), std::sin(angle));
+    region_position_to_ray(pos_re, ray_start, ray_end);
+    const float3 ray_direction = math::normalize(ray_end - ray_start);
+
+    BVHTreeRayHit ray_hit;
+    ray_hit.dist = FLT_MAX;
+    ray_hit.index = -1;
+    BLI_bvhtree_ray_cast(mesh_bvhtree.tree,
+                         ray_start,
+                         ray_direction,
+                         0.0f,
+                         &ray_hit,
+                         mesh_bvhtree.raycast_callback,
+                         &mesh_bvhtree);
+
+    if (ray_hit.index == -1) {
+      continue;
+    }
+
+    if (front_face_only) {
+      const float3 normal = ray_hit.no;
+      if (math::dot(ray_direction, normal) >= 0.0f) {
+        continue;
+      }
+    }
+
+    const int looptri_index = ray_hit.index;
+    const float3 pos = ray_hit.co;
+
+    const float3 bary_coords = compute_bary_coord_in_triangle(mesh, looptris[looptri_index], pos);
+
+    r_positions.append(pos);
+    r_bary_coords.append(bary_coords);
+    r_looptri_indices.append(looptri_index);
+    point_count++;
+  }
+  return point_count;
+}
+
+float3 compute_bary_coord_in_triangle(const Mesh &mesh,
+                                   

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list