From d8baafd693ebf830d6153bd31bd63521c7569984 Mon Sep 17 00:00:00 2001 From: Pablo Dobarro Date: Tue, 27 Aug 2019 16:19:25 +0200 Subject: Edit Mesh: Poly build tool improvements This commit changes the functionality of the Poly build tool to make it more suitable for retopology tasks: - Click and drag from a boundary edge extrudes a new quad - Click and drag on vertices tweaks the position - Ctrl + click adds geometry. There is a geometry preview in the gizmo. It also can automatically convert triangles to quads. - Shift + click deletes mesh elements (faces or vertices) - Updated preselection code. Different mesh elements take priority depending on the selected action. Reviewed By: campbellbarton Differential Revision: https://developer.blender.org/D5573 --- source/blender/editors/include/ED_mesh.h | 22 ++- source/blender/editors/mesh/editmesh_polybuild.c | 180 ++++++++++++++++-- .../blender/editors/mesh/editmesh_preselect_elem.c | 206 ++++++++++++++++++++- source/blender/editors/mesh/editmesh_select.c | 159 ++++++++++------ source/blender/editors/mesh/mesh_intern.h | 2 + source/blender/editors/mesh/mesh_ops.c | 21 +++ .../space_view3d/view3d_gizmo_preselect_type.c | 126 ++++++++----- 7 files changed, 591 insertions(+), 125 deletions(-) (limited to 'source') diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index 234f36a587b..194378d6bb6 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -194,8 +194,11 @@ bool EDBM_unified_findnearest(struct ViewContext *vc, bool EDBM_unified_findnearest_from_raycast(struct ViewContext *vc, struct Base **bases, const uint bases_len, - bool use_boundary, - int *r_base_index, + bool use_boundary_vertices, + bool use_boundary_edges, + int *r_base_index_vert, + int *r_base_index_edge, + int *r_base_index_face, struct BMVert **r_eve, struct BMEdge **r_eed, struct BMFace **r_efa); @@ -245,15 +248,30 @@ void EDBM_preselect_edgering_update_from_edge(struct EditMesh_PreSelEdgeRing *ps /* editmesh_preselect_elem.c */ struct EditMesh_PreSelElem; +typedef enum eEditMesh_PreSelPreviewAction { + PRESELECT_ACTION_TRANSFORM = 1, + PRESELECT_ACTION_CREATE = 2, + PRESELECT_ACTION_DELETE = 3, +} eEditMesh_PreSelPreviewAction; + struct EditMesh_PreSelElem *EDBM_preselect_elem_create(void); void EDBM_preselect_elem_destroy(struct EditMesh_PreSelElem *psel); void EDBM_preselect_elem_clear(struct EditMesh_PreSelElem *psel); +void EDBM_preselect_preview_clear(struct EditMesh_PreSelElem *psel); void EDBM_preselect_elem_draw(struct EditMesh_PreSelElem *psel, const float matrix[4][4]); void EDBM_preselect_elem_update_from_single(struct EditMesh_PreSelElem *psel, struct BMesh *bm, struct BMElem *ele, const float (*coords)[3]); +void EDBM_preselect_elem_update_preview(struct EditMesh_PreSelElem *psel, + struct ViewContext *vc, + struct BMesh *bm, + struct BMElem *ele, + const int mval[2]); +void EDBM_preselect_action_set(struct EditMesh_PreSelElem *psel, + eEditMesh_PreSelPreviewAction action); +eEditMesh_PreSelPreviewAction EDBM_preselect_action_get(struct EditMesh_PreSelElem *psel); /* mesh_ops.c */ void ED_operatortypes_mesh(void); void ED_operatormacros_mesh(void); diff --git a/source/blender/editors/mesh/editmesh_polybuild.c b/source/blender/editors/mesh/editmesh_polybuild.c index 088d1672cc9..a182bfeb945 100644 --- a/source/blender/editors/mesh/editmesh_polybuild.c +++ b/source/blender/editors/mesh/editmesh_polybuild.c @@ -122,15 +122,151 @@ static bool edbm_preselect_or_active_init_viewcontext(bContext *C, return ok; } +static int edbm_polybuild_transform_at_cursor_invoke(bContext *C, + wmOperator *UNUSED(op), + const wmEvent *UNUSED(event)) +{ + ViewContext vc; + Base *basact = NULL; + BMElem *ele_act = NULL; + edbm_preselect_or_active_init_viewcontext(C, &vc, &basact, &ele_act); + BMEditMesh *em = vc.em; + BMesh *bm = em->bm; + + invert_m4_m4(vc.obedit->imat, vc.obedit->obmat); + ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d); + + edbm_selectmode_ensure(vc.scene, vc.em, SCE_SELECT_VERTEX); + + edbm_flag_disable_all_multi(vc.view_layer, vc.v3d, BM_ELEM_SELECT); + if (ele_act->head.htype == BM_VERT) { + BM_vert_select_set(bm, (BMVert *)ele_act, true); + } + if (ele_act->head.htype == BM_EDGE) { + BM_edge_select_set(bm, (BMEdge *)ele_act, true); + } + if (ele_act->head.htype == BM_FACE) { + BM_face_select_set(bm, (BMFace *)ele_act, true); + } + + EDBM_mesh_normals_update(em); + EDBM_update_generic(em, true, true); + if (basact != NULL) { + if (vc.view_layer->basact != basact) { + ED_object_base_activate(C, basact); + } + } + BM_select_history_store(bm, ele_act); + WM_event_add_mousemove(C); + return OPERATOR_FINISHED; +} + +void MESH_OT_polybuild_transform_at_cursor(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Poly Build Transform at Cursor"; + ot->idname = "MESH_OT_polybuild_transform_at_cursor"; + + /* api callbacks */ + ot->invoke = edbm_polybuild_transform_at_cursor_invoke; + ot->poll = EDBM_view3d_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* to give to transform */ + Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR_DUMMY); +} + +static int edbm_polybuild_delete_at_cursor_invoke(bContext *C, + wmOperator *op, + const wmEvent *UNUSED(event)) +{ + bool changed = false; + + ViewContext vc; + Base *basact = NULL; + BMElem *ele_act = NULL; + edbm_preselect_or_active_init_viewcontext(C, &vc, &basact, &ele_act); + BMEditMesh *em = vc.em; + BMesh *bm = em->bm; + + invert_m4_m4(vc.obedit->imat, vc.obedit->obmat); + ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d); + + edbm_selectmode_ensure(vc.scene, vc.em, SCE_SELECT_VERTEX); + + if (ele_act->head.htype == BM_FACE) { + BMFace *f_act = (BMFace *)ele_act; + EDBM_flag_disable_all(em, BM_ELEM_TAG); + BM_elem_flag_enable(f_act, BM_ELEM_TAG); + if (!EDBM_op_callf(em, op, "delete geom=%hf context=%i", BM_ELEM_TAG, DEL_FACES)) { + return OPERATOR_CANCELLED; + } + changed = true; + } + if (ele_act->head.htype == BM_VERT) { + BMVert *v_act = (BMVert *)ele_act; + if (BM_vert_is_edge_pair(v_act)) { + BM_edge_collapse(bm, v_act->e, v_act, true, true); + changed = true; + } + else { + EDBM_flag_disable_all(em, BM_ELEM_TAG); + BM_elem_flag_enable(v_act, BM_ELEM_TAG); + + if (!EDBM_op_callf(em, + op, + "dissolve_verts verts=%hv use_face_split=%b use_boundary_tear=%b", + BM_ELEM_TAG, + false, + false)) { + return OPERATOR_CANCELLED; + } + changed = true; + } + } + + if (changed) { + EDBM_mesh_normals_update(em); + EDBM_update_generic(em, true, true); + if (basact != NULL) { + if (vc.view_layer->basact != basact) { + ED_object_base_activate(C, basact); + } + } + WM_event_add_mousemove(C); + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } +} + +void MESH_OT_polybuild_delete_at_cursor(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Poly Build Delete at Cursor"; + ot->idname = "MESH_OT_polybuild_delete_at_cursor"; + + /* api callbacks */ + ot->invoke = edbm_polybuild_delete_at_cursor_invoke; + ot->poll = EDBM_view3d_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* to give to transform */ + Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR_DUMMY); +} + /** \} */ /* -------------------------------------------------------------------- */ /** \name Face at Cursor * \{ */ -static int edbm_polybuild_face_at_cursor_invoke(bContext *C, - wmOperator *UNUSED(op), - const wmEvent *event) +static int edbm_polybuild_face_at_cursor_invoke(bContext *C, wmOperator *op, const wmEvent *event) { float center[3]; bool changed = false; @@ -168,20 +304,27 @@ static int edbm_polybuild_face_at_cursor_invoke(bContext *C, mul_m4_v3(vc.obedit->obmat, center); ED_view3d_win_to_3d_int(vc.v3d, vc.ar, center, event->mval, center); mul_m4_v3(vc.obedit->imat, center); - - BMVert *v_tri[3]; - v_tri[0] = e_act->v1; - v_tri[1] = e_act->v2; - v_tri[2] = BM_vert_create(bm, center, NULL, BM_CREATE_NOP); - if (e_act->l && e_act->l->v == v_tri[0]) { - SWAP(BMVert *, v_tri[0], v_tri[1]); + if (f_reference->len == 3 && RNA_boolean_get(op->ptr, "create_quads")) { + const float fac = line_point_factor_v3(center, e_act->v1->co, e_act->v2->co); + BMVert *v_new = BM_edge_split(bm, e_act, e_act->v1, NULL, CLAMPIS(fac, 0.0f, 1.0f)); + copy_v3_v3(v_new->co, center); + edbm_flag_disable_all_multi(vc.view_layer, vc.v3d, BM_ELEM_SELECT); + BM_vert_select_set(bm, v_new, true); + BM_select_history_store(bm, v_new); + } + else { + BMVert *v_tri[3]; + v_tri[0] = e_act->v1; + v_tri[1] = e_act->v2; + v_tri[2] = BM_vert_create(bm, center, NULL, BM_CREATE_NOP); + if (e_act->l && e_act->l->v == v_tri[0]) { + SWAP(BMVert *, v_tri[0], v_tri[1]); + } + BM_face_create_verts(bm, v_tri, 3, f_reference, BM_CREATE_NOP, true); + edbm_flag_disable_all_multi(vc.view_layer, vc.v3d, BM_ELEM_SELECT); + BM_vert_select_set(bm, v_tri[2], true); + BM_select_history_store(bm, v_tri[2]); } - // BMFace *f_new = - BM_face_create_verts(bm, v_tri, 3, f_reference, BM_CREATE_NOP, true); - - edbm_flag_disable_all_multi(vc.view_layer, vc.v3d, BM_ELEM_SELECT); - BM_vert_select_set(bm, v_tri[2], true); - BM_select_history_store(bm, v_tri[2]); changed = true; } else if (ele_act->head.htype == BM_VERT) { @@ -281,6 +424,11 @@ void MESH_OT_polybuild_face_at_cursor(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + RNA_def_boolean(ot->srna, + "create_quads", + true, + "Create quads", + "Automatically split edges in triangles to maintain quad topology"); /* to give to transform */ Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR_DUMMY); } diff --git a/source/blender/editors/mesh/editmesh_preselect_elem.c b/source/blender/editors/mesh/editmesh_preselect_elem.c index a3e684a5493..05c4da68355 100644 --- a/source/blender/editors/mesh/editmesh_preselect_elem.c +++ b/source/blender/editors/mesh/editmesh_preselect_elem.c @@ -75,20 +75,49 @@ struct EditMesh_PreSelElem { float (*verts)[3]; int verts_len; + + float (*preview_tris)[3][3]; + int preview_tris_len; + float (*preview_lines)[2][3]; + int preview_lines_len; + + eEditMesh_PreSelPreviewAction preview_action; }; +void EDBM_preselect_action_set(struct EditMesh_PreSelElem *psel, + eEditMesh_PreSelPreviewAction action) +{ + psel->preview_action = action; +} + +eEditMesh_PreSelPreviewAction EDBM_preselect_action_get(struct EditMesh_PreSelElem *psel) +{ + return psel->preview_action; +} + struct EditMesh_PreSelElem *EDBM_preselect_elem_create(void) { struct EditMesh_PreSelElem *psel = MEM_callocN(sizeof(*psel), __func__); + psel->preview_action = PRESELECT_ACTION_TRANSFORM; return psel; } void EDBM_preselect_elem_destroy(struct EditMesh_PreSelElem *psel) { EDBM_preselect_elem_clear(psel); + EDBM_preselect_preview_clear(psel); MEM_freeN(psel); } +void EDBM_preselect_preview_clear(struct EditMesh_PreSelElem *psel) +{ + MEM_SAFE_FREE(psel->preview_tris); + psel->preview_tris_len = 0; + + MEM_SAFE_FREE(psel->preview_lines); + psel->preview_lines_len = 0; +} + void EDBM_preselect_elem_clear(struct EditMesh_PreSelElem *psel) { MEM_SAFE_FREE(psel->edges); @@ -112,9 +141,42 @@ void EDBM_preselect_elem_draw(struct EditMesh_PreSelElem *psel, const float matr uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); - immUniformColor3ub(255, 0, 255); + + immUniformColor4ub(141, 171, 186, 100); + if (psel->preview_action != PRESELECT_ACTION_TRANSFORM) { + if (psel->preview_tris_len > 0) { + immBegin(GPU_PRIM_TRIS, psel->preview_tris_len * 3); + + for (int i = 0; i < psel->preview_tris_len; i++) { + immVertex3fv(pos, psel->preview_tris[i][0]); + immVertex3fv(pos, psel->preview_tris[i][1]); + immVertex3fv(pos, psel->preview_tris[i][2]); + } + immEnd(); + } + + if (psel->preview_lines_len > 0) { + + immUniformColor4ub(3, 161, 252, 200); + GPU_line_width(2.0f); + immBegin(GPU_PRIM_LINES, psel->preview_lines_len * 2); + for (int i = 0; i < psel->preview_lines_len; i++) { + immVertex3fv(pos, psel->preview_lines[i][0]); + immVertex3fv(pos, psel->preview_lines[i][1]); + } + immEnd(); + } + } + + if (psel->preview_action == PRESELECT_ACTION_DELETE) { + immUniformColor4ub(252, 49, 10, 200); + } + else { + immUniformColor4ub(3, 161, 252, 200); + } if (psel->edges_len > 0) { + GPU_line_width(3.0f); immBegin(GPU_PRIM_LINES, psel->edges_len * 2); for (int i = 0; i < psel->edges_len; i++) { @@ -126,7 +188,7 @@ void EDBM_preselect_elem_draw(struct EditMesh_PreSelElem *psel, const float matr } if (psel->verts_len > 0) { - GPU_point_size(3.0f); + GPU_point_size(4.0f); immBegin(GPU_PRIM_POINTS, psel->verts_len); @@ -167,6 +229,122 @@ static void view3d_preselect_mesh_elem_update_from_edge(struct EditMesh_PreSelEl psel->edges_len = 1; } +static void view3d_preselect_update_preview_triangle_from_vert(struct EditMesh_PreSelElem *psel, + ViewContext *vc, + BMesh *UNUSED(bm), + BMVert *eed, + const int mval[2]) +{ + BMVert *v_act = eed; + BMEdge *e_pair[2] = {NULL}; + float center[3]; + + if (v_act->e != NULL) { + for (uint allow_wire = 0; allow_wire < 2 && (e_pair[1] == NULL); allow_wire++) { + int i = 0; + BMEdge *e_iter = v_act->e; + do { + if ((BM_elem_flag_test(e_iter, BM_ELEM_HIDDEN) == false) && + (allow_wire ? BM_edge_is_wire(e_iter) : BM_edge_is_boundary(e_iter))) { + if (i == 2) { + e_pair[0] = e_pair[1] = NULL; + break; + } + e_pair[i++] = e_iter; + } + } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v_act)) != v_act->e); + } + } + + if (e_pair[1] != NULL) { + mul_v3_m4v3(center, vc->obedit->obmat, v_act->co); + ED_view3d_win_to_3d_int(vc->v3d, vc->ar, center, mval, center); + mul_m4_v3(vc->obedit->imat, center); + + psel->preview_tris = MEM_mallocN(sizeof(*psel->preview_tris) * 2, __func__); + psel->preview_lines = MEM_mallocN(sizeof(*psel->preview_lines) * 4, __func__); + + copy_v3_v3(psel->preview_tris[0][0], e_pair[0]->v1->co); + copy_v3_v3(psel->preview_tris[0][1], e_pair[0]->v2->co); + copy_v3_v3(psel->preview_tris[0][2], center); + + copy_v3_v3(psel->preview_tris[1][0], e_pair[1]->v1->co); + copy_v3_v3(psel->preview_tris[1][1], e_pair[1]->v2->co); + copy_v3_v3(psel->preview_tris[1][2], center); + + copy_v3_v3(psel->preview_lines[0][0], e_pair[0]->v1->co); + copy_v3_v3(psel->preview_lines[0][1], e_pair[0]->v2->co); + + copy_v3_v3(psel->preview_lines[1][0], e_pair[1]->v1->co); + copy_v3_v3(psel->preview_lines[1][1], e_pair[1]->v2->co); + + copy_v3_v3(psel->preview_lines[2][0], center); + if (e_pair[0]->v1 == v_act) { + copy_v3_v3(psel->preview_lines[2][1], e_pair[0]->v2->co); + } + else { + copy_v3_v3(psel->preview_lines[2][1], e_pair[0]->v1->co); + } + + copy_v3_v3(psel->preview_lines[3][0], center); + if (e_pair[1]->v1 == v_act) { + copy_v3_v3(psel->preview_lines[3][1], e_pair[1]->v2->co); + } + else { + copy_v3_v3(psel->preview_lines[3][1], e_pair[1]->v1->co); + } + psel->preview_tris_len = 2; + psel->preview_lines_len = 4; + } +} + +static void view3d_preselect_update_preview_triangle_from_face(struct EditMesh_PreSelElem *psel, + ViewContext *UNUSED(vc), + BMesh *UNUSED(bm), + BMFace *efa, + const int UNUSED(mval[2])) +{ + float(*preview_lines)[2][3] = MEM_mallocN(sizeof(*psel->edges) * efa->len, __func__); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(efa); + int i = 0; + do { + vcos_get_pair(&l_iter->e->v1, preview_lines[i++], NULL); + } while ((l_iter = l_iter->next) != l_first); + psel->preview_lines = preview_lines; + psel->preview_lines_len = efa->len; +} + +static void view3d_preselect_update_preview_triangle_from_edge(struct EditMesh_PreSelElem *psel, + ViewContext *vc, + BMesh *UNUSED(bm), + BMEdge *eed, + const int mval[2]) +{ + float center[3]; + psel->preview_tris = MEM_mallocN(sizeof(*psel->preview_tris), __func__); + psel->preview_lines = MEM_mallocN(sizeof(*psel->preview_lines) * 3, __func__); + mid_v3_v3v3(center, eed->v1->co, eed->v2->co); + mul_m4_v3(vc->obedit->obmat, center); + ED_view3d_win_to_3d_int(vc->v3d, vc->ar, center, mval, center); + mul_m4_v3(vc->obedit->imat, center); + + copy_v3_v3(psel->preview_tris[0][0], eed->v1->co); + copy_v3_v3(psel->preview_tris[0][1], eed->v2->co); + copy_v3_v3(psel->preview_tris[0][2], center); + + copy_v3_v3(psel->preview_lines[0][0], eed->v1->co); + copy_v3_v3(psel->preview_lines[0][1], eed->v2->co); + + copy_v3_v3(psel->preview_lines[1][0], eed->v2->co); + copy_v3_v3(psel->preview_lines[1][1], center); + + copy_v3_v3(psel->preview_lines[2][0], center); + copy_v3_v3(psel->preview_lines[2][1], eed->v1->co); + psel->preview_tris_len = 1; + psel->preview_lines_len = 3; +} + static void view3d_preselect_mesh_elem_update_from_face(struct EditMesh_PreSelElem *psel, BMesh *UNUSED(bm), BMFace *efa, @@ -209,4 +387,28 @@ void EDBM_preselect_elem_update_from_single(struct EditMesh_PreSelElem *psel, } } +void EDBM_preselect_elem_update_preview(struct EditMesh_PreSelElem *psel, + struct ViewContext *vc, + struct BMesh *bm, + struct BMElem *ele, + const int mval[2]) +{ + EDBM_preselect_preview_clear(psel); + + switch (ele->head.htype) { + case BM_VERT: + if (EDBM_preselect_action_get(psel) == PRESELECT_ACTION_CREATE) { + view3d_preselect_update_preview_triangle_from_vert(psel, vc, bm, (BMVert *)ele, mval); + } + break; + case BM_EDGE: + view3d_preselect_update_preview_triangle_from_edge(psel, vc, bm, (BMEdge *)ele, mval); + break; + case BM_FACE: + view3d_preselect_update_preview_triangle_from_face(psel, vc, bm, (BMFace *)ele, mval); + break; + default: + BLI_assert(0); + } +} /** \} */ diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index f1055103d16..4d511d45642 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -1448,8 +1448,11 @@ bool EDBM_unified_findnearest(ViewContext *vc, bool EDBM_unified_findnearest_from_raycast(ViewContext *vc, Base **bases, const uint bases_len, - bool use_boundary, - int *r_base_index, + bool use_boundary_vertices, + bool use_boundary_edges, + int *r_base_index_vert, + int *r_base_index_edge, + int *r_base_index_face, struct BMVert **r_eve, struct BMEdge **r_eed, struct BMFace **r_efa) @@ -1463,9 +1466,27 @@ bool EDBM_unified_findnearest_from_raycast(ViewContext *vc, BMElem *ele; } best = {0, NULL}; + struct { + uint base_index; + BMElem *ele; + } best_vert = {0, NULL}; + + struct { + uint base_index; + BMElem *ele; + } best_edge = {0, NULL}; + + struct { + uint base_index; + BMElem *ele; + } best_face = {0, NULL}; + if (ED_view3d_win_to_ray_clipped( vc->depsgraph, vc->ar, vc->v3d, mval_fl, ray_origin, ray_direction, true)) { float dist_sq_best = FLT_MAX; + float dist_sq_best_vert = FLT_MAX; + float dist_sq_best_edge = FLT_MAX; + float dist_sq_best_face = FLT_MAX; const bool use_vert = (r_eve != NULL); const bool use_edge = (r_eed != NULL); @@ -1495,18 +1516,23 @@ bool EDBM_unified_findnearest_from_raycast(ViewContext *vc, BM_mesh_elem_index_ensure(bm, BM_VERT); } - if (use_boundary && (use_vert || use_edge)) { + if ((use_boundary_vertices || use_boundary_edges) && (use_vert || use_edge)) { BMEdge *e; BMIter eiter; BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) { if ((BM_elem_flag_test(e, BM_ELEM_HIDDEN) == false) && (BM_edge_is_boundary(e))) { - if (use_vert) { + if (use_vert && use_boundary_vertices) { for (uint j = 0; j < 2; j++) { BMVert *v = *((&e->v1) + j); float point[3]; mul_v3_m4v3(point, obedit->obmat, coords ? coords[BM_elem_index_get(v)] : v->co); const float dist_sq_test = dist_squared_to_ray_v3_normalized( ray_origin, ray_direction, point); + if (dist_sq_test < dist_sq_best_vert) { + dist_sq_best_vert = dist_sq_test; + best_vert.base_index = base_index; + best_vert.ele = (BMElem *)v; + } if (dist_sq_test < dist_sq_best) { dist_sq_best = dist_sq_test; best.base_index = base_index; @@ -1515,7 +1541,7 @@ bool EDBM_unified_findnearest_from_raycast(ViewContext *vc, } } - if (use_edge) { + if (use_edge && use_boundary_edges) { float point[3]; #if 0 const float dist_sq_test = dist_squared_ray_to_seg_v3( @@ -1531,6 +1557,11 @@ bool EDBM_unified_findnearest_from_raycast(ViewContext *vc, mul_m4_v3(obedit->obmat, point); const float dist_sq_test = dist_squared_to_ray_v3_normalized( ray_origin, ray_direction, point); + if (dist_sq_test < dist_sq_best_edge) { + dist_sq_best_edge = dist_sq_test; + best_edge.base_index = base_index; + best_edge.ele = (BMElem *)e; + } if (dist_sq_test < dist_sq_best) { dist_sq_best = dist_sq_test; best.base_index = base_index; @@ -1541,46 +1572,55 @@ bool EDBM_unified_findnearest_from_raycast(ViewContext *vc, } } } - else { - /* Non boundary case. */ - if (use_vert) { - BMVert *v; - BMIter viter; - BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) { - if (BM_elem_flag_test(v, BM_ELEM_HIDDEN) == false) { - float point[3]; - mul_v3_m4v3(point, obedit->obmat, v->co); - const float dist_sq_test = dist_squared_to_ray_v3_normalized( - ray_origin, ray_direction, v->co); - if (dist_sq_test < dist_sq_best) { - dist_sq_best = dist_sq_test; - best.base_index = base_index; - best.ele = (BMElem *)v; - } + /* Non boundary case. */ + if (use_vert && !use_boundary_vertices) { + BMVert *v; + BMIter viter; + BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) { + if (BM_elem_flag_test(v, BM_ELEM_HIDDEN) == false) { + float point[3]; + mul_v3_m4v3(point, obedit->obmat, coords ? coords[BM_elem_index_get(v)] : v->co); + const float dist_sq_test = dist_squared_to_ray_v3_normalized( + ray_origin, ray_direction, point); + if (dist_sq_test < dist_sq_best_vert) { + dist_sq_best_vert = dist_sq_test; + best_vert.base_index = base_index; + best_vert.ele = (BMElem *)v; + } + if (dist_sq_test < dist_sq_best) { + dist_sq_best = dist_sq_test; + best.base_index = base_index; + best.ele = (BMElem *)v; } } } - if (use_edge) { - BMEdge *e; - BMIter eiter; - BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(e, BM_ELEM_HIDDEN) == false) { - float point[3]; - if (coords) { - mid_v3_v3v3( - point, coords[BM_elem_index_get(e->v1)], coords[BM_elem_index_get(e->v2)]); - } - else { - mid_v3_v3v3(point, e->v1->co, e->v2->co); - } - mul_m4_v3(obedit->obmat, point); - const float dist_sq_test = dist_squared_to_ray_v3_normalized( - ray_origin, ray_direction, point); - if (dist_sq_test < dist_sq_best) { - dist_sq_best = dist_sq_test; - best.base_index = base_index; - best.ele = (BMElem *)e; - } + } + + if (use_edge && !use_boundary_edges) { + BMEdge *e; + BMIter eiter; + BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) { + if (BM_elem_flag_test(e, BM_ELEM_HIDDEN) == false) { + float point[3]; + if (coords) { + mid_v3_v3v3( + point, coords[BM_elem_index_get(e->v1)], coords[BM_elem_index_get(e->v2)]); + } + else { + mid_v3_v3v3(point, e->v1->co, e->v2->co); + } + mul_m4_v3(obedit->obmat, point); + const float dist_sq_test = dist_squared_to_ray_v3_normalized( + ray_origin, ray_direction, point); + if (dist_sq_test < dist_sq_best_edge) { + dist_sq_best_edge = dist_sq_test; + best_edge.base_index = base_index; + best_edge.ele = (BMElem *)e; + } + if (dist_sq_test < dist_sq_best) { + dist_sq_best = dist_sq_test; + best.base_index = base_index; + best.ele = (BMElem *)e; } } } @@ -1601,6 +1641,11 @@ bool EDBM_unified_findnearest_from_raycast(ViewContext *vc, mul_m4_v3(obedit->obmat, point); const float dist_sq_test = dist_squared_to_ray_v3_normalized( ray_origin, ray_direction, point); + if (dist_sq_test < dist_sq_best_face) { + dist_sq_best_face = dist_sq_test; + best_face.base_index = base_index; + best_face.ele = (BMElem *)f; + } if (dist_sq_test < dist_sq_best) { dist_sq_best = dist_sq_test; best.base_index = base_index; @@ -1612,7 +1657,10 @@ bool EDBM_unified_findnearest_from_raycast(ViewContext *vc, } } - *r_base_index = best.base_index; + *r_base_index_vert = best_vert.base_index; + *r_base_index_edge = best_edge.base_index; + *r_base_index_face = best_face.base_index; + if (r_eve) { *r_eve = NULL; } @@ -1623,22 +1671,17 @@ bool EDBM_unified_findnearest_from_raycast(ViewContext *vc, *r_efa = NULL; } - if (best.ele) { - switch (best.ele->head.htype) { - case BM_VERT: - *r_eve = (BMVert *)best.ele; - break; - case BM_EDGE: - *r_eed = (BMEdge *)best.ele; - break; - case BM_FACE: - *r_efa = (BMFace *)best.ele; - break; - default: - BLI_assert(0); - } + if (best_vert.ele) { + *r_eve = (BMVert *)best_vert.ele; + } + if (best_edge.ele) { + *r_eed = (BMEdge *)best_edge.ele; } - return (best.ele != NULL); + if (best_face.ele) { + *r_efa = (BMFace *)best_face.ele; + } + + return (best_vert.ele != NULL || best_edge.ele != NULL || best_face.ele != NULL); } /** \} */ diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index 1a03879ed17..8332cb71f95 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -122,6 +122,8 @@ void MESH_GGT_spin_redo(struct wmGizmoGroupType *gzgt); void MESH_OT_polybuild_face_at_cursor(struct wmOperatorType *ot); void MESH_OT_polybuild_split_at_cursor(struct wmOperatorType *ot); void MESH_OT_polybuild_dissolve_at_cursor(struct wmOperatorType *ot); +void MESH_OT_polybuild_transform_at_cursor(struct wmOperatorType *ot); +void MESH_OT_polybuild_delete_at_cursor(struct wmOperatorType *ot); /* *** editmesh_inset.c *** */ void MESH_OT_inset(struct wmOperatorType *ot); diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c index 102ce3efc22..28c55afbf2e 100644 --- a/source/blender/editors/mesh/mesh_ops.c +++ b/source/blender/editors/mesh/mesh_ops.c @@ -148,6 +148,8 @@ void ED_operatortypes_mesh(void) WM_operatortype_append(MESH_OT_polybuild_face_at_cursor); WM_operatortype_append(MESH_OT_polybuild_split_at_cursor); WM_operatortype_append(MESH_OT_polybuild_dissolve_at_cursor); + WM_operatortype_append(MESH_OT_polybuild_transform_at_cursor); + WM_operatortype_append(MESH_OT_polybuild_delete_at_cursor); WM_operatortype_append(MESH_OT_uv_texture_add); WM_operatortype_append(MESH_OT_uv_texture_remove); @@ -334,6 +336,25 @@ void ED_operatormacros_mesh(void) otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false); RNA_boolean_set(otmacro->ptr, "mirror", false); + + ot = WM_operatortype_append_macro("MESH_OT_polybuild_transform_at_cursor_move", + "Transform at Cursor Move", + "", + OPTYPE_UNDO | OPTYPE_REGISTER); + WM_operatortype_macro_define(ot, "MESH_OT_polybuild_transform_at_cursor"); + otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); + RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false); + RNA_boolean_set(otmacro->ptr, "mirror", false); + + ot = WM_operatortype_append_macro("MESH_OT_polybuild_extrude_at_cursor_move", + "Extrude at Cursor Move", + "", + OPTYPE_UNDO | OPTYPE_REGISTER); + WM_operatortype_macro_define(ot, "MESH_OT_polybuild_transform_at_cursor"); + otmacro = WM_operatortype_macro_define(ot, "MESH_OT_extrude_edges_indiv"); + otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); + RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false); + RNA_boolean_set(otmacro->ptr, "mirror", false); } /* note mesh keymap also for other space? */ diff --git a/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c b/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c index a984e339305..bfc5956bb94 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c @@ -77,7 +77,20 @@ static void gizmo_preselect_elem_draw(const bContext *UNUSED(C), wmGizmo *gz) static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int mval[2]) { + wmEvent *event = CTX_wm_window(C)->eventstate; MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz; + + /* Hack: Switch action mode based on key input */ + const bool is_ctrl_pressed = WM_event_modifier_flag(event) & KM_CTRL; + const bool is_shift_pressed = WM_event_modifier_flag(event) & KM_SHIFT; + EDBM_preselect_action_set(gz_ele->psel, PRESELECT_ACTION_TRANSFORM); + if (is_ctrl_pressed && !is_shift_pressed) { + EDBM_preselect_action_set(gz_ele->psel, PRESELECT_ACTION_CREATE); + } + if (!is_ctrl_pressed && is_shift_pressed) { + EDBM_preselect_action_set(gz_ele->psel, PRESELECT_ACTION_DELETE); + } + struct { Object *ob; BMElem *ele; @@ -87,18 +100,6 @@ static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int .dist = ED_view3d_select_dist_px(), }; - struct { - int base_index; - int vert_index; - int edge_index; - int face_index; - } prev = { - .base_index = gz_ele->base_index, - .vert_index = gz_ele->vert_index, - .edge_index = gz_ele->edge_index, - .face_index = gz_ele->face_index, - }; - { ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); @@ -115,32 +116,66 @@ static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int { /* TODO: support faces. */ - int base_index = -1; + int base_index_vert = -1; + int base_index_edge = -1; + int base_index_face = -1; BMVert *eve_test; BMEdge *eed_test; + BMFace *efa_test; if (EDBM_unified_findnearest_from_raycast(&vc, gz_ele->bases, gz_ele->bases_len, + false, true, - &base_index, + &base_index_vert, + &base_index_edge, + &base_index_face, &eve_test, &eed_test, - NULL)) { - Base *base = gz_ele->bases[base_index]; - best.ob = base->object; - if (eve_test) { - best.ele = (BMElem *)eve_test; - } - else if (eed_test) { - best.ele = (BMElem *)eed_test; + &efa_test)) { + if (EDBM_preselect_action_get(gz_ele->psel) == PRESELECT_ACTION_DELETE) { + /* Delete action */ + if (efa_test) { + best.ele = (BMElem *)efa_test; + best.base_index = base_index_face; + } } + else { - BLI_assert(0); + /* Transform and create action */ + if (eed_test) { + best.ele = (BMElem *)eed_test; + best.base_index = base_index_edge; + } + } + + /* All actions use same vertex preselection */ + /* Retopology should always prioritize edge preselection. Only preselct a vertex when the + * cursor is really close to it*/ + if (eve_test) { + BMVert *vert = (BMVert *)eve_test; + float vert_p_co[3], vert_co[3]; + float mval_f[2] = {UNPACK2(vc.mval)}; + mul_v3_m4v3(vert_co, gz_ele->bases[base_index_vert]->object->obmat, vert->co); + ED_view3d_project(vc.ar, vert_co, vert_p_co); + float len = len_v2v2(vert_p_co, mval_f); + if (len < 35) { + best.ele = (BMElem *)eve_test; + best.base_index = base_index_vert; + } + if (!BM_vert_is_boundary(vert) && + EDBM_preselect_action_get(gz_ele->psel) != PRESELECT_ACTION_DELETE) { + best.ele = (BMElem *)eve_test; + best.base_index = base_index_vert; + } } - best.base_index = base_index; + /* Check above should never fail, if it does it's an internal error. */ BLI_assert(best.base_index != -1); + + Base *base = gz_ele->bases[best.base_index]; + best.ob = base->object; } } @@ -167,32 +202,30 @@ static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int } } - if ((prev.base_index == gz_ele->base_index) && (prev.vert_index == gz_ele->vert_index) && - (prev.edge_index == gz_ele->edge_index) && (prev.face_index == gz_ele->face_index)) { - /* pass (only recalculate on change) */ - } - else { - if (best.ele) { - const float(*coords)[3] = NULL; - { - Object *ob = gz_ele->bases[gz_ele->base_index]->object; - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Mesh *me_eval = (Mesh *)DEG_get_evaluated_id(depsgraph, ob->data); - if (me_eval->runtime.edit_data) { - coords = me_eval->runtime.edit_data->vertexCos; - } + if (best.ele) { + const float(*coords)[3] = NULL; + { + Object *ob = gz_ele->bases[gz_ele->base_index]->object; + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Mesh *me_eval = (Mesh *)DEG_get_evaluated_id(depsgraph, ob->data); + if (me_eval->runtime.edit_data) { + coords = me_eval->runtime.edit_data->vertexCos; } - EDBM_preselect_elem_update_from_single(gz_ele->psel, bm, best.ele, coords); - } - else { - EDBM_preselect_elem_clear(gz_ele->psel); } + EDBM_preselect_elem_update_from_single(gz_ele->psel, bm, best.ele, coords); + EDBM_preselect_elem_update_preview(gz_ele->psel, &vc, bm, best.ele, mval); + } + else { + EDBM_preselect_elem_clear(gz_ele->psel); + EDBM_preselect_preview_clear(gz_ele->psel); + } - RNA_int_set(gz->ptr, "object_index", gz_ele->base_index); - RNA_int_set(gz->ptr, "vert_index", gz_ele->vert_index); - RNA_int_set(gz->ptr, "edge_index", gz_ele->edge_index); - RNA_int_set(gz->ptr, "face_index", gz_ele->face_index); + RNA_int_set(gz->ptr, "object_index", gz_ele->base_index); + RNA_int_set(gz->ptr, "vert_index", gz_ele->vert_index); + RNA_int_set(gz->ptr, "edge_index", gz_ele->edge_index); + RNA_int_set(gz->ptr, "face_index", gz_ele->face_index); + if (best.ele) { ARegion *ar = CTX_wm_region(C); ED_region_tag_redraw(ar); } @@ -471,5 +504,4 @@ void ED_view3d_gizmo_mesh_preselect_get_active(bContext *C, } } } - /** \} */ -- cgit v1.2.3