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