diff options
author | Jacques Lucke <jacques@blender.org> | 2022-06-17 16:31:07 +0300 |
---|---|---|
committer | Jacques Lucke <jacques@blender.org> | 2022-06-17 16:31:21 +0300 |
commit | 133095fff4347cb4ff74ab5425b6d6581f524218 (patch) | |
tree | a39ee3041fe3c58cf40cddc6105dc6247dd1819a /source/blender/blenkernel | |
parent | 18def163f89374cc0f594c1ab7917c5bbde4f074 (diff) |
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
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r-- | source/blender/blenkernel/BKE_mesh_sample.hh | 59 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/mesh_sample.cc | 172 |
2 files changed, 231 insertions, 0 deletions
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, + const MLoopTri &looptri, + const float3 &position) +{ + 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; + float3 bary_coords; + interp_weights_tri_v3(bary_coords, v0, v1, v2, position); + return bary_coords; +} + } // namespace blender::bke::mesh_surface_sample |