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