* 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:
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user