Blender V5.0
mesh_remap.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
10
11#include "CLG_log.h"
12
13#include "MEM_guardedalloc.h"
14
15#include "BLI_array.hh"
16#include "BLI_astar.h"
17#include "BLI_bit_vector.hh"
18#include "BLI_index_mask.hh"
19#include "BLI_math_geom.h"
20#include "BLI_math_matrix.h"
21#include "BLI_math_solvers.h"
22#include "BLI_math_statistics.h"
23#include "BLI_math_vector.h"
24#include "BLI_memarena.h"
25#include "BLI_polyfill_2d.h"
26#include "BLI_rand.h"
27#include "BLI_utildefines.h"
28
29#include "DNA_modifier_enums.h"
30
31#include "BKE_attribute.hh"
32#include "BKE_bvhutils.hh"
33#include "BKE_customdata.hh"
34#include "BKE_mesh.hh"
35#include "BKE_mesh_mapping.hh"
36#include "BKE_mesh_remap.hh" /* own include */
37
38#include "BLI_strict_flags.h" /* IWYU pragma: keep. Keep last. */
39
40static CLG_LogRef LOG = {"geom.mesh"};
41
42using blender::float3;
43using blender::Span;
44
45/* -------------------------------------------------------------------- */
48
50 BVHTreeNearest *nearest,
51 const float co[3],
52 const float max_dist_sq,
53 float *r_hit_dist)
54{
55 /* Use local proximity heuristics (to reduce the nearest search). */
56 if (nearest->index != -1) {
57 nearest->dist_sq = len_squared_v3v3(co, nearest->co);
58 if (nearest->dist_sq > max_dist_sq) {
59 /* The previous valid index is too far away and not valid for this check. */
60 nearest->dist_sq = max_dist_sq;
61 nearest->index = -1;
62 }
63 }
64 else {
65 nearest->dist_sq = max_dist_sq;
66 }
67 /* Compute and store result. If invalid (-1 index), keep FLT_MAX dist. */
68 BLI_bvhtree_find_nearest(treedata->tree, co, nearest, treedata->nearest_callback, treedata);
69
70 if ((nearest->index != -1) && (nearest->dist_sq <= max_dist_sq)) {
71 *r_hit_dist = sqrtf(nearest->dist_sq);
72 return true;
73 }
74
75 return false;
76}
77
79 BVHTreeRayHit *rayhit,
80 const float co[3],
81 const float no[3],
82 const float radius,
83 const float max_dist,
84 float *r_hit_dist)
85{
86 BVHTreeRayHit rayhit_tmp;
87 float inv_no[3];
88
89 rayhit->index = -1;
90 rayhit->dist = max_dist;
92 treedata->tree, co, no, radius, rayhit, treedata->raycast_callback, treedata);
93
94 /* Also cast in the other direction! */
95 rayhit_tmp = *rayhit;
96 negate_v3_v3(inv_no, no);
98 treedata->tree, co, inv_no, radius, &rayhit_tmp, treedata->raycast_callback, treedata);
99 if (rayhit_tmp.dist < rayhit->dist) {
100 *rayhit = rayhit_tmp;
101 }
102
103 if ((rayhit->index != -1) && (rayhit->dist <= max_dist)) {
104 *r_hit_dist = rayhit->dist;
105 return true;
106 }
107
108 return false;
109}
110
112
113/* -------------------------------------------------------------------- */
118
120 const Span<float3> vert_positions_dst,
121 const Mesh *me_src)
122{
123 BVHTreeNearest nearest = {0};
124 float hit_dist;
125
126 float result = 0.0f;
127 int i;
128
129 blender::bke::BVHTreeFromMesh treedata = me_src->bvh_verts();
130 nearest.index = -1;
131
132 for (i = 0; i < vert_positions_dst.size(); i++) {
133 float tmp_co[3];
134
135 copy_v3_v3(tmp_co, vert_positions_dst[i]);
136
137 /* Convert the vertex to tree coordinates, if needed. */
138 if (space_transform) {
139 BLI_space_transform_apply(space_transform, tmp_co);
140 }
141
142 if (mesh_remap_bvhtree_query_nearest(&treedata, &nearest, tmp_co, FLT_MAX, &hit_dist)) {
143 result += 1.0f / (hit_dist + 1.0f);
144 }
145 else {
146 /* No source for this dest vertex! */
147 result += 1e-18f;
148 }
149 }
150
151 result = (float(vert_positions_dst.size()) / result) - 1.0f;
152
153#if 0
154 printf("%s: Computed difference between meshes (the lower the better): %f\n", __func__, result);
155#endif
156
157 return result;
158}
159
160/* This helper computes the eigen values & vectors for
161 * covariance matrix of all given vertices coordinates.
162 *
163 * Those vectors define the 'average ellipsoid' of the mesh (i.e. the 'best fitting' ellipsoid
164 * containing 50% of the vertices).
165 *
166 * Note that it will not perform fantastic in case two or more eigen values are equal
167 * (e.g. a cylinder or parallelepiped with a square section give two identical eigenvalues,
168 * a sphere or tetrahedron give three identical ones, etc.), since you cannot really define all
169 * axes in those cases. We default to dummy generated orthogonal vectors in this case,
170 * instead of using eigen vectors.
171 */
172static void mesh_calc_eigen_matrix(const Span<float3> positions, float r_mat[4][4])
173{
174 float center[3], covmat[3][3];
175 float eigen_val[3], eigen_vec[3][3];
176
177 bool eigen_success;
178 int i;
179
180 unit_m4(r_mat);
181
182 /* NOTE: here we apply sample correction to covariance matrix, since we consider the vertices
183 * as a sample of the whole 'surface' population of our mesh. */
184 BLI_covariance_m3_v3n(reinterpret_cast<const float (*)[3]>(positions.data()),
185 int(positions.size()),
186 true,
187 covmat,
188 center);
189
190 eigen_success = BLI_eigen_solve_selfadjoint_m3((const float (*)[3])covmat, eigen_val, eigen_vec);
191 BLI_assert(eigen_success);
192 UNUSED_VARS_NDEBUG(eigen_success);
193
194 /* Special handling of cases where some eigen values are (nearly) identical. */
195 if (compare_ff_relative(eigen_val[0], eigen_val[1], FLT_EPSILON, 64)) {
196 if (compare_ff_relative(eigen_val[0], eigen_val[2], FLT_EPSILON, 64)) {
197 /* No preferred direction, that set of vertices has a spherical average,
198 * so we simply returned scaled/translated identity matrix (with no rotation). */
199 unit_m3(eigen_vec);
200 }
201 else {
202 /* Ellipsoid defined by eigen values/vectors has a spherical section,
203 * we can only define one axis from eigen_vec[2] (two others computed eigen vecs
204 * are not so nice for us here, they tend to 'randomly' rotate around valid one).
205 * Note that eigen vectors as returned by BLI_eigen_solve_selfadjoint_m3() are normalized. */
206 ortho_basis_v3v3_v3(eigen_vec[0], eigen_vec[1], eigen_vec[2]);
207 }
208 }
209 else if (compare_ff_relative(eigen_val[0], eigen_val[2], FLT_EPSILON, 64)) {
210 /* Same as above, but with eigen_vec[1] as valid axis. */
211 ortho_basis_v3v3_v3(eigen_vec[2], eigen_vec[0], eigen_vec[1]);
212 }
213 else if (compare_ff_relative(eigen_val[1], eigen_val[2], FLT_EPSILON, 64)) {
214 /* Same as above, but with eigen_vec[0] as valid axis. */
215 ortho_basis_v3v3_v3(eigen_vec[1], eigen_vec[2], eigen_vec[0]);
216 }
217
218 for (i = 0; i < 3; i++) {
219 float evi = eigen_val[i];
220
221 /* Protect against 1D/2D degenerated cases! */
222 /* NOTE: not sure why we need square root of eigen values here
223 * (which are equivalent to singular values, as far as I have understood),
224 * but it seems to heavily reduce (if not completely nullify)
225 * the error due to non-uniform scalings... */
226 evi = (evi < 1e-6f && evi > -1e-6f) ? ((evi < 0.0f) ? -1e-3f : 1e-3f) : sqrtf_signed(evi);
227 mul_v3_fl(eigen_vec[i], evi);
228 }
229
230 copy_m4_m3(r_mat, eigen_vec);
231 copy_v3_v3(r_mat[3], center);
232}
233
235 const Mesh *me_src,
236 SpaceTransform *r_space_transform)
237{
238 /* Note that those are done so that we successively get actual mirror matrix
239 * (by multiplication of columns). */
240 const float mirrors[][3] = {
241 {-1.0f, 1.0f, 1.0f}, /* -> -1, 1, 1 */
242 {1.0f, -1.0f, 1.0f}, /* -> -1, -1, 1 */
243 {1.0f, 1.0f, -1.0f}, /* -> -1, -1, -1 */
244 {1.0f, -1.0f, 1.0f}, /* -> -1, 1, -1 */
245 {-1.0f, 1.0f, 1.0f}, /* -> 1, 1, -1 */
246 {1.0f, -1.0f, 1.0f}, /* -> 1, -1, -1 */
247 {1.0f, 1.0f, -1.0f}, /* -> 1, -1, 1 */
248 {0.0f, 0.0f, 0.0f},
249 };
250 const float (*mirr)[3];
251
252 float mat_src[4][4], mat_dst[4][4], best_mat_dst[4][4];
253 float best_match = FLT_MAX, match;
254
255 const Span<float3> positions_src = me_src->vert_positions();
256 mesh_calc_eigen_matrix(positions_src, mat_src);
257 mesh_calc_eigen_matrix(vert_positions_dst, mat_dst);
258
259 BLI_space_transform_global_from_matrices(r_space_transform, mat_dst, mat_src);
260 match = BKE_mesh_remap_calc_difference_from_mesh(r_space_transform, vert_positions_dst, me_src);
261 best_match = match;
262 copy_m4_m4(best_mat_dst, mat_dst);
263
264 /* And now, we have to check the other sixth possible mirrored versions... */
265 for (mirr = mirrors; (*mirr)[0]; mirr++) {
266 mul_v3_fl(mat_dst[0], (*mirr)[0]);
267 mul_v3_fl(mat_dst[1], (*mirr)[1]);
268 mul_v3_fl(mat_dst[2], (*mirr)[2]);
269
270 BLI_space_transform_global_from_matrices(r_space_transform, mat_dst, mat_src);
272 r_space_transform, vert_positions_dst, me_src);
273 if (match < best_match) {
274 best_match = match;
275 copy_m4_m4(best_mat_dst, mat_dst);
276 }
277 }
278
279 BLI_space_transform_global_from_matrices(r_space_transform, best_mat_dst, mat_src);
280}
281
283
284/* -------------------------------------------------------------------- */
287
288void BKE_mesh_remap_init(MeshPairRemap *map, const int items_num)
289{
291
293
294 map->items = static_cast<MeshPairRemapItem *>(
295 BLI_memarena_alloc(mem, sizeof(*map->items) * size_t(items_num)));
296 map->items_num = items_num;
297
298 map->mem = mem;
299}
300
302{
303 if (map->mem) {
305 }
306
307 map->items_num = 0;
308 map->items = nullptr;
309 map->mem = nullptr;
310}
311
313 const int index,
314 const float /*hit_dist*/,
315 const int island,
316 const int sources_num,
317 const int *indices_src,
318 const float *weights_src)
319{
320 MeshPairRemapItem *mapit = &map->items[index];
321 MemArena *mem = map->mem;
322
323 if (sources_num) {
324 mapit->sources_num = sources_num;
325 mapit->indices_src = static_cast<int *>(
326 BLI_memarena_alloc(mem, sizeof(*mapit->indices_src) * size_t(sources_num)));
327 memcpy(mapit->indices_src, indices_src, sizeof(*mapit->indices_src) * size_t(sources_num));
328 mapit->weights_src = static_cast<float *>(
329 BLI_memarena_alloc(mem, sizeof(*mapit->weights_src) * size_t(sources_num)));
330 memcpy(mapit->weights_src, weights_src, sizeof(*mapit->weights_src) * size_t(sources_num));
331 }
332 else {
333 mapit->sources_num = 0;
334 mapit->indices_src = nullptr;
335 mapit->weights_src = nullptr;
336 }
337 // mapit->hit_dist = hit_dist;
338 mapit->island = island;
339}
340
342{
343 mesh_remap_item_define(map, index, FLT_MAX, 0, 0, nullptr, nullptr);
344}
345
347 const blender::Span<int> corner_verts,
348 const blender::Span<blender::float3> positions_src,
349 const float point[3],
350 size_t *buff_size,
351 float (**vcos)[3],
352 const bool use_loops,
353 int **indices,
354 float **weights,
355 const bool do_weights,
356 int *r_closest_index)
357{
358 float (*vco)[3];
359 float ref_dist_sq = FLT_MAX;
360 int *index;
361 const int sources_num = int(face.size());
362 int i;
363
364 if (size_t(sources_num) > *buff_size) {
365 *buff_size = size_t(sources_num);
366 *vcos = static_cast<float (*)[3]>(MEM_reallocN(*vcos, sizeof(**vcos) * *buff_size));
367 *indices = static_cast<int *>(MEM_reallocN(*indices, sizeof(**indices) * *buff_size));
368 if (do_weights) {
369 *weights = static_cast<float *>(MEM_reallocN(*weights, sizeof(**weights) * *buff_size));
370 }
371 }
372
373 for (i = 0, vco = *vcos, index = *indices; i < sources_num; i++, vco++, index++) {
374 const int vert = corner_verts[face[i]];
375 *index = use_loops ? int(face[i]) : vert;
376 copy_v3_v3(*vco, positions_src[vert]);
377 if (r_closest_index) {
378 /* Find closest vert/loop in this case. */
379 const float dist_sq = len_squared_v3v3(point, *vco);
380 if (dist_sq < ref_dist_sq) {
381 ref_dist_sq = dist_sq;
382 *r_closest_index = *index;
383 }
384 }
385 }
386
387 if (do_weights) {
388 interp_weights_poly_v3(*weights, *vcos, sources_num, point);
389 }
390
391 return sources_num;
392}
393
397 float factor;
401 float hit_dist;
403 float hit_point[3];
404};
405
421
423#define MREMAP_RAYCAST_APPROXIMATE_NR 3
425#define MREMAP_RAYCAST_APPROXIMATE_FAC 5.0f
426
427/* min 16 rays/face, max 400. */
428#define MREMAP_RAYCAST_TRI_SAMPLES_MIN 4
429#define MREMAP_RAYCAST_TRI_SAMPLES_MAX 20
430
431/* Will be enough in 99% of cases. */
432#define MREMAP_DEFAULT_BUFSIZE 32
433
435 const SpaceTransform *space_transform,
436 const float max_dist,
437 const float ray_radius,
438 const Span<float3> vert_positions_dst,
439 const Mesh *me_src,
440 Mesh *me_dst,
441 MeshPairRemap *r_map)
442{
443 const float full_weight = 1.0f;
444 const float max_dist_sq = max_dist * max_dist;
445 int i;
446
448
449 BKE_mesh_remap_init(r_map, int(vert_positions_dst.size()));
450
451 if (mode == MREMAP_MODE_TOPOLOGY) {
452 BLI_assert(vert_positions_dst.size() == me_src->verts_num);
453 for (i = 0; i < vert_positions_dst.size(); i++) {
454 mesh_remap_item_define(r_map, i, FLT_MAX, 0, 1, &i, &full_weight);
455 }
456 }
457 else {
459 BVHTreeNearest nearest = {0};
460 BVHTreeRayHit rayhit = {0};
461 float hit_dist;
462 float tmp_co[3], tmp_no[3];
463
464 if (mode == MREMAP_MODE_VERT_NEAREST) {
465 treedata = me_src->bvh_verts();
466 nearest.index = -1;
467
468 for (i = 0; i < vert_positions_dst.size(); i++) {
469 copy_v3_v3(tmp_co, vert_positions_dst[i]);
470
471 /* Convert the vertex to tree coordinates, if needed. */
472 if (space_transform) {
473 BLI_space_transform_apply(space_transform, tmp_co);
474 }
475
476 if (mesh_remap_bvhtree_query_nearest(&treedata, &nearest, tmp_co, max_dist_sq, &hit_dist))
477 {
478 mesh_remap_item_define(r_map, i, hit_dist, 0, 1, &nearest.index, &full_weight);
479 }
480 else {
481 /* No source for this dest vertex! */
483 }
484 }
485 }
487 const blender::Span<blender::int2> edges_src = me_src->edges();
488 const blender::Span<blender::float3> positions_src = me_src->vert_positions();
489
490 treedata = me_src->bvh_edges();
491 nearest.index = -1;
492
493 for (i = 0; i < vert_positions_dst.size(); i++) {
494 copy_v3_v3(tmp_co, vert_positions_dst[i]);
495
496 /* Convert the vertex to tree coordinates, if needed. */
497 if (space_transform) {
498 BLI_space_transform_apply(space_transform, tmp_co);
499 }
500
501 if (mesh_remap_bvhtree_query_nearest(&treedata, &nearest, tmp_co, max_dist_sq, &hit_dist))
502 {
503 const blender::int2 &edge = edges_src[nearest.index];
504 const float *v1cos = positions_src[edge[0]];
505 const float *v2cos = positions_src[edge[1]];
506
507 if (mode == MREMAP_MODE_VERT_EDGE_NEAREST) {
508 const float dist_v1 = len_squared_v3v3(tmp_co, v1cos);
509 const float dist_v2 = len_squared_v3v3(tmp_co, v2cos);
510 const int index = (dist_v1 > dist_v2) ? edge[1] : edge[0];
511 mesh_remap_item_define(r_map, i, hit_dist, 0, 1, &index, &full_weight);
512 }
513 else if (mode == MREMAP_MODE_VERT_EDGEINTERP_NEAREST) {
514 int indices[2];
515 float weights[2];
516
517 indices[0] = edge[0];
518 indices[1] = edge[1];
519
520 /* Weight is inverse of point factor here... */
521 weights[0] = line_point_factor_v3(tmp_co, v2cos, v1cos);
522 CLAMP(weights[0], 0.0f, 1.0f);
523 weights[1] = 1.0f - weights[0];
524
525 mesh_remap_item_define(r_map, i, hit_dist, 0, 2, indices, weights);
526 }
527 }
528 else {
529 /* No source for this dest vertex! */
531 }
532 }
533 }
534 else if (ELEM(mode,
538 {
539 const blender::OffsetIndices faces_src = me_src->faces();
540 const blender::Span<int> corner_verts_src = me_src->corner_verts();
541 const blender::Span<blender::float3> positions_src = me_src->vert_positions();
542 const blender::Span<blender::float3> vert_normals_dst = me_dst->vert_normals();
543 const blender::Span<int> tri_faces = me_src->corner_tri_faces();
544
545 size_t tmp_buff_size = MREMAP_DEFAULT_BUFSIZE;
546 float (*vcos)[3] = MEM_malloc_arrayN<float[3]>(tmp_buff_size, __func__);
547 int *indices = MEM_malloc_arrayN<int>(tmp_buff_size, __func__);
548 float *weights = MEM_malloc_arrayN<float>(tmp_buff_size, __func__);
549
550 treedata = me_src->bvh_corner_tris();
551
553 for (i = 0; i < vert_positions_dst.size(); i++) {
554 copy_v3_v3(tmp_co, vert_positions_dst[i]);
555 copy_v3_v3(tmp_no, vert_normals_dst[i]);
556
557 /* Convert the vertex to tree coordinates, if needed. */
558 if (space_transform) {
559 BLI_space_transform_apply(space_transform, tmp_co);
560 BLI_space_transform_apply_normal(space_transform, tmp_no);
561 }
562
564 &treedata, &rayhit, tmp_co, tmp_no, ray_radius, max_dist, &hit_dist))
565 {
566 const int face_index = tri_faces[rayhit.index];
567 const int sources_num = mesh_remap_interp_face_data_get(faces_src[face_index],
568 corner_verts_src,
569 positions_src,
570 rayhit.co,
571 &tmp_buff_size,
572 &vcos,
573 false,
574 &indices,
575 &weights,
576 true,
577 nullptr);
578
579 mesh_remap_item_define(r_map, i, hit_dist, 0, sources_num, indices, weights);
580 }
581 else {
582 /* No source for this dest vertex! */
584 }
585 }
586 }
587 else {
588 nearest.index = -1;
589
590 for (i = 0; i < vert_positions_dst.size(); i++) {
591 copy_v3_v3(tmp_co, vert_positions_dst[i]);
592
593 /* Convert the vertex to tree coordinates, if needed. */
594 if (space_transform) {
595 BLI_space_transform_apply(space_transform, tmp_co);
596 }
597
599 &treedata, &nearest, tmp_co, max_dist_sq, &hit_dist))
600 {
601 const int face_index = tri_faces[nearest.index];
602
603 if (mode == MREMAP_MODE_VERT_FACE_NEAREST) {
604 int index;
605 mesh_remap_interp_face_data_get(faces_src[face_index],
606 corner_verts_src,
607 positions_src,
608 nearest.co,
609 &tmp_buff_size,
610 &vcos,
611 false,
612 &indices,
613 &weights,
614 false,
615 &index);
616
617 mesh_remap_item_define(r_map, i, hit_dist, 0, 1, &index, &full_weight);
618 }
619 else if (mode == MREMAP_MODE_VERT_POLYINTERP_NEAREST) {
620 const int sources_num = mesh_remap_interp_face_data_get(faces_src[face_index],
621 corner_verts_src,
622 positions_src,
623 nearest.co,
624 &tmp_buff_size,
625 &vcos,
626 false,
627 &indices,
628 &weights,
629 true,
630 nullptr);
631
632 mesh_remap_item_define(r_map, i, hit_dist, 0, sources_num, indices, weights);
633 }
634 }
635 else {
636 /* No source for this dest vertex! */
638 }
639 }
640 }
641
642 MEM_freeN(vcos);
644 MEM_freeN(weights);
645 }
646 else {
647 CLOG_WARN(&LOG, "Unsupported mesh-to-mesh vertex mapping mode (%d)!", mode);
648 memset(r_map->items, 0, sizeof(*r_map->items) * size_t(vert_positions_dst.size()));
649 }
650 }
651}
652
654 const SpaceTransform *space_transform,
655 const float max_dist,
656 const float ray_radius,
657 const Span<float3> vert_positions_dst,
658 const Span<blender::int2> edges_dst,
659 const Mesh *me_src,
660 Mesh *me_dst,
661 MeshPairRemap *r_map)
662{
663 using namespace blender;
664 const float full_weight = 1.0f;
665 const float max_dist_sq = max_dist * max_dist;
666 int i;
667
669
670 BKE_mesh_remap_init(r_map, int(edges_dst.size()));
671
672 if (mode == MREMAP_MODE_TOPOLOGY) {
673 BLI_assert(edges_dst.size() == me_src->edges_num);
674 for (i = 0; i < edges_dst.size(); i++) {
675 mesh_remap_item_define(r_map, i, FLT_MAX, 0, 1, &i, &full_weight);
676 }
677 }
678 else {
680 BVHTreeNearest nearest = {0};
681 BVHTreeRayHit rayhit = {0};
682 float hit_dist;
683 float tmp_co[3], tmp_no[3];
684
685 if (mode == MREMAP_MODE_EDGE_VERT_NEAREST) {
686 const int num_verts_src = me_src->verts_num;
687 const blender::Span<blender::int2> edges_src = me_src->edges();
688 const blender::Span<blender::float3> positions_src = me_src->vert_positions();
689
690 struct HitData {
691 float hit_dist;
692 int index;
693 };
694 HitData *v_dst_to_src_map = MEM_malloc_arrayN<HitData>(size_t(vert_positions_dst.size()),
695 __func__);
696
697 for (i = 0; i < vert_positions_dst.size(); i++) {
698 v_dst_to_src_map[i].hit_dist = -1.0f;
699 }
700
701 Array<int> vert_to_edge_src_offsets;
702 Array<int> vert_to_edge_src_indices;
703 const GroupedSpan<int> vert_to_edge_src_map = bke::mesh::build_vert_to_edge_map(
704 edges_src, num_verts_src, vert_to_edge_src_offsets, vert_to_edge_src_indices);
705
706 treedata = me_src->bvh_verts();
707 nearest.index = -1;
708
709 for (i = 0; i < edges_dst.size(); i++) {
710 const blender::int2 &e_dst = edges_dst[i];
711 float best_totdist = FLT_MAX;
712 int best_eidx_src = -1;
713 int j = 2;
714
715 while (j--) {
716 const int vidx_dst = j ? e_dst[0] : e_dst[1];
717
718 /* Compute closest verts only once! */
719 if (v_dst_to_src_map[vidx_dst].hit_dist == -1.0f) {
720 copy_v3_v3(tmp_co, vert_positions_dst[vidx_dst]);
721
722 /* Convert the vertex to tree coordinates, if needed. */
723 if (space_transform) {
724 BLI_space_transform_apply(space_transform, tmp_co);
725 }
726
728 &treedata, &nearest, tmp_co, max_dist_sq, &hit_dist))
729 {
730 v_dst_to_src_map[vidx_dst].hit_dist = hit_dist;
731 v_dst_to_src_map[vidx_dst].index = nearest.index;
732 }
733 else {
734 /* No source for this dest vert! */
735 v_dst_to_src_map[vidx_dst].hit_dist = FLT_MAX;
736 }
737 }
738 }
739
740 /* Now, check all source edges of closest sources vertices,
741 * and select the one giving the smallest total verts-to-verts distance. */
742 for (j = 2; j--;) {
743 const int vidx_dst = j ? e_dst[0] : e_dst[1];
744 const float first_dist = v_dst_to_src_map[vidx_dst].hit_dist;
745 const int vidx_src = v_dst_to_src_map[vidx_dst].index;
746 const int *eidx_src;
747 int k;
748
749 if (vidx_src < 0) {
750 continue;
751 }
752
753 eidx_src = vert_to_edge_src_map[vidx_src].data();
754 k = int(vert_to_edge_src_map[vidx_src].size());
755
756 for (; k--; eidx_src++) {
757 const blender::int2 &edge_src = edges_src[*eidx_src];
758 const float *other_co_src =
759 positions_src[blender::bke::mesh::edge_other_vert(edge_src, vidx_src)];
760 const float *other_co_dst =
761 vert_positions_dst[blender::bke::mesh::edge_other_vert(e_dst, int(vidx_dst))];
762 const float totdist = first_dist + len_v3v3(other_co_src, other_co_dst);
763
764 if (totdist < best_totdist) {
765 best_totdist = totdist;
766 best_eidx_src = *eidx_src;
767 }
768 }
769 }
770
771 if (best_eidx_src >= 0) {
772 const float *co1_src = positions_src[edges_src[best_eidx_src][0]];
773 const float *co2_src = positions_src[edges_src[best_eidx_src][1]];
774 const float *co1_dst = vert_positions_dst[e_dst[0]];
775 const float *co2_dst = vert_positions_dst[e_dst[1]];
776 float co_src[3], co_dst[3];
777
778 /* TODO: would need an isect_seg_seg_v3(), actually! */
779 const int isect_type = isect_line_line_v3(
780 co1_src, co2_src, co1_dst, co2_dst, co_src, co_dst);
781 if (isect_type != 0) {
782 const float fac_src = line_point_factor_v3(co_src, co1_src, co2_src);
783 const float fac_dst = line_point_factor_v3(co_dst, co1_dst, co2_dst);
784 if (fac_src < 0.0f) {
785 copy_v3_v3(co_src, co1_src);
786 }
787 else if (fac_src > 1.0f) {
788 copy_v3_v3(co_src, co2_src);
789 }
790 if (fac_dst < 0.0f) {
791 copy_v3_v3(co_dst, co1_dst);
792 }
793 else if (fac_dst > 1.0f) {
794 copy_v3_v3(co_dst, co2_dst);
795 }
796 }
797 hit_dist = len_v3v3(co_dst, co_src);
798 mesh_remap_item_define(r_map, i, hit_dist, 0, 1, &best_eidx_src, &full_weight);
799 }
800 else {
801 /* No source for this dest edge! */
803 }
804 }
805
806 MEM_freeN(v_dst_to_src_map);
807 }
808 else if (mode == MREMAP_MODE_EDGE_NEAREST) {
809 treedata = me_src->bvh_edges();
810 nearest.index = -1;
811
812 for (i = 0; i < edges_dst.size(); i++) {
813 interp_v3_v3v3(tmp_co,
814 vert_positions_dst[edges_dst[i][0]],
815 vert_positions_dst[edges_dst[i][1]],
816 0.5f);
817
818 /* Convert the vertex to tree coordinates, if needed. */
819 if (space_transform) {
820 BLI_space_transform_apply(space_transform, tmp_co);
821 }
822
823 if (mesh_remap_bvhtree_query_nearest(&treedata, &nearest, tmp_co, max_dist_sq, &hit_dist))
824 {
825 mesh_remap_item_define(r_map, i, hit_dist, 0, 1, &nearest.index, &full_weight);
826 }
827 else {
828 /* No source for this dest edge! */
830 }
831 }
832 }
833 else if (mode == MREMAP_MODE_EDGE_POLY_NEAREST) {
834 const blender::Span<blender::int2> edges_src = me_src->edges();
835 const blender::OffsetIndices faces_src = me_src->faces();
836 const blender::Span<int> corner_edges_src = me_src->corner_edges();
837 const blender::Span<blender::float3> positions_src = me_src->vert_positions();
838 const blender::Span<int> tri_faces = me_src->corner_tri_faces();
839
840 treedata = me_src->bvh_corner_tris();
841
842 for (i = 0; i < edges_dst.size(); i++) {
843 interp_v3_v3v3(tmp_co,
844 vert_positions_dst[edges_dst[i][0]],
845 vert_positions_dst[edges_dst[i][1]],
846 0.5f);
847
848 /* Convert the vertex to tree coordinates, if needed. */
849 if (space_transform) {
850 BLI_space_transform_apply(space_transform, tmp_co);
851 }
852
853 if (mesh_remap_bvhtree_query_nearest(&treedata, &nearest, tmp_co, max_dist_sq, &hit_dist))
854 {
855 const int face_index = tri_faces[nearest.index];
856 const blender::IndexRange face_src = faces_src[face_index];
857 const int *corner_edge_src = &corner_edges_src[face_src.start()];
858 int nloops = int(face_src.size());
859 float best_dist_sq = FLT_MAX;
860 int best_eidx_src = -1;
861
862 for (; nloops--; corner_edge_src++) {
863 const blender::int2 &edge_src = edges_src[*corner_edge_src];
864 const float *co1_src = positions_src[edge_src[0]];
865 const float *co2_src = positions_src[edge_src[1]];
866 float co_src[3];
867 float dist_sq;
868
869 interp_v3_v3v3(co_src, co1_src, co2_src, 0.5f);
870 dist_sq = len_squared_v3v3(tmp_co, co_src);
871 if (dist_sq < best_dist_sq) {
872 best_dist_sq = dist_sq;
873 best_eidx_src = *corner_edge_src;
874 }
875 }
876 if (best_eidx_src >= 0) {
877 mesh_remap_item_define(r_map, i, hit_dist, 0, 1, &best_eidx_src, &full_weight);
878 }
879 }
880 else {
881 /* No source for this dest edge! */
883 }
884 }
885 }
886 else if (mode == MREMAP_MODE_EDGE_EDGEINTERP_VNORPROJ) {
887 const int num_rays_min = 5, num_rays_max = 100;
888 const int numedges_src = me_src->edges_num;
889
890 /* Subtleness - this one we can allocate only max number of cast rays per edges! */
891 int *indices = MEM_malloc_arrayN<int>(size_t(min_ii(numedges_src, num_rays_max)), __func__);
892 /* Here it's simpler to just allocate for all edges :/ */
893 float *weights = MEM_malloc_arrayN<float>(size_t(numedges_src), __func__);
894
895 treedata = me_src->bvh_edges();
896
897 const blender::Span<blender::float3> vert_normals_dst = me_dst->vert_normals();
898
899 for (i = 0; i < edges_dst.size(); i++) {
900 /* For each dst edge, we sample some rays from it (interpolated from its vertices)
901 * and use their hits to interpolate from source edges. */
902 const blender::int2 &edge = edges_dst[i];
903 float v1_co[3], v2_co[3];
904 float v1_no[3], v2_no[3];
905
906 int grid_size;
907 float edge_dst_len;
908 float grid_step;
909
910 float totweights = 0.0f;
911 float hit_dist_accum = 0.0f;
912 int sources_num = 0;
913 int j;
914
915 copy_v3_v3(v1_co, vert_positions_dst[edge[0]]);
916 copy_v3_v3(v2_co, vert_positions_dst[edge[1]]);
917
918 copy_v3_v3(v1_no, vert_normals_dst[edge[0]]);
919 copy_v3_v3(v2_no, vert_normals_dst[edge[1]]);
920
921 /* We do our transform here, allows to interpolate from normals already in src space. */
922 if (space_transform) {
923 BLI_space_transform_apply(space_transform, v1_co);
924 BLI_space_transform_apply(space_transform, v2_co);
925 BLI_space_transform_apply_normal(space_transform, v1_no);
926 BLI_space_transform_apply_normal(space_transform, v2_no);
927 }
928
929 copy_vn_fl(weights, int(numedges_src), 0.0f);
930
931 /* We adjust our ray-casting grid to ray_radius (the smaller, the more rays are cast),
932 * with lower/upper bounds. */
933 edge_dst_len = len_v3v3(v1_co, v2_co);
934
935 grid_size = int((edge_dst_len / ray_radius) + 0.5f);
936 CLAMP(grid_size, num_rays_min, num_rays_max); /* min 5 rays/edge, max 100. */
937
938 /* Not actual distance here, rather an interp fac... */
939 grid_step = 1.0f / float(grid_size);
940
941 /* And now we can cast all our rays, and see what we get! */
942 for (j = 0; j < grid_size; j++) {
943 const float fac = grid_step * float(j);
944
945 int n = (ray_radius > 0.0f) ? MREMAP_RAYCAST_APPROXIMATE_NR : 1;
946 float w = 1.0f;
947
948 interp_v3_v3v3(tmp_co, v1_co, v2_co, fac);
949 interp_v3_v3v3_slerp_safe(tmp_no, v1_no, v2_no, fac);
950
951 while (n--) {
953 &treedata, &rayhit, tmp_co, tmp_no, ray_radius / w, max_dist, &hit_dist))
954 {
955 weights[rayhit.index] += w;
956 totweights += w;
957 hit_dist_accum += hit_dist;
958 break;
959 }
960 /* Next iteration will get bigger radius but smaller weight! */
962 }
963 }
964 /* A sampling is valid (as in, its result can be considered as valid sources)
965 * only if at least half of the rays found a source! */
966 if (totweights > (float(grid_size) / 2.0f)) {
967 for (j = 0; j < int(numedges_src); j++) {
968 if (!weights[j]) {
969 continue;
970 }
971 /* NOTE: sources_num is always <= j! */
972 weights[sources_num] = weights[j] / totweights;
973 indices[sources_num] = j;
974 sources_num++;
975 }
977 r_map, i, hit_dist_accum / totweights, 0, sources_num, indices, weights);
978 }
979 else {
980 /* No source for this dest edge! */
982 }
983 }
984
986 MEM_freeN(weights);
987 }
988 else {
989 CLOG_WARN(&LOG, "Unsupported mesh-to-mesh edge mapping mode (%d)!", mode);
990 memset(r_map->items, 0, sizeof(*r_map->items) * size_t(edges_dst.size()));
991 }
992 }
993}
994
995#define POLY_UNSET 0
996#define POLY_CENTER_INIT 1
997#define POLY_COMPLETE 2
998
1000 MeshIslandStore *islands,
1001 const int island_index,
1002 BLI_AStarGraph *as_graph,
1003 const blender::Span<blender::float3> positions,
1005 const blender::Span<int> corner_verts,
1006 const int edge_idx,
1007 BLI_bitmap *done_edges,
1008 const blender::GroupedSpan<int> edge_to_face_map,
1009 const bool is_edge_innercut,
1010 const int *face_island_index_map,
1011 float (*face_centers)[3],
1012 uchar *face_status)
1013{
1014 blender::Array<int, 16> face_island_indices(edge_to_face_map[edge_idx].size());
1015 int i, j;
1016
1017 for (i = 0; i < edge_to_face_map[edge_idx].size(); i++) {
1018 const int pidx = edge_to_face_map[edge_idx][i];
1019 const blender::IndexRange face = faces[pidx];
1020 const int pidx_isld = islands ? face_island_index_map[pidx] : pidx;
1021 void *custom_data = is_edge_innercut ? POINTER_FROM_INT(edge_idx) : POINTER_FROM_INT(-1);
1022
1023 if (UNLIKELY(islands && (islands->items_to_islands[face.start()] != island_index))) {
1024 /* face not in current island, happens with border edges... */
1025 face_island_indices[i] = -1;
1026 continue;
1027 }
1028
1029 if (face_status[pidx_isld] == POLY_COMPLETE) {
1030 face_island_indices[i] = pidx_isld;
1031 continue;
1032 }
1033
1034 if (face_status[pidx_isld] == POLY_UNSET) {
1035 copy_v3_v3(face_centers[pidx_isld],
1036 blender::bke::mesh::face_center_calc(positions, corner_verts.slice(face)));
1037 BLI_astar_node_init(as_graph, pidx_isld, face_centers[pidx_isld]);
1038 face_status[pidx_isld] = POLY_CENTER_INIT;
1039 }
1040
1041 for (j = i; j--;) {
1042 float dist_cost;
1043 const int pidx_isld_other = face_island_indices[j];
1044
1045 if (pidx_isld_other == -1 || face_status[pidx_isld_other] == POLY_COMPLETE) {
1046 /* If the other face is complete, that link has already been added! */
1047 continue;
1048 }
1049 dist_cost = len_v3v3(face_centers[pidx_isld_other], face_centers[pidx_isld]);
1050 BLI_astar_node_link_add(as_graph, pidx_isld_other, pidx_isld, dist_cost, custom_data);
1051 }
1052
1053 face_island_indices[i] = pidx_isld;
1054 }
1055
1056 BLI_BITMAP_ENABLE(done_edges, edge_idx);
1057}
1058
1060 const int island_index,
1061 const blender::Span<blender::float3> positions,
1062 const blender::GroupedSpan<int> edge_to_face_map,
1063 const int numedges,
1065 const blender::Span<int> corner_verts,
1066 const blender::Span<int> corner_edges,
1067 BLI_AStarGraph *r_as_graph)
1068{
1069 MeshElemMap *island_face_map = islands ? islands->islands[island_index] : nullptr;
1070 MeshElemMap *island_einnercut_map = islands ? islands->innercuts[island_index] : nullptr;
1071
1072 int *face_island_index_map = nullptr;
1073 BLI_bitmap *done_edges = BLI_BITMAP_NEW(numedges, __func__);
1074
1075 const int node_num = islands ? island_face_map->count : int(faces.size());
1076 uchar *face_status = MEM_calloc_arrayN<uchar>(size_t(node_num), __func__);
1077 float (*face_centers)[3];
1078
1079 int pidx_isld;
1080 int i;
1081
1082 BLI_astar_graph_init(r_as_graph, node_num, nullptr);
1083 /* face_centers is owned by graph memarena. */
1084 face_centers = static_cast<float (*)[3]>(
1085 BLI_memarena_calloc(r_as_graph->mem, sizeof(*face_centers) * size_t(node_num)));
1086
1087 if (islands) {
1088 /* face_island_index_map is owned by graph memarena. */
1089 face_island_index_map = static_cast<int *>(BLI_memarena_calloc(
1090 r_as_graph->mem, sizeof(*face_island_index_map) * size_t(faces.size())));
1091 for (i = island_face_map->count; i--;) {
1092 face_island_index_map[island_face_map->indices[i]] = i;
1093 }
1094
1095 r_as_graph->custom_data = face_island_index_map;
1096
1097 for (i = island_einnercut_map->count; i--;) {
1099 island_index,
1100 r_as_graph,
1101 positions,
1102 faces,
1103 corner_verts,
1104 island_einnercut_map->indices[i],
1105 done_edges,
1106 edge_to_face_map,
1107 true,
1108 face_island_index_map,
1109 face_centers,
1110 face_status);
1111 }
1112 }
1113
1114 for (pidx_isld = node_num; pidx_isld--;) {
1115 const int pidx = islands ? island_face_map->indices[pidx_isld] : pidx_isld;
1116
1117 if (face_status[pidx_isld] == POLY_COMPLETE) {
1118 continue;
1119 }
1120
1121 for (const int edge : corner_edges.slice(faces[pidx])) {
1122 if (BLI_BITMAP_TEST(done_edges, edge)) {
1123 continue;
1124 }
1125
1127 island_index,
1128 r_as_graph,
1129 positions,
1130 faces,
1131 corner_verts,
1132 edge,
1133 done_edges,
1134 edge_to_face_map,
1135 false,
1136 face_island_index_map,
1137 face_centers,
1138 face_status);
1139 }
1140 face_status[pidx_isld] = POLY_COMPLETE;
1141 }
1142
1143 MEM_freeN(done_edges);
1144 MEM_freeN(face_status);
1145}
1146
1147#undef POLY_UNSET
1148#undef POLY_CENTER_INIT
1149#undef POLY_COMPLETE
1150
1151/* Our 'f_cost' callback func, to find shortest face-path between two remapped-loops.
1152 * Note we do not want to make innercuts 'walls' here,
1153 * just detect when the shortest path goes by those. */
1155 BLI_AStarSolution *as_solution,
1156 BLI_AStarGNLink *link,
1157 const int node_idx_curr,
1158 const int node_idx_next,
1159 const int node_idx_dst)
1160{
1161 float *co_next, *co_dest;
1162
1163 if (link && (POINTER_AS_INT(link->custom_data) != -1)) {
1164 /* An innercut edge... We tag our solution as potentially crossing innercuts.
1165 * Note it might not be the case in the end (AStar will explore around optimal path), but helps
1166 * trimming off some processing later... */
1167 if (!POINTER_AS_INT(as_solution->custom_data)) {
1168 as_solution->custom_data = POINTER_FROM_INT(true);
1169 }
1170 }
1171
1172 /* Our heuristic part of current f_cost is distance from next node to destination one.
1173 * It is guaranteed to be less than (or equal to)
1174 * actual shortest face-path between next node and destination one. */
1175 co_next = (float *)as_graph->nodes[node_idx_next].custom_data;
1176 co_dest = (float *)as_graph->nodes[node_idx_dst].custom_data;
1177 return (link ? (as_solution->g_costs[node_idx_curr] + link->cost) : 0.0f) +
1178 len_v3v3(co_next, co_dest);
1179}
1180
1181#define ASTAR_STEPS_MAX 64
1182
1184 const SpaceTransform *space_transform,
1185 const float max_dist,
1186 const float ray_radius,
1187 const Mesh *mesh_dst,
1188 const Span<float3> vert_positions_dst,
1189 const Span<int> corner_verts_dst,
1190 const blender::OffsetIndices<int> faces_dst,
1191 const Mesh *me_src,
1192 MeshRemapIslandsCalc gen_islands_src,
1193 const float islands_precision_src,
1194 MeshPairRemap *r_map)
1195{
1196 using namespace blender;
1197 const float full_weight = 1.0f;
1198 const float max_dist_sq = max_dist * max_dist;
1199
1201 BLI_assert((islands_precision_src >= 0.0f) && (islands_precision_src <= 1.0f));
1202
1203 BKE_mesh_remap_init(r_map, int(corner_verts_dst.size()));
1204
1205 if (mode == MREMAP_MODE_TOPOLOGY) {
1206 /* In topology mapping, we assume meshes are identical, islands included! */
1207 BLI_assert(corner_verts_dst.size() == me_src->corners_num);
1208 for (int i = 0; i < corner_verts_dst.size(); i++) {
1209 mesh_remap_item_define(r_map, i, FLT_MAX, 0, 1, &i, &full_weight);
1210 }
1211 }
1212 else {
1214 BVHTreeNearest nearest = {0};
1215 BVHTreeRayHit rayhit = {0};
1216 int num_trees = 0;
1217 float hit_dist;
1218 float tmp_co[3], tmp_no[3];
1219
1220 const bool use_from_vert = (mode & MREMAP_USE_VERT);
1221
1222 MeshIslandStore island_store = {0};
1223 bool use_islands = false;
1224
1225 BLI_AStarGraph *as_graphdata = nullptr;
1226 BLI_AStarSolution as_solution = {0};
1227 const int isld_steps_src = (islands_precision_src ?
1228 max_ii(int(ASTAR_STEPS_MAX * islands_precision_src + 0.499f),
1229 1) :
1230 0);
1231
1232 blender::Span<blender::float3> face_normals_src;
1233 blender::Span<blender::float3> loop_normals_src;
1234
1235 blender::Span<blender::float3> face_normals_dst;
1236 blender::Span<blender::float3> loop_normals_dst;
1237
1238 blender::Array<blender::float3> face_cents_src;
1239
1240 GroupedSpan<int> vert_to_corner_map_src;
1241 GroupedSpan<int> vert_to_face_map_src;
1242
1243 Array<int> edge_to_face_src_offsets;
1244 Array<int> edge_to_face_src_indices;
1245 GroupedSpan<int> edge_to_face_map_src;
1246
1247 MeshElemMap *face_to_corner_tri_map_src = nullptr;
1248 int *face_to_corner_tri_map_src_buff = nullptr;
1249
1250 /* Unlike above, those are one-to-one mappings, simpler! */
1251 blender::Span<int> loop_to_face_map_src;
1252
1253 const blender::Span<blender::float3> positions_src = me_src->vert_positions();
1254 const int num_verts_src = me_src->verts_num;
1255 const blender::Span<blender::int2> edges_src = me_src->edges();
1256 const blender::OffsetIndices faces_src = me_src->faces();
1257 const blender::Span<int> corner_verts_src = me_src->corner_verts();
1258 const blender::Span<int> corner_edges_src = me_src->corner_edges();
1259 blender::Span<blender::int3> corner_tris_src;
1260 blender::Span<int> tri_faces_src;
1261
1262 size_t buff_size_interp = MREMAP_DEFAULT_BUFSIZE;
1263 float (*vcos_interp)[3] = nullptr;
1264 int *indices_interp = nullptr;
1265 float *weights_interp = nullptr;
1266
1267 int tindex, pidx_dst, lidx_dst, plidx_dst, pidx_src, lidx_src, plidx_src;
1268
1269 IslandResult **islands_res;
1270 size_t islands_res_buff_size = MREMAP_DEFAULT_BUFSIZE;
1271
1272 if (!use_from_vert) {
1273 vcos_interp = MEM_malloc_arrayN<float[3]>(buff_size_interp, __func__);
1274 indices_interp = MEM_malloc_arrayN<int>(buff_size_interp, __func__);
1275 weights_interp = MEM_malloc_arrayN<float>(buff_size_interp, __func__);
1276 }
1277
1278 {
1279 const bool need_lnors_src = (mode & MREMAP_USE_LOOP) && (mode & MREMAP_USE_NORMAL);
1280 const bool need_lnors_dst = need_lnors_src || (mode & MREMAP_USE_NORPROJ);
1281 const bool need_pnors_src = need_lnors_src ||
1282 ((mode & MREMAP_USE_POLY) && (mode & MREMAP_USE_NORMAL));
1283 const bool need_pnors_dst = need_lnors_dst || need_pnors_src;
1284
1285 if (need_pnors_dst) {
1286 face_normals_dst = mesh_dst->face_normals();
1287 }
1288 if (need_lnors_dst) {
1289 loop_normals_dst = mesh_dst->corner_normals();
1290 }
1291 if (need_pnors_src) {
1292 face_normals_src = me_src->face_normals();
1293 }
1294 if (need_lnors_src) {
1295 loop_normals_src = me_src->corner_normals();
1296 }
1297 }
1298
1299 if (use_from_vert) {
1300 vert_to_corner_map_src = me_src->vert_to_corner_map();
1301 if (mode & MREMAP_USE_POLY) {
1302 vert_to_face_map_src = me_src->vert_to_face_map();
1303 }
1304 }
1305
1306 /* Needed for islands (or plain mesh) to AStar graph conversion. */
1307 edge_to_face_map_src = bke::mesh::build_edge_to_face_map(faces_src,
1308 corner_edges_src,
1309 int(edges_src.size()),
1310 edge_to_face_src_offsets,
1311 edge_to_face_src_indices);
1312
1313 if (use_from_vert) {
1314 loop_to_face_map_src = me_src->corner_to_face_map();
1315 face_cents_src.reinitialize(faces_src.size());
1316 for (const int64_t i : faces_src.index_range()) {
1317 face_cents_src[i] = blender::bke::mesh::face_center_calc(
1318 positions_src, corner_verts_src.slice(faces_src[i]));
1319 }
1320 }
1321
1322 /* Island makes things slightly more complex here.
1323 * Basically, we:
1324 * * Make one treedata for each island's elements.
1325 * * Check all loops of a same dest face against all treedata.
1326 * * Choose the island's elements giving the best results.
1327 */
1328
1329 /* First, generate the islands, if possible. */
1330 if (gen_islands_src) {
1331 const bke::AttributeAccessor attributes = me_src->attributes();
1332 const VArraySpan uv_seams = *attributes.lookup<bool>("uv_seam", bke::AttrDomain::Edge);
1333 use_islands = gen_islands_src(positions_src,
1334 edges_src,
1335 uv_seams,
1336 faces_src,
1337 corner_verts_src,
1338 corner_edges_src,
1339 &island_store);
1340
1341 num_trees = use_islands ? island_store.islands_num : 1;
1342 treedata.reinitialize(num_trees);
1343 if (isld_steps_src) {
1344 as_graphdata = MEM_calloc_arrayN<BLI_AStarGraph>(size_t(num_trees), __func__);
1345 }
1346
1347 if (use_islands) {
1348 /* We expect our islands to contain face indices, with edge indices of 'inner cuts',
1349 * and a mapping loops -> islands indices.
1350 * This implies all loops of a same face are in the same island. */
1351 BLI_assert((island_store.item_type == MISLAND_TYPE_LOOP) &&
1352 (island_store.island_type == MISLAND_TYPE_POLY) &&
1353 (island_store.innercut_type == MISLAND_TYPE_EDGE));
1354 }
1355 }
1356 else {
1357 num_trees = 1;
1358 treedata.reinitialize(1);
1359 if (isld_steps_src) {
1360 as_graphdata = MEM_callocN<BLI_AStarGraph>(__func__);
1361 }
1362 }
1363
1364 /* Build our AStar graphs. */
1365 if (isld_steps_src) {
1366 for (tindex = 0; tindex < num_trees; tindex++) {
1367 mesh_island_to_astar_graph(use_islands ? &island_store : nullptr,
1368 tindex,
1369 positions_src,
1370 edge_to_face_map_src,
1371 int(edges_src.size()),
1372 faces_src,
1373 corner_verts_src,
1374 corner_edges_src,
1375 &as_graphdata[tindex]);
1376 }
1377 }
1378
1379 /* Build our BVHtrees, either from verts or tessfaces. */
1380 if (use_from_vert) {
1381 if (use_islands) {
1382 blender::BitVector<> verts_active(num_verts_src);
1383
1384 for (tindex = 0; tindex < num_trees; tindex++) {
1385 MeshElemMap *isld = island_store.islands[tindex];
1386 verts_active.fill(false);
1387 for (int i = 0; i < isld->count; i++) {
1388 for (const int vidx_src : corner_verts_src.slice(faces_src[isld->indices[i]])) {
1389 if (!verts_active[vidx_src]) {
1390 verts_active[vidx_src].set();
1391 }
1392 }
1393 }
1394 IndexMaskMemory memory;
1396 positions_src, IndexMask::from_bits(verts_active, memory));
1397 }
1398 }
1399 else {
1400 BLI_assert(num_trees == 1);
1401 treedata[0] = me_src->bvh_verts();
1402 }
1403 }
1404 else { /* We use faces. */
1405 if (use_islands) {
1406 corner_tris_src = me_src->corner_tris();
1407 tri_faces_src = me_src->corner_tri_faces();
1408 blender::BitVector<> faces_active(corner_tris_src.size());
1409
1410 for (tindex = 0; tindex < num_trees; tindex++) {
1411 faces_active.fill(false);
1412 for (const int64_t i : faces_src.index_range()) {
1413 const blender::IndexRange face = faces_src[i];
1414 if (island_store.items_to_islands[face.start()] == tindex) {
1415 faces_active[i].set();
1416 }
1417 }
1418 IndexMaskMemory memory;
1420 positions_src,
1421 faces_src,
1422 corner_verts_src,
1423 corner_tris_src,
1424 IndexMask::from_bits(faces_active, memory));
1425 }
1426 }
1427 else {
1428 BLI_assert(num_trees == 1);
1429 treedata[0] = me_src->bvh_corner_tris();
1430 }
1431 }
1432
1433 /* And check each dest face! */
1434 islands_res = MEM_malloc_arrayN<IslandResult *>(size_t(num_trees), __func__);
1435 for (tindex = 0; tindex < num_trees; tindex++) {
1436 islands_res[tindex] = MEM_malloc_arrayN<IslandResult>(islands_res_buff_size, __func__);
1437 }
1438 const blender::Span<int> tri_faces = me_src->corner_tri_faces();
1439
1440 for (pidx_dst = 0; pidx_dst < faces_dst.size(); pidx_dst++) {
1441 const blender::IndexRange face_dst = faces_dst[pidx_dst];
1442 float pnor_dst[3];
1443
1444 /* Only in use_from_vert case, we may need faces' centers as fallback
1445 * in case we cannot decide which corner to use from normals only. */
1446 blender::float3 pcent_dst;
1447 bool pcent_dst_valid = false;
1448
1450 copy_v3_v3(pnor_dst, face_normals_dst[pidx_dst]);
1451 if (space_transform) {
1452 BLI_space_transform_apply_normal(space_transform, pnor_dst);
1453 }
1454 }
1455
1456 if (size_t(face_dst.size()) > islands_res_buff_size) {
1457 islands_res_buff_size = size_t(face_dst.size()) + MREMAP_DEFAULT_BUFSIZE;
1458 for (tindex = 0; tindex < num_trees; tindex++) {
1459 islands_res[tindex] = static_cast<IslandResult *>(
1460 MEM_reallocN(islands_res[tindex], sizeof(**islands_res) * islands_res_buff_size));
1461 }
1462 }
1463
1464 for (tindex = 0; tindex < num_trees; tindex++) {
1465 blender::bke::BVHTreeFromMesh *tdata = &treedata[tindex];
1466
1467 for (plidx_dst = 0; plidx_dst < face_dst.size(); plidx_dst++) {
1468 const int vert_dst = corner_verts_dst[face_dst.start() + plidx_dst];
1469 if (use_from_vert) {
1470 blender::Span<int> vert_to_refelem_map_src;
1471
1472 copy_v3_v3(tmp_co, vert_positions_dst[vert_dst]);
1473 nearest.index = -1;
1474
1475 /* Convert the vertex to tree coordinates, if needed. */
1476 if (space_transform) {
1477 BLI_space_transform_apply(space_transform, tmp_co);
1478 }
1479
1480 if (mesh_remap_bvhtree_query_nearest(tdata, &nearest, tmp_co, max_dist_sq, &hit_dist))
1481 {
1482 float (*nor_dst)[3];
1484 float best_nor_dot = -2.0f;
1485 float best_sqdist_fallback = FLT_MAX;
1486 int best_index_src = -1;
1487
1489 copy_v3_v3(tmp_no, loop_normals_dst[plidx_dst + face_dst.start()]);
1490 if (space_transform) {
1491 BLI_space_transform_apply_normal(space_transform, tmp_no);
1492 }
1493 nor_dst = &tmp_no;
1494 nors_src = loop_normals_src;
1495 vert_to_refelem_map_src = vert_to_corner_map_src[nearest.index];
1496 }
1497 else { /* if (mode == MREMAP_MODE_LOOP_NEAREST_POLYNOR) { */
1498 nor_dst = &pnor_dst;
1499 nors_src = face_normals_src;
1500 vert_to_refelem_map_src = vert_to_face_map_src[nearest.index];
1501 }
1502
1503 for (const int index_src : vert_to_refelem_map_src) {
1504 BLI_assert(index_src != -1);
1505 const float dot = dot_v3v3(nors_src[index_src], *nor_dst);
1506
1507 pidx_src = ((mode == MREMAP_MODE_LOOP_NEAREST_LOOPNOR) ?
1508 loop_to_face_map_src[index_src] :
1509 index_src);
1510 /* WARNING! This is not the *real* lidx_src in case of POLYNOR, we only use it
1511 * to check we stay on current island (all loops from a given face are
1512 * on same island!). */
1513 lidx_src = ((mode == MREMAP_MODE_LOOP_NEAREST_LOOPNOR) ?
1514 index_src :
1515 int(faces_src[pidx_src].start()));
1516
1517 /* A same vert may be at the boundary of several islands! Hence, we have to ensure
1518 * face/loop we are currently considering *belongs* to current island! */
1519 if (use_islands && island_store.items_to_islands[lidx_src] != tindex) {
1520 continue;
1521 }
1522
1523 if (dot > best_nor_dot - 1e-6f) {
1524 /* We need something as fallback decision in case dest normal matches several
1525 * source normals (see #44522), using distance between faces' centers here. */
1526 float *pcent_src;
1527 float sqdist;
1528
1529 if (!pcent_dst_valid) {
1531 vert_positions_dst, corner_verts_dst.slice(face_dst));
1532 pcent_dst_valid = true;
1533 }
1534 pcent_src = face_cents_src[pidx_src];
1535 sqdist = len_squared_v3v3(pcent_dst, pcent_src);
1536
1537 if ((dot > best_nor_dot + 1e-6f) || (sqdist < best_sqdist_fallback)) {
1538 best_nor_dot = dot;
1539 best_sqdist_fallback = sqdist;
1540 best_index_src = index_src;
1541 }
1542 }
1543 }
1544 if (best_index_src == -1) {
1545 /* We found no item to map back from closest vertex... */
1546 best_nor_dot = -1.0f;
1547 hit_dist = FLT_MAX;
1548 }
1549 else if (mode == MREMAP_MODE_LOOP_NEAREST_POLYNOR) {
1550 /* Our best_index_src is a face one for now!
1551 * Have to find its loop matching our closest vertex. */
1552 const blender::IndexRange face_src = faces_src[best_index_src];
1553 for (plidx_src = 0; plidx_src < face_src.size(); plidx_src++) {
1554 const int vert_src = corner_verts_src[face_src.start() + plidx_src];
1555 if (vert_src == nearest.index) {
1556 best_index_src = plidx_src + int(face_src.start());
1557 break;
1558 }
1559 }
1560 }
1561 best_nor_dot = (best_nor_dot + 1.0f) * 0.5f;
1562 islands_res[tindex][plidx_dst].factor = hit_dist ? (best_nor_dot / hit_dist) : 1e18f;
1563 islands_res[tindex][plidx_dst].hit_dist = hit_dist;
1564 islands_res[tindex][plidx_dst].index_src = best_index_src;
1565 }
1566 else {
1567 /* No source for this dest loop! */
1568 islands_res[tindex][plidx_dst].factor = 0.0f;
1569 islands_res[tindex][plidx_dst].hit_dist = FLT_MAX;
1570 islands_res[tindex][plidx_dst].index_src = -1;
1571 }
1572 }
1573 else if (mode & MREMAP_USE_NORPROJ) {
1574 int n = (ray_radius > 0.0f) ? MREMAP_RAYCAST_APPROXIMATE_NR : 1;
1575 float w = 1.0f;
1576
1577 copy_v3_v3(tmp_co, vert_positions_dst[vert_dst]);
1578 copy_v3_v3(tmp_no, loop_normals_dst[plidx_dst + face_dst.start()]);
1579
1580 /* We do our transform here, since we may do several raycast/nearest queries. */
1581 if (space_transform) {
1582 BLI_space_transform_apply(space_transform, tmp_co);
1583 BLI_space_transform_apply_normal(space_transform, tmp_no);
1584 }
1585
1586 while (n--) {
1588 tdata, &rayhit, tmp_co, tmp_no, ray_radius / w, max_dist, &hit_dist))
1589 {
1590 islands_res[tindex][plidx_dst].factor = (hit_dist ? (1.0f / hit_dist) : 1e18f) * w;
1591 islands_res[tindex][plidx_dst].hit_dist = hit_dist;
1592 islands_res[tindex][plidx_dst].index_src = tri_faces[rayhit.index];
1593 copy_v3_v3(islands_res[tindex][plidx_dst].hit_point, rayhit.co);
1594 break;
1595 }
1596 /* Next iteration will get bigger radius but smaller weight! */
1598 }
1599 if (n == -1) {
1600 /* Fall back to 'nearest' hit here, loops usually comes in 'face group', not good to
1601 * have only part of one dest face's loops to map to source.
1602 * Note that since we give this a null weight, if whole weight for a given face
1603 * is null, it means none of its loop mapped to this source island,
1604 * hence we can skip it later.
1605 */
1606 copy_v3_v3(tmp_co, vert_positions_dst[vert_dst]);
1607 nearest.index = -1;
1608
1609 /* Convert the vertex to tree coordinates, if needed. */
1610 if (space_transform) {
1611 BLI_space_transform_apply(space_transform, tmp_co);
1612 }
1613
1614 /* In any case, this fallback nearest hit should have no weight at all
1615 * in 'best island' decision! */
1616 islands_res[tindex][plidx_dst].factor = 0.0f;
1617
1619 tdata, &nearest, tmp_co, max_dist_sq, &hit_dist))
1620 {
1621 islands_res[tindex][plidx_dst].hit_dist = hit_dist;
1622 islands_res[tindex][plidx_dst].index_src = tri_faces[nearest.index];
1623 copy_v3_v3(islands_res[tindex][plidx_dst].hit_point, nearest.co);
1624 }
1625 else {
1626 /* No source for this dest loop! */
1627 islands_res[tindex][plidx_dst].hit_dist = FLT_MAX;
1628 islands_res[tindex][plidx_dst].index_src = -1;
1629 }
1630 }
1631 }
1632 else { /* Nearest face either to use all its loops/verts or just closest one. */
1633 copy_v3_v3(tmp_co, vert_positions_dst[vert_dst]);
1634 nearest.index = -1;
1635
1636 /* Convert the vertex to tree coordinates, if needed. */
1637 if (space_transform) {
1638 BLI_space_transform_apply(space_transform, tmp_co);
1639 }
1640
1641 if (mesh_remap_bvhtree_query_nearest(tdata, &nearest, tmp_co, max_dist_sq, &hit_dist))
1642 {
1643 islands_res[tindex][plidx_dst].factor = hit_dist ? (1.0f / hit_dist) : 1e18f;
1644 islands_res[tindex][plidx_dst].hit_dist = hit_dist;
1645 islands_res[tindex][plidx_dst].index_src = tri_faces[nearest.index];
1646 copy_v3_v3(islands_res[tindex][plidx_dst].hit_point, nearest.co);
1647 }
1648 else {
1649 /* No source for this dest loop! */
1650 islands_res[tindex][plidx_dst].factor = 0.0f;
1651 islands_res[tindex][plidx_dst].hit_dist = FLT_MAX;
1652 islands_res[tindex][plidx_dst].index_src = -1;
1653 }
1654 }
1655 }
1656 }
1657
1658 /* And now, find best island to use! */
1659 /* We have to first select the 'best source island' for given dst face and its loops.
1660 * Then, we have to check that face does not 'spread' across some island's limits
1661 * (like inner seams for UVs, etc.).
1662 * Note we only still partially support that kind of situation here, i.e.
1663 * Faces spreading over actual cracks
1664 * (like a narrow space without faces on src, splitting a 'tube-like' geometry).
1665 * That kind of situation should be relatively rare, though.
1666 */
1667 /* XXX This block in itself is big and complex enough to be a separate function but...
1668 * it uses a bunch of locale vars.
1669 * Not worth sending all that through parameters (for now at least). */
1670 {
1671 BLI_AStarGraph *as_graph = nullptr;
1672 int *face_island_index_map = nullptr;
1673 int pidx_src_prev = -1;
1674
1675 MeshElemMap *best_island = nullptr;
1676 float best_island_fac = 0.0f;
1677 int best_island_index = -1;
1678
1679 for (tindex = 0; tindex < num_trees; tindex++) {
1680 float island_fac = 0.0f;
1681
1682 for (plidx_dst = 0; plidx_dst < face_dst.size(); plidx_dst++) {
1683 island_fac += islands_res[tindex][plidx_dst].factor;
1684 }
1685 island_fac /= float(face_dst.size());
1686
1687 if (island_fac > best_island_fac) {
1688 best_island_fac = island_fac;
1689 best_island_index = tindex;
1690 }
1691 }
1692
1693 if (best_island_index != -1 && isld_steps_src) {
1694 best_island = use_islands ? island_store.islands[best_island_index] : nullptr;
1695 as_graph = &as_graphdata[best_island_index];
1696 face_island_index_map = (int *)as_graph->custom_data;
1697 BLI_astar_solution_init(as_graph, &as_solution, nullptr);
1698 }
1699
1700 for (plidx_dst = 0; plidx_dst < face_dst.size(); plidx_dst++) {
1701 IslandResult *isld_res;
1702 lidx_dst = plidx_dst + int(face_dst.start());
1703
1704 if (best_island_index == -1) {
1705 /* No source for any loops of our dest face in any source islands. */
1706 BKE_mesh_remap_item_define_invalid(r_map, lidx_dst);
1707 continue;
1708 }
1709
1710 as_solution.custom_data = POINTER_FROM_INT(false);
1711
1712 isld_res = &islands_res[best_island_index][plidx_dst];
1713 if (use_from_vert) {
1714 /* Indices stored in islands_res are those of loops, one per dest loop. */
1715 lidx_src = isld_res->index_src;
1716 if (lidx_src >= 0) {
1717 pidx_src = loop_to_face_map_src[lidx_src];
1718 /* If prev and curr face are the same, no need to do anything more!!! */
1719 if (!ELEM(pidx_src_prev, -1, pidx_src) && isld_steps_src) {
1720 int pidx_isld_src, pidx_isld_src_prev;
1721 if (face_island_index_map) {
1722 pidx_isld_src = face_island_index_map[pidx_src];
1723 pidx_isld_src_prev = face_island_index_map[pidx_src_prev];
1724 }
1725 else {
1726 pidx_isld_src = pidx_src;
1727 pidx_isld_src_prev = pidx_src_prev;
1728 }
1729
1730 BLI_astar_graph_solve(as_graph,
1731 pidx_isld_src_prev,
1732 pidx_isld_src,
1734 &as_solution,
1735 isld_steps_src);
1736 if (POINTER_AS_INT(as_solution.custom_data) && (as_solution.steps > 0)) {
1737 /* Find first 'cutting edge' on path, and bring back lidx_src on face just
1738 * before that edge.
1739 * Note we could try to be much smarter, g.g. Storing a whole face's indices,
1740 * and making decision (on which side of cutting edge(s!) to be) on the end,
1741 * but this is one more level of complexity, better to first see if
1742 * simple solution works!
1743 */
1744 int last_valid_pidx_isld_src = -1;
1745 /* Note we go backward here, from dest to src face. */
1746 for (int i = as_solution.steps - 1; i--;) {
1747 BLI_AStarGNLink *as_link = as_solution.prev_links[pidx_isld_src];
1748 const int eidx = POINTER_AS_INT(as_link->custom_data);
1749 pidx_isld_src = as_solution.prev_nodes[pidx_isld_src];
1750 BLI_assert(pidx_isld_src != -1);
1751 if (eidx != -1) {
1752 /* we are 'crossing' a cutting edge. */
1753 last_valid_pidx_isld_src = pidx_isld_src;
1754 }
1755 }
1756 if (last_valid_pidx_isld_src != -1) {
1757 /* Find a new valid loop in that new face (nearest one for now).
1758 * Note we could be much more subtle here, again that's for later... */
1759 float best_dist_sq = FLT_MAX;
1760
1761 copy_v3_v3(tmp_co, vert_positions_dst[corner_verts_dst[lidx_dst]]);
1762
1763 /* We do our transform here,
1764 * since we may do several raycast/nearest queries. */
1765 if (space_transform) {
1766 BLI_space_transform_apply(space_transform, tmp_co);
1767 }
1768
1769 pidx_src = (use_islands ? best_island->indices[last_valid_pidx_isld_src] :
1770 last_valid_pidx_isld_src);
1771 const blender::IndexRange face_src = faces_src[pidx_src];
1772 for (const int64_t corner : face_src) {
1773 const int vert_src = corner_verts_src[corner];
1774 const float dist_sq = len_squared_v3v3(positions_src[vert_src], tmp_co);
1775 if (dist_sq < best_dist_sq) {
1776 best_dist_sq = dist_sq;
1777 lidx_src = int(corner);
1778 }
1779 }
1780 }
1781 }
1782 }
1784 lidx_dst,
1785 isld_res->hit_dist,
1786 best_island_index,
1787 1,
1788 &lidx_src,
1789 &full_weight);
1790 pidx_src_prev = pidx_src;
1791 }
1792 else {
1793 /* No source for this loop in this island. */
1794 /* TODO: would probably be better to get a source
1795 * at all cost in best island anyway? */
1797 r_map, lidx_dst, FLT_MAX, best_island_index, 0, nullptr, nullptr);
1798 }
1799 }
1800 else {
1801 /* Else, we use source face, indices stored in islands_res are those of faces. */
1802 pidx_src = isld_res->index_src;
1803 if (pidx_src >= 0) {
1804 float *hit_co = isld_res->hit_point;
1805 int best_loop_index_src;
1806
1807 const blender::IndexRange face_src = faces_src[pidx_src];
1808 /* If prev and curr face are the same, no need to do anything more!!! */
1809 if (!ELEM(pidx_src_prev, -1, pidx_src) && isld_steps_src) {
1810 int pidx_isld_src, pidx_isld_src_prev;
1811 if (face_island_index_map) {
1812 pidx_isld_src = face_island_index_map[pidx_src];
1813 pidx_isld_src_prev = face_island_index_map[pidx_src_prev];
1814 }
1815 else {
1816 pidx_isld_src = pidx_src;
1817 pidx_isld_src_prev = pidx_src_prev;
1818 }
1819
1820 BLI_astar_graph_solve(as_graph,
1821 pidx_isld_src_prev,
1822 pidx_isld_src,
1824 &as_solution,
1825 isld_steps_src);
1826 if (POINTER_AS_INT(as_solution.custom_data) && (as_solution.steps > 0)) {
1827 /* Find first 'cutting edge' on path, and bring back lidx_src on face just
1828 * before that edge.
1829 * Note we could try to be much smarter: e.g. Storing a whole face's indices,
1830 * and making decision (one which side of cutting edge(s)!) to be on the end,
1831 * but this is one more level of complexity, better to first see if
1832 * simple solution works!
1833 */
1834 int last_valid_pidx_isld_src = -1;
1835 /* Note we go backward here, from dest to src face. */
1836 for (int i = as_solution.steps - 1; i--;) {
1837 BLI_AStarGNLink *as_link = as_solution.prev_links[pidx_isld_src];
1838 int eidx = POINTER_AS_INT(as_link->custom_data);
1839
1840 pidx_isld_src = as_solution.prev_nodes[pidx_isld_src];
1841 BLI_assert(pidx_isld_src != -1);
1842 if (eidx != -1) {
1843 /* we are 'crossing' a cutting edge. */
1844 last_valid_pidx_isld_src = pidx_isld_src;
1845 }
1846 }
1847 if (last_valid_pidx_isld_src != -1) {
1848 /* Find a new valid loop in that new face (nearest point on face for now).
1849 * Note we could be much more subtle here, again that's for later... */
1850 float best_dist_sq = FLT_MAX;
1851 int j;
1852
1853 const int vert_dst = corner_verts_dst[lidx_dst];
1854 copy_v3_v3(tmp_co, vert_positions_dst[vert_dst]);
1855
1856 /* We do our transform here,
1857 * since we may do several raycast/nearest queries. */
1858 if (space_transform) {
1859 BLI_space_transform_apply(space_transform, tmp_co);
1860 }
1861
1862 pidx_src = (use_islands ? best_island->indices[last_valid_pidx_isld_src] :
1863 last_valid_pidx_isld_src);
1864
1865 /* Create that one on demand. */
1866 if (face_to_corner_tri_map_src == nullptr) {
1867 BKE_mesh_origindex_map_create_corner_tri(&face_to_corner_tri_map_src,
1868 &face_to_corner_tri_map_src_buff,
1869 faces_src,
1870 tri_faces_src.data(),
1871 int(tri_faces_src.size()));
1872 }
1873
1874 for (j = face_to_corner_tri_map_src[pidx_src].count; j--;) {
1875 float h[3];
1876 const blender::int3 &tri =
1877 corner_tris_src[face_to_corner_tri_map_src[pidx_src].indices[j]];
1878 float dist_sq;
1879
1881 tmp_co,
1882 positions_src[corner_verts_src[tri[0]]],
1883 positions_src[corner_verts_src[tri[1]]],
1884 positions_src[corner_verts_src[tri[2]]]);
1885 dist_sq = len_squared_v3v3(tmp_co, h);
1886 if (dist_sq < best_dist_sq) {
1887 copy_v3_v3(hit_co, h);
1888 best_dist_sq = dist_sq;
1889 }
1890 }
1891 }
1892 }
1893 }
1894
1895 if (mode == MREMAP_MODE_LOOP_POLY_NEAREST) {
1897 corner_verts_src,
1898 positions_src,
1899 hit_co,
1900 &buff_size_interp,
1901 &vcos_interp,
1902 true,
1903 &indices_interp,
1904 &weights_interp,
1905 false,
1906 &best_loop_index_src);
1907
1909 lidx_dst,
1910 isld_res->hit_dist,
1911 best_island_index,
1912 1,
1913 &best_loop_index_src,
1914 &full_weight);
1915 }
1916 else {
1917 const int sources_num = mesh_remap_interp_face_data_get(face_src,
1918 corner_verts_src,
1919 positions_src,
1920 hit_co,
1921 &buff_size_interp,
1922 &vcos_interp,
1923 true,
1924 &indices_interp,
1925 &weights_interp,
1926 true,
1927 nullptr);
1928
1930 lidx_dst,
1931 isld_res->hit_dist,
1932 best_island_index,
1933 sources_num,
1934 indices_interp,
1935 weights_interp);
1936 }
1937
1938 pidx_src_prev = pidx_src;
1939 }
1940 else {
1941 /* No source for this loop in this island. */
1942 /* TODO: would probably be better to get a source
1943 * at all cost in best island anyway? */
1945 r_map, lidx_dst, FLT_MAX, best_island_index, 0, nullptr, nullptr);
1946 }
1947 }
1948 }
1949
1950 BLI_astar_solution_clear(&as_solution);
1951 }
1952 }
1953
1954 for (tindex = 0; tindex < num_trees; tindex++) {
1955 MEM_freeN(islands_res[tindex]);
1956 if (isld_steps_src) {
1957 BLI_astar_graph_free(&as_graphdata[tindex]);
1958 }
1959 }
1960 MEM_freeN(islands_res);
1961 BKE_mesh_loop_islands_free(&island_store);
1962 if (isld_steps_src) {
1963 MEM_freeN(as_graphdata);
1964 BLI_astar_solution_free(&as_solution);
1965 }
1966
1967 if (face_to_corner_tri_map_src) {
1968 MEM_freeN(face_to_corner_tri_map_src);
1969 }
1970 if (face_to_corner_tri_map_src_buff) {
1971 MEM_freeN(face_to_corner_tri_map_src_buff);
1972 }
1973 if (vcos_interp) {
1974 MEM_freeN(vcos_interp);
1975 }
1976 if (indices_interp) {
1977 MEM_freeN(indices_interp);
1978 }
1979 if (weights_interp) {
1980 MEM_freeN(weights_interp);
1981 }
1982 }
1983}
1984
1986 const SpaceTransform *space_transform,
1987 const float max_dist,
1988 const float ray_radius,
1989 const Mesh *mesh_dst,
1990 const Span<float3> vert_positions_dst,
1991 const Span<int> corner_verts_dst,
1992 const blender::OffsetIndices<int> faces_dst,
1993 const Mesh *me_src,
1994 MeshPairRemap *r_map)
1995{
1996 const float full_weight = 1.0f;
1997 const float max_dist_sq = max_dist * max_dist;
1998 blender::Span<blender::float3> face_normals_dst;
1999 blender::float3 tmp_co, tmp_no;
2000
2002
2003 if (mode & (MREMAP_USE_NORMAL | MREMAP_USE_NORPROJ)) {
2004 face_normals_dst = mesh_dst->face_normals();
2005 }
2006
2007 BKE_mesh_remap_init(r_map, int(faces_dst.size()));
2008
2009 if (mode == MREMAP_MODE_TOPOLOGY) {
2010 BLI_assert(faces_dst.size() == me_src->faces_num);
2011 for (const int64_t i : faces_dst.index_range()) {
2012 const int index = int(i);
2013 mesh_remap_item_define(r_map, int(i), FLT_MAX, 0, 1, &index, &full_weight);
2014 }
2015 }
2016 else {
2017 BVHTreeNearest nearest = {0};
2018 BVHTreeRayHit rayhit = {0};
2019 float hit_dist;
2020 const blender::Span<int> tri_faces = me_src->corner_tri_faces();
2021
2022 blender::bke::BVHTreeFromMesh treedata = me_src->bvh_corner_tris();
2023
2024 if (mode == MREMAP_MODE_POLY_NEAREST) {
2025 nearest.index = -1;
2026
2027 for (const int64_t i : faces_dst.index_range()) {
2028 const blender::IndexRange face = faces_dst[i];
2029 tmp_co = blender::bke::mesh::face_center_calc(vert_positions_dst,
2030 corner_verts_dst.slice(face));
2031
2032 /* Convert the vertex to tree coordinates, if needed. */
2033 if (space_transform) {
2034 BLI_space_transform_apply(space_transform, tmp_co);
2035 }
2036
2037 if (mesh_remap_bvhtree_query_nearest(&treedata, &nearest, tmp_co, max_dist_sq, &hit_dist))
2038 {
2039 const int face_index = tri_faces[nearest.index];
2040 mesh_remap_item_define(r_map, int(i), hit_dist, 0, 1, &face_index, &full_weight);
2041 }
2042 else {
2043 /* No source for this dest face! */
2045 }
2046 }
2047 }
2048 else if (mode == MREMAP_MODE_POLY_NOR) {
2049 for (const int64_t i : faces_dst.index_range()) {
2050 const blender::IndexRange face = faces_dst[i];
2051
2052 tmp_co = blender::bke::mesh::face_center_calc(vert_positions_dst,
2053 corner_verts_dst.slice(face));
2054 copy_v3_v3(tmp_no, face_normals_dst[i]);
2055
2056 /* Convert the vertex to tree coordinates, if needed. */
2057 if (space_transform) {
2058 BLI_space_transform_apply(space_transform, tmp_co);
2059 BLI_space_transform_apply_normal(space_transform, tmp_no);
2060 }
2061
2063 &treedata, &rayhit, tmp_co, tmp_no, ray_radius, max_dist, &hit_dist))
2064 {
2065 const int face_index = tri_faces[rayhit.index];
2066 mesh_remap_item_define(r_map, int(i), hit_dist, 0, 1, &face_index, &full_weight);
2067 }
2068 else {
2069 /* No source for this dest face! */
2071 }
2072 }
2073 }
2074 else if (mode == MREMAP_MODE_POLY_POLYINTERP_PNORPROJ) {
2075 /* We cast our rays randomly, with a pseudo-even distribution
2076 * (since we spread across tessellated triangles,
2077 * with additional weighting based on each triangle's relative area). */
2078 RNG *rng = BLI_rng_new(0);
2079
2080 const size_t numfaces_src = size_t(me_src->faces_num);
2081
2082 /* Here it's simpler to just allocate for all faces :/ */
2083 int *indices = MEM_malloc_arrayN<int>(numfaces_src, __func__);
2084 float *weights = MEM_malloc_arrayN<float>(numfaces_src, __func__);
2085
2086 size_t tmp_face_size = MREMAP_DEFAULT_BUFSIZE;
2087 float (*face_vcos_2d)[2] = MEM_malloc_arrayN<float[2]>(tmp_face_size, __func__);
2088 /* Tessellated 2D face, always (num_loops - 2) triangles. */
2089 int (*tri_vidx_2d)[3] = MEM_malloc_arrayN<int[3]>(tmp_face_size - 2, __func__);
2090
2091 for (const int64_t i : faces_dst.index_range()) {
2092 /* For each dst face, we sample some rays from it (2D grid in pnor space)
2093 * and use their hits to interpolate from source faces. */
2094 /* NOTE: dst face is early-converted into src space! */
2095 const blender::IndexRange face = faces_dst[i];
2096
2097 int tot_rays, done_rays = 0;
2098 float face_area_2d_inv, done_area = 0.0f;
2099
2100 blender::float3 pcent_dst;
2101 float to_pnor_2d_mat[3][3], from_pnor_2d_mat[3][3];
2102 float faces_dst_2d_min[2], faces_dst_2d_max[2], faces_dst_2d_z;
2103 float faces_dst_2d_size[2];
2104
2105 float totweights = 0.0f;
2106 float hit_dist_accum = 0.0f;
2107 int sources_num = 0;
2108 const int tris_num = int(face.size()) - 2;
2109 int j;
2110
2111 pcent_dst = blender::bke::mesh::face_center_calc(vert_positions_dst,
2112 corner_verts_dst.slice(face));
2113
2114 copy_v3_v3(tmp_no, face_normals_dst[i]);
2115
2116 /* We do our transform here, else it'd be redone by raycast helper for each ray, ugh! */
2117 if (space_transform) {
2118 BLI_space_transform_apply(space_transform, pcent_dst);
2119 BLI_space_transform_apply_normal(space_transform, tmp_no);
2120 }
2121
2122 copy_vn_fl(weights, int(numfaces_src), 0.0f);
2123
2124 if (UNLIKELY(size_t(face.size()) > tmp_face_size)) {
2125 tmp_face_size = size_t(face.size());
2126 face_vcos_2d = static_cast<float (*)[2]>(
2127 MEM_reallocN(face_vcos_2d, sizeof(*face_vcos_2d) * tmp_face_size));
2128 tri_vidx_2d = static_cast<int (*)[3]>(
2129 MEM_reallocN(tri_vidx_2d, sizeof(*tri_vidx_2d) * (tmp_face_size - 2)));
2130 }
2131
2132 axis_dominant_v3_to_m3(to_pnor_2d_mat, tmp_no);
2133 invert_m3_m3(from_pnor_2d_mat, to_pnor_2d_mat);
2134
2135 mul_m3_v3(to_pnor_2d_mat, pcent_dst);
2136 faces_dst_2d_z = pcent_dst[2];
2137
2138 /* Get (2D) bounding square of our face. */
2139 INIT_MINMAX2(faces_dst_2d_min, faces_dst_2d_max);
2140
2141 for (j = 0; j < face.size(); j++) {
2142 const int vert = corner_verts_dst[face[j]];
2143 copy_v3_v3(tmp_co, vert_positions_dst[vert]);
2144 if (space_transform) {
2145 BLI_space_transform_apply(space_transform, tmp_co);
2146 }
2147 mul_v2_m3v3(face_vcos_2d[j], to_pnor_2d_mat, tmp_co);
2148 minmax_v2v2_v2(faces_dst_2d_min, faces_dst_2d_max, face_vcos_2d[j]);
2149 }
2150
2151 /* We adjust our ray-casting grid to ray_radius (the smaller, the more rays are cast),
2152 * with lower/upper bounds. */
2153 sub_v2_v2v2(faces_dst_2d_size, faces_dst_2d_max, faces_dst_2d_min);
2154
2155 if (ray_radius) {
2156 tot_rays = int((max_ff(faces_dst_2d_size[0], faces_dst_2d_size[1]) / ray_radius) + 0.5f);
2158 }
2159 else {
2160 /* If no radius (pure rays), give max number of rays! */
2162 }
2163 tot_rays *= tot_rays;
2164
2165 face_area_2d_inv = area_poly_v2(face_vcos_2d, uint(face.size()));
2166 /* In case we have a null-area degenerated face... */
2167 face_area_2d_inv = 1.0f / max_ff(face_area_2d_inv, 1e-9f);
2168
2169 /* Tessellate our face. */
2170 if (face.size() == 3) {
2171 tri_vidx_2d[0][0] = 0;
2172 tri_vidx_2d[0][1] = 1;
2173 tri_vidx_2d[0][2] = 2;
2174 }
2175 if (face.size() == 4) {
2176 tri_vidx_2d[0][0] = 0;
2177 tri_vidx_2d[0][1] = 1;
2178 tri_vidx_2d[0][2] = 2;
2179 tri_vidx_2d[1][0] = 0;
2180 tri_vidx_2d[1][1] = 2;
2181 tri_vidx_2d[1][2] = 3;
2182 }
2183 else {
2184 BLI_polyfill_calc(face_vcos_2d, uint(face.size()), -1, (uint(*)[3])tri_vidx_2d);
2185 }
2186
2187 for (j = 0; j < tris_num; j++) {
2188 float *v1 = face_vcos_2d[tri_vidx_2d[j][0]];
2189 float *v2 = face_vcos_2d[tri_vidx_2d[j][1]];
2190 float *v3 = face_vcos_2d[tri_vidx_2d[j][2]];
2191 int rays_num;
2192
2193 /* All this allows us to get 'absolute' number of rays for each tri,
2194 * avoiding accumulating errors over iterations, and helping better even distribution. */
2195 done_area += area_tri_v2(v1, v2, v3);
2196 rays_num = max_ii(int(float(tot_rays) * done_area * face_area_2d_inv + 0.5f) - done_rays,
2197 0);
2198 done_rays += rays_num;
2199
2200 while (rays_num--) {
2201 int n = (ray_radius > 0.0f) ? MREMAP_RAYCAST_APPROXIMATE_NR : 1;
2202 float w = 1.0f;
2203
2204 BLI_rng_get_tri_sample_float_v2(rng, v1, v2, v3, tmp_co);
2205
2206 tmp_co[2] = faces_dst_2d_z;
2207 mul_m3_v3(from_pnor_2d_mat, tmp_co);
2208
2209 /* At this point, tmp_co is a point on our face surface, in mesh_src space! */
2210 while (n--) {
2212 &treedata, &rayhit, tmp_co, tmp_no, ray_radius / w, max_dist, &hit_dist))
2213 {
2214 const int face_index = tri_faces[rayhit.index];
2215 weights[face_index] += w;
2216 totweights += w;
2217 hit_dist_accum += hit_dist;
2218 break;
2219 }
2220 /* Next iteration will get bigger radius but smaller weight! */
2222 }
2223 }
2224 }
2225
2226 if (totweights > 0.0f) {
2227 for (j = 0; j < int(numfaces_src); j++) {
2228 if (!weights[j]) {
2229 continue;
2230 }
2231 /* NOTE: sources_num is always <= j! */
2232 weights[sources_num] = weights[j] / totweights;
2233 indices[sources_num] = j;
2234 sources_num++;
2235 }
2237 r_map, int(i), hit_dist_accum / totweights, 0, sources_num, indices, weights);
2238 }
2239 else {
2240 /* No source for this dest face! */
2242 }
2243 }
2244
2245 MEM_freeN(tri_vidx_2d);
2246 MEM_freeN(face_vcos_2d);
2248 MEM_freeN(weights);
2249 BLI_rng_free(rng);
2250 }
2251 else {
2252 CLOG_WARN(&LOG, "Unsupported mesh-to-mesh face mapping mode (%d)!", mode);
2253 memset(r_map->items, 0, sizeof(*r_map->items) * size_t(faces_dst.size()));
2254 }
2255 }
2256}
2257
2258#undef MREMAP_RAYCAST_APPROXIMATE_NR
2259#undef MREMAP_RAYCAST_APPROXIMATE_FAC
2260#undef MREMAP_RAYCAST_TRI_SAMPLES_MIN
2261#undef MREMAP_RAYCAST_TRI_SAMPLES_MAX
2262#undef MREMAP_DEFAULT_BUFSIZE
2263
CustomData interface, see also DNA_customdata_types.h.
bool(*)(blender::Span< blender::float3 > vert_positions, blender::Span< blender::int2 > edges, blender::Span< bool > uv_seams, blender::OffsetIndices< int > faces, blender::Span< int > corner_verts, blender::Span< int > corner_edges, MeshIslandStore *r_island_store) MeshRemapIslandsCalc
void BKE_mesh_loop_islands_free(MeshIslandStore *island_store)
@ MISLAND_TYPE_POLY
@ MISLAND_TYPE_LOOP
@ MISLAND_TYPE_EDGE
void BKE_mesh_origindex_map_create_corner_tri(MeshElemMap **r_map, int **r_mem, blender::OffsetIndices< int > faces, const int *corner_tri_faces, int corner_tris_num)
#define BLI_assert(a)
Definition BLI_assert.h:46
An implementation of the A* (AStar) algorithm to solve shortest path problem.
bool BLI_astar_graph_solve(BLI_AStarGraph *as_graph, int node_index_src, int node_index_dst, astar_f_cost f_cost_cb, BLI_AStarSolution *r_solution, int max_steps)
Definition astar.cc:140
void BLI_astar_solution_clear(BLI_AStarSolution *as_solution)
Definition astar.cc:91
void BLI_astar_node_init(BLI_AStarGraph *as_graph, int node_index, void *custom_data)
Definition astar.cc:36
void BLI_astar_node_link_add(BLI_AStarGraph *as_graph, int node1_index, int node2_index, float cost, void *custom_data)
Definition astar.cc:41
void BLI_astar_solution_init(BLI_AStarGraph *as_graph, BLI_AStarSolution *as_solution, void *custom_data)
Definition astar.cc:67
void BLI_astar_graph_init(BLI_AStarGraph *as_graph, int node_num, void *custom_data)
Definition astar.cc:116
void BLI_astar_solution_free(BLI_AStarSolution *as_solution)
Definition astar.cc:108
void BLI_astar_graph_free(BLI_AStarGraph *as_graph)
Definition astar.cc:132
#define BLI_BITMAP_NEW(_num, _alloc_string)
Definition BLI_bitmap.h:37
#define BLI_BITMAP_TEST(_bitmap, _index)
Definition BLI_bitmap.h:61
#define BLI_BITMAP_ENABLE(_bitmap, _index)
Definition BLI_bitmap.h:78
unsigned int BLI_bitmap
Definition BLI_bitmap.h:13
int BLI_bvhtree_find_nearest(const BVHTree *tree, const float co[3], BVHTreeNearest *nearest, BVHTree_NearestPointCallback callback, void *userdata)
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 max_ff(float a, float b)
MINLINE int min_ii(int a, int b)
MINLINE int max_ii(int a, int b)
MINLINE float sqrtf_signed(float f)
MINLINE int compare_ff_relative(float a, float b, float max_diff, int max_ulps)
int isect_line_line_v3(const float v1[3], const float v2[3], const float v3[3], const float v4[3], float r_i1[3], float r_i2[3])
MINLINE float area_tri_v2(const float v1[2], const float v2[2], const float v3[2])
void closest_on_tri_to_point_v3(float r[3], const float p[3], const float v1[3], const float v2[3], const float v3[3])
void axis_dominant_v3_to_m3(float r_mat[3][3], const float normal[3])
Normal to x,y matrix.
float line_point_factor_v3(const float p[3], const float l1[3], const float l2[3])
float area_poly_v2(const float verts[][2], unsigned int nr)
Definition math_geom.cc:182
void interp_weights_poly_v3(float w[], float v[][3], int n, const float co[3])
void mul_m3_v3(const float M[3][3], float r[3])
void BLI_space_transform_apply_normal(const struct SpaceTransform *data, float no[3])
void BLI_space_transform_apply(const struct SpaceTransform *data, float co[3])
void unit_m3(float m[3][3])
void mul_v2_m3v3(float r[2], const float M[3][3], const float a[3])
bool invert_m3_m3(float inverse[3][3], const float mat[3][3])
void copy_m4_m3(float m1[4][4], const float m2[3][3])
void BLI_space_transform_global_from_matrices(struct SpaceTransform *data, const float local[4][4], const float target[4][4])
void copy_m4_m4(float m1[4][4], const float m2[4][4])
void unit_m4(float m[4][4])
bool BLI_eigen_solve_selfadjoint_m3(const float m3[3][3], float r_eigen_values[3], float r_eigen_vectors[3][3])
Compute the eigen values and/or vectors of given 3D symmetric (aka adjoint) matrix.
void BLI_covariance_m3_v3n(const float(*cos_v3)[3], int cos_v3_num, bool use_sample_correction, float r_covmat[3][3], float r_center[3])
Compute the covariance matrix of given set of 3D coordinates.
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void negate_v3_v3(float r[3], const float a[3])
void copy_vn_fl(float *array_tar, int size, float val)
void interp_v3_v3v3_slerp_safe(float target[3], const float a[3], const float b[3], float t)
void minmax_v2v2_v2(float min[2], float max[2], const float vec[2])
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
void interp_v3_v3v3(float r[3], const float a[3], const float b[3], float t)
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
void ortho_basis_v3v3_v3(float r_n1[3], float r_n2[3], const float n[3])
#define BLI_MEMARENA_STD_BUFSIZE
MemArena * BLI_memarena_new(size_t bufsize, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(2) ATTR_MALLOC
void * BLI_memarena_calloc(MemArena *ma, size_t size) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_ALLOC_SIZE(2)
void BLI_memarena_free(MemArena *ma) ATTR_NONNULL(1)
void * BLI_memarena_alloc(MemArena *ma, size_t size) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_ALLOC_SIZE(2)
void BLI_polyfill_calc(const float(*coords)[2], unsigned int coords_num, int coords_sign, unsigned int(*r_tris)[3])
Random number functions.
struct RNG * BLI_rng_new(unsigned int seed)
Definition rand.cc:39
void BLI_rng_free(struct RNG *rng) ATTR_NONNULL(1)
Definition rand.cc:53
void BLI_rng_get_tri_sample_float_v2(struct RNG *rng, const float v1[2], const float v2[2], const float v3[2], float r_pt[2]) ATTR_NONNULL()
Definition rand.cc:93
unsigned char uchar
unsigned int uint
#define CLAMP(a, b, c)
#define INIT_MINMAX2(min, max)
#define POINTER_FROM_INT(i)
#define UNUSED_VARS_NDEBUG(...)
#define POINTER_AS_INT(i)
#define UNLIKELY(x)
#define ELEM(...)
#define CLOG_WARN(clg_ref,...)
Definition CLG_log.h:189
@ MREMAP_MODE_VERT_EDGE_NEAREST
@ MREMAP_MODE_VERT_POLYINTERP_VNORPROJ
@ MREMAP_MODE_VERT_FACE_NEAREST
@ MREMAP_MODE_LOOP
@ MREMAP_MODE_EDGE_POLY_NEAREST
@ MREMAP_MODE_POLY
@ MREMAP_MODE_VERT_EDGEINTERP_NEAREST
@ MREMAP_MODE_VERT_NEAREST
@ MREMAP_MODE_LOOP_NEAREST_POLYNOR
@ MREMAP_MODE_EDGE_VERT_NEAREST
@ MREMAP_USE_NORMAL
@ MREMAP_USE_LOOP
@ MREMAP_MODE_TOPOLOGY
@ MREMAP_MODE_VERT
@ MREMAP_USE_POLY
@ MREMAP_MODE_EDGE
@ MREMAP_MODE_EDGE_NEAREST
@ MREMAP_MODE_POLY_NOR
@ MREMAP_MODE_LOOP_NEAREST_LOOPNOR
@ MREMAP_MODE_LOOP_POLY_NEAREST
@ MREMAP_USE_NORPROJ
@ MREMAP_MODE_EDGE_EDGEINTERP_VNORPROJ
@ MREMAP_USE_VERT
@ MREMAP_MODE_POLY_POLYINTERP_PNORPROJ
@ MREMAP_MODE_POLY_NEAREST
@ MREMAP_MODE_VERT_POLYINTERP_NEAREST
Read Guarded memory(de)allocation.
#define MEM_reallocN(vmemh, len)
ATTR_WARN_UNUSED_RESULT const BMVert * v2
long long int int64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:419
AttributeSet attributes
static IndexMask from_bits(BitSpan bits, IndexMaskMemory &memory)
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:419
constexpr int64_t size() const
constexpr int64_t start() const
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:137
constexpr const T * data() const
Definition BLI_span.hh:215
constexpr int64_t size() const
Definition BLI_span.hh:252
void fill(const bool value)
GAttributeReader lookup(const StringRef attribute_id) const
nullptr float
dot(value.rgb, luminance_coefficients)") DEFINE_VALUE("REDUCE(lhs
static ushort indices[]
#define printf(...)
int count
#define LOG(level)
Definition log.h:97
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static char faces[256]
void BKE_mesh_remap_calc_edges_from_mesh(const int mode, const SpaceTransform *space_transform, const float max_dist, const float ray_radius, const Span< float3 > vert_positions_dst, const Span< blender::int2 > edges_dst, const Mesh *me_src, Mesh *me_dst, MeshPairRemap *r_map)
#define POLY_UNSET
#define MREMAP_RAYCAST_TRI_SAMPLES_MIN
#define MREMAP_RAYCAST_TRI_SAMPLES_MAX
void BKE_mesh_remap_calc_loops_from_mesh(const int mode, const SpaceTransform *space_transform, const float max_dist, const float ray_radius, const Mesh *mesh_dst, const Span< float3 > vert_positions_dst, const Span< int > corner_verts_dst, const blender::OffsetIndices< int > faces_dst, const Mesh *me_src, MeshRemapIslandsCalc gen_islands_src, const float islands_precision_src, MeshPairRemap *r_map)
static bool mesh_remap_bvhtree_query_nearest(blender::bke::BVHTreeFromMesh *treedata, BVHTreeNearest *nearest, const float co[3], const float max_dist_sq, float *r_hit_dist)
Definition mesh_remap.cc:49
void BKE_mesh_remap_find_best_match_from_mesh(const Span< float3 > vert_positions_dst, const Mesh *me_src, SpaceTransform *r_space_transform)
static bool mesh_remap_bvhtree_query_raycast(blender::bke::BVHTreeFromMesh *treedata, BVHTreeRayHit *rayhit, const float co[3], const float no[3], const float radius, const float max_dist, float *r_hit_dist)
Definition mesh_remap.cc:78
void BKE_mesh_remap_calc_faces_from_mesh(const int mode, const SpaceTransform *space_transform, const float max_dist, const float ray_radius, const Mesh *mesh_dst, const Span< float3 > vert_positions_dst, const Span< int > corner_verts_dst, const blender::OffsetIndices< int > faces_dst, const Mesh *me_src, MeshPairRemap *r_map)
void BKE_mesh_remap_free(MeshPairRemap *map)
#define MREMAP_RAYCAST_APPROXIMATE_NR
static void mesh_calc_eigen_matrix(const Span< float3 > positions, float r_mat[4][4])
void BKE_mesh_remap_item_define_invalid(MeshPairRemap *map, const int index)
static void mesh_island_to_astar_graph(MeshIslandStore *islands, const int island_index, const blender::Span< blender::float3 > positions, const blender::GroupedSpan< int > edge_to_face_map, const int numedges, const blender::OffsetIndices< int > faces, const blender::Span< int > corner_verts, const blender::Span< int > corner_edges, BLI_AStarGraph *r_as_graph)
void BKE_mesh_remap_calc_verts_from_mesh(const int mode, const SpaceTransform *space_transform, const float max_dist, const float ray_radius, const Span< float3 > vert_positions_dst, const Mesh *me_src, Mesh *me_dst, MeshPairRemap *r_map)
#define MREMAP_RAYCAST_APPROXIMATE_FAC
static int mesh_remap_interp_face_data_get(const blender::IndexRange face, const blender::Span< int > corner_verts, const blender::Span< blender::float3 > positions_src, const float point[3], size_t *buff_size, float(**vcos)[3], const bool use_loops, int **indices, float **weights, const bool do_weights, int *r_closest_index)
#define POLY_COMPLETE
static float mesh_remap_calc_loops_astar_f_cost(BLI_AStarGraph *as_graph, BLI_AStarSolution *as_solution, BLI_AStarGNLink *link, const int node_idx_curr, const int node_idx_next, const int node_idx_dst)
#define MREMAP_DEFAULT_BUFSIZE
static void mesh_remap_item_define(MeshPairRemap *map, const int index, const float, const int island, const int sources_num, const int *indices_src, const float *weights_src)
static void mesh_island_to_astar_graph_edge_process(MeshIslandStore *islands, const int island_index, BLI_AStarGraph *as_graph, const blender::Span< blender::float3 > positions, const blender::OffsetIndices< int > faces, const blender::Span< int > corner_verts, const int edge_idx, BLI_bitmap *done_edges, const blender::GroupedSpan< int > edge_to_face_map, const bool is_edge_innercut, const int *face_island_index_map, float(*face_centers)[3], uchar *face_status)
float BKE_mesh_remap_calc_difference_from_mesh(const SpaceTransform *space_transform, const Span< float3 > vert_positions_dst, const Mesh *me_src)
#define ASTAR_STEPS_MAX
#define POLY_CENTER_INIT
void BKE_mesh_remap_init(MeshPairRemap *map, const int items_num)
int edge_other_vert(const int2 edge, const int vert)
Definition BKE_mesh.hh:370
GroupedSpan< int > build_edge_to_face_map(OffsetIndices< int > faces, Span< int > corner_edges, int edges_num, Array< int > &r_offsets, Array< int > &r_indices)
float3 face_center_calc(Span< float3 > vert_positions, Span< int > face_verts)
GroupedSpan< int > build_vert_to_edge_map(Span< int2 > edges, int verts_num, Array< int > &r_offsets, Array< int > &r_indices)
BVHTreeFromMesh bvhtree_from_mesh_corner_tris_ex(Span< float3 > vert_positions, OffsetIndices< int > faces, Span< int > corner_verts, Span< int3 > corner_tris, const IndexMask &faces_mask)
Definition bvhutils.cc:556
BVHTreeFromMesh bvhtree_from_mesh_verts_ex(Span< float3 > vert_positions, const IndexMask &verts_mask)
Definition bvhutils.cc:450
VecBase< int32_t, 2 > int2
VecBase< int32_t, 3 > int3
VecBase< float, 3 > float3
#define sqrtf
#define FLT_MAX
Definition stdcycles.h:14
void * custom_data
Definition BLI_astar.h:28
BLI_AStarGNode * nodes
Definition BLI_astar.h:53
void * custom_data
Definition BLI_astar.h:55
struct MemArena * mem
Definition BLI_astar.h:57
BLI_AStarGNLink ** prev_links
Definition BLI_astar.h:39
float hit_point[3]
MeshElemMap ** islands
MeshElemMap ** innercuts
MeshPairRemapItem * items
int corners_num
int edges_num
int faces_num
int verts_num
Definition rand.cc:33
BVHTree_NearestPointCallback nearest_callback
BVHTree_RayCastCallback raycast_callback
i
Definition text_draw.cc:230