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