View 3D: refactor edit-mode meta-element selection

Meta-element selection now follows conventions for other picking
functions (e.g. EDBM_select_pick).

- Split meta-element find-nearest into a separate function.

- Cycle the meta-element starting from the active & selected
  instead of comparing & setting a static variable.

- Order elements using depth (from front-to-back)
  when cycling multiple elements.
This commit is contained in:
Campbell Barton 2022-03-16 16:57:22 +11:00
parent 9df27e7f00
commit 2d42187395
2 changed files with 148 additions and 128 deletions

View File

@ -12,6 +12,7 @@ extern "C" {
#endif
struct Base;
struct MetaElem;
struct Object;
struct SelectPick_Params;
struct UndoType;
@ -32,6 +33,11 @@ struct MetaElem *ED_mball_add_primitive(struct bContext *C,
float dia,
int type);
struct Base *ED_mball_base_and_elem_from_select_buffer(struct Base **bases,
uint bases_len,
const uint select_id,
struct MetaElem **r_ml);
/**
* Select meta-element with mouse click (user can select radius circle or stiffness circle).
*

View File

@ -736,15 +736,42 @@ void MBALL_OT_reveal_metaelems(wmOperatorType *ot)
/** \name Select Pick Utility
* \{ */
bool ED_mball_select_pick(bContext *C, const int mval[2], const struct SelectPick_Params *params)
Base *ED_mball_base_and_elem_from_select_buffer(Base **bases,
uint bases_len,
const uint select_id,
MetaElem **r_ml)
{
const uint hit_object = select_id & 0xFFFF;
Base *base = NULL;
MetaElem *ml = NULL;
/* TODO(campbell): optimize, eg: sort & binary search. */
for (uint base_index = 0; base_index < bases_len; base_index++) {
if (bases[base_index]->object->runtime.select_id == hit_object) {
base = bases[base_index];
break;
}
}
if (base != NULL) {
const uint hit_elem = (select_id & ~MBALLSEL_ANY) >> 16;
MetaBall *mb = base->object->data;
ml = BLI_findlink(mb->editelems, hit_elem);
}
*r_ml = ml;
return base;
}
static bool ed_mball_findnearest_metaelem(bContext *C,
const int mval[2],
bool use_cycle,
Base **r_base,
MetaElem **r_ml,
uint *r_selmask)
{
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
static MetaElem *startelem = NULL;
ViewContext vc;
int a, hits;
GPUSelectResult buffer[MAXPICKELEMS];
rcti rect;
bool changed = false;
bool found = false;
ED_view3d_viewcontext_init(C, &vc, depsgraph);
@ -755,144 +782,131 @@ bool ED_mball_select_pick(bContext *C, const int mval[2], const struct SelectPic
buffer,
ARRAY_SIZE(buffer),
&rect,
VIEW3D_SELECT_PICK_NEAREST,
use_cycle ? VIEW3D_SELECT_PICK_ALL : VIEW3D_SELECT_PICK_NEAREST,
VIEW3D_SELECT_FILTER_NOP);
FOREACH_BASE_IN_EDIT_MODE_BEGIN (vc.view_layer, vc.v3d, base) {
ED_view3d_viewcontext_init_object(&vc, base->object);
MetaBall *mb = (MetaBall *)base->object->data;
MetaElem *ml, *ml_act = NULL;
if (hits == 0) {
return false;
}
/* does startelem exist? */
ml = mb->editelems->first;
while (ml) {
if (ml == startelem) {
break;
}
ml = ml->next;
}
uint bases_len = 0;
Base **bases = BKE_view_layer_array_from_bases_in_edit_mode(vc.view_layer, vc.v3d, &bases_len);
if (ml == NULL) {
startelem = mb->editelems->first;
}
int hit_cycle_offset = 0;
if (use_cycle) {
/* When cycling, use the hit directly after the current active meta-element (when set). */
const int base_index = vc.obact->runtime.select_id;
MetaBall *mb = (MetaBall *)vc.obact->data;
MetaElem *ml = mb->lastelem;
if (ml && (ml->flag & SELECT)) {
const int ml_index = BLI_findindex(mb->editelems, ml);
BLI_assert(ml_index != -1);
if (hits > 0) {
int metaelem_id = 0;
ml = startelem;
while (ml) {
for (a = 0; a < hits; a++) {
const int hitresult = buffer[a].id;
if (hitresult == -1) {
continue;
}
const uint hit_object = hitresult & 0xFFFF;
if (vc.obedit->runtime.select_id != hit_object) {
continue;
}
if (metaelem_id != (hitresult & 0xFFFF0000 & ~MBALLSEL_ANY)) {
continue;
}
if (hitresult & MBALLSEL_RADIUS) {
ml->flag |= MB_SCALE_RAD;
ml_act = ml;
break;
}
if (hitresult & MBALLSEL_STIFF) {
ml->flag &= ~MB_SCALE_RAD;
ml_act = ml;
break;
}
/* Count backwards in case the active meta-element has multiple entries,
* ensure this steps onto the next meta-element. */
a = hits;
while (a--) {
const int select_id = buffer[a].id;
if (select_id == -1) {
continue;
}
if (ml_act) {
if (((select_id & 0xFFFF) == base_index) &&
((select_id & ~MBALLSEL_ANY) >> 16 == ml_index)) {
hit_cycle_offset = a + 1;
break;
}
ml = ml->next;
if (ml == NULL) {
ml = mb->editelems->first;
}
if (ml == startelem) {
break;
}
metaelem_id += 0x10000;
}
/* When some metaelem was found, then it is necessary to select or deselect it. */
if (ml_act) {
found = true;
if (params->sel_op == SEL_OP_SET) {
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
vc.view_layer, vc.v3d, &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob_iter = objects[ob_index];
if (ob_iter == base->object) {
continue;
}
BKE_mball_deselect_all((MetaBall *)ob_iter->data);
DEG_id_tag_update(ob_iter->data, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob_iter->data);
}
MEM_freeN(objects);
}
switch (params->sel_op) {
case SEL_OP_ADD: {
ml_act->flag |= SELECT;
break;
}
case SEL_OP_SUB: {
ml_act->flag &= ~SELECT;
break;
}
case SEL_OP_XOR: {
if (ml_act->flag & SELECT) {
ml_act->flag &= ~SELECT;
}
else {
ml_act->flag |= SELECT;
}
break;
}
case SEL_OP_SET: {
/* Deselect all existing metaelems */
BKE_mball_deselect_all(mb);
/* Select only metaelem clicked on */
ml_act->flag |= SELECT;
break;
}
case SEL_OP_AND: {
BLI_assert_unreachable(); /* Doesn't make sense for picking. */
break;
}
}
mb->lastelem = ml_act;
DEG_id_tag_update(&mb->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, mb);
if (vc.view_layer->basact != base) {
ED_object_base_activate(C, base);
}
changed = true;
}
}
}
FOREACH_BASE_IN_EDIT_MODE_END;
if (params->deselect_all && !found) {
ED_mball_deselect_all_multi(C);
for (a = 0; a < hits; a++) {
const int index = (hit_cycle_offset == 0) ? a : ((a + hit_cycle_offset) % hits);
const uint select_id = buffer[index].id;
if (select_id == -1) {
continue;
}
MetaElem *ml;
Base *base = ED_mball_base_and_elem_from_select_buffer(bases, bases_len, select_id, &ml);
if (ml == NULL) {
continue;
}
*r_base = base;
*r_ml = ml;
*r_selmask = select_id & MBALLSEL_ANY;
found = true;
break;
}
MEM_freeN(bases);
return found;
}
bool ED_mball_select_pick(bContext *C, const int mval[2], const struct SelectPick_Params *params)
{
Base *base = NULL;
MetaElem *ml = NULL;
uint selmask = 0;
bool changed = false;
bool found = ed_mball_findnearest_metaelem(C, mval, true, &base, &ml, &selmask);
if ((params->sel_op == SEL_OP_SET) && (found || params->deselect_all)) {
/* Deselect everything. */
changed |= ED_mball_deselect_all_multi(C);
}
if (found) {
if (selmask & MBALLSEL_RADIUS) {
ml->flag |= MB_SCALE_RAD;
}
else if (selmask & MBALLSEL_STIFF) {
ml->flag &= ~MB_SCALE_RAD;
}
switch (params->sel_op) {
case SEL_OP_ADD: {
ml->flag |= SELECT;
break;
}
case SEL_OP_SUB: {
ml->flag &= ~SELECT;
break;
}
case SEL_OP_XOR: {
if (ml->flag & SELECT) {
ml->flag &= ~SELECT;
}
else {
ml->flag |= SELECT;
}
break;
}
case SEL_OP_SET: {
/* Deselect has already been performed. */
ml->flag |= SELECT;
break;
}
case SEL_OP_AND: {
BLI_assert_unreachable(); /* Doesn't make sense for picking. */
break;
}
}
ViewLayer *view_layer = CTX_data_view_layer(C);
MetaBall *mb = (MetaBall *)base->object->data;
mb->lastelem = ml;
DEG_id_tag_update(&mb->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, mb);
if (view_layer->basact != base) {
ED_object_base_activate(C, base);
}
changed = true;
}