Blender V4.3
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
14#include "MEM_guardedalloc.h"
15
16#include "BLI_math_geom.h"
17#include "BLI_math_rotation.h"
18#include "BLI_math_vector.h"
19#include "BLI_sort_utils.h"
20
21#include "BKE_customdata.hh"
22
23#include "bmesh.hh"
24
25#include "intern/bmesh_operators_private.hh" /* own include */
26
30static float quad_calc_error(const float v1[3],
31 const float v2[3],
32 const float v3[3],
33 const float v4[3])
34{
35 /* Gives a 'weight' to a pair of triangles that join an edge
36 * to decide how good a join they would make. */
37 /* NOTE: this is more complicated than it needs to be and should be cleaned up. */
38 float error = 0.0f;
39
40 /* Normal difference */
41 {
42 float n1[3], n2[3];
43 float angle_a, angle_b;
44 float diff;
45
46 normal_tri_v3(n1, v1, v2, v3);
47 normal_tri_v3(n2, v1, v3, v4);
48 angle_a = compare_v3v3(n1, n2, FLT_EPSILON) ? 0.0f : angle_normalized_v3v3(n1, n2);
49
50 normal_tri_v3(n1, v2, v3, v4);
51 normal_tri_v3(n2, v4, v1, v2);
52 angle_b = compare_v3v3(n1, n2, FLT_EPSILON) ? 0.0f : angle_normalized_v3v3(n1, n2);
53
54 diff = (angle_a + angle_b) / float(M_PI * 2);
55
56 error += diff;
57 }
58
59 /* Co-linearity */
60 {
61 float edge_vecs[4][3];
62 float diff;
63
64 sub_v3_v3v3(edge_vecs[0], v1, v2);
65 sub_v3_v3v3(edge_vecs[1], v2, v3);
66 sub_v3_v3v3(edge_vecs[2], v3, v4);
67 sub_v3_v3v3(edge_vecs[3], v4, v1);
68
69 normalize_v3(edge_vecs[0]);
70 normalize_v3(edge_vecs[1]);
71 normalize_v3(edge_vecs[2]);
72 normalize_v3(edge_vecs[3]);
73
74 /* a completely skinny face is 'pi' after halving */
75 diff = (fabsf(angle_normalized_v3v3(edge_vecs[0], edge_vecs[1]) - float(M_PI_2)) +
76 fabsf(angle_normalized_v3v3(edge_vecs[1], edge_vecs[2]) - float(M_PI_2)) +
77 fabsf(angle_normalized_v3v3(edge_vecs[2], edge_vecs[3]) - float(M_PI_2)) +
78 fabsf(angle_normalized_v3v3(edge_vecs[3], edge_vecs[0]) - float(M_PI_2))) /
79 float(M_PI * 2);
80
81 error += diff;
82 }
83
84 /* Concavity */
85 {
86 float area_min, area_max, area_a, area_b;
87 float diff;
88
89 area_a = area_tri_v3(v1, v2, v3) + area_tri_v3(v1, v3, v4);
90 area_b = area_tri_v3(v2, v3, v4) + area_tri_v3(v4, v1, v2);
91
92 area_min = min_ff(area_a, area_b);
93 area_max = max_ff(area_a, area_b);
94
95 diff = area_max ? (1.0f - (area_min / area_max)) : 1.0f;
96
97 error += diff;
98 }
99
100 return error;
101}
102
103static void bm_edge_to_quad_verts(const BMEdge *e, const BMVert *r_v_quad[4])
104{
105 BLI_assert(e->l->f->len == 3 && e->l->radial_next->f->len == 3);
107 r_v_quad[0] = e->l->v;
108 r_v_quad[1] = e->l->prev->v;
109 r_v_quad[2] = e->l->next->v;
110 r_v_quad[3] = e->l->radial_next->prev->v;
111}
112
113/* cache customdata delimiters */
120
136
137static bool bm_edge_is_contiguous_loop_cd_all(const BMEdge *e, const DelimitData_CD *delimit_data)
138{
139 int cd_offset;
140 for (cd_offset = delimit_data->cd_offset; cd_offset < delimit_data->cd_offset_end;
141 cd_offset += delimit_data->cd_size)
142 {
143 if (BM_edge_is_contiguous_loop_cd(e, delimit_data->cd_type, cd_offset) == false) {
144 return false;
145 }
146 }
147
148 return true;
149}
150
152 eCustomDataType type,
153 DelimitData_CD *r_delim_cd)
154{
155 const int layer_len = CustomData_number_of_layers(ldata, type);
156 r_delim_cd->cd_type = type;
157 r_delim_cd->cd_size = CustomData_sizeof(eCustomDataType(r_delim_cd->cd_type));
158 r_delim_cd->cd_offset = CustomData_get_n_offset(ldata, type, 0);
159 r_delim_cd->cd_offset_end = r_delim_cd->cd_offset + (r_delim_cd->cd_size * layer_len);
160 return (r_delim_cd->cd_offset != -1);
161}
162
163static float bm_edge_is_delimit(const BMEdge *e, const DelimitData *delimit_data)
164{
165 BMFace *f_a = e->l->f, *f_b = e->l->radial_next->f;
166#if 0
167 const bool is_contig = BM_edge_is_contiguous(e);
168 float angle;
169#endif
170
171 if (delimit_data->do_seam && BM_elem_flag_test(e, BM_ELEM_SEAM)) {
172 goto fail;
173 }
174
175 if (delimit_data->do_sharp && (BM_elem_flag_test(e, BM_ELEM_SMOOTH) == 0)) {
176 goto fail;
177 }
178
179 if (delimit_data->do_mat && (f_a->mat_nr != f_b->mat_nr)) {
180 goto fail;
181 }
182
183 if (delimit_data->do_angle_face) {
184 if (dot_v3v3(f_a->no, f_b->no) < delimit_data->angle_face__cos) {
185 goto fail;
186 }
187 }
188
189 if (delimit_data->do_angle_shape) {
190 const BMVert *verts[4];
192
193 /* if we're checking the shape at all, a flipped face is out of the question */
194 if (is_quad_flip_v3(verts[0]->co, verts[1]->co, verts[2]->co, verts[3]->co)) {
195 goto fail;
196 }
197 else {
198 float edge_vecs[4][3];
199
200 sub_v3_v3v3(edge_vecs[0], verts[0]->co, verts[1]->co);
201 sub_v3_v3v3(edge_vecs[1], verts[1]->co, verts[2]->co);
202 sub_v3_v3v3(edge_vecs[2], verts[2]->co, verts[3]->co);
203 sub_v3_v3v3(edge_vecs[3], verts[3]->co, verts[0]->co);
204
205 normalize_v3(edge_vecs[0]);
206 normalize_v3(edge_vecs[1]);
207 normalize_v3(edge_vecs[2]);
208 normalize_v3(edge_vecs[3]);
209
210 if ((fabsf(angle_normalized_v3v3(edge_vecs[0], edge_vecs[1]) - float(M_PI_2)) >
211 delimit_data->angle_shape) ||
212 (fabsf(angle_normalized_v3v3(edge_vecs[1], edge_vecs[2]) - float(M_PI_2)) >
213 delimit_data->angle_shape) ||
214 (fabsf(angle_normalized_v3v3(edge_vecs[2], edge_vecs[3]) - float(M_PI_2)) >
215 delimit_data->angle_shape) ||
216 (fabsf(angle_normalized_v3v3(edge_vecs[3], edge_vecs[0]) - float(M_PI_2)) >
217 delimit_data->angle_shape))
218 {
219 goto fail;
220 }
221 }
222 }
223
224 if (delimit_data->cdata_len) {
225 int i;
226 for (i = 0; i < delimit_data->cdata_len; i++) {
227 if (!bm_edge_is_contiguous_loop_cd_all(e, &delimit_data->cdata[i])) {
228 goto fail;
229 }
230 }
231 }
232
233 return false;
234
235fail:
236 return true;
237}
238
239#define EDGE_MARK (1 << 0)
240
241#define FACE_OUT (1 << 0)
242#define FACE_INPUT (1 << 2)
243
245{
246 float angle_face, angle_shape;
247
248 BMIter iter;
249 BMOIter siter;
250 BMFace *f;
251 BMEdge *e;
252 /* data: edge-to-join, sort_value: error weight */
253 SortPtrByFloat *jedges;
254 uint i, totedge;
255 uint totedge_tag = 0;
256
257 DelimitData delimit_data = {0};
258
259 delimit_data.do_seam = BMO_slot_bool_get(op->slots_in, "cmp_seam");
260 delimit_data.do_sharp = BMO_slot_bool_get(op->slots_in, "cmp_sharp");
261 delimit_data.do_mat = BMO_slot_bool_get(op->slots_in, "cmp_materials");
262
263 angle_face = BMO_slot_float_get(op->slots_in, "angle_face_threshold");
264 if (angle_face < DEG2RADF(180.0f)) {
265 delimit_data.angle_face = angle_face;
266 delimit_data.angle_face__cos = cosf(angle_face);
267 delimit_data.do_angle_face = true;
268 }
269 else {
270 delimit_data.do_angle_face = false;
271 }
272
273 angle_shape = BMO_slot_float_get(op->slots_in, "angle_shape_threshold");
274 if (angle_shape < DEG2RADF(180.0f)) {
275 delimit_data.angle_shape = angle_shape;
276 delimit_data.do_angle_shape = true;
277 }
278 else {
279 delimit_data.do_angle_shape = false;
280 }
281
282 if (BMO_slot_bool_get(op->slots_in, "cmp_uvs") &&
284 &bm->ldata, CD_PROP_FLOAT2, &delimit_data.cdata[delimit_data.cdata_len]))
285 {
286 delimit_data.cdata_len += 1;
287 }
288
289 delimit_data.cdata[delimit_data.cdata_len].cd_offset = -1;
290 if (BMO_slot_bool_get(op->slots_in, "cmp_vcols") &&
292 &bm->ldata, CD_PROP_BYTE_COLOR, &delimit_data.cdata[delimit_data.cdata_len]))
293 {
294 delimit_data.cdata_len += 1;
295 }
296
297 /* flag all edges of all input face */
298 BMO_ITER (f, &siter, op->slots_in, "faces", BM_FACE) {
299 if (f->len == 3) {
301 }
302 }
303
304 /* flag edges surrounded by 2 flagged triangles */
305 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
306 BMFace *f_a, *f_b;
307 if (BM_edge_face_pair(e, &f_a, &f_b) &&
309 {
310 if (!bm_edge_is_delimit(e, &delimit_data)) {
312 totedge_tag++;
313 }
314 }
315 }
316
317 if (totedge_tag == 0) {
318 return;
319 }
320
321 /* over alloc, some of the edges will be delimited */
322 jedges = static_cast<SortPtrByFloat *>(MEM_mallocN(sizeof(*jedges) * totedge_tag, __func__));
323
324 i = 0;
325 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
326 const BMVert *verts[4];
327 float error;
328
330 continue;
331 }
332
334
335 error = quad_calc_error(verts[0]->co, verts[1]->co, verts[2]->co, verts[3]->co);
336
337 jedges[i].data = e;
338 jedges[i].sort_value = error;
339 i++;
340 }
341
342 totedge = i;
343 qsort(jedges, totedge, sizeof(*jedges), BLI_sortutil_cmp_float);
344
345 for (i = 0; i < totedge; i++) {
346 BMLoop *l_a, *l_b;
347
348 e = static_cast<BMEdge *>(jedges[i].data);
349 l_a = e->l;
350 l_b = e->l->radial_next;
351
352 /* check if another edge already claimed this face */
353 if ((l_a->f->len == 3) && (l_b->f->len == 3)) {
354 BMFace *f_new;
355 f_new = BM_faces_join_pair(bm, l_a, l_b, true);
356 if (f_new) {
358 }
359 }
360 }
361
362 MEM_freeN(jedges);
363
365}
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:50
MINLINE float max_ff(float a, float b)
MINLINE float min_ff(float a, float b)
#define M_PI_2
#define M_PI
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:98
float normal_tri_v3(float n[3], const float v1[3], const float v2[3], const float v3[3])
Definition math_geom.cc:39
#define DEG2RADF(_deg)
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE float dot_v3v3(const float a[3], const float b[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 float normalize_v3(float n[3])
int BLI_sortutil_cmp_float(const void *a_, const void *b_)
Definition sort_utils.c:25
unsigned int uint
@ CD_PROP_BYTE_COLOR
@ CD_PROP_FLOAT2
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:125
Read Guarded memory(de)allocation.
@ BM_ELEM_SEAM
@ BM_ELEM_SMOOTH
#define BM_elem_flag_test(ele, hflag)
#define BM_ITER_MESH(ele, iter, bm, itype)
@ BM_EDGES_OF_MESH
ATTR_WARN_UNUSED_RESULT BMesh * bm
BMFace * BM_faces_join_pair(BMesh *bm, BMLoop *l_a, BMLoop *l_b, const bool do_del)
Faces Join Pair.
#define BM_FACE
#define BMO_edge_flag_test(bm, e, oflag)
#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)
#define BMO_face_flag_test(bm, e, oflag)
bool BMO_slot_bool_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
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 BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMLoop * l_b
#define FACE_OUT
void bmo_join_triangles_exec(BMesh *bm, BMOperator *op)
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)
#define EDGE_MARK
#define FACE_INPUT
static float quad_calc_error(const float v1[3], const float v2[3], const float v3[3], const float v4[3])
static float bm_edge_is_delimit(const BMEdge *e, const DelimitData *delimit_data)
static bool bm_edge_delimit_cdata(CustomData *ldata, eCustomDataType type, DelimitData_CD *r_delim_cd)
#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]
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
static void error(const char *str)
short mat_nr
float no[3]
struct BMFace * f
struct BMOpSlot slots_out[BMO_OP_MAX_SLOTS]
struct BMOpSlot slots_in[BMO_OP_MAX_SLOTS]
CustomData ldata
DelimitData_CD cdata[4]