diff options
Diffstat (limited to 'source/blender/editors/armature/armature_select.c')
-rw-r--r-- | source/blender/editors/armature/armature_select.c | 221 |
1 files changed, 174 insertions, 47 deletions
diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c index 587cafa6d48..63864e75edc 100644 --- a/source/blender/editors/armature/armature_select.c +++ b/source/blender/editors/armature/armature_select.c @@ -29,6 +29,8 @@ * \ingroup edarmature */ +#include "MEM_guardedalloc.h" + #include "DNA_armature_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" @@ -40,6 +42,7 @@ #include "BKE_context.h" #include "BKE_action.h" #include "BKE_report.h" +#include "BKE_layer.h" #include "BIF_gl.h" @@ -63,26 +66,84 @@ /* **************** PoseMode & EditMode Selection Buffer Queries *************************** */ -/* only for opengl selection indices */ -Bone *ED_armature_bone_find_index(Object *ob, int index) +Base *ED_armature_base_and_ebone_from_select_buffer( + Base **bases, uint bases_len, int hit, EditBone **r_ebone) { - bPoseChannel *pchan; - if (ob->pose == NULL) return NULL; - index >>= 16; // bone selection codes use left 2 bytes - - pchan = BLI_findlink(&ob->pose->chanbase, index); - return pchan ? pchan->bone : NULL; + const uint hit_object = hit & 0xFFFF; + Base *base = NULL; + EditBone *ebone = NULL; + /* TODO(campbell): optimize, eg: sort & binary search. */ + for (uint base_index = 0; base_index < bases_len; base_index++) { + if (bases[base_index]->object->select_color == hit_object) { + base = bases[base_index]; + break; + } + } + if (base != NULL) { + const uint hit_bone = (hit & ~BONESEL_ANY) >> 16; + bArmature *arm = base->object->data; + ebone = BLI_findlink(arm->edbo, hit_bone); + } + *r_ebone = ebone; + return base; +} + +Object *ED_armature_object_and_ebone_from_select_buffer( + Object **objects, uint objects_len, int hit, EditBone **r_ebone) +{ + const uint hit_object = hit & 0xFFFF; + Object *ob = NULL; + EditBone *ebone = NULL; + /* TODO(campbell): optimize, eg: sort & binary search. */ + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + if (objects[ob_index]->select_color == hit_object) { + ob = objects[ob_index]; + break; + } + } + if (ob != NULL) { + const uint hit_bone = (hit & ~BONESEL_ANY) >> 16; + bArmature *arm = ob->data; + ebone = BLI_findlink(arm->edbo, hit_bone); + } + *r_ebone = ebone; + return ob; +} + +Base *ED_armature_base_and_bone_from_select_buffer( + Base **bases, uint bases_len, int hit, Bone **r_bone) +{ + const uint hit_object = hit & 0xFFFF; + Base *base = NULL; + Bone *bone = NULL; + /* TODO(campbell): optimize, eg: sort & binary search. */ + for (uint base_index = 0; base_index < bases_len; base_index++) { + if (bases[base_index]->object->select_color == hit_object) { + base = bases[base_index]; + break; + } + } + if (base != NULL) { + if (base->object->pose != NULL) { + const uint hit_bone = (hit & ~BONESEL_ANY) >> 16; + bPoseChannel *pchan = BLI_findlink(&base->object->pose->chanbase, hit_bone);; + bone = pchan ? pchan->bone : NULL; + } + } + *r_bone = bone; + return base; } /* See if there are any selected bones in this buffer */ /* only bones from base are checked on */ void *get_bone_from_selectbuffer( - Base *base, Object *obedit, const unsigned int *buffer, short hits, - bool findunsel, bool do_nearest) + Base **bases, uint bases_len, bool is_editmode, const unsigned int *buffer, short hits, + bool findunsel, bool do_nearest, Base **r_base) { Bone *bone; EditBone *ebone; void *firstunSel = NULL, *firstSel = NULL, *data; + Base *firstunSel_base = NULL, *firstSel_base = NULL; unsigned int hitresult; short i; bool takeNext = false; @@ -93,15 +154,14 @@ void *get_bone_from_selectbuffer( if (!(hitresult & BONESEL_NOSEL)) { if (hitresult & BONESEL_ANY) { /* to avoid including objects in selection */ + Base *base = NULL; bool sel; - + hitresult &= ~(BONESEL_ANY); /* Determine what the current bone is */ - if (obedit == NULL || base->object != obedit) { - /* no singular posemode, so check for correct object */ - if (base->object->select_color == (hitresult & 0xFFFF)) { - bone = ED_armature_bone_find_index(base->object, hitresult); - + if (is_editmode == false) { + base = ED_armature_base_and_bone_from_select_buffer(bases, bases_len, hitresult, &bone); + if (bone != NULL) { if (findunsel) sel = (bone->flag & BONE_SELECTED); else @@ -115,14 +175,12 @@ void *get_bone_from_selectbuffer( } } else { - bArmature *arm = obedit->data; - - ebone = BLI_findlink(arm->edbo, hitresult); + base = ED_armature_base_and_ebone_from_select_buffer(bases, bases_len, hitresult, &ebone); if (findunsel) sel = (ebone->flag & BONE_SELECTED); else sel = !(ebone->flag & BONE_SELECTED); - + data = ebone; } @@ -131,11 +189,15 @@ void *get_bone_from_selectbuffer( if (do_nearest) { if (minsel > buffer[4 * i + 1]) { firstSel = data; + firstSel_base = base; minsel = buffer[4 * i + 1]; } } else { - if (!firstSel) firstSel = data; + if (!firstSel) { + firstSel = data; + firstSel_base = base; + } takeNext = 1; } } @@ -143,12 +205,19 @@ void *get_bone_from_selectbuffer( if (do_nearest) { if (minunsel > buffer[4 * i + 1]) { firstunSel = data; + firstunSel_base = base; minunsel = buffer[4 * i + 1]; } } else { - if (!firstunSel) firstunSel = data; - if (takeNext) return data; + if (!firstunSel) { + firstunSel = data; + firstunSel_base = base; + } + if (takeNext) { + *r_base = base; + return data; + } } } } @@ -156,16 +225,22 @@ void *get_bone_from_selectbuffer( } } - if (firstunSel) + if (firstunSel) { + *r_base = firstunSel_base; return firstunSel; - else + } + else { + *r_base = firstSel_base; return firstSel; + } } /* used by posemode as well editmode */ /* only checks scene->basact! */ /* x and y are mouse coords (area space) */ -void *get_nearest_bone(bContext *C, const int xy[2], bool findunsel) +void *get_nearest_bone( + bContext *C, const int xy[2], bool findunsel, + Base **r_base) { EvaluationContext eval_ctx; ViewContext vc; @@ -182,8 +257,20 @@ void *get_nearest_bone(bContext *C, const int xy[2], bool findunsel) hits = view3d_opengl_select(&eval_ctx, &vc, buffer, MAXPICKBUF, &rect, VIEW3D_SELECT_PICK_NEAREST); + *r_base = NULL; + if (hits > 0) { - return get_bone_from_selectbuffer(vc.view_layer->basact, vc.obedit, buffer, hits, findunsel, true); + uint bases_len = 0; + Base **bases = BKE_view_layer_array_from_bases_in_mode( + eval_ctx.view_layer, &bases_len, { + .object_mode = vc.obedit ? OB_MODE_EDIT : OB_MODE_POSE, + .no_dup_data = true}); + + void *bone = get_bone_from_selectbuffer( + bases, bases_len, vc.obedit != NULL, buffer, hits, findunsel, true, r_base); + + MEM_freeN(bases); + return bone; } return NULL; } @@ -197,16 +284,17 @@ static int armature_select_linked_invoke(bContext *C, wmOperator *op, const wmEv bArmature *arm; EditBone *bone, *curBone, *next; const bool extend = RNA_boolean_get(op->ptr, "extend"); - Object *obedit = CTX_data_edit_object(C); - arm = obedit->data; view3d_operator_needs_opengl(C); - bone = get_nearest_bone(C, event->mval, !extend); + Base *base = NULL; + bone = get_nearest_bone(C, event->mval, !extend, &base); if (!bone) return OPERATOR_CANCELLED; + arm = base->object->data; + /* Select parents */ for (curBone = bone; curBone; curBone = next) { if ((curBone->flag & BONE_UNSELECTABLE) == 0) { @@ -249,7 +337,7 @@ static int armature_select_linked_invoke(bContext *C, wmOperator *op, const wmEv ED_armature_edit_sync_selection(arm->edbo); - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, base->object); return OPERATOR_FINISHED; } @@ -295,7 +383,8 @@ static int selectbuffer_ret_hits_5(unsigned int *buffer, const int hits12, const /* note that BONE ROOT only gets drawn for root bones (or without IK) */ static EditBone *get_nearest_editbonepoint( const EvaluationContext *eval_ctx, ViewContext *vc, - bool findunsel, bool use_cycle, int *r_selmask) + bool findunsel, bool use_cycle, + Base **r_base, int *r_selmask) { bArmature *arm = (bArmature *)vc->obedit->data; EditBone *ebone_next_act = arm->act_edbone; @@ -371,9 +460,11 @@ static EditBone *get_nearest_editbonepoint( cache_end: view3d_opengl_select_cache_end(); + uint bases_len; + Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(eval_ctx->view_layer, &bases_len); + /* See if there are any selected bones in this group */ if (hits > 0) { - if (hits == 1) { if (!(buffer[3] & BONESEL_NOSEL)) besthitresult = buffer[3]; @@ -382,10 +473,11 @@ cache_end: for (i = 0; i < hits; i++) { hitresult = buffer[3 + (i * 4)]; if (!(hitresult & BONESEL_NOSEL)) { + Base *base = NULL; + base = ED_armature_base_and_ebone_from_select_buffer(bases, bases_len, hitresult, &ebone); + arm = base->object->data; + int dep; - - ebone = BLI_findlink(arm->edbo, hitresult & ~BONESEL_ANY); - /* clicks on bone points get advantage */ if (hitresult & (BONESEL_ROOT | BONESEL_TIP)) { /* but also the unselected one */ @@ -425,11 +517,13 @@ cache_end: } } } - + if (!(besthitresult & BONESEL_NOSEL)) { - - ebone = BLI_findlink(arm->edbo, besthitresult & ~BONESEL_ANY); - + Base *base = NULL; + base = ED_armature_base_and_ebone_from_select_buffer(bases, bases_len, hitresult, &ebone); + arm = base->object->data; + *r_base = base; + *r_selmask = 0; if (besthitresult & BONESEL_ROOT) *r_selmask |= BONE_ROOTSEL; @@ -441,6 +535,7 @@ cache_end: } } *r_selmask = 0; + *r_base = NULL; return NULL; } @@ -469,6 +564,23 @@ void ED_armature_edit_deselect_all_visible(Object *obedit) ED_armature_edit_sync_selection(arm->edbo); } + +void ED_armature_edit_deselect_all_multi(struct Object **objects, uint objects_len) +{ + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + ED_armature_edit_deselect_all(obedit); + } +} + +void ED_armature_edit_deselect_all_visible_multi(struct Object **objects, uint objects_len) +{ + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + ED_armature_edit_deselect_all_visible(obedit); + } +} + /* accounts for connected parents */ static int ebone_select_flag(EditBone *ebone) { @@ -483,11 +595,11 @@ static int ebone_select_flag(EditBone *ebone) /* context: editmode armature in view3d */ bool ED_armature_edit_select_pick(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle) { - Object *obedit = CTX_data_edit_object(C); EvaluationContext eval_ctx; ViewContext vc; EditBone *nearBone = NULL; int selmask; + Base *basact = NULL; CTX_data_eval_ctx(C, &eval_ctx); ED_view3d_viewcontext_init(C, &vc); @@ -498,12 +610,16 @@ bool ED_armature_edit_select_pick(bContext *C, const int mval[2], bool extend, b return true; } - nearBone = get_nearest_editbonepoint(&eval_ctx, &vc, true, true, &selmask); + nearBone = get_nearest_editbonepoint(&eval_ctx, &vc, true, true, &basact, &selmask); if (nearBone) { - bArmature *arm = obedit->data; + ED_view3d_viewcontext_init_object(&vc, basact->object); + bArmature *arm = vc.obedit->data; if (!extend && !deselect && !toggle) { - ED_armature_edit_deselect_all(obedit); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(eval_ctx.view_layer, &objects_len); + ED_armature_edit_deselect_all_multi(objects, objects_len); + MEM_freeN(objects); } /* by definition the non-root connected bones have no root point drawn, @@ -581,9 +697,14 @@ bool ED_armature_edit_select_pick(bContext *C, const int mval[2], bool extend, b if (ebone_select_flag(nearBone)) { arm->act_edbone = nearBone; } + + if (eval_ctx.view_layer->basact != basact) { + eval_ctx.view_layer->basact = basact; + WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, vc.scene); + } } - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, vc.obedit); + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, basact->object); return true; } @@ -1296,17 +1417,23 @@ static int armature_shortest_path_pick_invoke(bContext *C, wmOperator *op, const EditBone *ebone_isect_parent = NULL; EditBone *ebone_isect_child[2]; bool changed; + Base *base_dst = NULL; view3d_operator_needs_opengl(C); ebone_src = arm->act_edbone; - ebone_dst = get_nearest_bone(C, event->mval, false); + ebone_dst = get_nearest_bone(C, event->mval, false, &base_dst); /* fallback to object selection */ if (ELEM(NULL, ebone_src, ebone_dst) || (ebone_src == ebone_dst)) { return OPERATOR_PASS_THROUGH; } + if (base_dst && base_dst->object != obedit) { + /* Disconnected, ignore. */ + return OPERATOR_CANCELLED; + } + ebone_isect_child[0] = ebone_src; ebone_isect_child[1] = ebone_dst; |