Armature: add armature dissolve
authorCampbell Barton <ideasman42@gmail.com>
Fri, 12 Jun 2015 17:20:07 +0000 (03:20 +1000)
committerCampbell Barton <ideasman42@gmail.com>
Fri, 12 Jun 2015 17:24:07 +0000 (03:24 +1000)
Works like mesh dissolve (access from delete or Ctrl+X)

release/scripts/startup/bl_ui/space_view3d.py
source/blender/blenkernel/BKE_action.h
source/blender/blenkernel/intern/action.c
source/blender/editors/armature/armature_edit.c
source/blender/editors/armature/armature_intern.h
source/blender/editors/armature/armature_ops.c
source/blender/editors/armature/armature_utils.c

index 43859a3a6a85c3bab6ba4739fb53de9f3b56f220..830c70ea20fb41a5c3dbd24904c843101847232a 100644 (file)
@@ -2789,6 +2789,20 @@ class VIEW3D_MT_edit_armature_roll(Menu):
 
         layout.operator("transform.transform", text="Set Roll").mode = 'BONE_ROLL'
 
+
+class VIEW3D_MT_edit_armature_delete(Menu):
+    bl_label = "Delete"
+
+    def draw(self, context):
+        layout = self.layout
+
+        layout.operator("armature.delete", text="Delete Bones")
+
+        layout.separator()
+
+        layout.operator("armature.dissolve", text="Dissolve")
+
+
 # ********** Panel **********
 
 
index fdb465f7105159ebc75020c2d152600c6820d593..3fceef5d95dd5f86264c7fbb7ac237c0f8b72439 100644 (file)
@@ -140,6 +140,10 @@ void                 BKE_pose_channels_free_ex(struct bPose *pose, bool do_id_us
 void                 BKE_pose_channels_hash_make(struct bPose *pose);
 void                 BKE_pose_channels_hash_free(struct bPose *pose);
 
+void BKE_pose_channels_remove(
+        struct Object *ob,
+        bool (*filter_fn)(const char *bone_name, void *user_data), void *user_data);
+
 void                 BKE_pose_free(struct bPose *pose);
 void                 BKE_pose_free_ex(struct bPose *pose, bool do_id_user);
 void                 BKE_pose_copy_data(struct bPose **dst, struct bPose *src, const bool copy_constraints);
index 7e09ad355a7359be8ea5bfc85f3e8eabc0368264..1b73978b524fbdc7ef8fc53afd74b91ed5c250b2 100644 (file)
@@ -716,6 +716,57 @@ void BKE_pose_channels_hash_free(bPose *pose)
        }
 }
 
+/**
+ * Selectively remove pose channels.
+ */
+void BKE_pose_channels_remove(
+        Object *ob,
+        bool (*filter_fn)(const char *bone_name, void *user_data), void *user_data)
+{
+       /*  First erase any associated pose channel */
+       if (ob->pose) {
+               bPoseChannel *pchan, *pchan_next;
+               bConstraint *con;
+
+               for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan_next) {
+                       pchan_next = pchan->next;
+
+                       if (filter_fn(pchan->name, user_data)) {
+                               BKE_pose_channel_free(pchan);
+                               if (ob->pose->chanhash) {
+                                       BLI_ghash_remove(ob->pose->chanhash, pchan->name, NULL, NULL);
+                               }
+                               BLI_freelinkN(&ob->pose->chanbase, pchan);
+                       }
+                       else {
+                               for (con = pchan->constraints.first; con; con = con->next) {
+                                       const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
+                                       ListBase targets = {NULL, NULL};
+                                       bConstraintTarget *ct;
+
+                                       if (cti && cti->get_constraint_targets) {
+                                               cti->get_constraint_targets(con, &targets);
+
+                                               for (ct = targets.first; ct; ct = ct->next) {
+                                                       if (ct->tar == ob) {
+                                                               if (ct->subtarget[0]) {
+                                                                       if (filter_fn(ct->subtarget, user_data)) {
+                                                                               con->flag |= CONSTRAINT_DISABLE;
+                                                                               ct->subtarget[0] = 0;
+                                                                       }
+                                                               }
+                                                       }
+                                               }
+
+                                               if (cti->flush_constraint_targets)
+                                                       cti->flush_constraint_targets(con, &targets, 0);
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
 /**
  * Deallocates a pose channel.
  * Does not free the pose channel itself.
index d4df77753eb16c33e53b480353411b367673b7ad..b3297db61763bb20c99ae7122ad6a0b49f4a49ec 100644 (file)
@@ -42,6 +42,7 @@
 
 #include "BLI_blenlib.h"
 #include "BLI_math.h"
+#include "BLI_ghash.h"
 
 #include "BKE_action.h"
 #include "BKE_armature.h"
@@ -1233,13 +1234,21 @@ void ARMATURE_OT_split(wmOperatorType *ot)
 
 /* ********************************* Delete ******************************* */
 
+static bool armature_delete_ebone_cb(const char *bone_name, void *arm_p)
+{
+       bArmature *arm = arm_p;
+       EditBone *ebone;
+
+       ebone = ED_armature_bone_find_name(arm->edbo, bone_name);
+       return (ebone && (ebone->flag & BONE_SELECTED) && (arm->layer & ebone->layer));
+}
+
 /* previously delete_armature */
 /* only editmode! */
 static int armature_delete_selected_exec(bContext *C, wmOperator *UNUSED(op))
 {
        bArmature *arm;
        EditBone *curBone, *ebone_next;
-       bConstraint *con;
        Object *obedit = CTX_data_edit_object(C); // XXX get from context
        bool changed = false;
        arm = obedit->data;
@@ -1250,48 +1259,8 @@ static int armature_delete_selected_exec(bContext *C, wmOperator *UNUSED(op))
        
        armature_select_mirrored(arm);
        
-       /*  First erase any associated pose channel */
-       if (obedit->pose) {
-               bPoseChannel *pchan, *pchan_next;
-               for (pchan = obedit->pose->chanbase.first; pchan; pchan = pchan_next) {
-                       pchan_next = pchan->next;
-                       curBone = ED_armature_bone_find_name(arm->edbo, pchan->name);
-                       
-                       if (curBone && (curBone->flag & BONE_SELECTED) && (arm->layer & curBone->layer)) {
-                               BKE_pose_channel_free(pchan);
-                               BKE_pose_channels_hash_free(obedit->pose);
-                               BLI_freelinkN(&obedit->pose->chanbase, pchan);
-                       }
-                       else {
-                               for (con = pchan->constraints.first; con; con = con->next) {
-                                       const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
-                                       ListBase targets = {NULL, NULL};
-                                       bConstraintTarget *ct;
-                                       
-                                       if (cti && cti->get_constraint_targets) {
-                                               cti->get_constraint_targets(con, &targets);
-                                               
-                                               for (ct = targets.first; ct; ct = ct->next) {
-                                                       if (ct->tar == obedit) {
-                                                               if (ct->subtarget[0]) {
-                                                                       curBone = ED_armature_bone_find_name(arm->edbo, ct->subtarget);
-                                                                       if (curBone && (curBone->flag & BONE_SELECTED) && (arm->layer & curBone->layer)) {
-                                                                               con->flag |= CONSTRAINT_DISABLE;
-                                                                               ct->subtarget[0] = 0;
-                                                                       }
-                                                               }
-                                                       }
-                                               }
-                                               
-                                               if (cti->flush_constraint_targets)
-                                                       cti->flush_constraint_targets(con, &targets, 0);
-                                       }
-                               }
-                       }
-               }
-       }
-       
-       
+       BKE_pose_channels_remove(obedit, armature_delete_ebone_cb, arm);
+
        for (curBone = arm->edbo->first; curBone; curBone = ebone_next) {
                ebone_next = curBone->next;
                if (arm->layer & curBone->layer) {
@@ -1329,6 +1298,170 @@ void ARMATURE_OT_delete(wmOperatorType *ot)
        ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 }
 
+static bool armature_dissolve_ebone_cb(const char *bone_name, void *arm_p)
+{
+       bArmature *arm = arm_p;
+       EditBone *ebone;
+
+       ebone = ED_armature_bone_find_name(arm->edbo, bone_name);
+       return (ebone && (ebone->flag & BONE_DONE));
+}
+
+static int armature_dissolve_selected_exec(bContext *C, wmOperator *UNUSED(op))
+{
+       bArmature *arm;
+       EditBone *ebone, *ebone_next;
+       Object *obedit = CTX_data_edit_object(C);
+       bool changed = false;
+
+       /* store for mirror */
+       GHash *ebone_flag_orig = NULL;
+       int ebone_num = 0;
+
+       arm = obedit->data;
+
+       for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
+               ebone->temp.p = NULL;
+               ebone->flag &= ~BONE_DONE;
+               ebone_num++;
+       }
+
+       if (arm->flag & ARM_MIRROR_EDIT) {
+               GHashIterator gh_iter;
+
+               ebone_flag_orig = BLI_ghash_ptr_new_ex(__func__, ebone_num);
+               for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
+                       union { int flag; void *p; } val = {0};
+                       val.flag = ebone->flag;
+                       BLI_ghash_insert(ebone_flag_orig, ebone, val.p);
+               }
+
+               armature_select_mirrored_ex(arm, BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL);
+
+               GHASH_ITER (gh_iter, ebone_flag_orig) {
+                       union { int flag; void *p; } *val_p = (void *)BLI_ghashIterator_getValue_p(&gh_iter);
+                       ebone = BLI_ghashIterator_getKey(&gh_iter);
+                       val_p->flag = ebone->flag & ~val_p->flag;
+               }
+       }
+
+       for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
+               if (ebone->parent && ebone->flag & BONE_CONNECTED) {
+                       if (ebone->parent->temp.ebone == ebone->parent) {
+                               /* ignore */
+                       }
+                       else if (ebone->parent->temp.ebone) {
+                               /* set ignored */
+                               ebone->parent->temp.ebone = ebone->parent;
+                       }
+                       else {
+                               /* set child */
+                               ebone->parent->temp.ebone = ebone;
+                       }
+               }
+       }
+
+       /* cleanup multiple used bones */
+       for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
+               if (ebone->temp.ebone == ebone) {
+                       ebone->temp.ebone = NULL;
+               }
+       }
+
+       for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
+               /* break connections for unseen bones */
+               if (((arm->layer & ebone->layer) &&
+                    ((ED_armature_ebone_selectflag_get(ebone) & (BONE_TIPSEL | BONE_SELECTED)))) == 0)
+               {
+                       ebone->temp.ebone = NULL;
+               }
+
+               if (((arm->layer & ebone->layer) &&
+                   ((ED_armature_ebone_selectflag_get(ebone) & (BONE_ROOTSEL | BONE_SELECTED)))) == 0)
+               {
+                       if (ebone->parent && (ebone->flag & BONE_CONNECTED)) {
+                               ebone->parent->temp.ebone = NULL;
+                       }
+
+               }
+       }
+
+       for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
+
+               if (ebone->parent &&
+                   (ebone->parent->temp.ebone == ebone))
+               {
+                       ebone->flag |= BONE_DONE;
+               }
+       }
+
+       BKE_pose_channels_remove(obedit, armature_dissolve_ebone_cb, arm);
+
+       for (ebone = arm->edbo->first; ebone; ebone = ebone_next) {
+               ebone_next = ebone->next;
+
+               if (ebone->flag & BONE_DONE) {
+                       copy_v3_v3(ebone->parent->tail, ebone->tail);
+                       ebone->parent->rad_tail = ebone->rad_tail;
+
+                       ED_armature_edit_bone_remove(arm, ebone);
+                       changed = true;
+               }
+       }
+
+       if (changed) {
+               for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
+                       if (ebone->parent &&
+                           ebone->parent->temp.ebone &&
+                           (ebone->flag & BONE_CONNECTED) == 0)
+                       {
+                               ebone->flag |= BONE_CONNECTED;
+                               ebone->rad_head = ebone->parent->rad_head;
+                       }
+               }
+
+               if (arm->flag & ARM_MIRROR_EDIT) {
+                       for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
+                               union { int flag; void *p; } *val_p = (void *)BLI_ghash_lookup_p(ebone_flag_orig, ebone);
+                               if (val_p && val_p->flag) {
+                                       ebone->flag &= ~val_p->flag;
+                               }
+                       }
+               }
+       }
+
+       if (arm->flag & ARM_MIRROR_EDIT) {
+               BLI_ghash_free(ebone_flag_orig, NULL, NULL);
+       }
+
+       if (!changed) {
+               return OPERATOR_CANCELLED;
+       }
+
+       ED_armature_sync_selection(arm->edbo);
+
+       WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);
+
+       return OPERATOR_FINISHED;
+}
+
+void ARMATURE_OT_dissolve(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Dissolve Selected Bone(s)";
+       ot->idname = "ARMATURE_OT_dissolve";
+       ot->description = "Dissolve selected bones from the armature";
+
+       /* api callbacks */
+       ot->exec = armature_dissolve_selected_exec;
+       ot->poll = ED_operator_editarmature;
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+
+
 /* ********************************* Show/Hide ******************************* */
 
 static int armature_hide_exec(bContext *C, wmOperator *op)
index 0607bc49515455beff8408d449bf3ad161ef9fef..2968851f2ebf53ba1caa92cc808e4e23ce4a7605 100644 (file)
@@ -71,6 +71,7 @@ void ARMATURE_OT_select_similar(struct wmOperatorType *ot);
 void ARMATURE_OT_shortest_path_pick(struct wmOperatorType *ot);
 
 void ARMATURE_OT_delete(struct wmOperatorType *ot);
+void ARMATURE_OT_dissolve(struct wmOperatorType *ot);
 void ARMATURE_OT_duplicate(struct wmOperatorType *ot);
 void ARMATURE_OT_symmetrize(struct wmOperatorType *ot);
 void ARMATURE_OT_extrude(struct wmOperatorType *ot);
@@ -234,6 +235,7 @@ EditBone *add_points_bone(struct Object *obedit, float head[3], float tail[3]);
 void bone_free(struct bArmature *arm, struct EditBone *bone);
 
 void armature_tag_select_mirrored(struct bArmature *arm);
+void armature_select_mirrored_ex(struct bArmature *arm, const int flag);
 void armature_select_mirrored(struct bArmature *arm);
 void armature_tag_unselect(struct bArmature *arm);
 
index ea435e3e4faf34ef60168814a4f7ec43962177fa..e7f036f6911c32e05295f6efa1ad6c8e1075e9a8 100644 (file)
@@ -65,6 +65,7 @@ void ED_operatortypes_armature(void)
        WM_operatortype_append(ARMATURE_OT_shortest_path_pick);
 
        WM_operatortype_append(ARMATURE_OT_delete);
+       WM_operatortype_append(ARMATURE_OT_dissolve);
        WM_operatortype_append(ARMATURE_OT_duplicate);
        WM_operatortype_append(ARMATURE_OT_symmetrize);
        WM_operatortype_append(ARMATURE_OT_extrude);
@@ -266,8 +267,9 @@ void ED_keymap_armature(wmKeyConfig *keyconf)
 
        WM_keymap_add_item(keymap, "ARMATURE_OT_shortest_path_pick", SELECTMOUSE, KM_PRESS, KM_CTRL, 0);
        
-       WM_keymap_add_item(keymap, "ARMATURE_OT_delete", XKEY, KM_PRESS, 0, 0);
-       WM_keymap_add_item(keymap, "ARMATURE_OT_delete", DELKEY, KM_PRESS, 0, 0);
+       WM_keymap_add_menu(keymap, "VIEW3D_MT_edit_armature_delete", XKEY, KM_PRESS, 0, 0);
+       WM_keymap_add_menu(keymap, "VIEW3D_MT_edit_armature_delete", DELKEY, KM_PRESS, 0, 0);
+       WM_keymap_add_item(keymap, "ARMATURE_OT_dissolve", XKEY, KM_PRESS, KM_CTRL, 0);
        WM_keymap_add_item(keymap, "ARMATURE_OT_duplicate_move", DKEY, KM_PRESS, KM_SHIFT, 0);
        WM_keymap_add_item(keymap, "ARMATURE_OT_extrude_move", EKEY, KM_PRESS, 0, 0);
        WM_keymap_add_item(keymap, "ARMATURE_OT_extrude_forked", EKEY, KM_PRESS, KM_SHIFT, 0);
index 183519504623bd533ca375a84619390273d140a7..09284860ec40be46c5f9735c6d3b809eb0d61d37 100644 (file)
@@ -276,18 +276,19 @@ EditBone *ED_armature_bone_get_mirrored(const ListBase *edbo, EditBone *ebo)
 
 /* helper function for tools to work on mirrored parts.
  * it leaves mirrored bones selected then too, which is a good indication of what happened */
-void armature_select_mirrored(bArmature *arm)
+void armature_select_mirrored_ex(bArmature *arm, const int flag)
 {
+       BLI_assert((flag & ~(BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL)) == 0);
        /* Select mirrored bones */
        if (arm->flag & ARM_MIRROR_EDIT) {
                EditBone *curBone, *ebone_mirr;
                
                for (curBone = arm->edbo->first; curBone; curBone = curBone->next) {
                        if (arm->layer & curBone->layer) {
-                               if (curBone->flag & BONE_SELECTED) {
+                               if (curBone->flag & flag) {
                                        ebone_mirr = ED_armature_bone_get_mirrored(arm->edbo, curBone);
                                        if (ebone_mirr)
-                                               ebone_mirr->flag |= BONE_SELECTED;
+                                               ebone_mirr->flag |= (curBone->flag & flag);
                                }
                        }
                }
@@ -295,6 +296,11 @@ void armature_select_mirrored(bArmature *arm)
        
 }
 
+void armature_select_mirrored(bArmature *arm)
+{
+       armature_select_mirrored_ex(arm, BONE_SELECTED);
+}
+
 void armature_tag_select_mirrored(bArmature *arm)
 {
        EditBone *curBone;