bmesh operator naming - use clearer names for args eg: (mat -> matrix, use_singleedge...
[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, op->slots_in, "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, op->slots_out, "faces.out", 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 The edge this face comes from needs to be from the first and second verts fo the face.
136  * The caller must ensure this else we will copy from the wrong source.
137  */
138 static void bm_extrude_copy_face_loop_attributes(BMesh *bm, BMFace *f)
139 {
140         /* edge we are extruded from */
141         BMLoop *l_first_0 = BM_FACE_FIRST_LOOP(f);
142         BMLoop *l_first_1 = l_first_0->next;
143         BMLoop *l_first_2 = l_first_1->next;
144         BMLoop *l_first_3 = l_first_2->next;
145
146         BMLoop *l_other_0;
147         BMLoop *l_other_1;
148
149         if (UNLIKELY(l_first_0 == l_first_0->radial_next)) {
150                 return;
151         }
152
153         l_other_0 = BM_edge_other_loop(l_first_0->e, l_first_0);
154         l_other_1 = BM_edge_other_loop(l_first_0->e, l_first_1);
155
156         /* copy data */
157         BM_elem_attrs_copy(bm, bm, l_other_0->f, f);
158         BM_elem_flag_disable(f, BM_ELEM_HIDDEN);  /* possibly we copy from a hidden face */
159
160         BM_elem_attrs_copy(bm, bm, l_other_0, l_first_0);
161         BM_elem_attrs_copy(bm, bm, l_other_0, l_first_3);
162
163         BM_elem_attrs_copy(bm, bm, l_other_1, l_first_1);
164         BM_elem_attrs_copy(bm, bm, l_other_1, l_first_2);
165 }
166
167 /* Disable the skin root flag on the input vert, assumes that the vert
168  * data includes an CD_MVERT_SKIN layer */
169 static void bm_extrude_disable_skin_root(BMesh *bm, BMVert *v)
170 {
171         MVertSkin *vs;
172         
173         vs = CustomData_bmesh_get(&bm->vdata, v->head.data, CD_MVERT_SKIN);
174         vs->flag &= ~MVERT_SKIN_ROOT;
175 }
176
177 void bmo_extrude_edge_only_exec(BMesh *bm, BMOperator *op)
178 {
179         BMOIter siter;
180         BMOperator dupeop;
181         BMFace *f;
182         BMEdge *e, *e_new;
183         
184         BMO_ITER (e, &siter, op->slots_in, "edges", BM_EDGE) {
185                 BMO_elem_flag_enable(bm, e, EXT_INPUT);
186                 BMO_elem_flag_enable(bm, e->v1, EXT_INPUT);
187                 BMO_elem_flag_enable(bm, e->v2, EXT_INPUT);
188         }
189
190         BMO_op_initf(bm, &dupeop, op->flag, "duplicate geom=%fve", EXT_INPUT);
191         BMO_op_exec(bm, &dupeop);
192
193         /* disable root flag on all new skin nodes */
194         if (CustomData_has_layer(&bm->vdata, CD_MVERT_SKIN)) {
195                 BMVert *v;
196                 BMO_ITER(v, &siter, dupeop.slots_out, "geom.out", BM_VERT) {
197                         bm_extrude_disable_skin_root(bm, v);
198                 }
199         }
200
201         for (e = BMO_iter_new(&siter, dupeop.slots_out, "boundary_map.out", 0); e; e = BMO_iter_step(&siter)) {
202                 BMVert *f_verts[4];
203                 e_new = *(BMEdge **)BMO_iter_map_value(&siter);
204
205                 if (e->l && e->v1 != e->l->v) {
206                         f_verts[0] = e->v1;
207                         f_verts[1] = e->v2;
208                         f_verts[2] = e_new->v2;
209                         f_verts[3] = e_new->v1;
210                 }
211                 else {
212                         f_verts[0] = e->v2;
213                         f_verts[1] = e->v1;
214                         f_verts[2] = e_new->v1;
215                         f_verts[3] = e_new->v2;
216                 }
217                 /* not sure what to do about example face, pass NULL for now */
218                 f = BM_face_create_quad_tri_v(bm, f_verts, 4, NULL, FALSE);
219                 bm_extrude_copy_face_loop_attributes(bm, f);
220                 
221                 if (BMO_elem_flag_test(bm, e, EXT_INPUT))
222                         e = e_new;
223                 
224                 BMO_elem_flag_enable(bm, f, EXT_KEEP);
225                 BMO_elem_flag_enable(bm, e, EXT_KEEP);
226                 BMO_elem_flag_enable(bm, e->v1, EXT_KEEP);
227                 BMO_elem_flag_enable(bm, e->v2, EXT_KEEP);
228                 
229         }
230
231         BMO_op_finish(bm, &dupeop);
232
233         BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "geom.out", BM_ALL_NOLOOP, EXT_KEEP);
234 }
235
236 void bmo_extrude_vert_indiv_exec(BMesh *bm, BMOperator *op)
237 {
238         BMOIter siter;
239         BMVert *v, *dupev;
240         BMEdge *e;
241         const int has_vskin = CustomData_has_layer(&bm->vdata, CD_MVERT_SKIN);
242
243         for (v = BMO_iter_new(&siter, op->slots_in, "verts", BM_VERT); v; v = BMO_iter_step(&siter)) {
244                 dupev = BM_vert_create(bm, v->co, v);
245                 if (has_vskin)
246                         bm_extrude_disable_skin_root(bm, v);
247
248                 e = BM_edge_create(bm, v, dupev, NULL, FALSE);
249
250                 BMO_elem_flag_enable(bm, e, EXT_KEEP);
251                 BMO_elem_flag_enable(bm, dupev, EXT_KEEP);
252         }
253
254         BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "verts.out", BM_VERT, EXT_KEEP);
255         BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "edges.out", BM_EDGE, EXT_KEEP);
256 }
257
258 void bmo_extrude_face_region_exec(BMesh *bm, BMOperator *op)
259 {
260         BMOperator dupeop, delop;
261         BMOIter siter;
262         BMIter iter, fiter, viter;
263         BMEdge *e, *e_new;
264         BMVert *v, *v2;
265         BMFace *f;
266         int found, fwd, delorig = FALSE;
267         BMOpSlot *slot_facemap_out;
268         BMOpSlot *slot_edges_exclude;
269
270         /* initialize our sub-operators */
271         BMO_op_init(bm, &dupeop, op->flag, "duplicate");
272         
273         BMO_slot_buffer_flag_enable(bm, op->slots_in, "geom", BM_EDGE | BM_FACE, EXT_INPUT);
274         
275         /* if one flagged face is bordered by an un-flagged face, then we delete
276          * original geometry unless caller explicitly asked to keep it. */
277         if (!BMO_slot_bool_get(op->slots_in, "use_keep_orig")) {
278                 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
279
280                         int edge_face_tot;
281
282                         if (!BMO_elem_flag_test(bm, e, EXT_INPUT)) {
283                                 continue;
284                         }
285
286                         found = FALSE; /* found a face that isn't input? */
287                         edge_face_tot = 0; /* edge/face count */
288
289                         BM_ITER_ELEM (f, &fiter, e, BM_FACES_OF_EDGE) {
290                                 if (!BMO_elem_flag_test(bm, f, EXT_INPUT)) {
291                                         found = TRUE;
292                                         delorig = TRUE;
293                                         break;
294                                 }
295
296                                 edge_face_tot++;
297                         }
298
299                         if ((edge_face_tot > 1) && (found == FALSE)) {
300                                 /* edge has a face user, that face isn't extrude input */
301                                 BMO_elem_flag_enable(bm, e, EXT_DEL);
302                         }
303                 }
304         }
305
306         /* calculate verts to delete */
307         BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
308                 found = FALSE;
309
310                 BM_ITER_ELEM (e, &viter, v, BM_EDGES_OF_VERT) {
311                         if (!BMO_elem_flag_test(bm, e, EXT_INPUT) || !BMO_elem_flag_test(bm, e, EXT_DEL)) {
312                                 found = TRUE;
313                                 break;
314                         }
315                 }
316
317                 /* avoid an extra loop */
318                 if (found == TRUE) {
319                         BM_ITER_ELEM (f, &viter, v, BM_FACES_OF_VERT) {
320                                 if (!BMO_elem_flag_test(bm, f, EXT_INPUT)) {
321                                         found = TRUE;
322                                         break;
323                                 }
324                         }
325                 }
326
327                 if (found == FALSE) {
328                         BMO_elem_flag_enable(bm, v, EXT_DEL);
329                 }
330         }
331         
332         BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
333                 if (BMO_elem_flag_test(bm, f, EXT_INPUT)) {
334                         BMO_elem_flag_enable(bm, f, EXT_DEL);
335                 }
336         }
337
338         if (delorig == TRUE) {
339                 BMO_op_initf(bm, &delop, op->flag,
340                              "delete geom=%fvef context=%i",
341                              EXT_DEL, DEL_ONLYTAGGED);
342         }
343
344         BMO_slot_copy(op,      slots_in, "geom",
345                       &dupeop, slots_in, "geom");
346         BMO_op_exec(bm, &dupeop);
347
348         /* disable root flag on all new skin nodes */
349         if (CustomData_has_layer(&bm->vdata, CD_MVERT_SKIN)) {
350                 BMO_ITER(v, &siter, dupeop.slots_out, "geom.out", BM_VERT) {
351                         bm_extrude_disable_skin_root(bm, v);
352                 }
353         }
354
355         slot_facemap_out = BMO_slot_get(dupeop.slots_out, "face_map.out");
356         if (bm->act_face && BMO_elem_flag_test(bm, bm->act_face, EXT_INPUT)) {
357                 bm->act_face = BMO_slot_map_elem_get(slot_facemap_out, bm->act_face);
358         }
359
360         if (delorig) {
361                 BMO_op_exec(bm, &delop);
362         }
363         
364         /* if not delorig, reverse loops of original face */
365         if (!delorig) {
366                 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
367                         if (BMO_elem_flag_test(bm, f, EXT_INPUT)) {
368                                 BM_face_normal_flip(bm, f);
369                         }
370                 }
371         }
372         
373         BMO_slot_copy(&dupeop, slots_out, "geom.out",
374                       op,      slots_out, "geom.out");
375
376         slot_edges_exclude = BMO_slot_get(op->slots_in, "edges_exclude");
377         for (e = BMO_iter_new(&siter, dupeop.slots_out, "boundary_map.out", 0); e; e = BMO_iter_step(&siter)) {
378                 BMVert *f_verts[4];
379
380                 /* this should always be wire, so this is mainly a speedup to avoid map lookup */
381                 if (BM_edge_is_wire(e) && BMO_slot_map_contains(slot_edges_exclude, e)) {
382                         BMVert *v1 = e->v1, *v2 = e->v2;
383
384                         /* The original edge was excluded,
385                          * this would result in a standalone wire edge - see [#30399] */
386                         BM_edge_kill(bm, e);
387
388                         /* kill standalone vertices from this edge - see [#32341] */
389                         if (!v1->e)
390                                 BM_vert_kill(bm, v1);
391                         if (!v2->e)
392                                 BM_vert_kill(bm, v2);
393
394                         continue;
395                 }
396
397                 e_new = *(BMEdge **)BMO_iter_map_value(&siter);
398
399                 if (!e_new) {
400                         continue;
401                 }
402
403                 /* orient loop to give same normal as a loop of newedge
404                  * if it exists (will be an extruded face),
405                  * else same normal as a loop of e, if it exists */
406                 if (!e_new->l)
407                         fwd = !e->l || !(e->l->v == e->v1);
408                 else
409                         fwd = (e_new->l->v == e_new->v1);
410
411                 
412                 if (fwd) {
413                         f_verts[0] = e->v1;
414                         f_verts[1] = e->v2;
415                         f_verts[2] = e_new->v2;
416                         f_verts[3] = e_new->v1;
417                 }
418                 else {
419                         f_verts[0] = e->v2;
420                         f_verts[1] = e->v1;
421                         f_verts[2] = e_new->v1;
422                         f_verts[3] = e_new->v2;
423                 }
424
425                 /* not sure what to do about example face, pass NULL for now */
426                 f = BM_face_create_quad_tri_v(bm, f_verts, 4, NULL, FALSE);
427                 bm_extrude_copy_face_loop_attributes(bm, f);
428         }
429
430         /* link isolated vert */
431         for (v = BMO_iter_new(&siter, dupeop.slots_out, "isovert_map.out", 0); v; v = BMO_iter_step(&siter)) {
432                 v2 = *((void **)BMO_iter_map_value(&siter));
433                 BM_edge_create(bm, v, v2, v->e, TRUE);
434         }
435
436         /* cleanup */
437         if (delorig) BMO_op_finish(bm, &delop);
438         BMO_op_finish(bm, &dupeop);
439 }
440
441 /*
442  * Compute higher-quality vertex normals used by solidify.
443  * Only considers geometry in the marked solidify region.
444  * Note that this does not work so well for non-manifold
445  * regions.
446  */
447 static void calc_solidify_normals(BMesh *bm)
448 {
449         BMIter viter, eiter, fiter;
450         BMVert *v;
451         BMEdge *e;
452         BMFace *f, *f1, *f2;
453         float edge_normal[3];
454         int i;
455
456         /* can't use BM_edge_face_count because we need to count only marked faces */
457         int *edge_face_count = MEM_callocN(sizeof(int) * bm->totedge, __func__);
458
459         BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
460                 BM_elem_flag_enable(v, BM_ELEM_TAG);
461         }
462
463         BM_mesh_elem_index_ensure(bm, BM_EDGE);
464
465         BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
466                 if (!BMO_elem_flag_test(bm, f, FACE_MARK)) {
467                         continue;
468                 }
469
470                 BM_ITER_ELEM (e, &eiter, f, BM_EDGES_OF_FACE) {
471
472                         /* And mark all edges and vertices on the
473                          * marked faces */
474                         BMO_elem_flag_enable(bm, e, EDGE_MARK);
475                         BMO_elem_flag_enable(bm, e->v1, VERT_MARK);
476                         BMO_elem_flag_enable(bm, e->v2, VERT_MARK);
477                         edge_face_count[BM_elem_index_get(e)]++;
478                 }
479         }
480
481         BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
482                 if (!BMO_elem_flag_test(bm, e, EDGE_MARK)) {
483                         continue;
484                 }
485
486                 i = edge_face_count[BM_elem_index_get(e)]++;
487
488                 if (i == 0 || i > 2) {
489                         /* Edge & vertices are non-manifold even when considering
490                          * only marked faces */
491                         BMO_elem_flag_enable(bm, e, EDGE_NONMAN);
492                         BMO_elem_flag_enable(bm, e->v1, VERT_NONMAN);
493                         BMO_elem_flag_enable(bm, e->v2, VERT_NONMAN);
494                 }
495         }
496         MEM_freeN(edge_face_count);
497         edge_face_count = NULL; /* don't re-use */
498
499         BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
500                 if (!BM_vert_is_manifold(v)) {
501                         BMO_elem_flag_enable(bm, v, VERT_NONMAN);
502                         continue;
503                 }
504
505                 if (BMO_elem_flag_test(bm, v, VERT_MARK)) {
506                         zero_v3(v->no);
507                 }
508         }
509
510         BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
511
512                 /* If the edge is not part of a the solidify region
513                  * its normal should not be considered */
514                 if (!BMO_elem_flag_test(bm, e, EDGE_MARK)) {
515                         continue;
516                 }
517
518                 /* If the edge joins more than two marked faces high
519                  * quality normal computation won't work */
520                 if (BMO_elem_flag_test(bm, e, EDGE_NONMAN)) {
521                         continue;
522                 }
523
524                 f1 = f2 = NULL;
525
526                 BM_ITER_ELEM (f, &fiter, e, BM_FACES_OF_EDGE) {
527                         if (BMO_elem_flag_test(bm, f, FACE_MARK)) {
528                                 if (f1 == NULL) {
529                                         f1 = f;
530                                 }
531                                 else {
532                                         BLI_assert(f2 == NULL);
533                                         f2 = f;
534                                 }
535                         }
536                 }
537
538                 BLI_assert(f1 != NULL);
539
540                 if (f2 != NULL) {
541                         const float angle = angle_normalized_v3v3(f1->no, f2->no);
542
543                         if (angle > 0.0f) {
544                                 /* two faces using this edge, calculate the edge normal
545                                  * using the angle between the faces as a weighting */
546                                 add_v3_v3v3(edge_normal, f1->no, f2->no);
547                                 normalize_v3(edge_normal);
548                                 mul_v3_fl(edge_normal, angle);
549                         }
550                         else {
551                                 /* can't do anything useful here!
552                                  * Set the face index for a vert in case it gets a zero normal */
553                                 BM_elem_flag_disable(e->v1, BM_ELEM_TAG);
554                                 BM_elem_flag_disable(e->v2, BM_ELEM_TAG);
555                                 continue;
556                         }
557                 }
558                 else {
559                         /* only one face attached to that edge */
560                         /* an edge without another attached- the weight on this is
561                          * undefined, M_PI / 2 is 90d in radians and that seems good enough */
562                         copy_v3_v3(edge_normal, f1->no);
563                         mul_v3_fl(edge_normal, M_PI / 2);
564                 }
565
566                 add_v3_v3(e->v1->no, edge_normal);
567                 add_v3_v3(e->v2->no, edge_normal);
568         }
569
570         /* normalize accumulated vertex normal */
571         BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
572                 if (!BMO_elem_flag_test(bm, v, VERT_MARK)) {
573                         continue;
574                 }
575
576                 if (BMO_elem_flag_test(bm, v, VERT_NONMAN)) {
577                         /* use standard normals for vertices connected to non-manifold edges */
578                         BM_vert_normal_update(v);
579                 }
580                 else if (normalize_v3(v->no) == 0.0f && !BM_elem_flag_test(v, BM_ELEM_TAG)) {
581                         /* exceptional case, totally flat. use the normal
582                          * of any marked face around the vertex */
583                         BM_ITER_ELEM (f, &fiter, v, BM_FACES_OF_VERT) {
584                                 if (BMO_elem_flag_test(bm, f, FACE_MARK)) {
585                                         break;
586                                 }
587                         }
588                         copy_v3_v3(v->no, f->no);
589                 }
590         }
591 }
592
593 static void solidify_add_thickness(BMesh *bm, const float dist)
594 {
595         BMFace *f;
596         BMVert *v;
597         BMLoop *l;
598         BMIter iter, loopIter;
599         float *vert_angles = MEM_callocN(sizeof(float) * bm->totvert * 2, "solidify"); /* 2 in 1 */
600         float *vert_accum = vert_angles + bm->totvert;
601         int i, index;
602
603         /* array for passing verts to angle_poly_v3 */
604         float **verts = NULL;
605         BLI_array_staticdeclare(verts, BM_DEFAULT_NGON_STACK_SIZE);
606         /* array for receiving angles from angle_poly_v3 */
607         float *face_angles = NULL;
608         BLI_array_staticdeclare(face_angles, BM_DEFAULT_NGON_STACK_SIZE);
609
610         BM_mesh_elem_index_ensure(bm, BM_VERT);
611
612         BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
613                 if (!BMO_elem_flag_test(bm, f, FACE_MARK)) {
614                         continue;
615                 }
616
617                 BLI_array_grow_items(verts, f->len);
618                 BM_ITER_ELEM_INDEX (l, &loopIter, f, BM_LOOPS_OF_FACE, i) {
619                         verts[i] = l->v->co;
620                 }
621
622                 BLI_array_grow_items(face_angles, f->len);
623                 angle_poly_v3(face_angles, (const float **)verts, f->len);
624
625                 i = 0;
626                 BM_ITER_ELEM (l, &loopIter, f, BM_LOOPS_OF_FACE) {
627                         v = l->v;
628                         index = BM_elem_index_get(v);
629                         vert_accum[index] += face_angles[i];
630                         vert_angles[index] += shell_angle_to_dist(angle_normalized_v3v3(v->no, f->no)) * face_angles[i];
631                         i++;
632                 }
633
634                 BLI_array_empty(verts);
635                 BLI_array_empty(face_angles);
636         }
637
638         BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
639                 index = BM_elem_index_get(v);
640                 if (vert_accum[index]) { /* zero if unselected */
641                         madd_v3_v3fl(v->co, v->no, dist * (vert_angles[index] / vert_accum[index]));
642                 }
643         }
644
645         MEM_freeN(vert_angles);
646
647         BLI_array_free(verts);
648         BLI_array_free(face_angles);
649 }
650
651 void bmo_solidify_face_region_exec(BMesh *bm, BMOperator *op)
652 {
653         BMOperator extrudeop;
654         BMOperator reverseop;
655         float thickness;
656
657         thickness = BMO_slot_float_get(op->slots_in, "thickness");
658
659         /* Flip original faces (so the shell is extruded inward) */
660         BMO_op_init(bm, &reverseop, op->flag, "reverse_faces");
661         BMO_slot_copy(op,         slots_in, "geom",
662                       &reverseop, slots_in, "faces");
663         BMO_op_exec(bm, &reverseop);
664         BMO_op_finish(bm, &reverseop);
665
666         /* Extrude the region */
667         BMO_op_initf(bm, &extrudeop, op->flag, "extrude_face_region use_keep_orig=%b", TRUE);
668         BMO_slot_copy(op,         slots_in, "geom",
669                       &extrudeop, slots_in, "geom");
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.slots_out, "geom.out", BM_FACE, FACE_MARK);
674         calc_solidify_normals(bm);
675         solidify_add_thickness(bm, thickness);
676
677         BMO_slot_copy(&extrudeop, slots_out, "geom.out",
678                       op,         slots_out, "geom.out");
679
680         BMO_op_finish(bm, &extrudeop);
681 }