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