Blender V4.5
bmo_join_triangles.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
13
14#include <algorithm>
15
16#include "MEM_guardedalloc.h"
17
18#include "BLI_heap.h"
19#include "BLI_math_base.h"
20#include "BLI_math_geom.h"
21#include "BLI_math_rotation.h"
22#include "BLI_math_vector.h"
23
24#include "BKE_customdata.hh"
25
26#include "bmesh.hh"
27
28#include "intern/bmesh_operators_private.hh" /* own include */
29
33#define ASSERT_VALID_ERROR_METRIC(val) BLI_assert(isfinite(val) && (val) >= 0 && (val) <= 2 * M_PI)
34
35#if 0
78# define USE_JOIN_TRIANGLE_INTERACTIVE_TESTING
79#endif
80
81#define FACE_OUT (1 << 0)
82#define FACE_INPUT (1 << 2)
83
92constexpr float maximum_improvement = 0.99f;
93
94/* -------------------------------------------------------------------- */
98
102
108
111
114
117
118#ifdef USE_JOIN_TRIANGLE_INTERACTIVE_TESTING
120 BMesh *debug_bm;
121
126 int debug_merge_limit;
127
129 int debug_merge_count;
130
138 int debug_neighbor;
139
144 int debug_neighbor_global_count;
145
151 bool debug_this_step;
152#endif
153};
154
156/* -------------------------------------------------------------------- */
157
171static float quad_calc_error(const float v1[3],
172 const float v2[3],
173 const float v3[3],
174 const float v4[3])
175{
176 float error = 0.0f;
177
178 /* Normal difference: a perfectly flat planar face adds a difference of zero. */
179 {
180 float n1[3], n2[3];
181 float angle_a, angle_b;
182 float diff;
183
184 normal_tri_v3(n1, v1, v2, v3);
185 normal_tri_v3(n2, v1, v3, v4);
186 angle_a = compare_v3v3(n1, n2, FLT_EPSILON) ? 0.0f : angle_normalized_v3v3(n1, n2);
187
188 normal_tri_v3(n1, v2, v3, v4);
189 normal_tri_v3(n2, v4, v1, v2);
190 angle_b = compare_v3v3(n1, n2, FLT_EPSILON) ? 0.0f : angle_normalized_v3v3(n1, n2);
191
192 diff = (angle_a + angle_b) / float(M_PI * 2);
193
195
196 error += diff;
197 }
198
199 /* Co-linearity: a face with four right angle corners adds a difference of zero. */
200 {
201 float edge_vecs[4][3];
202 float diff;
203
204 sub_v3_v3v3(edge_vecs[0], v1, v2);
205 sub_v3_v3v3(edge_vecs[1], v2, v3);
206 sub_v3_v3v3(edge_vecs[2], v3, v4);
207 sub_v3_v3v3(edge_vecs[3], v4, v1);
208
209 normalize_v3(edge_vecs[0]);
210 normalize_v3(edge_vecs[1]);
211 normalize_v3(edge_vecs[2]);
212 normalize_v3(edge_vecs[3]);
213
214 /* A completely skinny face is 'pi' after halving. */
215 diff = (fabsf(angle_normalized_v3v3(edge_vecs[0], edge_vecs[1]) - float(M_PI_2)) +
216 fabsf(angle_normalized_v3v3(edge_vecs[1], edge_vecs[2]) - float(M_PI_2)) +
217 fabsf(angle_normalized_v3v3(edge_vecs[2], edge_vecs[3]) - float(M_PI_2)) +
218 fabsf(angle_normalized_v3v3(edge_vecs[3], edge_vecs[0]) - float(M_PI_2))) /
219 float(M_PI * 2);
220
222
223 error += diff;
224 }
225
226 /* Concavity: a face with no concavity adds an error of 0. */
227 {
228 float area_min, area_max, area_a, area_b;
229 float diff;
230
231 area_a = area_tri_v3(v1, v2, v3) + area_tri_v3(v1, v3, v4);
232 area_b = area_tri_v3(v2, v3, v4) + area_tri_v3(v4, v1, v2);
233
234 area_min = min_ff(area_a, area_b);
235 area_max = max_ff(area_a, area_b);
236
237 /* Note use of ternary operator to guard against divide by zero. */
238 diff = area_max ? (1.0f - (area_min / area_max)) : 1.0f;
239
241
242 error += diff;
243 }
244
246
247 return error;
248}
249
256static void bm_edge_to_quad_verts(const BMEdge *e, const BMVert *r_v_quad[4])
257{
259 BLI_assert(e->l->f->len == 3 && e->l->radial_next->f->len == 3);
260 r_v_quad[0] = e->l->v;
261 r_v_quad[1] = e->l->radial_next->prev->v;
262 r_v_quad[2] = e->l->next->v;
263 r_v_quad[3] = e->l->prev->v;
264}
265
266/* -------------------------------------------------------------------- */
269
271
272namespace {
273
274struct DelimitData_CD {
275 int cd_type;
276 int cd_size;
277 int cd_offset;
278 int cd_offset_end;
279};
280
281struct DelimitData {
282 uint do_seam : 1;
283 uint do_sharp : 1;
284 uint do_mat : 1;
285 uint do_angle_face : 1;
286 uint do_angle_shape : 1;
287
288 float angle_face;
289 float angle_face__cos;
290
291 float angle_shape;
292
293 DelimitData_CD cdata[4];
294 int cdata_len;
295};
296
297} // namespace
298
300static bool bm_edge_is_contiguous_loop_cd_all(const BMEdge *e, const DelimitData_CD *delimit_data)
301{
302 int cd_offset;
303 for (cd_offset = delimit_data->cd_offset; cd_offset < delimit_data->cd_offset_end;
304 cd_offset += delimit_data->cd_size)
305 {
306 if (BM_edge_is_contiguous_loop_cd(e, delimit_data->cd_type, cd_offset) == false) {
307 return false;
308 }
309 }
310
311 return true;
312}
313
316 eCustomDataType type,
317 DelimitData_CD *r_delim_cd)
318{
319 const int layer_len = CustomData_number_of_layers(ldata, type);
320 r_delim_cd->cd_type = type;
321 r_delim_cd->cd_size = CustomData_sizeof(eCustomDataType(r_delim_cd->cd_type));
322 r_delim_cd->cd_offset = CustomData_get_n_offset(ldata, type, 0);
323 r_delim_cd->cd_offset_end = r_delim_cd->cd_offset + (r_delim_cd->cd_size * layer_len);
324 return (r_delim_cd->cd_offset != -1);
325}
326
334{
335 DelimitData delimit_data = {0};
336 delimit_data.do_seam = BMO_slot_bool_get(op->slots_in, "cmp_seam");
337 delimit_data.do_sharp = BMO_slot_bool_get(op->slots_in, "cmp_sharp");
338 delimit_data.do_mat = BMO_slot_bool_get(op->slots_in, "cmp_materials");
339
340 /* Determine if angle face processing occurs and its parameters. */
341 float angle_face = BMO_slot_float_get(op->slots_in, "angle_face_threshold");
342 if (angle_face < DEG2RADF(180.0f)) {
343 delimit_data.angle_face = angle_face;
344 delimit_data.angle_face__cos = cosf(angle_face);
345 delimit_data.do_angle_face = true;
346 }
347 else {
348 delimit_data.do_angle_face = false;
349 }
350
351 /* Determine if angle shape processing occurs and its parameters. */
352 float angle_shape = BMO_slot_float_get(op->slots_in, "angle_shape_threshold");
353 if (angle_shape < DEG2RADF(180.0f)) {
354 delimit_data.angle_shape = angle_shape;
355 delimit_data.do_angle_shape = true;
356 }
357 else {
358 delimit_data.do_angle_shape = false;
359 }
360
361 if (BMO_slot_bool_get(op->slots_in, "cmp_uvs") &&
363 &bm->ldata, CD_PROP_FLOAT2, &delimit_data.cdata[delimit_data.cdata_len]))
364 {
365 delimit_data.cdata_len += 1;
366 }
367
368 delimit_data.cdata[delimit_data.cdata_len].cd_offset = -1;
369 if (BMO_slot_bool_get(op->slots_in, "cmp_vcols") &&
371 &bm->ldata, CD_PROP_BYTE_COLOR, &delimit_data.cdata[delimit_data.cdata_len]))
372 {
373 delimit_data.cdata_len += 1;
374 }
375 delimit_data.cdata[delimit_data.cdata_len].cd_offset = -1;
376 if (BMO_slot_bool_get(op->slots_in, "cmp_vcols") &&
378 &bm->ldata, CD_PROP_COLOR, &delimit_data.cdata[delimit_data.cdata_len]))
379 {
380 delimit_data.cdata_len += 1;
381 }
382
383 return delimit_data;
384}
385
393static bool bm_edge_is_delimit(const BMEdge *e, const DelimitData *delimit_data)
394{
395 BMFace *f_a = e->l->f, *f_b = e->l->radial_next->f;
396#if 0
397 const bool is_contig = BM_edge_is_contiguous(e);
398 float angle;
399#endif
400
401 if (delimit_data->do_seam && BM_elem_flag_test(e, BM_ELEM_SEAM)) {
402 return true;
403 }
404
405 if (delimit_data->do_sharp && (BM_elem_flag_test(e, BM_ELEM_SMOOTH) == 0)) {
406 return true;
407 }
408
409 if (delimit_data->do_mat && (f_a->mat_nr != f_b->mat_nr)) {
410 return true;
411 }
412
413 if (delimit_data->do_angle_face) {
414 if (dot_v3v3(f_a->no, f_b->no) < delimit_data->angle_face__cos) {
415 return true;
416 }
417 }
418
419 if (delimit_data->do_angle_shape) {
420 const BMVert *verts[4];
422
423 /* if we're checking the shape at all, a flipped face is out of the question */
424 if (is_quad_flip_v3(verts[0]->co, verts[1]->co, verts[2]->co, verts[3]->co)) {
425 return true;
426 }
427
428 float edge_vecs[4][3];
429
430 sub_v3_v3v3(edge_vecs[0], verts[0]->co, verts[1]->co);
431 sub_v3_v3v3(edge_vecs[1], verts[1]->co, verts[2]->co);
432 sub_v3_v3v3(edge_vecs[2], verts[2]->co, verts[3]->co);
433 sub_v3_v3v3(edge_vecs[3], verts[3]->co, verts[0]->co);
434
435 normalize_v3(edge_vecs[0]);
436 normalize_v3(edge_vecs[1]);
437 normalize_v3(edge_vecs[2]);
438 normalize_v3(edge_vecs[3]);
439
440 if ((fabsf(angle_normalized_v3v3(edge_vecs[0], edge_vecs[1]) - float(M_PI_2)) >
441 delimit_data->angle_shape) ||
442 (fabsf(angle_normalized_v3v3(edge_vecs[1], edge_vecs[2]) - float(M_PI_2)) >
443 delimit_data->angle_shape) ||
444 (fabsf(angle_normalized_v3v3(edge_vecs[2], edge_vecs[3]) - float(M_PI_2)) >
445 delimit_data->angle_shape) ||
446 (fabsf(angle_normalized_v3v3(edge_vecs[3], edge_vecs[0]) - float(M_PI_2)) >
447 delimit_data->angle_shape))
448 {
449 return true;
450 }
451 }
452
453 if (delimit_data->cdata_len) {
454 int i;
455 for (i = 0; i < delimit_data->cdata_len; i++) {
456 if (!bm_edge_is_contiguous_loop_cd_all(e, &delimit_data->cdata[i])) {
457 return true;
458 }
459 }
460 }
461
462 return false;
463}
464
466
471
483
498{
499 BLI_assert(neighbor_info.items_num < ARRAY_SIZE(neighbor_info.items));
500
501 /* Don't add null pointers. Another safeguard which cannot happen. */
502 BLI_assert(e != nullptr);
503
504 /* Don't add duplicates. */
505 for (uint index = 0; index < neighbor_info.items_num; index++) {
506 if (neighbor_info.items[neighbor_info.items_num].e == e) {
507 return;
508 }
509 }
510
511 /* Add the edge and increase the count by 1. */
512 JoinEdgesNeighborItem *item = &neighbor_info.items[neighbor_info.items_num++];
513 item->e = e;
514 item->l = l;
515}
516
525static void add_neighbors(JoinEdgesNeighborInfo &neighbor_info, BMLoop *l_in_quad)
526{
527 /* If the edge is not manifold, there is no neighboring face to process. */
528 if (!BM_edge_is_manifold(l_in_quad->e)) {
529 /* No new edges added. */
530 return;
531 }
532
533 BMLoop *l_in_neighbor = l_in_quad->radial_next;
534
535 /* If the neighboring face is not a triangle, don't process it. */
536 if (l_in_neighbor->f->len != 3) {
537 /* No new edges added. */
538 return;
539 }
540
541#ifndef NDEBUG
542 const int items_num_prev = neighbor_info.items_num;
543#endif
544
545 /* Get the other two loops of the neighboring triangle. */
546 BMLoop *l_other_arr[2] = {l_in_neighbor->prev, l_in_neighbor->next};
547 for (int i = 0; i < ARRAY_SIZE(l_other_arr); i++) {
548 BMLoop *l_other = l_other_arr[i];
549
550 /* If `l_other` is manifold, and the adjacent face is also a triangle,
551 * mark it for potential improvement. */
552 if (BM_edge_is_manifold(l_other->e) && l_other->radial_next->f->len == 3) {
553 add_without_duplicates(neighbor_info, l_other->e, l_in_neighbor);
554 }
555 }
556
557 /* Added either 0, 1, or 2 edges. */
558#ifndef NDEBUG
559 BLI_assert(neighbor_info.items_num - items_num_prev < 3);
560#endif
561}
562
573static void rotate_to_plane(const JoinEdgesState &s,
574 const BMVert *quad_verts[4],
575 const BMLoop *l_shared,
576 const float plane_normal[3],
577 float r_quad_coordinates[4][3])
578{
579#ifdef USE_JOIN_TRIANGLE_INTERACTIVE_TESTING
580 if (s.debug_this_step) {
581 printf("angle");
582 }
583#else
584 UNUSED_VARS(s);
585#endif
586
587 float rotation_axis[3];
588 sub_v3_v3v3(rotation_axis, l_shared->v->co, l_shared->next->v->co);
589 normalize_v3(rotation_axis);
590
591 float quad_normal[3] = {0};
593 quad_normal, quad_verts[0]->co, quad_verts[1]->co, quad_verts[2]->co, quad_verts[3]->co);
594
595 float angle = angle_signed_on_axis_v3v3_v3(plane_normal, quad_normal, rotation_axis);
596
597#ifdef USE_JOIN_TRIANGLE_INTERACTIVE_TESTING
598 if (s.debug_this_step) {
599 printf(" %f, ", RAD2DEGF(angle));
600 }
601#endif
602
603 for (int i = 0; i < 4; i++) {
604 if (ELEM(quad_verts[i], l_shared->v, l_shared->next->v)) {
605 /* Two coordinates of the quad match the vector that defines the axis of rotation, so they
606 * don't change. */
607 copy_v3_v3(r_quad_coordinates[i], quad_verts[i]->co);
608 }
609 else {
610 /* The other two coordinates get rotated around the axis, and so they change. */
611 float local_coordinate[3];
612 sub_v3_v3v3(local_coordinate, quad_verts[i]->co, l_shared->v->co);
613 rotate_normalized_v3_v3v3fl(r_quad_coordinates[i], local_coordinate, rotation_axis, angle);
614 add_v3_v3(r_quad_coordinates[i], l_shared->v->co);
615 }
616 }
617}
618
650static float compute_alignment(const JoinEdgesState &s,
651 const float quad_a_vecs[4][3],
652 const BMVert *quad_b_verts[4],
653 const BMLoop *l_shared,
654 const float plane_normal[3])
655{
656 /* Many meshes have lots of curvature or sharp edges. Pairs of quads shouldn't be penalized
657 * *worse* because they represent a curved surface or define an edge. So we rotate quad_b around
658 * its common edge with quad_a until both are, as much as possible, in the same plane.
659 * This ensures the best possible chance to align. */
660 float quad_b_coordinates[4][3];
661 rotate_to_plane(s, quad_b_verts, l_shared, plane_normal, quad_b_coordinates);
662
663#ifdef USE_JOIN_TRIANGLE_INTERACTIVE_TESTING
664 if (s.debug_this_step) {
665 /* For visualization purposes *only*, rotate the face being considered.
666 * The const_cast here is purposeful. We want to specify `const BMVert` deliberately
667 * to show that we're not *supposed* to be moving verts around.
668 * But only for debug visualization, we do. This alters the mesh to visualize the
669 * effect of rotating the face into the plane for alignment testing. */
670 copy_v3_v3(const_cast<BMVert *>(quad_b_verts[0])->co, quad_b_coordinates[0]);
671 copy_v3_v3(const_cast<BMVert *>(quad_b_verts[1])->co, quad_b_coordinates[1]);
672 copy_v3_v3(const_cast<BMVert *>(quad_b_verts[2])->co, quad_b_coordinates[2]);
673 copy_v3_v3(const_cast<BMVert *>(quad_b_verts[3])->co, quad_b_coordinates[3]);
674 }
675#endif
676
677 /* compute the four unit vectors of the quad b edges. */
678 float quad_b_vecs[4][3];
679 sub_v3_v3v3(quad_b_vecs[0], quad_b_coordinates[0], quad_b_coordinates[1]);
680 sub_v3_v3v3(quad_b_vecs[1], quad_b_coordinates[1], quad_b_coordinates[2]);
681 sub_v3_v3v3(quad_b_vecs[2], quad_b_coordinates[2], quad_b_coordinates[3]);
682 sub_v3_v3v3(quad_b_vecs[3], quad_b_coordinates[3], quad_b_coordinates[0]);
683 normalize_v3(quad_b_vecs[0]);
684 normalize_v3(quad_b_vecs[1]);
685 normalize_v3(quad_b_vecs[2]);
686 normalize_v3(quad_b_vecs[3]);
687
688 /* Given that we're not certain of how the first loop of the quad and the first loop
689 * of the proposed merge quad relate to each other, there are four possible combinations
690 * to check, to test that the neighbor face and the merged face have good alignment.
691 *
692 * In theory, a very nuanced analysis involving l_shared, loop pointers, vertex pointers,
693 * etc, would allow determining which sets of vectors are the right matches sets to compare.
694 *
695 * Do not meddle in the affairs of algorithms, for they are subtle and quick to anger.
696 *
697 * Instead, this code does the math twice, then it just flips each component by 180 degrees to
698 * pick up the other two cases. Four extra angle tests aren't that much worse than optimal.
699 * Brute forcing the math and ending up with clear and understandable code is better. */
700
701 float error[4] = {0.0f};
702 for (int i = 0; i < ARRAY_SIZE(error); i++) {
703 const float angle_a = fabsf(angle_normalized_v3v3(quad_a_vecs[i], quad_b_vecs[i]));
704 const float angle_b = fabsf(angle_normalized_v3v3(quad_a_vecs[i], quad_b_vecs[(i + 1) % 4]));
705
706 /* Compute the case if the quads are aligned. */
707 error[0] += angle_a;
708 /* Compute the case if the quads are 90 degrees rotated. */
709 error[1] += angle_b;
710
711 /* Compute the case if the quads are 180 degrees rotated.
712 * This is `error[0]` except each error component is individually rotated 180 degrees. */
713 error[2] += M_PI - angle_a;
714
715 /* Compute the case if the quads are 270 degrees rotated.
716 * This is `error[1]` except each error component is individually rotated 180 degrees. */
717 error[3] += M_PI - angle_b;
718 }
719
720 /* Pick the best option and average the four components. */
721 const float best_error = std::min({error[0], error[1], error[2], error[3]}) / 4.0f;
722
723 ASSERT_VALID_ERROR_METRIC(best_error);
724
725 /* Based on the best error, we scale how aligned we are to the range 0...1
726 * `M_PI / 4` is used here because the worst case is a quad with all four edges
727 * at 45 degree angles. */
728 float alignment = 1.0f - (best_error / (M_PI / 4.0f));
729
730 /* if alignment is *truly* awful, then do nothing. Don't make a join worse. */
731 alignment = std::max(alignment, 0.0f);
732
733 ASSERT_VALID_ERROR_METRIC(alignment);
734
735 return alignment;
736}
737
768 BMEdge *e_merge,
769 BMLoop *l_shared,
770 float neighbor_quad_vecs[4][3],
771 const float neighbor_quad_error,
772 const float neighbor_quad_normal[3])
773{
774 ASSERT_VALID_ERROR_METRIC(neighbor_quad_error);
775
776 /* If the edge wasn't found, (delimit, non-manifold, etc) then return.
777 * Nothing to do here. */
778 BLI_assert(BM_elem_index_get(e_merge) >= 0);
779 HeapNode *node = s.edge_queue_nodes[BM_elem_index_get(e_merge)];
780 if (node == nullptr) {
781 return;
782 }
783
784 float join_error_curr = BLI_heap_node_value(node);
785
786 ASSERT_VALID_ERROR_METRIC(join_error_curr);
787
788 /* Never make a join *worse* because of topology around it.
789 * Because we are sorted during the join phase of the algorithm, this should *only* happen when
790 * processing any pre-existing quads in the input mesh during setup. They might have high error.
791 * If they do, ignore them. */
792 if (neighbor_quad_error > join_error_curr) {
793
794#ifdef USE_JOIN_TRIANGLE_INTERACTIVE_TESTING
795 /* This should only happen during setup.
796 * Indicates an error, if it happens once we've started merging. */
797 BLI_assert(s.debug_merge_count == 0);
798#endif
799
800 return;
801 }
802
803#ifdef USE_JOIN_TRIANGLE_INTERACTIVE_TESTING
804 if (s.debug_this_step) {
805 printf("Edge improved from ");
806 }
807#endif
808
809 /* Get the four corners of the quad that would result if we merged. */
810 const BMVert *quad_verts_merge[4];
811 bm_edge_to_quad_verts(e_merge, quad_verts_merge);
812
813 /* Now compute the alignment.
814 * Regular grids of rectangles or trapezoids have high alignment
815 * Mismatched combinations of rectangles diamonds and trapezoids have low alignment. */
816 const float alignment = compute_alignment(
817 s, neighbor_quad_vecs, quad_verts_merge, l_shared, neighbor_quad_normal);
818
819 /* Compute how much the neighbor is better than the candidate.
820 * Since the neighbor quad error is smaller, Improvement is always represented as negative. */
821 const float improvement = (neighbor_quad_error - join_error_curr);
822
823 ASSERT_VALID_ERROR_METRIC(-improvement);
824
825 /* Compute the scale factor for how much of that possible improvement we should apply to this
826 * edge. This combines... topology_influence, which is an operator setting... and alignment,
827 * which is computed. Faces which are diagonal have an alignment of 0% - perfect rectangular
828 * grids have an alignment of 100% Neither topology_influence nor alignment can be negative;
829 * therefore the multiplier *never* makes error worse. once combined, 0 means no improvement, 1
830 * means improve all the way to exactly match the quality of the contributing neighbor.
831 * topology_influece is allowed to exceed 1.0, which lets it cancel out some of the alignment
832 * penalty. */
833 float multiplier = s.topo_influnce * alignment;
834
835 /* However, the combined multiplier shouldn't ever be allowed to exceed 1.0 because permitting
836 * that would cause exponential growth when alignment is very good, and when that happens, the
837 * algorithm becomes crazy.
838 *
839 * Further, if we allow a multiplier of exactly 1.0, then all eight edges around the neighbor
840 * quad would end up with a quality that is *exactly* equal to the neighbor - and each other;
841 * losing valuable information about their relative sorting.
842 * In order to preserve that, the multiplier is capped at 99%.
843 * The last 1% that is left uncorrected is enough to preserve relative ordering.
844 *
845 * This especially helps in quads that touch 3-poles and 5-poles. Since those quads naturally
846 * have diamond shapes, their initial error values tend to be higher and they sort to the end of
847 * the priority queue. Limiting improvement at 99% ensures those quads tend to retain their bad
848 * sort, meaning they end up surrounded by quads that define a good grid,
849 * then they merge last, which tends to produce better results. */
850 multiplier = std::min(multiplier, maximum_improvement);
851
852 ASSERT_VALID_ERROR_METRIC(multiplier);
853
854 /* improvement is always represented as a negative number (that will reduce error)
855 * Based on that convention, `+` is correct here. */
856 float join_error_next = join_error_curr + (improvement * multiplier);
857
858 ASSERT_VALID_ERROR_METRIC(join_error_next);
859
860#ifdef USE_JOIN_TRIANGLE_INTERACTIVE_TESTING
861 if (s.debug_this_step) {
862 printf("%f to %f with alignment of %f.\n", join_error_curr, join_error_next, alignment);
863 BMO_face_flag_enable(s.debug_bm, e_merge->l->f, FACE_OUT);
864 BMO_face_flag_enable(s.debug_bm, e_merge->l->radial_next->f, FACE_OUT);
865 }
866#endif
867
868 /* Now, update the node value in the heap, which may cause the node
869 * to be moved toward the head of the priority queue. */
870 BLI_heap_node_value_update(s.edge_queue, node, join_error_next);
871}
872
880static void reprioritize_face_neighbors(JoinEdgesState &s, BMFace *f, float f_error)
881{
882 BLI_assert(f->len == 4);
883
884 /* Identify any mergable edges of any neighbor triangles that face us.
885 * - Some of our four edges... might not be manifold.
886 * - Some of our neighbor faces... might not be triangles.
887 * - Some of our neighbor triangles... might have other non-manifold (un-mergable) edges.
888 * - Some of our neighbor triangles' manifold edges... might have non-triangle neighbors.
889 * Therefore, there can be have up to eight mergable edges, although there are often fewer. */
890 JoinEdgesNeighborInfo neighbor_info = {};
891
892 /* Get the four loops around the face. */
893 BMLoop *l_quad[4];
895
896 /* Add the mergable neighbors for each of those loops. */
897 for (int i = 0; i < ARRAY_SIZE(l_quad); i++) {
898 add_neighbors(neighbor_info, l_quad[i]);
899 }
900
901 /* Return if there is nothing to do. */
902 if (neighbor_info.items_num == 0) {
903 return;
904 }
905
906 /* Compute the four unit vectors around this quad. */
907 float quad_vecs[4][3];
908 for (int i_next = 0, i = ARRAY_SIZE(l_quad) - 1; i_next < ARRAY_SIZE(l_quad); i = i_next++) {
909 sub_v3_v3v3(quad_vecs[i], l_quad[i]->v->co, l_quad[i_next]->v->co);
910 normalize_v3(quad_vecs[i]);
911 }
912
913 /* Re-prioritize each neighbor. */
914 for (int i = 0; i < neighbor_info.items_num; i++) {
915#ifdef USE_JOIN_TRIANGLE_INTERACTIVE_TESTING
916 s.debug_this_step = (s.debug_merge_limit > 0 && s.debug_merge_count == s.debug_merge_limit &&
917 i + 1 == s.debug_neighbor) ||
918 (++s.debug_neighbor_global_count == s.debug_neighbor &&
919 s.debug_merge_limit == 0);
920#endif
921 const JoinEdgesNeighborItem *item = &neighbor_info.items[i];
922 reprioritize_join(s, item->e, item->l, quad_vecs, f_error, f->no);
923 }
924}
925
933 BMEdge *e
934#ifdef USE_JOIN_TRIANGLE_INTERACTIVE_TESTING
935 ,
937#endif
938)
939{
940 /* Non-manifold edges can't be merged. */
942
943 /* Identify the loops on either side of the edge which may be joined. */
944 BMLoop *l_a = e->l;
945 BMLoop *l_b = e->l->radial_next;
946
947 /* If previous face merges have created quads, which now make this edge un-mergable,
948 * then skip it and move on. This happens frequently and that's ok.
949 * It's much easier and more efficient to just skip these edges when we encounter them,
950 * than it is to try to search the heap for them and remove them preemptively. */
951 if ((l_a->f->len != 3) || (l_b->f->len != 3)) {
952 return nullptr;
953 }
954
955#ifdef USE_JOIN_TRIANGLE_INTERACTIVE_TESTING
956 /* Stop doing merges in the middle of processing if we reached a user limit.
957 * This is allowed so a developer can check steps in the process of the algorithm. */
958 if (++s.debug_merge_count > s.debug_merge_limit && s.debug_merge_limit != -1) {
959 return nullptr;
960 }
961#endif
962
963 BMFace *f_double;
964
965 /* Join the edge and identify the face. */
966 BMFace *f = BM_faces_join_pair(bm, l_a, l_b, true, &f_double);
967 /* See #BM_faces_join note on callers asserting when `r_double` is non-null. */
968 BLI_assert_msg(f_double == nullptr,
969 "Doubled face detected at " AT ". Resulting mesh may be corrupt.");
970
971 return f;
972}
973
976{
977 BMIter iter;
978 BMOIter siter;
979 BMFace *f;
980 BMEdge *e;
981
982 DelimitData delimit_data = bm_edge_delmimit_data_from_op(bm, op);
983
984 /* Initial setup of state. */
985 JoinEdgesState s = {nullptr};
986 s.topo_influnce = BMO_slot_float_get(op->slots_in, "topology_influence");
987 s.use_topo_influence = (s.topo_influnce != 0.0f);
989 s.select_tris_only = BMO_slot_bool_get(op->slots_in, "deselect_joined");
990 if (s.use_topo_influence) {
991 s.edge_queue_nodes = MEM_malloc_arrayN<HeapNode *>(bm->totedge, __func__);
992 }
993
994#ifdef USE_JOIN_TRIANGLE_INTERACTIVE_TESTING
995 s.debug_bm = bm;
996 s.debug_merge_limit = BMO_slot_int_get(op->slots_in, "merge_limit") - 1;
997 s.debug_neighbor = BMO_slot_int_get(op->slots_in, "neighbor_debug");
998 s.debug_merge_count = 0;
999 s.debug_neighbor_global_count = 0;
1000#endif
1001
1002 /* Go through every face in the input slot. Mark triangles for processing. */
1003 BMO_ITER (f, &siter, op->slots_in, "faces", BM_FACE) {
1004 if (f->len == 3) {
1006
1007 /* And setup the initial selection. */
1008 if (s.select_tris_only) {
1010 }
1011 }
1012 }
1013
1014 /* Go through every edge in the mesh, mark edges that can be merged. */
1015 int i = 0;
1017 BM_elem_index_set(e, i); /* set_inline */
1018
1019 /* If the edge is manifold, has a tagged input triangle on both sides,
1020 * and is *not* delimited, then it's a candidate to merge. */
1021 BMFace *f_a, *f_b;
1022 if (BM_edge_face_pair(e, &f_a, &f_b) && BMO_face_flag_test(bm, f_a, FACE_INPUT) &&
1023 BMO_face_flag_test(bm, f_b, FACE_INPUT) && !bm_edge_is_delimit(e, &delimit_data))
1024 {
1025 /* Compute the error that would result from a merge. */
1026 const BMVert *e_verts[4];
1027 bm_edge_to_quad_verts(e, e_verts);
1028 const float merge_error = quad_calc_error(
1029 e_verts[0]->co, e_verts[1]->co, e_verts[2]->co, e_verts[3]->co);
1030
1031 /* Record the candidate merge in both the heap, and the heap index. */
1032 HeapNode *node = BLI_heap_insert(s.edge_queue, merge_error, e);
1033 if (s.use_topo_influence) {
1034 s.edge_queue_nodes[i] = node;
1035 }
1036 }
1037 else {
1038 if (s.use_topo_influence) {
1039 s.edge_queue_nodes[i] = nullptr;
1040 }
1041 }
1042 }
1043
1044 /* Go through all the faces of the input slot, this time to find quads.
1045 * Improve the candidates around any preexisting quads in the mesh.
1046 *
1047 * NOTE: This unfortunately misses any quads which are not selected, but
1048 * which neighbor the selection. The only alternate would be to iterate the
1049 * whole mesh, which might be expensive for very large meshes with small selections. */
1050 if (s.use_topo_influence && (BLI_heap_is_empty(s.edge_queue) == false)) {
1051 BMO_ITER (f, &siter, op->slots_in, "faces", BM_FACE) {
1052 if (f->len == 4) {
1053 BMVert *f_verts[4];
1054 BM_face_as_array_vert_quad(f, f_verts);
1055
1056 /* Flat quads with right angle corners and no concavity have lower error. */
1057 float f_error = quad_calc_error(
1058 f_verts[0]->co, f_verts[1]->co, f_verts[2]->co, f_verts[3]->co);
1059
1060 /* Apply the compensated error.
1061 * Since we're early in the process we over-prioritize any already existing quads to
1062 * allow them to have an especially strong influence on the resulting mesh.
1063 * At a topology influence of 200%, they're considered to be *almost perfect* quads
1064 * regardless of their actual error. Either way, the multiplier is never completely
1065 * allowed to reach zero. Instead, 1% of the original error is preserved...
1066 * which is enough to maintain the relative priority sorting between existing quads. */
1067 f_error *= (2.0f - (s.topo_influnce * maximum_improvement));
1068
1069 reprioritize_face_neighbors(s, f, f_error);
1070 }
1071 }
1072 }
1073
1074 /* Process all possible merges. */
1075 while (!BLI_heap_is_empty(s.edge_queue)) {
1076
1077 /* Get the best merge from the priority queue.
1078 * Remove it from the priority queue. */
1079 const float f_error = BLI_heap_top_value(s.edge_queue);
1080 BMEdge *e = reinterpret_cast<BMEdge *>(BLI_heap_pop_min(s.edge_queue));
1081
1082 /* Attempt the merge. */
1084 e
1085#ifdef USE_JOIN_TRIANGLE_INTERACTIVE_TESTING
1086 ,
1087 s
1088#endif
1089 );
1090
1091#ifdef USE_JOIN_TRIANGLE_INTERACTIVE_TESTING
1092 /* If stopping partway through, clear the selection entirely, and instead
1093 * highlight the faces being considered in the step the user is checking. */
1094 if (s.debug_merge_limit != -1 && s.debug_merge_count == s.debug_merge_limit) {
1095 BMEdge *f;
1096 BMIter iter;
1097 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
1099 }
1100 }
1101#endif
1102
1103 if (f_new) {
1104
1105 /* Tag the face so the selection can be extended to include the new face. */
1106 if (s.select_tris_only == false) {
1108 }
1109
1110 /* Improve the neighbors on success. */
1111 if (s.use_topo_influence) {
1112 reprioritize_face_neighbors(s, f_new, f_error);
1113 }
1114 }
1115
1116#ifdef USE_JOIN_TRIANGLE_INTERACTIVE_TESTING
1117 /* If we're supposed to stop partway though, do that.
1118 * This allows a developer to inspect the mesh at intermediate stages of processing. */
1119 if (s.debug_merge_limit != -1 && s.debug_merge_count >= s.debug_merge_limit) {
1120 break;
1121 }
1122#endif
1123 }
1124
1125#ifdef USE_JOIN_TRIANGLE_INTERACTIVE_TESTING
1126 /* Expect a full processing to have occurred, *only* if we didn't stop partway through. */
1127 if (!(s.debug_merge_limit != -1 && s.debug_merge_count >= s.debug_merge_limit))
1128#endif
1129 {
1130 /* Expect a full processing to have occurred. */
1132 }
1133
1134 /* Clean up. */
1135 BLI_heap_free(s.edge_queue, nullptr);
1136 if (s.use_topo_influence) {
1138 }
1139
1140 /* Return the selection results. */
1142}
CustomData interface, see also DNA_customdata_types.h.
int CustomData_sizeof(eCustomDataType type)
int CustomData_get_n_offset(const CustomData *data, eCustomDataType type, int n)
int CustomData_number_of_layers(const CustomData *data, eCustomDataType type)
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
A min-heap / priority queue ADT.
void BLI_heap_free(Heap *heap, HeapFreeFP ptrfreefp) ATTR_NONNULL(1)
Definition BLI_heap.cc:191
void void float BLI_heap_node_value(const HeapNode *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition BLI_heap.cc:347
void void bool BLI_heap_is_empty(const Heap *heap) ATTR_NONNULL(1)
Definition BLI_heap.cc:269
float BLI_heap_top_value(const Heap *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition BLI_heap.cc:284
void BLI_heap_node_value_update(Heap *heap, HeapNode *node, float value) ATTR_NONNULL(1
void * BLI_heap_pop_min(Heap *heap) ATTR_NONNULL(1)
Definition BLI_heap.cc:291
HeapNode * BLI_heap_insert(Heap *heap, float value, void *ptr) ATTR_NONNULL(1)
Definition BLI_heap.cc:234
Heap * BLI_heap_new(void) ATTR_WARN_UNUSED_RESULT
Definition BLI_heap.cc:186
MINLINE float max_ff(float a, float b)
MINLINE float min_ff(float a, float b)
#define DEG2RADF(_deg)
#define M_PI_2
#define RAD2DEGF(_rad)
#define M_PI
float normal_quad_v3(float n[3], const float v1[3], const float v2[3], const float v3[3], const float v4[3])
Definition math_geom.cc:58
int is_quad_flip_v3(const float v1[3], const float v2[3], const float v3[3], const float v4[3])
float area_tri_v3(const float v1[3], const float v2[3], const float v3[3])
Definition math_geom.cc:100
float normal_tri_v3(float n[3], const float v1[3], const float v2[3], const float v3[3])
Definition math_geom.cc:41
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])
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
void rotate_normalized_v3_v3v3fl(float out[3], const float p[3], const float axis[3], float angle)
float angle_signed_on_axis_v3v3_v3(const float v1[3], const float v2[3], const float axis[3]) ATTR_WARN_UNUSED_RESULT
MINLINE bool compare_v3v3(const float v1[3], const float v2[3], float limit) ATTR_WARN_UNUSED_RESULT
float angle_normalized_v3v3(const float v1[3], const float v2[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3(float n[3])
unsigned int uint
#define ARRAY_SIZE(arr)
#define UNUSED_VARS(...)
#define ELEM(...)
#define AT
@ CD_PROP_BYTE_COLOR
@ CD_PROP_COLOR
@ CD_PROP_FLOAT2
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
Read Guarded memory(de)allocation.
@ BM_ELEM_SEAM
@ BM_ELEM_SMOOTH
#define BM_elem_index_get(ele)
#define BM_elem_index_set(ele, index)
#define BM_elem_flag_test(ele, hflag)
#define BM_ITER_MESH(ele, iter, bm, itype)
#define BM_ITER_MESH_INDEX(ele, iter, bm, itype, indexvar)
@ BM_EDGES_OF_MESH
@ BM_FACES_OF_MESH
BMesh * bm
BMFace * BM_faces_join_pair(BMesh *bm, BMLoop *l_a, BMLoop *l_b, const bool do_del, BMFace **r_double)
Faces Join Pair.
#define BM_FACE
float BMO_slot_float_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
void BMO_slot_buffer_from_enabled_flag(BMesh *bm, BMOperator *op, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, char htype, short oflag)
#define BMO_face_flag_enable(bm, e, oflag)
#define BMO_ITER(ele, iter, slot_args, slot_name, restrict_flag)
int BMO_slot_int_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
#define BMO_face_flag_disable(bm, e, oflag)
#define BMO_face_flag_test(bm, e, oflag)
bool BMO_slot_bool_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
void BM_face_as_array_vert_quad(BMFace *f, BMVert *r_verts[4])
void BM_face_as_array_loop_quad(BMFace *f, BMLoop *r_loops[4])
bool BM_edge_is_contiguous_loop_cd(const BMEdge *e, const int cd_loop_type, const int cd_loop_offset)
bool BM_edge_face_pair(BMEdge *e, BMFace **r_fa, BMFace **r_fb)
BLI_INLINE bool BM_edge_is_contiguous(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE bool BM_edge_is_manifold(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
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 BMLoop * l_b
ATTR_WARN_UNUSED_RESULT const BMVert * v
#define FACE_OUT
Definition bmo_bridge.cc:28
static bool bm_edge_is_delimit(const BMEdge *e, const DelimitData *delimit_data)
static void add_neighbors(JoinEdgesNeighborInfo &neighbor_info, BMLoop *l_in_quad)
#define ASSERT_VALID_ERROR_METRIC(val)
static DelimitData bm_edge_delmimit_data_from_op(BMesh *bm, BMOperator *op)
static void reprioritize_join(JoinEdgesState &s, BMEdge *e_merge, BMLoop *l_shared, float neighbor_quad_vecs[4][3], const float neighbor_quad_error, const float neighbor_quad_normal[3])
void bmo_join_triangles_exec(BMesh *bm, BMOperator *op)
static void add_without_duplicates(JoinEdgesNeighborInfo &neighbor_info, BMEdge *e, BMLoop *l)
static void bm_edge_to_quad_verts(const BMEdge *e, const BMVert *r_v_quad[4])
static bool bm_edge_is_contiguous_loop_cd_all(const BMEdge *e, const DelimitData_CD *delimit_data)
static void reprioritize_face_neighbors(JoinEdgesState &s, BMFace *f, float f_error)
static BMFace * bm_faces_join_pair_by_edge(BMesh *bm, BMEdge *e)
#define FACE_INPUT
static float compute_alignment(const JoinEdgesState &s, const float quad_a_vecs[4][3], const BMVert *quad_b_verts[4], const BMLoop *l_shared, const float plane_normal[3])
static float quad_calc_error(const float v1[3], const float v2[3], const float v3[3], const float v4[3])
static void rotate_to_plane(const JoinEdgesState &s, const BMVert *quad_verts[4], const BMLoop *l_shared, const float plane_normal[3], float r_quad_coordinates[4][3])
static bool bm_edge_delimit_cdata(CustomData *ldata, eCustomDataType type, DelimitData_CD *r_delim_cd)
constexpr float maximum_improvement
#define cosf(x)
#define fabsf(x)
IMETHOD Vector diff(const Vector &a, const Vector &b, double dt)
Definition frames.inl:1166
static float verts[][3]
#define printf(...)
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static void error(const char *str)
struct BMLoop * l
short mat_nr
float no[3]
struct BMVert * v
struct BMEdge * e
struct BMLoop * radial_next
struct BMLoop * prev
struct BMFace * f
struct BMLoop * next
struct BMOpSlot slots_out[BMO_OP_MAX_SLOTS]
struct BMOpSlot slots_in[BMO_OP_MAX_SLOTS]
float co[3]
JoinEdgesNeighborItem items[8]
HeapNode ** edge_queue_nodes
i
Definition text_draw.cc:230