Blender V4.3
bmo_connect_nonplanar.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
11#include "BLI_alloca.h"
12#include "BLI_linklist_stack.h"
13#include "BLI_math_geom.h"
14#include "BLI_utildefines.h"
15
16#include "bmesh.hh"
17
18#include "intern/bmesh_operators_private.hh" /* own include */
19
20#define EDGE_OUT (1 << 0)
21#define FACE_OUT (1 << 1)
22
26static float bm_face_subset_calc_planar(BMLoop *l_first, BMLoop *l_last, const float no[3])
27{
28 float axis_mat[3][3];
29 float z_prev;
30 float delta_z = 0.0f;
31
32 /* Newell's Method */
33 BMLoop *l_iter = l_first;
34 BMLoop *l_term = l_last->next;
35
36 axis_dominant_v3_to_m3(axis_mat, no);
37
38 z_prev = dot_m3_v3_row_z(axis_mat, l_last->v->co);
39 do {
40 const float z_curr = dot_m3_v3_row_z(axis_mat, l_iter->v->co);
41 delta_z += fabsf(z_curr - z_prev);
42 z_prev = z_curr;
43 } while ((l_iter = l_iter->next) != l_term);
44
45 return delta_z;
46}
47
48static bool bm_face_split_find(BMesh *bm, BMFace *f, BMLoop *l_pair[2], float *r_angle_cos)
49{
50 BMLoop *l_iter, *l_first;
51 BMLoop **l_arr = BLI_array_alloca(l_arr, f->len);
52 const uint f_len = f->len;
53 uint i_a, i_b;
54 bool found = false;
55
56 /* angle finding */
57 float err_best = FLT_MAX;
58 float angle_best_cos = -FLT_MAX;
59
60 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
61 i_a = 0;
62 do {
63 l_arr[i_a++] = l_iter;
64 } while ((l_iter = l_iter->next) != l_first);
65
66 /* now for the big search, O(N^2), however faces normally aren't so large */
67 for (i_a = 0; i_a < f_len; i_a++) {
68 BMLoop *l_a = l_arr[i_a];
69 for (i_b = i_a + 2; i_b < f_len; i_b++) {
70 BMLoop *l_b = l_arr[i_b];
71 /* check these are not touching
72 * (we could be smarter here) */
73 if (!BM_loop_is_adjacent(l_a, l_b)) {
74 /* first calculate normals */
75 float no_a[3], no_b[3];
76
77 if (BM_face_calc_normal_subset(l_a, l_b, no_a) != 0.0f &&
78 BM_face_calc_normal_subset(l_b, l_a, no_b) != 0.0f)
79 {
80 const float err_a = bm_face_subset_calc_planar(l_a, l_b, no_a);
81 const float err_b = bm_face_subset_calc_planar(l_b, l_a, no_b);
82 const float err_test = err_a + err_b;
83
84 if (err_test < err_best) {
85 /* check we're legal (we could batch this) */
86 BMLoop *l_split[2] = {l_a, l_b};
87 BM_face_splits_check_legal(bm, f, &l_split, 1);
88 if (l_split[0]) {
89 err_best = err_test;
90 l_pair[0] = l_a;
91 l_pair[1] = l_b;
92
93 angle_best_cos = dot_v3v3(no_a, no_b);
94 found = true;
95 }
96 }
97 }
98 }
99 }
100 }
101
102 *r_angle_cos = angle_best_cos;
103
104 return found;
105}
106
108 BMFace *f,
109 BMFace *r_f_pair[2],
110 const float angle_limit_cos)
111{
112 BMLoop *l_pair[2];
113 float angle_cos;
114
115 if (bm_face_split_find(bm, f, l_pair, &angle_cos) && (angle_cos < angle_limit_cos)) {
116 BMFace *f_new;
117 BMLoop *l_new;
118
119 f_new = BM_face_split(bm, f, l_pair[0], l_pair[1], &l_new, nullptr, false);
120 if (f_new) {
121 r_f_pair[0] = f;
122 r_f_pair[1] = f_new;
123
127 return true;
128 }
129 }
130
131 return false;
132}
133
135{
136 BMOIter siter;
137 BMFace *f;
138 bool changed = false;
139 BLI_LINKSTACK_DECLARE(fstack, BMFace *);
140
141 const float angle_limit_cos = cosf(BMO_slot_float_get(op->slots_in, "angle_limit"));
142
143 BLI_LINKSTACK_INIT(fstack);
144
145 BMO_ITER (f, &siter, op->slots_in, "faces", BM_FACE) {
146 if (f->len > 3) {
147 BLI_LINKSTACK_PUSH(fstack, f);
148 }
149 }
150
151 while ((f = BLI_LINKSTACK_POP(fstack))) {
152 BMFace *f_pair[2];
153 if (bm_face_split_by_angle(bm, f, f_pair, angle_limit_cos)) {
154 int j;
155 for (j = 0; j < 2; j++) {
156 BM_face_normal_update(f_pair[j]);
157 if (f_pair[j]->len > 3) {
158 BLI_LINKSTACK_PUSH(fstack, f_pair[j]);
159 }
160 }
161 changed = true;
162 }
163 }
164
165 BLI_LINKSTACK_FREE(fstack);
166
167 if (changed) {
170 }
171}
#define BLI_array_alloca(arr, realsize)
Definition BLI_alloca.h:25
void axis_dominant_v3_to_m3(float r_mat[3][3], const float normal[3])
Normal to x,y matrix.
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE float dot_m3_v3_row_z(const float M[3][3], const float a[3]) ATTR_WARN_UNUSED_RESULT
unsigned int uint
#define BM_FACE_FIRST_LOOP(p)
ATTR_WARN_UNUSED_RESULT BMesh * bm
BMFace * BM_face_split(BMesh *bm, BMFace *f, BMLoop *l_a, BMLoop *l_b, BMLoop **r_l, BMEdge *example, const bool no_double)
Face Split.
#define BM_FACE
#define BM_EDGE
#define BMO_edge_flag_enable(bm, e, oflag)
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)
void BM_face_splits_check_legal(BMesh *bm, BMFace *f, BMLoop *(*loops)[2], int len)
void BM_face_normal_update(BMFace *f)
float BM_face_calc_normal_subset(const BMLoop *l_first, const BMLoop *l_last, float r_no[3])
BLI_INLINE bool BM_loop_is_adjacent(const BMLoop *l_a, const BMLoop *l_b) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
ATTR_WARN_UNUSED_RESULT const BMLoop * l_b
static bool bm_face_split_find(BMesh *bm, BMFace *f, BMLoop *l_pair[2], float *r_angle_cos)
#define FACE_OUT
#define EDGE_OUT
void bmo_connect_verts_nonplanar_exec(BMesh *bm, BMOperator *op)
static float bm_face_subset_calc_planar(BMLoop *l_first, BMLoop *l_last, const float no[3])
static bool bm_face_split_by_angle(BMesh *bm, BMFace *f, BMFace *r_f_pair[2], const float angle_limit_cos)
#define cosf(x)
#define fabsf(x)
int len
#define FLT_MAX
Definition stdcycles.h:14
struct BMVert * v
struct BMEdge * e
struct BMLoop * next
struct BMOpSlot slots_out[BMO_OP_MAX_SLOTS]
struct BMOpSlot slots_in[BMO_OP_MAX_SLOTS]
float co[3]