Blender V5.0
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
10
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 = MEM_callocN<HullFinalEdges>("HullFinalEdges");
196 final_edges->edges = BLI_ghash_ptr_new("final edges ghash");
197 final_edges->base_pool = BLI_mempool_create(sizeof(ListBase), 0, 128, BLI_MEMPOOL_NOP);
198 final_edges->link_pool = BLI_mempool_create(sizeof(LinkData), 0, 128, BLI_MEMPOOL_NOP);
199
200 BLI_mempool_iter iter;
201 BLI_mempool_iternew(hull_triangles, &iter);
202 HullTriangle *t;
203
204 while ((t = static_cast<HullTriangle *>(BLI_mempool_iterstep(&iter)))) {
205 LinkData *link;
206 int i;
207
208 for (i = 0; i < 3; i++) {
209 BMVert *v1 = t->v[i];
210 BMVert *v2 = t->v[(i + 1) % 3];
211 ListBase *adj;
212
213 /* Use lower vertex pointer for hash key */
214 if (v1 > v2) {
215 std::swap(v1, v2);
216 }
217
218 adj = static_cast<ListBase *>(BLI_ghash_lookup(final_edges->edges, v1));
219 if (!adj) {
220 adj = static_cast<ListBase *>(BLI_mempool_calloc(final_edges->base_pool));
221 BLI_ghash_insert(final_edges->edges, v1, adj);
222 }
223
224 if (!final_edges_find_link(adj, v2)) {
225 link = static_cast<LinkData *>(BLI_mempool_calloc(final_edges->link_pool));
226 link->data = v2;
227 BLI_addtail(adj, link);
228 }
229 }
230 }
231
232 return final_edges;
233}
234
235static void hull_final_edges_free(HullFinalEdges *final_edges)
236{
237 BLI_ghash_free(final_edges->edges, nullptr, nullptr);
238 BLI_mempool_destroy(final_edges->base_pool);
239 BLI_mempool_destroy(final_edges->link_pool);
240 MEM_freeN(final_edges);
241}
242
243/**************************** Final Output ****************************/
244
245static void hull_remove_overlapping(BMesh *bm,
246 BLI_mempool *hull_triangles,
247 HullFinalEdges *final_edges)
248{
249 BLI_mempool_iter iter;
250 BLI_mempool_iternew(hull_triangles, &iter);
251 HullTriangle *t;
252
253 while ((t = static_cast<HullTriangle *>(BLI_mempool_iterstep(&iter)))) {
254 BMIter bm_iter1, bm_iter2;
255 BMFace *f;
256 bool f_on_hull;
257
258 BM_ITER_ELEM (f, &bm_iter1, t->v[0], BM_FACES_OF_VERT) {
259 BMEdge *e;
260
261 /* Check that all the face's edges are on the hull,
262 * otherwise can't reuse it */
263 f_on_hull = true;
264 BM_ITER_ELEM (e, &bm_iter2, f, BM_EDGES_OF_FACE) {
265 if (!hull_final_edges_lookup(final_edges, e->v1, e->v2)) {
266 f_on_hull = false;
267 break;
268 }
269 }
270
271 /* NOTE: can't change ghash while iterating, so mark
272 * with 'skip' flag rather than deleting triangles */
273 if (BM_vert_in_face(t->v[1], f) && BM_vert_in_face(t->v[2], f) && f_on_hull) {
274 t->skip = true;
275 BMO_face_flag_disable(bm, f, HULL_FLAG_INTERIOR_ELE);
276 BMO_face_flag_enable(bm, f, HULL_FLAG_HOLE);
277 }
278 }
279 }
280}
281
282static void hull_mark_interior_elements(BMesh *bm, BMOperator *op, HullFinalEdges *final_edges)
283{
284 BMEdge *e;
285 BMFace *f;
286 BMOIter oiter;
287
288 /* Check for interior edges too */
289 BMO_ITER (e, &oiter, op->slots_in, "input", BM_EDGE) {
290 if (!hull_final_edges_lookup(final_edges, e->v1, e->v2)) {
291 BMO_edge_flag_enable(bm, e, HULL_FLAG_INTERIOR_ELE);
292 }
293 }
294
295 /* Mark all input faces as interior, some may be unmarked in
296 * hull_remove_overlapping() */
297 BMO_ITER (f, &oiter, op->slots_in, "input", BM_FACE) {
298 BMO_face_flag_enable(bm, f, HULL_FLAG_INTERIOR_ELE);
299 }
300}
301
302static void hull_tag_unused(BMesh *bm, BMOperator *op)
303{
304 BMIter iter;
305 BMOIter oiter;
306 BMVert *v;
307 BMEdge *e;
308 BMFace *f;
309
310 /* Mark vertices, edges, and faces that are already marked
311 * interior (i.e. were already part of the input, but not part of
312 * the hull), but that aren't also used by elements outside the
313 * input set */
314 BMO_ITER (v, &oiter, op->slots_in, "input", BM_VERT) {
315 if (BMO_vert_flag_test(bm, v, HULL_FLAG_INTERIOR_ELE)) {
316 bool del = true;
317
318 BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) {
319 if (!BMO_edge_flag_test(bm, e, HULL_FLAG_INPUT)) {
320 del = false;
321 break;
322 }
323 }
324
325 BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) {
326 if (!BMO_face_flag_test(bm, f, HULL_FLAG_INPUT)) {
327 del = false;
328 break;
329 }
330 }
331
332 if (del) {
333 BMO_vert_flag_enable(bm, v, HULL_FLAG_DEL);
334 }
335 }
336 }
337
338 BMO_ITER (e, &oiter, op->slots_in, "input", BM_EDGE) {
339 if (BMO_edge_flag_test(bm, e, HULL_FLAG_INTERIOR_ELE)) {
340 bool del = true;
341
342 BM_ITER_ELEM (f, &iter, e, BM_FACES_OF_EDGE) {
343 if (!BMO_face_flag_test(bm, f, HULL_FLAG_INPUT)) {
344 del = false;
345 break;
346 }
347 }
348
349 if (del) {
350 BMO_edge_flag_enable(bm, e, HULL_FLAG_DEL);
351 }
352 }
353 }
354
355 BMO_ITER (f, &oiter, op->slots_in, "input", BM_FACE) {
356 if (BMO_face_flag_test(bm, f, HULL_FLAG_INTERIOR_ELE)) {
357 BMO_face_flag_enable(bm, f, HULL_FLAG_DEL);
358 }
359 }
360}
361
362static void hull_tag_holes(BMesh *bm, BMOperator *op)
363{
364 BMIter iter;
365 BMOIter oiter;
366 BMFace *f;
367 BMEdge *e;
368
369 /* Unmark any hole faces if they are isolated or part of a
370 * border */
371 BMO_ITER (f, &oiter, op->slots_in, "input", BM_FACE) {
372 if (BMO_face_flag_test(bm, f, HULL_FLAG_HOLE)) {
373 BM_ITER_ELEM (e, &iter, f, BM_EDGES_OF_FACE) {
374 if (BM_edge_is_boundary(e)) {
375 BMO_face_flag_disable(bm, f, HULL_FLAG_HOLE);
376 break;
377 }
378 }
379 }
380 }
381
382 /* Mark edges too if all adjacent faces are holes and the edge is
383 * not already isolated */
384 BMO_ITER (e, &oiter, op->slots_in, "input", BM_EDGE) {
385 bool hole = true;
386 bool any_faces = false;
387
388 BM_ITER_ELEM (f, &iter, e, BM_FACES_OF_EDGE) {
389 any_faces = true;
390 if (!BMO_face_flag_test(bm, f, HULL_FLAG_HOLE)) {
391 hole = false;
392 break;
393 }
394 }
395
396 if (hole && any_faces) {
397 BMO_edge_flag_enable(bm, e, HULL_FLAG_HOLE);
398 }
399 }
400}
401
402static int hull_input_vert_count(BMOperator *op)
403{
404 BMOIter oiter;
405 BMVert *v;
406 int count = 0;
407
408 BMO_ITER (v, &oiter, op->slots_in, "input", BM_VERT) {
409 count++;
410 }
411
412 return count;
413}
414
415static BMVert **hull_input_verts_copy(BMOperator *op, const int num_input_verts)
416{
417 BMOIter oiter;
418 BMVert *v;
419 BMVert **input_verts = static_cast<BMVert **>(
420 MEM_callocN(sizeof(*input_verts) * num_input_verts, AT));
421 int i = 0;
422
423 BMO_ITER (v, &oiter, op->slots_in, "input", BM_VERT) {
424 input_verts[i++] = v;
425 }
426
427 return input_verts;
428}
429
430static float (*hull_verts_for_bullet(BMVert **input_verts, const int num_input_verts))[3]
431{
432 float (*coords)[3] = static_cast<float (*)[3]>(
433 MEM_callocN(sizeof(*coords) * num_input_verts, __func__));
434 int i;
435
436 for (i = 0; i < num_input_verts; i++) {
437 copy_v3_v3(coords[i], input_verts[i]->co);
438 }
439
440 return coords;
441}
442
443static BMVert **hull_verts_from_bullet(plConvexHull hull,
444 BMVert **input_verts,
445 const int num_input_verts)
446{
447 const int num_verts = plConvexHullNumVertices(hull);
448 BMVert **hull_verts = MEM_malloc_arrayN<BMVert *>(num_verts, AT);
449 int i;
450
451 for (i = 0; i < num_verts; i++) {
452 float co[3];
453 int original_index;
454 plConvexHullGetVertex(hull, i, co, &original_index);
455
456 if (original_index >= 0 && original_index < num_input_verts) {
457 hull_verts[i] = input_verts[original_index];
458 }
459 else {
460 BLI_assert_msg(0, "Unexpected new vertex in hull output");
461 }
462 }
463
464 return hull_verts;
465}
466
467static void hull_from_bullet(BMesh *bm, BMOperator *op, BLI_mempool *hull_triangles)
468{
469 BMVert **input_verts;
470 float (*coords)[3];
471 BMVert **hull_verts;
472
473 plConvexHull hull;
474 int i, count = 0;
475
476 const int num_input_verts = hull_input_vert_count(op);
477
478 input_verts = hull_input_verts_copy(op, num_input_verts);
479 coords = hull_verts_for_bullet(input_verts, num_input_verts);
480
481 hull = plConvexHullCompute(coords, num_input_verts);
482 hull_verts = hull_verts_from_bullet(hull, input_verts, num_input_verts);
483
485 Vector<int> fvi;
486 for (i = 0; i < count; i++) {
487 const int len = plConvexHullGetFaceSize(hull, i);
488
489 if (len > 2) {
490 BMVert *fv[3];
491 int j;
492
493 /* Get face vertex indices */
494 fvi.reinitialize(len);
495 plConvexHullGetFaceVertices(hull, i, fvi.data());
496
497 /* NOTE: here we throw away any NGons from Bullet and turn
498 * them into triangle fans. Would be nice to use these
499 * directly, but will have to wait until HullTriangle goes
500 * away (TODO) */
501 fv[0] = hull_verts[fvi[0]];
502 for (j = 2; j < len; j++) {
503 fv[1] = hull_verts[fvi[j - 1]];
504 fv[2] = hull_verts[fvi[j]];
505
506 hull_add_triangle(bm, hull_triangles, fv[0], fv[1], fv[2]);
507 }
508 }
509 }
510
511 plConvexHullDelete(hull);
512
513 MEM_freeN(hull_verts);
514 MEM_freeN(coords);
515 MEM_freeN(input_verts);
516}
517
518/* Check that there are at least three vertices in the input */
519static bool hull_num_input_verts_is_ok(BMOperator *op)
520{
521 BMOIter oiter;
522 BMVert *v;
523 int partial_num_verts = 0;
524
525 BMO_ITER (v, &oiter, op->slots_in, "input", BM_VERT) {
526 partial_num_verts++;
527 if (partial_num_verts >= 3) {
528 break;
529 }
530 }
531
532 return (partial_num_verts >= 3);
533}
534
536{
537 HullFinalEdges *final_edges;
538 BLI_mempool *hull_triangles;
539 BMElemF *ele;
540 BMOIter oiter;
541
542 /* Verify that at least three verts in the input */
543 if (!hull_num_input_verts_is_ok(op)) {
544 BMO_error_raise(bm, op, BMO_ERROR_CANCEL, "Requires at least three vertices");
545 return;
546 }
547
548 /* Tag input elements */
549 BMO_ITER (ele, &oiter, op->slots_in, "input", BM_ALL) {
550
551 /* Mark all vertices as interior to begin with */
552 if (ele->head.htype == BM_VERT) {
553 BMO_vert_flag_enable(bm, (BMVert *)ele, HULL_FLAG_INPUT | HULL_FLAG_INTERIOR_ELE);
554 }
555 else if (ele->head.htype == BM_EDGE) {
556 BMO_edge_flag_enable(bm, (BMEdge *)ele, HULL_FLAG_INPUT);
557 }
558 else {
559 BMO_face_flag_enable(bm, (BMFace *)ele, HULL_FLAG_INPUT);
560 }
561 }
562
563 hull_triangles = BLI_mempool_create(sizeof(HullTriangle), 0, 128, BLI_MEMPOOL_ALLOW_ITER);
564
565 hull_from_bullet(bm, op, hull_triangles);
566
567 final_edges = hull_final_edges(hull_triangles);
568
569 hull_mark_interior_elements(bm, op, final_edges);
570
571 /* Remove hull triangles covered by an existing face */
572 if (BMO_slot_bool_get(op->slots_in, "use_existing_faces")) {
573 hull_remove_overlapping(bm, hull_triangles, final_edges);
574
575 hull_tag_holes(bm, op);
576 }
577
578 /* Done with edges */
579 hull_final_edges_free(final_edges);
580
581 /* Convert hull triangles to BMesh faces */
582 hull_output_triangles(bm, hull_triangles);
583 BLI_mempool_destroy(hull_triangles);
584
585 hull_tag_unused(bm, op);
586
587 /* Output slot of input elements that ended up inside the hull
588 * rather than part of it */
590 bm, op, op->slots_out, "geom_interior.out", BM_ALL_NOLOOP, HULL_FLAG_INTERIOR_ELE);
591
592 /* Output slot of input elements that ended up inside the hull and
593 * are unused by other geometry. */
595 bm, op, op->slots_out, "geom_unused.out", BM_ALL_NOLOOP, HULL_FLAG_DEL);
596
597 /* Output slot of faces and edges that were in the input and on
598 * the hull (useful for cases like bridging where you want to
599 * delete some input geometry) */
601 bm, op, op->slots_out, "geom_holes.out", BM_ALL_NOLOOP, HULL_FLAG_HOLE);
602
603 /* Output slot of all hull vertices, faces, and edges */
605 bm, op, op->slots_out, "geom.out", BM_ALL_NOLOOP, HULL_FLAG_OUTPUT_GEOM);
606}
607
608#endif /* WITH_BULLET */
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
struct GHash GHash
Definition BLI_ghash.h:39
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.cc:731
void BLI_ghash_insert(GHash *gh, void *key, void *val)
Definition BLI_ghash.cc:707
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition BLI_ghash.cc:860
#define LISTBASE_FOREACH(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
float normal_tri_v3(float n[3], const float v1[3], const float v2[3], const float v3[3])
Definition math_geom.cc:41
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_ALLOW_ITER
@ BLI_MEMPOOL_NOP
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
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)
#define AT
Read Guarded memory(de)allocation.
struct plConvexHull__ * plConvexHull
#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:30
@ 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
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
T * data()
void reinitialize(const int64_t new_size)
nullptr float
int count
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
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
i
Definition text_draw.cc:230
uint len