diff options
Diffstat (limited to 'source/blender/editors/space_view3d/view3d_select.c')
-rw-r--r-- | source/blender/editors/space_view3d/view3d_select.c | 1659 |
1 files changed, 1057 insertions, 602 deletions
diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index d8c21da48a4..30dbaed1d0f 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -44,9 +44,11 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_tracking_types.h" +#include "DNA_gpencil_types.h" #include "MEM_guardedalloc.h" +#include "BLI_array.h" #include "BLI_math.h" #include "BLI_lasso_2d.h" #include "BLI_rect.h" @@ -55,6 +57,10 @@ #include "BLI_string.h" #include "BLI_utildefines.h" +#ifdef __BIG_ENDIAN__ +# include "BLI_endian_switch.h" +#endif + /* vertex box select */ #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" @@ -63,16 +69,17 @@ #include "BKE_armature.h" #include "BKE_context.h" #include "BKE_curve.h" -#include "BKE_depsgraph.h" +#include "BKE_layer.h" #include "BKE_mball.h" #include "BKE_mesh.h" #include "BKE_object.h" #include "BKE_paint.h" #include "BKE_editmesh.h" +#include "BKE_scene.h" #include "BKE_tracking.h" +#include "BKE_workspace.h" -#include "BIF_gl.h" -#include "BIF_glutil.h" +#include "DEG_depsgraph.h" #include "WM_api.h" #include "WM_types.h" @@ -88,12 +95,16 @@ #include "ED_mesh.h" #include "ED_object.h" #include "ED_screen.h" +#include "ED_select_utils.h" #include "ED_sculpt.h" #include "ED_mball.h" +#include "ED_gpencil.h" #include "UI_interface.h" #include "GPU_draw.h" +#include "GPU_glew.h" +#include "GPU_matrix.h" #include "view3d_intern.h" /* own include */ @@ -108,9 +119,12 @@ float ED_view3d_select_dist_px(void) void ED_view3d_viewcontext_init(bContext *C, ViewContext *vc) { memset(vc, 0, sizeof(ViewContext)); + vc->C = C; vc->ar = CTX_wm_region(C); vc->bmain = CTX_data_main(C); + vc->depsgraph = CTX_data_depsgraph(C); vc->scene = CTX_data_scene(C); + vc->view_layer = CTX_data_view_layer(C); vc->v3d = CTX_wm_view3d(C); vc->win = CTX_wm_window(C); vc->rv3d = CTX_wm_region_view3d(C); @@ -118,39 +132,26 @@ void ED_view3d_viewcontext_init(bContext *C, ViewContext *vc) vc->obedit = CTX_data_edit_object(C); } -/* - * ob == NULL if you want global matrices - * */ -void view3d_get_transformation(const ARegion *ar, RegionView3D *rv3d, Object *ob, bglMats *mats) +void ED_view3d_viewcontext_init_object(ViewContext *vc, Object *obact) { - float cpy[4][4]; - int i, j; - - if (ob) { - mul_m4_m4m4(cpy, rv3d->viewmat, ob->obmat); - } - else { - copy_m4_m4(cpy, rv3d->viewmat); - } + vc->obact = obact; + if (vc->obedit) { + BLI_assert(BKE_object_is_in_editmode(obact)); + vc->obedit = obact; + /* previous selections are now invalid. */ + vc->v3d->flag |= V3D_INVALID_BACKBUF; - for (i = 0; i < 4; ++i) { - for (j = 0; j < 4; ++j) { - mats->projection[i * 4 + j] = rv3d->winmat[i][j]; - mats->modelview[i * 4 + j] = cpy[i][j]; + if (vc->em) { + vc->em = BKE_editmesh_from_object(vc->obedit); } } - - mats->viewport[0] = ar->winrct.xmin; - mats->viewport[1] = ar->winrct.ymin; - mats->viewport[2] = ar->winx; - mats->viewport[3] = ar->winy; } /* ********************** view3d_select: selection manipulations ********************* */ /* local prototypes */ -static void edbm_backbuf_check_and_select_verts(BMEditMesh *em, const bool select) +static void edbm_backbuf_check_and_select_verts(BMEditMesh *em, const eSelectOp sel_op) { BMVert *eve; BMIter iter; @@ -158,15 +159,18 @@ static void edbm_backbuf_check_and_select_verts(BMEditMesh *em, const bool selec BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { - if (EDBM_backbuf_check(index)) { - BM_vert_select_set(em->bm, eve, select); + const bool is_select = BM_elem_flag_test(eve, BM_ELEM_SELECT); + const bool is_inside = EDBM_backbuf_check(index); + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + if (sel_op_result != -1) { + BM_vert_select_set(em->bm, eve, sel_op_result); } } index++; } } -static void edbm_backbuf_check_and_select_edges(BMEditMesh *em, const bool select) +static void edbm_backbuf_check_and_select_edges(BMEditMesh *em, const eSelectOp sel_op) { BMEdge *eed; BMIter iter; @@ -174,15 +178,18 @@ static void edbm_backbuf_check_and_select_edges(BMEditMesh *em, const bool selec BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { - if (EDBM_backbuf_check(index)) { - BM_edge_select_set(em->bm, eed, select); + const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT); + const bool is_inside = EDBM_backbuf_check(index); + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + if (sel_op_result != -1) { + BM_edge_select_set(em->bm, eed, sel_op_result); } } index++; } } -static void edbm_backbuf_check_and_select_faces(BMEditMesh *em, const bool select) +static void edbm_backbuf_check_and_select_faces(BMEditMesh *em, const eSelectOp sel_op) { BMFace *efa; BMIter iter; @@ -190,26 +197,31 @@ static void edbm_backbuf_check_and_select_faces(BMEditMesh *em, const bool selec BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { - if (EDBM_backbuf_check(index)) { - BM_face_select_set(em->bm, efa, select); + const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT); + const bool is_inside = EDBM_backbuf_check(index); + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + if (sel_op_result != -1) { + BM_face_select_set(em->bm, efa, sel_op_result); } } index++; } } - /* object mode, edbm_ prefix is confusing here, rename? */ -static void edbm_backbuf_check_and_select_verts_obmode(Mesh *me, const bool select) +static void edbm_backbuf_check_and_select_verts_obmode(Mesh *me, const eSelectOp sel_op) { MVert *mv = me->mvert; unsigned int index; if (mv) { for (index = 1; index <= me->totvert; index++, mv++) { - if (EDBM_backbuf_check(index)) { - if (!(mv->flag & ME_HIDE)) { - mv->flag = select ? (mv->flag | SELECT) : (mv->flag & ~SELECT); + if (!(mv->flag & ME_HIDE)) { + const bool is_select = mv->flag & SELECT; + const bool is_inside = EDBM_backbuf_check(index); + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + if (sel_op_result != -1) { + SET_FLAG_FROM_TEST(mv->flag, sel_op_result, SELECT); } } } @@ -217,15 +229,20 @@ static void edbm_backbuf_check_and_select_verts_obmode(Mesh *me, const bool sele } /* object mode, edbm_ prefix is confusing here, rename? */ -static void edbm_backbuf_check_and_select_tfaces(Mesh *me, const bool select) +static void edbm_backbuf_check_and_select_tfaces(Mesh *me, const eSelectOp sel_op) { MPoly *mpoly = me->mpoly; unsigned int index; if (mpoly) { for (index = 1; index <= me->totpoly; index++, mpoly++) { - if (EDBM_backbuf_check(index)) { - mpoly->flag = select ? (mpoly->flag | ME_FACE_SEL) : (mpoly->flag & ~ME_FACE_SEL); + if (!(mpoly->flag & ME_HIDE)) { + const bool is_select = mpoly->flag & ME_FACE_SEL; + const bool is_inside = EDBM_backbuf_check(index); + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + if (sel_op_result != -1) { + SET_FLAG_FROM_TEST(mpoly->flag, sel_op_result, ME_FACE_SEL); + } } } } @@ -240,7 +257,7 @@ typedef struct LassoSelectUserData { rctf _rect_fl; const int (*mcords)[2]; int moves; - bool select; + eSelectOp sel_op; /* runtime */ int pass; @@ -251,7 +268,7 @@ typedef struct LassoSelectUserData { static void view3d_userdata_lassoselect_init( LassoSelectUserData *r_data, ViewContext *vc, const rcti *rect, const int (*mcords)[2], - const int moves, const bool select) + const int moves, const eSelectOp sel_op) { r_data->vc = vc; @@ -261,7 +278,7 @@ static void view3d_userdata_lassoselect_init( r_data->mcords = mcords; r_data->moves = moves; - r_data->select = select; + r_data->sel_op = sel_op; /* runtime */ r_data->pass = 0; @@ -295,7 +312,7 @@ static bool view3d_selectable_data(bContext *C) } -/* helper also for borderselect */ +/* helper also for box_select */ static bool edge_fully_inside_rect(const rctf *rect, const float v1[2], const float v2[2]) { return BLI_rctf_isect_pt_v(rect, v1) && BLI_rctf_isect_pt_v(rect, v2); @@ -326,7 +343,7 @@ static bool edge_inside_rect(const rctf *rect, const float v1[2], const float v2 return 1; } -static void do_lasso_select_pose__doSelectBone( +static void do_lasso_select_pose__do_tag( void *userData, struct bPoseChannel *pchan, const float screen_co_a[2], const float screen_co_b[2]) { LassoSelectUserData *data = userData; @@ -336,26 +353,21 @@ static void do_lasso_select_pose__doSelectBone( bool is_point_done = false; int points_proj_tot = 0; - const int x0 = screen_co_a[0]; - const int y0 = screen_co_a[1]; - const int x1 = screen_co_b[0]; - const int y1 = screen_co_b[1]; - /* project head location to screenspace */ - if (x0 != IS_CLIPPED) { + if (screen_co_a[0] != IS_CLIPPED) { points_proj_tot++; - if (BLI_rcti_isect_pt(data->rect, x0, y0) && - BLI_lasso_is_point_inside(data->mcords, data->moves, x0, y0, INT_MAX)) + if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_a)) && + BLI_lasso_is_point_inside(data->mcords, data->moves, UNPACK2(screen_co_a), INT_MAX)) { is_point_done = true; } } /* project tail location to screenspace */ - if (x1 != IS_CLIPPED) { + if (screen_co_b[0] != IS_CLIPPED) { points_proj_tot++; - if (BLI_rcti_isect_pt(data->rect, x1, y1) && - BLI_lasso_is_point_inside(data->mcords, data->moves, x1, y1, INT_MAX)) + if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_b)) && + BLI_lasso_is_point_inside(data->mcords, data->moves, UNPACK2(screen_co_b), INT_MAX)) { is_point_done = true; } @@ -364,16 +376,14 @@ static void do_lasso_select_pose__doSelectBone( /* if one of points selected, we skip the bone itself */ if ((is_point_done == true) || ((is_point_done == false) && (points_proj_tot == 2) && - BLI_lasso_is_edge_inside(data->mcords, data->moves, x0, y0, x1, y1, INT_MAX))) + BLI_lasso_is_edge_inside(data->mcords, data->moves, UNPACK2(screen_co_a), UNPACK2(screen_co_b), INT_MAX))) { - if (data->select) pchan->bone->flag |= BONE_SELECTED; - else pchan->bone->flag &= ~BONE_SELECTED; - data->is_changed = true; + pchan->bone->flag |= BONE_DONE; } data->is_changed |= is_point_done; } } -static void do_lasso_select_pose(ViewContext *vc, Object *ob, const int mcords[][2], short moves, const bool select) +static void do_lasso_tag_pose(ViewContext *vc, Object *ob, const int mcords[][2], short moves) { ViewContext vc_tmp; LassoSelectUserData data; @@ -388,106 +398,218 @@ static void do_lasso_select_pose(ViewContext *vc, Object *ob, const int mcords[] BLI_lasso_boundbox(&rect, mcords, moves); - view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, select); + view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, 0); - ED_view3d_init_mats_rv3d(vc->obact, vc->rv3d); + ED_view3d_init_mats_rv3d(vc_tmp.obact, vc->rv3d); - pose_foreachScreenBone(&vc_tmp, do_lasso_select_pose__doSelectBone, &data, V3D_PROJ_TEST_CLIP_DEFAULT); - - if (data.is_changed) { - bArmature *arm = ob->data; - if (arm->flag & ARM_HAS_VIZ_DEPS) { - /* mask modifier ('armature' mode), etc. */ - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); - } - } + pose_foreachScreenBone(&vc_tmp, do_lasso_select_pose__do_tag, &data, V3D_PROJ_TEST_CLIP_DEFAULT); } -static void object_deselect_all_visible(Scene *scene, View3D *v3d) +static void object_deselect_all_visible(ViewLayer *view_layer, View3D *v3d) { Base *base; - for (base = scene->base.first; base; base = base->next) { + for (base = view_layer->object_bases.first; base; base = base->next) { if (BASE_SELECTABLE(v3d, base)) { - ED_base_object_select(base, BA_DESELECT); + ED_object_base_select(base, BA_DESELECT); } } } static void do_lasso_select_objects( ViewContext *vc, const int mcords[][2], const short moves, - const bool extend, const bool select) + const eSelectOp sel_op) { + View3D *v3d = vc->v3d; Base *base; - if (extend == false && select) - object_deselect_all_visible(vc->scene, vc->v3d); - - for (base = vc->scene->base.first; base; base = base->next) { - if (BASE_SELECTABLE(vc->v3d, base)) { /* use this to avoid un-needed lasso lookups */ - if (ED_view3d_project_base(vc->ar, base) == V3D_PROJ_RET_OK) { - if (BLI_lasso_is_point_inside(mcords, moves, base->sx, base->sy, IS_CLIPPED)) { + if (SEL_OP_USE_PRE_DESELECT(sel_op)) { + object_deselect_all_visible(vc->view_layer, vc->v3d); + } - ED_base_object_select(base, select ? BA_SELECT : BA_DESELECT); - base->object->flag = base->flag; - } - } - if (vc->obact == base->object && (base->object->mode & OB_MODE_POSE)) { - do_lasso_select_pose(vc, base->object, mcords, moves, select); + bool changed = false; + for (base = vc->view_layer->object_bases.first; base; base = base->next) { + if (BASE_SELECTABLE(v3d, base)) { /* use this to avoid un-needed lasso lookups */ + const bool is_select = base->flag & BASE_SELECTED; + const bool is_inside = ( + (ED_view3d_project_base(vc->ar, base) == V3D_PROJ_RET_OK) && + BLI_lasso_is_point_inside(mcords, moves, base->sx, base->sy, IS_CLIPPED)); + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + if (sel_op_result != -1) { + ED_object_base_select(base, sel_op_result ? BA_SELECT : BA_DESELECT); + changed = true; } } } + + if (changed) { + DEG_id_tag_update(&vc->scene->id, DEG_TAG_SELECT_UPDATE); + WM_main_add_notifier(NC_SCENE | ND_OB_SELECT, vc->scene); + } } -static void do_lasso_select_mesh__doSelectVert(void *userData, BMVert *eve, const float screen_co[2], int UNUSED(index)) -{ - LassoSelectUserData *data = userData; - if (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) && - BLI_lasso_is_point_inside(data->mcords, data->moves, screen_co[0], screen_co[1], IS_CLIPPED)) - { - BM_vert_select_set(data->vc->em->bm, eve, data->select); +/** + * Use for lasso & box select. + */ +static Base **do_pose_tag_select_op_prepare(ViewContext *vc, uint *r_bases_len) +{ + Base **bases = NULL; + BLI_array_declare(bases); + FOREACH_BASE_IN_MODE_BEGIN (vc->view_layer, vc->v3d, OB_MODE_POSE, base_iter) { + Object *ob_iter = base_iter->object; + bArmature *arm = ob_iter->data; + for (bPoseChannel *pchan = ob_iter->pose->chanbase.first; pchan; pchan = pchan->next) { + Bone *bone = pchan->bone; + bone->flag &= ~BONE_DONE; + } + arm->id.tag |= LIB_TAG_DOIT; + ob_iter->id.tag &= ~LIB_TAG_DOIT; + BLI_array_append(bases, base_iter); } + FOREACH_BASE_IN_MODE_END; + *r_bases_len = BLI_array_len(bases); + return bases; } -static void do_lasso_select_mesh__doSelectEdge( - void *userData, BMEdge *eed, const float screen_co_a[2], const float screen_co_b[2], int index) -{ - LassoSelectUserData *data = userData; - if (EDBM_backbuf_check(bm_solidoffs + index)) { - const int x0 = screen_co_a[0]; - const int y0 = screen_co_a[1]; - const int x1 = screen_co_b[0]; - const int y1 = screen_co_b[1]; +static bool do_pose_tag_select_op_exec(Base **bases, const uint bases_len, const eSelectOp sel_op) +{ + bool changed_multi = false; - if (data->pass == 0) { - if (edge_fully_inside_rect(data->rect_fl, screen_co_a, screen_co_b) && - BLI_lasso_is_point_inside(data->mcords, data->moves, x0, y0, IS_CLIPPED) && - BLI_lasso_is_point_inside(data->mcords, data->moves, x1, y1, IS_CLIPPED)) - { - BM_edge_select_set(data->vc->em->bm, eed, data->select); - data->is_done = true; + if (SEL_OP_USE_PRE_DESELECT(sel_op)) { + for (int i = 0; i < bases_len; i++) { + Base *base_iter = bases[i]; + Object *ob_iter = base_iter->object; + if (ED_pose_deselect_all(ob_iter, SEL_DESELECT, false)) { + ED_pose_bone_select_tag_update(ob_iter); + changed_multi = true; } } + } + + for (int i = 0; i < bases_len; i++) { + Base *base_iter = bases[i]; + Object *ob_iter = base_iter->object; + bArmature *arm = ob_iter->data; + + /* Don't handle twice. */ + if (arm->id.tag & LIB_TAG_DOIT) { + arm->id.tag &= ~LIB_TAG_DOIT; + } else { - if (BLI_lasso_is_edge_inside(data->mcords, data->moves, x0, y0, x1, y1, IS_CLIPPED)) { - BM_edge_select_set(data->vc->em->bm, eed, data->select); + continue; + } + + bool changed = true; + for (bPoseChannel *pchan = ob_iter->pose->chanbase.first; pchan; pchan = pchan->next) { + Bone *bone = pchan->bone; + if ((bone->flag & BONE_UNSELECTABLE) == 0) { + const bool is_select = bone->flag & BONE_SELECTED; + const bool is_inside = bone->flag & BONE_DONE; + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + if (sel_op_result != -1) { + SET_FLAG_FROM_TEST(bone->flag, sel_op_result, BONE_SELECTED); + if (sel_op_result == 0) { + if (arm->act_bone == bone) { + arm->act_bone = NULL; + } + } + changed = true; + } } } + if (changed) { + ED_pose_bone_select_tag_update(ob_iter); + changed_multi = true; + } } + return changed_multi; } -static void do_lasso_select_mesh__doSelectFace(void *userData, BMFace *efa, const float screen_co[2], int UNUSED(index)) + +static void do_lasso_select_pose( + ViewContext *vc, const int mcords[][2], const short moves, + const eSelectOp sel_op) +{ + uint bases_len; + Base **bases = do_pose_tag_select_op_prepare(vc, &bases_len); + + for (int i = 0; i < bases_len; i++) { + Base *base_iter = bases[i]; + Object *ob_iter = base_iter->object; + do_lasso_tag_pose(vc, ob_iter, mcords, moves); + } + + const bool changed_multi = do_pose_tag_select_op_exec(bases, bases_len, sel_op); + if (changed_multi) { + DEG_id_tag_update(&vc->scene->id, DEG_TAG_SELECT_UPDATE); + WM_main_add_notifier(NC_SCENE | ND_OB_SELECT, vc->scene); + } + + if (bases != NULL) { + MEM_freeN(bases); + } +} + +static void do_lasso_select_mesh__doSelectVert(void *userData, BMVert *eve, const float screen_co[2], int UNUSED(index)) { LassoSelectUserData *data = userData; + const bool is_select = BM_elem_flag_test(eve, BM_ELEM_SELECT); + const bool is_inside = ( + BLI_rctf_isect_pt_v(data->rect_fl, screen_co) && + BLI_lasso_is_point_inside(data->mcords, data->moves, screen_co[0], screen_co[1], IS_CLIPPED)); + const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + if (sel_op_result != -1) { + BM_vert_select_set(data->vc->em->bm, eve, sel_op_result); + } +} +static void do_lasso_select_mesh__doSelectEdge_pass0( + void *userData, BMEdge *eed, const float screen_co_a[2], const float screen_co_b[2], int index) +{ + LassoSelectUserData *data = userData; + const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT); + const bool is_inside = ( + EDBM_backbuf_check(bm_solidoffs + index) && + edge_fully_inside_rect(data->rect_fl, screen_co_a, screen_co_b) && + BLI_lasso_is_point_inside(data->mcords, data->moves, UNPACK2(screen_co_a), IS_CLIPPED) && + BLI_lasso_is_point_inside(data->mcords, data->moves, UNPACK2(screen_co_b), IS_CLIPPED)); + const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + if (sel_op_result != -1) { + BM_edge_select_set(data->vc->em->bm, eed, sel_op_result); + data->is_done = true; + } +} +static void do_lasso_select_mesh__doSelectEdge_pass1( + void *userData, BMEdge *eed, const float screen_co_a[2], const float screen_co_b[2], int index) +{ + LassoSelectUserData *data = userData; + const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT); + const bool is_inside = ( + EDBM_backbuf_check(bm_solidoffs + index) && + BLI_lasso_is_edge_inside( + data->mcords, data->moves, UNPACK2(screen_co_a), UNPACK2(screen_co_b), IS_CLIPPED)); + const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + if (sel_op_result != -1) { + BM_edge_select_set(data->vc->em->bm, eed, sel_op_result); + } +} - if (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) && - BLI_lasso_is_point_inside(data->mcords, data->moves, screen_co[0], screen_co[1], IS_CLIPPED)) - { - BM_face_select_set(data->vc->em->bm, efa, data->select); +static void do_lasso_select_mesh__doSelectFace(void *userData, BMFace *efa, const float screen_co[2], int UNUSED(index)) +{ + LassoSelectUserData *data = userData; + const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT); + const bool is_inside = ( + BLI_rctf_isect_pt_v(data->rect_fl, screen_co) && + BLI_lasso_is_point_inside( + data->mcords, data->moves, screen_co[0], screen_co[1], IS_CLIPPED)); + const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + if (sel_op_result != -1) { + BM_face_select_set(data->vc->em->bm, efa, sel_op_result); } } -static void do_lasso_select_mesh(ViewContext *vc, const int mcords[][2], short moves, bool extend, bool select) +static void do_lasso_select_mesh( + ViewContext *vc, + const int mcords[][2], short moves, const eSelectOp sel_op) { LassoSelectUserData data; ToolSettings *ts = vc->scene->toolsettings; @@ -499,20 +621,21 @@ static void do_lasso_select_mesh(ViewContext *vc, const int mcords[][2], short m BLI_lasso_boundbox(&rect, mcords, moves); - view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, select); + view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, sel_op); - if (extend == false && select) + if (SEL_OP_USE_PRE_DESELECT(sel_op)) { EDBM_flag_disable_all(vc->em, BM_ELEM_SELECT); + } /* for non zbuf projections, don't change the GL state */ ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); - glLoadMatrixf(vc->rv3d->viewmat); + GPU_matrix_set(vc->rv3d->viewmat); bbsel = EDBM_backbuf_border_mask_init(vc, mcords, moves, rect.xmin, rect.ymin, rect.xmax, rect.ymax); if (ts->selectmode & SCE_SELECT_VERTEX) { if (bbsel) { - edbm_backbuf_check_and_select_verts(vc->em, select); + edbm_backbuf_check_and_select_verts(vc->em, sel_op); } else { mesh_foreachScreenVert(vc, do_lasso_select_mesh__doSelectVert, &data, V3D_PROJ_TEST_CLIP_DEFAULT); @@ -520,18 +643,15 @@ static void do_lasso_select_mesh(ViewContext *vc, const int mcords[][2], short m } if (ts->selectmode & SCE_SELECT_EDGE) { /* Does both bbsel and non-bbsel versions (need screen cos for both) */ - data.pass = 0; - mesh_foreachScreenEdge(vc, do_lasso_select_mesh__doSelectEdge, &data, V3D_PROJ_TEST_CLIP_NEAR); - + mesh_foreachScreenEdge(vc, do_lasso_select_mesh__doSelectEdge_pass0, &data, V3D_PROJ_TEST_CLIP_NEAR); if (data.is_done == false) { - data.pass = 1; - mesh_foreachScreenEdge(vc, do_lasso_select_mesh__doSelectEdge, &data, V3D_PROJ_TEST_CLIP_NEAR); + mesh_foreachScreenEdge(vc, do_lasso_select_mesh__doSelectEdge_pass1, &data, V3D_PROJ_TEST_CLIP_NEAR); } } if (ts->selectmode & SCE_SELECT_FACE) { if (bbsel) { - edbm_backbuf_check_and_select_faces(vc->em, select); + edbm_backbuf_check_and_select_faces(vc->em, sel_op); } else { mesh_foreachScreenFace(vc, do_lasso_select_mesh__doSelectFace, &data, V3D_PROJ_TEST_CLIP_DEFAULT); @@ -546,43 +666,46 @@ static void do_lasso_select_curve__doSelect( void *userData, Nurb *UNUSED(nu), BPoint *bp, BezTriple *bezt, int beztindex, const float screen_co[2]) { LassoSelectUserData *data = userData; - Object *obedit = data->vc->obedit; - Curve *cu = (Curve *)obedit->data; - if (BLI_lasso_is_point_inside(data->mcords, data->moves, screen_co[0], screen_co[1], IS_CLIPPED)) { - if (bp) { - bp->f1 = data->select ? (bp->f1 | SELECT) : (bp->f1 & ~SELECT); + const bool is_inside = BLI_lasso_is_point_inside(data->mcords, data->moves, screen_co[0], screen_co[1], IS_CLIPPED); + if (bp) { + const bool is_select = bp->f1 & SELECT; + const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + if (sel_op_result != -1) { + SET_FLAG_FROM_TEST(bp->f1, sel_op_result, SELECT); } - else { - if (cu->drawflag & CU_HIDE_HANDLES) { - /* can only be (beztindex == 0) here since handles are hidden */ - bezt->f1 = bezt->f2 = bezt->f3 = data->select ? (bezt->f2 | SELECT) : (bezt->f2 & ~SELECT); + } + else { + if ((data->vc->v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_CU_HANDLES) == 0) { + /* can only be (beztindex == 0) here since handles are hidden */ + const bool is_select = bezt->f2 & SELECT; + const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + if (sel_op_result != -1) { + SET_FLAG_FROM_TEST(bezt->f2, sel_op_result, SELECT); } - else { - if (beztindex == 0) { - bezt->f1 = data->select ? (bezt->f1 | SELECT) : (bezt->f1 & ~SELECT); - } - else if (beztindex == 1) { - bezt->f2 = data->select ? (bezt->f2 | SELECT) : (bezt->f2 & ~SELECT); - } - else { - bezt->f3 = data->select ? (bezt->f3 | SELECT) : (bezt->f3 & ~SELECT); - } + bezt->f1 = bezt->f3 = bezt->f2; + } + else { + char *flag_p = (&bezt->f1) + beztindex; + const bool is_select = *flag_p & SELECT; + const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + if (sel_op_result != -1) { + SET_FLAG_FROM_TEST(*flag_p, sel_op_result, SELECT); } } } } -static void do_lasso_select_curve(ViewContext *vc, const int mcords[][2], short moves, bool extend, bool select) +static void do_lasso_select_curve(ViewContext *vc, const int mcords[][2], short moves, const eSelectOp sel_op) { LassoSelectUserData data; rcti rect; BLI_lasso_boundbox(&rect, mcords, moves); - view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, select); + view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, sel_op); - if (extend == false && select) { + if (SEL_OP_USE_PRE_DESELECT(sel_op)) { Curve *curve = (Curve *) vc->obedit->data; ED_curve_deselect_all(curve->editnurb); } @@ -595,74 +718,83 @@ static void do_lasso_select_curve(ViewContext *vc, const int mcords[][2], short static void do_lasso_select_lattice__doSelect(void *userData, BPoint *bp, const float screen_co[2]) { LassoSelectUserData *data = userData; - - if (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) && - BLI_lasso_is_point_inside(data->mcords, data->moves, screen_co[0], screen_co[1], IS_CLIPPED)) - { - bp->f1 = data->select ? (bp->f1 | SELECT) : (bp->f1 & ~SELECT); + const bool is_select = bp->f1 & SELECT; + const bool is_inside = ( + BLI_rctf_isect_pt_v(data->rect_fl, screen_co) && + BLI_lasso_is_point_inside(data->mcords, data->moves, screen_co[0], screen_co[1], IS_CLIPPED)); + const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + if (sel_op_result != -1) { + SET_FLAG_FROM_TEST(bp->f1, sel_op_result, SELECT); } } -static void do_lasso_select_lattice(ViewContext *vc, const int mcords[][2], short moves, bool extend, bool select) +static void do_lasso_select_lattice(ViewContext *vc, const int mcords[][2], short moves, const eSelectOp sel_op) { LassoSelectUserData data; rcti rect; BLI_lasso_boundbox(&rect, mcords, moves); - view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, select); + view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, sel_op); - if (extend == false && select) + if (SEL_OP_USE_PRE_DESELECT(sel_op)) { ED_lattice_flags_set(vc->obedit, 0); + } ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */ lattice_foreachScreenVert(vc, do_lasso_select_lattice__doSelect, &data, V3D_PROJ_TEST_CLIP_DEFAULT); } -static void do_lasso_select_armature__doSelectBone( - void *userData, struct EditBone *ebone, const float screen_co_a[2], const float screen_co_b[2]) +static void do_lasso_select_armature__doSelectBone(void *userData, struct EditBone *ebone, const float screen_co_a[2], const float screen_co_b[2]) { LassoSelectUserData *data = userData; bArmature *arm = data->vc->obedit->data; - - if (data->select ? EBONE_SELECTABLE(arm, ebone) : EBONE_VISIBLE(arm, ebone)) { + if (EBONE_VISIBLE(arm, ebone)) { bool is_point_done = false; int points_proj_tot = 0; - const int x0 = screen_co_a[0]; - const int y0 = screen_co_a[1]; - const int x1 = screen_co_b[0]; - const int y1 = screen_co_b[1]; - /* project head location to screenspace */ - if (x0 != IS_CLIPPED) { + if (screen_co_a[0] != IS_CLIPPED) { points_proj_tot++; - if (BLI_rcti_isect_pt(data->rect, x0, y0) && - BLI_lasso_is_point_inside(data->mcords, data->moves, x0, y0, INT_MAX)) - { - is_point_done = true; - if (data->select) ebone->flag |= BONE_ROOTSEL; - else ebone->flag &= ~BONE_ROOTSEL; + const bool is_select = ebone->flag & BONE_ROOTSEL; + const bool is_inside = ( + BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_a)) && + BLI_lasso_is_point_inside(data->mcords, data->moves, UNPACK2(screen_co_a), INT_MAX)); + const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + if (sel_op_result != -1) { + if (sel_op_result == 0 || EBONE_SELECTABLE(arm, ebone)) { + SET_FLAG_FROM_TEST(ebone->flag, sel_op_result, BONE_ROOTSEL); + } } + is_point_done |= is_inside; } /* project tail location to screenspace */ - if (x1 != IS_CLIPPED) { + if (screen_co_b[0] != IS_CLIPPED) { points_proj_tot++; - if (BLI_rcti_isect_pt(data->rect, x1, y1) && - BLI_lasso_is_point_inside(data->mcords, data->moves, x1, y1, INT_MAX)) - { - is_point_done = true; - if (data->select) ebone->flag |= BONE_TIPSEL; - else ebone->flag &= ~BONE_TIPSEL; + const bool is_select = ebone->flag & BONE_TIPSEL; + const bool is_inside = ( + BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_b)) && + BLI_lasso_is_point_inside(data->mcords, data->moves, UNPACK2(screen_co_b), INT_MAX)); + const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + if (sel_op_result != -1) { + if (sel_op_result == 0 || EBONE_SELECTABLE(arm, ebone)) { + SET_FLAG_FROM_TEST(ebone->flag, sel_op_result, BONE_TIPSEL); + } } + is_point_done |= is_inside; } /* if one of points selected, we skip the bone itself */ - if ((is_point_done == false) && (points_proj_tot == 2) && - BLI_lasso_is_edge_inside(data->mcords, data->moves, x0, y0, x1, y1, INT_MAX)) - { - if (data->select) ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - else ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + if ((is_point_done == false) && (points_proj_tot == 2)) { + const bool is_select = ebone->flag & (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + const bool is_inside = BLI_lasso_is_edge_inside( + data->mcords, data->moves, UNPACK2(screen_co_a), UNPACK2(screen_co_b), INT_MAX); + const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + if (sel_op_result != -1) { + if (sel_op_result == 0 || EBONE_SELECTABLE(arm, ebone)) { + SET_FLAG_FROM_TEST(ebone->flag, sel_op_result, (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL)); + } + } data->is_changed = true; } @@ -670,19 +802,20 @@ static void do_lasso_select_armature__doSelectBone( } } -static void do_lasso_select_armature(ViewContext *vc, const int mcords[][2], short moves, bool extend, bool select) +static void do_lasso_select_armature(ViewContext *vc, const int mcords[][2], short moves, const eSelectOp sel_op) { LassoSelectUserData data; rcti rect; BLI_lasso_boundbox(&rect, mcords, moves); - view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, select); + view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, sel_op); ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); - if (extend == false && select) + if (SEL_OP_USE_PRE_DESELECT(sel_op)) { ED_armature_edit_deselect_all_visible(vc->obedit); + } armature_foreachScreenBone(vc, do_lasso_select_armature__doSelectBone, &data, V3D_PROJ_TEST_CLIP_DEFAULT); @@ -697,48 +830,52 @@ static void do_lasso_select_armature(ViewContext *vc, const int mcords[][2], sho static void do_lasso_select_mball__doSelectElem(void *userData, struct MetaElem *ml, const float screen_co[2]) { LassoSelectUserData *data = userData; - - if (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) && - BLI_lasso_is_point_inside(data->mcords, data->moves, screen_co[0], screen_co[1], INT_MAX)) - { - if (data->select) ml->flag |= SELECT; - else ml->flag &= ~SELECT; + const bool is_select = ml->flag & SELECT; + const bool is_inside = ( + BLI_rctf_isect_pt_v(data->rect_fl, screen_co) && + BLI_lasso_is_point_inside(data->mcords, data->moves, screen_co[0], screen_co[1], INT_MAX)); + const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + if (sel_op_result != -1) { + SET_FLAG_FROM_TEST(ml->flag, sel_op_result, SELECT); data->is_changed = true; } } -static void do_lasso_select_meta(ViewContext *vc, const int mcords[][2], short moves, bool extend, bool select) +static void do_lasso_select_meta(ViewContext *vc, const int mcords[][2], short moves, const eSelectOp sel_op) { LassoSelectUserData data; rcti rect; MetaBall *mb = (MetaBall *)vc->obedit->data; - if (extend == false && select) + if (SEL_OP_USE_PRE_DESELECT(sel_op)) { BKE_mball_deselect_all(mb); + } BLI_lasso_boundbox(&rect, mcords, moves); - view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, select); + view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, sel_op); ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); mball_foreachScreenElem(vc, do_lasso_select_mball__doSelectElem, &data, V3D_PROJ_TEST_CLIP_DEFAULT); } -static void do_lasso_select_meshobject__doSelectVert( - void *userData, MVert *mv, const float screen_co[2], int UNUSED(index)) +static void do_lasso_select_meshobject__doSelectVert(void *userData, MVert *mv, const float screen_co[2], int UNUSED(index)) { LassoSelectUserData *data = userData; - - if (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) && - BLI_lasso_is_point_inside(data->mcords, data->moves, screen_co[0], screen_co[1], IS_CLIPPED)) - { - SET_FLAG_FROM_TEST(mv->flag, data->select, SELECT); + const bool is_select = mv->flag & SELECT; + const bool is_inside = ( + BLI_rctf_isect_pt_v(data->rect_fl, screen_co) && + BLI_lasso_is_point_inside(data->mcords, data->moves, screen_co[0], screen_co[1], IS_CLIPPED)); + const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + if (sel_op_result != -1) { + SET_FLAG_FROM_TEST(mv->flag, sel_op_result, SELECT); + data->is_changed = true; } } -static void do_lasso_select_paintvert(ViewContext *vc, const int mcords[][2], short moves, bool extend, bool select) +static void do_lasso_select_paintvert(ViewContext *vc, const int mcords[][2], short moves, const eSelectOp sel_op) { - const bool use_zbuf = (vc->v3d->flag & V3D_ZBUF_SELECT) != 0; + const bool use_zbuf = V3D_IS_ZBUF(vc->v3d); Object *ob = vc->obact; Mesh *me = ob->data; rcti rect; @@ -746,8 +883,9 @@ static void do_lasso_select_paintvert(ViewContext *vc, const int mcords[][2], sh if (me == NULL || me->totvert == 0) return; - if (extend == false && select) + if (SEL_OP_USE_PRE_DESELECT(sel_op)) { paintvert_deselect_all_visible(ob, SEL_DESELECT, false); /* flush selection at the end */ + } BLI_lasso_boundbox(&rect, mcords, moves); @@ -756,14 +894,14 @@ static void do_lasso_select_paintvert(ViewContext *vc, const int mcords[][2], sh EDBM_backbuf_border_mask_init(vc, mcords, moves, rect.xmin, rect.ymin, rect.xmax, rect.ymax); - edbm_backbuf_check_and_select_verts_obmode(me, select); + edbm_backbuf_check_and_select_verts_obmode(me, sel_op); EDBM_backbuf_free(); } else { LassoSelectUserData data; - view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, select); + view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, sel_op); ED_view3d_init_mats_rv3d(vc->obact, vc->rv3d); @@ -771,12 +909,13 @@ static void do_lasso_select_paintvert(ViewContext *vc, const int mcords[][2], sh } - if (select == false) { + if (SEL_OP_CAN_DESELECT(sel_op)) { BKE_mesh_mselect_validate(me); } paintvert_flush_flags(ob); + paintvert_tag_select_update(vc->C, ob); } -static void do_lasso_select_paintface(ViewContext *vc, const int mcords[][2], short moves, bool extend, bool select) +static void do_lasso_select_paintface(ViewContext *vc, const int mcords[][2], short moves, const eSelectOp sel_op) { Object *ob = vc->obact; Mesh *me = ob->data; @@ -785,23 +924,25 @@ static void do_lasso_select_paintface(ViewContext *vc, const int mcords[][2], sh if (me == NULL || me->totpoly == 0) return; - if (extend == false && select) + if (SEL_OP_USE_PRE_DESELECT(sel_op)) { paintface_deselect_all_visible(ob, SEL_DESELECT, false); /* flush selection at the end */ + } bm_vertoffs = me->totpoly + 1; /* max index array */ BLI_lasso_boundbox(&rect, mcords, moves); EDBM_backbuf_border_mask_init(vc, mcords, moves, rect.xmin, rect.ymin, rect.xmax, rect.ymax); - edbm_backbuf_check_and_select_tfaces(me, select); + edbm_backbuf_check_and_select_tfaces(me, sel_op); EDBM_backbuf_free(); paintface_flush_flags(ob, SELECT); + paintface_tag_select_update(vc->C, ob); } #if 0 -static void do_lasso_select_node(int mcords[][2], short moves, const bool select) +static void do_lasso_select_node(int mcords[][2], short moves, const eSelectOp sel_op) { SpaceNode *snode = sa->spacedata.first; @@ -818,13 +959,13 @@ static void do_lasso_select_node(int mcords[][2], short moves, const bool select node_centf[1] = BLI_RCT_CENTER_Y(&node->totr); ipoco_to_areaco_noclip(G.v2d, node_centf, node_cent); - if (BLI_rcti_isect_pt_v(&rect, node_cent) && BLI_lasso_is_point_inside(mcords, moves, node_cent[0], node_cent[1])) { - if (select) { - node->flag |= SELECT; - } - else { - node->flag &= ~SELECT; - } + const bool is_select = node->flag & SELECT; + const bool is_inside = ( + BLI_rcti_isect_pt_v(&rect, node_cent) && + BLI_lasso_is_point_inside(mcords, moves, node_cent[0], node_cent[1])); + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + if (sel_op_result != -1) { + SET_FLAG_FROM_TEST(node->flag, sel_op_result, SELECT); } } BIF_undo_push("Lasso select nodes"); @@ -834,49 +975,61 @@ static void do_lasso_select_node(int mcords[][2], short moves, const bool select static void view3d_lasso_select( bContext *C, ViewContext *vc, const int mcords[][2], short moves, - bool extend, bool select) + const eSelectOp sel_op) { Object *ob = CTX_data_active_object(C); if (vc->obedit == NULL) { /* Object Mode */ - if (BKE_paint_select_face_test(ob)) - do_lasso_select_paintface(vc, mcords, moves, extend, select); - else if (BKE_paint_select_vert_test(ob)) - do_lasso_select_paintvert(vc, mcords, moves, extend, select); + if (BKE_paint_select_face_test(ob)) { + do_lasso_select_paintface(vc, mcords, moves, sel_op); + } + else if (BKE_paint_select_vert_test(ob)) { + do_lasso_select_paintvert(vc, mcords, moves, sel_op); + } else if (ob && (ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT))) { /* pass */ } - else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT)) - PE_lasso_select(C, mcords, moves, extend, select); + else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT)) { + PE_lasso_select(C, mcords, moves, sel_op); + } + else if (ob && (ob->mode & OB_MODE_POSE)) { + do_lasso_select_pose(vc, mcords, moves, sel_op); + } else { - do_lasso_select_objects(vc, mcords, moves, extend, select); - WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, vc->scene); + do_lasso_select_objects(vc, mcords, moves, sel_op); } } else { /* Edit Mode */ - switch (vc->obedit->type) { - case OB_MESH: - do_lasso_select_mesh(vc, mcords, moves, extend, select); - break; - case OB_CURVE: - case OB_SURF: - do_lasso_select_curve(vc, mcords, moves, extend, select); - break; - case OB_LATTICE: - do_lasso_select_lattice(vc, mcords, moves, extend, select); - break; - case OB_ARMATURE: - do_lasso_select_armature(vc, mcords, moves, extend, select); - break; - case OB_MBALL: - do_lasso_select_meta(vc, mcords, moves, extend, select); - break; - default: - assert(!"lasso select on incorrect object type"); - break; - } - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc->obedit->data); + FOREACH_OBJECT_IN_MODE_BEGIN (vc->view_layer, vc->v3d, ob->mode, ob_iter) { + ED_view3d_viewcontext_init_object(vc, ob_iter); + + switch (vc->obedit->type) { + case OB_MESH: + do_lasso_select_mesh(vc, mcords, moves, sel_op); + break; + case OB_CURVE: + case OB_SURF: + do_lasso_select_curve(vc, mcords, moves, sel_op); + break; + case OB_LATTICE: + do_lasso_select_lattice(vc, mcords, moves, sel_op); + break; + case OB_ARMATURE: + do_lasso_select_armature(vc, mcords, moves, sel_op); + break; + case OB_MBALL: + do_lasso_select_meta(vc, mcords, moves, sel_op); + break; + default: + assert(!"lasso select on incorrect object type"); + break; + } + + DEG_id_tag_update(vc->obedit->data, DEG_TAG_SELECT_UPDATE); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc->obedit->data); + } + FOREACH_OBJECT_IN_MODE_END; } } @@ -890,15 +1043,13 @@ static int view3d_lasso_select_exec(bContext *C, wmOperator *op) const int (*mcords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcords_tot); if (mcords) { - bool extend, select; view3d_operator_needs_opengl(C); /* setup view context for argument to callbacks */ ED_view3d_viewcontext_init(C, &vc); - extend = RNA_boolean_get(op->ptr, "extend"); - select = !RNA_boolean_get(op->ptr, "deselect"); - view3d_lasso_select(C, &vc, mcords, mcords_tot, extend, select); + eSelectOp sel_op = RNA_enum_get(op->ptr, "mode"); + view3d_lasso_select(C, &vc, mcords, mcords_tot, sel_op); MEM_freeN((void *)mcords); @@ -923,7 +1074,8 @@ void VIEW3D_OT_select_lasso(wmOperatorType *ot) ot->flag = OPTYPE_UNDO; /* properties */ - WM_operator_properties_gesture_lasso_select(ot); + WM_operator_properties_gesture_lasso(ot); + WM_operator_properties_select_operation(ot); } /* ************************** mouse select ************************* */ @@ -974,8 +1126,8 @@ static int object_select_menu_exec(bContext *C, wmOperator *op) if (!toggle) { CTX_DATA_BEGIN (C, Base *, base, selectable_bases) { - if (base->flag & SELECT) { - ED_base_object_select(base, BA_DESELECT); + if ((base->flag & BASE_SELECTED) != 0) { + ED_object_base_select(base, BA_DESELECT); changed = true; } } @@ -986,8 +1138,8 @@ static int object_select_menu_exec(bContext *C, wmOperator *op) { /* this is a bit dodjy, there should only be ONE object with this name, but library objects can mess this up */ if (STREQ(name, base->object->id.name + 2)) { - ED_base_object_activate(C, base); - ED_base_object_select(base, BA_SELECT); + ED_object_base_activate(C, base); + ED_object_base_select(base, BA_SELECT); changed = true; } } @@ -998,7 +1150,9 @@ static int object_select_menu_exec(bContext *C, wmOperator *op) /* undo? */ if (changed) { - WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, CTX_data_scene(C)); + Scene *scene = CTX_data_scene(C); + DEG_id_tag_update(&scene->id, DEG_TAG_SELECT_UPDATE); + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); return OPERATOR_FINISHED; } else { @@ -1031,14 +1185,12 @@ void VIEW3D_OT_select_menu(wmOperatorType *ot) RNA_def_boolean(ot->srna, "toggle", 0, "Toggle", "Toggle selection instead of deselecting everything first"); } -static void deselectall_except(Scene *scene, Base *b) /* deselect all except b */ +static void deselectall_except(ViewLayer *view_layer, Base *b) /* deselect all except b */ { - Base *base; - - for (base = FIRSTBASE; base; base = base->next) { - if (base->flag & SELECT) { + for (Base *base = view_layer->object_bases.first; base; base = base->next) { + if (base->flag & BASE_SELECTED) { if (b != base) { - ED_base_object_select(base, BA_DESELECT); + ED_object_base_select(base, BA_DESELECT); } } } @@ -1052,6 +1204,7 @@ static Base *object_mouse_select_menu( bool ok; LinkNode *linklist = NULL; + /* handle base->object->select_color */ CTX_DATA_BEGIN (C, Base *, base, selectable_bases) { ok = false; @@ -1060,7 +1213,7 @@ static Base *object_mouse_select_menu( if (buffer) { for (int a = 0; a < hits; a++) { /* index was converted */ - if (base->selcol == (buffer[(4 * a) + 3] & ~0xFFFF0000)) { + if (base->object->select_color == (buffer[(4 * a) + 3] & ~0xFFFF0000)) { ok = true; break; } @@ -1160,7 +1313,7 @@ static int selectbuffer_ret_hits_5(unsigned int *buffer, const int hits15, const /* so check three selection levels and compare */ static int mixed_bones_object_selectbuffer( ViewContext *vc, unsigned int *buffer, const int mval[2], - bool use_cycle, bool enumerate, + bool use_cycle, bool enumerate, eV3DSelectObjectFilter select_filter, bool *r_do_nearest) { rcti rect; @@ -1172,7 +1325,7 @@ static int mixed_bones_object_selectbuffer( /* define if we use solid nearest select or not */ if (use_cycle) { - if (v3d->drawtype > OB_WIRE) { + if (v3d->shading.type > OB_WIRE) { do_nearest = true; if (len_manhattan_v2v2_int(mval, last_mval) < 3) { do_nearest = false; @@ -1181,7 +1334,7 @@ static int mixed_bones_object_selectbuffer( copy_v2_v2_int(last_mval, mval); } else { - if (v3d->drawtype > OB_WIRE) { + if (v3d->shading.type > OB_WIRE) { do_nearest = true; } } @@ -1199,7 +1352,7 @@ static int mixed_bones_object_selectbuffer( view3d_opengl_select_cache_begin(); BLI_rcti_init_pt_radius(&rect, mval, 14); - hits15 = view3d_opengl_select(vc, buffer, MAXPICKBUF, &rect, select_mode); + hits15 = view3d_opengl_select(vc, buffer, MAXPICKBUF, &rect, select_mode, select_filter); if (hits15 == 1) { hits = selectbuffer_ret_hits_15(buffer, hits15); goto finally; @@ -1210,7 +1363,7 @@ static int mixed_bones_object_selectbuffer( offs = 4 * hits15; BLI_rcti_init_pt_radius(&rect, mval, 9); - hits9 = view3d_opengl_select(vc, buffer + offs, MAXPICKBUF - offs, &rect, select_mode); + hits9 = view3d_opengl_select(vc, buffer + offs, MAXPICKBUF - offs, &rect, select_mode, select_filter); if (hits9 == 1) { hits = selectbuffer_ret_hits_9(buffer, hits15, hits9); goto finally; @@ -1220,7 +1373,7 @@ static int mixed_bones_object_selectbuffer( offs += 4 * hits9; BLI_rcti_init_pt_radius(&rect, mval, 5); - hits5 = view3d_opengl_select(vc, buffer + offs, MAXPICKBUF - offs, &rect, select_mode); + hits5 = view3d_opengl_select(vc, buffer + offs, MAXPICKBUF - offs, &rect, select_mode, select_filter); if (hits5 == 1) { hits = selectbuffer_ret_hits_5(buffer, hits15, hits9, hits5); goto finally; @@ -1242,6 +1395,25 @@ static int mixed_bones_object_selectbuffer( finally: view3d_opengl_select_cache_end(); + if (vc->scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) { + const bool is_pose_mode = ( + (vc->obact && vc->obact->mode & OB_MODE_POSE) || + (select_filter == VIEW3D_SELECT_FILTER_WPAINT_POSE_MODE_LOCK)); + struct { + uint data[4]; + } *buffer4 = (void *)buffer; + uint j = 0; + for (uint i = 0; i < hits; i++) { + if (((buffer4[i].data[3] & 0xFFFF0000) != 0) == is_pose_mode) { + if (i != j) { + buffer4[j] = buffer4[i]; + } + j++; + } + } + hits = j; + } + return hits; } @@ -1250,7 +1422,7 @@ static Base *mouse_select_eval_buffer( ViewContext *vc, const uint *buffer, int hits, Base *startbase, bool has_bones, bool do_nearest) { - Scene *scene = vc->scene; + ViewLayer *view_layer = vc->view_layer; View3D *v3d = vc->v3d; Base *base, *basact = NULL; int a; @@ -1271,7 +1443,9 @@ static Base *mouse_select_eval_buffer( } else { /* only exclude active object when it is selected... */ - if (BASACT && (BASACT->flag & SELECT) && hits > 1) notcol = BASACT->selcol; + if (BASACT(view_layer) && (BASACT(view_layer)->flag & BASE_SELECTED) && hits > 1) { + notcol = BASACT(view_layer)->object->select_color; + } for (a = 0; a < hits; a++) { if (min > buffer[4 * a + 1] && notcol != (buffer[4 * a + 3] & 0xFFFF)) { @@ -1281,10 +1455,10 @@ static Base *mouse_select_eval_buffer( } } - base = FIRSTBASE; + base = FIRSTBASE(view_layer); while (base) { if (BASE_SELECTABLE(v3d, base)) { - if (base->selcol == selcol) break; + if (base->object->select_color == selcol) break; } base = base->next; } @@ -1296,9 +1470,9 @@ static Base *mouse_select_eval_buffer( while (base) { /* skip objects with select restriction, to prevent prematurely ending this loop * with an un-selectable choice */ - if (base->object->restrictflag & OB_RESTRICT_SELECT) { + if ((base->flag & BASE_SELECTABLE) == 0) { base = base->next; - if (base == NULL) base = FIRSTBASE; + if (base == NULL) base = FIRSTBASE(view_layer); if (base == startbase) break; } @@ -1307,12 +1481,12 @@ static Base *mouse_select_eval_buffer( if (has_bones) { /* skip non-bone objects */ if ((buffer[4 * a + 3] & 0xFFFF0000)) { - if (base->selcol == (buffer[(4 * a) + 3] & 0xFFFF)) + if (base->object->select_color == (buffer[(4 * a) + 3] & 0xFFFF)) basact = base; } } else { - if (base->selcol == (buffer[(4 * a) + 3] & 0xFFFF)) + if (base->object->select_color == (buffer[(4 * a) + 3] & 0xFFFF)) basact = base; } } @@ -1321,7 +1495,7 @@ static Base *mouse_select_eval_buffer( if (basact) break; base = base->next; - if (base == NULL) base = FIRSTBASE; + if (base == NULL) base = FIRSTBASE(view_layer); if (base == startbase) break; } } @@ -1340,18 +1514,34 @@ Base *ED_view3d_give_base_under_cursor(bContext *C, const int mval[2]) /* setup view context for argument to callbacks */ view3d_operator_needs_opengl(C); + ED_view3d_viewcontext_init(C, &vc); - hits = mixed_bones_object_selectbuffer(&vc, buffer, mval, false, false, &do_nearest); + hits = mixed_bones_object_selectbuffer( + &vc, buffer, mval, + false, false, VIEW3D_SELECT_FILTER_NOP, + &do_nearest); if (hits > 0) { const bool has_bones = selectbuffer_has_bones(buffer, hits); - basact = mouse_select_eval_buffer(&vc, buffer, hits, vc.scene->base.first, has_bones, do_nearest); + basact = mouse_select_eval_buffer(&vc, buffer, hits, vc.view_layer->object_bases.first, has_bones, do_nearest); } return basact; } +Object *ED_view3d_give_object_under_cursor(bContext *C, const int mval[2]) +{ + Base *base = ED_view3d_give_base_under_cursor(C, mval); + if (base) return base->object; + return NULL; +} + +bool ED_view3d_is_object_under_cursor(bContext *C, const int mval[2]) +{ + return ED_view3d_give_object_under_cursor(C, mval) != NULL; +} + static void deselect_all_tracks(MovieTracking *tracking) { MovieTrackingObject *object; @@ -1378,16 +1568,17 @@ static bool ed_object_select_pick( { ViewContext vc; ARegion *ar = CTX_wm_region(C); - View3D *v3d = CTX_wm_view3d(C); Scene *scene = CTX_data_scene(C); - Base *base, *startbase = NULL, *basact = NULL, *oldbasact = NULL; + ViewLayer *view_layer = CTX_data_view_layer(C); + View3D *v3d = CTX_wm_view3d(C); + Base *base, *startbase = NULL, *basact = NULL, *oldbasact = BASACT(view_layer); + const eObjectMode object_mode = oldbasact ? oldbasact->object->mode : OB_MODE_OBJECT; bool is_obedit; float dist = ED_view3d_select_dist_px() * 1.3333f; bool retval = false; int hits; const float mval_fl[2] = {(float)mval[0], (float)mval[1]}; - /* setup view context for argument to callbacks */ ED_view3d_viewcontext_init(C, &vc); @@ -1397,9 +1588,12 @@ static bool ed_object_select_pick( vc.obedit = NULL; } + /* In pose mode we don't want to mess with object selection. */ + const bool is_pose_mode = (vc.obact && vc.obact->mode & OB_MODE_POSE); + /* always start list from basact in wire mode */ - startbase = FIRSTBASE; - if (BASACT && BASACT->next) startbase = BASACT->next; + startbase = FIRSTBASE(view_layer); + if (BASACT(view_layer) && BASACT(view_layer)->next) startbase = BASACT(view_layer)->next; /* This block uses the control key to make the object selected by its center point rather than its contents */ /* in editmode do not activate */ @@ -1419,7 +1613,7 @@ static bool ed_object_select_pick( V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_WIN | V3D_PROJ_TEST_CLIP_NEAR) == V3D_PROJ_RET_OK) { float dist_temp = len_manhattan_v2v2(mval_fl, screen_co); - if (base == BASACT) dist_temp += 10.0f; + if (base == BASACT(view_layer)) dist_temp += 10.0f; if (dist_temp < dist) { dist = dist_temp; basact = base; @@ -1428,10 +1622,23 @@ static bool ed_object_select_pick( } base = base->next; - if (base == NULL) base = FIRSTBASE; + if (base == NULL) base = FIRSTBASE(view_layer); if (base == startbase) break; } } + if (scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) { + if (is_obedit == false) { + if (basact && !BKE_object_is_mode_compat(basact->object, object_mode)) { + if (object_mode == OB_MODE_OBJECT) { + struct Main *bmain = CTX_data_main(C); + ED_object_mode_generic_exit(bmain, vc.depsgraph, scene, basact->object); + } + if (!BKE_object_is_mode_compat(basact->object, object_mode)) { + basact = NULL; + } + } + } + } } else { unsigned int buffer[MAXPICKBUF]; @@ -1440,7 +1647,12 @@ static bool ed_object_select_pick( // TIMEIT_START(select_time); /* if objects have posemode set, the bones are in the same selection buffer */ - hits = mixed_bones_object_selectbuffer(&vc, buffer, mval, true, enumerate, &do_nearest); + const eV3DSelectObjectFilter select_filter = ( + (object == false) ? ED_view3d_select_filter_from_mode(scene, vc.obact) : VIEW3D_SELECT_FILTER_NOP); + hits = mixed_bones_object_selectbuffer( + &vc, buffer, mval, + true, enumerate, select_filter, + &do_nearest); // TIMEIT_END(select_time); @@ -1458,7 +1670,7 @@ static bool ed_object_select_pick( if (has_bones && basact) { if (basact->object->type == OB_CAMERA) { - if (BASACT == basact) { + if (BASACT(view_layer) == basact) { int i, hitresult; bool changed = false; @@ -1467,7 +1679,7 @@ static bool ed_object_select_pick( /* if there's bundles in buffer select bundles first, * so non-camera elements should be ignored in buffer */ - if (basact->selcol != (hitresult & 0xFFFF)) { + if (basact->object->select_color != (hitresult & 0xFFFF)) { continue; } @@ -1496,11 +1708,13 @@ static bool ed_object_select_pick( changed = true; } - basact->flag |= SELECT; - basact->object->flag = basact->flag; + basact->flag |= BASE_SELECTED; + BKE_scene_object_base_flag_sync_from_base(basact); retval = true; + DEG_id_tag_update(&scene->id, DEG_TAG_SELECT_UPDATE); + DEG_id_tag_update(&clip->id, DEG_TAG_SELECT_UPDATE); WM_event_add_notifier(C, NC_MOVIECLIP | ND_SELECT, track); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); @@ -1516,30 +1730,58 @@ static bool ed_object_select_pick( } } else if (ED_armature_pose_select_pick_with_buffer( - scene, basact, buffer, hits, extend, deselect, toggle, do_nearest)) + view_layer, v3d, basact, buffer, hits, extend, deselect, toggle, do_nearest)) { /* then bone is found */ /* we make the armature selected: * not-selected active object in posemode won't work well for tools */ - basact->flag |= SELECT; - basact->object->flag = basact->flag; + basact->flag |= BASE_SELECTED; + BKE_scene_object_base_flag_sync_from_base(basact); retval = true; WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, basact->object); WM_event_add_notifier(C, NC_OBJECT | ND_BONE_ACTIVE, basact->object); /* in weightpaint, we use selected bone to select vertexgroup, so no switch to new active object */ - if (BASACT && (BASACT->object->mode & OB_MODE_WEIGHT_PAINT)) { + if (BASACT(view_layer) && (BASACT(view_layer)->object->mode & OB_MODE_WEIGHT_PAINT)) { /* prevent activating */ basact = NULL; } } /* prevent bone selecting to pass on to object selecting */ - if (basact == BASACT) + if (basact == BASACT(view_layer)) basact = NULL; } + + if (scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) { + if (is_obedit == false) { + if (basact && !BKE_object_is_mode_compat(basact->object, object_mode)) { + if (object_mode == OB_MODE_OBJECT) { + struct Main *bmain = CTX_data_main(C); + ED_object_mode_generic_exit(bmain, vc.depsgraph, scene, basact->object); + } + if (!BKE_object_is_mode_compat(basact->object, object_mode)) { + basact = NULL; + } + } + } + } + } + } + + if (scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) { + /* Disallow switching modes, + * special exception for edit-mode - vertex-parent operator. */ + if (is_obedit == false) { + if (oldbasact && basact) { + if ((oldbasact->object->mode != basact->object->mode) && + (oldbasact->object->mode & basact->object->mode) == 0) + { + basact = NULL; + } + } } } @@ -1549,40 +1791,63 @@ static bool ed_object_select_pick( if (vc.obedit) { /* only do select */ - deselectall_except(scene, basact); - ED_base_object_select(basact, BA_SELECT); + deselectall_except(view_layer, basact); + ED_object_base_select(basact, BA_SELECT); } /* also prevent making it active on mouse selection */ else if (BASE_SELECTABLE(v3d, basact)) { - - oldbasact = BASACT; - if (extend) { - ED_base_object_select(basact, BA_SELECT); + ED_object_base_select(basact, BA_SELECT); } else if (deselect) { - ED_base_object_select(basact, BA_DESELECT); + ED_object_base_select(basact, BA_DESELECT); } else if (toggle) { - if (basact->flag & SELECT) { + if (basact->flag & BASE_SELECTED) { if (basact == oldbasact) { - ED_base_object_select(basact, BA_DESELECT); + ED_object_base_select(basact, BA_DESELECT); } } else { - ED_base_object_select(basact, BA_SELECT); + ED_object_base_select(basact, BA_SELECT); } } else { - deselectall_except(scene, basact); - ED_base_object_select(basact, BA_SELECT); + /* When enabled, this puts other objects out of multi pose-mode. */ + if (is_pose_mode == false) { + deselectall_except(view_layer, basact); + ED_object_base_select(basact, BA_SELECT); + } } if ((oldbasact != basact) && (is_obedit == false)) { - ED_base_object_activate(C, basact); /* adds notifier */ + ED_object_base_activate(C, basact); /* adds notifier */ + } + + /* Set special modes for grease pencil + The grease pencil modes are not real modes, but a hack to make the interface + consistent, so need some tricks to keep UI synchronized */ + // XXX: This stuff needs reviewing (Aligorith) + if (false && + (((oldbasact) && oldbasact->object->type == OB_GPENCIL) || + (basact->object->type == OB_GPENCIL))) + { + /* set cursor */ + if (ELEM(basact->object->mode, + OB_MODE_GPENCIL_PAINT, + OB_MODE_GPENCIL_SCULPT, + OB_MODE_GPENCIL_WEIGHT)) + { + ED_gpencil_toggle_brush_cursor(C, true, NULL); + } + else { + /* TODO: maybe is better use restore */ + ED_gpencil_toggle_brush_cursor(C, false, NULL); + } } } + DEG_id_tag_update(&scene->id, DEG_TAG_SELECT_UPDATE); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); } @@ -1596,17 +1861,16 @@ typedef struct BoxSelectUserData { const rcti *rect; const rctf *rect_fl; rctf _rect_fl; - bool select; + eSelectOp sel_op; /* runtime */ - int pass; bool is_done; bool is_changed; } BoxSelectUserData; static void view3d_userdata_boxselect_init( BoxSelectUserData *r_data, - ViewContext *vc, const rcti *rect, const bool select) + ViewContext *vc, const rcti *rect, const eSelectOp sel_op) { r_data->vc = vc; @@ -1614,10 +1878,9 @@ static void view3d_userdata_boxselect_init( r_data->rect_fl = &r_data->_rect_fl; BLI_rctf_rcti_copy(&r_data->_rect_fl, rect); - r_data->select = select; + r_data->sel_op = sel_op; /* runtime */ - r_data->pass = 0; r_data->is_done = false; r_data->is_changed = false; } @@ -1631,14 +1894,17 @@ bool edge_inside_circle(const float cent[2], float radius, const float screen_co static void do_paintvert_box_select__doSelectVert(void *userData, MVert *mv, const float screen_co[2], int UNUSED(index)) { BoxSelectUserData *data = userData; - - if (BLI_rctf_isect_pt_v(data->rect_fl, screen_co)) { - SET_FLAG_FROM_TEST(mv->flag, data->select, SELECT); + const bool is_select = mv->flag & SELECT; + const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co); + const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + if (sel_op_result != -1) { + SET_FLAG_FROM_TEST(mv->flag, sel_op_result, SELECT); } } -static int do_paintvert_box_select(ViewContext *vc, rcti *rect, bool select, bool extend) +static int do_paintvert_box_select( + ViewContext *vc, rcti *rect, const eSelectOp sel_op) { - const bool use_zbuf = (vc->v3d->flag & V3D_ZBUF_SELECT) != 0; + const bool use_zbuf = V3D_IS_ZBUF(vc->v3d); Mesh *me; MVert *mvert; struct ImBuf *ibuf; @@ -1655,8 +1921,9 @@ static int do_paintvert_box_select(ViewContext *vc, rcti *rect, bool select, boo return OPERATOR_CANCELLED; } - if (extend == false && select) + if (SEL_OP_USE_PRE_DESELECT(sel_op)) { paintvert_deselect_all_visible(vc->obact, SEL_DESELECT, false); + } if (use_zbuf) { selar = MEM_callocN(me->totvert + 1, "selar"); @@ -1686,10 +1953,12 @@ static int do_paintvert_box_select(ViewContext *vc, rcti *rect, bool select, boo mvert = me->mvert; for (a = 1; a <= me->totvert; a++, mvert++) { - if (selar[a]) { - if ((mvert->flag & ME_HIDE) == 0) { - if (select) mvert->flag |= SELECT; - else mvert->flag &= ~SELECT; + if ((mvert->flag & ME_HIDE) == 0) { + const bool is_select = mvert->flag & SELECT; + const bool is_inside = (selar[a] != 0); + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + if (sel_op_result != -1) { + SET_FLAG_FROM_TEST(mvert->flag, sel_op_result, SELECT); } } } @@ -1704,17 +1973,18 @@ static int do_paintvert_box_select(ViewContext *vc, rcti *rect, bool select, boo else { BoxSelectUserData data; - view3d_userdata_boxselect_init(&data, vc, rect, select); + view3d_userdata_boxselect_init(&data, vc, rect, sel_op); ED_view3d_init_mats_rv3d(vc->obact, vc->rv3d); meshobject_foreachScreenVert(vc, do_paintvert_box_select__doSelectVert, &data, V3D_PROJ_TEST_CLIP_DEFAULT); } - if (select == false) { + if (SEL_OP_CAN_DESELECT(sel_op)) { BKE_mesh_mselect_validate(me); } paintvert_flush_flags(vc->obact); + paintvert_tag_select_update(vc->C, vc->obact); return OPERATOR_FINISHED; } @@ -1723,39 +1993,42 @@ static void do_nurbs_box_select__doSelect( void *userData, Nurb *UNUSED(nu), BPoint *bp, BezTriple *bezt, int beztindex, const float screen_co[2]) { BoxSelectUserData *data = userData; - Object *obedit = data->vc->obedit; - Curve *cu = (Curve *)obedit->data; - if (BLI_rctf_isect_pt_v(data->rect_fl, screen_co)) { - if (bp) { - bp->f1 = data->select ? (bp->f1 | SELECT) : (bp->f1 & ~SELECT); + const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co); + if (bp) { + const bool is_select = bp->f1 & SELECT; + const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + if (sel_op_result != -1) { + SET_FLAG_FROM_TEST(bp->f1, sel_op_result, SELECT); } - else { - if (cu->drawflag & CU_HIDE_HANDLES) { - /* can only be (beztindex == 0) here since handles are hidden */ - bezt->f1 = bezt->f2 = bezt->f3 = data->select ? (bezt->f2 | SELECT) : (bezt->f2 & ~SELECT); + } + else { + if ((data->vc->v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_CU_HANDLES) == 0) { + /* can only be (beztindex == 0) here since handles are hidden */ + const bool is_select = bezt->f2 & SELECT; + const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + if (sel_op_result != -1) { + SET_FLAG_FROM_TEST(bezt->f2, sel_op_result, SELECT); } - else { - if (beztindex == 0) { - bezt->f1 = data->select ? (bezt->f1 | SELECT) : (bezt->f1 & ~SELECT); - } - else if (beztindex == 1) { - bezt->f2 = data->select ? (bezt->f2 | SELECT) : (bezt->f2 & ~SELECT); - } - else { - bezt->f3 = data->select ? (bezt->f3 | SELECT) : (bezt->f3 & ~SELECT); - } + bezt->f1 = bezt->f3 = bezt->f2; + } + else { + char *flag_p = (&bezt->f1) + beztindex; + const bool is_select = *flag_p & SELECT; + const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + if (sel_op_result != -1) { + SET_FLAG_FROM_TEST(*flag_p, sel_op_result, SELECT); } } } } -static int do_nurbs_box_select(ViewContext *vc, rcti *rect, bool select, bool extend) +static int do_nurbs_box_select(ViewContext *vc, rcti *rect, const eSelectOp sel_op) { BoxSelectUserData data; - view3d_userdata_boxselect_init(&data, vc, rect, select); + view3d_userdata_boxselect_init(&data, vc, rect, sel_op); - if (extend == false && select) { + if (SEL_OP_USE_PRE_DESELECT(sel_op)) { Curve *curve = (Curve *) vc->obedit->data; ED_curve_deselect_all(curve->editnurb); } @@ -1770,19 +2043,22 @@ static int do_nurbs_box_select(ViewContext *vc, rcti *rect, bool select, bool ex static void do_lattice_box_select__doSelect(void *userData, BPoint *bp, const float screen_co[2]) { BoxSelectUserData *data = userData; - - if (BLI_rctf_isect_pt_v(data->rect_fl, screen_co)) { - bp->f1 = data->select ? (bp->f1 | SELECT) : (bp->f1 & ~SELECT); + const bool is_select = bp->f1 & SELECT; + const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co); + const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + if (sel_op_result != -1) { + SET_FLAG_FROM_TEST(bp->f1, sel_op_result, SELECT); } } -static int do_lattice_box_select(ViewContext *vc, rcti *rect, bool select, bool extend) +static int do_lattice_box_select(ViewContext *vc, rcti *rect, const eSelectOp sel_op) { BoxSelectUserData data; - view3d_userdata_boxselect_init(&data, vc, rect, select); + view3d_userdata_boxselect_init(&data, vc, rect, sel_op); - if (extend == false && select) + if (SEL_OP_USE_PRE_DESELECT(sel_op)) { ED_lattice_flags_set(vc->obedit, 0); + } ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */ lattice_foreachScreenVert(vc, do_lattice_box_select__doSelect, &data, V3D_PROJ_TEST_CLIP_DEFAULT); @@ -1793,58 +2069,72 @@ static int do_lattice_box_select(ViewContext *vc, rcti *rect, bool select, bool static void do_mesh_box_select__doSelectVert(void *userData, BMVert *eve, const float screen_co[2], int UNUSED(index)) { BoxSelectUserData *data = userData; - - if (BLI_rctf_isect_pt_v(data->rect_fl, screen_co)) { - BM_vert_select_set(data->vc->em->bm, eve, data->select); + const bool is_select = BM_elem_flag_test(eve, BM_ELEM_SELECT); + const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co); + const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + if (sel_op_result != -1) { + BM_vert_select_set(data->vc->em->bm, eve, sel_op_result); } } -static void do_mesh_box_select__doSelectEdge( +static void do_mesh_box_select__doSelectEdge_pass0( void *userData, BMEdge *eed, const float screen_co_a[2], const float screen_co_b[2], int index) { BoxSelectUserData *data = userData; - - if (EDBM_backbuf_check(bm_solidoffs + index)) { - if (data->pass == 0) { - if (edge_fully_inside_rect(data->rect_fl, screen_co_a, screen_co_b)) { - BM_edge_select_set(data->vc->em->bm, eed, data->select); - data->is_done = true; - } - } - else { - if (edge_inside_rect(data->rect_fl, screen_co_a, screen_co_b)) { - BM_edge_select_set(data->vc->em->bm, eed, data->select); - } - } + const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT); + const bool is_inside = ( + EDBM_backbuf_check(bm_solidoffs + index) && + edge_fully_inside_rect(data->rect_fl, screen_co_a, screen_co_b)); + const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + if (sel_op_result != -1) { + BM_edge_select_set(data->vc->em->bm, eed, sel_op_result); + data->is_done = true; + } +} +static void do_mesh_box_select__doSelectEdge_pass1( + void *userData, BMEdge *eed, const float screen_co_a[2], const float screen_co_b[2], int index) +{ + BoxSelectUserData *data = userData; + const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT); + const bool is_inside = ( + EDBM_backbuf_check(bm_solidoffs + index) && + edge_inside_rect(data->rect_fl, screen_co_a, screen_co_b)); + const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + if (sel_op_result != -1) { + BM_edge_select_set(data->vc->em->bm, eed, sel_op_result); } } static void do_mesh_box_select__doSelectFace(void *userData, BMFace *efa, const float screen_co[2], int UNUSED(index)) { BoxSelectUserData *data = userData; - - if (BLI_rctf_isect_pt_v(data->rect_fl, screen_co)) { - BM_face_select_set(data->vc->em->bm, efa, data->select); + const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT); + const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co); + const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + if (sel_op_result != -1) { + BM_face_select_set(data->vc->em->bm, efa, sel_op_result); } } -static int do_mesh_box_select(ViewContext *vc, rcti *rect, bool select, bool extend) +static int do_mesh_box_select( + ViewContext *vc, rcti *rect, const eSelectOp sel_op) { BoxSelectUserData data; ToolSettings *ts = vc->scene->toolsettings; int bbsel; - view3d_userdata_boxselect_init(&data, vc, rect, select); + view3d_userdata_boxselect_init(&data, vc, rect, sel_op); - if (extend == false && select) + if (SEL_OP_USE_PRE_DESELECT(sel_op)) { EDBM_flag_disable_all(vc->em, BM_ELEM_SELECT); + } /* for non zbuf projections, don't change the GL state */ ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); - glLoadMatrixf(vc->rv3d->viewmat); + GPU_matrix_set(vc->rv3d->viewmat); bbsel = EDBM_backbuf_border_init(vc, rect->xmin, rect->ymin, rect->xmax, rect->ymax); if (ts->selectmode & SCE_SELECT_VERTEX) { if (bbsel) { - edbm_backbuf_check_and_select_verts(vc->em, select); + edbm_backbuf_check_and_select_verts(vc->em, sel_op); } else { mesh_foreachScreenVert(vc, do_mesh_box_select__doSelectVert, &data, V3D_PROJ_TEST_CLIP_DEFAULT); @@ -1852,19 +2142,15 @@ static int do_mesh_box_select(ViewContext *vc, rcti *rect, bool select, bool ext } if (ts->selectmode & SCE_SELECT_EDGE) { /* Does both bbsel and non-bbsel versions (need screen cos for both) */ - - data.pass = 0; - mesh_foreachScreenEdge(vc, do_mesh_box_select__doSelectEdge, &data, V3D_PROJ_TEST_CLIP_NEAR); - - if (data.is_done == 0) { - data.pass = 1; - mesh_foreachScreenEdge(vc, do_mesh_box_select__doSelectEdge, &data, V3D_PROJ_TEST_CLIP_NEAR); + mesh_foreachScreenEdge(vc, do_mesh_box_select__doSelectEdge_pass0, &data, V3D_PROJ_TEST_CLIP_NEAR); + if (data.is_done == false) { + mesh_foreachScreenEdge(vc, do_mesh_box_select__doSelectEdge_pass1, &data, V3D_PROJ_TEST_CLIP_NEAR); } } if (ts->selectmode & SCE_SELECT_FACE) { if (bbsel) { - edbm_backbuf_check_and_select_faces(vc->em, select); + edbm_backbuf_check_and_select_faces(vc->em, sel_op); } else { mesh_foreachScreenFace(vc, do_mesh_box_select__doSelectFace, &data, V3D_PROJ_TEST_CLIP_DEFAULT); @@ -1878,56 +2164,109 @@ static int do_mesh_box_select(ViewContext *vc, rcti *rect, bool select, bool ext return OPERATOR_FINISHED; } -static int do_meta_box_select(ViewContext *vc, rcti *rect, bool select, bool extend) +static int do_meta_box_select( + ViewContext *vc, + const rcti *rect, const eSelectOp sel_op) { - MetaBall *mb = (MetaBall *)vc->obedit->data; + Object *ob = vc->obedit; + MetaBall *mb = (MetaBall *)ob->data; MetaElem *ml; int a; unsigned int buffer[MAXPICKBUF]; int hits; - hits = view3d_opengl_select(vc, buffer, MAXPICKBUF, rect, VIEW3D_SELECT_ALL); + hits = view3d_opengl_select( + vc, buffer, MAXPICKBUF, rect, + VIEW3D_SELECT_ALL, VIEW3D_SELECT_FILTER_NOP); - if (extend == false && select) + if (SEL_OP_USE_PRE_DESELECT(sel_op)) { BKE_mball_deselect_all(mb); + } + + int metaelem_id = 0; + for (ml = mb->editelems->first; ml; ml = ml->next, metaelem_id += 0x10000) { + bool is_inside_radius = false; + bool is_inside_stiff = false; - for (ml = mb->editelems->first; ml; ml = ml->next) { for (a = 0; a < hits; a++) { - if (ml->selcol1 == buffer[(4 * a) + 3]) { - ml->flag |= MB_SCALE_RAD; - if (select) ml->flag |= SELECT; - else ml->flag &= ~SELECT; + int hitresult = buffer[(4 * a) + 3]; + + if (hitresult == -1) { + continue; + } + else if (hitresult & MBALL_NOSEL) { + continue; + } + + const uint hit_object = hitresult & 0xFFFF; + if (vc->obedit->select_color != hit_object) { + continue; + } + + if (metaelem_id != (hitresult & 0xFFFF0000 & ~(MBALLSEL_ANY))) { + continue; + } + + if (hitresult & MBALLSEL_RADIUS) { + is_inside_radius = true; break; } - if (ml->selcol2 == buffer[(4 * a) + 3]) { - ml->flag &= ~MB_SCALE_RAD; - if (select) ml->flag |= SELECT; - else ml->flag &= ~SELECT; + + if (hitresult & MBALLSEL_STIFF) { + is_inside_stiff = true; break; } } + if (is_inside_radius) { + ml->flag |= MB_SCALE_RAD; + } + if (is_inside_stiff) { + ml->flag &= ~MB_SCALE_RAD; + } + + const bool is_select = (ml->flag & SELECT); + const bool is_inside = is_inside_radius || is_inside_stiff; + + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + if (sel_op_result != -1) { + SET_FLAG_FROM_TEST(ml->flag, sel_op_result, SELECT); + } } return OPERATOR_FINISHED; } -static int do_armature_box_select(ViewContext *vc, rcti *rect, bool select, bool extend) +static int do_armature_box_select( + ViewContext *vc, + const rcti *rect, const eSelectOp sel_op) { - bArmature *arm = vc->obedit->data; + /* TODO(campbell): Full support for seleciton operations for edit bones. */ + const bool select = sel_op == SEL_OP_ADD; int a; unsigned int buffer[MAXPICKBUF]; int hits; - hits = view3d_opengl_select(vc, buffer, MAXPICKBUF, rect, VIEW3D_SELECT_ALL); + hits = view3d_opengl_select( + vc, buffer, MAXPICKBUF, rect, + VIEW3D_SELECT_ALL, VIEW3D_SELECT_FILTER_NOP); + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(vc->view_layer, vc->v3d, &objects_len); /* clear flag we use to detect point was affected */ - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) - ebone->flag &= ~BONE_DONE; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + bArmature *arm = obedit->data; + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + ebone->flag &= ~BONE_DONE; + } + } - if (extend == false && select) - ED_armature_edit_deselect_all_visible(vc->obedit); + if (SEL_OP_USE_PRE_DESELECT(sel_op)) { + ED_armature_edit_deselect_all_visible_multi(objects, objects_len); + } /* first we only check points inside the border */ for (a = 0; a < hits; a++) { @@ -1936,7 +2275,9 @@ static int do_armature_box_select(ViewContext *vc, rcti *rect, bool select, bool if ((index & 0xFFFF0000) == 0) { continue; } - EditBone *ebone = BLI_findlink(arm->edbo, index & ~(BONESEL_ANY)); + + EditBone *ebone; + ED_armature_object_and_ebone_from_select_buffer(objects, objects_len, index, &ebone); if ((select == false) || ((ebone->flag & BONE_UNSELECTABLE) == 0)) { if (index & BONESEL_TIP) { ebone->flag |= BONE_DONE; @@ -1954,10 +2295,14 @@ static int do_armature_box_select(ViewContext *vc, rcti *rect, bool select, bool } /* now we have to flush tag from parents... */ - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (ebone->parent && (ebone->flag & BONE_CONNECTED)) { - if (ebone->parent->flag & BONE_DONE) { - ebone->flag |= BONE_DONE; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + bArmature *arm = obedit->data; + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (ebone->parent && (ebone->flag & BONE_CONNECTED)) { + if (ebone->parent->flag & BONE_DONE) { + ebone->flag |= BONE_DONE; + } } } } @@ -1967,7 +2312,8 @@ static int do_armature_box_select(ViewContext *vc, rcti *rect, bool select, bool int index = buffer[(4 * a) + 3]; if (index != -1) { if (index & BONESEL_BONE) { - EditBone *ebone = BLI_findlink(arm->edbo, index & ~(BONESEL_ANY)); + EditBone *ebone; + ED_armature_object_and_ebone_from_select_buffer(objects, objects_len, index, &ebone); if ((select == false) || ((ebone->flag & BONE_UNSELECTABLE) == 0)) { if (!(ebone->flag & BONE_DONE)) { if (select) { @@ -1982,45 +2328,131 @@ static int do_armature_box_select(ViewContext *vc, rcti *rect, bool select, bool } } - ED_armature_edit_sync_selection(arm->edbo); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + bArmature *arm = obedit->data; + ED_armature_edit_sync_selection(arm->edbo); + } + + MEM_freeN(objects); return hits > 0 ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } -static int do_object_pose_box_select(bContext *C, ViewContext *vc, rcti *rect, bool select, bool extend) +/** + * Compare result of 'GPU_select': 'uint[4]', + * needed for when we need to align with object draw-order. + */ +static int opengl_bone_select_buffer_cmp(const void *sel_a_p, const void *sel_b_p) { - Bone *bone; - Object *ob = vc->obact; - unsigned int *vbuffer = NULL; /* selection buffer */ - unsigned int *col; /* color in buffer */ - int bone_only; - int bone_selected = 0; + /* 4th element is select id */ + uint sel_a = ((uint *)sel_a_p)[3]; + uint sel_b = ((uint *)sel_b_p)[3]; + +#ifdef __BIG_ENDIAN__ + BLI_endian_switch_uint32(&sel_a); + BLI_endian_switch_uint32(&sel_b); +#endif + + if (sel_a < sel_b) { + return -1; + } + else if (sel_a > sel_b) { + return 1; + } + else { + return 0; + } +} + +static int do_object_box_select(bContext *C, ViewContext *vc, rcti *rect, const eSelectOp sel_op) +{ + View3D *v3d = vc->v3d; + bool changed = false; int totobj = MAXPICKBUF; /* XXX solve later */ - int hits; - if ((ob) && (ob->mode & OB_MODE_POSE)) - bone_only = 1; - else - bone_only = 0; + /* selection buffer now has bones potentially too, so we add MAXPICKBUF */ + uint *vbuffer = MEM_mallocN(4 * (totobj + MAXPICKELEMS) * sizeof(uint[4]), "selection buffer"); + const eV3DSelectObjectFilter select_filter = ED_view3d_select_filter_from_mode(vc->scene, vc->obact); + const int hits = view3d_opengl_select( + vc, vbuffer, 4 * (totobj + MAXPICKELEMS), rect, + VIEW3D_SELECT_ALL, select_filter); - if (extend == false && select) { - if (bone_only) { - CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) - { - if ((select == false) || ((pchan->bone->flag & BONE_UNSELECTABLE) == 0)) { - pchan->bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - } + for (Base *base = vc->view_layer->object_bases.first; base; base = base->next) { + base->object->id.tag &= ~LIB_TAG_DOIT; + } + + Base **bases = NULL; + BLI_array_declare(bases); + + if (SEL_OP_USE_PRE_DESELECT(sel_op)) { + object_deselect_all_visible(vc->view_layer, vc->v3d); + changed = true; + } + + if ((hits == -1) && !SEL_OP_USE_OUTSIDE(sel_op)) { + goto finally; + } + + for (Base *base = vc->view_layer->object_bases.first; base; base = base->next) { + if (BASE_SELECTABLE(v3d, base)) { + if ((base->object->select_color & 0x0000FFFF) != 0) { + BLI_array_append(bases, base); } - CTX_DATA_END; } - else { - object_deselect_all_visible(vc->scene, vc->v3d); + } + + /* The draw order doesn't always match the order we populate the engine, see: T51695. */ + qsort(vbuffer, hits, sizeof(uint[4]), opengl_bone_select_buffer_cmp); + + for (const uint *col = vbuffer + 3, *col_end = col + (hits * 4); col < col_end; col += 4) { + Bone *bone; + Base *base = ED_armature_base_and_bone_from_select_buffer(bases, BLI_array_len(bases), *col, &bone); + base->object->id.tag |= LIB_TAG_DOIT; + } + + for (Base *base = vc->view_layer->object_bases.first; base && hits; base = base->next) { + if (BASE_SELECTABLE(v3d, base)) { + const bool is_select = base->flag & BASE_SELECTED; + const bool is_inside = base->object->id.tag & LIB_TAG_DOIT; + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + if (sel_op_result != -1) { + ED_object_base_select(base, sel_op_result ? BA_SELECT : BA_DESELECT); + changed = true; + } } } +finally: + if (bases != NULL) { + MEM_freeN(bases); + } + + MEM_freeN(vbuffer); + + if (changed) { + DEG_id_tag_update(&vc->scene->id, DEG_TAG_SELECT_UPDATE); + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, vc->scene); + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } +} + +static int do_pose_box_select(bContext *C, ViewContext *vc, rcti *rect, const eSelectOp sel_op) +{ + uint bases_len; + Base **bases = do_pose_tag_select_op_prepare(vc, &bases_len); + + int totobj = MAXPICKBUF; /* XXX solve later */ + /* selection buffer now has bones potentially too, so we add MAXPICKBUF */ - vbuffer = MEM_mallocN(4 * (totobj + MAXPICKELEMS) * sizeof(unsigned int), "selection buffer"); - hits = view3d_opengl_select(vc, vbuffer, 4 * (totobj + MAXPICKELEMS), rect, VIEW3D_SELECT_ALL); + uint *vbuffer = MEM_mallocN((totobj + MAXPICKELEMS) * sizeof(uint[4]), "selection buffer"); + const eV3DSelectObjectFilter select_filter = ED_view3d_select_filter_from_mode(vc->scene, vc->obact); + const int hits = view3d_opengl_select( + vc, vbuffer, 4 * (totobj + MAXPICKELEMS), rect, + VIEW3D_SELECT_ALL, select_filter); /* * LOGIC NOTES (theeth): * The buffer and ListBase have the same relative order, which makes the selection @@ -2028,73 +2460,66 @@ static int do_object_pose_box_select(bContext *C, ViewContext *vc, rcti *rect, b * is the same as the object, we have a hit and can move to the next color * and object pair, if not, just move to the next object, * keeping the same color until we have a hit. - * - * The buffer order is defined by OGL standard, hopefully no stupid GFX card - * does it incorrectly. */ - if (hits > 0) { /* no need to loop if there's no hit */ - Base *base; - col = vbuffer + 3; - - for (base = vc->scene->base.first; base && hits; base = base->next) { - if (BASE_SELECTABLE(vc->v3d, base)) { - while (base->selcol == (*col & 0xFFFF)) { /* we got an object */ - if (*col & 0xFFFF0000) { /* we got a bone */ - bone = ED_armature_bone_find_index(base->object, *col & ~(BONESEL_ANY)); - if (bone) { - if (select) { - if ((bone->flag & BONE_UNSELECTABLE) == 0) { - bone->flag |= BONE_SELECTED; - bone_selected = 1; - } - } - else { - bArmature *arm = base->object->data; - bone->flag &= ~BONE_SELECTED; - if (arm->act_bone == bone) - arm->act_bone = NULL; - } - } - } - else if (!bone_only) { - ED_base_object_select(base, select ? BA_SELECT : BA_DESELECT); - } + if (hits > 0) { + /* no need to loop if there's no hit */ - col += 4; /* next color */ - hits--; - if (hits == 0) break; - } - } + /* The draw order doesn't always match the order we populate the engine, see: T51695. */ + qsort(vbuffer, hits, sizeof(uint[4]), opengl_bone_select_buffer_cmp); - if (bone_selected) { - if (base->object && (base->object->type == OB_ARMATURE)) { - bArmature *arm = base->object->data; + for (const uint *col = vbuffer + 3, *col_end = col + (hits * 4); col < col_end; col += 4) { + Bone *bone; + Base *base = ED_armature_base_and_bone_from_select_buffer(bases, bases_len, *col, &bone); - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, base->object); + if (base == NULL) { + continue; + } - if (arm && (arm->flag & ARM_HAS_VIZ_DEPS)) { - /* mask modifier ('armature' mode), etc. */ - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + /* Loop over contiguous bone hits for 'base'. */ + for (; col != col_end; col += 4) { + /* should never fail */ + if (bone != NULL) { + base->object->id.tag |= LIB_TAG_DOIT; + bone->flag |= BONE_DONE; + } + + /* Select the next bone if we're not switching bases. */ + if (col + 4 != col_end) { + if ((base->object->select_color & 0x0000FFFF) != (col[4] & 0x0000FFFF)) { + break; + } + if (base->object->pose != NULL) { + const uint hit_bone = (col[4] & ~BONESEL_ANY) >> 16; + bPoseChannel *pchan = BLI_findlink(&base->object->pose->chanbase, hit_bone); + bone = pchan ? pchan->bone : NULL; + } + else { + bone = NULL; } } } } + } + const bool changed_multi = do_pose_tag_select_op_exec(bases, bases_len, sel_op); + if (changed_multi) { + DEG_id_tag_update(&vc->scene->id, DEG_TAG_SELECT_UPDATE); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, vc->scene); } + + if (bases != NULL) { + MEM_freeN(bases); + } MEM_freeN(vbuffer); return hits > 0 ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } -static int view3d_borderselect_exec(bContext *C, wmOperator *op) +static int view3d_box_select_exec(bContext *C, wmOperator *op) { ViewContext vc; rcti rect; - bool extend; - bool select; - int ret = OPERATOR_CANCELLED; view3d_operator_needs_opengl(C); @@ -2102,94 +2527,115 @@ static int view3d_borderselect_exec(bContext *C, wmOperator *op) /* setup view context for argument to callbacks */ ED_view3d_viewcontext_init(C, &vc); - select = !RNA_boolean_get(op->ptr, "deselect"); - extend = RNA_boolean_get(op->ptr, "extend"); + eSelectOp sel_op = RNA_enum_get(op->ptr, "mode"); WM_operator_properties_border_to_rcti(op, &rect); if (vc.obedit) { - switch (vc.obedit->type) { - case OB_MESH: - vc.em = BKE_editmesh_from_object(vc.obedit); - ret = do_mesh_box_select(&vc, &rect, select, extend); -// if (EM_texFaceCheck()) - if (ret & OPERATOR_FINISHED) { - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data); - } - break; - case OB_CURVE: - case OB_SURF: - ret = do_nurbs_box_select(&vc, &rect, select, extend); - if (ret & OPERATOR_FINISHED) { - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data); - } - break; - case OB_MBALL: - ret = do_meta_box_select(&vc, &rect, select, extend); - if (ret & OPERATOR_FINISHED) { - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data); - } - break; - case OB_ARMATURE: - ret = do_armature_box_select(&vc, &rect, select, extend); - if (ret & OPERATOR_FINISHED) { - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, vc.obedit); - } - break; - case OB_LATTICE: - ret = do_lattice_box_select(&vc, &rect, select, extend); - if (ret & OPERATOR_FINISHED) { - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data); - } - break; - default: - assert(!"border select on incorrect object type"); - break; + + FOREACH_OBJECT_IN_MODE_BEGIN (vc.view_layer, vc.v3d, vc.obedit->mode, ob_iter) { + ED_view3d_viewcontext_init_object(&vc, ob_iter); + + switch (vc.obedit->type) { + case OB_MESH: + vc.em = BKE_editmesh_from_object(vc.obedit); + ret |= do_mesh_box_select(&vc, &rect, sel_op); + if (ret & OPERATOR_FINISHED) { + DEG_id_tag_update(vc.obedit->data, DEG_TAG_SELECT_UPDATE); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data); + } + break; + case OB_CURVE: + case OB_SURF: + ret |= do_nurbs_box_select(&vc, &rect, sel_op); + if (ret & OPERATOR_FINISHED) { + DEG_id_tag_update(vc.obedit->data, DEG_TAG_SELECT_UPDATE); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data); + } + break; + case OB_MBALL: + ret |= do_meta_box_select(&vc, &rect, sel_op); + if (ret & OPERATOR_FINISHED) { + DEG_id_tag_update(vc.obedit->data, DEG_TAG_SELECT_UPDATE); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data); + } + break; + case OB_ARMATURE: + ret |= do_armature_box_select(&vc, &rect, sel_op); + if (ret & OPERATOR_FINISHED) { + DEG_id_tag_update(&vc.obedit->id, DEG_TAG_SELECT_UPDATE); + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, vc.obedit); + } + break; + case OB_LATTICE: + ret |= do_lattice_box_select(&vc, &rect, sel_op); + if (ret & OPERATOR_FINISHED) { + DEG_id_tag_update(vc.obedit->data, DEG_TAG_SELECT_UPDATE); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data); + } + break; + default: + assert(!"box select on incorrect object type"); + break; + } } + FOREACH_OBJECT_IN_MODE_END; } else { /* no editmode, unified for bones and objects */ if (vc.obact && vc.obact->mode & OB_MODE_SCULPT) { - ret = ED_sculpt_mask_box_select(C, &vc, &rect, select, extend); + /* XXX, this is not selection, could be it's own operator. */ + ret |= ED_sculpt_mask_box_select(C, &vc, &rect, sel_op == SEL_OP_ADD ? true : false); } else if (vc.obact && BKE_paint_select_face_test(vc.obact)) { - ret = do_paintface_box_select(&vc, &rect, select, extend); + ret |= do_paintface_box_select(&vc, &rect, sel_op); } else if (vc.obact && BKE_paint_select_vert_test(vc.obact)) { - ret = do_paintvert_box_select(&vc, &rect, select, extend); + ret |= do_paintvert_box_select(&vc, &rect, sel_op); } else if (vc.obact && vc.obact->mode & OB_MODE_PARTICLE_EDIT) { - ret = PE_border_select(C, &rect, select, extend); + ret |= PE_box_select(C, &rect, sel_op); + } + else if (vc.obact && vc.obact->mode & OB_MODE_POSE) { + ret |= do_pose_box_select(C, &vc, &rect, sel_op); } else { /* object mode with none active */ - ret = do_object_pose_box_select(C, &vc, &rect, select, extend); + ret |= do_object_box_select(C, &vc, &rect, sel_op); } } + if (ret & OPERATOR_FINISHED) { + ret = OPERATOR_FINISHED; + } + else { + ret = OPERATOR_CANCELLED; + } + return ret; } /* *****************Selection Operators******************* */ -/* ****** Border Select ****** */ -void VIEW3D_OT_select_border(wmOperatorType *ot) +/* ****** Box Select ****** */ +void VIEW3D_OT_select_box(wmOperatorType *ot) { /* identifiers */ - ot->name = "Border Select"; - ot->description = "Select items using border selection"; - ot->idname = "VIEW3D_OT_select_border"; + ot->name = "Box Select"; + ot->description = "Select items using box selection"; + ot->idname = "VIEW3D_OT_select_box"; /* api callbacks */ - ot->invoke = WM_gesture_border_invoke; - ot->exec = view3d_borderselect_exec; - ot->modal = WM_gesture_border_modal; + ot->invoke = WM_gesture_box_invoke; + ot->exec = view3d_box_select_exec; + ot->modal = WM_gesture_box_modal; ot->poll = view3d_selectable_data; - ot->cancel = WM_gesture_border_cancel; + ot->cancel = WM_gesture_box_cancel; /* flags */ ot->flag = OPTYPE_UNDO; /* rna */ - WM_operator_properties_gesture_border_select(ot); + WM_operator_properties_gesture_box(ot); + WM_operator_properties_select_operation(ot); } @@ -2200,7 +2646,7 @@ static bool ed_wpaint_vertex_select_pick( bool extend, bool deselect, bool toggle, Object *obact) { View3D *v3d = CTX_wm_view3d(C); - const bool use_zbuf = (v3d->flag & V3D_ZBUF_SELECT) != 0; + const bool use_zbuf = V3D_IS_ZBUF(v3d); Mesh *me = obact->data; /* already checked for NULL */ unsigned int index = 0; @@ -2231,7 +2677,7 @@ static bool ed_wpaint_vertex_select_pick( } paintvert_flush_flags(obact); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obact->data); + paintvert_tag_select_update(C, obact); return true; } return false; @@ -2319,8 +2765,8 @@ void VIEW3D_OT_select(wmOperatorType *ot) PropertyRNA *prop; /* identifiers */ - ot->name = "Activate/Select"; - ot->description = "Activate/select item(s)"; + ot->name = "Select"; + ot->description = "Select and activate item(s)"; ot->idname = "VIEW3D_OT_select"; /* api callbacks */ @@ -2334,9 +2780,12 @@ void VIEW3D_OT_select(wmOperatorType *ot) /* properties */ WM_operator_properties_mouse_select(ot); - RNA_def_boolean(ot->srna, "center", 0, "Center", "Use the object center when selecting, in editmode used to extend object selection"); - RNA_def_boolean(ot->srna, "enumerate", 0, "Enumerate", "List objects under the mouse (object mode only)"); - RNA_def_boolean(ot->srna, "object", 0, "Object", "Use object selection (editmode only)"); + prop = RNA_def_boolean(ot->srna, "center", 0, "Center", "Use the object center when selecting, in editmode used to extend object selection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "enumerate", 0, "Enumerate", "List objects under the mouse (object mode only)"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "object", 0, "Object", "Use object selection (editmode only)"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); prop = RNA_def_int_vector(ot->srna, "location", 2, NULL, INT_MIN, INT_MAX, "Location", "Mouse location", INT_MIN, INT_MAX); RNA_def_property_flag(prop, PROP_HIDDEN); @@ -2415,7 +2864,7 @@ static void mesh_circle_select(ViewContext *vc, const bool select, const int mva if (ts->selectmode & SCE_SELECT_VERTEX) { if (bbsel) { - edbm_backbuf_check_and_select_verts(vc->em, select); + edbm_backbuf_check_and_select_verts(vc->em, select ? SEL_OP_ADD : SEL_OP_SUB); } else { mesh_foreachScreenVert(vc, mesh_circle_doSelectVert, &data, V3D_PROJ_TEST_CLIP_DEFAULT); @@ -2424,7 +2873,7 @@ static void mesh_circle_select(ViewContext *vc, const bool select, const int mva if (ts->selectmode & SCE_SELECT_EDGE) { if (bbsel) { - edbm_backbuf_check_and_select_edges(vc->em, select); + edbm_backbuf_check_and_select_edges(vc->em, select ? SEL_OP_ADD : SEL_OP_SUB); } else { mesh_foreachScreenEdge(vc, mesh_circle_doSelectEdge, &data, V3D_PROJ_TEST_CLIP_NEAR); @@ -2433,7 +2882,7 @@ static void mesh_circle_select(ViewContext *vc, const bool select, const int mva if (ts->selectmode & SCE_SELECT_FACE) { if (bbsel) { - edbm_backbuf_check_and_select_faces(vc->em, select); + edbm_backbuf_check_and_select_faces(vc->em, select ? SEL_OP_ADD : SEL_OP_SUB); } else { mesh_foreachScreenFace(vc, mesh_circle_doSelectFace, &data, V3D_PROJ_TEST_CLIP_DEFAULT); @@ -2454,14 +2903,14 @@ static void paint_facesel_circle_select(ViewContext *vc, const bool select, cons bbsel = EDBM_backbuf_circle_init(vc, mval[0], mval[1], (short)(rad + 1.0f)); if (bbsel) { - edbm_backbuf_check_and_select_tfaces(me, select); + edbm_backbuf_check_and_select_tfaces(me, select ? SEL_OP_ADD : SEL_OP_SUB); EDBM_backbuf_free(); paintface_flush_flags(ob, SELECT); + paintface_tag_select_update(vc->C, ob); } } -static void paint_vertsel_circle_select_doSelectVert( - void *userData, MVert *mv, const float screen_co[2], int UNUSED(index)) +static void paint_vertsel_circle_select_doSelectVert(void *userData, MVert *mv, const float screen_co[2], int UNUSED(index)) { CircleSelectUserData *data = userData; @@ -2471,7 +2920,7 @@ static void paint_vertsel_circle_select_doSelectVert( } static void paint_vertsel_circle_select(ViewContext *vc, const bool select, const int mval[2], float rad) { - const bool use_zbuf = (vc->v3d->flag & V3D_ZBUF_SELECT) != 0; + const bool use_zbuf = V3D_IS_ZBUF(vc->v3d); Object *ob = vc->obact; Mesh *me = ob->data; bool bbsel; @@ -2482,7 +2931,7 @@ static void paint_vertsel_circle_select(ViewContext *vc, const bool select, cons bbsel = EDBM_backbuf_circle_init(vc, mval[0], mval[1], (short)(rad + 1.0f)); if (bbsel) { - edbm_backbuf_check_and_select_verts_obmode(me, select); + edbm_backbuf_check_and_select_verts_obmode(me, select ? SEL_OP_ADD : SEL_OP_SET); EDBM_backbuf_free(); } } @@ -2499,6 +2948,7 @@ static void paint_vertsel_circle_select(ViewContext *vc, const bool select, cons BKE_mesh_mselect_validate(me); } paintvert_flush_flags(ob); + paintvert_tag_select_update(vc->C, ob); } @@ -2506,15 +2956,13 @@ static void nurbscurve_circle_doSelect( void *userData, Nurb *UNUSED(nu), BPoint *bp, BezTriple *bezt, int beztindex, const float screen_co[2]) { CircleSelectUserData *data = userData; - Object *obedit = data->vc->obedit; - Curve *cu = (Curve *)obedit->data; if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) { if (bp) { bp->f1 = data->select ? (bp->f1 | SELECT) : (bp->f1 & ~SELECT); } else { - if (cu->drawflag & CU_HIDE_HANDLES) { + if ((data->vc->v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_CU_HANDLES) == 0) { /* can only be (beztindex == 0) here since handles are hidden */ bezt->f1 = bezt->f2 = bezt->f3 = data->select ? (bezt->f2 | SELECT) : (bezt->f2 & ~SELECT); } @@ -2632,14 +3080,7 @@ static void pose_circle_select(ViewContext *vc, const bool select, const int mva pose_foreachScreenBone(vc, do_circle_select_pose__doSelectBone, &data, V3D_PROJ_TEST_CLIP_DEFAULT); if (data.is_changed) { - bArmature *arm = vc->obact->data; - - WM_main_add_notifier(NC_OBJECT | ND_BONE_SELECT, vc->obact); - - if (arm->flag & ARM_HAS_VIZ_DEPS) { - /* mask modifier ('armature' mode), etc. */ - DAG_id_tag_update(&vc->obact->id, OB_RECALC_DATA); - } + ED_pose_bone_select_tag_update(vc->obact); } } @@ -2749,7 +3190,8 @@ static void mball_circle_select(ViewContext *vc, const bool select, const int mv /** Callbacks for circle selection in Editmode */ -static void obedit_circle_select(ViewContext *vc, const bool select, const int mval[2], float rad) +static void obedit_circle_select( + ViewContext *vc, const bool select, const int mval[2], float rad) { switch (vc->obedit->type) { case OB_MESH: @@ -2775,23 +3217,25 @@ static void obedit_circle_select(ViewContext *vc, const bool select, const int m static bool object_circle_select(ViewContext *vc, const bool select, const int mval[2], float rad) { - Scene *scene = vc->scene; + ViewLayer *view_layer = vc->view_layer; + View3D *v3d = vc->v3d; + const float radius_squared = rad * rad; const float mval_fl[2] = {mval[0], mval[1]}; bool changed = false; - const int select_flag = select ? SELECT : 0; + const int select_flag = select ? BASE_SELECTED : 0; Base *base; - for (base = FIRSTBASE; base; base = base->next) { - if (BASE_SELECTABLE(vc->v3d, base) && ((base->flag & SELECT) != select_flag)) { + for (base = FIRSTBASE(view_layer); base; base = base->next) { + if (BASE_SELECTABLE(v3d, base) && ((base->flag & BASE_SELECTED) != select_flag)) { float screen_co[2]; if (ED_view3d_project_float_global( vc->ar, base->object->obmat[3], screen_co, V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_WIN | V3D_PROJ_TEST_CLIP_NEAR) == V3D_PROJ_RET_OK) { if (len_squared_v2v2(mval_fl, screen_co) <= radius_squared) { - ED_base_object_select(base, select ? BA_SELECT : BA_DESELECT); + ED_object_base_select(base, select ? BA_SELECT : BA_DESELECT); changed = true; } } @@ -2805,43 +3249,54 @@ static bool object_circle_select(ViewContext *vc, const bool select, const int m static int view3d_circle_select_exec(bContext *C, wmOperator *op) { ViewContext vc; - ED_view3d_viewcontext_init(C, &vc); - Object *obact = vc.obact; - Object *obedit = vc.obedit; const int radius = RNA_int_get(op->ptr, "radius"); const bool select = !RNA_boolean_get(op->ptr, "deselect"); const int mval[2] = {RNA_int_get(op->ptr, "x"), RNA_int_get(op->ptr, "y")}; + + ED_view3d_viewcontext_init(C, &vc); + + Object *obact = vc.obact; + Object *obedit = vc.obedit; + if (obedit || BKE_paint_select_elem_test(obact) || (obact && (obact->mode & (OB_MODE_PARTICLE_EDIT | OB_MODE_POSE))) ) { view3d_operator_needs_opengl(C); - if (CTX_data_edit_object(C)) { - obedit_circle_select(&vc, select, mval, (float)radius); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obact->data); - } - else if (BKE_paint_select_face_test(obact)) { - paint_facesel_circle_select(&vc, select, mval, (float)radius); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obact->data); - } - else if (BKE_paint_select_vert_test(obact)) { - paint_vertsel_circle_select(&vc, select, mval, (float)radius); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obact->data); - } - else if (obact->mode & OB_MODE_POSE) { - pose_circle_select(&vc, select, mval, (float)radius); - } - else { - return PE_circle_select(C, select, mval, (float)radius); + FOREACH_OBJECT_IN_MODE_BEGIN (vc.view_layer, vc.v3d, obact->mode, ob_iter) { + ED_view3d_viewcontext_init_object(&vc, ob_iter); + + obact = vc.obact; + obedit = vc.obedit; + + if (CTX_data_edit_object(C)) { + obedit_circle_select(&vc, select, mval, (float)radius); + DEG_id_tag_update(obact->data, DEG_TAG_SELECT_UPDATE); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obact->data); + } + else if (BKE_paint_select_face_test(obact)) { + paint_facesel_circle_select(&vc, select, mval, (float)radius); + } + else if (BKE_paint_select_vert_test(obact)) { + paint_vertsel_circle_select(&vc, select, mval, (float)radius); + } + else if (obact->mode & OB_MODE_POSE) { + pose_circle_select(&vc, select, mval, (float)radius); + } + else { + return PE_circle_select(C, select, mval, (float)radius); + } } + FOREACH_OBJECT_IN_MODE_END; } else if (obact && obact->mode & OB_MODE_SCULPT) { return OPERATOR_CANCELLED; } else { if (object_circle_select(&vc, select, mval, (float)radius)) { + DEG_id_tag_update(&vc.scene->id, DEG_TAG_SELECT_UPDATE); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, vc.scene); } } |