diff --git a/scripts/presets/keyconfig/keymap_data/blender_default.py b/scripts/presets/keyconfig/keymap_data/blender_default.py index c4ba8924173..5169f827391 100644 --- a/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -4437,6 +4437,8 @@ def km_weight_paint_vertex_selection(params): {"properties": [("select", True)]}), ("paint.vert_select_linked_pick", {"type": 'L', "value": 'PRESS', "shift": True}, {"properties": [("select", False)]}), + ("paint.vert_select_more", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "ctrl": True}, None), + ("paint.vert_select_less", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True}, None), ]) return keymap diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index b60988353fc..7376128cb95 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -2032,6 +2032,9 @@ class VIEW3D_MT_select_paint_mask_vertex(Menu): layout.operator("paint.vert_select_all", text="None").action = 'DESELECT' layout.operator("paint.vert_select_all", text="Invert").action = 'INVERT' + layout.operator("paint.vert_select_more"), + layout.operator("paint.vert_select_less"), + layout.separator() layout.operator("view3d.select_box") diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index 1d00f4f288d..b54f61d232d 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -444,6 +444,8 @@ void paintvert_select_linked_pick(struct bContext *C, struct Object *ob, const int region_coordinates[2], bool select); +void paintvert_select_more(struct Mesh *mesh, bool face_step); +void paintvert_select_less(struct Mesh *mesh, bool face_step); void paintvert_hide(struct bContext *C, struct Object *ob, bool unselected); void paintvert_reveal(struct bContext *C, struct Object *ob, bool select); diff --git a/source/blender/editors/mesh/editface.cc b/source/blender/editors/mesh/editface.cc index cc4d950d64d..3c566c945da 100644 --- a/source/blender/editors/mesh/editface.cc +++ b/source/blender/editors/mesh/editface.cc @@ -24,6 +24,7 @@ #include "BKE_customdata.h" #include "BKE_global.h" #include "BKE_mesh.hh" +#include "BKE_mesh_mapping.h" #include "BKE_object.h" #include "ED_mesh.h" @@ -660,6 +661,126 @@ void paintvert_select_linked(bContext *C, Object *ob) paintvert_select_linked_vertices(C, ob, indices, true); } +void paintvert_select_more(Mesh *mesh, const bool face_step) +{ + using namespace blender; + + bke::MutableAttributeAccessor attributes = mesh->attributes_for_write(); + bke::SpanAttributeWriter select_vert = attributes.lookup_or_add_for_write_span( + ".select_vert", ATTR_DOMAIN_POINT); + const VArray hide_edge = attributes.lookup_or_default( + ".hide_edge", ATTR_DOMAIN_EDGE, false); + const VArray hide_poly = attributes.lookup_or_default( + ".hide_poly", ATTR_DOMAIN_FACE, false); + + const Span polys = mesh->polys(); + const Span corner_edges = mesh->corner_edges(); + const Span corner_verts = mesh->corner_verts(); + const Span edges = mesh->edges(); + + Array> edge_to_face_map; + if (face_step) { + edge_to_face_map = bke::mesh_topology::build_edge_to_poly_map( + polys, corner_edges, mesh->totedge); + } + + /* Need a copy of the selected verts that we can read from and is not modified. */ + BitVector<> select_vert_original(mesh->totvert, false); + for (int i = 0; i < mesh->totvert; i++) { + select_vert_original[i].set(select_vert.span[i]); + } + + /* If we iterated over polys we wouldn't extend the selection through edges that have no face + * attached to them. */ + for (const int i : edges.index_range()) { + const MEdge &edge = edges[i]; + if ((!select_vert_original[edge.v1] && !select_vert_original[edge.v2]) || hide_edge[i]) { + continue; + } + select_vert.span[edge.v1] = true; + select_vert.span[edge.v2] = true; + if (!face_step) { + continue; + } + const Span neighbor_polys = edge_to_face_map[i]; + for (const int poly_i : neighbor_polys) { + if (hide_poly[poly_i]) { + continue; + } + const MPoly &poly = polys[poly_i]; + for (const int vert : corner_verts.slice(poly.loopstart, poly.totloop)) { + select_vert.span[vert] = true; + } + } + } + + select_vert.finish(); +} + +void paintvert_select_less(Mesh *mesh, const bool face_step) +{ + using namespace blender; + + bke::MutableAttributeAccessor attributes = mesh->attributes_for_write(); + bke::SpanAttributeWriter select_vert = attributes.lookup_or_add_for_write_span( + ".select_vert", ATTR_DOMAIN_POINT); + const VArray hide_edge = attributes.lookup_or_default( + ".hide_edge", ATTR_DOMAIN_EDGE, false); + const VArray hide_poly = attributes.lookup_or_default( + ".hide_poly", ATTR_DOMAIN_FACE, false); + + const Span polys = mesh->polys(); + const Span corner_edges = mesh->corner_edges(); + const Span corner_verts = mesh->corner_verts(); + const Span edges = mesh->edges(); + + MeshElemMap *edge_poly_map; + int *edge_poly_mem = nullptr; + if (face_step) { + BKE_mesh_edge_poly_map_create(&edge_poly_map, + &edge_poly_mem, + edges.size(), + polys.data(), + polys.size(), + corner_edges.data(), + corner_edges.size()); + } + + /* Need a copy of the selected verts that we can read from and is not modified. */ + BitVector<> select_vert_original(mesh->totvert); + for (int i = 0; i < mesh->totvert; i++) { + select_vert_original[i].set(select_vert.span[i]); + } + + for (const int i : edges.index_range()) { + const MEdge &edge = edges[i]; + if ((select_vert_original[edge.v1] && select_vert_original[edge.v2]) && !hide_edge[i]) { + continue; + } + select_vert.span[edge.v1] = false; + select_vert.span[edge.v2] = false; + + if (!face_step) { + continue; + } + const Span neighbor_polys(edge_poly_map[i].indices, edge_poly_map[i].count); + for (const int poly_i : neighbor_polys) { + if (hide_poly[poly_i]) { + continue; + } + const MPoly &poly = polys[poly_i]; + for (const int vert : corner_verts.slice(poly.loopstart, poly.totloop)) { + select_vert.span[vert] = false; + } + } + } + if (edge_poly_mem) { + MEM_freeN(edge_poly_map); + MEM_freeN(edge_poly_mem); + } + select_vert.finish(); +} + void paintvert_tag_select_update(bContext *C, Object *ob) { DEG_id_tag_update(static_cast(ob->data), ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT); diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index f1071695908..a29c9c99bf4 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -387,6 +387,8 @@ void PAINT_OT_vert_select_ungrouped(struct wmOperatorType *ot); void PAINT_OT_vert_select_hide(struct wmOperatorType *ot); void PAINT_OT_vert_select_linked(struct wmOperatorType *ot); void PAINT_OT_vert_select_linked_pick(struct wmOperatorType *ot); +void PAINT_OT_vert_select_more(struct wmOperatorType *ot); +void PAINT_OT_vert_select_less(struct wmOperatorType *ot); bool vert_paint_poll(struct bContext *C); bool mask_paint_poll(struct bContext *C); diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index ca102b5a0b8..8b49c96542e 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -1501,6 +1501,8 @@ void ED_operatortypes_paint(void) WM_operatortype_append(PAINT_OT_vert_select_hide); WM_operatortype_append(PAINT_OT_vert_select_linked); WM_operatortype_append(PAINT_OT_vert_select_linked_pick); + WM_operatortype_append(PAINT_OT_vert_select_more); + WM_operatortype_append(PAINT_OT_vert_select_less); /* vertex */ WM_operatortype_append(PAINT_OT_vertex_paint_toggle); diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c index ba9a1c75446..588cd1835b0 100644 --- a/source/blender/editors/sculpt_paint/paint_utils.c +++ b/source/blender/editors/sculpt_paint/paint_utils.c @@ -796,6 +796,72 @@ void PAINT_OT_vert_select_linked_pick(wmOperatorType *ot) "Whether to select or deselect linked vertices under the cursor"); } +static int paintvert_select_more_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + Mesh *mesh = BKE_mesh_from_object(ob); + if (mesh == NULL || mesh->totpoly == 0) { + return OPERATOR_CANCELLED; + } + + const bool face_step = RNA_boolean_get(op->ptr, "face_step"); + paintvert_select_more(mesh, face_step); + + paintvert_flush_flags(ob); + paintvert_tag_select_update(C, ob); + ED_region_tag_redraw(CTX_wm_region(C)); + + return OPERATOR_FINISHED; +} + +void PAINT_OT_vert_select_more(wmOperatorType *ot) +{ + ot->name = "Select More"; + ot->description = "Select Vertices connected to existing selection"; + ot->idname = "PAINT_OT_vert_select_more"; + + ot->exec = paintvert_select_more_exec; + ot->poll = vert_paint_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_boolean( + ot->srna, "face_step", true, "Face Step", "Also select faces that only touch on a corner"); +} + +static int paintvert_select_less_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + Mesh *mesh = BKE_mesh_from_object(ob); + if (mesh == NULL || mesh->totpoly == 0) { + return OPERATOR_CANCELLED; + } + + const bool face_step = RNA_boolean_get(op->ptr, "face_step"); + paintvert_select_less(mesh, face_step); + + paintvert_flush_flags(ob); + paintvert_tag_select_update(C, ob); + ED_region_tag_redraw(CTX_wm_region(C)); + + return OPERATOR_FINISHED; +} + +void PAINT_OT_vert_select_less(wmOperatorType *ot) +{ + ot->name = "Select Less"; + ot->description = "Deselect Vertices connected to existing selection"; + ot->idname = "PAINT_OT_vert_select_less"; + + ot->exec = paintvert_select_less_exec; + ot->poll = vert_paint_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_boolean( + ot->srna, "face_step", true, "Face Step", "Also deselect faces that only touch on a corner"); +} + static int face_select_hide_exec(bContext *C, wmOperator *op) { const bool unselected = RNA_boolean_get(op->ptr, "unselected");