diff options
author | Sebastian Parborg <darkdefende@gmail.com> | 2021-04-08 18:21:01 +0300 |
---|---|---|
committer | Sebastian Parborg <darkdefende@gmail.com> | 2021-04-08 18:26:33 +0300 |
commit | 5e77ff79ccdaffb2df4d54c97f17359c017c9b85 (patch) | |
tree | 187207537fe318dd45cfbf7a42ab7be513536dbf /source/blender/editors/space_view3d/view3d_select.c | |
parent | 5c4d24e1fd752a8a89d44d05e8e3f9b31f2d7db0 (diff) |
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
Diffstat (limited to 'source/blender/editors/space_view3d/view3d_select.c')
-rw-r--r-- | source/blender/editors/space_view3d/view3d_select.c | 259 |
1 files changed, 251 insertions, 8 deletions
diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 3166b818d3c..757ed13ac28 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -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); } |