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