Blender  V2.93
bmesh_boolean.cc
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 
23 #include "BLI_array.hh"
24 #include "BLI_math.h"
25 #include "BLI_math_mpq.hh"
26 #include "BLI_mesh_boolean.hh"
27 #include "BLI_mesh_intersect.hh"
28 
29 #include "bmesh.h"
30 #include "bmesh_boolean.h"
31 #include "bmesh_edgesplit.h"
32 
33 #include "PIL_time.h"
34 
35 // #define PERF_DEBUG
36 
37 namespace blender::meshintersect {
38 
39 #ifdef WITH_GMP
40 
50 static IMesh mesh_from_bm(BMesh *bm,
51  struct BMLoop *(*looptris)[3],
52  const int looptris_tot,
53  IMesh *r_triangulated,
54  IMeshArena *arena)
55 {
56  BLI_assert(r_triangulated != nullptr);
59  /* Account for triangulation and intersects. */
60  const int estimate_num_outv = 3 * bm->totvert;
61  const int estimate_num_outf = 4 * bm->totface;
62  arena->reserve(estimate_num_outv, estimate_num_outf);
63  Array<const Vert *> vert(bm->totvert);
64  for (int v = 0; v < bm->totvert; ++v) {
65  BMVert *bmv = BM_vert_at_index(bm, v);
66  vert[v] = arena->add_or_find_vert(mpq3(bmv->co[0], bmv->co[1], bmv->co[2]), v);
67  }
68  Array<Face *> face(bm->totface);
69  constexpr int estimated_max_facelen = 100;
70  Vector<const Vert *, estimated_max_facelen> face_vert;
71  Vector<int, estimated_max_facelen> face_edge_orig;
72  for (int f = 0; f < bm->totface; ++f) {
73  BMFace *bmf = BM_face_at_index(bm, f);
74  int flen = bmf->len;
75  face_vert.clear();
76  face_edge_orig.clear();
77  BMLoop *l = bmf->l_first;
78  for (int i = 0; i < flen; ++i) {
79  const Vert *v = vert[BM_elem_index_get(l->v)];
80  face_vert.append(v);
81  int e_index = BM_elem_index_get(l->e);
82  face_edge_orig.append(e_index);
83  l = l->next;
84  }
85  face[f] = arena->add_face(face_vert, f, face_edge_orig);
86  }
87  /* Now do the triangulation mesh.
88  * The loop_tris have accurate v and f members for the triangles,
89  * but their next and e pointers are not correct for the loops
90  * that start added-diagonal edges. */
91  Array<Face *> tri_face(looptris_tot);
92  face_vert.resize(3);
93  face_edge_orig.resize(3);
94  for (int i = 0; i < looptris_tot; ++i) {
95  BMFace *bmf = looptris[i][0]->f;
96  int f = BM_elem_index_get(bmf);
97  for (int j = 0; j < 3; ++j) {
98  BMLoop *l = looptris[i][j];
99  int v_index = BM_elem_index_get(l->v);
100  int e_index;
101  if (l->next->v == looptris[i][(j + 1) % 3]->v) {
102  e_index = BM_elem_index_get(l->e);
103  }
104  else {
105  e_index = NO_INDEX;
106  }
107  face_vert[j] = vert[v_index];
108  face_edge_orig[j] = e_index;
109  }
110  tri_face[i] = arena->add_face(face_vert, f, face_edge_orig);
111  }
112  r_triangulated->set_faces(tri_face);
113  return IMesh(face);
114 }
115 
116 static bool bmvert_attached_to_wire(const BMVert *bmv)
117 {
118  /* This is not quite right. It returns true if the only edges
119  * Attached to \a bmv are wire edges. TODO: iterate through edges
120  * attached to \a bmv and check #BM_edge_is_wire. */
121  return BM_vert_is_wire(bmv);
122 }
123 
124 static bool bmvert_attached_to_hidden_face(BMVert *bmv)
125 {
126  BMIter iter;
127  for (BMFace *bmf = static_cast<BMFace *>(BM_iter_new(&iter, nullptr, BM_FACES_OF_VERT, bmv));
128  bmf;
129  bmf = static_cast<BMFace *>(BM_iter_step(&iter))) {
130  if (BM_elem_flag_test(bmf, BM_ELEM_HIDDEN)) {
131  return true;
132  }
133  }
134  return false;
135 }
136 
137 static bool face_has_verts_in_order(BMesh *bm, BMFace *bmf, const BMVert *v1, const BMVert *v2)
138 {
139  BMIter liter;
140  BMLoop *l = static_cast<BMLoop *>(BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, bmf));
141  while (l != nullptr) {
142  if (l->v == v1 && l->next->v == v2) {
143  return true;
144  }
145  l = static_cast<BMLoop *>(BM_iter_step(&liter));
146  }
147  return false;
148 }
149 
151 constexpr uint KEEP_FLAG = (1 << 6);
152 
160 static bool apply_mesh_output_to_bmesh(BMesh *bm, IMesh &m_out, bool keep_hidden)
161 {
162  bool any_change = false;
163 
164  m_out.populate_vert();
165 
166  /* Initially mark all existing verts as "don't keep", except hidden verts
167  * (if keep_hidden is true), and verts attached to wire edges. */
168  for (int v = 0; v < bm->totvert; ++v) {
169  BMVert *bmv = BM_vert_at_index(bm, v);
170  if ((keep_hidden &&
171  (BM_elem_flag_test(bmv, BM_ELEM_HIDDEN) || bmvert_attached_to_hidden_face(bmv))) ||
172  bmvert_attached_to_wire(bmv)) {
173  BM_elem_flag_enable(bmv, KEEP_FLAG);
174  }
175  else {
176  BM_elem_flag_disable(bmv, KEEP_FLAG);
177  }
178  }
179 
180  /* Reuse old or make new #BMVert's, depending on if there's an orig or not.
181  * For those reused, mark them "keep".
182  * Store needed old #BMVert's in new_bmvs first, as the table may be unusable after
183  * creating a new #BMVert. */
184  Array<BMVert *> new_bmvs(m_out.vert_size());
185  for (int v : m_out.vert_index_range()) {
186  const Vert *vertp = m_out.vert(v);
187  int orig = vertp->orig;
188  if (orig != NO_INDEX) {
189  BLI_assert(orig >= 0 && orig < bm->totvert);
190  BMVert *bmv = BM_vert_at_index(bm, orig);
191  new_bmvs[v] = bmv;
192  BM_elem_flag_enable(bmv, KEEP_FLAG);
193  }
194  else {
195  new_bmvs[v] = NULL;
196  }
197  }
198  for (int v : m_out.vert_index_range()) {
199  const Vert *vertp = m_out.vert(v);
200  if (new_bmvs[v] == NULL) {
201  float co[3];
202  const double3 &d_co = vertp->co;
203  for (int i = 0; i < 3; ++i) {
204  co[i] = static_cast<float>(d_co[i]);
205  }
206  BMVert *bmv = BM_vert_create(bm, co, nullptr, BM_CREATE_NOP);
207  new_bmvs[v] = bmv;
208  BM_elem_flag_enable(bmv, KEEP_FLAG);
209  any_change = true;
210  }
211  }
212 
213  /* Initially mark all existing faces as "don't keep", except hidden faces (if keep_hidden).
214  * Also, save current #BMFace pointers as creating faces will disturb the table. */
215  Array<BMFace *> old_bmfs(bm->totface);
217  for (int f = 0; f < bm->totface; ++f) {
218  BMFace *bmf = BM_face_at_index(bm, f);
219  old_bmfs[f] = bmf;
220  if (keep_hidden && BM_elem_flag_test(bmf, BM_ELEM_HIDDEN)) {
221  BM_elem_flag_enable(bmf, KEEP_FLAG);
222  }
223  else {
224  BM_elem_flag_disable(bmf, KEEP_FLAG);
225  }
226  }
227 
228  /* Save the original #BMEdge's so we can use them as examples. */
229  Array<BMEdge *> old_edges(bm->totedge);
230  std::copy(bm->etable, bm->etable + bm->totedge, old_edges.begin());
231 
232  /* Reuse or make new #BMFace's, as the faces are identical to old ones or not.
233  * If reusing, mark them as "keep". First find the maximum face length
234  * so we can declare some arrays outside of the face-creating loop. */
235  int maxflen = 0;
236  for (const Face *f : m_out.faces()) {
237  maxflen = max_ii(maxflen, f->size());
238  }
239  Array<BMVert *> face_bmverts(maxflen);
240  Array<BMEdge *> face_bmedges(maxflen);
241  for (const Face *f : m_out.faces()) {
242  const Face &face = *f;
243  int flen = face.size();
244  for (int i = 0; i < flen; ++i) {
245  const Vert *v = face[i];
246  int v_index = m_out.lookup_vert(v);
247  BLI_assert(v_index < new_bmvs.size());
248  face_bmverts[i] = new_bmvs[v_index];
249  }
250  BMFace *bmf = BM_face_exists(face_bmverts.data(), flen);
251  /* #BM_face_exists checks if the face exists with the vertices in either order.
252  * We can only reuse the face if the orientations are the same. */
253  if (bmf != nullptr && face_has_verts_in_order(bm, bmf, face_bmverts[0], face_bmverts[1])) {
254  BM_elem_flag_enable(bmf, KEEP_FLAG);
255  }
256  else {
257  int orig = face.orig;
258  BMFace *orig_face;
259  /* There should always be an orig face, but just being extra careful here. */
260  if (orig != NO_INDEX) {
261  orig_face = old_bmfs[orig];
262  }
263  else {
264  orig_face = nullptr;
265  }
266  /* Make or find #BMEdge's. */
267  for (int i = 0; i < flen; ++i) {
268  BMVert *bmv1 = face_bmverts[i];
269  BMVert *bmv2 = face_bmverts[(i + 1) % flen];
270  BMEdge *bme = BM_edge_exists(bmv1, bmv2);
271  if (bme == nullptr) {
272  BMEdge *orig_edge = nullptr;
273  if (face.edge_orig[i] != NO_INDEX) {
274  orig_edge = old_edges[face.edge_orig[i]];
275  }
276  bme = BM_edge_create(bm, bmv1, bmv2, orig_edge, BM_CREATE_NOP);
277  if (orig_edge != nullptr) {
278  BM_elem_select_copy(bm, bme, orig_edge);
279  }
280  }
281  face_bmedges[i] = bme;
282  if (face.is_intersect[i]) {
284  }
285  else {
287  }
288  }
289  BMFace *bmf = BM_face_create(
290  bm, face_bmverts.data(), face_bmedges.data(), flen, orig_face, BM_CREATE_NOP);
291  if (orig_face != nullptr) {
292  BM_elem_select_copy(bm, bmf, orig_face);
293  }
294  BM_elem_flag_enable(bmf, KEEP_FLAG);
295  /* Now do interpolation of loop data (e.g., UV's) using the example face. */
296  if (orig_face != nullptr) {
297  BMIter liter;
298  BMLoop *l = static_cast<BMLoop *>(BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, bmf));
299  while (l != nullptr) {
300  BM_loop_interp_from_face(bm, l, orig_face, false, true);
301  l = static_cast<BMLoop *>(BM_iter_step(&liter));
302  }
303  }
304  any_change = true;
305  }
306  }
307 
308  /* Now kill the unused faces and verts, and clear flags for kept ones. */
309  /* #BM_ITER_MESH_MUTABLE macro needs type casts for C++, so expand here.
310  * TODO(howard): make some nice C++ iterators for #BMesh. */
311  BMIter iter;
312  BMFace *bmf = static_cast<BMFace *>(BM_iter_new(&iter, bm, BM_FACES_OF_MESH, nullptr));
313  while (bmf != nullptr) {
314 # ifdef DEBUG
316 # endif
317  BMFace *bmf_next = static_cast<BMFace *>(BM_iter_step(&iter));
318  if (BM_elem_flag_test(bmf, KEEP_FLAG)) {
319  BM_elem_flag_disable(bmf, KEEP_FLAG);
320  }
321  else {
322  BM_face_kill_loose(bm, bmf);
323 # if 0
324  BM_face_kill(bm, bmf);
325 # endif
326  any_change = true;
327  }
328  bmf = bmf_next;
329  }
330  BMVert *bmv = static_cast<BMVert *>(BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, nullptr));
331  while (bmv != nullptr) {
332 # ifdef DEBUG
334 # endif
335  BMVert *bmv_next = static_cast<BMVert *>(BM_iter_step(&iter));
336  if (BM_elem_flag_test(bmv, KEEP_FLAG)) {
337  BM_elem_flag_disable(bmv, KEEP_FLAG);
338  }
339  else {
340  BM_vert_kill(bm, bmv);
341  any_change = true;
342  }
343  bmv = bmv_next;
344  }
345 
346  return any_change;
347 }
348 
349 static bool bmesh_boolean(BMesh *bm,
350  struct BMLoop *(*looptris)[3],
351  const int looptris_tot,
352  int (*test_fn)(BMFace *f, void *user_data),
353  void *user_data,
354  int nshapes,
355  const bool use_self,
356  const bool use_separate_all,
357  const bool keep_hidden,
358  const bool hole_tolerant,
359  const BoolOpType boolean_mode)
360 {
361  IMeshArena arena;
362  IMesh m_triangulated;
363 # ifdef PERF_DEBUG
364  double start_time = PIL_check_seconds_timer();
365 # endif
366  IMesh m_in = mesh_from_bm(bm, looptris, looptris_tot, &m_triangulated, &arena);
367 # ifdef PERF_DEBUG
368  double mesh_time = PIL_check_seconds_timer();
369  std::cout << "bmesh_boolean, imesh_from_bm done, time = " << mesh_time - start_time << "\n";
370 # endif
371  std::function<int(int)> shape_fn;
372  if (use_self && boolean_mode == BoolOpType::None) {
373  /* Unary knife operation. Want every face where test_fn doesn't return -1. */
374  BLI_assert(nshapes == 1);
375  shape_fn = [bm, test_fn, user_data](int f) {
376  BMFace *bmf = BM_face_at_index(bm, f);
377  if (test_fn(bmf, user_data) != -1) {
378  return 0;
379  }
380  return -1;
381  };
382  }
383  else {
384  shape_fn = [bm, test_fn, user_data](int f) {
385  BMFace *bmf = BM_face_at_index(bm, f);
386  int test_val = test_fn(bmf, user_data);
387  if (test_val >= 0) {
388  return test_val;
389  }
390  return -1;
391  };
392  }
393  IMesh m_out = boolean_mesh(
394  m_in, boolean_mode, nshapes, shape_fn, use_self, hole_tolerant, &m_triangulated, &arena);
395 # ifdef PERF_DEBUG
396  double boolean_time = PIL_check_seconds_timer();
397  std::cout << "boolean done, time = " << boolean_time - mesh_time << "\n";
398 # endif
399  bool any_change = apply_mesh_output_to_bmesh(bm, m_out, keep_hidden);
400 # ifdef PERF_DEBUG
401  double apply_mesh_time = PIL_check_seconds_timer();
402  std::cout << "applied boolean output to bmesh, time = " << apply_mesh_time - boolean_time
403  << "\n";
404 # endif
405  if (use_separate_all) {
406  /* We are supposed to separate all faces that are incident on intersection edges. */
407  BM_mesh_edgesplit(bm, false, true, false);
408  }
409  return any_change;
410 }
411 
412 #endif // WITH_GMP
413 
414 } // namespace blender::meshintersect
415 
416 extern "C" {
433 #ifdef WITH_GMP
434 bool BM_mesh_boolean(BMesh *bm,
435  struct BMLoop *(*looptris)[3],
436  const int looptris_tot,
437  int (*test_fn)(BMFace *f, void *user_data),
438  void *user_data,
439  const int nshapes,
440  const bool use_self,
441  const bool keep_hidden,
442  const bool hole_tolerant,
443  const int boolean_mode)
444 {
445  return blender::meshintersect::bmesh_boolean(
446  bm,
447  looptris,
448  looptris_tot,
449  test_fn,
450  user_data,
451  nshapes,
452  use_self,
453  false,
454  keep_hidden,
455  hole_tolerant,
456  static_cast<blender::meshintersect::BoolOpType>(boolean_mode));
457 }
458 
468  struct BMLoop *(*looptris)[3],
469  const int looptris_tot,
470  int (*test_fn)(BMFace *f, void *user_data),
471  void *user_data,
472  const int nshapes,
473  const bool use_self,
474  const bool use_separate_all,
475  const bool hole_tolerant,
476  const bool keep_hidden)
477 {
478  return blender::meshintersect::bmesh_boolean(bm,
479  looptris,
480  looptris_tot,
481  test_fn,
482  user_data,
483  nshapes,
484  use_self,
485  use_separate_all,
486  keep_hidden,
487  hole_tolerant,
488  blender::meshintersect::BoolOpType::None);
489 }
490 #else
492  struct BMLoop *(*looptris)[3],
493  const int UNUSED(looptris_tot),
494  int (*test_fn)(BMFace *, void *),
495  void *UNUSED(user_data),
496  const int UNUSED(nshapes),
497  const bool UNUSED(use_self),
498  const bool UNUSED(keep_hidden),
499  const bool UNUSED(hole_tolerant),
500  const int UNUSED(boolean_mode))
501 {
502  UNUSED_VARS(looptris, test_fn);
503  return false;
504 }
505 
515  struct BMLoop *(*looptris)[3],
516  const int UNUSED(looptris_tot),
517  int (*test_fn)(BMFace *, void *),
518  void *UNUSED(user_data),
519  const int UNUSED(nshapes),
520  const bool UNUSED(use_self),
521  const bool UNUSED(use_separate_all),
522  const bool UNUSED(hole_tolerant),
523  const bool UNUSED(keep_hidden))
524 {
525  UNUSED_VARS(looptris, test_fn);
526  return false;
527 }
528 #endif
529 
530 } /* extern "C" */
#define BLI_assert(a)
Definition: BLI_assert.h:58
MINLINE int max_ii(int a, int b)
unsigned int uint
Definition: BLI_sys_types.h:83
#define UNUSED_VARS(...)
#define UNUSED(x)
_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
Platform independent time functions.
bool BM_mesh_boolean(BMesh *UNUSED(bm), struct BMLoop *(*looptris)[3], const int UNUSED(looptris_tot), int(*test_fn)(BMFace *, void *), void *UNUSED(user_data), const int UNUSED(nshapes), const bool UNUSED(use_self), const bool UNUSED(keep_hidden), const bool UNUSED(hole_tolerant), const int UNUSED(boolean_mode))
bool BM_mesh_boolean_knife(BMesh *UNUSED(bm), struct BMLoop *(*looptris)[3], const int UNUSED(looptris_tot), int(*test_fn)(BMFace *, void *), void *UNUSED(user_data), const int UNUSED(nshapes), const bool UNUSED(use_self), const bool UNUSED(use_separate_all), const bool UNUSED(hole_tolerant), const bool UNUSED(keep_hidden))
@ BM_FACE
Definition: bmesh_class.h:386
@ BM_VERT
Definition: bmesh_class.h:383
@ BM_EDGE
Definition: bmesh_class.h:384
@ BM_ELEM_HIDDEN
Definition: bmesh_class.h:472
@ BM_ELEM_TAG
Definition: bmesh_class.h:484
void BM_elem_select_copy(BMesh *bm_dst, void *ele_dst_v, const void *ele_src_v)
BMVert * BM_vert_create(BMesh *bm, const float co[3], const BMVert *v_example, const eBMCreateFlag create_flag)
Main function for creating a new vertex.
Definition: bmesh_core.c:58
void BM_vert_kill(BMesh *bm, BMVert *v)
Definition: bmesh_core.c:1002
void BM_face_kill(BMesh *bm, BMFace *f)
Definition: bmesh_core.c:881
void BM_face_kill_loose(BMesh *bm, BMFace *f)
Definition: bmesh_core.c:929
BMFace * BM_face_create(BMesh *bm, BMVert **verts, BMEdge **edges, const int len, const BMFace *f_example, const eBMCreateFlag create_flag)
Definition: bmesh_core.c:428
BMEdge * BM_edge_create(BMesh *bm, BMVert *v1, BMVert *v2, const BMEdge *e_example, const eBMCreateFlag create_flag)
Main function for creating a new edge.
Definition: bmesh_core.c:147
@ BM_CREATE_NOP
Definition: bmesh_core.h:27
void BM_mesh_edgesplit(BMesh *bm, const bool use_verts, const bool tag_only, const bool copy_select)
#define BM_elem_index_get(ele)
Definition: bmesh_inline.h:124
#define BM_elem_flag_disable(ele, hflag)
Definition: bmesh_inline.h:29
#define BM_elem_flag_test(ele, hflag)
Definition: bmesh_inline.h:26
#define BM_elem_flag_enable(ele, hflag)
Definition: bmesh_inline.h:28
void BM_loop_interp_from_face(BMesh *bm, BMLoop *l_dst, const BMFace *f_src, const bool do_vertex, const bool do_multires)
Definition: bmesh_interp.c:737
int BM_iter_mesh_count(const char itype, BMesh *bm)
@ BM_FACES_OF_VERT
@ BM_VERTS_OF_MESH
@ BM_FACES_OF_MESH
@ BM_LOOPS_OF_FACE
#define BM_iter_new(iter, bm, itype, data)
ATTR_WARN_UNUSED_RESULT BMesh * bm
void BM_mesh_elem_table_ensure(BMesh *bm, const char htype)
Definition: bmesh_mesh.c:2276
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
Definition: bmesh_mesh.c:2152
BLI_INLINE BMFace * BM_face_at_index(BMesh *bm, const int index)
Definition: bmesh_mesh.h:110
BLI_INLINE BMVert * BM_vert_at_index(BMesh *bm, const int index)
Definition: bmesh_mesh.h:98
BMEdge * BM_edge_exists(BMVert *v_a, BMVert *v_b)
Definition: bmesh_query.c:1995
bool BM_vert_is_wire(const BMVert *v)
Definition: bmesh_query.c:919
BMFace * BM_face_exists(BMVert **varr, int len)
Definition: bmesh_query.c:2070
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert * v
void * user_data
static void copy(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node)
int len
Definition: bmesh_class.h:279
BMLoop * l_first
Definition: bmesh_class.h:273
struct BMVert * v
Definition: bmesh_class.h:165
struct BMEdge * e
Definition: bmesh_class.h:176
struct BMLoop * next
Definition: bmesh_class.h:245
float co[3]
Definition: bmesh_class.h:99
int totvert
Definition: bmesh_class.h:297
BMEdge ** etable
Definition: bmesh_class.h:322
int totedge
Definition: bmesh_class.h:297
int totface
Definition: bmesh_class.h:297
double PIL_check_seconds_timer(void)
Definition: time.c:80