Blender V4.5
bmo_extrude.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#include "MEM_guardedalloc.h"
12
13#include "DNA_meshdata_types.h"
14
15#include "BLI_buffer.h"
16#include "BLI_math_base.h"
17#include "BLI_math_geom.h"
18#include "BLI_math_vector.h"
19
20#include "BKE_customdata.hh"
21
22#include "bmesh.hh"
23
24#include "intern/bmesh_operators_private.hh" /* own include */
25
26#define USE_EDGE_REGION_FLAGS
27
28enum {
33};
34
35#define VERT_MARK 1
36#define EDGE_MARK 1
37#define FACE_MARK 1
38#define VERT_NONMAN 2
39#define EDGE_NONMAN 2
40
42{
43 const bool use_select_history = BMO_slot_bool_get(op->slots_in, "use_select_history");
44 GHash *select_history_map = nullptr;
45
46 BMOIter siter;
47 BMFace *f_org;
48
49 if (use_select_history) {
50 select_history_map = BM_select_history_map_create(bm);
51 }
52
53 BMO_ITER (f_org, &siter, op->slots_in, "faces", BM_FACE) {
54 BMFace *f_new;
55 BMLoop *l_org, *l_org_first;
56 BMLoop *l_new;
57
59
60 f_new = BM_face_copy(bm, f_org, true, true);
62
63 if (select_history_map) {
64 BMEditSelection *ese;
65 ese = static_cast<BMEditSelection *>(BLI_ghash_lookup(select_history_map, f_org));
66 if (ese) {
67 ese->ele = (BMElem *)f_new;
68 }
69 }
70
71 l_org = l_org_first = BM_FACE_FIRST_LOOP(f_org);
72 l_new = BM_FACE_FIRST_LOOP(f_new);
73
74 do {
75 BMFace *f_side;
76 BMLoop *l_side_iter;
77
78 BM_elem_attrs_copy(bm, l_org, l_new);
79
81 bm, l_org->next->v, l_new->next->v, l_new->v, l_org->v, f_org, BM_CREATE_NOP);
82
83 l_side_iter = BM_FACE_FIRST_LOOP(f_side);
84
85 BM_elem_attrs_copy(bm, l_org->next, l_side_iter);
86 l_side_iter = l_side_iter->next;
87 BM_elem_attrs_copy(bm, l_org->next, l_side_iter);
88 l_side_iter = l_side_iter->next;
89 BM_elem_attrs_copy(bm, l_org, l_side_iter);
90 l_side_iter = l_side_iter->next;
91 BM_elem_attrs_copy(bm, l_org, l_side_iter);
92
93 if (select_history_map) {
94 BMEditSelection *ese;
95
96 ese = static_cast<BMEditSelection *>(BLI_ghash_lookup(select_history_map, l_org->v));
97 if (ese) {
98 ese->ele = (BMElem *)l_new->v;
99 }
100 ese = static_cast<BMEditSelection *>(BLI_ghash_lookup(select_history_map, l_org->e));
101 if (ese) {
102 ese->ele = (BMElem *)l_new->e;
103 }
104 }
105
106 } while (((void)(l_new = l_new->next), (l_org = l_org->next)) != l_org_first);
107 }
108
109 if (select_history_map) {
110 BLI_ghash_free(select_history_map, nullptr, nullptr);
111 }
112
113 BMO_op_callf(bm, op->flag, "delete geom=%ff context=%i", EXT_DEL, DEL_ONLYFACES);
115}
116
128{
129 /* edge we are extruded from */
130 BMLoop *l_first_0 = BM_FACE_FIRST_LOOP(f);
131 BMLoop *l_first_1 = l_first_0->next;
132 BMLoop *l_first_2 = l_first_1->next;
133 BMLoop *l_first_3 = l_first_2->next;
134
135 BMLoop *l_other_0;
136 BMLoop *l_other_1;
137
138 if (UNLIKELY(l_first_0 == l_first_0->radial_next)) {
139 return;
140 }
141
142 l_other_0 = BM_edge_other_loop(l_first_0->e, l_first_0);
143 l_other_1 = BM_edge_other_loop(l_first_0->e, l_first_1);
144
145 /* copy data */
146 BM_elem_attrs_copy(bm, l_other_0->f, f);
147 BM_elem_flag_disable(f, BM_ELEM_HIDDEN); /* possibly we copy from a hidden face */
148
149 BM_elem_attrs_copy(bm, l_other_0, l_first_0);
150 BM_elem_attrs_copy(bm, l_other_0, l_first_3);
151
152 BM_elem_attrs_copy(bm, l_other_1, l_first_1);
153 BM_elem_attrs_copy(bm, l_other_1, l_first_2);
154}
155
156/* Disable the skin root flag on the input vert, assumes that the vert
157 * data includes an CD_MVERT_SKIN layer */
159{
160 MVertSkin *vs;
161
162 vs = static_cast<MVertSkin *>(CustomData_bmesh_get(&bm->vdata, v->head.data, CD_MVERT_SKIN));
163 vs->flag &= ~MVERT_SKIN_ROOT;
164}
165
167{
168 BMOIter siter;
169 BMOperator dupeop;
170 BMFace *f;
171 BMEdge *e, *e_new;
172 const bool use_normal_flip = BMO_slot_bool_get(op->slots_in, "use_normal_flip");
173
174 BMO_ITER (e, &siter, op->slots_in, "edges", BM_EDGE) {
178 }
179
181 &dupeop,
182 op->flag,
183 "duplicate geom=%fve use_select_history=%b",
184 EXT_INPUT,
185 BMO_slot_bool_get(op->slots_in, "use_select_history"));
186
187 BMO_op_exec(bm, &dupeop);
188
189 /* disable root flag on all new skin nodes */
190 if (CustomData_has_layer(&bm->vdata, CD_MVERT_SKIN)) {
191 BMVert *v;
192 BMO_ITER (v, &siter, dupeop.slots_out, "geom.out", BM_VERT) {
194 }
195 }
196
197 for (e = static_cast<BMEdge *>(BMO_iter_new(&siter, dupeop.slots_out, "boundary_map.out", 0)); e;
198 e = static_cast<BMEdge *>(BMO_iter_step(&siter)))
199 {
200 BMVert *f_verts[4];
201 e_new = static_cast<BMEdge *>(BMO_iter_map_value_ptr(&siter));
202
203 const bool edge_normal_flip = !(e->l && e->v1 != e->l->v);
204 if (edge_normal_flip == use_normal_flip) {
205 f_verts[0] = e->v1;
206 f_verts[1] = e->v2;
207 f_verts[2] = e_new->v2;
208 f_verts[3] = e_new->v1;
209 }
210 else {
211 f_verts[0] = e->v2;
212 f_verts[1] = e->v1;
213 f_verts[2] = e_new->v1;
214 f_verts[3] = e_new->v2;
215 }
216 /* not sure what to do about example face, pass nullptr for now */
217 f = BM_face_create_verts(bm, f_verts, 4, nullptr, BM_CREATE_NOP, true);
219
221 e = e_new;
222 }
223
228 }
229
230 BMO_op_finish(bm, &dupeop);
231
233}
234
236{
237 const bool use_select_history = BMO_slot_bool_get(op->slots_in, "use_select_history");
238 BMOIter siter;
239 BMVert *v, *dupev;
240 BMEdge *e;
241 const bool has_vskin = CustomData_has_layer(&bm->vdata, CD_MVERT_SKIN);
242 GHash *select_history_map = nullptr;
243
244 if (use_select_history) {
245 select_history_map = BM_select_history_map_create(bm);
246 }
247
248 for (v = static_cast<BMVert *>(BMO_iter_new(&siter, op->slots_in, "verts", BM_VERT)); v;
249 v = static_cast<BMVert *>(BMO_iter_step(&siter)))
250 {
251 dupev = BM_vert_create(bm, v->co, v, BM_CREATE_NOP);
253
254 if (has_vskin) {
256 }
257
258 if (select_history_map) {
259 BMEditSelection *ese;
260 ese = static_cast<BMEditSelection *>(BLI_ghash_lookup(select_history_map, v));
261 if (ese) {
262 ese->ele = (BMElem *)dupev;
263 }
264 }
265
266 /* not essential, but ensures face normals from extruded edges are contiguous */
267 if (BM_vert_is_wire_endpoint(v)) {
268 if (v->e->v1 == v) {
269 std::swap(v, dupev);
270 }
271 }
272
273 e = BM_edge_create(bm, v, dupev, nullptr, BM_CREATE_NOP);
275 }
276
277 if (select_history_map) {
278 BLI_ghash_free(select_history_map, nullptr, nullptr);
279 }
280
283}
284
285#ifdef USE_EDGE_REGION_FLAGS
290static bool bm_extrude_region_edge_flag(const BMVert *v, char r_e_hflag[2])
291{
292 BMEdge *e_iter;
293 const char hflag_enable = BM_ELEM_SEAM;
294 const char hflag_disable = BM_ELEM_SMOOTH;
295 bool ok = false;
296
297 r_e_hflag[0] = 0x0;
298 r_e_hflag[1] = 0xff;
299
300 /* clear flags on both disks */
301 e_iter = v->e;
302 do {
303 if (e_iter->l && !BM_edge_is_boundary(e_iter)) {
304 r_e_hflag[0] |= e_iter->head.hflag;
305 r_e_hflag[1] &= e_iter->head.hflag;
306 ok = true;
307 }
308 } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != v->e);
309
310 if (ok) {
311 r_e_hflag[0] &= hflag_enable;
312 r_e_hflag[1] = hflag_disable & ~r_e_hflag[1];
313 }
314 return ok;
315}
316#endif /* USE_EDGE_REGION_FLAGS */
317
319{
320 BMOperator dupeop, delop;
321 BMOIter siter;
322 BMIter iter, fiter, viter;
323 BMEdge *e, *e_new;
324 BMVert *v;
325 BMFace *f;
326 bool found, delorig = false;
327 BMOpSlot *slot_facemap_out;
328 BMOpSlot *slot_edges_exclude;
329 const bool use_normal_flip = BMO_slot_bool_get(op->slots_in, "use_normal_flip");
330 const bool use_normal_from_adjacent = BMO_slot_bool_get(op->slots_in,
331 "use_normal_from_adjacent");
332 const bool use_dissolve_ortho_edges = BMO_slot_bool_get(op->slots_in,
333 "use_dissolve_ortho_edges");
334
335 /* initialize our sub-operators */
337 &dupeop,
338 op->flag,
339 "duplicate use_select_history=%b",
340 BMO_slot_bool_get(op->slots_in, "use_select_history"));
341
343
344 /* if one flagged face is bordered by an un-flagged face, then we delete
345 * original geometry unless caller explicitly asked to keep it. */
346 if (!BMO_slot_bool_get(op->slots_in, "use_keep_orig")) {
347 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
348
349 int edge_face_tot;
350
352 continue;
353 }
354
355 found = false; /* found a face that isn't input? */
356 edge_face_tot = 0; /* edge/face count */
357
358 BM_ITER_ELEM (f, &fiter, e, BM_FACES_OF_EDGE) {
359 if (!BMO_face_flag_test(bm, f, EXT_INPUT)) {
360 found = true;
361 delorig = true;
362 break;
363 }
364
365 edge_face_tot++;
366 }
367
368 if ((edge_face_tot > 1) && (found == false)) {
369 /* edge has a face user, that face isn't extrude input */
371 }
372 }
373 }
374
375 /* calculate verts to delete */
376 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
377 if (v->e) { /* only deal with verts attached to geometry #33651. */
378 found = false;
379
380 BM_ITER_ELEM (e, &viter, v, BM_EDGES_OF_VERT) {
382 found = true;
383 break;
384 }
385 }
386
387 /* avoid an extra loop */
388 if (found == false) {
389 BM_ITER_ELEM (f, &viter, v, BM_FACES_OF_VERT) {
390 if (!BMO_face_flag_test(bm, f, EXT_INPUT)) {
391 found = true;
392 break;
393 }
394 }
395 }
396
397 if (found == false) {
399 }
400 }
401 }
402
403 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
406 }
407 }
408
409 if (delorig == true) {
410 BMO_op_initf(bm, &delop, op->flag, "delete geom=%fvef context=%i", EXT_DEL, DEL_ONLYTAGGED);
411 }
412
413 BMO_slot_copy(op, slots_in, "geom", &dupeop, slots_in, "geom");
414 BMO_op_exec(bm, &dupeop);
415
416 /* disable root flag on all new skin nodes */
417 if (CustomData_has_layer(&bm->vdata, CD_MVERT_SKIN)) {
418 BMO_ITER (v, &siter, dupeop.slots_out, "geom.out", BM_VERT) {
420 }
421 }
422
423 slot_facemap_out = BMO_slot_get(dupeop.slots_out, "face_map.out");
424 if (bm->act_face && BMO_face_flag_test(bm, bm->act_face, EXT_INPUT)) {
425 bm->act_face = static_cast<BMFace *>(BMO_slot_map_elem_get(slot_facemap_out, bm->act_face));
426 }
427
428 if (delorig) {
429 BMO_op_exec(bm, &delop);
430 }
431
432 /* if not delorig, reverse loops of original face */
433 if (!delorig) {
434 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
437 }
438 }
439 }
440
441 BMVert **dissolve_verts = nullptr;
442 int dissolve_verts_len = 0;
443 float average_normal[3];
444 if (use_dissolve_ortho_edges) {
445 /* Calc average normal. */
446 zero_v3(average_normal);
447 BMO_ITER (f, &siter, dupeop.slots_out, "geom.out", BM_FACE) {
448 add_v3_v3(average_normal, f->no);
449 }
450 if (normalize_v3(average_normal) == 0.0f) {
451 average_normal[2] = 1.0f;
452 }
453
454 /* Allocate array to store possible vertices that will be dissolved. */
455 int boundary_edges_len = BMO_slot_map_len(dupeop.slots_out, "boundary_map.out");
456 /* We do not know the real number of boundary vertices. */
457 int boundary_verts_len_maybe = 2 * boundary_edges_len;
458 dissolve_verts = static_cast<BMVert **>(
459 MEM_mallocN(boundary_verts_len_maybe * sizeof(*dissolve_verts), __func__));
460 }
461
462 BMO_slot_copy(&dupeop, slots_out, "geom.out", op, slots_out, "geom.out");
463
464 slot_edges_exclude = BMO_slot_get(op->slots_in, "edges_exclude");
465 for (e = static_cast<BMEdge *>(BMO_iter_new(&siter, dupeop.slots_out, "boundary_map.out", 0)); e;
466 e = static_cast<BMEdge *>(BMO_iter_step(&siter)))
467 {
468 BMVert *f_verts[4];
469#ifdef USE_EDGE_REGION_FLAGS
470 BMEdge *f_edges[4];
471#endif
472
473 /* this should always be wire, so this is mainly a speedup to avoid map lookup */
474 if (BM_edge_is_wire(e) && BMO_slot_map_contains(slot_edges_exclude, e)) {
475 BMVert *v1 = e->v1, *v2 = e->v2;
476
477 /* The original edge was excluded,
478 * this would result in a standalone wire edge - see #30399. */
479 BM_edge_kill(bm, e);
480
481 /* kill standalone vertices from this edge - see #32341. */
482 if (!v1->e) {
483 BM_vert_kill(bm, v1);
484 }
485 if (!v2->e) {
487 }
488
489 continue;
490 }
491
492 /* skip creating face for excluded edges see #35503. */
493 if (BMO_slot_map_contains(slot_edges_exclude, e)) {
494 /* simply skip creating the face */
495 continue;
496 }
497
498 e_new = static_cast<BMEdge *>(BMO_iter_map_value_ptr(&siter));
499
500 if (!e_new) {
501 continue;
502 }
503
504 BMFace *join_face = nullptr;
505 if (use_dissolve_ortho_edges) {
506 if (BM_edge_is_boundary(e)) {
507 join_face = e->l->f;
508 if (fabs(dot_v3v3(average_normal, join_face->no)) > 0.0001f) {
509 join_face = nullptr;
510 }
511 }
512 }
513
514 bool edge_normal_flip;
515 if (use_normal_from_adjacent == false) {
516 /* Orient loop to give same normal as a loop of 'e_new'
517 * if it exists (will be one of the faces from the region),
518 * else same normal as a loop of e, if it exists. */
519 edge_normal_flip = !(e_new->l ? (e_new->l->v == e_new->v1) : (!e->l || !(e->l->v == e->v1)));
520 }
521 else {
522 /* Special case, needed for repetitive extrusions
523 * that use the normals from the previously created faces. */
524 edge_normal_flip = !(e->l && e->v1 != e->l->v);
525 }
526
527 if (edge_normal_flip == use_normal_flip) {
528 f_verts[0] = e->v1;
529 f_verts[1] = e->v2;
530 f_verts[2] = e_new->v2;
531 f_verts[3] = e_new->v1;
532 }
533 else {
534 f_verts[0] = e->v2;
535 f_verts[1] = e->v1;
536 f_verts[2] = e_new->v1;
537 f_verts[3] = e_new->v2;
538 }
539
540#ifdef USE_EDGE_REGION_FLAGS
541 /* handle new edges */
542 f_edges[0] = e;
543 f_edges[2] = e_new;
544
545 f_edges[1] = BM_edge_exists(f_verts[1], f_verts[2]);
546 if (f_edges[1] == nullptr) {
547 char e_hflag[2];
548 bool e_hflag_ok = bm_extrude_region_edge_flag(f_verts[2], e_hflag);
549 f_edges[1] = BM_edge_create(bm, f_verts[1], f_verts[2], nullptr, BM_CREATE_NOP);
550 if (e_hflag_ok) {
551 BM_elem_flag_enable(f_edges[1], e_hflag[0]);
552 BM_elem_flag_disable(f_edges[1], e_hflag[1]);
553 }
554 }
555
556 f_edges[3] = BM_edge_exists(f_verts[3], f_verts[0]);
557 if (f_edges[3] == nullptr) {
558 char e_hflag[2];
559 bool e_hflag_ok = bm_extrude_region_edge_flag(f_verts[3], e_hflag);
560 f_edges[3] = BM_edge_create(bm, f_verts[3], f_verts[0], nullptr, BM_CREATE_NOP);
561 if (e_hflag_ok) {
562 BM_elem_flag_enable(f_edges[3], e_hflag[0]);
563 BM_elem_flag_disable(f_edges[3], e_hflag[1]);
564 }
565 }
566
567 f = BM_face_create(bm, f_verts, f_edges, 4, nullptr, BM_CREATE_NOP);
568#else
569 f = BM_face_create_verts(bm, f_verts, 4, nullptr, BM_CREATE_NOP, true);
570#endif
571
573 if (join_face) {
574 BMVert *v1 = e->v1;
575 BMVert *v2 = e->v2;
576 if (!BMO_elem_flag_test(bm, v1, EXT_TAG)) {
578 dissolve_verts[dissolve_verts_len++] = v1;
579 }
582 dissolve_verts[dissolve_verts_len++] = v2;
583 }
584 /* Tag the edges that can collapse. */
585 BMO_elem_flag_enable(bm, f_edges[0], EXT_TAG);
586 BMO_elem_flag_enable(bm, f_edges[1], EXT_TAG);
587 bmesh_kernel_join_face_kill_edge(bm, join_face, f, e);
588 }
589 }
590
591 /* link isolated vert */
592 for (v = static_cast<BMVert *>(BMO_iter_new(&siter, dupeop.slots_out, "isovert_map.out", 0)); v;
593 v = static_cast<BMVert *>(BMO_iter_step(&siter)))
594 {
595 BMVert *v2 = static_cast<BMVert *>(BMO_iter_map_value_ptr(&siter));
596
597 /* not essential, but ensures face normals from extruded edges are contiguous */
598 if (BM_vert_is_wire_endpoint(v)) {
599 if (v->e->v1 == v) {
600 std::swap(v, v2);
601 }
602 }
603
605 }
606
607 if (dissolve_verts) {
608 BMVert **v_iter = &dissolve_verts[0];
609 for (int i = dissolve_verts_len; i--; v_iter++) {
610 v = *v_iter;
611 e = v->e;
612 BMEdge *e_other = BM_DISK_EDGE_NEXT(e, v);
613 if ((e_other == e) || (BM_DISK_EDGE_NEXT(e_other, v) == e)) {
614 /* Loose edge or BMVert is edge pair. */
615 BM_edge_collapse(bm, BMO_elem_flag_test(bm, e, EXT_TAG) ? e : e_other, v, true, true);
616 }
617 else {
619 }
620 }
621 MEM_freeN(dissolve_verts);
622 }
623
624 /* cleanup */
625 if (delorig) {
626 BMO_op_finish(bm, &delop);
627 }
628 BMO_op_finish(bm, &dupeop);
629}
630
631/*
632 * Compute higher-quality vertex normals used by solidify.
633 * Only considers geometry in the marked solidify region.
634 * Note that this does not work so well for non-manifold
635 * regions.
636 */
638{
639 BMIter viter, eiter, fiter;
640 BMVert *v;
641 BMEdge *e;
642 BMFace *f, *f1, *f2;
643 float edge_normal[3];
644 int i;
645
646 /* can't use BM_edge_face_count because we need to count only marked faces */
647 int *edge_face_count = MEM_calloc_arrayN<int>(bm->totedge, __func__);
648
649 BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
651 }
652
654
655 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
656 if (!BMO_face_flag_test(bm, f, FACE_MARK)) {
657 continue;
658 }
659
660 BM_ITER_ELEM (e, &eiter, f, BM_EDGES_OF_FACE) {
661
662 /* And mark all edges and vertices on the
663 * marked faces */
667 edge_face_count[BM_elem_index_get(e)]++;
668 }
669 }
670
671 BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
673 continue;
674 }
675
676 i = edge_face_count[BM_elem_index_get(e)];
677
678 if (i == 0 || i > 2) {
679 /* Edge & vertices are non-manifold even when considering
680 * only marked faces */
684 }
685 }
686 MEM_freeN(edge_face_count);
687 edge_face_count = nullptr; /* don't re-use */
688
689 BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
690 if (!BM_vert_is_manifold(v)) {
692 continue;
693 }
694
696 zero_v3(v->no);
697 }
698 }
699
700 BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
701
702 /* If the edge is not part of a the solidify region
703 * its normal should not be considered */
705 continue;
706 }
707
708 /* If the edge joins more than two marked faces high
709 * quality normal computation won't work */
711 continue;
712 }
713
714 f1 = f2 = nullptr;
715
716 BM_ITER_ELEM (f, &fiter, e, BM_FACES_OF_EDGE) {
718 if (f1 == nullptr) {
719 f1 = f;
720 }
721 else {
722 BLI_assert(f2 == nullptr);
723 f2 = f;
724 }
725 }
726 }
727
728 BLI_assert(f1 != nullptr);
729
730 if (f2 != nullptr) {
731 const float angle = angle_normalized_v3v3(f1->no, f2->no);
732
733 if (angle > 0.0f) {
734 /* two faces using this edge, calculate the edge normal
735 * using the angle between the faces as a weighting */
736 add_v3_v3v3(edge_normal, f1->no, f2->no);
737 normalize_v3_length(edge_normal, angle);
738 }
739 else {
740 /* can't do anything useful here!
741 * Set the face index for a vert in case it gets a zero normal */
744 continue;
745 }
746 }
747 else {
748 /* only one face attached to that edge */
749 /* an edge without another attached- the weight on this is undefined,
750 * M_PI_2 is 90d in radians and that seems good enough */
751 copy_v3_v3(edge_normal, f1->no);
752 mul_v3_fl(edge_normal, M_PI_2);
753 }
754
755 add_v3_v3(e->v1->no, edge_normal);
756 add_v3_v3(e->v2->no, edge_normal);
757 }
758
759 /* normalize accumulated vertex normal */
760 BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
762 continue;
763 }
764
766 /* use standard normals for vertices connected to non-manifold edges */
768 }
769 else if (normalize_v3(v->no) == 0.0f && !BM_elem_flag_test(v, BM_ELEM_TAG)) {
770 /* exceptional case, totally flat. use the normal
771 * of any marked face around the vertex */
772 BM_ITER_ELEM (f, &fiter, v, BM_FACES_OF_VERT) {
774 break;
775 }
776 }
777 copy_v3_v3(v->no, f->no);
778 }
779 }
780}
781
782static void solidify_add_thickness(BMesh *bm, const float dist)
783{
784 BMFace *f;
785 BMVert *v;
786 BMLoop *l;
787 BMIter iter, loopIter;
788 float *vert_angles = MEM_calloc_arrayN<float>(size_t(bm->totvert) * 2, "solidify"); /* 2 in 1 */
789 float *vert_accum = vert_angles + bm->totvert;
790 int i, index;
791
794
796
797 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
799
800 /* array for passing verts to angle_poly_v3 */
801 float *face_angles = BLI_buffer_reinit_data(&face_angles_buf, float, f->len);
802 /* array for receiving angles from angle_poly_v3 */
803 float **verts = BLI_buffer_reinit_data(&verts_buf, float *, f->len);
804
805 BM_ITER_ELEM_INDEX (l, &loopIter, f, BM_LOOPS_OF_FACE, i) {
806 verts[i] = l->v->co;
807 }
808
809 angle_poly_v3(face_angles, (const float **)verts, f->len);
810
811 i = 0;
812 BM_ITER_ELEM (l, &loopIter, f, BM_LOOPS_OF_FACE) {
813 v = l->v;
814 index = BM_elem_index_get(v);
815 vert_accum[index] += face_angles[i];
816 vert_angles[index] += shell_v3v3_normalized_to_dist(v->no, f->no) * face_angles[i];
817 i++;
818 }
819 }
820 }
821
822 BLI_buffer_free(&face_angles_buf);
823 BLI_buffer_free(&verts_buf);
824
825 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
826 index = BM_elem_index_get(v);
827 if (vert_accum[index]) { /* zero if unselected */
828 madd_v3_v3fl(v->co, v->no, dist * (vert_angles[index] / vert_accum[index]));
829 }
830 }
831
832 MEM_freeN(vert_angles);
833}
834
836{
837 BMOperator extrudeop;
838 BMOperator reverseop;
839 float thickness;
840
841 thickness = BMO_slot_float_get(op->slots_in, "thickness");
842
843 /* Flip original faces (so the shell is extruded inward) */
844 BMO_op_init(bm, &reverseop, op->flag, "reverse_faces");
845 BMO_slot_bool_set(reverseop.slots_in, "flip_multires", true);
846 BMO_slot_copy(op, slots_in, "geom", &reverseop, slots_in, "faces");
847 BMO_op_exec(bm, &reverseop);
848 BMO_op_finish(bm, &reverseop);
849
850 /* Extrude the region */
851 BMO_op_initf(bm, &extrudeop, op->flag, "extrude_face_region use_keep_orig=%b", true);
852 BMO_slot_copy(op, slots_in, "geom", &extrudeop, slots_in, "geom");
853 BMO_op_exec(bm, &extrudeop);
854
855 /* Push the verts of the extruded faces inward to create thickness */
856 BMO_slot_buffer_flag_enable(bm, extrudeop.slots_out, "geom.out", BM_FACE, FACE_MARK);
858 solidify_add_thickness(bm, thickness);
859
860 BMO_slot_copy(&extrudeop, slots_out, "geom.out", op, slots_out, "geom.out");
861
862 BMO_op_finish(bm, &extrudeop);
863}
CustomData interface, see also DNA_customdata_types.h.
void * CustomData_bmesh_get(const CustomData *data, void *block, eCustomDataType type)
bool CustomData_has_layer(const CustomData *data, eCustomDataType type)
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_buffer_reinit_data(buffer_, type_, new_count_)
Definition BLI_buffer.h:47
@ BLI_BUFFER_NOP
Definition BLI_buffer.h:21
#define BLI_buffer_declare_static(type_, name_, flag_, static_count_)
Definition BLI_buffer.h:25
#define BLI_buffer_free(name_)
Definition BLI_buffer.h:94
void * BLI_ghash_lookup(const GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.cc:731
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition BLI_ghash.cc:860
#define M_PI_2
MINLINE float shell_v3v3_normalized_to_dist(const float a[3], const float b[3])
MINLINE void madd_v3_v3fl(float r[3], const float a[3], float f)
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void add_v3_v3v3(float r[3], const float a[3], const float b[3])
void angle_poly_v3(float *angles, const float *verts[3], int len)
MINLINE void zero_v3(float r[3])
float angle_normalized_v3v3(const float v1[3], const float v2[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3_length(float n[3], float unit_length)
MINLINE float normalize_v3(float n[3])
#define UNLIKELY(x)
@ CD_MVERT_SKIN
@ MVERT_SKIN_ROOT
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
Read Guarded memory(de)allocation.
#define BM_DISK_EDGE_NEXT(e, v)
#define BM_DEFAULT_NGON_STACK_SIZE
#define BM_ALL_NOLOOP
#define BM_FACE_FIRST_LOOP(p)
@ BM_ELEM_HIDDEN
@ BM_ELEM_SEAM
@ BM_ELEM_SMOOTH
@ BM_ELEM_TAG
void BM_elem_attrs_copy(BMesh *bm, const BMCustomDataCopyMap &map, const BMVert *src, BMVert *dst)
BMFace * BM_face_create_quad_tri(BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v3, BMVert *v4, const BMFace *f_example, const eBMCreateFlag create_flag)
Make Quad/Triangle.
void BM_vert_kill(BMesh *bm, BMVert *v)
BMFace * bmesh_kernel_join_face_kill_edge(BMesh *bm, BMFace *f1, BMFace *f2, BMEdge *e)
Join Face Kill Edge (JFKE).
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)
BMFace * BM_face_copy(BMesh *bm_dst, const BMCustomDataCopyMap &cd_face_map, const BMCustomDataCopyMap &cd_loop_map, BMFace *f, const bool copy_verts, const bool copy_edges)
BMFace * BM_face_create(BMesh *bm, BMVert *const *verts, BMEdge *const *edges, const int len, const BMFace *f_example, const eBMCreateFlag create_flag)
void BM_edge_kill(BMesh *bm, BMEdge *e)
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.cc:41
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_NOP
Definition bmesh_core.hh:28
@ BM_CREATE_NO_DOUBLE
Definition bmesh_core.hh:30
#define BM_elem_index_get(ele)
#define BM_elem_flag_disable(ele, hflag)
#define BM_elem_flag_test(ele, hflag)
#define BM_elem_flag_enable(ele, hflag)
#define BM_ITER_ELEM(ele, iter, data, itype)
#define BM_ITER_MESH(ele, iter, bm, itype)
@ BM_FACES_OF_EDGE
@ BM_FACES_OF_VERT
@ BM_EDGES_OF_MESH
@ BM_VERTS_OF_MESH
@ BM_FACES_OF_MESH
@ BM_EDGES_OF_VERT
@ BM_EDGES_OF_FACE
@ BM_LOOPS_OF_FACE
#define BM_ITER_ELEM_INDEX(ele, iter, data, itype, indexvar)
BMesh * bm
GHash * BM_select_history_map_create(BMesh *bm)
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
BMVert * BM_edge_collapse(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, const bool do_del, const bool kill_degenerate_faces)
#define BM_FACE
#define BM_EDGE
#define BM_VERT
@ DEL_ONLYTAGGED
@ DEL_ONLYFACES
void BMO_slot_buffer_flag_enable(BMesh *bm, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, char htype, short oflag)
BMO_FLAG_BUFFER.
#define BMO_edge_flag_test(bm, e, oflag)
void * BMO_iter_map_value_ptr(BMOIter *iter)
#define BMO_edge_flag_enable(bm, e, oflag)
int BMO_slot_map_len(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
float BMO_slot_float_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
#define BMO_vert_flag_enable(bm, e, oflag)
void * BMO_iter_new(BMOIter *iter, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, char restrictmask)
New Iterator.
BMOpSlot * BMO_slot_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *identifier)
BMESH OPSTACK GET SLOT.
void BMO_op_exec(BMesh *bm, BMOperator *op)
BMESH OPSTACK EXEC OP.
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_slot_copy(op_src, slots_src, slot_name_src, op_dst, slots_dst, slot_name_dst)
void BMO_op_init(BMesh *bm, BMOperator *op, int flag, const char *opname)
BMESH OPSTACK INIT OP.
#define BMO_ITER(ele, iter, slot_args, slot_name, restrict_flag)
#define BMO_vert_flag_test(bm, e, oflag)
bool BMO_op_initf(BMesh *bm, BMOperator *op, int flag, const char *fmt,...)
void BMO_op_finish(BMesh *bm, BMOperator *op)
BMESH OPSTACK FINISH OP.
#define BMO_elem_flag_enable(bm, ele, oflag)
bool BMO_op_callf(BMesh *bm, int flag, const char *fmt,...)
void BMO_slot_bool_set(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, bool i)
#define BMO_elem_flag_test(bm, ele, 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_iter_step(BMOIter *iter)
void BM_vert_normal_update(BMVert *v)
void BM_face_normal_flip(BMesh *bm, BMFace *f)
bool BM_vert_is_manifold(const BMVert *v)
BMEdge * BM_edge_exists(BMVert *v_a, BMVert *v_b)
BMLoop * BM_edge_other_loop(BMEdge *e, BMLoop *l)
bool BM_vert_is_edge_pair(const BMVert *v)
BLI_INLINE bool BM_edge_is_boundary(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE bool BM_edge_is_wire(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
#define FACE_MARK
#define EDGE_MARK
Definition bmo_bridge.cc:26
#define VERT_MARK
static void solidify_add_thickness(BMesh *bm, const float dist)
void bmo_extrude_discrete_faces_exec(BMesh *bm, BMOperator *op)
static void bm_extrude_copy_face_loop_attributes(BMesh *bm, BMFace *f)
Copy the loop pair from an adjacent face to both sides of this quad.
static bool bm_extrude_region_edge_flag(const BMVert *v, char r_e_hflag[2])
void bmo_extrude_edge_only_exec(BMesh *bm, BMOperator *op)
void bmo_extrude_vert_indiv_exec(BMesh *bm, BMOperator *op)
void bmo_solidify_face_region_exec(BMesh *bm, BMOperator *op)
#define EDGE_NONMAN
@ EXT_INPUT
@ EXT_TAG
@ EXT_KEEP
@ EXT_DEL
#define VERT_NONMAN
static void bm_extrude_disable_skin_root(BMesh *bm, BMVert *v)
static void calc_solidify_normals(BMesh *bm)
void bmo_extrude_face_region_exec(BMesh *bm, BMOperator *op)
static float verts[][3]
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
ccl_device_inline float2 fabs(const float2 a)
BMHeader head
BMVert * v1
BMVert * v2
struct BMLoop * l
float no[3]
struct BMVert * v
struct BMEdge * e
struct BMLoop * radial_next
struct BMFace * f
struct BMLoop * next
struct BMOpSlot slots_out[BMO_OP_MAX_SLOTS]
struct BMOpSlot slots_in[BMO_OP_MAX_SLOTS]
struct BMEdge * e
i
Definition text_draw.cc:230