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