diff options
-rw-r--r-- | source/blender/bmesh/intern/bmesh_operator_api.h | 2 | ||||
-rw-r--r-- | source/blender/bmesh/intern/bmesh_operators.c | 2 | ||||
-rw-r--r-- | source/blender/editors/mesh/editmesh_tools.c | 152 |
3 files changed, 152 insertions, 4 deletions
diff --git a/source/blender/bmesh/intern/bmesh_operator_api.h b/source/blender/bmesh/intern/bmesh_operator_api.h index fd6571d136e..978aec0c610 100644 --- a/source/blender/bmesh/intern/bmesh_operator_api.h +++ b/source/blender/bmesh/intern/bmesh_operator_api.h @@ -451,7 +451,7 @@ typedef struct BMOIter { char restrictmask; /* bitwise '&' with BMHeader.htype */ } BMOIter; -void *BMO_slot_buffer_elem_first(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name); +void *BMO_slot_buffer_get_first(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name); void *BMO_iter_new(BMOIter *iter, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, diff --git a/source/blender/bmesh/intern/bmesh_operators.c b/source/blender/bmesh/intern/bmesh_operators.c index 00f8dbe06bf..085b8b9bc80 100644 --- a/source/blender/bmesh/intern/bmesh_operators.c +++ b/source/blender/bmesh/intern/bmesh_operators.c @@ -1355,7 +1355,7 @@ static void bmo_flag_layer_clear(BMesh *bm) bm->elem_index_dirty &= ~(BM_VERT | BM_EDGE | BM_FACE); } -void *BMO_slot_buffer_elem_first(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name) +void *BMO_slot_buffer_get_first(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name) { BMOpSlot *slot = BMO_slot_get(slot_args, slot_name); diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index ffedef6c0b5..5da01daff55 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -76,6 +76,8 @@ #include "mesh_intern.h" +#define USE_FACE_CREATE_SEL_EXTEND + #define MVAL_PIXEL_MARGIN 5.0f /* allow accumulated normals to form a new direction but don't @@ -1098,6 +1100,129 @@ static int edbm_add_edge_face__smooth_get(BMesh *bm) return (vote_on_smooth[0] < vote_on_smooth[1]); } +#ifdef USE_FACE_CREATE_SEL_EXTEND +/** + * Function used to get a fixed number of edges linked to a vertex that passes a test function. + * This is used so we can request all boundary edges connected to a vertex for eg. + */ +static int edbm_add_edge_face_exec__vert_edge_lookup(BMVert *v, BMEdge *e_used, BMEdge **e_arr, const int e_arr_len, + bool (* func)(BMEdge *)) +{ + BMIter iter; + BMEdge *e_iter; + int i = 0; + BM_ITER_ELEM (e_iter, &iter, v, BM_EDGES_OF_VERT) { + if (BM_elem_flag_test(e_iter, BM_ELEM_HIDDEN) == false) { + if ((e_used == NULL) || (e_used != e_iter)) { + if (func(e_iter)) { + e_arr[i++] = e_iter; + if (i >= e_arr_len) { + break; + } + } + } + } + } + return i; +} + +static BMElem *edbm_add_edge_face_exec__tricky_extend_sel(BMesh *bm) +{ + BMIter iter; + bool found = false; + + if (bm->totvertsel == 1 && bm->totedgesel == 0 && bm->totfacesel == 0) { + /* first look for 2 boundary edges */ + BMVert *v; + + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { + found = true; + break; + } + } + + if (found) { + BMEdge *ed_pair[3] = {NULL}; + if ( + ((edbm_add_edge_face_exec__vert_edge_lookup(v, NULL, ed_pair, 3, BM_edge_is_wire) == 2) && + (BM_edge_share_face_check(ed_pair[0], ed_pair[1]) == false)) || + + ((edbm_add_edge_face_exec__vert_edge_lookup(v, NULL, ed_pair, 3, BM_edge_is_boundary) == 2) && + (BM_edge_share_face_check(ed_pair[0], ed_pair[1]) == false)) + ) + { + BM_edge_select_set(bm, ed_pair[0], true); + BM_edge_select_set(bm, ed_pair[1], true); + return (BMElem *)v; + } + } + } + else if (bm->totvertsel == 2 && bm->totedgesel == 1 && bm->totfacesel == 0) { + /* first look for 2 boundary edges */ + BMEdge *e; + + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + if (BM_elem_flag_test(e, BM_ELEM_SELECT)) { + found = true; + break; + } + } + if (found) { + BMEdge *ed_pair_a[2] = {NULL}; + BMEdge *ed_pair_b[2] = {NULL}; + if ( + ((edbm_add_edge_face_exec__vert_edge_lookup(e->v1, e, ed_pair_a, 2, BM_edge_is_wire) == 1) && + (edbm_add_edge_face_exec__vert_edge_lookup(e->v2, e, ed_pair_b, 2, BM_edge_is_wire) == 1) && + (BM_edge_share_face_check(e, ed_pair_a[0]) == false) && + (BM_edge_share_face_check(e, ed_pair_b[0]) == false)) || + + ((edbm_add_edge_face_exec__vert_edge_lookup(e->v1, e, ed_pair_a, 2, BM_edge_is_boundary) == 1) && + (edbm_add_edge_face_exec__vert_edge_lookup(e->v2, e, ed_pair_b, 2, BM_edge_is_boundary) == 1) && + (BM_edge_share_face_check(e, ed_pair_a[0]) == false) && + (BM_edge_share_face_check(e, ed_pair_b[0]) == false)) + ) + { + BM_edge_select_set(bm, ed_pair_a[0], true); + BM_edge_select_set(bm, ed_pair_b[0], true); + return (BMElem *)e; + } + } + } + + return NULL; +} +static void edbm_add_edge_face_exec__tricky_finalize_sel(BMesh *bm, BMElem *ele_desel, BMFace *f) +{ + /* now we need to find the edge that isnt connected to this element */ + BM_select_history_clear(bm); + + if (ele_desel->head.htype == BM_VERT) { + BMLoop *l = BM_face_vert_share_loop(f, (BMVert *)ele_desel); + BLI_assert(f->len == 3); + BM_face_select_set(bm, f, false); + BM_vert_select_set(bm, (BMVert *)ele_desel, false); + + BM_edge_select_set(bm, l->next->e, true); + BM_select_history_store(bm, l->next->e); + } + else { + BMLoop *l = BM_face_edge_share_loop(f, (BMEdge *)ele_desel); + BLI_assert(f->len == 4 || f->len == 3); + BM_face_select_set(bm, f, false); + BM_edge_select_set(bm, (BMEdge *)ele_desel, false); + if (f->len == 4) { + BM_edge_select_set(bm, l->next->next->e, true); + BM_select_history_store(bm, l->next->next->e); + } + else { + BM_vert_select_set(bm, l->next->next->v, true); + BM_select_history_store(bm, l->next->next->v); + } + } +} +#endif /* USE_FACE_CREATE_SEL_EXTEND */ + static int edbm_add_edge_face_exec(bContext *C, wmOperator *op) { BMOperator bmop; @@ -1106,6 +1231,15 @@ static int edbm_add_edge_face_exec(bContext *C, wmOperator *op) const short use_smooth = edbm_add_edge_face__smooth_get(em->bm); /* when this is used to dissolve we could avoid this, but checking isnt too slow */ +#ifdef USE_FACE_CREATE_SEL_EXTEND + BMElem *ele_desel; + BMFace *ele_desel_face; + + /* be extra clever, figure out if a partial selection should be extended so we can create geometry + * with single vert or single edge selection */ + ele_desel = edbm_add_edge_face_exec__tricky_extend_sel(em->bm); +#endif + if (!EDBM_op_init(em, &bmop, op, "contextual_create geom=%hfev mat_nr=%i use_smooth=%b", BM_ELEM_SELECT, em->mat_nr, use_smooth)) @@ -1114,8 +1248,22 @@ static int edbm_add_edge_face_exec(bContext *C, wmOperator *op) } BMO_op_exec(em->bm, &bmop); - BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, TRUE); - BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_SELECT, TRUE); + +#ifdef USE_FACE_CREATE_SEL_EXTEND + /* normally we would want to leave the new geometry selected, + * but being able to press F many times to add geometry is too useful! */ + if (ele_desel && + (BMO_slot_buffer_count(bmop.slots_out, "faces.out") == 1) && + (ele_desel_face = BMO_slot_buffer_get_first(bmop.slots_out, "faces.out"))) + { + edbm_add_edge_face_exec__tricky_finalize_sel(em->bm, ele_desel, ele_desel_face); + } + else +#endif + { + BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true); + BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_SELECT, true); + } if (!EDBM_op_finish(em, &bmop, op, TRUE)) { return OPERATOR_CANCELLED; |