From 20634fd433b07bbd90358a625792292e9581a0f6 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sun, 9 Sep 2018 16:11:02 +1000 Subject: Tool System: use preselect highlight w/ poly-build - Poly build now uses a new gizmo for pre-selection which has the same behavior as loop-cut. This replaces hack where mouse-move set the active element which was no longer working properly because of missing depsgraph updates. - Multi-object support for poly-build. - Support for deformed cage. - Fix error where changing active object wasn't properly refreshing the preselect gizmo (for loopcut too). Currently holding Alt to select non-boundary element's isn't working. --- .../startup/bl_ui/space_toolsystem_toolbar.py | 4 +- source/blender/editors/include/ED_mesh.h | 19 ++ source/blender/editors/mesh/CMakeLists.txt | 1 + source/blender/editors/mesh/editmesh_polybuild.c | 321 +++++++++------------ .../blender/editors/mesh/editmesh_preselect_elem.c | 218 ++++++++++++++ source/blender/editors/mesh/editmesh_select.c | 221 ++++++++++++++ source/blender/editors/mesh/mesh_ops.c | 1 - source/blender/editors/space_view3d/space_view3d.c | 1 + .../editors/space_view3d/view3d_gizmo_preselect.c | 48 +++ .../space_view3d/view3d_gizmo_preselect_type.c | 220 +++++++++++++- .../blender/editors/space_view3d/view3d_intern.h | 1 + 11 files changed, 862 insertions(+), 193 deletions(-) create mode 100644 source/blender/editors/mesh/editmesh_preselect_elem.c diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index f65a77ce3bf..293c7ecb975 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -572,7 +572,7 @@ class _defs_edit_mesh: return dict( text="Poly Build", icon="ops.mesh.polybuild_hover", - widget=None, + widget="VIEW3D_GGT_mesh_preselect_elem", keymap=( ("mesh.polybuild_face_at_cursor_move", dict(TRANSFORM_OT_translate=dict(release_confirm=True)), @@ -581,8 +581,6 @@ class _defs_edit_mesh: dict(TRANSFORM_OT_translate=dict(release_confirm=True)), dict(type='ACTIONMOUSE', value='PRESS', ctrl=True)), ("mesh.polybuild_dissolve_at_cursor", dict(), dict(type='ACTIONMOUSE', value='CLICK', alt=True)), - ("mesh.polybuild_hover", dict(use_boundary=False), dict(type='MOUSEMOVE', value='ANY', alt=True)), - ("mesh.polybuild_hover", dict(use_boundary=True), dict(type='MOUSEMOVE', value='ANY', any=True)), ), ) diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index c084033bbb6..92338aa508f 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -52,6 +52,7 @@ struct Mesh; struct UvVertMap; struct UvMapVert; struct BMEditMesh; +struct BMElem; struct BMesh; struct BMVert; struct BMLoop; @@ -177,6 +178,14 @@ bool EDBM_unified_findnearest( struct BMEdge **r_eed, struct BMFace **r_efa); +bool EDBM_unified_findnearest_from_raycast( + struct ViewContext *vc, + bool use_boundary, + struct Base **r_base, + struct BMVert **r_eve, + struct BMEdge **r_eed, + struct BMFace **r_efa); + bool EDBM_select_pick(struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle); void EDBM_selectmode_set(struct BMEditMesh *em); @@ -210,6 +219,16 @@ void EDBM_preselect_edgering_update_from_edge( struct EditMesh_PreSelEdgeRing *psel, struct BMesh *bm, struct BMEdge *eed_start, int previewlines, const float (*coords)[3]); +/* editmesh_preselect_elem.c */ +struct EditMesh_PreSelElem; +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_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]); + /* mesh_ops.c */ void ED_operatortypes_mesh(void); void ED_operatormacros_mesh(void); diff --git a/source/blender/editors/mesh/CMakeLists.txt b/source/blender/editors/mesh/CMakeLists.txt index 4784f07c297..43360af9e18 100644 --- a/source/blender/editors/mesh/CMakeLists.txt +++ b/source/blender/editors/mesh/CMakeLists.txt @@ -58,6 +58,7 @@ set(SRC editmesh_path.c editmesh_polybuild.c editmesh_preselect.c + editmesh_preselect_elem.c editmesh_rip.c editmesh_rip_edge.c editmesh_select.c diff --git a/source/blender/editors/mesh/editmesh_polybuild.c b/source/blender/editors/mesh/editmesh_polybuild.c index 9b7d460973a..adf5802cdcf 100644 --- a/source/blender/editors/mesh/editmesh_polybuild.c +++ b/source/blender/editors/mesh/editmesh_polybuild.c @@ -25,6 +25,8 @@ * an experimental tool for quickly constructing/manipulating faces. */ +#include "MEM_guardedalloc.h" + #include "DNA_object_types.h" #include "BLI_math.h" @@ -33,10 +35,13 @@ #include "BKE_report.h" #include "BKE_editmesh.h" #include "BKE_mesh.h" +#include "BKE_layer.h" #include "WM_types.h" +#include "ED_object.h" #include "ED_mesh.h" +#include "ED_scene.h" #include "ED_screen.h" #include "ED_transform.h" #include "ED_view3d.h" @@ -50,6 +55,8 @@ #include "WM_api.h" +#include "DEG_depsgraph.h" + /* -------------------------------------------------------------------- */ /** \name Local Utilities * \{ */ @@ -63,6 +70,98 @@ static void edbm_selectmode_ensure(Scene *scene, BMEditMesh *em, short selectmod } } +/* Could make public, for now just keep here. */ +static void edbm_flag_disable_all_multi(ViewLayer *view_layer, const char hflag) +{ + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob_iter = objects[ob_index]; + BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter); + BMesh *bm_iter = em_iter->bm; + if (bm_iter->totvertsel) { + EDBM_flag_disable_all(em_iter, hflag); + if (hflag & BM_ELEM_SELECT) { + BM_select_history_clear(em_iter->bm); + } + DEG_id_tag_update(ob_iter->data, DEG_TAG_SELECT_UPDATE); + } + } + MEM_freeN(objects); +} + +/* When accessed as a tool, get the active edge from the preselection gizmo. */ +static bool edbm_preselect_or_active( + bContext *C, + Base **r_base, + BMElem **r_ele) +{ + ViewLayer *view_layer = CTX_data_view_layer(C); + ARegion *ar = CTX_wm_region(C); + wmGizmoMap *gzmap = ar->gizmo_map; + wmGizmoGroup *gzgroup = gzmap ? WM_gizmomap_group_find(gzmap, "VIEW3D_GGT_mesh_preselect_elem") : NULL; + if (gzgroup != NULL) { + wmGizmo *gz = gzgroup->gizmos.first; + const int object_index = RNA_int_get(gz->ptr, "object_index"); + + /* weak, allocate an array just to access the index. */ + Base *base = NULL; + Object *obedit = NULL; + { + uint bases_len; + Base **bases = BKE_view_layer_array_from_bases_in_edit_mode(view_layer, &bases_len); + if (object_index < bases_len) { + base = bases[object_index]; + obedit = base->object; + } + MEM_freeN(bases); + } + + *r_base = base; + *r_ele = NULL; + + if (obedit) { + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + const int vert_index = RNA_int_get(gz->ptr, "vert_index"); + const int edge_index = RNA_int_get(gz->ptr, "edge_index"); + const int face_index = RNA_int_get(gz->ptr, "face_index"); + if (vert_index != -1) { + *r_ele = (BMElem *)BM_vert_at_index_find(bm, vert_index); + } + else if (edge_index != -1) { + *r_ele = (BMElem *)BM_edge_at_index_find(bm, edge_index); + } + else if (face_index != -1) { + *r_ele = (BMElem *)BM_face_at_index_find(bm, face_index); + } + } + } + else { + Base *base = view_layer->basact; + Object *obedit = base->object; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + *r_base = base; + *r_ele = BM_mesh_active_elem_get(bm); + } + return (*r_ele != NULL); +} + +static bool edbm_preselect_or_active_init_viewcontext( + bContext *C, + ViewContext *vc, + Base **r_base, + BMElem **r_ele) +{ + em_setup_viewcontext(C, vc); + bool ok = edbm_preselect_or_active(C, r_base, r_ele); + if (ok) { + ED_view3d_viewcontext_init_object(vc, (*r_base)->object); + } + return ok; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -72,15 +171,15 @@ static void edbm_selectmode_ensure(Scene *scene, BMEditMesh *em, short selectmod static int edbm_polybuild_face_at_cursor_invoke( bContext *C, wmOperator *UNUSED(op), const wmEvent *event) { - ViewContext vc; float center[3]; bool changed = false; - em_setup_viewcontext(C, &vc); - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); + 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; - BMElem *ele_act = BM_mesh_active_elem_get(bm); invert_m4_m4(vc.obedit->imat, vc.obedit->obmat); ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d); @@ -95,7 +194,7 @@ static int edbm_polybuild_face_at_cursor_invoke( mul_m4_v3(vc.obedit->imat, center); BMVert *v_new = BM_vert_create(bm, center, NULL, BM_CREATE_NOP); - EDBM_flag_disable_all(em, BM_ELEM_SELECT); + edbm_flag_disable_all_multi(vc.view_layer, BM_ELEM_SELECT); BM_vert_select_set(bm, v_new, true); changed = true; } @@ -118,7 +217,7 @@ static int edbm_polybuild_face_at_cursor_invoke( // BMFace *f_new = BM_face_create_verts(bm, v_tri, 3, f_reference, BM_CREATE_NOP, true); - EDBM_flag_disable_all(em, BM_ELEM_SELECT); + edbm_flag_disable_all_multi(vc.view_layer, BM_ELEM_SELECT); BM_vert_select_set(bm, v_tri[2], true); changed = true; } @@ -169,7 +268,7 @@ static int edbm_polybuild_face_at_cursor_invoke( // BMFace *f_new = BM_face_create_verts(bm, v_quad, 4, f_reference, BM_CREATE_NOP, true); - EDBM_flag_disable_all(em, BM_ELEM_SELECT); + edbm_flag_disable_all_multi(vc.view_layer, BM_ELEM_SELECT); BM_vert_select_set(bm, v_quad[2], true); changed = true; } @@ -188,11 +287,13 @@ static int edbm_polybuild_face_at_cursor_invoke( } if (changed) { - BM_select_history_clear(bm); - EDBM_mesh_normals_update(em); EDBM_update_generic(em, true, true); + if (vc.view_layer->basact != basact) { + ED_object_base_activate(C, basact); + } + WM_event_add_mousemove(C); return OPERATOR_FINISHED; @@ -229,13 +330,14 @@ void MESH_OT_polybuild_face_at_cursor(wmOperatorType *ot) static int edbm_polybuild_split_at_cursor_invoke( bContext *C, wmOperator *UNUSED(op), const wmEvent *event) { - ViewContext vc; float center[3]; bool changed = false; - em_setup_viewcontext(C, &vc); - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); + 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); @@ -243,8 +345,6 @@ static int edbm_polybuild_split_at_cursor_invoke( edbm_selectmode_ensure(vc.scene, vc.em, SCE_SELECT_VERTEX); - BMElem *ele_act = BM_mesh_active_elem_get(bm); - if (ele_act == NULL || ele_act->head.hflag == BM_FACE) { return OPERATOR_PASS_THROUGH; } @@ -259,7 +359,7 @@ static int edbm_polybuild_split_at_cursor_invoke( 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(em, BM_ELEM_SELECT); + edbm_flag_disable_all_multi(vc.view_layer, BM_ELEM_SELECT); BM_vert_select_set(bm, v_new, true); changed = true; } @@ -269,13 +369,15 @@ static int edbm_polybuild_split_at_cursor_invoke( } if (changed) { - BM_select_history_clear(bm); - EDBM_mesh_normals_update(em); EDBM_update_generic(em, true, true); WM_event_add_mousemove(C); + if (vc.view_layer->basact != basact) { + ED_object_base_activate(C, basact); + } + return OPERATOR_FINISHED; } else { @@ -312,23 +414,19 @@ void MESH_OT_polybuild_split_at_cursor(wmOperatorType *ot) static int edbm_polybuild_dissolve_at_cursor_invoke( bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - ViewContext vc; - em_setup_viewcontext(C, &vc); bool changed = false; - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); + 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; - BMVert *v_act = BM_mesh_active_vert_get(bm); - BMEdge *e_act = BM_mesh_active_edge_get(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 (e_act) { + if (ele_act->head.htype == BM_EDGE) { + BMEdge *e_act = (BMEdge *)ele_act; BMLoop *l_a, *l_b; if (BM_edge_loop_pair(e_act, &l_a, &l_b)) { BMFace *f_new = BM_faces_join_pair(bm, l_a, l_b, true); @@ -337,7 +435,8 @@ static int edbm_polybuild_dissolve_at_cursor_invoke( } } } - else if (v_act) { + else 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, @@ -356,13 +455,15 @@ static int edbm_polybuild_dissolve_at_cursor_invoke( } if (changed) { - EDBM_flag_disable_all(em, BM_ELEM_SELECT); - - BM_select_history_clear(bm); + edbm_flag_disable_all_multi(vc.view_layer, BM_ELEM_SELECT); EDBM_mesh_normals_update(em); EDBM_update_generic(em, true, true); + if (vc.view_layer->basact != basact) { + ED_object_base_activate(C, basact); + } + WM_event_add_mousemove(C); return OPERATOR_FINISHED; @@ -388,155 +489,3 @@ void MESH_OT_polybuild_dissolve_at_cursor(wmOperatorType *ot) } /** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Cursor Gizmo - * - * \note This may need its own file, for now not. - * \{ */ - -static BMElem *edbm_hover_preselect( - bContext *C, - const int mval[2], - bool use_boundary) -{ - ViewContext vc; - - em_setup_viewcontext(C, &vc); - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMesh *bm = em->bm; - - invert_m4_m4(vc.obedit->imat, vc.obedit->obmat); - ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d); - - const float mval_fl[2] = {UNPACK2(mval)}; - float ray_origin[3], ray_direction[3]; - - BMElem *ele_best = NULL; - - if (ED_view3d_win_to_ray( - CTX_data_depsgraph(C), - vc.ar, vc.v3d, mval_fl, - ray_origin, ray_direction, true)) - { - BMEdge *e; - - BMIter eiter; - float dist_sq_best = FLT_MAX; - - BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) { - if ((BM_elem_flag_test(e, BM_ELEM_HIDDEN) == false) && - (!use_boundary || BM_edge_is_boundary(e))) - { - float dist_sq_test; - float point[3]; - float depth; -#if 0 - dist_sq_test = dist_squared_ray_to_seg_v3( - ray_origin, ray_direction, - e->v1->co, e->v2->co, - point, &depth); -#else - mid_v3_v3v3(point, e->v1->co, e->v2->co); - dist_sq_test = dist_squared_to_ray_v3( - ray_origin, ray_direction, - point, &depth); -#endif - - if (dist_sq_test < dist_sq_best) { - dist_sq_best = dist_sq_test; - ele_best = (BMElem *)e; - } - - dist_sq_test = dist_squared_to_ray_v3( - ray_origin, ray_direction, - e->v1->co, &depth); - if (dist_sq_test < dist_sq_best) { - dist_sq_best = dist_sq_test; - ele_best = (BMElem *)e->v1; - } - dist_sq_test = dist_squared_to_ray_v3( - ray_origin, ray_direction, - e->v2->co, &depth); - if (dist_sq_test < dist_sq_best) { - dist_sq_best = dist_sq_test; - ele_best = (BMElem *)e->v2; - } - } - } - } - return ele_best; -} - -/* - * Developer note: this is not advocating pre-selection highlighting. - * This is just a quick way to test how a tool for interactively editing polygons may work. */ -static int edbm_polybuild_hover_invoke( - bContext *C, wmOperator *op, const wmEvent *event) -{ - const bool use_boundary = RNA_boolean_get(op->ptr, "use_boundary"); - ViewContext vc; - - em_setup_viewcontext(C, &vc); - - /* Vertex selection is needed */ - if ((vc.scene->toolsettings->selectmode & SCE_SELECT_VERTEX) == 0) { - return OPERATOR_PASS_THROUGH; - } - - /* Don't overwrite click-drag events. */ - if (use_boundary == false) { - /* pass */ - } - else if (vc.win->tweak || - (vc.win->eventstate->check_click && - vc.win->eventstate->prevval == KM_PRESS && - ISMOUSE(vc.win->eventstate->prevtype))) - { - return OPERATOR_PASS_THROUGH; - } - - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMesh *bm = em->bm; - BMElem *ele_active = BM_mesh_active_elem_get(bm); - BMElem *ele_hover = edbm_hover_preselect(C, event->mval, use_boundary); - - if (ele_hover && (ele_hover != ele_active)) { - if (event->shift == 0) { - EDBM_flag_disable_all(em, BM_ELEM_SELECT); - BM_select_history_clear(bm); - } - BM_elem_select_set(bm, ele_hover, true); - BM_select_history_store(em->bm, ele_hover); - BKE_mesh_batch_cache_dirty_tag(obedit->data, BKE_MESH_BATCH_DIRTY_SELECT); - - ED_region_tag_redraw(vc.ar); - - return OPERATOR_FINISHED; - } - else { - return OPERATOR_CANCELLED; - } -} - -void MESH_OT_polybuild_hover(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Poly Build Hover"; - ot->idname = "MESH_OT_polybuild_hover"; - ot->description = ""; - - /* api callbacks */ - ot->invoke = edbm_polybuild_hover_invoke; - ot->poll = EDBM_view3d_poll; - - /* flags */ - ot->flag = OPTYPE_INTERNAL; - - /* properties */ - RNA_def_boolean(ot->srna, "use_boundary", false, "Boundary", "Select only boundary geometry"); -} - -/** \} */ diff --git a/source/blender/editors/mesh/editmesh_preselect_elem.c b/source/blender/editors/mesh/editmesh_preselect_elem.c new file mode 100644 index 00000000000..e0b06019de1 --- /dev/null +++ b/source/blender/editors/mesh/editmesh_preselect_elem.c @@ -0,0 +1,218 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/mesh/editmesh_preselect_elem.c + * \ingroup edmesh + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_stack.h" +#include "BLI_math.h" + +#include "BKE_context.h" +#include "BKE_editmesh.h" + +#include "GPU_immediate.h" +#include "GPU_matrix.h" +#include "GPU_state.h" + +#include "DNA_object_types.h" + +#include "ED_mesh.h" +#include "ED_view3d.h" + +/* -------------------------------------------------------------------- */ +/** \name Mesh Element Pre-Select + * Public API: + * + * #EDBM_preselect_elem_create + * #EDBM_preselect_elem_destroy + * #EDBM_preselect_elem_clear + * #EDBM_preselect_elem_draw + * #EDBM_preselect_elem_update_from_single + * + * \{ */ + +static void vcos_get(BMVert *v, float r_co[3], const float (*coords)[3]) +{ + if (coords) { + copy_v3_v3(r_co, coords[BM_elem_index_get(v)]); + } + else { + copy_v3_v3(r_co, v->co); + } +} + +static void vcos_get_pair(BMVert *v[2], float r_cos[2][3], const float (*coords)[3]) +{ + if (coords) { + for (int j = 0; j < 2; j++) { + copy_v3_v3(r_cos[j], coords[BM_elem_index_get(v[j])]); + } + } + else { + for (int j = 0; j < 2; j++) { + copy_v3_v3(r_cos[j], v[j]->co); + } + } +} + +struct EditMesh_PreSelElem { + float (*edges)[2][3]; + int edges_len; + + float (*verts)[3]; + int verts_len; +}; + +struct EditMesh_PreSelElem *EDBM_preselect_elem_create(void) +{ + struct EditMesh_PreSelElem *psel = MEM_callocN(sizeof(*psel), __func__); + return psel; +} + +void EDBM_preselect_elem_destroy( + struct EditMesh_PreSelElem *psel) +{ + EDBM_preselect_elem_clear(psel); + MEM_freeN(psel); +} + +void EDBM_preselect_elem_clear( + struct EditMesh_PreSelElem *psel) +{ + MEM_SAFE_FREE(psel->edges); + psel->edges_len = 0; + + MEM_SAFE_FREE(psel->verts); + psel->verts_len = 0; +} + +void EDBM_preselect_elem_draw( + struct EditMesh_PreSelElem *psel, const float matrix[4][4]) +{ + if ((psel->edges_len == 0) && (psel->verts_len == 0)) { + return; + } + + GPU_depth_test(false); + + GPU_matrix_push(); + GPU_matrix_mul(matrix); + + 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); + + if (psel->edges_len > 0) { + immBegin(GPU_PRIM_LINES, psel->edges_len * 2); + + for (int i = 0; i < psel->edges_len; i++) { + immVertex3fv(pos, psel->edges[i][0]); + immVertex3fv(pos, psel->edges[i][1]); + } + + immEnd(); + } + + if (psel->verts_len > 0) { + GPU_point_size(3.0f); + + immBegin(GPU_PRIM_POINTS, psel->verts_len); + + for (int i = 0; i < psel->verts_len; i++) { + immVertex3fv(pos, psel->verts[i]); + } + + immEnd(); + } + + immUnbindProgram(); + + GPU_matrix_pop(); + + /* Reset default */ + GPU_depth_test(true); +} + +static void view3d_preselect_mesh_elem_update_from_vert( + struct EditMesh_PreSelElem *psel, + BMesh *UNUSED(bm), BMVert *eve, const float (*coords)[3]) +{ + float (*verts)[3] = MEM_mallocN(sizeof(*psel->verts), __func__); + vcos_get(eve, verts[0], coords); + psel->verts = verts; + psel->verts_len = 1; +} + +static void view3d_preselect_mesh_elem_update_from_edge( + struct EditMesh_PreSelElem *psel, + BMesh *UNUSED(bm), BMEdge *eed, const float (*coords)[3]) +{ + float (*edges)[2][3] = MEM_mallocN(sizeof(*psel->edges), __func__); + vcos_get_pair(&eed->v1, edges[0], coords); + psel->edges = edges; + psel->edges_len = 1; +} + +static void view3d_preselect_mesh_elem_update_from_face( + struct EditMesh_PreSelElem *psel, + BMesh *UNUSED(bm), BMFace *efa, const float (*coords)[3]) +{ + float (*edges)[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, edges[i++], coords); + } while ((l_iter = l_iter->next) != l_first); + psel->edges = edges; + psel->edges_len = efa->len; +} + +void EDBM_preselect_elem_update_from_single( + struct EditMesh_PreSelElem *psel, + BMesh *bm, BMElem *ele, + const float (*coords)[3]) +{ + EDBM_preselect_elem_clear(psel); + + if (coords) { + BM_mesh_elem_index_ensure(bm, BM_VERT); + } + + switch (ele->head.htype) { + case BM_VERT: + view3d_preselect_mesh_elem_update_from_vert(psel, bm, (BMVert *)ele, coords); + break; + case BM_EDGE: + view3d_preselect_mesh_elem_update_from_edge(psel, bm, (BMEdge *)ele, coords); + break; + case BM_FACE: + view3d_preselect_mesh_elem_update_from_face(psel, bm, (BMFace *)ele, coords); + break; + default: + BLI_assert(0); + } +} + +/** \} */ diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index df8c095e7bd..86a1366bda3 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -1097,6 +1097,227 @@ bool EDBM_unified_findnearest( /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Alternate Find Nearest Vert/Edge (optional boundary) + * + * \note This uses ray-cast method instead of backbuffer, + * currently used for poly-build. + * \{ */ + +bool EDBM_unified_findnearest_from_raycast( + ViewContext *vc, + bool use_boundary, + Base **r_base, + struct BMVert **r_eve, + struct BMEdge **r_eed, + struct BMFace **r_efa) +{ + + const float mval_fl[2] = {UNPACK2(vc->mval)}; + float ray_origin[3], ray_direction[3]; + + struct { + Base *base; + BMElem *ele; + } best = {NULL}; + + if (ED_view3d_win_to_ray( + vc->depsgraph, + vc->ar, vc->v3d, mval_fl, + ray_origin, ray_direction, true)) + { + float dist_sq_best = FLT_MAX; + + const bool use_vert = (r_eve != NULL); + const bool use_edge = (r_eed != NULL); + const bool use_face = (r_efa != NULL); + + uint bases_len; + Base **bases = BKE_view_layer_array_from_bases_in_edit_mode(vc->view_layer, &bases_len); + for (uint base_index = 0; base_index < bases_len; base_index++) { + Base *base_iter = bases[base_index]; + Object *obedit = base_iter->object; + + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + float imat3[3][3]; + + ED_view3d_viewcontext_init_object(vc, obedit); + copy_m3_m4(imat3, obedit->obmat); + invert_m3(imat3); + + const float (*coords)[3] = NULL; + { + Mesh *me_eval = (Mesh *)DEG_get_evaluated_id(vc->depsgraph, obedit->data); + if (me_eval->runtime.edit_data) { + coords = me_eval->runtime.edit_data->vertexCos; + } + } + + if (coords != NULL) { + BM_mesh_elem_index_ensure(bm, BM_VERT); + } + + if (use_boundary && (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))) + { + float depth; + + if (use_vert) { + 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( + ray_origin, ray_direction, + point, &depth); + if (dist_sq_test < dist_sq_best) { + dist_sq_best = dist_sq_test; + best.base = base_iter; + best.ele = (BMElem *)v; + } + } + } + + if (use_edge) { + float point[3]; +#if 0 + const float dist_sq_test = dist_squared_ray_to_seg_v3( + ray_origin, ray_direction, + e->v1->co, e->v2->co, + point, &depth); +#else + 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( + ray_origin, ray_direction, + point, &depth); + if (dist_sq_test < dist_sq_best) { + dist_sq_best = dist_sq_test; + best.base = base_iter; + best.ele = (BMElem *)e; + } +#endif + } + } + } + } + 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); + float depth; + const float dist_sq_test = dist_squared_to_ray_v3( + ray_origin, ray_direction, + v->co, &depth); + if (dist_sq_test < dist_sq_best) { + dist_sq_best = dist_sq_test; + best.base = base_iter; + 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); + float depth; + const float dist_sq_test = dist_squared_to_ray_v3( + ray_origin, ray_direction, + point, &depth); + if (dist_sq_test < dist_sq_best) { + dist_sq_best = dist_sq_test; + best.base = base_iter; + best.ele = (BMElem *)e; + } + } + } + } + } + + if (use_face) { + BMFace *f; + BMIter fiter; + BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(f, BM_ELEM_HIDDEN) == false) { + float point[3]; + if (coords) { + BM_face_calc_center_mean_vcos(bm, f, point, coords); + } + else { + BM_face_calc_center_mean(f, point); + } + mul_m4_v3(obedit->obmat, point); + float depth; + const float dist_sq_test = dist_squared_to_ray_v3( + ray_origin, ray_direction, + point, &depth); + if (dist_sq_test < dist_sq_best) { + dist_sq_best = dist_sq_test; + best.base = base_iter; + best.ele = (BMElem *)f; + } + } + } + } + } + } + + *r_base = best.base; + if (r_eve) { + *r_eve = NULL; + } + if (r_eed) { + *r_eed = NULL; + } + if (r_efa) { + *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); + } + } + return (best.ele != NULL); +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Select Similar (Vert/Edge/Face) Operator * \{ */ diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c index acd8079e75a..d1a3aec9551 100644 --- a/source/blender/editors/mesh/mesh_ops.c +++ b/source/blender/editors/mesh/mesh_ops.c @@ -157,7 +157,6 @@ 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_hover); WM_operatortype_append(MESH_OT_uv_texture_add); WM_operatortype_append(MESH_OT_uv_texture_remove); diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index a8e530540a1..c1c097b6e3d 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -702,6 +702,7 @@ static void view3d_widgets(void) WM_gizmogrouptype_append(TRANSFORM_GGT_gizmo); WM_gizmogrouptype_append(VIEW3D_GGT_xform_cage); + WM_gizmogrouptype_append(VIEW3D_GGT_mesh_preselect_elem); WM_gizmogrouptype_append(VIEW3D_GGT_mesh_preselect_edgering); WM_gizmogrouptype_append(VIEW3D_GGT_ruler); diff --git a/source/blender/editors/space_view3d/view3d_gizmo_preselect.c b/source/blender/editors/space_view3d/view3d_gizmo_preselect.c index aebad326a57..e7a35c39bf3 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_preselect.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_preselect.c @@ -40,6 +40,54 @@ #include "view3d_intern.h" /* own include */ +/* -------------------------------------------------------------------- */ +/** \name Mesh Pre-Select Element Gizmo + * + * \{ */ + +struct GizmoGroupPreSelElem { + wmGizmo *gizmo; +}; + +static bool WIDGETGROUP_mesh_preselect_elem_poll(const bContext *C, wmGizmoGroupType *gzgt) +{ + bToolRef_Runtime *tref_rt = WM_toolsystem_runtime_from_context((bContext *)C); + if ((tref_rt == NULL) || + !STREQ(gzgt->idname, tref_rt->gizmo_group)) + { + WM_gizmo_group_type_unlink_delayed_ptr(gzgt); + return false; + } + return true; +} + +static void WIDGETGROUP_mesh_preselect_elem_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup) +{ + const wmGizmoType *gzt_presel = WM_gizmotype_find("GIZMO_GT_preselect_elem_3d", true); + struct GizmoGroupPreSelElem *man = MEM_callocN(sizeof(struct GizmoGroupPreSelElem), __func__); + gzgroup->customdata = man; + + wmGizmo *gz = man->gizmo = WM_gizmo_new_ptr(gzt_presel, gzgroup, NULL); + UI_GetThemeColor3fv(TH_GIZMO_PRIMARY, gz->color); + UI_GetThemeColor3fv(TH_GIZMO_HI, gz->color_hi); +} + +void VIEW3D_GGT_mesh_preselect_elem(wmGizmoGroupType *gzgt) +{ + gzgt->name = "Mesh Preselect Element"; + gzgt->idname = "VIEW3D_GGT_mesh_preselect_elem"; + + gzgt->flag = WM_GIZMOGROUPTYPE_3D; + + gzgt->gzmap_params.spaceid = SPACE_VIEW3D; + gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW; + + gzgt->poll = WIDGETGROUP_mesh_preselect_elem_poll; + gzgt->setup = WIDGETGROUP_mesh_preselect_elem_setup; +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Mesh Pre-Select Edge Ring Gizmo * 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 fffca6c0887..33de6621db3 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c @@ -54,6 +54,207 @@ #include "ED_view3d.h" #include "ED_gizmo_library.h" +/* -------------------------------------------------------------------- */ +/** \name Mesh Element (Vert/Edge/Face) Pre-Select Gizmo API + * + * \{ */ + + +typedef struct MeshElemGizmo3D { + wmGizmo gizmo; + Object **objects; + uint objects_len; + int object_index; + int vert_index; + int edge_index; + int face_index; + struct EditMesh_PreSelElem *psel; +} MeshElemGizmo3D; + +static void gizmo_preselect_elem_draw(const bContext *UNUSED(C), wmGizmo *gz) +{ + MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz; + if (gz_ele->object_index != -1) { + Object *ob = gz_ele->objects[gz_ele->object_index]; + EDBM_preselect_elem_draw(gz_ele->psel, ob->obmat); + } +} + +static int gizmo_preselect_elem_test_select( + bContext *C, wmGizmo *gz, const int mval[2]) +{ + MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz; + struct { + Object *ob; + BMElem *ele; + float dist; + int ob_index; + } best = { + .dist = ED_view3d_select_dist_px(), + }; + + struct { + int object_index; + int vert_index; + int edge_index; + int face_index; + } prev = { + .object_index = gz_ele->object_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); + if (((gz_ele->objects)) == NULL || + (gz_ele->objects[0] != OBEDIT_FROM_VIEW_LAYER(view_layer))) + { + gz_ele->objects = BKE_view_layer_array_from_objects_in_edit_mode( + view_layer, &gz_ele->objects_len); + } + } + + ViewContext vc; + em_setup_viewcontext(C, &vc); + copy_v2_v2_int(vc.mval, mval); + + { + /* TODO: support faces. */ + Base *base = NULL; + BMVert *eve_test; + BMEdge *eed_test; + + if (EDBM_unified_findnearest_from_raycast(&vc, true, &base, &eve_test, &eed_test, NULL)) { + best.ob = base->object; + if (eve_test) { + best.ele = (BMElem *)eve_test; + } + else if (eed_test) { + best.ele = (BMElem *)eed_test; + } + else { + BLI_assert(0); + } + best.ob_index = -1; + /* weak, we could ensure the arrays are aligned, + * or allow EDBM_unified_findnearest_from_raycast to take an array arg. */ + for (int ob_index = 0; ob_index < gz_ele->objects_len; ob_index++) { + if (best.ob == gz_ele->objects[ob_index]) { + best.ob_index = ob_index; + break; + } + } + /* Check above should never fail, if it does it's an internal error. */ + BLI_assert(best.ob_index != -1); + } + } + + BMesh *bm = NULL; + + gz_ele->object_index = -1; + gz_ele->vert_index = -1; + gz_ele->edge_index = -1; + gz_ele->face_index = -1; + + if (best.ele) { + gz_ele->object_index = best.ob_index; + bm = BKE_editmesh_from_object(gz_ele->objects[gz_ele->object_index])->bm; + BM_mesh_elem_index_ensure(bm, best.ele->head.htype); + + if (best.ele->head.htype == BM_VERT) { + gz_ele->vert_index = BM_elem_index_get(best.ele); + } + else if (best.ele->head.htype == BM_EDGE) { + gz_ele->edge_index = BM_elem_index_get(best.ele); + } + else if (best.ele->head.htype == BM_FACE) { + gz_ele->face_index = BM_elem_index_get(best.ele); + } + } + + if ((prev.object_index == gz_ele->object_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->objects[gz_ele->object_index]; + Depsgraph *depsgraph = CTX_data_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); + } + + RNA_int_set(gz->ptr, "object_index", gz_ele->object_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); + + ARegion *ar = CTX_wm_region(C); + ED_region_tag_redraw(ar); + } + + // return best.eed ? 0 : -1; + return -1; +} + +static void gizmo_preselect_elem_setup(wmGizmo *gz) +{ + MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz; + if (gz_ele->psel == NULL) { + gz_ele->psel = EDBM_preselect_elem_create(); + } + gz_ele->object_index = -1; +} + +static void gizmo_preselect_elem_free(wmGizmo *gz) +{ + MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz; + EDBM_preselect_elem_destroy(gz_ele->psel); + gz_ele->psel = NULL; + MEM_SAFE_FREE(gz_ele->objects); +} + +static int gizmo_preselect_elem_invoke( + bContext *UNUSED(C), wmGizmo *UNUSED(gz), const wmEvent *UNUSED(event)) +{ + return OPERATOR_PASS_THROUGH; +} + +static void GIZMO_GT_preselect_elem_3d(wmGizmoType *gzt) +{ + /* identifiers */ + gzt->idname = "GIZMO_GT_preselect_elem_3d"; + + /* api callbacks */ + gzt->invoke = gizmo_preselect_elem_invoke; + gzt->draw = gizmo_preselect_elem_draw; + gzt->test_select = gizmo_preselect_elem_test_select; + gzt->setup = gizmo_preselect_elem_setup; + gzt->free = gizmo_preselect_elem_free; + + gzt->struct_size = sizeof(MeshElemGizmo3D); + + RNA_def_int(gzt->srna, "object_index", -1, -1, INT_MAX, "Object Index", "", -1, INT_MAX); + RNA_def_int(gzt->srna, "vert_index", -1, -1, INT_MAX, "Vert Index", "", -1, INT_MAX); + RNA_def_int(gzt->srna, "edge_index", -1, -1, INT_MAX, "Edge Index", "", -1, INT_MAX); + RNA_def_int(gzt->srna, "face_index", -1, -1, INT_MAX, "Face Index", "", -1, INT_MAX); +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Mesh Edge-Ring Pre-Select Gizmo API * @@ -98,10 +299,14 @@ static int gizmo_preselect_edgering_test_select( .edge_index = gz_ring->edge_index, }; - if (gz_ring->objects == NULL) { + { ViewLayer *view_layer = CTX_data_view_layer(C); - gz_ring->objects = BKE_view_layer_array_from_objects_in_edit_mode( - view_layer, &gz_ring->objects_len); + if (((gz_ring->objects)) == NULL || + (gz_ring->objects[0] != OBEDIT_FROM_VIEW_LAYER(view_layer))) + { + gz_ring->objects = BKE_view_layer_array_from_objects_in_edit_mode( + view_layer, &gz_ring->objects_len); + } } ViewContext vc; @@ -207,8 +412,17 @@ static void GIZMO_GT_preselect_edgering_3d(wmGizmoType *gzt) RNA_def_int(gzt->srna, "edge_index", -1, -1, INT_MAX, "Edge Index", "", -1, INT_MAX); } +/** \} */ + + +/* -------------------------------------------------------------------- */ +/** \name Gizmo API + * + * \{ */ + void ED_gizmotypes_preselect_3d(void) { + WM_gizmotype_append(GIZMO_GT_preselect_elem_3d); WM_gizmotype_append(GIZMO_GT_preselect_edgering_3d); } diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h index e4ef442d49a..b8edbfe9b65 100644 --- a/source/blender/editors/space_view3d/view3d_intern.h +++ b/source/blender/editors/space_view3d/view3d_intern.h @@ -263,6 +263,7 @@ void VIEW3D_GGT_force_field(struct wmGizmoGroupType *gzgt); void VIEW3D_GGT_empty_image(struct wmGizmoGroupType *gzgt); void VIEW3D_GGT_armature_spline(struct wmGizmoGroupType *gzgt); void VIEW3D_GGT_navigate(struct wmGizmoGroupType *gzgt); +void VIEW3D_GGT_mesh_preselect_elem(struct wmGizmoGroupType *gzgt); void VIEW3D_GGT_mesh_preselect_edgering(struct wmGizmoGroupType *gzgt); void VIEW3D_GGT_ruler(struct wmGizmoGroupType *gzgt); -- cgit v1.2.3