Add ability to get a selection list of bones

This adds the ability to get a selection list for both edit mode bones
and pose mode bones.

To do this the selection menu list logic had to be reworked a bit.
Before it only stored the names of objects. This might work will of
objects, however as stated in the code, it might fail for linked objects
(so multiple object can have the same name in some corner cases).

For bones it is a very common occurance where you can have multiple
armature that has the same bone names. So now it also stores the object
and bone pointers for this case.

Reviewed By: Sybren

Differential Revision: http://developer.blender.org/D10570

Fix T85796
This commit is contained in:
Sebastian Parborg 2021-04-08 17:21:01 +02:00
parent 5c4d24e1fd
commit 5e77ff79cc
Notes: blender-bot 2023-02-13 19:32:00 +01:00
Referenced by issue #85796, Alt+Click to select overlapping bones
6 changed files with 492 additions and 203 deletions

View File

@ -961,6 +961,129 @@ bool ED_armature_edit_deselect_all_visible_multi(bContext *C)
/** \name Select Cursor Pick API
* \{ */
bool ED_armature_edit_select_pick_bone(bContext *C,
Base *basact,
EditBone *ebone,
const int selmask,
const bool extend,
const bool deselect,
const bool toggle)
{
if (!ebone) {
return false;
}
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
BLI_assert(BKE_object_is_in_editmode(basact->object));
bArmature *arm = basact->object->data;
if (!EBONE_SELECTABLE(arm, ebone)) {
return false;
}
if (!extend && !deselect && !toggle) {
uint bases_len = 0;
Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
view_layer, v3d, &bases_len);
ED_armature_edit_deselect_all_multi_ex(bases, bases_len);
MEM_freeN(bases);
}
/* By definition the non-root connected bones have no root point drawn,
* so a root selection needs to be delivered to the parent tip. */
if (selmask & BONE_SELECTED) {
if (ebone->parent && (ebone->flag & BONE_CONNECTED)) {
/* Bone is in a chain. */
if (extend) {
/* Select this bone. */
ebone->flag |= BONE_TIPSEL;
ebone->parent->flag |= BONE_TIPSEL;
}
else if (deselect) {
/* Deselect this bone. */
ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED);
/* Only deselect parent tip if it is not selected. */
if (!(ebone->parent->flag & BONE_SELECTED)) {
ebone->parent->flag &= ~BONE_TIPSEL;
}
}
else if (toggle) {
/* Toggle inverts this bone's selection. */
if (ebone->flag & BONE_SELECTED) {
/* Deselect this bone. */
ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED);
/* Only deselect parent tip if it is not selected. */
if (!(ebone->parent->flag & BONE_SELECTED)) {
ebone->parent->flag &= ~BONE_TIPSEL;
}
}
else {
/* Select this bone. */
ebone->flag |= BONE_TIPSEL;
ebone->parent->flag |= BONE_TIPSEL;
}
}
else {
/* Select this bone. */
ebone->flag |= BONE_TIPSEL;
ebone->parent->flag |= BONE_TIPSEL;
}
}
else {
if (extend) {
ebone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
}
else if (deselect) {
ebone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL);
}
else if (toggle) {
/* Toggle inverts this bone's selection. */
if (ebone->flag & BONE_SELECTED) {
ebone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL);
}
else {
ebone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
}
}
else {
ebone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
}
}
}
else {
if (extend) {
ebone->flag |= selmask;
}
else if (deselect) {
ebone->flag &= ~selmask;
}
else if (toggle && (ebone->flag & selmask)) {
ebone->flag &= ~selmask;
}
else {
ebone->flag |= selmask;
}
}
ED_armature_edit_sync_selection(arm->edbo);
/* Then now check for active status. */
if (ED_armature_ebone_selectflag_get(ebone)) {
arm->act_edbone = ebone;
}
if (view_layer->basact != basact) {
ED_object_base_activate(C, basact);
}
WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, basact->object);
DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE);
return true;
}
/* context: editmode armature in view3d */
bool ED_armature_edit_select_pick(
bContext *C, const int mval[2], bool extend, bool deselect, bool toggle)
@ -976,116 +1099,7 @@ bool ED_armature_edit_select_pick(
vc.mval[1] = mval[1];
nearBone = get_nearest_editbonepoint(&vc, true, true, &basact, &selmask);
if (nearBone) {
ED_view3d_viewcontext_init_object(&vc, basact->object);
bArmature *arm = vc.obedit->data;
if (!EBONE_SELECTABLE(arm, nearBone)) {
return false;
}
if (!extend && !deselect && !toggle) {
uint bases_len = 0;
Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
vc.view_layer, vc.v3d, &bases_len);
ED_armature_edit_deselect_all_multi_ex(bases, bases_len);
MEM_freeN(bases);
}
/* by definition the non-root connected bones have no root point drawn,
* so a root selection needs to be delivered to the parent tip */
if (selmask & BONE_SELECTED) {
if (nearBone->parent && (nearBone->flag & BONE_CONNECTED)) {
/* click in a chain */
if (extend) {
/* select this bone */
nearBone->flag |= BONE_TIPSEL;
nearBone->parent->flag |= BONE_TIPSEL;
}
else if (deselect) {
/* deselect this bone */
nearBone->flag &= ~(BONE_TIPSEL | BONE_SELECTED);
/* only deselect parent tip if it is not selected */
if (!(nearBone->parent->flag & BONE_SELECTED)) {
nearBone->parent->flag &= ~BONE_TIPSEL;
}
}
else if (toggle) {
/* hold shift inverts this bone's selection */
if (nearBone->flag & BONE_SELECTED) {
/* deselect this bone */
nearBone->flag &= ~(BONE_TIPSEL | BONE_SELECTED);
/* only deselect parent tip if it is not selected */
if (!(nearBone->parent->flag & BONE_SELECTED)) {
nearBone->parent->flag &= ~BONE_TIPSEL;
}
}
else {
/* select this bone */
nearBone->flag |= BONE_TIPSEL;
nearBone->parent->flag |= BONE_TIPSEL;
}
}
else {
/* select this bone */
nearBone->flag |= BONE_TIPSEL;
nearBone->parent->flag |= BONE_TIPSEL;
}
}
else {
if (extend) {
nearBone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
}
else if (deselect) {
nearBone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL);
}
else if (toggle) {
/* hold shift inverts this bone's selection */
if (nearBone->flag & BONE_SELECTED) {
nearBone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL);
}
else {
nearBone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
}
}
else {
nearBone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
}
}
}
else {
if (extend) {
nearBone->flag |= selmask;
}
else if (deselect) {
nearBone->flag &= ~selmask;
}
else if (toggle && (nearBone->flag & selmask)) {
nearBone->flag &= ~selmask;
}
else {
nearBone->flag |= selmask;
}
}
ED_armature_edit_sync_selection(arm->edbo);
/* then now check for active status */
if (ED_armature_ebone_selectflag_get(nearBone)) {
arm->act_edbone = nearBone;
}
if (vc.view_layer->basact != basact) {
ED_object_base_activate(C, basact);
}
WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, basact->object);
DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE);
return true;
}
return false;
return ED_armature_edit_select_pick_bone(C, basact, nearBone, selmask, extend, deselect, toggle);
}
/** \} */

View File

@ -138,6 +138,106 @@ void ED_pose_bone_select(Object *ob, bPoseChannel *pchan, bool select)
}
}
void ED_armature_pose_select_pick_bone(ViewLayer *view_layer,
View3D *v3d,
Object *ob,
Bone *bone,
const bool extend,
const bool deselect,
const bool toggle)
{
if (!ob || !ob->pose) {
return;
}
Object *ob_act = OBACT(view_layer);
BLI_assert(OBEDIT_FROM_VIEW_LAYER(view_layer) == NULL);
/* If the bone cannot be affected, don't do anything. */
if (bone == NULL || (bone->flag & BONE_UNSELECTABLE)) {
return;
}
bArmature *arm = ob->data;
/* Since we do unified select, we don't shift+select a bone if the
* armature object was not active yet.
* Note, special exception for armature mode so we can do multi-select
* we could check for multi-select explicitly but think its fine to
* always give predictable behavior in weight paint mode - campbell */
if ((ob_act == NULL) || ((ob_act != ob) && (ob_act->mode & OB_MODE_ALL_WEIGHT_PAINT) == 0)) {
/* When we are entering into posemode via toggle-select,
* from another active object - always select the bone. */
if (!extend && !deselect && toggle) {
/* Re-select the bone again later in this function. */
bone->flag &= ~BONE_SELECTED;
}
}
if (!extend && !deselect && !toggle) {
{
/* Don't use 'BKE_object_pose_base_array_get_unique'
* because we may be selecting from object mode. */
FOREACH_VISIBLE_BASE_BEGIN (view_layer, v3d, base_iter) {
Object *ob_iter = base_iter->object;
if ((ob_iter->type == OB_ARMATURE) && (ob_iter->mode & OB_MODE_POSE)) {
if (ED_pose_deselect_all(ob_iter, SEL_DESELECT, true)) {
ED_pose_bone_select_tag_update(ob_iter);
}
}
}
FOREACH_VISIBLE_BASE_END;
}
bone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
arm->act_bone = bone;
}
else {
if (extend) {
bone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
arm->act_bone = bone;
}
else if (deselect) {
bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
}
else if (toggle) {
if (bone->flag & BONE_SELECTED) {
/* If not active, we make it active. */
if (bone != arm->act_bone) {
arm->act_bone = bone;
}
else {
bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
}
}
else {
bone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
arm->act_bone = bone;
}
}
}
if (ob_act) {
/* In weightpaint we select the associated vertex group too. */
if (ob_act->mode & OB_MODE_ALL_WEIGHT_PAINT) {
if (bone == arm->act_bone) {
ED_vgroup_select_by_name(ob_act, bone->name);
DEG_id_tag_update(&ob_act->id, ID_RECALC_GEOMETRY);
}
}
/* If there are some dependencies for visualizing armature state
* (e.g. Mask Modifier in 'Armature' mode), force update.
*/
else if (arm->flag & ARM_HAS_VIZ_DEPS) {
/* NOTE: ob not ob_act here is intentional - it's the source of the
* bones being selected [T37247]
*/
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
}
/* Tag armature for copy-on-write update (since act_bone is in armature not object). */
DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE);
}
}
/**
* Called for mode-less pose selection.
* assumes the active object is still on old situation.
@ -159,96 +259,12 @@ bool ED_armature_pose_select_pick_with_buffer(ViewLayer *view_layer,
return 0;
}
Object *ob_act = OBACT(view_layer);
BLI_assert(OBEDIT_FROM_VIEW_LAYER(view_layer) == NULL);
/* Callers happen to already get the active base */
Base *base_dummy = NULL;
nearBone = ED_armature_pick_bone_from_selectbuffer(
&base, 1, buffer, hits, 1, do_nearest, &base_dummy);
/* if the bone cannot be affected, don't do anything */
if ((nearBone) && !(nearBone->flag & BONE_UNSELECTABLE)) {
bArmature *arm = ob->data;
/* since we do unified select, we don't shift+select a bone if the
* armature object was not active yet.
* note, special exception for armature mode so we can do multi-select
* we could check for multi-select explicitly but think its fine to
* always give predictable behavior in weight paint mode - campbell */
if ((ob_act == NULL) || ((ob_act != ob) && (ob_act->mode & OB_MODE_ALL_WEIGHT_PAINT) == 0)) {
/* when we are entering into posemode via toggle-select,
* from another active object - always select the bone. */
if (!extend && !deselect && toggle) {
/* re-select below */
nearBone->flag &= ~BONE_SELECTED;
}
}
if (!extend && !deselect && !toggle) {
{
/* Don't use 'BKE_object_pose_base_array_get_unique'
* because we may be selecting from object mode. */
FOREACH_VISIBLE_BASE_BEGIN (view_layer, v3d, base_iter) {
Object *ob_iter = base_iter->object;
if ((ob_iter->type == OB_ARMATURE) && (ob_iter->mode & OB_MODE_POSE)) {
if (ED_pose_deselect_all(ob_iter, SEL_DESELECT, true)) {
ED_pose_bone_select_tag_update(ob_iter);
}
}
}
FOREACH_VISIBLE_BASE_END;
}
nearBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
arm->act_bone = nearBone;
}
else {
if (extend) {
nearBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
arm->act_bone = nearBone;
}
else if (deselect) {
nearBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
}
else if (toggle) {
if (nearBone->flag & BONE_SELECTED) {
/* if not active, we make it active */
if (nearBone != arm->act_bone) {
arm->act_bone = nearBone;
}
else {
nearBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
}
}
else {
nearBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
arm->act_bone = nearBone;
}
}
}
if (ob_act) {
/* in weightpaint we select the associated vertex group too */
if (ob_act->mode & OB_MODE_ALL_WEIGHT_PAINT) {
if (nearBone == arm->act_bone) {
ED_vgroup_select_by_name(ob_act, nearBone->name);
DEG_id_tag_update(&ob_act->id, ID_RECALC_GEOMETRY);
}
}
/* if there are some dependencies for visualizing armature state
* (e.g. Mask Modifier in 'Armature' mode), force update
*/
else if (arm->flag & ARM_HAS_VIZ_DEPS) {
/* NOTE: ob not ob_act here is intentional - it's the source of the
* bones being selected [T37247]
*/
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
}
/* tag armature for copy-on-write update (since act_bone is in armature not object) */
DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE);
}
}
ED_armature_pose_select_pick_bone(view_layer, v3d, ob, nearBone, extend, deselect, toggle);
return nearBone != NULL;
}

View File

@ -125,6 +125,13 @@ bool ED_armature_edit_deselect_all_visible(struct Object *obedit);
bool ED_armature_edit_deselect_all_multi_ex(struct Base **bases, uint bases_len);
bool ED_armature_edit_deselect_all_visible_multi_ex(struct Base **bases, uint bases_len);
bool ED_armature_edit_deselect_all_visible_multi(struct bContext *C);
bool ED_armature_edit_select_pick_bone(struct bContext *C,
struct Base *basact,
struct EditBone *ebone,
int selmask,
bool extend,
bool deselect,
bool toggle);
bool ED_armature_edit_select_pick(
struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle);
bool ED_armature_edit_select_op_from_tagged(struct bArmature *arm, const int sel_op);
@ -201,6 +208,13 @@ void ED_pose_recalculate_paths(struct bContext *C,
ePosePathCalcRange range);
/* pose_select.c */
void ED_armature_pose_select_pick_bone(struct ViewLayer *view_layer,
struct View3D *v3d,
struct Object *ob,
struct Bone *bone,
bool extend,
bool deselect,
bool toggle);
bool ED_armature_pose_select_pick_with_buffer(struct ViewLayer *view_layer,
struct View3D *v3d,
struct Base *base,

View File

@ -146,6 +146,7 @@ void VIEW3D_OT_select_circle(struct wmOperatorType *ot);
void VIEW3D_OT_select_box(struct wmOperatorType *ot);
void VIEW3D_OT_select_lasso(struct wmOperatorType *ot);
void VIEW3D_OT_select_menu(struct wmOperatorType *ot);
void VIEW3D_OT_bone_select_menu(struct wmOperatorType *ot);
/* view3d_view.c */
void VIEW3D_OT_smoothview(struct wmOperatorType *ot);

View File

@ -187,6 +187,7 @@ void view3d_operatortypes(void)
WM_operatortype_append(VIEW3D_OT_cursor3d);
WM_operatortype_append(VIEW3D_OT_select_lasso);
WM_operatortype_append(VIEW3D_OT_select_menu);
WM_operatortype_append(VIEW3D_OT_bone_select_menu);
WM_operatortype_append(VIEW3D_OT_camera_to_view);
WM_operatortype_append(VIEW3D_OT_camera_to_view_selected);
WM_operatortype_append(VIEW3D_OT_object_as_camera);

View File

@ -96,6 +96,7 @@
#include "ED_select_utils.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "GPU_matrix.h"
@ -1432,6 +1433,8 @@ void VIEW3D_OT_select_lasso(wmOperatorType *ot)
typedef struct SelMenuItemF {
char idname[MAX_ID_NAME - 2];
int icon;
Base *base_ptr;
void *item_ptr;
} SelMenuItemF;
#define SEL_MENU_SIZE 22
@ -1580,7 +1583,7 @@ static Base *object_mouse_select_menu(bContext *C,
{
short baseCount = 0;
bool ok;
LinkNode *linklist = NULL;
LinkNodePair linklist = {NULL, NULL};
/* handle base->object->select_id */
CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
@ -1608,7 +1611,7 @@ static Base *object_mouse_select_menu(bContext *C,
if (ok) {
baseCount++;
BLI_linklist_prepend(&linklist, base);
BLI_linklist_append(&linklist, base);
if (baseCount == SEL_MENU_SIZE) {
break;
@ -1621,8 +1624,8 @@ static Base *object_mouse_select_menu(bContext *C,
return NULL;
}
if (baseCount == 1) {
Base *base = (Base *)linklist->link;
BLI_linklist_free(linklist, NULL);
Base *base = (Base *)linklist.list->link;
BLI_linklist_free(linklist.list, NULL);
return base;
}
@ -1632,7 +1635,7 @@ static Base *object_mouse_select_menu(bContext *C,
memset(object_mouse_select_menu_data, 0, sizeof(object_mouse_select_menu_data));
for (node = linklist, i = 0; node; node = node->next, i++) {
for (node = linklist.list, i = 0; node; node = node->next, i++) {
Base *base = node->link;
Object *ob = base->object;
const char *name = ob->id.name + 2;
@ -1651,10 +1654,231 @@ static Base *object_mouse_select_menu(bContext *C,
WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr);
WM_operator_properties_free(&ptr);
BLI_linklist_free(linklist, NULL);
BLI_linklist_free(linklist.list, NULL);
return NULL;
}
static int bone_select_menu_exec(bContext *C, wmOperator *op)
{
const int name_index = RNA_enum_get(op->ptr, "name");
const bool extend = RNA_boolean_get(op->ptr, "extend");
const bool deselect = RNA_boolean_get(op->ptr, "deselect");
const bool toggle = RNA_boolean_get(op->ptr, "toggle");
View3D *v3d = CTX_wm_view3d(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
const Base *oldbasact = BASACT(view_layer);
Base *basact = object_mouse_select_menu_data[name_index].base_ptr;
if (basact == NULL) {
return OPERATOR_CANCELLED;
}
BLI_assert(BASE_SELECTABLE(v3d, basact));
if (basact->object->mode == OB_MODE_EDIT) {
EditBone *ebone = (EditBone *)object_mouse_select_menu_data[name_index].item_ptr;
ED_armature_edit_select_pick_bone(C, basact, ebone, BONE_SELECTED, extend, deselect, toggle);
}
else {
bPoseChannel *pchan = (bPoseChannel *)object_mouse_select_menu_data[name_index].item_ptr;
ED_armature_pose_select_pick_bone(
view_layer, v3d, basact->object, pchan->bone, extend, deselect, toggle);
}
/* Weak but ensures we activate the menu again before using the enum. */
memset(object_mouse_select_menu_data, 0, sizeof(object_mouse_select_menu_data));
/* We make the armature selected:
* Not-selected active object in posemode won't work well for tools. */
ED_object_base_select(basact, BA_SELECT);
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 weight-paint, we use selected bone to select vertex-group,
* so don't switch to new active object. */
if (oldbasact && (oldbasact->object->mode & OB_MODE_ALL_WEIGHT_PAINT)) {
/* Prevent activating.
* Selection causes this to be considered the 'active' pose in weight-paint mode.
* Eventually this limitation may be removed.
* For now, de-select all other pose objects deforming this mesh. */
ED_armature_pose_select_in_wpaint_mode(view_layer, basact);
basact = NULL;
}
/* Undo? */
Scene *scene = CTX_data_scene(C);
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
ED_outliner_select_sync_from_object_tag(C);
return OPERATOR_FINISHED;
}
void VIEW3D_OT_bone_select_menu(wmOperatorType *ot)
{
PropertyRNA *prop;
/* identifiers */
ot->name = "Select Menu";
ot->description = "Menu bone selection";
ot->idname = "VIEW3D_OT_bone_select_menu";
/* api callbacks */
ot->invoke = WM_menu_invoke;
ot->exec = bone_select_menu_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* keyingset to use (dynamic enum) */
prop = RNA_def_enum(ot->srna, "name", DummyRNA_NULL_items, 0, "Bone Name", "");
RNA_def_enum_funcs(prop, object_select_menu_enum_itemf);
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_ENUM_NO_TRANSLATE);
ot->prop = prop;
RNA_def_boolean(ot->srna, "extend", 0, "Extend", "");
RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "");
RNA_def_boolean(ot->srna, "toggle", 0, "Toggle", "");
}
static bool bone_mouse_select_menu(bContext *C,
const uint *buffer,
const int hits,
const bool is_editmode,
const bool extend,
const bool deselect,
const bool toggle)
{
BLI_assert(buffer);
short baseCount = 0;
LinkNodePair base_list = {NULL, NULL};
LinkNodePair bone_list = {NULL, NULL};
GSet *added_bones = BLI_gset_ptr_new("Bone mouse select menu");
/* Select logic taken from ed_armature_pick_bone_from_selectbuffer_impl in armature_select.c */
for (int a = 0; a < hits; a++) {
void *bone_ptr = NULL;
Base *bone_base = NULL;
uint hitresult = buffer[3 + (a * 4)];
if (!(hitresult & BONESEL_ANY)) {
/* To avoid including objects in selection. */
continue;
}
hitresult &= ~BONESEL_ANY;
const uint hit_object = hitresult & 0xFFFF;
/* Find the hit bone base (armature object). */
CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
if (base->object->runtime.select_id == hit_object) {
bone_base = base;
break;
}
}
CTX_DATA_END;
if (!bone_base) {
continue;
}
/* Determine what the current bone is */
if (is_editmode) {
EditBone *ebone;
const uint hit_bone = (hitresult & ~BONESEL_ANY) >> 16;
bArmature *arm = bone_base->object->data;
ebone = BLI_findlink(arm->edbo, hit_bone);
if (ebone && !(ebone->flag & BONE_UNSELECTABLE)) {
bone_ptr = ebone;
}
}
else {
bPoseChannel *pchan;
const uint hit_bone = (hitresult & ~BONESEL_ANY) >> 16;
pchan = BLI_findlink(&bone_base->object->pose->chanbase, hit_bone);
if (pchan && !(pchan->bone->flag & BONE_UNSELECTABLE)) {
bone_ptr = pchan;
}
}
if (!bone_ptr) {
continue;
}
/* We can hit a bone multiple times, so make sure we are not adding an already included bone
* to the list.*/
const bool is_duplicate_bone = BLI_gset_haskey(added_bones, bone_ptr);
if (!is_duplicate_bone) {
baseCount++;
BLI_linklist_append(&base_list, bone_base);
BLI_linklist_append(&bone_list, bone_ptr);
BLI_gset_insert(added_bones, bone_ptr);
if (baseCount == SEL_MENU_SIZE) {
break;
}
}
}
BLI_gset_free(added_bones, NULL);
if (baseCount == 0) {
return false;
}
if (baseCount == 1) {
BLI_linklist_free(base_list.list, NULL);
BLI_linklist_free(bone_list.list, NULL);
return false;
}
/* UI, full in static array values that we later use in an enum function */
LinkNode *bone_node, *base_node;
int i;
memset(object_mouse_select_menu_data, 0, sizeof(object_mouse_select_menu_data));
for (base_node = base_list.list, bone_node = bone_list.list, i = 0; bone_node;
base_node = base_node->next, bone_node = bone_node->next, i++) {
char *name;
object_mouse_select_menu_data[i].base_ptr = base_node->link;
if (is_editmode) {
EditBone *ebone = bone_node->link;
object_mouse_select_menu_data[i].item_ptr = ebone;
name = ebone->name;
}
else {
bPoseChannel *pchan = bone_node->link;
object_mouse_select_menu_data[i].item_ptr = pchan;
name = pchan->name;
}
BLI_strncpy(object_mouse_select_menu_data[i].idname, name, MAX_ID_NAME - 2);
object_mouse_select_menu_data[i].icon = ICON_BONE_DATA;
}
wmOperatorType *ot = WM_operatortype_find("VIEW3D_OT_bone_select_menu", false);
PointerRNA ptr;
WM_operator_properties_create_ptr(&ptr, ot);
RNA_boolean_set(&ptr, "extend", extend);
RNA_boolean_set(&ptr, "deselect", deselect);
RNA_boolean_set(&ptr, "toggle", toggle);
WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr);
WM_operator_properties_free(&ptr);
BLI_linklist_free(base_list.list, NULL);
BLI_linklist_free(bone_list.list, NULL);
return true;
}
static bool selectbuffer_has_bones(const uint *buffer, const uint hits)
{
for (uint i = 0; i < hits; i++) {
@ -2113,7 +2337,13 @@ static bool ed_object_select_pick(bContext *C,
/* note; shift+alt goes to group-flush-selecting */
if (enumerate) {
basact = object_mouse_select_menu(C, &vc, buffer, hits, mval, extend, deselect, toggle);
if (has_bones &&
bone_mouse_select_menu(C, buffer, hits, false, extend, deselect, toggle)) {
basact = NULL;
}
else {
basact = object_mouse_select_menu(C, &vc, buffer, hits, mval, extend, deselect, toggle);
}
}
else {
basact = mouse_select_eval_buffer(&vc, buffer, hits, startbase, has_bones, do_nearest);
@ -2410,7 +2640,20 @@ static int view3d_select_exec(bContext *C, wmOperator *op)
}
}
else if (obedit->type == OB_ARMATURE) {
retval = ED_armature_edit_select_pick(C, location, extend, deselect, toggle);
if (enumerate) {
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
ViewContext vc;
ED_view3d_viewcontext_init(C, &vc, depsgraph);
uint buffer[MAXPICKBUF];
const int hits = mixed_bones_object_selectbuffer(
&vc, buffer, location, VIEW3D_SELECT_FILTER_NOP, false, true);
retval = bone_mouse_select_menu(C, buffer, hits, true, extend, deselect, toggle);
}
if (!retval) {
retval = ED_armature_edit_select_pick(C, location, extend, deselect, toggle);
}
if (!retval && deselect_all) {
retval = ED_armature_edit_deselect_all_visible_multi(C);
}