bmesh: face creation from a single selected vertex/edge, now extends the selection along wire/boundary edges and makes a face.
Selection is specifically so you can continuously fill in holes by tapping the Fkey. Similar functionality to the F2 addon, however the mouse location isn't used.
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user