81cad277beea705d124cc11899af118a59ffce6a
[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
27 #include "MEM_guardedalloc.h"
28
29 #include "DNA_meshdata_types.h"
30
31 #include "BLI_math.h"
32 #include "BLI_array.h"
33
34 #include "BKE_customdata.h"
35
36 #include "bmesh.h"
37
38 #include "intern/bmesh_operators_private.h" /* own include */
39
40 enum {
41         EXT_INPUT   = 1,
42         EXT_KEEP    = 2,
43         EXT_DEL     = 4
44 };
45
46 #define VERT_MARK 1
47 #define EDGE_MARK 1
48 #define FACE_MARK 1
49 #define VERT_NONMAN 2
50 #define EDGE_NONMAN 2
51
52 void bmo_extrude_discrete_faces_exec(BMesh *bm, BMOperator *op)
53 {
54         BMOIter siter;
55         BMIter liter, liter2;
56         BMFace *f, *f2, *f3;
57         BMLoop *l, *l2, *l3, *l4, *l_tmp;
58         BMEdge **edges = NULL, *e, *laste;
59         BMVert *v, *lastv, *firstv;
60         BLI_array_declare(edges);
61         int i;
62
63         BMO_ITER (f, &siter, bm, op, "faces", BM_FACE) {
64                 BLI_array_empty(edges);
65                 BLI_array_grow_items(edges, f->len);
66
67                 i = 0;
68                 firstv = lastv = NULL;
69                 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
70                         v = BM_vert_create(bm, l->v->co, l->v);
71
72                         /* skip on the first iteration */
73                         if (lastv) {
74                                 e = BM_edge_create(bm, lastv, v, l->e, FALSE);
75                                 edges[i++] = e;
76                         }
77
78                         lastv = v;
79                         laste = l->e;
80                         if (!firstv) firstv = v;
81                 }
82
83                 /* this fits in the array because we skip one in the loop above */
84                 e = BM_edge_create(bm, v, firstv, laste, FALSE);
85                 edges[i++] = e;
86
87                 BMO_elem_flag_enable(bm, f, EXT_DEL);
88
89                 f2 = BM_face_create_ngon(bm, firstv, BM_edge_other_vert(edges[0], firstv), edges, f->len, FALSE);
90                 if (UNLIKELY(f2 == NULL)) {
91                         BMO_error_raise(bm, op, BMERR_MESH_ERROR, "Extrude failed; could not create face");
92                         BLI_array_free(edges);
93                         return;
94                 }
95                 
96                 BMO_elem_flag_enable(bm, f2, EXT_KEEP);
97                 BM_elem_attrs_copy(bm, bm, f, f2);
98
99                 l2 = BM_iter_new(&liter2, bm, BM_LOOPS_OF_FACE, f2);
100                 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
101                         BM_elem_attrs_copy(bm, bm, l, l2);
102
103                         l3 = l->next;
104                         l4 = l2->next;
105
106                         f3 = BM_face_create_quad_tri(bm, l3->v, l4->v, l2->v, l->v, f, FALSE);
107                         /* XXX, no error check here, why? - Campbell */
108
109                         l_tmp = BM_FACE_FIRST_LOOP(f3);
110
111                         BM_elem_attrs_copy(bm, bm, l->next, l_tmp);  l_tmp = l_tmp->next;
112                         BM_elem_attrs_copy(bm, bm, l->next, l_tmp);  l_tmp = l_tmp->next;
113                         BM_elem_attrs_copy(bm, bm, l, l_tmp);        l_tmp = l_tmp->next;
114                         BM_elem_attrs_copy(bm, bm, l, l_tmp);
115
116                         l2 = BM_iter_step(&liter2);
117                 }
118         }
119
120         BLI_array_free(edges);
121
122         BMO_op_callf(bm, op->flag,
123                      "delete geom=%ff context=%i",
124                      EXT_DEL, DEL_ONLYFACES);
125         BMO_slot_buffer_from_enabled_flag(bm, op, "faceout", BM_FACE, EXT_KEEP);
126 }
127
128 /**
129  * \brief Copy the loop pair from an adjacent face to both sides of this quad.
130  *
131  * The face is assumed to be a quad, created by extruding.
132  * This function won't crash if its not but won't work right either.
133  * \a e_b is the new edge.
134  *
135  * \note this function could be exposed as an api call if other areas need it,
136  * so far only extrude does.
137  */
138 static void bm_extrude_copy_face_loop_attributes(BMesh *bm, BMFace *f, BMEdge *e_a, BMEdge *e_b)
139 {
140         /* 'a' is the starting edge #e, 'b' is the final edge #newedge */
141         BMLoop *l_dst_a = BM_face_edge_share_loop(f, e_a);
142         BMLoop *l_dst_b = BM_face_edge_share_loop(f, e_b);
143         /* we could only have a face on one-or the other edges,
144          * check if either side of the face has an adjacent face */
145         BMLoop *l_src_1;
146         BMLoop *l_src_2;
147
148         /* there is no l_src_b */
149
150         /* sanity */
151         BLI_assert(l_dst_a->f == l_dst_b->f);
152
153         if (l_dst_a != l_dst_a->radial_next) {
154                 l_src_1 = l_dst_a->radial_next;
155                 l_src_2 = l_src_1->next;
156         }
157         else if (l_dst_b != l_dst_b->radial_next) {
158                 l_src_2 = l_dst_b->radial_next;
159                 l_src_1 = l_src_2->next;
160         }
161         else {
162                 /* no new faces on either edge, nothing to copy from */
163                 return;
164         }
165
166         BM_elem_attrs_copy(bm, bm, l_src_1->f, l_dst_a->f);
167         BM_elem_flag_disable(f, BM_ELEM_HIDDEN); /* possibly we copy from a hidden face */
168
169         /* copy data */
170         BM_elem_attrs_copy(bm, bm, l_src_2, l_dst_a);
171         BM_elem_attrs_copy(bm, bm, l_src_2, l_dst_b->next);
172
173         BM_elem_attrs_copy(bm, bm, l_src_1, l_dst_a->next);
174         BM_elem_attrs_copy(bm, bm, l_src_1, l_dst_b);
175 }
176
177 /* Disable the skin root flag on the input vert, assumes that the vert
178  * data includes an CD_MVERT_SKIN layer */
179 static void bm_extrude_disable_skin_root(BMesh *bm, BMVert *v)
180 {
181         MVertSkin *vs;
182         
183         vs = CustomData_bmesh_get(&bm->vdata, v->head.data, CD_MVERT_SKIN);
184         vs->flag &= ~MVERT_SKIN_ROOT;
185 }
186
187 void bmo_extrude_edge_only_exec(BMesh *bm, BMOperator *op)
188 {
189         BMOIter siter;
190         BMOperator dupeop;
191         BMVert *v1, *v2, *v3, *v4;
192         BMEdge *e, *e2;
193         BMFace *f;
194         
195         BMO_ITER (e, &siter, bm, op, "edges", BM_EDGE) {
196                 BMO_elem_flag_enable(bm, e, EXT_INPUT);
197                 BMO_elem_flag_enable(bm, e->v1, EXT_INPUT);
198                 BMO_elem_flag_enable(bm, e->v2, EXT_INPUT);
199         }
200
201         BMO_op_initf(bm, &dupeop, op->flag, "duplicate geom=%fve", EXT_INPUT);
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                 BMO_ITER(v1, &siter, bm, &dupeop, "newout", BM_VERT) {
207                         bm_extrude_disable_skin_root(bm, v1);
208                 }
209         }
210
211         for (e = BMO_iter_new(&siter, bm, &dupeop, "boundarymap", 0); e; e = BMO_iter_step(&siter)) {
212                 e2 = BMO_iter_map_value(&siter);
213                 e2 = *(BMEdge **)e2;
214
215                 if (e->l && e->v1 != e->l->v) {
216                         v1 = e->v1;
217                         v2 = e->v2;
218                         v3 = e2->v2;
219                         v4 = e2->v1;
220                 }
221                 else {
222                         v1 = e2->v1;
223                         v2 = e2->v2;
224                         v3 = e->v2;
225                         v4 = e->v1;
226                 }
227                 /* not sure what to do about example face, pass NULL for now */
228                 f = BM_face_create_quad_tri(bm, v1, v2, v3, v4, NULL, FALSE);
229                 bm_extrude_copy_face_loop_attributes(bm, f, e, e2);
230                 
231                 if (BMO_elem_flag_test(bm, e, EXT_INPUT))
232                         e = e2;
233                 
234                 BMO_elem_flag_enable(bm, f, EXT_KEEP);
235                 BMO_elem_flag_enable(bm, e, EXT_KEEP);
236                 BMO_elem_flag_enable(bm, e->v1, EXT_KEEP);
237                 BMO_elem_flag_enable(bm, e->v2, EXT_KEEP);
238                 
239         }
240
241         BMO_op_finish(bm, &dupeop);
242
243         BMO_slot_buffer_from_enabled_flag(bm, op, "geomout", BM_ALL, EXT_KEEP);
244 }
245
246 void bmo_extrude_vert_indiv_exec(BMesh *bm, BMOperator *op)
247 {
248         BMOIter siter;
249         BMVert *v, *dupev;
250         BMEdge *e;
251         const int has_vskin = CustomData_has_layer(&bm->vdata, CD_MVERT_SKIN);
252
253         for (v = BMO_iter_new(&siter, bm, op, "verts", BM_VERT); v; v = BMO_iter_step(&siter)) {
254                 dupev = BM_vert_create(bm, v->co, v);
255                 if (has_vskin)
256                         bm_extrude_disable_skin_root(bm, v);
257
258                 e = BM_edge_create(bm, v, dupev, NULL, FALSE);
259
260                 BMO_elem_flag_enable(bm, e, EXT_KEEP);
261                 BMO_elem_flag_enable(bm, dupev, EXT_KEEP);
262         }
263
264         BMO_slot_buffer_from_enabled_flag(bm, op, "vertout", BM_VERT, EXT_KEEP);
265         BMO_slot_buffer_from_enabled_flag(bm, op, "edgeout", BM_EDGE, EXT_KEEP);
266 }
267
268 void bmo_extrude_face_region_exec(BMesh *bm, BMOperator *op)
269 {
270         BMOperator dupeop, delop;
271         BMOIter siter;
272         BMIter iter, fiter, viter;
273         BMEdge *e, *newedge;
274         BMVert *verts[4], *v, *v2;
275         BMFace *f;
276         int found, fwd, delorig = FALSE;
277
278         /* initialize our sub-operators */
279         BMO_op_init(bm, &dupeop, op->flag, "duplicate");
280         
281         BMO_slot_buffer_flag_enable(bm, op, "edgefacein", BM_EDGE | BM_FACE, EXT_INPUT);
282         
283         /* if one flagged face is bordered by an un-flagged face, then we delete
284          * original geometry unless caller explicitly asked to keep it. */
285         if (!BMO_slot_bool_get(op, "alwayskeeporig")) {
286                 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
287
288                         int edge_face_tot;
289
290                         if (!BMO_elem_flag_test(bm, e, EXT_INPUT)) {
291                                 continue;
292                         }
293
294                         found = FALSE; /* found a face that isn't input? */
295                         edge_face_tot = 0; /* edge/face count */
296
297                         BM_ITER_ELEM (f, &fiter, e, BM_FACES_OF_EDGE) {
298                                 if (!BMO_elem_flag_test(bm, f, EXT_INPUT)) {
299                                         found = TRUE;
300                                         delorig = TRUE;
301                                         break;
302                                 }
303
304                                 edge_face_tot++;
305                         }
306
307                         if ((edge_face_tot > 1) && (found == FALSE)) {
308                                 /* edge has a face user, that face isn't extrude input */
309                                 BMO_elem_flag_enable(bm, e, EXT_DEL);
310                         }
311                 }
312         }
313
314         /* calculate verts to delete */
315         BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
316                 found = FALSE;
317
318                 BM_ITER_ELEM (e, &viter, v, BM_EDGES_OF_VERT) {
319                         if (!BMO_elem_flag_test(bm, e, EXT_INPUT) || !BMO_elem_flag_test(bm, e, EXT_DEL)) {
320                                 found = TRUE;
321                                 break;
322                         }
323                 }
324
325                 /* avoid an extra loop */
326                 if (found == TRUE) {
327                         BM_ITER_ELEM (f, &viter, v, BM_FACES_OF_VERT) {
328                                 if (!BMO_elem_flag_test(bm, f, EXT_INPUT)) {
329                                         found = TRUE;
330                                         break;
331                                 }
332                         }
333                 }
334
335                 if (found == FALSE) {
336                         BMO_elem_flag_enable(bm, v, EXT_DEL);
337                 }
338         }
339         
340         BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
341                 if (BMO_elem_flag_test(bm, f, EXT_INPUT)) {
342                         BMO_elem_flag_enable(bm, f, EXT_DEL);
343                 }
344         }
345
346         if (delorig == TRUE) {
347                 BMO_op_initf(bm, &delop, op->flag,
348                              "delete geom=%fvef context=%i",
349                              EXT_DEL, DEL_ONLYTAGGED);
350         }
351
352         BMO_slot_copy(op, &dupeop, "edgefacein", "geom");
353         BMO_op_exec(bm, &dupeop);
354
355         /* disable root flag on all new skin nodes */
356         if (CustomData_has_layer(&bm->vdata, CD_MVERT_SKIN)) {
357                 BMO_ITER(v, &siter, bm, &dupeop, "newout", BM_VERT) {
358                         bm_extrude_disable_skin_root(bm, v);
359                 }
360         }
361
362         if (bm->act_face && BMO_elem_flag_test(bm, bm->act_face, EXT_INPUT))
363                 bm->act_face = BMO_slot_map_ptr_get(bm, &dupeop, "facemap", bm->act_face);
364
365         if (delorig) {
366                 BMO_op_exec(bm, &delop);
367         }
368         
369         /* if not delorig, reverse loops of original face */
370         if (!delorig) {
371                 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
372                         if (BMO_elem_flag_test(bm, f, EXT_INPUT)) {
373                                 BM_face_normal_flip(bm, f);
374                         }
375                 }
376         }
377         
378         BMO_slot_copy(&dupeop, op, "newout", "geomout");
379
380         for (e = BMO_iter_new(&siter, bm, &dupeop, "boundarymap", 0); e; e = BMO_iter_step(&siter)) {
381
382                 /* this should always be wire, so this is mainly a speedup to avoid map lookup */
383                 if (BM_edge_is_wire(e) && BMO_slot_map_contains(bm, op, "exclude", e)) {
384                         BMVert *v1 = e->v1, *v2 = e->v2;
385
386                         /* The original edge was excluded,
387                          * this would result in a standalone wire edge - see [#30399] */
388                         BM_edge_kill(bm, e);
389
390                         /* kill standalone vertices from this edge - see [#32341] */
391                         if (!v1->e)
392                                 BM_vert_kill(bm, v1);
393                         if (!v2->e)
394                                 BM_vert_kill(bm, v2);
395
396                         continue;
397                 }
398
399                 newedge = *(BMEdge **)BMO_iter_map_value(&siter);
400
401                 if (!newedge) {
402                         continue;
403                 }
404
405                 /* orient loop to give same normal as a loop of newedge
406                  * if it exists (will be an extruded face),
407                  * else same normal as a loop of e, if it exists */
408                 if (!newedge->l)
409                         fwd = !e->l || !(e->l->v == e->v1);
410                 else
411                         fwd = (newedge->l->v == newedge->v1);
412
413                 
414                 if (fwd) {
415                         verts[0] = e->v1;
416                         verts[1] = e->v2;
417                         verts[2] = newedge->v2;
418                         verts[3] = newedge->v1;
419                 }
420                 else {
421                         verts[3] = e->v1;
422                         verts[2] = e->v2;
423                         verts[1] = newedge->v2;
424                         verts[0] = newedge->v1;
425                 }
426
427                 /* not sure what to do about example face, pass NULL for now */
428                 f = BM_face_create_quad_tri_v(bm, verts, 4, NULL, FALSE);
429                 bm_extrude_copy_face_loop_attributes(bm, f, e, newedge);
430         }
431
432         /* link isolated vert */
433         for (v = BMO_iter_new(&siter, bm, &dupeop, "isovertmap", 0); v; v = BMO_iter_step(&siter)) {
434                 v2 = *((void **)BMO_iter_map_value(&siter));
435                 BM_edge_create(bm, v, v2, v->e, TRUE);
436         }
437
438         /* cleanup */
439         if (delorig) BMO_op_finish(bm, &delop);
440         BMO_op_finish(bm, &dupeop);
441 }
442
443 /*
444  * Compute higher-quality vertex normals used by solidify.
445  * Only considers geometry in the marked solidify region.
446  * Note that this does not work so well for non-manifold
447  * regions.
448  */
449 static void calc_solidify_normals(BMesh *bm)
450 {
451         BMIter viter, eiter, fiter;
452         BMVert *v;
453         BMEdge *e;
454         BMFace *f, *f1, *f2;
455         float edge_normal[3];
456         int i;
457
458         /* can't use BM_edge_face_count because we need to count only marked faces */
459         int *edge_face_count = MEM_callocN(sizeof(int) * bm->totedge, __func__);
460
461         BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
462                 BM_elem_flag_enable(v, BM_ELEM_TAG);
463         }
464
465         BM_mesh_elem_index_ensure(bm, BM_EDGE);
466
467         BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
468                 if (!BMO_elem_flag_test(bm, f, FACE_MARK)) {
469                         continue;
470                 }
471
472                 BM_ITER_ELEM (e, &eiter, f, BM_EDGES_OF_FACE) {
473
474                         /* And mark all edges and vertices on the
475                          * marked faces */
476                         BMO_elem_flag_enable(bm, e, EDGE_MARK);
477                         BMO_elem_flag_enable(bm, e->v1, VERT_MARK);
478                         BMO_elem_flag_enable(bm, e->v2, VERT_MARK);
479                         edge_face_count[BM_elem_index_get(e)]++;
480                 }
481         }
482
483         BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
484                 if (!BMO_elem_flag_test(bm, e, EDGE_MARK)) {
485                         continue;
486                 }
487
488                 i = edge_face_count[BM_elem_index_get(e)]++;
489
490                 if (i == 0 || i > 2) {
491                         /* Edge & vertices are non-manifold even when considering
492                          * only marked faces */
493                         BMO_elem_flag_enable(bm, e, EDGE_NONMAN);
494                         BMO_elem_flag_enable(bm, e->v1, VERT_NONMAN);
495                         BMO_elem_flag_enable(bm, e->v2, VERT_NONMAN);
496                 }
497         }
498         MEM_freeN(edge_face_count);
499         edge_face_count = NULL; /* don't re-use */
500
501         BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
502                 if (!BM_vert_is_manifold(v)) {
503                         BMO_elem_flag_enable(bm, v, VERT_NONMAN);
504                         continue;
505                 }
506
507                 if (BMO_elem_flag_test(bm, v, VERT_MARK)) {
508                         zero_v3(v->no);
509                 }
510         }
511
512         BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
513
514                 /* If the edge is not part of a the solidify region
515                  * its normal should not be considered */
516                 if (!BMO_elem_flag_test(bm, e, EDGE_MARK)) {
517                         continue;
518                 }
519
520                 /* If the edge joins more than two marked faces high
521                  * quality normal computation won't work */
522                 if (BMO_elem_flag_test(bm, e, EDGE_NONMAN)) {
523                         continue;
524                 }
525
526                 f1 = f2 = NULL;
527
528                 BM_ITER_ELEM (f, &fiter, e, BM_FACES_OF_EDGE) {
529                         if (BMO_elem_flag_test(bm, f, FACE_MARK)) {
530                                 if (f1 == NULL) {
531                                         f1 = f;
532                                 }
533                                 else {
534                                         BLI_assert(f2 == NULL);
535                                         f2 = f;
536                                 }
537                         }
538                 }
539
540                 BLI_assert(f1 != NULL);
541
542                 if (f2 != NULL) {
543                         const float angle = angle_normalized_v3v3(f1->no, f2->no);
544
545                         if (angle > 0.0f) {
546                                 /* two faces using this edge, calculate the edge normal
547                                  * using the angle between the faces as a weighting */
548                                 add_v3_v3v3(edge_normal, f1->no, f2->no);
549                                 normalize_v3(edge_normal);
550                                 mul_v3_fl(edge_normal, angle);
551                         }
552                         else {
553                                 /* can't do anything useful here!
554                                  * Set the face index for a vert in case it gets a zero normal */
555                                 BM_elem_flag_disable(e->v1, BM_ELEM_TAG);
556                                 BM_elem_flag_disable(e->v2, BM_ELEM_TAG);
557                                 continue;
558                         }
559                 }
560                 else {
561                         /* only one face attached to that edge */
562                         /* an edge without another attached- the weight on this is
563                          * undefined, M_PI / 2 is 90d in radians and that seems good enough */
564                         copy_v3_v3(edge_normal, f1->no);
565                         mul_v3_fl(edge_normal, M_PI / 2);
566                 }
567
568                 add_v3_v3(e->v1->no, edge_normal);
569                 add_v3_v3(e->v2->no, edge_normal);
570         }
571
572         /* normalize accumulated vertex normal */
573         BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
574                 if (!BMO_elem_flag_test(bm, v, VERT_MARK)) {
575                         continue;
576                 }
577
578                 if (BMO_elem_flag_test(bm, v, VERT_NONMAN)) {
579                         /* use standard normals for vertices connected to non-manifold edges */
580                         BM_vert_normal_update(v);
581                 }
582                 else if (normalize_v3(v->no) == 0.0f && !BM_elem_flag_test(v, BM_ELEM_TAG)) {
583                         /* exceptional case, totally flat. use the normal
584                          * of any marked face around the vertex */
585                         BM_ITER_ELEM (f, &fiter, v, BM_FACES_OF_VERT) {
586                                 if (BMO_elem_flag_test(bm, f, FACE_MARK)) {
587                                         break;
588                                 }
589                         }
590                         copy_v3_v3(v->no, f->no);
591                 }
592         }
593 }
594
595 static void solidify_add_thickness(BMesh *bm, const float dist)
596 {
597         BMFace *f;
598         BMVert *v;
599         BMLoop *l;
600         BMIter iter, loopIter;
601         float *vert_angles = MEM_callocN(sizeof(float) * bm->totvert * 2, "solidify"); /* 2 in 1 */
602         float *vert_accum = vert_angles + bm->totvert;
603         int i, index;
604
605         /* array for passing verts to angle_poly_v3 */
606         float **verts = NULL;
607         BLI_array_staticdeclare(verts, BM_NGON_STACK_SIZE);
608         /* array for receiving angles from angle_poly_v3 */
609         float *face_angles = NULL;
610         BLI_array_staticdeclare(face_angles, BM_NGON_STACK_SIZE);
611
612         BM_mesh_elem_index_ensure(bm, BM_VERT);
613
614         BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
615                 if (!BMO_elem_flag_test(bm, f, FACE_MARK)) {
616                         continue;
617                 }
618
619                 BLI_array_grow_items(verts, f->len);
620                 BM_ITER_ELEM_INDEX (l, &loopIter, f, BM_LOOPS_OF_FACE, i) {
621                         verts[i] = l->v->co;
622                 }
623
624                 BLI_array_grow_items(face_angles, f->len);
625                 angle_poly_v3(face_angles, (const float **)verts, f->len);
626
627                 i = 0;
628                 BM_ITER_ELEM (l, &loopIter, f, BM_LOOPS_OF_FACE) {
629                         v = l->v;
630                         index = BM_elem_index_get(v);
631                         vert_accum[index] += face_angles[i];
632                         vert_angles[index] += shell_angle_to_dist(angle_normalized_v3v3(v->no, f->no)) * face_angles[i];
633                         i++;
634                 }
635
636                 BLI_array_empty(verts);
637                 BLI_array_empty(face_angles);
638         }
639
640         BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
641                 index = BM_elem_index_get(v);
642                 if (vert_accum[index]) { /* zero if unselected */
643                         madd_v3_v3fl(v->co, v->no, dist * (vert_angles[index] / vert_accum[index]));
644                 }
645         }
646
647         MEM_freeN(vert_angles);
648
649         BLI_array_free(verts);
650         BLI_array_free(face_angles);
651 }
652
653 void bmo_solidify_face_region_exec(BMesh *bm, BMOperator *op)
654 {
655         BMOperator extrudeop;
656         BMOperator reverseop;
657         float thickness;
658
659         thickness = BMO_slot_float_get(op, "thickness");
660
661         /* Flip original faces (so the shell is extruded inward) */
662         BMO_op_init(bm, &reverseop, op->flag, "reverse_faces");
663         BMO_slot_copy(op, &reverseop, "geom", "faces");
664         BMO_op_exec(bm, &reverseop);
665         BMO_op_finish(bm, &reverseop);
666
667         /* Extrude the region */
668         BMO_op_initf(bm, &extrudeop, op->flag, "extrude_face_region alwayskeeporig=%b", TRUE);
669         BMO_slot_copy(op, &extrudeop, "geom", "edgefacein");
670         BMO_op_exec(bm, &extrudeop);
671
672         /* Push the verts of the extruded faces inward to create thickness */
673         BMO_slot_buffer_flag_enable(bm, &extrudeop, "geomout", BM_FACE, FACE_MARK);
674         calc_solidify_normals(bm);
675         solidify_add_thickness(bm, thickness);
676
677         BMO_slot_copy(&extrudeop, op, "geomout", "geomout");
678
679         BMO_op_finish(bm, &extrudeop);
680 }