* Armature hierarchy selection tools

These are for use in pose mode or armature edit mode, to let you quickly traverse up and down a chain of bones. 
It's quite useful for bones that are in hard-to-click places.

The tools are:
*Select parent/child ( [ and ] )
selects the parent or child of the active bone, deselecting the original active bone

* Extend select parent/child (shift [ and shift ] )
selects the parent or child of the active bone, adding to the selection

Thanks to Joshua for reviewing this so promptly!

PS. I'd like to use these [ and ] keys more widely in blender as consistent 'select next / previous' tools. I can imagine it being very useful for a lot of things like keyframes, nodes, mesh edges, etc.
This commit is contained in:
Matt Ebb
2008-08-04 11:47:17 +00:00
parent 3598dcd279
commit 2e6a3710bb
6 changed files with 213 additions and 96 deletions

View File

@@ -108,7 +108,8 @@ void mouse_armature(void);
void remake_editArmature(void);
void selectconnected_armature(void);
void selectconnected_posearmature(void);
void select_bone_parent(void);
void armature_select_hierarchy(short direction, short add_to_sel);
void setflag_armature(short mode);
void unique_editbone_name (struct ListBase *ebones, char *name);
@@ -143,6 +144,10 @@ void set_locks_armature_bones(short lock);
#define BONESEL_NOSEL 0x80000000 /* Indicates a negative number */
/* used in bone_select_hierachy() */
#define BONE_SELECT_PARENT 0
#define BONE_SELECT_CHILD 1
#endif

View File

@@ -65,6 +65,8 @@ void pose_assign_to_posegroup(short active);
void pose_remove_from_posegroups(void);
void pgroup_operation_with_menu(void);
void pose_select_hierarchy(short direction, short add_to_sel);
void pose_select_grouped(short nr);
void pose_select_grouped_menu(void);

View File

@@ -1033,87 +1033,6 @@ static void *get_nearest_bone (short findunsel)
return NULL;
}
/* used by posemode and editmode */
void select_bone_parent (void)
{
Object *ob;
bArmature *arm;
/* get data */
if (G.obedit)
ob= G.obedit;
else if (OBACT)
ob= OBACT;
else
return;
arm= (bArmature *)ob->data;
/* determine which mode armature is in */
if ((!G.obedit) && (ob->flag & OB_POSEMODE)) {
/* deal with pose channels */
/* channels are sorted on dependency, so the loop below won't result in a flood-select */
bPoseChannel *pchan=NULL;
for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
/* check if bone in original selection */
if (pchan->bone->flag & BONE_SELECTED) {
bPoseChannel *chanpar= pchan->parent;
/* check if any parent */
if ((chanpar) && ((chanpar->bone->flag & BONE_SELECTED)==0)) {
chanpar->bone->flag |= BONE_SELECTED;
select_actionchannel_by_name (ob->action, pchan->name, 1);
}
}
}
}
else if (G.obedit) {
/* deal with editbones */
EditBone *curbone, *parbone, *parpar;
/* prevent floods */
for (curbone= G.edbo.first; curbone; curbone= curbone->next)
curbone->temp= NULL;
for (curbone= G.edbo.first; curbone; curbone= curbone->next) {
/* check if bone selected */
if ((curbone->flag & BONE_SELECTED) && curbone->temp==NULL) {
parbone= curbone->parent;
/* check if any parent */
if ((parbone) && ((parbone->flag & BONE_SELECTED)==0)) {
/* select the parent bone */
parbone->flag |= (BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL);
/* check if parent has parent */
parpar= parbone->parent;
if ((parpar) && (parbone->flag & BONE_CONNECTED)) {
parpar->flag |= BONE_TIPSEL;
}
/* tag this bone to not flood selection */
parbone->temp= parbone;
}
}
}
/* to be sure... */
for (curbone= G.edbo.first; curbone; curbone= curbone->next)
curbone->temp= NULL;
}
/* undo + redraw pushes */
countall(); // flushes selection!
allqueue (REDRAWVIEW3D, 0);
allqueue (REDRAWBUTSEDIT, 0);
allqueue(REDRAWBUTSOBJECT, 0);
allqueue(REDRAWOOPS, 0);
BIF_undo_push("Select Parent");
}
/* helper for setflag_sel_bone() */
static void bone_setflag (int *bone, int flag, short mode)
{
@@ -1139,6 +1058,89 @@ static void bone_setflag (int *bone, int flag, short mode)
}
}
/* Get the first available child of an editbone */
static EditBone *editbone_get_child(EditBone *pabone, short use_visibility)
{
Object *ob;
bArmature *arm;
EditBone *curbone, *chbone=NULL;
if (!G.obedit) return NULL;
else ob= G.obedit;
arm= (bArmature *)ob->data;
for (curbone= G.edbo.first; curbone; curbone= curbone->next) {
if (curbone->parent == pabone) {
if (use_visibility) {
if ((arm->layer & curbone->layer) && !(pabone->flag & BONE_HIDDEN_A))
chbone = curbone;
}
else
chbone = curbone;
}
}
return chbone;
}
void armature_select_hierarchy(short direction, short add_to_sel)
{
Object *ob;
bArmature *arm;
EditBone *curbone, *pabone, *chbone;
if (!G.obedit) return;
else ob= G.obedit;
arm= (bArmature *)ob->data;
for (curbone= G.edbo.first; curbone; curbone= curbone->next) {
if (arm->layer & curbone->layer) {
if (curbone->flag & (BONE_ACTIVE)) {
if (direction == BONE_SELECT_PARENT) {
if (curbone->parent == NULL) continue;
else pabone = curbone->parent;
if ((arm->layer & pabone->layer) && !(pabone->flag & BONE_HIDDEN_A)) {
pabone->flag |= (BONE_ACTIVE|BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL);
if (pabone->parent) pabone->parent->flag |= BONE_TIPSEL;
if (!add_to_sel) curbone->flag &= ~(BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL);
curbone->flag &= ~BONE_ACTIVE;
break;
}
} else { // BONE_SELECT_CHILD
chbone = editbone_get_child(curbone, 1);
if (chbone == NULL) continue;
if ((arm->layer & chbone->layer) && !(chbone->flag & BONE_HIDDEN_A)) {
chbone->flag |= (BONE_ACTIVE|BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL);
if (!add_to_sel) {
curbone->flag &= ~(BONE_SELECTED|BONE_ROOTSEL);
if (curbone->parent) curbone->parent->flag &= ~BONE_TIPSEL;
}
curbone->flag &= ~BONE_ACTIVE;
break;
}
}
}
}
}
countall(); // flushes selection!
allqueue (REDRAWVIEW3D, 0);
allqueue (REDRAWBUTSEDIT, 0);
allqueue (REDRAWBUTSOBJECT, 0);
allqueue (REDRAWOOPS, 0);
if (direction==BONE_SELECT_PARENT)
BIF_undo_push("Select edit bone parent");
if (direction==BONE_SELECT_CHILD)
BIF_undo_push("Select edit bone child");
}
/* used by posemode and editmode */
void setflag_armature (short mode)
{

View File

@@ -1325,12 +1325,21 @@ static void do_view3d_select_armaturemenu(void *arg, int event)
case 2: /* Select/Deselect all */
deselectall_armature(1, 1);
break;
case 3: /* Select Parent(s) */
select_bone_parent();
break;
case 4: /* Swap Select All */
case 3: /* Swap Select All */
deselectall_armature(3, 1);
break;
case 4: /* Select parent */
armature_select_hierarchy(BONE_SELECT_PARENT, 0);
break;
case 5: /* Select child */
armature_select_hierarchy(BONE_SELECT_CHILD, 0);
break;
case 6: /* Extend Select parent */
armature_select_hierarchy(BONE_SELECT_PARENT, 1);
break;
case 7: /* Extend Select child */
armature_select_hierarchy(BONE_SELECT_CHILD, 1);
break;
}
allqueue(REDRAWVIEW3D, 0);
}
@@ -1348,11 +1357,18 @@ static uiBlock *view3d_select_armaturemenu(void *arg_unused)
uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Select/Deselect All|A", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 2, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Inverse|Ctrl I", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 3, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Swap Select All|Ctrl I", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 4, "");
uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Select Parent|[", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 4, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Select Child|]", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 5, "");
uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Extend Select Parent|Shift [", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 6, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Extend Select Child|Shift ]", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 7, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Select Parent(s)|P", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 3, "");
if(curarea->headertype==HEADERTOP) {
uiBlockSetDirection(block, UI_DOWN);
}
@@ -1379,12 +1395,21 @@ static void do_view3d_select_pose_armaturemenu(void *arg, int event)
case 3: /* Select Target(s) of Constraint(s) */
pose_select_constraint_target();
break;
case 4: /* Select Bone's Parent */
select_bone_parent();
break;
case 5: /* Swap Select All */
deselectall_posearmature(OBACT, 3, 1);
break;
case 6: /* Select parent */
pose_select_hierarchy(BONE_SELECT_PARENT, 0);
break;
case 7: /* Select child */
pose_select_hierarchy(BONE_SELECT_CHILD, 0);
break;
case 8: /* Extend Select parent */
pose_select_hierarchy(BONE_SELECT_PARENT, 1);
break;
case 9: /* Extend Select child */
pose_select_hierarchy(BONE_SELECT_CHILD, 1);
break;
}
allqueue(REDRAWVIEW3D, 0);
}
@@ -1404,8 +1429,17 @@ static uiBlock *view3d_select_pose_armaturemenu(void *arg_unused)
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Select/Deselect All|A", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 2, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Swap Select All|Ctrl I", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 5, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Select Constraint Target|W", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 3, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Select Parent(s)|P", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 4, "");
uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Select Parent|[", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 6, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Select Child|]", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 7, "");
uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Extend Select Parent|Shift [", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 8, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Extend Select Child|Shift ]", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 9, "");
if(curarea->headertype==HEADERTOP) {
uiBlockSetDirection(block, UI_DOWN);
}

View File

@@ -479,6 +479,67 @@ void pose_select_constraint_target(void)
}
void pose_select_hierarchy(short direction, short add_to_sel)
{
Object *ob= OBACT;
bArmature *arm= ob->data;
bPoseChannel *pchan;
Bone *curbone, *pabone, *chbone;
/* paranoia checks */
if (!ob && !ob->pose) return;
if (ob==G.obedit || (ob->flag & OB_POSEMODE)==0) return;
for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
curbone= pchan->bone;
if (arm->layer & curbone->layer) {
if (curbone->flag & (BONE_ACTIVE)) {
if (direction == BONE_SELECT_PARENT) {
if (pchan->parent == NULL) continue;
else pabone= pchan->parent->bone;
if ((arm->layer & pabone->layer) && !(pabone->flag & BONE_HIDDEN_P)) {
if (!add_to_sel) curbone->flag &= ~BONE_SELECTED;
curbone->flag &= ~BONE_ACTIVE;
pabone->flag |= (BONE_ACTIVE|BONE_SELECTED);
select_actionchannel_by_name (ob->action, pchan->name, 0);
select_actionchannel_by_name (ob->action, pchan->parent->name, 1);
break;
}
} else { // BONE_SELECT_CHILD
if (pchan->child == NULL) continue;
else chbone = pchan->child->bone;
if ((arm->layer & chbone->layer) && !(chbone->flag & BONE_HIDDEN_P)) {
if (!add_to_sel) curbone->flag &= ~BONE_SELECTED;
curbone->flag &= ~BONE_ACTIVE;
chbone->flag |= (BONE_ACTIVE|BONE_SELECTED);
select_actionchannel_by_name (ob->action, pchan->name, 0);
select_actionchannel_by_name (ob->action, pchan->child->name, 1);
break;
}
}
}
}
}
allqueue (REDRAWVIEW3D, 0);
allqueue (REDRAWBUTSOBJECT, 0);
allqueue (REDRAWOOPS, 0);
if (direction==BONE_SELECT_PARENT)
BIF_undo_push("Select pose bone parent");
if (direction==BONE_SELECT_CHILD)
BIF_undo_push("Select pose bone child");
}
/* context: active channel */
void pose_special_editmenu(void)
{

View File

@@ -2428,7 +2428,7 @@ static void winqreadview3dspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
else if(G.qual==LR_ALTKEY && G.obedit->type==OB_ARMATURE)
clear_bone_parent();
else if((G.qual==0) && (G.obedit->type==OB_ARMATURE))
select_bone_parent();
armature_select_hierarchy(BONE_SELECT_PARENT, 1); // 1 = add to selection
else if((G.qual==(LR_CTRLKEY|LR_ALTKEY)) && (G.obedit->type==OB_ARMATURE))
separate_armature();
else if((G.qual==0) && G.obedit->type==OB_MESH)
@@ -2458,7 +2458,7 @@ static void winqreadview3dspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
start_RBSimulation();
}
else if((G.qual==0) && (OBACT) && (OBACT->type==OB_ARMATURE) && (OBACT->flag & OB_POSEMODE))
select_bone_parent();
pose_select_hierarchy(BONE_SELECT_PARENT, 1); // 1 = add to selection
else if((G.qual==0)) {
start_game();
}
@@ -2761,6 +2761,19 @@ static void winqreadview3dspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
scrarea_queue_winredraw(curarea);
break;
case LEFTBRACKETKEY:
if ((G.obedit) && (G.obedit->type == OB_ARMATURE))
armature_select_hierarchy(BONE_SELECT_PARENT, (G.qual == LR_SHIFTKEY));
else if ((ob) && (ob->flag & OB_POSEMODE))
pose_select_hierarchy(BONE_SELECT_PARENT, (G.qual == LR_SHIFTKEY));
break;
case RIGHTBRACKETKEY:
if ((G.obedit) && (G.obedit->type == OB_ARMATURE))
armature_select_hierarchy(BONE_SELECT_CHILD, (G.qual == LR_SHIFTKEY));
if ((ob) && (ob->flag & OB_POSEMODE))
pose_select_hierarchy(BONE_SELECT_CHILD, (G.qual == LR_SHIFTKEY));
break;
case PADSLASHKEY:
if(G.qual==0) {
if(G.vd->localview) {