Blender V4.3
bmo_hull.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#ifdef WITH_BULLET
12
13# include "MEM_guardedalloc.h"
14
15# include "BLI_listbase.h"
16# include "BLI_math_geom.h"
17# include "BLI_vector.hh"
18
19# include "RBI_hull_api.h"
20
21/* XXX: using 128 for totelem and `pchunk` of `mempool`, no idea what good
22 * values would be though */
23
24# include "bmesh.hh"
25
26# include "intern/bmesh_operators_private.hh" /* own include */
27
28using blender::Vector;
29
30/* Internal operator flags */
31enum {
32 HULL_FLAG_INPUT = (1 << 0),
33
34 HULL_FLAG_INTERIOR_ELE = (1 << 1),
35 HULL_FLAG_OUTPUT_GEOM = (1 << 2),
36
37 HULL_FLAG_DEL = (1 << 3),
38 HULL_FLAG_HOLE = (1 << 4),
39};
40
41/* Store hull triangles separate from BMesh faces until the end; this
42 * way we don't have to worry about cleaning up extraneous edges or
43 * incorrectly deleting existing geometry. */
44struct HullTriangle {
45 BMVert *v[3];
46 float no[3];
47 int skip;
48};
49
50/*************************** Hull Triangles ***************************/
51
52static void hull_add_triangle(
53 BMesh *bm, BLI_mempool *hull_triangles, BMVert *v1, BMVert *v2, BMVert *v3)
54{
55 HullTriangle *t;
56 int i;
57
58 t = static_cast<HullTriangle *>(BLI_mempool_calloc(hull_triangles));
59 t->v[0] = v1;
60 t->v[1] = v2;
61 t->v[2] = v3;
62
63 /* Mark triangles vertices as not interior */
64 for (i = 0; i < 3; i++) {
65 BMO_vert_flag_disable(bm, t->v[i], HULL_FLAG_INTERIOR_ELE);
66 }
67
68 normal_tri_v3(t->no, v1->co, v2->co, v3->co);
69}
70
71static BMFace *hull_find_example_face(BMesh *bm, BMEdge *e)
72{
73 BMIter iter;
74 BMFace *f;
75
76 BM_ITER_ELEM (f, &iter, e, BM_FACES_OF_EDGE) {
77 if (BMO_face_flag_test(bm, f, HULL_FLAG_INPUT) ||
78 BMO_face_flag_test(bm, f, HULL_FLAG_OUTPUT_GEOM) == false)
79 {
80 return f;
81 }
82 }
83
84 return nullptr;
85}
86
87static void hull_output_triangles(BMesh *bm, BLI_mempool *hull_triangles)
88{
90 BLI_mempool_iternew(hull_triangles, &iter);
91 HullTriangle *t;
92
93 while ((t = static_cast<HullTriangle *>(BLI_mempool_iterstep(&iter)))) {
94 int i;
95
96 if (!t->skip) {
97 BMEdge *edges[3] = {
98 BM_edge_create(bm, t->v[0], t->v[1], nullptr, BM_CREATE_NO_DOUBLE),
99 BM_edge_create(bm, t->v[1], t->v[2], nullptr, BM_CREATE_NO_DOUBLE),
100 BM_edge_create(bm, t->v[2], t->v[0], nullptr, BM_CREATE_NO_DOUBLE),
101 };
102 BMFace *f, *example = nullptr;
103
104 f = BM_face_exists(t->v, 3);
105 if (f != nullptr) {
106 /* If the operator is run with "use_existing_faces"
107 * disabled, but an output face in the hull is the
108 * same as a face in the existing mesh, it should not
109 * be marked as unused or interior. */
110 BMO_face_flag_enable(bm, f, HULL_FLAG_OUTPUT_GEOM);
111 BMO_face_flag_disable(bm, f, HULL_FLAG_HOLE);
112 BMO_face_flag_disable(bm, f, HULL_FLAG_INTERIOR_ELE);
113 }
114 else {
115 /* Look for an adjacent face that existed before the hull */
116 for (i = 0; i < 3; i++) {
117 if (!example) {
118 example = hull_find_example_face(bm, edges[i]);
119 }
120 }
121
122 /* Create new hull face */
123 f = BM_face_create_verts(bm, t->v, 3, example, BM_CREATE_NO_DOUBLE, true);
124 BM_face_copy_shared(bm, f, nullptr, nullptr);
125 }
126 /* Mark face for 'geom.out' slot and select */
127 BMO_face_flag_enable(bm, f, HULL_FLAG_OUTPUT_GEOM);
128 BM_face_select_set(bm, f, true);
129
130 /* Mark edges for 'geom.out' slot */
131 for (i = 0; i < 3; i++) {
132 BMO_edge_flag_enable(bm, edges[i], HULL_FLAG_OUTPUT_GEOM);
133 }
134 }
135 else {
136 /* Mark input edges for 'geom.out' slot */
137 for (i = 0; i < 3; i++) {
138 const int next = (i == 2 ? 0 : i + 1);
139 BMEdge *e = BM_edge_exists(t->v[i], t->v[next]);
140 if (e && BMO_edge_flag_test(bm, e, HULL_FLAG_INPUT) &&
141 !BMO_edge_flag_test(bm, e, HULL_FLAG_HOLE))
142 {
143 BMO_edge_flag_enable(bm, e, HULL_FLAG_OUTPUT_GEOM);
144 }
145 }
146 }
147
148 /* Mark verts for 'geom.out' slot */
149 for (i = 0; i < 3; i++) {
150 BMO_vert_flag_enable(bm, t->v[i], HULL_FLAG_OUTPUT_GEOM);
151 }
152 }
153}
154
155/***************************** Final Edges ****************************/
156
157struct HullFinalEdges {
158 GHash *edges;
159 BLI_mempool *base_pool, *link_pool;
160};
161
162static LinkData *final_edges_find_link(ListBase *adj, BMVert *v)
163{
164 LISTBASE_FOREACH (LinkData *, link, adj) {
165 if (link->data == v) {
166 return link;
167 }
168 }
169
170 return nullptr;
171}
172
173static int hull_final_edges_lookup(HullFinalEdges *final_edges, BMVert *v1, BMVert *v2)
174{
175 ListBase *adj;
176
177 /* Use lower vertex pointer for hash key */
178 if (v1 > v2) {
179 std::swap(v1, v2);
180 }
181
182 adj = static_cast<ListBase *>(BLI_ghash_lookup(final_edges->edges, v1));
183 if (!adj) {
184 return false;
185 }
186
187 return !!final_edges_find_link(adj, v2);
188}
189
190/* Used for checking whether a pre-existing edge lies on the hull */
191static HullFinalEdges *hull_final_edges(BLI_mempool *hull_triangles)
192{
193 HullFinalEdges *final_edges;
194
195 final_edges = static_cast<HullFinalEdges *>(
196 MEM_callocN(sizeof(HullFinalEdges), "HullFinalEdges"));
197 final_edges->edges = BLI_ghash_ptr_new("final edges ghash");
198 final_edges->base_pool = BLI_mempool_create(sizeof(ListBase), 0, 128, BLI_MEMPOOL_NOP);
199 final_edges->link_pool = BLI_mempool_create(sizeof(LinkData), 0, 128, BLI_MEMPOOL_NOP);
200
201 BLI_mempool_iter iter;
202 BLI_mempool_iternew(hull_triangles, &iter);
203 HullTriangle *t;
204
205 while ((t = static_cast<HullTriangle *>(BLI_mempool_iterstep(&iter)))) {
206 LinkData *link;
207 int i;
208
209 for (i = 0; i < 3; i++) {
210 BMVert *v1 = t->v[i];
211 BMVert *v2 = t->v[(i + 1) % 3];
212 ListBase *adj;
213
214 /* Use lower vertex pointer for hash key */
215 if (v1 > v2) {
216 std::swap(v1, v2);
217 }
218
219 adj = static_cast<ListBase *>(BLI_ghash_lookup(final_edges->edges, v1));
220 if (!adj) {
221 adj = static_cast<ListBase *>(BLI_mempool_calloc(final_edges->base_pool));
222 BLI_ghash_insert(final_edges->edges, v1, adj);
223 }
224
225 if (!final_edges_find_link(adj, v2)) {
226 link = static_cast<LinkData *>(BLI_mempool_calloc(final_edges->link_pool));
227 link->data = v2;
228 BLI_addtail(adj, link);
229 }
230 }
231 }
232
233 return final_edges;
234}
235
236static void hull_final_edges_free(HullFinalEdges *final_edges)
237{
238 BLI_ghash_free(final_edges->edges, nullptr, nullptr);
239 BLI_mempool_destroy(final_edges->base_pool);
240 BLI_mempool_destroy(final_edges->link_pool);
241 MEM_freeN(final_edges);
242}
243
244/**************************** Final Output ****************************/
245
246static void hull_remove_overlapping(BMesh *bm,
247 BLI_mempool *hull_triangles,
248 HullFinalEdges *final_edges)
249{
250 BLI_mempool_iter iter;
251 BLI_mempool_iternew(hull_triangles, &iter);
252 HullTriangle *t;
253
254 while ((t = static_cast<HullTriangle *>(BLI_mempool_iterstep(&iter)))) {
255 BMIter bm_iter1, bm_iter2;
256 BMFace *f;
257 bool f_on_hull;
258
259 BM_ITER_ELEM (f, &bm_iter1, t->v[0], BM_FACES_OF_VERT) {
260 BMEdge *e;
261
262 /* Check that all the face's edges are on the hull,
263 * otherwise can't reuse it */
264 f_on_hull = true;
265 BM_ITER_ELEM (e, &bm_iter2, f, BM_EDGES_OF_FACE) {
266 if (!hull_final_edges_lookup(final_edges, e->v1, e->v2)) {
267 f_on_hull = false;
268 break;
269 }
270 }
271
272 /* NOTE: can't change ghash while iterating, so mark
273 * with 'skip' flag rather than deleting triangles */
274 if (BM_vert_in_face(t->v[1], f) && BM_vert_in_face(t->v[2], f) && f_on_hull) {
275 t->skip = true;
276 BMO_face_flag_disable(bm, f, HULL_FLAG_INTERIOR_ELE);
277 BMO_face_flag_enable(bm, f, HULL_FLAG_HOLE);
278 }
279 }
280 }
281}
282
283static void hull_mark_interior_elements(BMesh *bm, BMOperator *op, HullFinalEdges *final_edges)
284{
285 BMEdge *e;
286 BMFace *f;
287 BMOIter oiter;
288
289 /* Check for interior edges too */
290 BMO_ITER (e, &oiter, op->slots_in, "input", BM_EDGE) {
291 if (!hull_final_edges_lookup(final_edges, e->v1, e->v2)) {
292 BMO_edge_flag_enable(bm, e, HULL_FLAG_INTERIOR_ELE);
293 }
294 }
295
296 /* Mark all input faces as interior, some may be unmarked in
297 * hull_remove_overlapping() */
298 BMO_ITER (f, &oiter, op->slots_in, "input", BM_FACE) {
299 BMO_face_flag_enable(bm, f, HULL_FLAG_INTERIOR_ELE);
300 }
301}
302
303static void hull_tag_unused(BMesh *bm, BMOperator *op)
304{
305 BMIter iter;
306 BMOIter oiter;
307 BMVert *v;
308 BMEdge *e;
309 BMFace *f;
310
311 /* Mark vertices, edges, and faces that are already marked
312 * interior (i.e. were already part of the input, but not part of
313 * the hull), but that aren't also used by elements outside the
314 * input set */
315 BMO_ITER (v, &oiter, op->slots_in, "input", BM_VERT) {
316 if (BMO_vert_flag_test(bm, v, HULL_FLAG_INTERIOR_ELE)) {
317 bool del = true;
318
319 BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) {
320 if (!BMO_edge_flag_test(bm, e, HULL_FLAG_INPUT)) {
321 del = false;
322 break;
323 }
324 }
325
326 BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) {
327 if (!BMO_face_flag_test(bm, f, HULL_FLAG_INPUT)) {
328 del = false;
329 break;
330 }
331 }
332
333 if (del) {
334 BMO_vert_flag_enable(bm, v, HULL_FLAG_DEL);
335 }
336 }
337 }
338
339 BMO_ITER (e, &oiter, op->slots_in, "input", BM_EDGE) {
340 if (BMO_edge_flag_test(bm, e, HULL_FLAG_INTERIOR_ELE)) {
341 bool del = true;
342
343 BM_ITER_ELEM (f, &iter, e, BM_FACES_OF_EDGE) {
344 if (!BMO_face_flag_test(bm, f, HULL_FLAG_INPUT)) {
345 del = false;
346 break;
347 }
348 }
349
350 if (del) {
351 BMO_edge_flag_enable(bm, e, HULL_FLAG_DEL);
352 }
353 }
354 }
355
356 BMO_ITER (f, &oiter, op->slots_in, "input", BM_FACE) {
357 if (BMO_face_flag_test(bm, f, HULL_FLAG_INTERIOR_ELE)) {
358 BMO_face_flag_enable(bm, f, HULL_FLAG_DEL);
359 }
360 }
361}
362
363static void hull_tag_holes(BMesh *bm, BMOperator *op)
364{
365 BMIter iter;
366 BMOIter oiter;
367 BMFace *f;
368 BMEdge *e;
369
370 /* Unmark any hole faces if they are isolated or part of a
371 * border */
372 BMO_ITER (f, &oiter, op->slots_in, "input", BM_FACE) {
373 if (BMO_face_flag_test(bm, f, HULL_FLAG_HOLE)) {
374 BM_ITER_ELEM (e, &iter, f, BM_EDGES_OF_FACE) {
375 if (BM_edge_is_boundary(e)) {
376 BMO_face_flag_disable(bm, f, HULL_FLAG_HOLE);
377 break;
378 }
379 }
380 }
381 }
382
383 /* Mark edges too if all adjacent faces are holes and the edge is
384 * not already isolated */
385 BMO_ITER (e, &oiter, op->slots_in, "input", BM_EDGE) {
386 bool hole = true;
387 bool any_faces = false;
388
389 BM_ITER_ELEM (f, &iter, e, BM_FACES_OF_EDGE) {
390 any_faces = true;
391 if (!BMO_face_flag_test(bm, f, HULL_FLAG_HOLE)) {
392 hole = false;
393 break;
394 }
395 }
396
397 if (hole && any_faces) {
398 BMO_edge_flag_enable(bm, e, HULL_FLAG_HOLE);
399 }
400 }
401}
402
403static int hull_input_vert_count(BMOperator *op)
404{
405 BMOIter oiter;
406 BMVert *v;
407 int count = 0;
408
409 BMO_ITER (v, &oiter, op->slots_in, "input", BM_VERT) {
410 count++;
411 }
412
413 return count;
414}
415
416static BMVert **hull_input_verts_copy(BMOperator *op, const int num_input_verts)
417{
418 BMOIter oiter;
419 BMVert *v;
420 BMVert **input_verts = static_cast<BMVert **>(
421 MEM_callocN(sizeof(*input_verts) * num_input_verts, AT));
422 int i = 0;
423
424 BMO_ITER (v, &oiter, op->slots_in, "input", BM_VERT) {
425 input_verts[i++] = v;
426 }
427
428 return input_verts;
429}
430
431static float (*hull_verts_for_bullet(BMVert **input_verts, const int num_input_verts))[3]
432{
433 float(*coords)[3] = static_cast<float(*)[3]>(
434 MEM_callocN(sizeof(*coords) * num_input_verts, __func__));
435 int i;
436
437 for (i = 0; i < num_input_verts; i++) {
438 copy_v3_v3(coords[i], input_verts[i]->co);
439 }
440
441 return coords;
442}
443
444static BMVert **hull_verts_from_bullet(plConvexHull hull,
445 BMVert **input_verts,
446 const int num_input_verts)
447{
448 const int num_verts = plConvexHullNumVertices(hull);
449 BMVert **hull_verts = static_cast<BMVert **>(MEM_mallocN(sizeof(*hull_verts) * num_verts, AT));
450 int i;
451
452 for (i = 0; i < num_verts; i++) {
453 float co[3];
454 int original_index;
455 plConvexHullGetVertex(hull, i, co, &original_index);
456
457 if (original_index >= 0 && original_index < num_input_verts) {
458 hull_verts[i] = input_verts[original_index];
459 }
460 else {
461 BLI_assert_msg(0, "Unexpected new vertex in hull output");
462 }
463 }
464
465 return hull_verts;
466}
467
468static void hull_from_bullet(BMesh *bm, BMOperator *op, BLI_mempool *hull_triangles)
469{
470 BMVert **input_verts;
471 float(*coords)[3];
472 BMVert **hull_verts;
473
474 plConvexHull hull;
475 int i, count = 0;
476
477 const int num_input_verts = hull_input_vert_count(op);
478
479 input_verts = hull_input_verts_copy(op, num_input_verts);
480 coords = hull_verts_for_bullet(input_verts, num_input_verts);
481
482 hull = plConvexHullCompute(coords, num_input_verts);
483 hull_verts = hull_verts_from_bullet(hull, input_verts, num_input_verts);
484
486 Vector<int> fvi;
487 for (i = 0; i < count; i++) {
488 const int len = plConvexHullGetFaceSize(hull, i);
489
490 if (len > 2) {
491 BMVert *fv[3];
492 int j;
493
494 /* Get face vertex indices */
495 fvi.reinitialize(len);
496 plConvexHullGetFaceVertices(hull, i, fvi.data());
497
498 /* NOTE: here we throw away any NGons from Bullet and turn
499 * them into triangle fans. Would be nice to use these
500 * directly, but will have to wait until HullTriangle goes
501 * away (TODO) */
502 fv[0] = hull_verts[fvi[0]];
503 for (j = 2; j < len; j++) {
504 fv[1] = hull_verts[fvi[j - 1]];
505 fv[2] = hull_verts[fvi[j]];
506
507 hull_add_triangle(bm, hull_triangles, fv[0], fv[1], fv[2]);
508 }
509 }
510 }
511
512 plConvexHullDelete(hull);
513
514 MEM_freeN(hull_verts);
515 MEM_freeN(coords);
516 MEM_freeN(input_verts);
517}
518
519/* Check that there are at least three vertices in the input */
520static bool hull_num_input_verts_is_ok(BMOperator *op)
521{
522 BMOIter oiter;
523 BMVert *v;
524 int partial_num_verts = 0;
525
526 BMO_ITER (v, &oiter, op->slots_in, "input", BM_VERT) {
527 partial_num_verts++;
528 if (partial_num_verts >= 3) {
529 break;
530 }
531 }
532
533 return (partial_num_verts >= 3);
534}
535
537{
538 HullFinalEdges *final_edges;
539 BLI_mempool *hull_triangles;
540 BMElemF *ele;
541 BMOIter oiter;
542
543 /* Verify that at least three verts in the input */
544 if (!hull_num_input_verts_is_ok(op)) {
545 BMO_error_raise(bm, op, BMO_ERROR_CANCEL, "Requires at least three vertices");
546 return;
547 }
548
549 /* Tag input elements */
550 BMO_ITER (ele, &oiter, op->slots_in, "input", BM_ALL) {
551
552 /* Mark all vertices as interior to begin with */
553 if (ele->head.htype == BM_VERT) {
554 BMO_vert_flag_enable(bm, (BMVert *)ele, HULL_FLAG_INPUT | HULL_FLAG_INTERIOR_ELE);
555 }
556 else if (ele->head.htype == BM_EDGE) {
557 BMO_edge_flag_enable(bm, (BMEdge *)ele, HULL_FLAG_INPUT);
558 }
559 else {
560 BMO_face_flag_enable(bm, (BMFace *)ele, HULL_FLAG_INPUT);
561 }
562 }
563
564 hull_triangles = BLI_mempool_create(sizeof(HullTriangle), 0, 128, BLI_MEMPOOL_ALLOW_ITER);
565
566 hull_from_bullet(bm, op, hull_triangles);
567
568 final_edges = hull_final_edges(hull_triangles);
569
570 hull_mark_interior_elements(bm, op, final_edges);
571
572 /* Remove hull triangles covered by an existing face */
573 if (BMO_slot_bool_get(op->slots_in, "use_existing_faces")) {
574 hull_remove_overlapping(bm, hull_triangles, final_edges);
575
576 hull_tag_holes(bm, op);
577 }
578
579 /* Done with edges */
580 hull_final_edges_free(final_edges);
581
582 /* Convert hull triangles to BMesh faces */
583 hull_output_triangles(bm, hull_triangles);
584 BLI_mempool_destroy(hull_triangles);
585
586 hull_tag_unused(bm, op);
587
588 /* Output slot of input elements that ended up inside the hull
589 * rather than part of it */
591 bm, op, op->slots_out, "geom_interior.out", BM_ALL_NOLOOP, HULL_FLAG_INTERIOR_ELE);
592
593 /* Output slot of input elements that ended up inside the hull and
594 * are unused by other geometry. */
596 bm, op, op->slots_out, "geom_unused.out", BM_ALL_NOLOOP, HULL_FLAG_DEL);
597
598 /* Output slot of faces and edges that were in the input and on
599 * the hull (useful for cases like bridging where you want to
600 * delete some input geometry) */
602 bm, op, op->slots_out, "geom_holes.out", BM_ALL_NOLOOP, HULL_FLAG_HOLE);
603
604 /* Output slot of all hull vertices, faces, and edges */
606 bm, op, op->slots_out, "geom.out", BM_ALL_NOLOOP, HULL_FLAG_OUTPUT_GEOM);
607}
608
609#endif /* WITH_BULLET */
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
GHash * BLI_ghash_ptr_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
void * BLI_ghash_lookup(const GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.c:731
void BLI_ghash_insert(GHash *gh, void *key, void *val)
Definition BLI_ghash.c:707
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition BLI_ghash.c:860
#define LISTBASE_FOREACH(type, var, list)
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
float normal_tri_v3(float n[3], const float v1[3], const float v2[3], const float v3[3])
Definition math_geom.cc:39
MINLINE void copy_v3_v3(float r[3], const float a[3])
void BLI_mempool_iternew(BLI_mempool *pool, BLI_mempool_iter *iter) ATTR_NONNULL()
void * BLI_mempool_iterstep(BLI_mempool_iter *iter) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_mempool * BLI_mempool_create(unsigned int esize, unsigned int elem_num, unsigned int pchunk, unsigned int flag) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL
@ BLI_MEMPOOL_ALLOW_ITER
Definition BLI_mempool.h:95
@ BLI_MEMPOOL_NOP
Definition BLI_mempool.h:86
void * BLI_mempool_calloc(BLI_mempool *pool) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1)
void BLI_mempool_destroy(BLI_mempool *pool) ATTR_NONNULL(1)
Read Guarded memory(de)allocation.
#define BM_ALL_NOLOOP
#define BM_ALL
void BM_face_copy_shared(BMesh *bm, BMFace *f, BMLoopFilterFunc filter_fn, void *user_data)
copies face loop data from shared adjacent faces.
BMFace * BM_face_create_verts(BMesh *bm, BMVert **vert_arr, const int len, const BMFace *f_example, const eBMCreateFlag create_flag, const bool create_edges)
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.
@ BM_CREATE_NO_DOUBLE
Definition bmesh_core.hh:26
@ BMO_ERROR_CANCEL
void BMO_error_raise(BMesh *bm, BMOperator *owner, eBMOpErrorLevel level, const char *msg) ATTR_NONNULL(1
#define BM_ITER_ELEM(ele, iter, data, itype)
@ BM_FACES_OF_EDGE
@ BM_FACES_OF_VERT
@ BM_EDGES_OF_VERT
@ BM_EDGES_OF_FACE
ATTR_WARN_UNUSED_RESULT BMesh * bm
void BM_face_select_set(BMesh *bm, BMFace *f, const bool select)
Select Face.
#define BM_FACE
#define BM_EDGE
#define BM_VERT
#define BMO_vert_flag_disable(bm, e, oflag)
#define BMO_edge_flag_test(bm, e, oflag)
#define BMO_edge_flag_enable(bm, e, oflag)
#define BMO_vert_flag_enable(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, 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_vert_flag_test(bm, e, oflag)
#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 bmo_convex_hull_exec(BMesh *bm, BMOperator *op)
BMFace * BM_face_exists(BMVert *const *varr, int len)
BMEdge * BM_edge_exists(BMVert *v_a, BMVert *v_b)
bool BM_vert_in_face(BMVert *v, BMFace *f)
BLI_INLINE bool BM_edge_is_boundary(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 BMVert * v
int len
draw_view in_light_buf[] float
int count
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
static ulong * next
int plConvexHullGetFaceSize(plConvexHull hull, int n)
void plConvexHullGetVertex(plConvexHull hull, int n, float coords[3], int *original_index)
int plConvexHullNumVertices(plConvexHull hull)
plConvexHull plConvexHullCompute(float(*coords)[3], int count)
void plConvexHullDelete(plConvexHull hull)
int plConvexHullNumFaces(plConvexHull hull)
void plConvexHullGetFaceVertices(plConvexHull hull, int n, int *vertices)
BMHeader head
struct BMOpSlot slots_out[BMO_OP_MAX_SLOTS]
struct BMOpSlot slots_in[BMO_OP_MAX_SLOTS]
float co[3]
void * data