View 3D: move picking arguments into a struct & minor refactor
- Add SelectPick_Params struct to make picking logic more straightforward and easier to extend. - Use `eSelectOp` instead of booleans (extend, deselect, toggle) which were used to represent 4 states (which wasn't obvious). - Handle deselect_all when pocking instead of view3d_select_exec, de-duplicate de-selection which was already needed in when replacing the selection in picking functions. - Handle outliner update & notifiers in the picking functions instead of view3d_select_exec. - Fix particle select deselect_all option which did nothing.
This commit is contained in:
@@ -366,59 +366,71 @@ bool paintface_minmax(Object *ob, float r_min[3], float r_max[3])
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool paintface_mouse_select(
|
||||
struct bContext *C, Object *ob, const int mval[2], bool extend, bool deselect, bool toggle)
|
||||
bool paintface_mouse_select(struct bContext *C,
|
||||
const int mval[2],
|
||||
const struct SelectPick_Params *params,
|
||||
Object *ob)
|
||||
{
|
||||
Mesh *me;
|
||||
MPoly *mpoly_sel;
|
||||
MPoly *mpoly_sel = NULL;
|
||||
uint index;
|
||||
bool changed = false;
|
||||
bool found = false;
|
||||
|
||||
/* Get the face under the cursor */
|
||||
me = BKE_mesh_from_object(ob);
|
||||
|
||||
if (!ED_mesh_pick_face(C, ob, mval, ED_MESH_PICK_DEFAULT_FACE_DIST, &index)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index >= me->totpoly) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mpoly_sel = me->mpoly + index;
|
||||
if (mpoly_sel->flag & ME_HIDE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* clear flags */
|
||||
if (!extend && !deselect && !toggle) {
|
||||
paintface_deselect_all_visible(C, ob, SEL_DESELECT, false);
|
||||
}
|
||||
|
||||
me->act_face = (int)index;
|
||||
|
||||
if (extend) {
|
||||
mpoly_sel->flag |= ME_FACE_SEL;
|
||||
}
|
||||
else if (deselect) {
|
||||
mpoly_sel->flag &= ~ME_FACE_SEL;
|
||||
}
|
||||
else if (toggle) {
|
||||
if (mpoly_sel->flag & ME_FACE_SEL) {
|
||||
mpoly_sel->flag &= ~ME_FACE_SEL;
|
||||
}
|
||||
else {
|
||||
mpoly_sel->flag |= ME_FACE_SEL;
|
||||
if (ED_mesh_pick_face(C, ob, mval, ED_MESH_PICK_DEFAULT_FACE_DIST, &index)) {
|
||||
if (index < me->totpoly) {
|
||||
mpoly_sel = me->mpoly + index;
|
||||
if ((mpoly_sel->flag & ME_HIDE) == 0) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
mpoly_sel->flag |= ME_FACE_SEL;
|
||||
|
||||
if ((params->sel_op == SEL_OP_SET) && (found || params->deselect_all)) {
|
||||
changed |= paintface_deselect_all_visible(C, ob, SEL_DESELECT, false);
|
||||
}
|
||||
|
||||
/* image window redraw */
|
||||
if (found) {
|
||||
me->act_face = (int)index;
|
||||
|
||||
paintface_flush_flags(C, ob, SELECT);
|
||||
ED_region_tag_redraw(CTX_wm_region(C)); /* XXX: should redraw all 3D views. */
|
||||
return true;
|
||||
switch (params->sel_op) {
|
||||
case SEL_OP_ADD: {
|
||||
mpoly_sel->flag |= ME_FACE_SEL;
|
||||
break;
|
||||
}
|
||||
case SEL_OP_SUB: {
|
||||
mpoly_sel->flag &= ~ME_FACE_SEL;
|
||||
break;
|
||||
}
|
||||
case SEL_OP_XOR: {
|
||||
if (mpoly_sel->flag & ME_FACE_SEL) {
|
||||
mpoly_sel->flag &= ~ME_FACE_SEL;
|
||||
}
|
||||
else {
|
||||
mpoly_sel->flag |= ME_FACE_SEL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SEL_OP_SET: {
|
||||
mpoly_sel->flag |= ME_FACE_SEL;
|
||||
break;
|
||||
}
|
||||
case SEL_OP_AND: {
|
||||
BLI_assert_unreachable(); /* Doesn't make sense for picking. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* image window redraw */
|
||||
|
||||
paintface_flush_flags(C, ob, SELECT);
|
||||
ED_region_tag_redraw(CTX_wm_region(C)); /* XXX: should redraw all 3D views. */
|
||||
changed = true;
|
||||
}
|
||||
return changed || found;
|
||||
}
|
||||
|
||||
void paintvert_flush_flags(Object *ob)
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "ED_mesh.h"
|
||||
#include "ED_object.h"
|
||||
#include "ED_screen.h"
|
||||
#include "ED_select_utils.h"
|
||||
#include "ED_uvedit.h"
|
||||
#include "ED_view3d.h"
|
||||
|
||||
@@ -700,7 +701,10 @@ static int edbm_shortest_path_pick_invoke(bContext *C, wmOperator *op, const wmE
|
||||
/* TODO(dfelinto): right now we try to find the closest element twice.
|
||||
* The ideal is to refactor EDBM_select_pick so it doesn't
|
||||
* have to pick the nearest vert/edge/face again. */
|
||||
EDBM_select_pick(C, event->mval, true, false, false);
|
||||
const struct SelectPick_Params params = {
|
||||
.sel_op = SEL_OP_ADD,
|
||||
};
|
||||
EDBM_select_pick(C, event->mval, ¶ms);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
|
||||
@@ -2016,7 +2016,7 @@ void MESH_OT_select_interior_faces(wmOperatorType *ot)
|
||||
* Gets called via generic mouse select operator.
|
||||
* \{ */
|
||||
|
||||
bool EDBM_select_pick(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle)
|
||||
bool EDBM_select_pick(bContext *C, const int mval[2], const struct SelectPick_Params *params)
|
||||
{
|
||||
ViewContext vc;
|
||||
|
||||
@@ -2033,100 +2033,148 @@ bool EDBM_select_pick(bContext *C, const int mval[2], bool extend, bool deselect
|
||||
uint bases_len = 0;
|
||||
Base **bases = BKE_view_layer_array_from_bases_in_edit_mode(vc.view_layer, vc.v3d, &bases_len);
|
||||
|
||||
bool ok = false;
|
||||
bool changed = false;
|
||||
const bool found = unified_findnearest(
|
||||
&vc, bases, bases_len, &base_index_active, &eve, &eed, &efa);
|
||||
|
||||
if (unified_findnearest(&vc, bases, bases_len, &base_index_active, &eve, &eed, &efa)) {
|
||||
if ((params->sel_op == SEL_OP_SET) && (found || params->deselect_all)) {
|
||||
/* Deselect everything. */
|
||||
for (uint base_index = 0; base_index < bases_len; base_index++) {
|
||||
Base *base_iter = bases[base_index];
|
||||
Object *ob_iter = base_iter->object;
|
||||
EDBM_flag_disable_all(BKE_editmesh_from_object(ob_iter), BM_ELEM_SELECT);
|
||||
DEG_id_tag_update(ob_iter->data, ID_RECALC_SELECT);
|
||||
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob_iter->data);
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (found) {
|
||||
Base *basact = bases[base_index_active];
|
||||
ED_view3d_viewcontext_init_object(&vc, basact->object);
|
||||
|
||||
/* Deselect everything */
|
||||
if (extend == false && deselect == false && toggle == false) {
|
||||
for (uint base_index = 0; base_index < bases_len; base_index++) {
|
||||
Base *base_iter = bases[base_index];
|
||||
Object *ob_iter = base_iter->object;
|
||||
EDBM_flag_disable_all(BKE_editmesh_from_object(ob_iter), BM_ELEM_SELECT);
|
||||
if (basact->object != ob_iter) {
|
||||
DEG_id_tag_update(ob_iter->data, ID_RECALC_SELECT);
|
||||
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob_iter->data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (efa) {
|
||||
if (extend) {
|
||||
/* set the last selected face */
|
||||
BM_mesh_active_face_set(vc.em->bm, efa);
|
||||
switch (params->sel_op) {
|
||||
case SEL_OP_ADD: {
|
||||
BM_mesh_active_face_set(vc.em->bm, efa);
|
||||
|
||||
/* Work-around: deselect first, so we can guarantee it will */
|
||||
/* be active even if it was already selected */
|
||||
BM_select_history_remove(vc.em->bm, efa);
|
||||
BM_face_select_set(vc.em->bm, efa, false);
|
||||
BM_select_history_store(vc.em->bm, efa);
|
||||
BM_face_select_set(vc.em->bm, efa, true);
|
||||
}
|
||||
else if (deselect) {
|
||||
BM_select_history_remove(vc.em->bm, efa);
|
||||
BM_face_select_set(vc.em->bm, efa, false);
|
||||
}
|
||||
else {
|
||||
/* set the last selected face */
|
||||
BM_mesh_active_face_set(vc.em->bm, efa);
|
||||
|
||||
if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
|
||||
BM_select_history_store(vc.em->bm, efa);
|
||||
BM_face_select_set(vc.em->bm, efa, true);
|
||||
}
|
||||
else if (toggle) {
|
||||
/* Work-around: deselect first, so we can guarantee it will
|
||||
* be active even if it was already selected. */
|
||||
BM_select_history_remove(vc.em->bm, efa);
|
||||
BM_face_select_set(vc.em->bm, efa, false);
|
||||
BM_select_history_store(vc.em->bm, efa);
|
||||
BM_face_select_set(vc.em->bm, efa, true);
|
||||
break;
|
||||
}
|
||||
case SEL_OP_SUB: {
|
||||
BM_select_history_remove(vc.em->bm, efa);
|
||||
BM_face_select_set(vc.em->bm, efa, false);
|
||||
break;
|
||||
}
|
||||
case SEL_OP_XOR: {
|
||||
BM_mesh_active_face_set(vc.em->bm, efa);
|
||||
if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
|
||||
BM_select_history_store(vc.em->bm, efa);
|
||||
BM_face_select_set(vc.em->bm, efa, true);
|
||||
}
|
||||
else {
|
||||
BM_select_history_remove(vc.em->bm, efa);
|
||||
BM_face_select_set(vc.em->bm, efa, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SEL_OP_SET: {
|
||||
BM_mesh_active_face_set(vc.em->bm, efa);
|
||||
if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
|
||||
BM_select_history_store(vc.em->bm, efa);
|
||||
BM_face_select_set(vc.em->bm, efa, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SEL_OP_AND: {
|
||||
BLI_assert_unreachable(); /* Doesn't make sense for picking. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (eed) {
|
||||
if (extend) {
|
||||
/* Work-around: deselect first, so we can guarantee it will */
|
||||
/* be active even if it was already selected */
|
||||
BM_select_history_remove(vc.em->bm, eed);
|
||||
BM_edge_select_set(vc.em->bm, eed, false);
|
||||
BM_select_history_store(vc.em->bm, eed);
|
||||
BM_edge_select_set(vc.em->bm, eed, true);
|
||||
}
|
||||
else if (deselect) {
|
||||
BM_select_history_remove(vc.em->bm, eed);
|
||||
BM_edge_select_set(vc.em->bm, eed, false);
|
||||
}
|
||||
else {
|
||||
if (!BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
|
||||
BM_select_history_store(vc.em->bm, eed);
|
||||
BM_edge_select_set(vc.em->bm, eed, true);
|
||||
}
|
||||
else if (toggle) {
|
||||
|
||||
switch (params->sel_op) {
|
||||
case SEL_OP_ADD: {
|
||||
/* Work-around: deselect first, so we can guarantee it will
|
||||
* be active even if it was already selected. */
|
||||
BM_select_history_remove(vc.em->bm, eed);
|
||||
BM_edge_select_set(vc.em->bm, eed, false);
|
||||
BM_select_history_store(vc.em->bm, eed);
|
||||
BM_edge_select_set(vc.em->bm, eed, true);
|
||||
break;
|
||||
}
|
||||
case SEL_OP_SUB: {
|
||||
BM_select_history_remove(vc.em->bm, eed);
|
||||
BM_edge_select_set(vc.em->bm, eed, false);
|
||||
break;
|
||||
}
|
||||
case SEL_OP_XOR: {
|
||||
if (!BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
|
||||
BM_select_history_store(vc.em->bm, eed);
|
||||
BM_edge_select_set(vc.em->bm, eed, true);
|
||||
}
|
||||
else {
|
||||
BM_select_history_remove(vc.em->bm, eed);
|
||||
BM_edge_select_set(vc.em->bm, eed, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SEL_OP_SET: {
|
||||
if (!BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
|
||||
BM_select_history_store(vc.em->bm, eve);
|
||||
BM_vert_select_set(vc.em->bm, eve, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SEL_OP_AND: {
|
||||
BLI_assert_unreachable(); /* Doesn't make sense for picking. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (eve) {
|
||||
if (extend) {
|
||||
/* Work-around: deselect first, so we can guarantee it will */
|
||||
/* be active even if it was already selected */
|
||||
BM_select_history_remove(vc.em->bm, eve);
|
||||
BM_vert_select_set(vc.em->bm, eve, false);
|
||||
BM_select_history_store(vc.em->bm, eve);
|
||||
BM_vert_select_set(vc.em->bm, eve, true);
|
||||
}
|
||||
else if (deselect) {
|
||||
BM_select_history_remove(vc.em->bm, eve);
|
||||
BM_vert_select_set(vc.em->bm, eve, false);
|
||||
}
|
||||
else {
|
||||
if (!BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
|
||||
BM_select_history_store(vc.em->bm, eve);
|
||||
BM_vert_select_set(vc.em->bm, eve, true);
|
||||
}
|
||||
else if (toggle) {
|
||||
switch (params->sel_op) {
|
||||
case SEL_OP_ADD: {
|
||||
/* Work-around: deselect first, so we can guarantee it will
|
||||
* be active even if it was already selected. */
|
||||
BM_select_history_remove(vc.em->bm, eve);
|
||||
BM_vert_select_set(vc.em->bm, eve, false);
|
||||
BM_select_history_store(vc.em->bm, eve);
|
||||
BM_vert_select_set(vc.em->bm, eve, true);
|
||||
break;
|
||||
}
|
||||
case SEL_OP_SUB: {
|
||||
BM_select_history_remove(vc.em->bm, eve);
|
||||
BM_vert_select_set(vc.em->bm, eve, false);
|
||||
break;
|
||||
}
|
||||
case SEL_OP_XOR: {
|
||||
if (!BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
|
||||
BM_select_history_store(vc.em->bm, eve);
|
||||
BM_vert_select_set(vc.em->bm, eve, true);
|
||||
}
|
||||
else {
|
||||
BM_select_history_remove(vc.em->bm, eve);
|
||||
BM_vert_select_set(vc.em->bm, eve, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SEL_OP_SET: {
|
||||
if (!BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
|
||||
BM_select_history_store(vc.em->bm, eve);
|
||||
BM_vert_select_set(vc.em->bm, eve, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SEL_OP_AND: {
|
||||
BLI_assert_unreachable(); /* Doesn't make sense for picking. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2168,12 +2216,12 @@ bool EDBM_select_pick(bContext *C, const int mval[2], bool extend, bool deselect
|
||||
DEG_id_tag_update(vc.obedit->data, ID_RECALC_SELECT);
|
||||
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data);
|
||||
|
||||
ok = true;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
MEM_freeN(bases);
|
||||
|
||||
return ok;
|
||||
return changed;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
#include "ED_object.h"
|
||||
#include "ED_outliner.h"
|
||||
#include "ED_screen.h"
|
||||
#include "ED_select_utils.h"
|
||||
#include "ED_transform.h"
|
||||
#include "ED_uvedit.h"
|
||||
#include "ED_view3d.h"
|
||||
@@ -8540,7 +8541,10 @@ static int edbm_point_normals_modal(bContext *C, wmOperator *op, const wmEvent *
|
||||
case EDBM_CLNOR_MODAL_POINTTO_SET_USE_SELECTED:
|
||||
new_mode = EDBM_CLNOR_POINTTO_MODE_COORDINATES;
|
||||
view3d_operator_needs_opengl(C);
|
||||
if (EDBM_select_pick(C, event->mval, false, false, false)) {
|
||||
const struct SelectPick_Params params = {
|
||||
.sel_op = SEL_OP_SET,
|
||||
};
|
||||
if (EDBM_select_pick(C, event->mval, ¶ms)) {
|
||||
/* Point to newly selected active. */
|
||||
ED_object_calc_active_center_for_editmode(obedit, false, target);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user