Blender V4.3
mesh_merge_by_distance.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
5// #define USE_WELD_DEBUG
6// #define USE_WELD_DEBUG_TIME
7
8#include "BLI_array.hh"
9#include "BLI_bit_vector.hh"
10#include "BLI_index_mask.hh"
11#include "BLI_kdtree.h"
12#include "BLI_math_vector.h"
13#include "BLI_offset_indices.hh"
14#include "BLI_vector.hh"
15
16#include "BKE_customdata.hh"
17#include "BKE_mesh.hh"
18#include "DNA_meshdata_types.h"
19
21#include "GEO_randomize.hh"
22
23#ifdef USE_WELD_DEBUG_TIME
24# include "BLI_timeit.hh"
25
26# if WIN32 and NDEBUG
27# pragma optimize("t", on)
28# endif
29#endif
30
31namespace blender::geometry {
32
33/* Indicates when the element was not computed. */
34#define OUT_OF_CONTEXT int(-1)
35/* Indicates if the edge or face will be collapsed. */
36#define ELEM_COLLAPSED int(-2)
37/* indicates whether an edge or vertex in groups_map will be merged. */
38#define ELEM_MERGED int(-2)
39
40struct WeldEdge {
41 /* Indices relative to the original Mesh. */
43 int vert_a;
44 int vert_b;
45};
46
47struct WeldLoop {
48 union {
49 int flag;
50 struct {
51 /* Indices relative to the original Mesh. */
52 int edge;
53 int vert;
56 };
57 };
58};
59
60struct WeldPoly {
61 union {
62 int flag;
63 struct {
64 /* Indices relative to the original Mesh. */
69
70 /* To find groups. */
73#ifdef USE_WELD_DEBUG
74 int loop_len;
75#endif
76 };
77 };
78};
79
80struct WeldMesh {
81 /* These vectors indicate the index of elements that will participate in the creation of groups.
82 * These groups are used in customdata interpolation (`do_mix_data`). */
85
86 /* Group of edges to be merged. */
89
90 /* References all polygons and loops that will be affected. */
94
95 /* From the actual index of the element in the mesh, it indicates what is the index of the Weld
96 * element above. */
99
103 int face_kill_len; /* Including the new polygons. */
104
105 /* Size of the affected face with more sides. */
107
108#ifdef USE_WELD_DEBUG
109 Span<int> corner_verts;
110 Span<int> corner_edges;
112#endif
113};
114
134
135/* -------------------------------------------------------------------- */
139#ifdef USE_WELD_DEBUG
141 const WeldPoly &wp,
142 Span<WeldLoop> wloop,
143 const Span<int> corner_verts,
144 const Span<int> corner_edges,
145 Span<int> loop_map,
146 int *group_buffer);
147
149
150static void weld_assert_edge_kill_len(Span<int> edge_dest_map, const int expected_kill_len)
151{
152 int kills = 0;
153 for (const int edge_orig : edge_dest_map.index_range()) {
154 if (!ELEM(edge_dest_map[edge_orig], edge_orig, OUT_OF_CONTEXT)) {
155 kills++;
156 }
157 }
158 BLI_assert(kills == expected_kill_len);
159}
160
161static void weld_assert_poly_and_loop_kill_len(WeldMesh *weld_mesh,
162 const int expected_faces_kill_len,
163 const int expected_loop_kill_len)
164{
165 const Span<int> corner_verts = weld_mesh->corner_verts;
166 const Span<int> corner_edges = weld_mesh->corner_edges;
167 const OffsetIndices<int> faces = weld_mesh->faces;
168
169 int poly_kills = 0;
170 int loop_kills = corner_verts.size();
171 for (const int i : faces.index_range()) {
172 int poly_ctx = weld_mesh->face_map[i];
173 if (poly_ctx != OUT_OF_CONTEXT) {
174 const WeldPoly *wp = &weld_mesh->wpoly[poly_ctx];
175 WeldLoopOfPolyIter iter;
177 *wp,
178 weld_mesh->wloop,
179 corner_verts,
180 corner_edges,
181 weld_mesh->loop_map,
182 nullptr))
183 {
184 poly_kills++;
185 continue;
186 }
187 else {
188 if (wp->poly_dst != OUT_OF_CONTEXT) {
189 poly_kills++;
190 continue;
191 }
192 int remain = wp->loop_len;
193 int l = wp->loop_start;
194 while (remain) {
195 int l_next = l + 1;
196 int loop_ctx = weld_mesh->loop_map[l];
197 if (loop_ctx != OUT_OF_CONTEXT) {
198 const WeldLoop *wl = &weld_mesh->wloop[loop_ctx];
199 if (wl->flag != ELEM_COLLAPSED) {
200 loop_kills--;
201 remain--;
202 }
203 }
204 else {
205 loop_kills--;
206 remain--;
207 }
208 l = l_next;
209 }
210 }
211 }
212 else {
213 loop_kills -= faces[i].size();
214 }
215 }
216
217 for (const int i : weld_mesh->wpoly.index_range().take_back(weld_mesh->wpoly_new_len)) {
218 const WeldPoly &wp = weld_mesh->wpoly[i];
219 if (wp.poly_dst != OUT_OF_CONTEXT) {
220 poly_kills++;
221 continue;
222 }
223 int remain = wp.loop_len;
224 int l = wp.loop_start;
225 while (remain) {
226 int l_next = l + 1;
227 int loop_ctx = weld_mesh->loop_map[l];
228 if (loop_ctx != OUT_OF_CONTEXT) {
229 const WeldLoop *wl = &weld_mesh->wloop[loop_ctx];
230 if (wl->flag != ELEM_COLLAPSED) {
231 loop_kills--;
232 remain--;
233 }
234 }
235 else {
236 loop_kills--;
237 remain--;
238 }
239 l = l_next;
240 }
241 }
242
243 BLI_assert(poly_kills == expected_faces_kill_len);
244 BLI_assert(loop_kills == expected_loop_kill_len);
245}
246
247static void weld_assert_poly_no_vert_repetition(const WeldPoly *wp,
248 Span<WeldLoop> wloop,
249 const Span<int> corner_verts,
250 const Span<int> corner_edges,
251 Span<int> loop_map)
252{
253 int i = 0;
254 if (wp->loop_len == 0) {
255 BLI_assert(wp->flag == ELEM_COLLAPSED);
256 return;
257 }
258
259 Array<int, 64> verts(wp->loop_len);
260 WeldLoopOfPolyIter iter;
262 iter, *wp, wloop, corner_verts, corner_edges, loop_map, nullptr))
263 {
264 return;
265 }
266 else {
267 do {
268 verts[i++] = iter.v;
269 } while (weld_iter_loop_of_poly_next(iter));
270 }
271
272 BLI_assert(i == wp->loop_len);
273
274 for (i = 0; i < wp->loop_len; i++) {
275 int va = verts[i];
276 for (int j = i + 1; j < wp->loop_len; j++) {
277 int vb = verts[j];
278 BLI_assert(va != vb);
279 }
280 }
281}
282
283#endif /* USE_WELD_DEBUG */
284
287/* -------------------------------------------------------------------- */
297 const int vert_kill_len)
298{
299 Vector<int> wvert;
300 wvert.reserve(std::min<int>(2 * vert_kill_len, vert_dest_map.size()));
301
302 for (const int i : vert_dest_map.index_range()) {
303 if (vert_dest_map[i] != OUT_OF_CONTEXT) {
304 const int vert_dest = vert_dest_map[i];
305 wvert.append(i);
306
307 if (vert_dest_map[vert_dest] != vert_dest) {
308 /* The target vertex is also part of the context and needs to be referenced.
309 * #vert_dest_map could already indicate this from the beginning, but for better
310 * compatibility, it is done here as well. */
311 vert_dest_map[vert_dest] = vert_dest;
312 wvert.append(vert_dest);
313 }
314 }
315 }
316 return wvert;
317}
318
321/* -------------------------------------------------------------------- */
331 Span<int> vert_dest_map,
332 MutableSpan<int> r_edge_dest_map,
333 int *r_edge_collapsed_len)
334{
335 /* Edge Context. */
336 int edge_collapsed_len = 0;
337
338 Vector<WeldEdge> wedge;
339 wedge.reserve(edges.size());
340
341 for (const int i : edges.index_range()) {
342 int v1 = edges[i][0];
343 int v2 = edges[i][1];
344 int v_dest_1 = vert_dest_map[v1];
345 int v_dest_2 = vert_dest_map[v2];
346 if (v_dest_1 == OUT_OF_CONTEXT && v_dest_2 == OUT_OF_CONTEXT) {
347 r_edge_dest_map[i] = OUT_OF_CONTEXT;
348 continue;
349 }
350
351 const int vert_a = (v_dest_1 == OUT_OF_CONTEXT) ? v1 : v_dest_1;
352 const int vert_b = (v_dest_2 == OUT_OF_CONTEXT) ? v2 : v_dest_2;
353
354 if (vert_a == vert_b) {
355 r_edge_dest_map[i] = ELEM_COLLAPSED;
356 edge_collapsed_len++;
357 }
358 else {
359 wedge.append({i, vert_a, vert_b});
360 r_edge_dest_map[i] = i;
361 }
362 }
363
364 *r_edge_collapsed_len = edge_collapsed_len;
365 return wedge;
366}
367
378 int mvert_num,
379 MutableSpan<int> r_edge_dest_map,
380 int *r_edge_double_kill_len)
381{
382 /* Setup Edge Overlap. */
383 int edge_double_kill_len = 0;
384
385 if (weld_edges.is_empty()) {
386 *r_edge_double_kill_len = edge_double_kill_len;
387 return;
388 }
389
390 /* Add +1 to allow calculation of the length of the last group. */
391 Array<int> v_links(mvert_num + 1, 0);
392
393 for (const WeldEdge &we : weld_edges) {
394 BLI_assert(r_edge_dest_map[we.edge_orig] != ELEM_COLLAPSED);
395 BLI_assert(we.vert_a != we.vert_b);
396 v_links[we.vert_a]++;
397 v_links[we.vert_b]++;
398 }
399
400 int link_len = 0;
401 for (const int i : IndexRange(mvert_num)) {
402 link_len += v_links[i];
403 v_links[i] = link_len;
404 }
405 v_links.last() = link_len;
406
407 BLI_assert(link_len > 0);
408 Array<int> link_edge_buffer(link_len);
409
410 /* Use a reverse for loop to ensure that indexes are assigned in ascending order. */
411 for (int i = weld_edges.size(); i--;) {
412 const WeldEdge &we = weld_edges[i];
413 BLI_assert(r_edge_dest_map[we.edge_orig] != ELEM_COLLAPSED);
414 int dst_vert_a = we.vert_a;
415 int dst_vert_b = we.vert_b;
416
417 link_edge_buffer[--v_links[dst_vert_a]] = i;
418 link_edge_buffer[--v_links[dst_vert_b]] = i;
419 }
420
421 for (const int i : weld_edges.index_range()) {
422 const WeldEdge &we = weld_edges[i];
423 BLI_assert(r_edge_dest_map[we.edge_orig] != OUT_OF_CONTEXT);
424 if (r_edge_dest_map[we.edge_orig] != we.edge_orig) {
425 /* Already a duplicate. */
426 continue;
427 }
428
429 int dst_vert_a = we.vert_a;
430 int dst_vert_b = we.vert_b;
431
432 const int link_a = v_links[dst_vert_a];
433 const int link_b = v_links[dst_vert_b];
434
435 int edges_len_a = v_links[dst_vert_a + 1] - link_a;
436 int edges_len_b = v_links[dst_vert_b + 1] - link_b;
437
438 int edge_orig = we.edge_orig;
439 if (edges_len_a <= 1 || edges_len_b <= 1) {
440 /* This edge would form a group with only one element.
441 * For better performance, mark these edges and avoid forming these groups. */
442 r_edge_dest_map[edge_orig] = OUT_OF_CONTEXT;
443 continue;
444 }
445
446 int *edges_ctx_a = &link_edge_buffer[link_a];
447 int *edges_ctx_b = &link_edge_buffer[link_b];
448
449 const int edge_double_len_prev = edge_double_kill_len;
450 for (; edges_len_a--; edges_ctx_a++) {
451 int e_ctx_a = *edges_ctx_a;
452 if (e_ctx_a == i) {
453 continue;
454 }
455 while (edges_len_b && *edges_ctx_b < e_ctx_a) {
456 edges_ctx_b++;
457 edges_len_b--;
458 }
459 if (edges_len_b == 0) {
460 break;
461 }
462 int e_ctx_b = *edges_ctx_b;
463 if (e_ctx_a == e_ctx_b) {
464 const WeldEdge &we_b = weld_edges[e_ctx_b];
465 BLI_assert(ELEM(we_b.vert_a, dst_vert_a, dst_vert_b));
466 BLI_assert(ELEM(we_b.vert_b, dst_vert_a, dst_vert_b));
467 BLI_assert(we_b.edge_orig != edge_orig);
468 BLI_assert(r_edge_dest_map[we_b.edge_orig] == we_b.edge_orig);
469 r_edge_dest_map[we_b.edge_orig] = edge_orig;
470 edge_double_kill_len++;
471 }
472 }
473 if (edge_double_len_prev == edge_double_kill_len) {
474 /* This edge would form a group with only one element.
475 * For better performance, mark these edges and avoid forming these groups. */
476 r_edge_dest_map[edge_orig] = OUT_OF_CONTEXT;
477 }
478 }
479
480 *r_edge_double_kill_len = edge_double_kill_len;
481}
482
485/* -------------------------------------------------------------------- */
490{
491 if (iter.loop_iter > iter.loop_end) {
492 return false;
493 }
494
495 Span<WeldLoop> wloop = iter.wloop;
496 Span<int> loop_map = iter.loop_map;
497 int l = iter.loop_iter;
498 int l_next = l + 1;
499
500 int loop_ctx = loop_map[l];
501 if (loop_ctx != OUT_OF_CONTEXT) {
502 const WeldLoop *wl = &wloop[loop_ctx];
503#ifdef USE_WELD_DEBUG
505 BLI_assert(iter.v != wl->vert);
506#endif
507 iter.v = wl->vert;
508 iter.e = wl->edge;
509 if (wl->loop_next > l) {
510 /* Allow the loop to break. */
511 l_next = wl->loop_next;
512 }
513
514 if (iter.group) {
515 iter.group_len = 0;
516 int count = iter.loop_ctx_len;
517 for (wl = &wloop[iter.loop_ctx_start]; count--; wl++) {
518 if (wl->vert == iter.v) {
519 iter.group[iter.group_len++] = wl->loop_orig;
520 }
521 }
522 }
523 }
524 else {
525#ifdef USE_WELD_DEBUG
526 BLI_assert(iter.v != iter.corner_verts[l]);
527#endif
528 iter.v = iter.corner_verts[l];
529 iter.e = iter.corner_edges[l];
530 if (iter.group) {
531 iter.group[0] = l;
532 iter.group_len = 1;
533 }
534 }
535
536 iter.loop_iter = l_next;
537 return true;
538}
539
541 const WeldPoly &wp,
542 Span<WeldLoop> wloop,
543 const Span<int> corner_verts,
544 const Span<int> corner_edges,
545 Span<int> loop_map,
546 int *group_buffer)
547{
548 if (wp.flag == ELEM_COLLAPSED) {
549 return false;
550 }
551
552 iter.loop_iter = wp.loop_start;
553 iter.loop_end = wp.loop_end;
555 iter.loop_ctx_len = wp.loop_ctx_len;
556
557 iter.wloop = wloop;
558 iter.corner_verts = corner_verts;
559 iter.corner_edges = corner_edges;
560 iter.loop_map = loop_map;
561 iter.group = group_buffer;
562 iter.group_len = 0;
563
564#ifdef USE_WELD_DEBUG
565 iter.v = OUT_OF_CONTEXT;
566#endif
567 return weld_iter_loop_of_poly_next(iter);
568}
569
576 const Span<int> corner_verts,
577 const Span<int> corner_edges,
578 WeldMesh *r_weld_mesh)
579{
580 Span<int> vert_dest_map = r_weld_mesh->vert_dest_map;
581 Span<int> edge_dest_map = r_weld_mesh->edge_dest_map;
582
583 /* Loop/Poly Context. */
584 Array<int> loop_map(corner_verts.size());
585 Array<int> face_map(faces.size());
586 int wloop_len = 0;
587 int wpoly_len = 0;
588 int max_ctx_poly_len = 4;
589
590 Vector<WeldLoop> wloop;
591 wloop.reserve(corner_verts.size());
592
593 Vector<WeldPoly> wpoly;
594 wpoly.reserve(faces.size());
595
596 int maybe_new_poly = 0;
597
598 for (const int i : faces.index_range()) {
599 const int loopstart = faces[i].start();
600 const int totloop = faces[i].size();
601 const int loop_end = loopstart + totloop - 1;
602 int v_first = corner_verts[loopstart];
603 int v_dest_first = vert_dest_map[v_first];
604 bool is_vert_first_ctx = v_dest_first != OUT_OF_CONTEXT;
605
606 int v_next = v_first;
607 int v_dest_next = v_dest_first;
608 bool is_vert_next_ctx = is_vert_first_ctx;
609
610 int prev_wloop_len = wloop_len;
611 for (const int loop_orig : faces[i]) {
612 int v = v_next;
613 int v_dest = v_dest_next;
614 bool is_vert_ctx = is_vert_next_ctx;
615
616 int loop_next;
617 if (loop_orig != loop_end) {
618 loop_next = loop_orig + 1;
619 v_next = corner_verts[loop_next];
620 v_dest_next = vert_dest_map[v_next];
621 is_vert_next_ctx = v_dest_next != OUT_OF_CONTEXT;
622 }
623 else {
624 loop_next = loopstart;
625 v_next = v_first;
626 v_dest_next = v_dest_first;
627 is_vert_next_ctx = is_vert_first_ctx;
628 }
629
630 if (is_vert_ctx || is_vert_next_ctx) {
631 int e = corner_edges[loop_orig];
632 int e_dest = edge_dest_map[e];
633 bool is_edge_ctx = e_dest != OUT_OF_CONTEXT;
634
636 WeldLoop &wl = wloop.last();
637 wl.vert = is_vert_ctx ? v_dest : v;
638 wl.edge = is_edge_ctx ? e_dest : e;
639 wl.loop_orig = loop_orig;
640 wl.loop_next = loop_next;
641
642 loop_map[loop_orig] = wloop_len++;
643 }
644 else {
645 loop_map[loop_orig] = OUT_OF_CONTEXT;
646 }
647 }
648
649 if (wloop_len != prev_wloop_len) {
650 int loop_ctx_len = wloop_len - prev_wloop_len;
652
653 WeldPoly &wp = wpoly.last();
655 wp.poly_orig = i;
656 wp.loop_start = loopstart;
657 wp.loop_end = loop_end;
658
659 wp.loop_ctx_start = prev_wloop_len;
660 wp.loop_ctx_len = loop_ctx_len;
661
662#ifdef USE_WELD_DEBUG
663 wp.loop_len = totloop;
664#endif
665
666 face_map[i] = wpoly_len++;
667 if (totloop > 5 && loop_ctx_len > 1) {
668 /* We could be smarter here and actually count how many new polygons will be created.
669 * But counting this can be inefficient as it depends on the number of non-consecutive
670 * self face merges. For now just estimate a maximum value. */
671 int max_new = std::min((totloop / 3), loop_ctx_len) - 1;
672 maybe_new_poly += max_new;
673 CLAMP_MIN(max_ctx_poly_len, totloop);
674 }
675 }
676 else {
677 face_map[i] = OUT_OF_CONTEXT;
678 }
679 }
680
681 wpoly.reserve(wpoly.size() + maybe_new_poly);
682
683 r_weld_mesh->wloop = std::move(wloop);
684 r_weld_mesh->wpoly = std::move(wpoly);
685 r_weld_mesh->wpoly_new_len = 0;
686 r_weld_mesh->loop_map = std::move(loop_map);
687 r_weld_mesh->face_map = std::move(face_map);
688 r_weld_mesh->max_face_len = max_ctx_poly_len;
689}
690
691static void weld_poly_split_recursive(int poly_loop_len,
692 Span<int> vert_dest_map,
693 WeldPoly *r_wp,
694 WeldMesh *r_weld_mesh,
695 int *r_poly_kill,
696 int *r_loop_kill)
697{
698 if (poly_loop_len < 3) {
699 return;
700 }
701
702 Span<int> loop_map = r_weld_mesh->loop_map;
703 MutableSpan<WeldLoop> wloop = r_weld_mesh->wloop;
704
705 int loop_kill = 0;
706
707 int loop_end = r_wp->loop_end;
708 int loop_ctx_a = loop_map[loop_end];
709 WeldLoop *wla_prev = (loop_ctx_a != OUT_OF_CONTEXT) ? &wloop[loop_ctx_a] : nullptr;
710 int la = r_wp->loop_start;
711 do {
712 int loop_ctx_a = loop_map[la];
713 if (loop_ctx_a == OUT_OF_CONTEXT) {
714 la++;
715 wla_prev = nullptr;
716 continue;
717 }
718
719 WeldLoop *wla = &wloop[loop_ctx_a];
721
722 int vert_a = wla->vert;
723 if (vert_dest_map[vert_a] == OUT_OF_CONTEXT) {
724 /* Only test vertices that will be merged. */
725 la = wla->loop_next;
726 wla_prev = wla;
727 continue;
728 }
729
730 int dist_a = 1;
731 int lb_prev = la;
732 WeldLoop *wlb_prev = wla;
733 int lb = wla->loop_next;
734 do {
735 int loop_ctx_b = loop_map[lb];
736 if (loop_ctx_b == OUT_OF_CONTEXT) {
737 dist_a++;
738 lb_prev = lb;
739 wlb_prev = nullptr;
740 lb++;
741 continue;
742 }
743
744 WeldLoop *wlb = &wloop[loop_ctx_b];
746 int vert_b = wlb->vert;
747 if (vert_a != vert_b) {
748 dist_a++;
749 lb_prev = lb;
750 wlb_prev = wlb;
751 lb = wlb->loop_next;
752 continue;
753 }
754
755 int dist_b = poly_loop_len - dist_a;
756
757 BLI_assert(dist_a != 0 && dist_b != 0);
758 if (dist_a == 1 || dist_b == 1) {
759 BLI_assert(dist_a != dist_b);
760 BLI_assert((wla->flag == ELEM_COLLAPSED) || (wlb->flag == ELEM_COLLAPSED));
761 }
762 else if (dist_a == 2 && dist_b == 2) {
763 /* All loops are "collapsed".
764 * They could be flagged, but just the face is enough.
765 *
766 * \code{.cc}
767 * WeldLoop *wla_prev = &wloop[loop_ctx_a_prev];
768 * WeldLoop *wlb_prev = &wloop[loop_ctx_b_prev];
769 * wla_prev->flag = ELEM_COLLAPSED;
770 * wla->flag = ELEM_COLLAPSED;
771 * wlb_prev->flag = ELEM_COLLAPSED;
772 * wlb->flag = ELEM_COLLAPSED;
773 * \endcode */
774 loop_kill += 4;
775 dist_b = 0;
776 r_wp->flag = ELEM_COLLAPSED;
777 *r_poly_kill += 1;
778 *r_loop_kill += loop_kill;
779 /* Since all the loops are collapsed, avoid looping through them.
780 * This may result in wrong poly_kill counts. */
781 return;
782 }
783 else {
784 wla_prev->loop_next = lb;
785 wlb_prev->loop_next = la;
786 if (r_wp->loop_start == la) {
787 r_wp->loop_start = lb;
788 }
789
790 if (dist_a == 2) {
791 BLI_assert(wlb_prev->flag != ELEM_COLLAPSED);
792 wla->flag = ELEM_COLLAPSED;
793 wlb_prev->flag = ELEM_COLLAPSED;
794 loop_kill += 2;
795 }
796 else if (dist_b == 2) {
797 BLI_assert(wla_prev->flag != ELEM_COLLAPSED);
798 wlb->flag = ELEM_COLLAPSED;
799 wla_prev->flag = ELEM_COLLAPSED;
800 loop_kill += 2;
801
802 r_wp->loop_start = la;
803 r_wp->loop_end = loop_end = lb_prev;
804
805 poly_loop_len = dist_a;
806 break;
807 }
808 else {
809 r_weld_mesh->wpoly.increase_size_by_unchecked(1);
810 r_weld_mesh->wpoly_new_len++;
811
812 WeldPoly *new_test = &r_weld_mesh->wpoly.last();
813 new_test->poly_dst = OUT_OF_CONTEXT;
814 new_test->poly_orig = r_wp->poly_orig;
815 new_test->loop_start = la;
816 new_test->loop_end = lb_prev;
817 new_test->loop_ctx_start = r_wp->loop_ctx_start;
818 new_test->loop_ctx_len = r_wp->loop_ctx_len;
819
820#ifdef USE_WELD_DEBUG
821 new_test->loop_len = dist_a;
822#endif
824 dist_a, vert_dest_map, new_test, r_weld_mesh, r_poly_kill, r_loop_kill);
825 }
826
827 la = lb;
828 wla = wlb;
829 poly_loop_len = dist_b;
830
831 dist_a = 1;
832 }
833
834 wlb_prev = wlb;
835 lb_prev = lb;
836 lb = wlb->loop_next;
837 } while (lb_prev != loop_end);
838
839 wla_prev = wla;
840 if (la == loop_end) {
841 /* No need to start again. */
842 break;
843 }
844 la = wla->loop_next;
845 } while (la != loop_end);
846
847 *r_loop_kill += loop_kill;
848#ifdef USE_WELD_DEBUG
849 r_wp->loop_len = poly_loop_len;
850 weld_assert_poly_no_vert_repetition(
851 r_wp, wloop, r_weld_mesh->corner_verts, r_weld_mesh->corner_edges, r_weld_mesh->loop_map);
852#endif
853}
854
864static void weld_poly_loop_ctx_setup_collapsed_and_split(const int remain_edge_ctx_len,
865 WeldMesh *r_weld_mesh)
866{
867 if (remain_edge_ctx_len == 0) {
868 r_weld_mesh->face_kill_len = r_weld_mesh->wpoly.size();
869 r_weld_mesh->loop_kill_len = r_weld_mesh->wloop.size();
870
871 for (WeldPoly &wp : r_weld_mesh->wpoly) {
872 wp.flag = ELEM_COLLAPSED;
873 }
874
875 return;
876 }
877
878 WeldPoly *wpoly = r_weld_mesh->wpoly.data();
879 MutableSpan<WeldLoop> wloop = r_weld_mesh->wloop;
880 Span<int> loop_map = r_weld_mesh->loop_map;
881 Span<int> vert_dest_map = r_weld_mesh->vert_dest_map;
882
883 int face_kill_len = 0;
884 int loop_kill_len = 0;
885
886 /* Setup Poly/Loop. */
887 /* `wpoly.size()` may change during the loop,
888 * so make it clear that we are only working with the original `wpoly` items. */
889 IndexRange wpoly_original_range = r_weld_mesh->wpoly.index_range();
890 for (const int i : wpoly_original_range) {
891 WeldPoly &wp = wpoly[i];
892 int poly_loop_len = (wp.loop_end - wp.loop_start) + 1;
893 WeldLoop *wl_prev = nullptr;
894 bool chang_loop_start = false;
895 int l = wp.loop_start;
896 do {
897 int loop_ctx = loop_map[l];
898 if (loop_ctx == OUT_OF_CONTEXT) {
899 wl_prev = nullptr;
900 continue;
901 }
902
903 WeldLoop *wl = &wloop[loop_ctx];
904 const int edge_dest = wl->edge;
905 if (edge_dest == ELEM_COLLAPSED) {
906 wl->flag = ELEM_COLLAPSED;
907 if (poly_loop_len == 3) {
908 wp.flag = ELEM_COLLAPSED;
909 face_kill_len++;
910 loop_kill_len += 3;
911 poly_loop_len = 0;
912 break;
913 }
914
915 if (l == wp.loop_start) {
916 chang_loop_start = true;
917 }
918
919 loop_kill_len++;
920 poly_loop_len--;
921 }
922 else {
923 if (chang_loop_start) {
924 wp.loop_start = l;
925 chang_loop_start = false;
926 }
927 if (wl_prev) {
928 wl_prev->loop_next = l;
929 }
930 wl_prev = wl;
931 BLI_assert(wl->loop_next == l + 1 || l == wp.loop_end);
932 }
933 } while (l++ != wp.loop_end);
934
935 if (poly_loop_len) {
936 if (wl_prev) {
937 wl_prev->loop_next = wp.loop_start;
938 wp.loop_end = wl_prev->loop_orig;
939 }
940
941#ifdef USE_WELD_DEBUG
942 wp.loop_len = poly_loop_len;
943
944 for (int loop_orig : IndexRange(wp.loop_start, poly_loop_len)) {
945 int loop_ctx = loop_map[loop_orig];
946 if (loop_ctx == OUT_OF_CONTEXT) {
947 continue;
948 }
949
950 WeldLoop *wl = &wloop[loop_ctx];
951 if (wl->flag == ELEM_COLLAPSED) {
952 continue;
953 }
954
955 loop_ctx = loop_map[wl->loop_next];
956 if (loop_ctx == OUT_OF_CONTEXT) {
957 continue;
958 }
959
960 wl = &wloop[loop_ctx];
962 }
963#endif
964
966 poly_loop_len, vert_dest_map, &wp, r_weld_mesh, &face_kill_len, &loop_kill_len);
967 }
968 }
969
970 r_weld_mesh->face_kill_len = face_kill_len;
971 r_weld_mesh->loop_kill_len = loop_kill_len;
972
973#ifdef USE_WELD_DEBUG
974 weld_assert_poly_and_loop_kill_len(
975 r_weld_mesh, r_weld_mesh->face_kill_len, r_weld_mesh->loop_kill_len);
976#endif
977}
978
979static int poly_find_doubles(const OffsetIndices<int> poly_corners_offsets,
980 const int poly_num,
981 const Span<int> corners,
982 const int corner_index_max,
983 Vector<int> &r_doubles_offsets,
984 Array<int> &r_doubles_buffer)
985{
986 /* Fills the `r_buffer` buffer with the intersection of the arrays in `buffer_a` and `buffer_b`.
987 * `buffer_a` and `buffer_b` have a sequence of sorted, non-repeating indices representing
988 * polygons. */
989 const auto intersect = [](const Span<int> buffer_a,
990 const Span<int> buffer_b,
991 const BitVector<> &is_double,
992 int *r_buffer) {
993 int result_num = 0;
994 int index_a = 0, index_b = 0;
995 while (index_a < buffer_a.size() && index_b < buffer_b.size()) {
996 const int value_a = buffer_a[index_a];
997 const int value_b = buffer_b[index_b];
998 if (value_a < value_b) {
999 index_a++;
1000 }
1001 else if (value_b < value_a) {
1002 index_b++;
1003 }
1004 else {
1005 /* Equality. */
1006
1007 /* Do not add duplicates.
1008 * As they are already in the original array, this can cause buffer overflow. */
1009 if (!is_double[value_a]) {
1010 r_buffer[result_num++] = value_a;
1011 }
1012 index_a++;
1013 index_b++;
1014 }
1015 }
1016
1017 return result_num;
1018 };
1019
1020 /* Add +1 to allow calculation of the length of the last group. */
1021 Array<int> linked_faces_offset(corner_index_max + 1, 0);
1022
1023 for (const int elem_index : corners) {
1024 linked_faces_offset[elem_index]++;
1025 }
1026
1027 int link_faces_buffer_len = 0;
1028 for (const int elem_index : IndexRange(corner_index_max)) {
1029 link_faces_buffer_len += linked_faces_offset[elem_index];
1030 linked_faces_offset[elem_index] = link_faces_buffer_len;
1031 }
1032 linked_faces_offset[corner_index_max] = link_faces_buffer_len;
1033
1034 if (link_faces_buffer_len == 0) {
1035 return 0;
1036 }
1037
1038 Array<int> linked_faces_buffer(link_faces_buffer_len);
1039
1040 /* Use a reverse for loop to ensure that indexes are assigned in ascending order. */
1041 for (int face_index = poly_num; face_index--;) {
1042 if (poly_corners_offsets[face_index].is_empty()) {
1043 continue;
1044 }
1045
1046 for (int corner_index = poly_corners_offsets[face_index].last();
1047 corner_index >= poly_corners_offsets[face_index].first();
1048 corner_index--)
1049 {
1050 const int elem_index = corners[corner_index];
1051 linked_faces_buffer[--linked_faces_offset[elem_index]] = face_index;
1052 }
1053 }
1054
1055 Array<int> doubles_buffer(poly_num);
1056
1057 Vector<int> doubles_offsets;
1058 doubles_offsets.reserve((poly_num / 2) + 1);
1059 doubles_offsets.append(0);
1060
1061 BitVector<> is_double(poly_num, false);
1062
1063 int doubles_buffer_num = 0;
1064 int doubles_num = 0;
1065 for (const int face_index : IndexRange(poly_num)) {
1066 if (is_double[face_index]) {
1067 continue;
1068 }
1069
1070 int corner_num = poly_corners_offsets[face_index].size();
1071 if (corner_num == 0) {
1072 continue;
1073 }
1074
1075 /* Set or overwrite the first slot of the possible group. */
1076 doubles_buffer[doubles_buffer_num] = face_index;
1077
1078 int corner_first = poly_corners_offsets[face_index].first();
1079 int elem_index = corners[corner_first];
1080 int link_offs = linked_faces_offset[elem_index];
1081 int faces_a_num = linked_faces_offset[elem_index + 1] - link_offs;
1082 if (faces_a_num == 1) {
1083 BLI_assert(linked_faces_buffer[linked_faces_offset[elem_index]] == face_index);
1084 continue;
1085 }
1086
1087 const int *faces_a = &linked_faces_buffer[link_offs];
1088 int poly_to_test;
1089
1090 /* Skip polygons with lower index as these have already been checked. */
1091 do {
1092 poly_to_test = *faces_a;
1093 faces_a++;
1094 faces_a_num--;
1095 } while (poly_to_test != face_index);
1096
1097 int *isect_result = doubles_buffer.data() + doubles_buffer_num + 1;
1098
1099 /* `faces_a` are the polygons connected to the first corner. So skip the first corner. */
1100 for (int corner_index : IndexRange(corner_first + 1, corner_num - 1)) {
1101 elem_index = corners[corner_index];
1102 link_offs = linked_faces_offset[elem_index];
1103 int faces_b_num = linked_faces_offset[elem_index + 1] - link_offs;
1104 const int *faces_b = &linked_faces_buffer[link_offs];
1105
1106 /* Skip polygons with lower index as these have already been checked. */
1107 do {
1108 poly_to_test = *faces_b;
1109 faces_b++;
1110 faces_b_num--;
1111 } while (poly_to_test != face_index);
1112
1113 doubles_num = intersect(Span<int>{faces_a, faces_a_num},
1114 Span<int>{faces_b, faces_b_num},
1115 is_double,
1116 isect_result);
1117
1118 if (doubles_num == 0) {
1119 break;
1120 }
1121
1122 /* Intersect the last result. */
1123 faces_a = isect_result;
1124 faces_a_num = doubles_num;
1125 }
1126
1127 if (doubles_num) {
1128 for (const int poly_double : Span<int>{isect_result, doubles_num}) {
1129 BLI_assert(poly_double > face_index);
1130 is_double[poly_double].set();
1131 }
1132 doubles_buffer_num += doubles_num;
1133 doubles_offsets.append(++doubles_buffer_num);
1134
1135 if ((doubles_buffer_num + 1) == poly_num) {
1136 /* The last slot is the remaining unduplicated face.
1137 * Avoid checking intersection as there are no more slots left. */
1138 break;
1139 }
1140 }
1141 }
1142
1143 r_doubles_buffer = std::move(doubles_buffer);
1144 r_doubles_offsets = std::move(doubles_offsets);
1145 return doubles_buffer_num - (r_doubles_offsets.size() - 1);
1146}
1147
1148static void weld_poly_find_doubles(const Span<int> corner_verts,
1149 const Span<int> corner_edges,
1150 const int medge_len,
1151 WeldMesh *r_weld_mesh)
1152{
1153 if (r_weld_mesh->face_kill_len == r_weld_mesh->wpoly.size()) {
1154 return;
1155 }
1156
1157 WeldPoly *wpoly = r_weld_mesh->wpoly.data();
1158 MutableSpan<WeldLoop> wloop = r_weld_mesh->wloop;
1159 Span<int> loop_map = r_weld_mesh->loop_map;
1160 int face_index = 0;
1161
1162 const int face_len = r_weld_mesh->wpoly.size();
1163 Array<int> poly_offs_(face_len + 1);
1164 Vector<int> new_corner_edges;
1165 new_corner_edges.reserve(corner_verts.size() - r_weld_mesh->loop_kill_len);
1166
1167 for (const WeldPoly &wp : r_weld_mesh->wpoly) {
1168 poly_offs_[face_index++] = new_corner_edges.size();
1169
1170 WeldLoopOfPolyIter iter;
1172 iter, wp, wloop, corner_verts, corner_edges, loop_map, nullptr))
1173 {
1174 continue;
1175 }
1176
1177 if (wp.poly_dst != OUT_OF_CONTEXT) {
1178 continue;
1179 }
1180
1181 do {
1182 new_corner_edges.append(iter.e);
1183 } while (weld_iter_loop_of_poly_next(iter));
1184 }
1185
1186 poly_offs_[face_len] = new_corner_edges.size();
1187 OffsetIndices<int> poly_offs(poly_offs_);
1188
1189 Vector<int> doubles_offsets;
1190 Array<int> doubles_buffer;
1191 const int doubles_num = poly_find_doubles(
1192 poly_offs, face_len, new_corner_edges, medge_len, doubles_offsets, doubles_buffer);
1193
1194 if (doubles_num) {
1195 int loop_kill_num = 0;
1196
1197 OffsetIndices<int> doubles_offset_indices(doubles_offsets);
1198 for (const int i : doubles_offset_indices.index_range()) {
1199 const int poly_dst = wpoly[doubles_buffer[doubles_offsets[i]]].poly_orig;
1200
1201 for (const int offset : doubles_offset_indices[i].drop_front(1)) {
1202 const int wpoly_index = doubles_buffer[offset];
1203 WeldPoly &wp = wpoly[wpoly_index];
1204
1206 wp.poly_dst = poly_dst;
1207 loop_kill_num += poly_offs[wpoly_index].size();
1208 }
1209 }
1210
1211 r_weld_mesh->face_kill_len += doubles_num;
1212 r_weld_mesh->loop_kill_len += loop_kill_num;
1213 }
1214
1215#ifdef USE_WELD_DEBUG
1216 weld_assert_poly_and_loop_kill_len(
1217 r_weld_mesh, r_weld_mesh->face_kill_len, r_weld_mesh->loop_kill_len);
1218#endif
1219}
1220
1223/* -------------------------------------------------------------------- */
1227static void weld_mesh_context_create(const Mesh &mesh,
1228 MutableSpan<int> vert_dest_map,
1229 const int vert_kill_len,
1230 const bool get_doubles,
1231 WeldMesh *r_weld_mesh)
1232{
1233 const Span<int2> edges = mesh.edges();
1234 const OffsetIndices faces = mesh.faces();
1235 const Span<int> corner_verts = mesh.corner_verts();
1236 const Span<int> corner_edges = mesh.corner_edges();
1237
1238 Vector<int> wvert = weld_vert_ctx_alloc_and_setup(vert_dest_map, vert_kill_len);
1239 r_weld_mesh->vert_kill_len = vert_kill_len;
1240
1241 r_weld_mesh->edge_dest_map.reinitialize(edges.size());
1242 r_weld_mesh->vert_dest_map = vert_dest_map;
1243
1244#ifdef USE_WELD_DEBUG
1245 r_weld_mesh->corner_verts = corner_verts;
1246 r_weld_mesh->corner_edges = corner_edges;
1247 r_weld_mesh->faces = faces;
1248#endif
1249
1250 int edge_collapsed_len, edge_double_kill_len;
1252 edges, vert_dest_map, r_weld_mesh->edge_dest_map, &edge_collapsed_len);
1253
1254 weld_edge_find_doubles(wedge, mesh.verts_num, r_weld_mesh->edge_dest_map, &edge_double_kill_len);
1255
1256 r_weld_mesh->edge_kill_len = edge_collapsed_len + edge_double_kill_len;
1257
1258#ifdef USE_WELD_DEBUG
1259 weld_assert_edge_kill_len(r_weld_mesh->edge_dest_map, r_weld_mesh->edge_kill_len);
1260#endif
1261
1262 weld_poly_loop_ctx_alloc(faces, corner_verts, corner_edges, r_weld_mesh);
1263
1264 weld_poly_loop_ctx_setup_collapsed_and_split(wedge.size() - edge_double_kill_len, r_weld_mesh);
1265
1266 weld_poly_find_doubles(corner_verts, corner_edges, edges.size(), r_weld_mesh);
1267
1268 if (get_doubles) {
1269 r_weld_mesh->double_verts = std::move(wvert);
1270 r_weld_mesh->double_edges.reserve(wedge.size());
1271 for (WeldEdge &we : wedge) {
1272 if (r_weld_mesh->edge_dest_map[we.edge_orig] >= 0) {
1273 r_weld_mesh->double_edges.append(we.edge_orig);
1274 }
1275 }
1276 }
1277}
1278
1281/* -------------------------------------------------------------------- */
1298static void merge_groups_create(Span<int> dest_map,
1299 Span<int> double_elems,
1300 MutableSpan<int> r_groups_offsets,
1301 Array<int> &r_groups_buffer)
1302{
1303 BLI_assert(r_groups_offsets.size() == dest_map.size() + 1);
1304 r_groups_offsets.fill(0);
1305
1306 /* TODO: Check using #array_utils::count_indices instead. At the moment it cannot be used
1307 * because `dest_map` has negative values and `double_elems` (which indicates only the indexes to
1308 * be read) is not used. */
1309 for (const int elem_orig : double_elems) {
1310 const int elem_dest = dest_map[elem_orig];
1311 r_groups_offsets[elem_dest]++;
1312 }
1313
1314 int offs = 0;
1315 for (const int i : dest_map.index_range()) {
1316 offs += r_groups_offsets[i];
1317 r_groups_offsets[i] = offs;
1318 }
1319 r_groups_offsets.last() = offs;
1320
1321 r_groups_buffer.reinitialize(offs);
1322 BLI_assert(r_groups_buffer.size() == double_elems.size());
1323
1324 /* Use a reverse for loop to ensure that indices are assigned in ascending order. */
1325 for (int i = double_elems.size(); i--;) {
1326 const int elem_orig = double_elems[i];
1327 const int elem_dest = dest_map[elem_orig];
1328 r_groups_buffer[--r_groups_offsets[elem_dest]] = elem_orig;
1329 }
1330}
1331
1333 const CustomData *source, CustomData *dest, const int *src_indices, int count, int dest_index)
1334{
1335 if (count == 1) {
1336 CustomData_copy_data(source, dest, src_indices[0], dest_index, 1);
1337 return;
1338 }
1339
1340 CustomData_interp(source, dest, (const int *)src_indices, nullptr, nullptr, count, dest_index);
1341
1342 int src_i, dest_i;
1343 int j;
1344
1345 int vs_flag = 0;
1346
1347 /* interpolates a layer at a time */
1348 dest_i = 0;
1349 for (src_i = 0; src_i < source->totlayer; src_i++) {
1350 const eCustomDataType type = eCustomDataType(source->layers[src_i].type);
1351
1352 /* find the first dest layer with type >= the source type
1353 * (this should work because layers are ordered by type)
1354 */
1355 while (dest_i < dest->totlayer && dest->layers[dest_i].type < type) {
1356 dest_i++;
1357 }
1358
1359 /* if there are no more dest layers, we're done */
1360 if (dest_i == dest->totlayer) {
1361 break;
1362 }
1363
1364 /* if we found a matching layer, add the data */
1365 if (dest->layers[dest_i].type == type) {
1366 void *src_data = source->layers[src_i].data;
1367 if (type == CD_MVERT_SKIN) {
1368 /* The `typeInfo->interp` of #CD_MVERT_SKIN does not include the flags, so #MVERT_SKIN_ROOT
1369 * and #MVERT_SKIN_LOOSE are lost after the interpolation.
1370 *
1371 * This behavior is not incorrect. Ideally, islands should be checked to avoid repeated
1372 * roots.
1373 *
1374 * However, for now, to prevent the loss of flags, they are simply re-added if any of the
1375 * merged vertices have them. */
1376 for (j = 0; j < count; j++) {
1377 MVertSkin *vs = &((MVertSkin *)src_data)[src_indices[j]];
1378 vs_flag |= vs->flag;
1379 }
1380 }
1381 else if (CustomData_layer_has_interp(dest, dest_i)) {
1382 /* Already calculated.
1383 * TODO: Optimize by exposing `typeInfo->interp`. */
1384 }
1385 else if (CustomData_layer_has_math(dest, dest_i)) {
1386 const int size = CustomData_sizeof(type);
1387 void *dst_data = dest->layers[dest_i].data;
1388 void *v_dst = POINTER_OFFSET(dst_data, size_t(dest_index) * size);
1389 for (j = 0; j < count; j++) {
1391 type, v_dst, POINTER_OFFSET(src_data, size_t(src_indices[j]) * size));
1392 }
1393 }
1394 else {
1395 CustomData_copy_layer_type_data(source, dest, type, src_indices[0], dest_index, 1);
1396 }
1397
1398 /* if there are multiple source & dest layers of the same type,
1399 * we don't want to copy all source layers to the same dest, so
1400 * increment dest_i
1401 */
1402 dest_i++;
1403 }
1404 }
1405
1406 float fac = 1.0f / count;
1407
1408 for (dest_i = 0; dest_i < dest->totlayer; dest_i++) {
1409 CustomDataLayer *layer_dst = &dest->layers[dest_i];
1410 const eCustomDataType type = eCustomDataType(layer_dst->type);
1411 if (type == CD_MVERT_SKIN) {
1412 MVertSkin *vs = &((MVertSkin *)layer_dst->data)[dest_index];
1413 vs->flag = vs_flag;
1414 }
1415 else if (CustomData_layer_has_interp(dest, dest_i)) {
1416 /* Already calculated. */
1417 }
1418 else if (CustomData_layer_has_math(dest, dest_i)) {
1419 const int size = CustomData_sizeof(type);
1420 void *dst_data = layer_dst->data;
1421 void *v_dst = POINTER_OFFSET(dst_data, size_t(dest_index) * size);
1422 CustomData_data_multiply(type, v_dst, fac);
1423 }
1424 }
1425}
1426
1442static void merge_customdata_all(const CustomData *source,
1443 CustomData *dest,
1444 Span<int> dest_map,
1445 Span<int> double_elems,
1446 const int dest_size,
1447 const bool do_mix_data,
1448 Array<int> &r_final_map)
1449{
1450 UNUSED_VARS_NDEBUG(dest_size);
1451
1452 const int source_size = dest_map.size();
1453
1454 MutableSpan<int> groups_offs_;
1455 Array<int> groups_buffer;
1456 if (do_mix_data) {
1457 r_final_map.reinitialize(source_size + 1);
1458
1459 /* Be careful when setting values to this array as it uses the same buffer as `r_final_map`. */
1460 groups_offs_ = r_final_map;
1461 merge_groups_create(dest_map, double_elems, groups_offs_, groups_buffer);
1462 }
1463 else {
1464 r_final_map.reinitialize(source_size);
1465 }
1466 OffsetIndices<int> groups_offs(groups_offs_);
1467
1468 bool finalize_map = false;
1469 int dest_index = 0;
1470 for (int i = 0; i < source_size; i++) {
1471 const int source_index = i;
1472 int count = 0;
1473 while (i < source_size && dest_map[i] == OUT_OF_CONTEXT) {
1474 r_final_map[i] = dest_index + count;
1475 count++;
1476 i++;
1477 }
1478 if (count) {
1479 CustomData_copy_data(source, dest, source_index, dest_index, count);
1480 dest_index += count;
1481 }
1482 if (i == source_size) {
1483 break;
1484 }
1485 if (dest_map[i] == i) {
1486 if (do_mix_data) {
1487 const IndexRange grp_buffer_range = groups_offs[i];
1488 customdata_weld(source,
1489 dest,
1490 &groups_buffer[grp_buffer_range.start()],
1491 grp_buffer_range.size(),
1492 dest_index);
1493 }
1494 else {
1495 CustomData_copy_data(source, dest, i, dest_index, 1);
1496 }
1497 r_final_map[i] = dest_index;
1498 dest_index++;
1499 }
1500 else if (dest_map[i] == ELEM_COLLAPSED) {
1501 /* Any value will do. This field must not be accessed anymore. */
1502 r_final_map[i] = 0;
1503 }
1504 else {
1505 const int elem_dest = dest_map[i];
1506 BLI_assert(elem_dest != OUT_OF_CONTEXT);
1507 BLI_assert(dest_map[elem_dest] == elem_dest);
1508 if (elem_dest < i) {
1509 r_final_map[i] = r_final_map[elem_dest];
1510 BLI_assert(r_final_map[i] < dest_size);
1511 }
1512 else {
1513 /* Mark as negative to set at the end. */
1514 r_final_map[i] = -elem_dest;
1515 finalize_map = true;
1516 }
1517 }
1518 }
1519
1520 if (finalize_map) {
1521 for (const int i : r_final_map.index_range()) {
1522 if (r_final_map[i] < 0) {
1523 r_final_map[i] = r_final_map[-r_final_map[i]];
1524 BLI_assert(r_final_map[i] < dest_size);
1525 }
1526 BLI_assert(r_final_map[i] >= 0);
1527 }
1528 }
1529
1530 BLI_assert(dest_index == dest_size);
1531}
1532
1535/* -------------------------------------------------------------------- */
1539static Mesh *create_merged_mesh(const Mesh &mesh,
1540 MutableSpan<int> vert_dest_map,
1541 const int removed_vertex_count,
1542 const bool do_mix_data)
1543{
1544#ifdef USE_WELD_DEBUG_TIME
1545 SCOPED_TIMER(__func__);
1546#endif
1547
1548 const OffsetIndices src_faces = mesh.faces();
1549 const Span<int> src_corner_verts = mesh.corner_verts();
1550 const Span<int> src_corner_edges = mesh.corner_edges();
1551 const int totvert = mesh.verts_num;
1552 const int totedge = mesh.edges_num;
1553
1554 WeldMesh weld_mesh;
1555 weld_mesh_context_create(mesh, vert_dest_map, removed_vertex_count, do_mix_data, &weld_mesh);
1556
1557 const int result_nverts = totvert - weld_mesh.vert_kill_len;
1558 const int result_nedges = totedge - weld_mesh.edge_kill_len;
1559 const int result_nloops = src_corner_verts.size() - weld_mesh.loop_kill_len;
1560 const int result_nfaces = src_faces.size() - weld_mesh.face_kill_len + weld_mesh.wpoly_new_len;
1561
1563 &mesh, result_nverts, result_nedges, result_nfaces, result_nloops);
1564 MutableSpan<int2> dst_edges = result->edges_for_write();
1565 MutableSpan<int> dst_face_offsets = result->face_offsets_for_write();
1566 MutableSpan<int> dst_corner_verts = result->corner_verts_for_write();
1567 MutableSpan<int> dst_corner_edges = result->corner_edges_for_write();
1568
1569 /* Vertices. */
1570
1571 Array<int> vert_final_map;
1572
1573 merge_customdata_all(&mesh.vert_data,
1574 &result->vert_data,
1575 vert_dest_map,
1576 weld_mesh.double_verts,
1577 result_nverts,
1578 do_mix_data,
1579 vert_final_map);
1580
1581 /* Edges. */
1582
1583 Array<int> edge_final_map;
1584
1585 merge_customdata_all(&mesh.edge_data,
1586 &result->edge_data,
1587 weld_mesh.edge_dest_map,
1588 weld_mesh.double_edges,
1589 result_nedges,
1590 do_mix_data,
1591 edge_final_map);
1592
1593 for (int2 &edge : dst_edges) {
1594 edge[0] = vert_final_map[edge[0]];
1595 edge[1] = vert_final_map[edge[1]];
1596 BLI_assert(edge[0] != edge[1]);
1597 BLI_assert(IN_RANGE_INCL(edge[0], 0, result_nverts - 1));
1598 BLI_assert(IN_RANGE_INCL(edge[1], 0, result_nverts - 1));
1599 }
1600
1601 /* Faces/Loops. */
1602
1603 int r_i = 0;
1604 int loop_cur = 0;
1605 Array<int, 64> group_buffer(weld_mesh.max_face_len);
1606 for (const int i : src_faces.index_range()) {
1607 const int loop_start = loop_cur;
1608 const int poly_ctx = weld_mesh.face_map[i];
1609 if (poly_ctx == OUT_OF_CONTEXT) {
1610 int mp_loop_len = src_faces[i].size();
1612 &mesh.corner_data, &result->corner_data, src_faces[i].start(), loop_cur, mp_loop_len);
1613 for (; mp_loop_len--; loop_cur++) {
1614 dst_corner_verts[loop_cur] = vert_final_map[dst_corner_verts[loop_cur]];
1615 dst_corner_edges[loop_cur] = edge_final_map[dst_corner_edges[loop_cur]];
1616 }
1617 }
1618 else {
1619 const WeldPoly &wp = weld_mesh.wpoly[poly_ctx];
1620 WeldLoopOfPolyIter iter;
1622 wp,
1623 weld_mesh.wloop,
1624 src_corner_verts,
1625 src_corner_edges,
1626 weld_mesh.loop_map,
1627 group_buffer.data()))
1628 {
1629 continue;
1630 }
1631
1632 if (wp.poly_dst != OUT_OF_CONTEXT) {
1633 continue;
1634 }
1635 do {
1636 customdata_weld(&mesh.corner_data,
1637 &result->corner_data,
1638 group_buffer.data(),
1639 iter.group_len,
1640 loop_cur);
1641 dst_corner_verts[loop_cur] = vert_final_map[iter.v];
1642 dst_corner_edges[loop_cur] = edge_final_map[iter.e];
1643 loop_cur++;
1644 } while (weld_iter_loop_of_poly_next(iter));
1645 }
1646
1647 CustomData_copy_data(&mesh.face_data, &result->face_data, i, r_i, 1);
1648 dst_face_offsets[r_i] = loop_start;
1649 r_i++;
1650 }
1651
1652 /* New Polygons. */
1653 for (const int i : weld_mesh.wpoly.index_range().take_back(weld_mesh.wpoly_new_len)) {
1654 const WeldPoly &wp = weld_mesh.wpoly[i];
1655 const int loop_start = loop_cur;
1656 WeldLoopOfPolyIter iter;
1658 wp,
1659 weld_mesh.wloop,
1660 src_corner_verts,
1661 src_corner_edges,
1662 weld_mesh.loop_map,
1663 group_buffer.data()))
1664 {
1665 continue;
1666 }
1667
1668 if (wp.poly_dst != OUT_OF_CONTEXT) {
1669 continue;
1670 }
1671 do {
1673 &mesh.corner_data, &result->corner_data, group_buffer.data(), iter.group_len, loop_cur);
1674 dst_corner_verts[loop_cur] = vert_final_map[iter.v];
1675 dst_corner_edges[loop_cur] = edge_final_map[iter.e];
1676 loop_cur++;
1677 } while (weld_iter_loop_of_poly_next(iter));
1678
1679 dst_face_offsets[r_i] = loop_start;
1680 r_i++;
1681 }
1682
1683 BLI_assert(int(r_i) == result_nfaces);
1684 BLI_assert(loop_cur == result_nloops);
1685
1687
1688 return result;
1689}
1690
1693/* -------------------------------------------------------------------- */
1697std::optional<Mesh *> mesh_merge_by_distance_all(const Mesh &mesh,
1698 const IndexMask &selection,
1699 const float merge_distance)
1700{
1701 Array<int> vert_dest_map(mesh.verts_num, OUT_OF_CONTEXT);
1702
1703 KDTree_3d *tree = BLI_kdtree_3d_new(selection.size());
1704
1705 const Span<float3> positions = mesh.vert_positions();
1706 selection.foreach_index([&](const int64_t i) { BLI_kdtree_3d_insert(tree, i, positions[i]); });
1707
1708 BLI_kdtree_3d_balance(tree);
1709 const int vert_kill_len = BLI_kdtree_3d_calc_duplicates_fast(
1710 tree, merge_distance, true, vert_dest_map.data());
1711 BLI_kdtree_3d_free(tree);
1712
1713 if (vert_kill_len == 0) {
1714 return std::nullopt;
1715 }
1716
1717 return create_merged_mesh(mesh, vert_dest_map, vert_kill_len, true);
1718}
1719
1721 float co[3];
1723};
1724
1725std::optional<Mesh *> mesh_merge_by_distance_connected(const Mesh &mesh,
1726 Span<bool> selection,
1727 const float merge_distance,
1728 const bool only_loose_edges)
1729{
1730 const Span<float3> positions = mesh.vert_positions();
1731 const Span<int2> edges = mesh.edges();
1732
1733 int vert_kill_len = 0;
1734
1735 /* From the original index of the vertex.
1736 * This indicates which vert it is or is going to be merged. */
1737 Array<int> vert_dest_map(mesh.verts_num, OUT_OF_CONTEXT);
1738
1739 Array<WeldVertexCluster> vert_clusters(mesh.verts_num);
1740
1741 for (const int i : positions.index_range()) {
1742 WeldVertexCluster &vc = vert_clusters[i];
1743 copy_v3_v3(vc.co, positions[i]);
1744 vc.merged_verts = 0;
1745 }
1746 const float merge_dist_sq = square_f(merge_distance);
1747
1748 range_vn_i(vert_dest_map.data(), mesh.verts_num, 0);
1749
1750 /* Collapse Edges that are shorter than the threshold. */
1751 const bke::LooseEdgeCache *loose_edges = nullptr;
1752 if (only_loose_edges) {
1753 loose_edges = &mesh.loose_edges();
1754 if (loose_edges->count == 0) {
1755 return {};
1756 }
1757 }
1758
1759 for (const int i : edges.index_range()) {
1760 int v1 = edges[i][0];
1761 int v2 = edges[i][1];
1762
1763 if (loose_edges && !loose_edges->is_loose_bits[i]) {
1764 continue;
1765 }
1766 while (v1 != vert_dest_map[v1]) {
1767 v1 = vert_dest_map[v1];
1768 }
1769 while (v2 != vert_dest_map[v2]) {
1770 v2 = vert_dest_map[v2];
1771 }
1772 if (v1 == v2) {
1773 continue;
1774 }
1775 if (!selection.is_empty() && (!selection[v1] || !selection[v2])) {
1776 continue;
1777 }
1778 if (v1 > v2) {
1779 std::swap(v1, v2);
1780 }
1781 WeldVertexCluster *v1_cluster = &vert_clusters[v1];
1782 WeldVertexCluster *v2_cluster = &vert_clusters[v2];
1783
1784 float edgedir[3];
1785 sub_v3_v3v3(edgedir, v2_cluster->co, v1_cluster->co);
1786 const float dist_sq = len_squared_v3(edgedir);
1787 if (dist_sq <= merge_dist_sq) {
1788 float influence = (v2_cluster->merged_verts + 1) /
1789 float(v1_cluster->merged_verts + v2_cluster->merged_verts + 2);
1790 madd_v3_v3fl(v1_cluster->co, edgedir, influence);
1791
1792 v1_cluster->merged_verts += v2_cluster->merged_verts + 1;
1793 vert_dest_map[v2] = v1;
1794 vert_kill_len++;
1795 }
1796 }
1797
1798 if (vert_kill_len == 0) {
1799 return std::nullopt;
1800 }
1801
1802 for (const int i : IndexRange(mesh.verts_num)) {
1803 if (i == vert_dest_map[i]) {
1804 vert_dest_map[i] = OUT_OF_CONTEXT;
1805 }
1806 else {
1807 int v = i;
1808 while ((v != vert_dest_map[v]) && (vert_dest_map[v] != OUT_OF_CONTEXT)) {
1809 v = vert_dest_map[v];
1810 }
1811 vert_dest_map[v] = v;
1812 vert_dest_map[i] = v;
1813 }
1814 }
1815
1816 return create_merged_mesh(mesh, vert_dest_map, vert_kill_len, true);
1817}
1818
1820 MutableSpan<int> vert_dest_map,
1821 int vert_dest_map_len,
1822 const bool do_mix_vert_data)
1823{
1824 return create_merged_mesh(mesh, vert_dest_map, vert_dest_map_len, do_mix_vert_data);
1825}
1826
1829} // namespace blender::geometry
CustomData interface, see also DNA_customdata_types.h.
int CustomData_sizeof(eCustomDataType type)
bool CustomData_layer_has_interp(const CustomData *data, int layer_n)
void CustomData_interp(const CustomData *source, CustomData *dest, const int *src_indices, const float *weights, const float *sub_weights, int count, int dest_index)
bool CustomData_layer_has_math(const CustomData *data, int layer_n)
void CustomData_copy_layer_type_data(const CustomData *source, CustomData *destination, eCustomDataType type, int source_index, int destination_index, int count)
void CustomData_data_multiply(eCustomDataType type, void *data, float fac)
void CustomData_copy_data(const CustomData *source, CustomData *dest, int source_index, int dest_index, int count)
void CustomData_data_add(eCustomDataType type, void *data1, const void *data2)
Mesh * BKE_mesh_new_nomain_from_template(const Mesh *me_src, int verts_num, int edges_num, int faces_num, int corners_num)
#define BLI_assert(a)
Definition BLI_assert.h:50
A KD-tree for nearest neighbor search.
MINLINE float square_f(float a)
MINLINE float len_squared_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void madd_v3_v3fl(float r[3], const float a[3], float f)
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
void range_vn_i(int *array_tar, int size, int start)
#define SCOPED_TIMER(name)
Definition BLI_timeit.hh:61
#define UNUSED_VARS_NDEBUG(...)
#define ELEM(...)
#define POINTER_OFFSET(v, ofs)
#define IN_RANGE_INCL(a, b, c)
#define CLAMP_MIN(a, b)
@ CD_MVERT_SKIN
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
__forceinline BoundBox intersect(const BoundBox &a, const BoundBox &b)
Definition boundbox.h:178
int64_t size() const
Definition BLI_array.hh:245
const T & last(const int64_t n=0) const
Definition BLI_array.hh:285
const T * data() const
Definition BLI_array.hh:301
IndexRange index_range() const
Definition BLI_array.hh:349
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:388
constexpr int64_t size() const
constexpr int64_t start() const
constexpr int64_t size() const
Definition BLI_span.hh:494
constexpr void fill(const T &value) const
Definition BLI_span.hh:518
constexpr IndexRange index_range() const
Definition BLI_span.hh:671
constexpr T & last(const int64_t n=0) const
Definition BLI_span.hh:690
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
constexpr bool is_empty() const
Definition BLI_span.hh:261
int64_t size() const
void append(const T &value)
const T & last(const int64_t n=0) const
void reserve(const int64_t min_capacity)
void increase_size_by_unchecked(const int64_t n) noexcept
KDTree_3d * tree
static float verts[][3]
int count
static char faces[256]
#define ELEM_COLLAPSED
#define OUT_OF_CONTEXT
static int poly_find_doubles(const OffsetIndices< int > poly_corners_offsets, const int poly_num, const Span< int > corners, const int corner_index_max, Vector< int > &r_doubles_offsets, Array< int > &r_doubles_buffer)
std::optional< Mesh * > mesh_merge_by_distance_connected(const Mesh &mesh, Span< bool > selection, float merge_distance, bool only_loose_edges)
static void weld_poly_split_recursive(int poly_loop_len, Span< int > vert_dest_map, WeldPoly *r_wp, WeldMesh *r_weld_mesh, int *r_poly_kill, int *r_loop_kill)
static Vector< WeldEdge > weld_edge_ctx_alloc_and_find_collapsed(Span< int2 > edges, Span< int > vert_dest_map, MutableSpan< int > r_edge_dest_map, int *r_edge_collapsed_len)
static void weld_poly_find_doubles(const Span< int > corner_verts, const Span< int > corner_edges, const int medge_len, WeldMesh *r_weld_mesh)
static void weld_edge_find_doubles(Span< WeldEdge > weld_edges, int mvert_num, MutableSpan< int > r_edge_dest_map, int *r_edge_double_kill_len)
static bool weld_iter_loop_of_poly_next(WeldLoopOfPolyIter &iter)
static bool weld_iter_loop_of_poly_begin(WeldLoopOfPolyIter &iter, const WeldPoly &wp, Span< WeldLoop > wloop, const Span< int > corner_verts, const Span< int > corner_edges, Span< int > loop_map, int *group_buffer)
static void merge_customdata_all(const CustomData *source, CustomData *dest, Span< int > dest_map, Span< int > double_elems, const int dest_size, const bool do_mix_data, Array< int > &r_final_map)
Applies to CustomData *dest the values in CustomData *source.
std::optional< Mesh * > mesh_merge_by_distance_all(const Mesh &mesh, const IndexMask &selection, float merge_distance)
static void weld_poly_loop_ctx_setup_collapsed_and_split(const int remain_edge_ctx_len, WeldMesh *r_weld_mesh)
static Mesh * create_merged_mesh(const Mesh &mesh, MutableSpan< int > vert_dest_map, const int removed_vertex_count, const bool do_mix_data)
void debug_randomize_mesh_order(Mesh *mesh)
Definition randomize.cc:220
static void merge_groups_create(Span< int > dest_map, Span< int > double_elems, MutableSpan< int > r_groups_offsets, Array< int > &r_groups_buffer)
Create groups to merge.
Mesh * mesh_merge_verts(const Mesh &mesh, MutableSpan< int > vert_dest_map, int vert_dest_map_len, const bool do_mix_data)
static Vector< int > weld_vert_ctx_alloc_and_setup(MutableSpan< int > vert_dest_map, const int vert_kill_len)
static void weld_mesh_context_create(const Mesh &mesh, MutableSpan< int > vert_dest_map, const int vert_kill_len, const bool get_doubles, WeldMesh *r_weld_mesh)
static void weld_poly_loop_ctx_alloc(const OffsetIndices< int > faces, const Span< int > corner_verts, const Span< int > corner_edges, WeldMesh *r_weld_mesh)
static void customdata_weld(const CustomData *source, CustomData *dest, const int *src_indices, int count, int dest_index)
__int64 int64_t
Definition stdint.h:89
CustomDataLayer * layers
blender::BitVector is_loose_bits