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