Blender  V2.93
bmo_join_triangles.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
26 #include "MEM_guardedalloc.h"
27 
28 #include "DNA_meshdata_types.h"
29 
30 #include "BLI_math.h"
31 #include "BLI_sort_utils.h"
32 
33 #include "BKE_customdata.h"
34 
35 #include "bmesh.h"
36 
37 #include "intern/bmesh_operators_private.h" /* own include */
38 
39 /* assumes edges are validated before reaching this poin */
40 static float quad_calc_error(const float v1[3],
41  const float v2[3],
42  const float v3[3],
43  const float v4[3])
44 {
45  /* Gives a 'weight' to a pair of triangles that join an edge
46  * to decide how good a join they would make. */
47  /* Note: this is more complicated than it needs to be and should be cleaned up.. */
48  float error = 0.0f;
49 
50  /* Normal difference */
51  {
52  float n1[3], n2[3];
53  float angle_a, angle_b;
54  float diff;
55 
56  normal_tri_v3(n1, v1, v2, v3);
57  normal_tri_v3(n2, v1, v3, v4);
58  angle_a = (compare_v3v3(n1, n2, FLT_EPSILON)) ? 0.0f : angle_normalized_v3v3(n1, n2);
59 
60  normal_tri_v3(n1, v2, v3, v4);
61  normal_tri_v3(n2, v4, v1, v2);
62  angle_b = (compare_v3v3(n1, n2, FLT_EPSILON)) ? 0.0f : angle_normalized_v3v3(n1, n2);
63 
64  diff = (angle_a + angle_b) / (float)(M_PI * 2);
65 
66  error += diff;
67  }
68 
69  /* Co-linearity */
70  {
71  float edge_vecs[4][3];
72  float diff;
73 
74  sub_v3_v3v3(edge_vecs[0], v1, v2);
75  sub_v3_v3v3(edge_vecs[1], v2, v3);
76  sub_v3_v3v3(edge_vecs[2], v3, v4);
77  sub_v3_v3v3(edge_vecs[3], v4, v1);
78 
79  normalize_v3(edge_vecs[0]);
80  normalize_v3(edge_vecs[1]);
81  normalize_v3(edge_vecs[2]);
82  normalize_v3(edge_vecs[3]);
83 
84  /* a completely skinny face is 'pi' after halving */
85  diff = (fabsf(angle_normalized_v3v3(edge_vecs[0], edge_vecs[1]) - (float)M_PI_2) +
86  fabsf(angle_normalized_v3v3(edge_vecs[1], edge_vecs[2]) - (float)M_PI_2) +
87  fabsf(angle_normalized_v3v3(edge_vecs[2], edge_vecs[3]) - (float)M_PI_2) +
88  fabsf(angle_normalized_v3v3(edge_vecs[3], edge_vecs[0]) - (float)M_PI_2)) /
89  (float)(M_PI * 2);
90 
91  error += diff;
92  }
93 
94  /* Concavity */
95  {
96  float area_min, area_max, area_a, area_b;
97  float diff;
98 
99  area_a = area_tri_v3(v1, v2, v3) + area_tri_v3(v1, v3, v4);
100  area_b = area_tri_v3(v2, v3, v4) + area_tri_v3(v4, v1, v2);
101 
102  area_min = min_ff(area_a, area_b);
103  area_max = max_ff(area_a, area_b);
104 
105  diff = area_max ? (1.0f - (area_min / area_max)) : 1.0f;
106 
107  error += diff;
108  }
109 
110  return error;
111 }
112 
113 static void bm_edge_to_quad_verts(const BMEdge *e, const BMVert *r_v_quad[4])
114 {
115  BLI_assert(e->l->f->len == 3 && e->l->radial_next->f->len == 3);
117  r_v_quad[0] = e->l->v;
118  r_v_quad[1] = e->l->prev->v;
119  r_v_quad[2] = e->l->next->v;
120  r_v_quad[3] = e->l->radial_next->prev->v;
121 }
122 
123 /* cache customdata delimiters */
125  int cd_type;
126  int cd_size;
129 };
130 
131 struct DelimitData {
137 
138  float angle_face;
140 
141  float angle_shape;
142 
143  struct DelimitData_CD cdata[4];
145 };
146 
148  const struct DelimitData_CD *delimit_data)
149 {
150  int cd_offset;
151  for (cd_offset = delimit_data->cd_offset; cd_offset < delimit_data->cd_offset_end;
152  cd_offset += delimit_data->cd_size) {
153  if (BM_edge_is_contiguous_loop_cd(e, delimit_data->cd_type, cd_offset) == false) {
154  return false;
155  }
156  }
157 
158  return true;
159 }
160 
163  struct DelimitData_CD *r_delim_cd)
164 {
165  const int layer_len = CustomData_number_of_layers(ldata, type);
166  r_delim_cd->cd_type = type;
167  r_delim_cd->cd_size = CustomData_sizeof(r_delim_cd->cd_type);
168  r_delim_cd->cd_offset = CustomData_get_n_offset(ldata, type, 0);
169  r_delim_cd->cd_offset_end = r_delim_cd->cd_offset + (r_delim_cd->cd_size * layer_len);
170  return (r_delim_cd->cd_offset != -1);
171 }
172 
173 static float bm_edge_is_delimit(const BMEdge *e, const struct DelimitData *delimit_data)
174 {
175  BMFace *f_a = e->l->f, *f_b = e->l->radial_next->f;
176 #if 0
177  const bool is_contig = BM_edge_is_contiguous(e);
178  float angle;
179 #endif
180 
181  if ((delimit_data->do_seam) && (BM_elem_flag_test(e, BM_ELEM_SEAM))) {
182  goto fail;
183  }
184 
185  if ((delimit_data->do_sharp) && (BM_elem_flag_test(e, BM_ELEM_SMOOTH) == 0)) {
186  goto fail;
187  }
188 
189  if ((delimit_data->do_mat) && (f_a->mat_nr != f_b->mat_nr)) {
190  goto fail;
191  }
192 
193  if (delimit_data->do_angle_face) {
194  if (dot_v3v3(f_a->no, f_b->no) < delimit_data->angle_face__cos) {
195  goto fail;
196  }
197  }
198 
199  if (delimit_data->do_angle_shape) {
200  const BMVert *verts[4];
202 
203  /* if we're checking the shape at all, a flipped face is out of the question */
204  if (is_quad_flip_v3(verts[0]->co, verts[1]->co, verts[2]->co, verts[3]->co)) {
205  goto fail;
206  }
207  else {
208  float edge_vecs[4][3];
209 
210  sub_v3_v3v3(edge_vecs[0], verts[0]->co, verts[1]->co);
211  sub_v3_v3v3(edge_vecs[1], verts[1]->co, verts[2]->co);
212  sub_v3_v3v3(edge_vecs[2], verts[2]->co, verts[3]->co);
213  sub_v3_v3v3(edge_vecs[3], verts[3]->co, verts[0]->co);
214 
215  normalize_v3(edge_vecs[0]);
216  normalize_v3(edge_vecs[1]);
217  normalize_v3(edge_vecs[2]);
218  normalize_v3(edge_vecs[3]);
219 
220  if ((fabsf(angle_normalized_v3v3(edge_vecs[0], edge_vecs[1]) - (float)M_PI_2) >
221  delimit_data->angle_shape) ||
222  (fabsf(angle_normalized_v3v3(edge_vecs[1], edge_vecs[2]) - (float)M_PI_2) >
223  delimit_data->angle_shape) ||
224  (fabsf(angle_normalized_v3v3(edge_vecs[2], edge_vecs[3]) - (float)M_PI_2) >
225  delimit_data->angle_shape) ||
226  (fabsf(angle_normalized_v3v3(edge_vecs[3], edge_vecs[0]) - (float)M_PI_2) >
227  delimit_data->angle_shape)) {
228  goto fail;
229  }
230  }
231  }
232 
233  if (delimit_data->cdata_len) {
234  int i;
235  for (i = 0; i < delimit_data->cdata_len; i++) {
236  if (!bm_edge_is_contiguous_loop_cd_all(e, &delimit_data->cdata[i])) {
237  goto fail;
238  }
239  }
240  }
241 
242  return false;
243 
244 fail:
245  return true;
246 }
247 
248 #define EDGE_MARK (1 << 0)
249 
250 #define FACE_OUT (1 << 0)
251 #define FACE_INPUT (1 << 2)
252 
254 {
255  float angle_face, angle_shape;
256 
257  BMIter iter;
258  BMOIter siter;
259  BMFace *f;
260  BMEdge *e;
261  /* data: edge-to-join, sort_value: error weight */
262  struct SortPtrByFloat *jedges;
263  uint i, totedge;
264  uint totedge_tag = 0;
265 
266  struct DelimitData delimit_data = {0};
267 
268  delimit_data.do_seam = BMO_slot_bool_get(op->slots_in, "cmp_seam");
269  delimit_data.do_sharp = BMO_slot_bool_get(op->slots_in, "cmp_sharp");
270  delimit_data.do_mat = BMO_slot_bool_get(op->slots_in, "cmp_materials");
271 
272  angle_face = BMO_slot_float_get(op->slots_in, "angle_face_threshold");
273  if (angle_face < DEG2RADF(180.0f)) {
274  delimit_data.angle_face = angle_face;
275  delimit_data.angle_face__cos = cosf(angle_face);
276  delimit_data.do_angle_face = true;
277  }
278  else {
279  delimit_data.do_angle_face = false;
280  }
281 
282  angle_shape = BMO_slot_float_get(op->slots_in, "angle_shape_threshold");
283  if (angle_shape < DEG2RADF(180.0f)) {
284  delimit_data.angle_shape = angle_shape;
285  delimit_data.do_angle_shape = true;
286  }
287  else {
288  delimit_data.do_angle_shape = false;
289  }
290 
291  if (BMO_slot_bool_get(op->slots_in, "cmp_uvs") &&
292  bm_edge_delimit_cdata(&bm->ldata, CD_MLOOPUV, &delimit_data.cdata[delimit_data.cdata_len])) {
293  delimit_data.cdata_len += 1;
294  }
295 
296  delimit_data.cdata[delimit_data.cdata_len].cd_offset = -1;
297  if (BMO_slot_bool_get(op->slots_in, "cmp_vcols") &&
299  &bm->ldata, CD_MLOOPCOL, &delimit_data.cdata[delimit_data.cdata_len])) {
300  delimit_data.cdata_len += 1;
301  }
302 
303  /* flag all edges of all input face */
304  BMO_ITER (f, &siter, op->slots_in, "faces", BM_FACE) {
305  if (f->len == 3) {
307  }
308  }
309 
310  /* flag edges surrounded by 2 flagged triangles */
311  BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
312  BMFace *f_a, *f_b;
313  if (BM_edge_face_pair(e, &f_a, &f_b) &&
315  if (!bm_edge_is_delimit(e, &delimit_data)) {
317  totedge_tag++;
318  }
319  }
320  }
321 
322  if (totedge_tag == 0) {
323  return;
324  }
325 
326  /* over alloc, some of the edges will be delimited */
327  jedges = MEM_mallocN(sizeof(*jedges) * totedge_tag, __func__);
328 
329  i = 0;
330  BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
331  const BMVert *verts[4];
332  float error;
333 
334  if (!BMO_edge_flag_test(bm, e, EDGE_MARK)) {
335  continue;
336  }
337 
339 
340  error = quad_calc_error(verts[0]->co, verts[1]->co, verts[2]->co, verts[3]->co);
341 
342  jedges[i].data = e;
343  jedges[i].sort_value = error;
344  i++;
345  }
346 
347  totedge = i;
348  qsort(jedges, totedge, sizeof(*jedges), BLI_sortutil_cmp_float);
349 
350  for (i = 0; i < totedge; i++) {
351  BMLoop *l_a, *l_b;
352 
353  e = jedges[i].data;
354  l_a = e->l;
355  l_b = e->l->radial_next;
356 
357  /* check if another edge already claimed this face */
358  if ((l_a->f->len == 3) && (l_b->f->len == 3)) {
359  BMFace *f_new;
360  f_new = BM_faces_join_pair(bm, l_a, l_b, true);
361  if (f_new) {
363  }
364  }
365  }
366 
367  MEM_freeN(jedges);
368 
370 }
CustomData interface, see also DNA_customdata_types.h.
int CustomData_number_of_layers(const struct CustomData *data, int type)
int CustomData_sizeof(int type)
Definition: customdata.c:4277
int CustomData_get_n_offset(const struct CustomData *data, int type, int n)
#define BLI_assert(a)
Definition: BLI_assert.h:58
MINLINE float max_ff(float a, float b)
MINLINE float min_ff(float a, float b)
#define M_PI_2
Definition: BLI_math_base.h:41
#define M_PI
Definition: BLI_math_base.h:38
int is_quad_flip_v3(const float v1[3], const float v2[3], const float v3[3], const float v4[3])
Definition: math_geom.c:6184
float area_tri_v3(const float v1[3], const float v2[3], const float v3[3])
Definition: math_geom.c:116
float normal_tri_v3(float n[3], const float v1[3], const float v2[3], const float v3[3])
Definition: math_geom.c:51
#define DEG2RADF(_deg)
MINLINE bool compare_v3v3(const float a[3], const float b[3], const float limit) ATTR_WARN_UNUSED_RESULT
MINLINE float normalize_v3(float r[3])
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
float angle_normalized_v3v3(const float v1[3], const float v2[3]) ATTR_WARN_UNUSED_RESULT
Definition: math_vector.c:505
int BLI_sortutil_cmp_float(const void *a_, const void *b_)
Definition: sort_utils.c:40
unsigned int uint
Definition: BLI_sys_types.h:83
CustomDataType
@ CD_MLOOPCOL
@ CD_MLOOPUV
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble v1
Read Guarded memory(de)allocation.
@ BM_FACE
Definition: bmesh_class.h:386
@ BM_ELEM_SEAM
Definition: bmesh_class.h:473
@ BM_ELEM_SMOOTH
Definition: bmesh_class.h:477
#define BM_elem_flag_test(ele, hflag)
Definition: bmesh_inline.h:26
#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.
Definition: bmesh_mods.c:221
#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)
#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)
void BMO_slot_buffer_from_enabled_flag(BMesh *bm, BMOperator *op, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, const char htype, const short 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)
Definition: bmesh_query.c:1127
bool BM_edge_face_pair(BMEdge *e, BMFace **r_fa, BMFace **r_fb)
Definition: bmesh_query.c:732
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
static bool bm_edge_delimit_cdata(CustomData *ldata, CustomDataType type, struct DelimitData_CD *r_delim_cd)
#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 struct 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 struct DelimitData *delimit_data)
SIMD_FORCE_INLINE btScalar angle(const btVector3 &v) const
Return the angle between this and another vector.
Definition: btVector3.h:356
static float verts[][3]
#define cosf(x)
#define fabsf(x)
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:47
static void error(const char *str)
Definition: meshlaplacian.c:65
IMETHOD Vector diff(const Vector &a, const Vector &b, double dt=1)
short mat_nr
Definition: bmesh_class.h:281
int len
Definition: bmesh_class.h:279
float no[3]
Definition: bmesh_class.h:280
struct BMFace * f
Definition: bmesh_class.h:183
struct BMOpSlot slots_out[BMO_OP_MAX_SLOTS]
struct BMOpSlot slots_in[BMO_OP_MAX_SLOTS]
CustomData ldata
Definition: bmesh_class.h:337
struct DelimitData_CD cdata[4]