Blender V5.0
mesh_sample.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
6#include "BKE_bvhutils.hh"
7#include "BKE_mesh.hh"
8#include "BKE_mesh_sample.hh"
9
10#include "BLI_math_geom.h"
11#include "BLI_rand.hh"
12
14
15template<typename T>
16BLI_NOINLINE static void sample_point_attribute(const Span<int> corner_verts,
17 const Span<int3> corner_tris,
18 const Span<int> tri_indices,
19 const Span<float3> bary_coords,
20 const VArray<T> &src,
21 const IndexMask &mask,
22 const MutableSpan<T> dst)
23{
24 mask.foreach_index([&](const int i) {
25 const int3 &tri = corner_tris[tri_indices[i]];
26 dst[i] = attribute_math::mix3(bary_coords[i],
27 src[corner_verts[tri[0]]],
28 src[corner_verts[tri[1]]],
29 src[corner_verts[tri[2]]]);
30 });
31}
32
33void sample_point_normals(const Span<int> corner_verts,
34 const Span<int3> corner_tris,
35 const Span<int> tri_indices,
36 const Span<float3> bary_coords,
37 const Span<float3> src,
38 const IndexMask mask,
39 const MutableSpan<float3> dst)
40{
41 mask.foreach_index([&](const int i) {
42 const int3 &tri = corner_tris[tri_indices[i]];
43 const float3 value = attribute_math::mix3(bary_coords[i],
44 src[corner_verts[tri[0]]],
45 src[corner_verts[tri[1]]],
46 src[corner_verts[tri[2]]]);
47 dst[i] = math::normalize(value);
48 });
49}
50
51void sample_point_attribute(const Span<int> corner_verts,
52 const Span<int3> corner_tris,
53 const Span<int> tri_indices,
54 const Span<float3> bary_coords,
55 const GVArray &src,
56 const IndexMask &mask,
57 const GMutableSpan dst)
58{
59 BLI_assert(src.type() == dst.type());
60
61 const CPPType &type = src.type();
62 attribute_math::convert_to_static_type(type, [&](auto dummy) {
63 using T = decltype(dummy);
65 corner_verts, corner_tris, tri_indices, bary_coords, src.typed<T>(), mask, dst.typed<T>());
66 });
67}
68
69template<typename T, bool check_indices = false>
70BLI_NOINLINE static void sample_corner_attribute(const Span<int3> corner_tris,
71 const Span<int> tri_indices,
72 const Span<float3> bary_coords,
73 const VArray<T> &src,
74 const IndexMask &mask,
75 const MutableSpan<T> dst)
76{
77 mask.foreach_index([&](const int i) {
78 if constexpr (check_indices) {
79 if (tri_indices[i] == -1) {
80 dst[i] = {};
81 return;
82 }
83 }
84 const int3 &tri = corner_tris[tri_indices[i]];
85 dst[i] = sample_corner_attribute_with_bary_coords(bary_coords[i], tri, src);
86 });
87}
88
89void sample_corner_normals(const Span<int3> corner_tris,
90 const Span<int> tri_indices,
91 const Span<float3> bary_coords,
92 const Span<float3> src,
93 const IndexMask &mask,
94 const MutableSpan<float3> dst)
95{
96 mask.foreach_index([&](const int i) {
97 const int3 &tri = corner_tris[tri_indices[i]];
98 const float3 value = sample_corner_attribute_with_bary_coords(bary_coords[i], tri, src);
99 dst[i] = math::normalize(value);
100 });
101}
102
103void sample_corner_attribute(const Span<int3> corner_tris,
104 const Span<int> tri_indices,
105 const Span<float3> bary_coords,
106 const GVArray &src,
107 const IndexMask &mask,
108 const GMutableSpan dst)
109{
110 BLI_assert(src.type() == dst.type());
111
112 const CPPType &type = src.type();
113 attribute_math::convert_to_static_type(type, [&](auto dummy) {
114 using T = decltype(dummy);
116 corner_tris, tri_indices, bary_coords, src.typed<T>(), mask, dst.typed<T>());
117 });
118}
119
120template<typename T>
121void sample_face_attribute(const Span<int> tri_faces,
122 const Span<int> tri_indices,
123 const VArray<T> &src,
124 const IndexMask &mask,
125 const MutableSpan<T> dst)
126{
127 mask.foreach_index([&](const int i) {
128 const int tri_index = tri_indices[i];
129 const int face_index = tri_faces[tri_index];
130 dst[i] = src[face_index];
131 });
132}
133
134void sample_face_attribute(const Span<int> corner_tri_faces,
135 const Span<int> tri_indices,
136 const GVArray &src,
137 const IndexMask &mask,
138 const GMutableSpan dst)
139{
140 BLI_assert(src.type() == dst.type());
141
142 const CPPType &type = src.type();
143 attribute_math::convert_to_static_type(type, [&](auto dummy) {
144 using T = decltype(dummy);
145 sample_face_attribute<T>(corner_tri_faces, tri_indices, src.typed<T>(), mask, dst.typed<T>());
146 });
147}
148
149template<bool check_indices = false>
150static void sample_barycentric_weights(const Span<float3> vert_positions,
151 const Span<int> corner_verts,
152 const Span<int3> corner_tris,
153 const Span<int> tri_indices,
154 const Span<float3> sample_positions,
155 const IndexMask &mask,
156 MutableSpan<float3> bary_coords)
157{
158 mask.foreach_index([&](const int i) {
159 if constexpr (check_indices) {
160 if (tri_indices[i] == -1) {
161 bary_coords[i] = {};
162 return;
163 }
164 }
165 const int3 &tri = corner_tris[tri_indices[i]];
166 bary_coords[i] = compute_bary_coord_in_triangle(
167 vert_positions, corner_verts, tri, sample_positions[i]);
168 });
169}
170
171template<bool check_indices = false>
172static void sample_nearest_weights(const Span<float3> vert_positions,
173 const Span<int> corner_verts,
174 const Span<int3> corner_tris,
175 const Span<int> tri_indices,
176 const Span<float3> sample_positions,
177 const IndexMask &mask,
178 MutableSpan<float3> bary_coords)
179{
180 mask.foreach_index([&](const int i) {
181 if constexpr (check_indices) {
182 if (tri_indices[i] == -1) {
183 bary_coords[i] = {};
184 return;
185 }
186 }
187 const int3 &tri = corner_tris[tri_indices[i]];
188 const std::array<float, 3> distances{
189 math::distance_squared(sample_positions[i], vert_positions[corner_verts[tri[0]]]),
190 math::distance_squared(sample_positions[i], vert_positions[corner_verts[tri[1]]]),
191 math::distance_squared(sample_positions[i], vert_positions[corner_verts[tri[2]]]),
192 };
193 const int index = std::min_element(distances.begin(), distances.end()) - distances.begin();
194 const std::array<float3, 3> weights{float3(1, 0, 0), float3(0, 1, 0), float3(0, 0, 1)};
195 bary_coords[i] = weights[index];
196 });
197}
198
200 const Mesh &mesh,
201 const Span<int> tris_to_sample,
202 const float3 &sample_pos,
203 const float sample_radius,
204 const float approximate_density,
205 Vector<float3> &r_bary_coords,
206 Vector<int> &r_tri_indices,
207 Vector<float3> &r_positions)
208{
209 const Span<float3> positions = mesh.vert_positions();
210 const Span<int> corner_verts = mesh.corner_verts();
211 const Span<int3> corner_tris = mesh.corner_tris();
212
213 const float sample_radius_sq = pow2f(sample_radius);
214 const float sample_plane_area = M_PI * sample_radius_sq;
215 /* Used for switching between two triangle sampling strategies. */
216 const float area_threshold = sample_plane_area;
217
218 const int old_num = r_bary_coords.size();
219
220 for (const int tri_index : tris_to_sample) {
221 const int3 &tri = corner_tris[tri_index];
222
223 const float3 &v0 = positions[corner_verts[tri[0]]];
224 const float3 &v1 = positions[corner_verts[tri[1]]];
225 const float3 &v2 = positions[corner_verts[tri[2]]];
226
227 const float corner_tri_area = area_tri_v3(v0, v1, v2);
228
229 if (corner_tri_area < area_threshold) {
230 /* The triangle is small compared to the sample radius. Sample by generating random
231 * barycentric coordinates. */
232 const int amount = rng.round_probabilistic(approximate_density * corner_tri_area);
233 for ([[maybe_unused]] const int i : IndexRange(amount)) {
234 const float3 bary_coord = rng.get_barycentric_coordinates();
235 const float3 point_pos = attribute_math::mix3(bary_coord, v0, v1, v2);
236 const float dist_to_sample_sq = math::distance_squared(point_pos, sample_pos);
237 if (dist_to_sample_sq > sample_radius_sq) {
238 continue;
239 }
240
241 r_bary_coords.append(bary_coord);
242 r_tri_indices.append(tri_index);
243 r_positions.append(point_pos);
244 }
245 }
246 else {
247 /* The triangle is large compared to the sample radius. Sample by generating random points
248 * on the triangle plane within the sample radius. */
249 float3 normal;
250 normal_tri_v3(normal, v0, v1, v2);
251
252 float3 sample_pos_proj = sample_pos;
253 project_v3_plane(sample_pos_proj, normal, v0);
254
255 const float proj_distance_sq = math::distance_squared(sample_pos_proj, sample_pos);
256 const float sample_radius_factor_sq = 1.0f -
257 std::min(1.0f, proj_distance_sq / sample_radius_sq);
258 const float radius_proj_sq = sample_radius_sq * sample_radius_factor_sq;
259 const float radius_proj = std::sqrt(radius_proj_sq);
260 const float circle_area = M_PI * radius_proj_sq;
261
262 const int amount = rng.round_probabilistic(approximate_density * circle_area);
263
264 const float3 axis_1 = math::normalize(v1 - v0) * radius_proj;
265 const float3 axis_2 = math::normalize(math::cross(axis_1, math::cross(axis_1, v2 - v0))) *
266 radius_proj;
267
268 for ([[maybe_unused]] const int i : IndexRange(amount)) {
269 const float r = std::sqrt(rng.get_float());
270 const float angle = rng.get_float() * 2.0f * M_PI;
271 const float x = r * std::cos(angle);
272 const float y = r * std::sin(angle);
273 const float3 point_pos = sample_pos_proj + axis_1 * x + axis_2 * y;
274 if (!isect_point_tri_prism_v3(point_pos, v0, v1, v2)) {
275 /* Sampled point is not in the triangle. */
276 continue;
277 }
278
279 float3 bary_coord;
280 interp_weights_tri_v3(bary_coord, v0, v1, v2, point_pos);
281
282 r_bary_coords.append(bary_coord);
283 r_tri_indices.append(tri_index);
284 r_positions.append(point_pos);
285 }
286 }
287 }
288 return r_bary_coords.size() - old_num;
289}
290
293 const Mesh &mesh,
294 BVHTreeFromMesh &mesh_bvhtree,
295 const float2 &sample_pos_re,
296 const float sample_radius_re,
297 const FunctionRef<void(const float2 &pos_re, float3 &r_start, float3 &r_end)>
298 region_position_to_ray,
299 const bool front_face_only,
300 const int tries_num,
301 const int max_points,
302 Vector<float3> &r_bary_coords,
303 Vector<int> &r_tri_indices,
304 Vector<float3> &r_positions)
305{
306 const Span<float3> positions = mesh.vert_positions();
307 const Span<int> corner_verts = mesh.corner_verts();
308 const Span<int3> corner_tris = mesh.corner_tris();
309
310 int point_count = 0;
311 for ([[maybe_unused]] const int _ : IndexRange(tries_num)) {
312 if (point_count == max_points) {
313 break;
314 }
315
316 const float r = sample_radius_re * std::sqrt(rng.get_float());
317 const float angle = rng.get_float() * 2.0f * M_PI;
318 float3 ray_start, ray_end;
319 const float2 pos_re = sample_pos_re + r * float2(std::cos(angle), std::sin(angle));
320 region_position_to_ray(pos_re, ray_start, ray_end);
321 const float3 ray_direction = math::normalize(ray_end - ray_start);
322
323 BVHTreeRayHit ray_hit;
324 ray_hit.dist = FLT_MAX;
325 ray_hit.index = -1;
326 BLI_bvhtree_ray_cast(mesh_bvhtree.tree,
327 ray_start,
328 ray_direction,
329 0.0f,
330 &ray_hit,
331 mesh_bvhtree.raycast_callback,
332 &mesh_bvhtree);
333
334 if (ray_hit.index == -1) {
335 continue;
336 }
337
338 if (front_face_only) {
339 const float3 normal = ray_hit.no;
340 if (math::dot(ray_direction, normal) >= 0.0f) {
341 continue;
342 }
343 }
344
345 const int tri_index = ray_hit.index;
346 const float3 pos = ray_hit.co;
347
348 const float3 bary_coords = compute_bary_coord_in_triangle(
349 positions, corner_verts, corner_tris[tri_index], pos);
350
351 r_positions.append(pos);
352 r_bary_coords.append(bary_coords);
353 r_tri_indices.append(tri_index);
354 point_count++;
355 }
356 return point_count;
357}
358
360 const Span<int> corner_verts,
361 const int3 &tri,
362 const float3 &position)
363{
364 const float3 &v0 = vert_positions[corner_verts[tri[0]]];
365 const float3 &v1 = vert_positions[corner_verts[tri[1]]];
366 const float3 &v2 = vert_positions[corner_verts[tri[2]]];
367 float3 bary_coords;
368 interp_weights_tri_v3(bary_coords, v0, v1, v2, position);
369 return bary_coords;
370}
371
373 : source_(std::move(geometry))
374{
375 source_.ensure_owns_direct_data();
376 static const mf::Signature signature = []() {
377 mf::Signature signature;
378 mf::SignatureBuilder builder{"Bary Weight from Position", signature};
379 builder.single_input<float3>("Position");
380 builder.single_input<int>("Triangle Index");
381 builder.single_output<float3>("Barycentric Weight");
382 return signature;
383 }();
384 this->set_signature(&signature);
385 const Mesh &mesh = *source_.get_mesh();
386 vert_positions_ = mesh.vert_positions();
387 corner_verts_ = mesh.corner_verts();
388 corner_tris_ = mesh.corner_tris();
389}
390
392 mf::Params params,
393 mf::Context /*context*/) const
394{
395 const VArraySpan<float3> sample_positions = params.readonly_single_input<float3>(0, "Position");
396 const VArraySpan<int> triangle_indices = params.readonly_single_input<int>(1, "Triangle Index");
397 MutableSpan<float3> bary_weights = params.uninitialized_single_output<float3>(
398 2, "Barycentric Weight");
399 sample_barycentric_weights<true>(vert_positions_,
400 corner_verts_,
401 corner_tris_,
402 triangle_indices,
403 sample_positions,
404 mask,
405 bary_weights);
406}
407
409 : source_(std::move(geometry))
410{
411 source_.ensure_owns_direct_data();
412 static const mf::Signature signature = []() {
413 mf::Signature signature;
414 mf::SignatureBuilder builder{"Nearest Weight from Position", signature};
415 builder.single_input<float3>("Position");
416 builder.single_input<int>("Triangle Index");
417 builder.single_output<float3>("Barycentric Weight");
418 return signature;
419 }();
420 this->set_signature(&signature);
421 const Mesh &mesh = *source_.get_mesh();
422 vert_positions_ = mesh.vert_positions();
423 corner_verts_ = mesh.corner_verts();
424 corner_tris_ = mesh.corner_tris();
425}
426
428 mf::Params params,
429 mf::Context /*context*/) const
430{
431 const VArraySpan<float3> sample_positions = params.readonly_single_input<float3>(0, "Position");
432 const VArraySpan<int> triangle_indices = params.readonly_single_input<int>(1, "Triangle Index");
433 MutableSpan<float3> bary_weights = params.uninitialized_single_output<float3>(
434 2, "Barycentric Weight");
435 sample_nearest_weights<true>(vert_positions_,
436 corner_verts_,
437 corner_tris_,
438 triangle_indices,
439 sample_positions,
440 mask,
441 bary_weights);
442}
443
445 : source_(std::move(geometry))
446{
447 source_.ensure_owns_direct_data();
448 this->evaluate_source(std::move(src_field));
449 mf::SignatureBuilder builder{"Sample Barycentric Triangles", signature_};
450 builder.single_input<int>("Triangle Index");
451 builder.single_input<float3>("Barycentric Weight");
452 builder.single_output("Value", source_data_->type());
453 this->set_signature(&signature_);
454}
455
457 mf::Params params,
458 mf::Context /*context*/) const
459{
460 const VArraySpan<int> triangle_indices = params.readonly_single_input<int>(0, "Triangle Index");
461 const VArraySpan<float3> bary_weights = params.readonly_single_input<float3>(
462 1, "Barycentric Weight");
463 GMutableSpan dst = params.uninitialized_single_output(2, "Value");
464 attribute_math::convert_to_static_type(dst.type(), [&](auto dummy) {
465 using T = decltype(dummy);
466 sample_corner_attribute<T, true>(corner_tris_,
467 triangle_indices,
468 bary_weights,
469 source_data_->typed<T>(),
470 mask,
471 dst.typed<T>());
472 });
473}
474
475void BaryWeightSampleFn::evaluate_source(fn::GField src_field)
476{
477 const Mesh &mesh = *source_.get_mesh();
478 corner_tris_ = mesh.corner_tris();
479 /* Use the most complex domain for now, ensuring no information is lost. In the future, it should
480 * be possible to use the most complex domain required by the field inputs, to simplify sampling
481 * and avoid domain conversions. */
482 domain_ = AttrDomain::Corner;
483 source_context_.emplace(MeshFieldContext(mesh, domain_));
484 const int domain_size = mesh.attributes().domain_size(domain_);
485 source_evaluator_ = std::make_unique<fn::FieldEvaluator>(*source_context_, domain_size);
486 source_evaluator_->add(std::move(src_field));
487 source_evaluator_->evaluate();
488 source_data_ = &source_evaluator_->get_evaluated(0);
489}
490
491} // namespace blender::bke::mesh_surface_sample
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_NOINLINE
int BLI_bvhtree_ray_cast(const BVHTree *tree, const float co[3], const float dir[3], float radius, BVHTreeRayHit *hit, BVHTree_RayCastCallback callback, void *userdata)
MINLINE float pow2f(float x)
#define M_PI
void interp_weights_tri_v3(float w[3], const float v1[3], const float v2[3], const float v3[3], const float co[3])
float area_tri_v3(const float v1[3], const float v2[3], const float v3[3])
Definition math_geom.cc:100
float normal_tri_v3(float n[3], const float v1[3], const float v2[3], const float v3[3])
Definition math_geom.cc:41
bool isect_point_tri_prism_v3(const float p[3], const float v1[3], const float v2[3], const float v3[3])
void project_v3_plane(float out[3], const float plane_no[3], const float plane_co[3])
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
ATTR_WARN_UNUSED_RESULT const BMVert * v2
const CPPType & type() const
MutableSpan< T > typed() const
int round_probabilistic(float x)
Definition rand.cc:306
constexpr const T * begin() const
Definition BLI_span.hh:220
int64_t size() const
void append(const T &value)
void call(const IndexMask &mask, mf::Params params, mf::Context context) const override
void call(const IndexMask &mask, mf::Params params, mf::Context context) const override
BaryWeightSampleFn(GeometrySet geometry, fn::GField src_field)
void call(const IndexMask &mask, mf::Params params, mf::Context context) const override
void set_signature(const Signature *signature)
uint pos
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
#define T
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
T mix3(const float3 &weights, const T &v0, const T &v1, const T &v2)
int sample_surface_points_projected(RandomNumberGenerator &rng, const Mesh &mesh, bke::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_tri_indices, Vector< float3 > &r_positions)
int sample_surface_points_spherical(RandomNumberGenerator &rng, const Mesh &mesh, Span< int > tris_to_sample, const float3 &sample_pos, float sample_radius, float approximate_density, Vector< float3 > &r_bary_coords, Vector< int > &r_tri_indices, Vector< float3 > &r_positions)
void sample_corner_normals(Span< int3 > corner_tris, Span< int > tri_indices, Span< float3 > bary_coords, Span< float3 > src, const IndexMask &mask, MutableSpan< float3 > dst)
void sample_point_attribute(Span< int > corner_verts, Span< int3 > corner_tris, Span< int > tri_indices, Span< float3 > bary_coords, const GVArray &src, const IndexMask &mask, GMutableSpan dst)
float3 compute_bary_coord_in_triangle(Span< float3 > vert_positions, Span< int > corner_verts, const int3 &corner_tri, const float3 &position)
static void sample_barycentric_weights(const Span< float3 > vert_positions, const Span< int > corner_verts, const Span< int3 > corner_tris, const Span< int > tri_indices, const Span< float3 > sample_positions, const IndexMask &mask, MutableSpan< float3 > bary_coords)
void sample_corner_attribute(Span< int3 > corner_tris, Span< int > tri_indices, Span< float3 > bary_coords, const GVArray &src, const IndexMask &mask, GMutableSpan dst)
static void sample_nearest_weights(const Span< float3 > vert_positions, const Span< int > corner_verts, const Span< int3 > corner_tris, const Span< int > tri_indices, const Span< float3 > sample_positions, const IndexMask &mask, MutableSpan< float3 > bary_coords)
void sample_face_attribute(Span< int > corner_tri_faces, Span< int > tri_indices, const GVArray &src, const IndexMask &mask, GMutableSpan dst)
T sample_corner_attribute_with_bary_coords(const float3 &bary_weights, const int3 &corner_tri, const Span< T > corner_attribute)
void sample_point_normals(Span< int > corner_verts, Span< int3 > corner_tris, Span< int > tri_indices, Span< float3 > bary_coords, Span< float3 > src, IndexMask mask, MutableSpan< float3 > dst)
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
AxisSigned cross(const AxisSigned a, const AxisSigned b)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
T distance_squared(const VecBase< T, Size > &a, const VecBase< T, Size > &b)
VecBase< float, 2 > float2
VecBase< int32_t, 3 > int3
VecBase< float, 3 > float3
#define FLT_MAX
Definition stdcycles.h:14
BVHTree_RayCastCallback raycast_callback
const Mesh * get_mesh() const
i
Definition text_draw.cc:230