Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/armature/armature_add.c')
-rw-r--r--source/blender/editors/armature/armature_add.c547
1 files changed, 503 insertions, 44 deletions
diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c
index f6875a6e158..dfb274fdefe 100644
--- a/source/blender/editors/armature/armature_add.c
+++ b/source/blender/editors/armature/armature_add.c
@@ -22,6 +22,7 @@
* \ingroup edarmature
*/
+#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
#include "DNA_constraint_types.h"
#include "DNA_object_types.h"
@@ -38,8 +39,12 @@
#include "BKE_constraint.h"
#include "BKE_context.h"
#include "BKE_deform.h"
+#include "BKE_fcurve.h"
#include "BKE_idprop.h"
#include "BKE_layer.h"
+#include "BKE_lib_id.h"
+#include "BKE_library.h"
+#include "BKE_main.h"
#include "RNA_access.h"
#include "RNA_define.h"
@@ -375,25 +380,20 @@ void postEditBoneDuplicate(struct ListBase *editbones, Object *ob)
BLI_ghash_free(name_map, NULL, NULL);
}
-/*
- * Note: When duplicating cross objects, editbones here is the list of bones
- * from the SOURCE object but ob is the DESTINATION object
- * */
-void updateDuplicateSubtargetObjects(EditBone *dupBone,
+static void updateDuplicateSubtarget(EditBone *dup_bone,
ListBase *editbones,
- Object *src_ob,
- Object *dst_ob)
+ Object *ob,
+ bool lookup_mirror_subtarget)
{
- /* If an edit bone has been duplicated, lets
- * update it's constraints if the subtarget
- * they point to has also been duplicated
+ /* If an edit bone has been duplicated, lets update it's constraints if the
+ * subtarget they point to has also been duplicated.
*/
EditBone *oldtarget, *newtarget;
bPoseChannel *pchan;
bConstraint *curcon;
ListBase *conlist;
- if ((pchan = BKE_pose_channel_verify(dst_ob->pose, dupBone->name))) {
+ if ((pchan = BKE_pose_channel_verify(ob->pose, dup_bone->name))) {
if ((conlist = &pchan->constraints)) {
for (curcon = conlist->first; curcon; curcon = curcon->next) {
/* does this constraint have a subtarget in
@@ -407,8 +407,7 @@ void updateDuplicateSubtargetObjects(EditBone *dupBone,
cti->get_constraint_targets(curcon, &targets);
for (ct = targets.first; ct; ct = ct->next) {
- if ((ct->tar == src_ob) && (ct->subtarget[0])) {
- ct->tar = dst_ob; /* update target */
+ if ((ct->tar == ob) && (ct->subtarget[0])) {
oldtarget = get_named_editbone(editbones, ct->subtarget);
if (oldtarget) {
/* was the subtarget bone duplicated too? If
@@ -419,6 +418,17 @@ void updateDuplicateSubtargetObjects(EditBone *dupBone,
newtarget = oldtarget->temp.ebone;
BLI_strncpy(ct->subtarget, newtarget->name, sizeof(ct->subtarget));
}
+ else if (lookup_mirror_subtarget) {
+ /* The subtarget was not selected for duplication, try to see if a mirror bone of
+ * the current target exists */
+ char name_flip[MAXBONENAME];
+
+ BLI_string_flip_side_name(name_flip, oldtarget->name, false, sizeof(name_flip));
+ newtarget = get_named_editbone(editbones, name_flip);
+ if (newtarget) {
+ BLI_strncpy(ct->subtarget, newtarget->name, sizeof(ct->subtarget));
+ }
+ }
}
}
}
@@ -432,32 +442,434 @@ void updateDuplicateSubtargetObjects(EditBone *dupBone,
}
}
-void updateDuplicateSubtarget(EditBone *dupBone, ListBase *editbones, Object *ob)
+static void updateDuplicateActionConstraintSettings(EditBone *dup_bone,
+ EditBone *orig_bone,
+ Object *ob,
+ bConstraint *curcon)
{
- updateDuplicateSubtargetObjects(dupBone, editbones, ob, ob);
+ bActionConstraint *act_con = (bActionConstraint *)curcon->data;
+ bAction *act = (bAction *)act_con->act;
+
+ float mat[4][4];
+
+ unit_m4(mat);
+ bPoseChannel *target_pchan = BKE_pose_channel_find_name(ob->pose, act_con->subtarget);
+ BKE_constraint_mat_convertspace(
+ ob, target_pchan, mat, curcon->tarspace, CONSTRAINT_SPACE_LOCAL, false);
+
+ float max_axis_val = 0;
+ int max_axis = 0;
+ /* Which axis represents X now. IE, which axis defines the mirror plane. */
+ for (int i = 0; i < 3; i++) {
+ float cur_val = fabsf(mat[0][i]);
+ if (cur_val > max_axis_val) {
+ max_axis = i;
+ max_axis_val = cur_val;
+ }
+ }
+
+ /* data->type is mapped as follows for backwards compatibility:
+ * 00,01,02 - rotation (it used to be like this)
+ * 10,11,12 - scaling
+ * 20,21,22 - location
+ */
+ /* Mirror the target range */
+ if (act_con->type < 10 && act_con->type != max_axis) {
+ /* Y or Z rotation */
+ act_con->min = -act_con->min;
+ act_con->max = -act_con->max;
+ }
+ else if (act_con->type == max_axis + 10) {
+ /* X scaling */
+ }
+ else if (act_con->type == max_axis + 20) {
+ /* X location */
+ float imat[4][4];
+
+ invert_m4_m4(imat, mat);
+
+ float min_vec[3], max_vec[3];
+
+ zero_v3(min_vec);
+ zero_v3(max_vec);
+
+ min_vec[0] = act_con->min;
+ max_vec[0] = act_con->max;
+
+ /* convert values into local object space */
+ mul_m4_v3(mat, min_vec);
+ mul_m4_v3(mat, max_vec);
+
+ min_vec[0] *= -1;
+ max_vec[0] *= -1;
+
+ /* convert back to the settings space */
+ mul_m4_v3(imat, min_vec);
+ mul_m4_v3(imat, max_vec);
+
+ act_con->min = min_vec[0];
+ act_con->max = max_vec[0];
+ }
+
+ /* See if there is any channels that uses this bone */
+ ListBase ani_curves;
+ BLI_listbase_clear(&ani_curves);
+ if (list_find_data_fcurves(&ani_curves, &act->curves, "pose.bones[", orig_bone->name)) {
+ /* Create a copy and mirror the animation */
+ for (LinkData *ld = ani_curves.first; ld; ld = ld->next) {
+ FCurve *old_curve = ld->data;
+ FCurve *new_curve = copy_fcurve(old_curve);
+ bActionGroup *agrp;
+
+ char *old_path = new_curve->rna_path;
+
+ new_curve->rna_path = BLI_str_replaceN(old_path, orig_bone->name, dup_bone->name);
+ MEM_freeN(old_path);
+
+ /* Flip the animation */
+ int i;
+ BezTriple *bezt;
+ for (i = 0, bezt = new_curve->bezt; i < new_curve->totvert; i++, bezt++) {
+ const size_t slength = strlen(new_curve->rna_path);
+ bool flip = false;
+ if (BLI_strn_endswith(new_curve->rna_path, "location", slength) &&
+ new_curve->array_index == 0) {
+ flip = true;
+ }
+ else if (BLI_strn_endswith(new_curve->rna_path, "rotation_quaternion", slength) &&
+ ELEM(new_curve->array_index, 2, 3)) {
+ flip = true;
+ }
+ else if (BLI_strn_endswith(new_curve->rna_path, "rotation_euler", slength) &&
+ ELEM(new_curve->array_index, 1, 2)) {
+ flip = true;
+ }
+ else if (BLI_strn_endswith(new_curve->rna_path, "rotation_axis_angle", slength) &&
+ ELEM(new_curve->array_index, 2, 3)) {
+ flip = true;
+ }
+
+ if (flip) {
+ bezt->vec[0][1] *= -1;
+ bezt->vec[1][1] *= -1;
+ bezt->vec[2][1] *= -1;
+ }
+ }
+
+ /* Make sure that a action group name for the new bone exists */
+ agrp = BKE_action_group_find_name(act, dup_bone->name);
+
+ if (agrp == NULL) {
+ agrp = action_groups_add_new(act, dup_bone->name);
+ }
+ BLI_assert(agrp != NULL);
+ action_groups_add_channel(act, agrp, new_curve);
+ }
+ }
+ BLI_freelistN(&ani_curves);
+
+ /* Make deps graph aware of our changes */
+ DEG_id_tag_update(&act->id, ID_RECALC_ANIMATION_NO_FLUSH);
}
-EditBone *duplicateEditBoneObjects(
- EditBone *curBone, const char *name, ListBase *editbones, Object *src_ob, Object *dst_ob)
+static void updateDuplicateKinematicConstraintSettings(bConstraint *curcon)
{
- EditBone *eBone = MEM_mallocN(sizeof(EditBone), "addup_editbone");
+ /* IK constraint */
+ bKinematicConstraint *ik = (bKinematicConstraint *)curcon->data;
+ ik->poleangle = -M_PI - ik->poleangle;
+ /* Wrap the angle to the +/-180.0f range (default soft limit of the input boxes). */
+ ik->poleangle = angle_wrap_rad(ik->poleangle);
+}
- /* Copy data from old bone to new bone */
- memcpy(eBone, curBone, sizeof(EditBone));
+static void updateDuplicateLocRotConstraintSettings(Object *ob,
+ bPoseChannel *pchan,
+ bConstraint *curcon)
+{
+ /* This code assumes that bRotLimitConstraint and bLocLimitConstraint have the same fields in
+ * the same memory locations. */
+ BLI_assert(sizeof(bLocLimitConstraint) == sizeof(bRotLimitConstraint));
- curBone->temp.ebone = eBone;
- eBone->temp.ebone = curBone;
+ bRotLimitConstraint *limit = (bRotLimitConstraint *)curcon->data;
+ float local_mat[4][4], imat[4][4];
- if (name != NULL) {
- BLI_strncpy(eBone->name, name, sizeof(eBone->name));
+ float min_vec[3], max_vec[3];
+
+ min_vec[0] = limit->xmin;
+ min_vec[1] = limit->ymin;
+ min_vec[2] = limit->zmin;
+
+ max_vec[0] = limit->xmax;
+ max_vec[1] = limit->ymax;
+ max_vec[2] = limit->zmax;
+
+ unit_m4(local_mat);
+
+ BKE_constraint_mat_convertspace(
+ ob, pchan, local_mat, curcon->ownspace, CONSTRAINT_SPACE_LOCAL, false);
+
+ if (curcon->type == CONSTRAINT_TYPE_ROTLIMIT) {
+ /* Zero out any location translation */
+ local_mat[3][0] = local_mat[3][1] = local_mat[3][2] = 0;
+ }
+
+ invert_m4_m4(imat, local_mat);
+ /* convert values into local object space */
+ mul_m4_v3(local_mat, min_vec);
+ mul_m4_v3(local_mat, max_vec);
+
+ if (curcon->type == CONSTRAINT_TYPE_ROTLIMIT) {
+ float min_copy[3];
+
+ copy_v3_v3(min_copy, min_vec);
+
+ min_vec[1] = max_vec[1] * -1;
+ min_vec[2] = max_vec[2] * -1;
+
+ max_vec[1] = min_copy[1] * -1;
+ max_vec[2] = min_copy[2] * -1;
+ }
+ else {
+ float min_x_copy = min_vec[0];
+
+ min_vec[0] = max_vec[0] * -1;
+ max_vec[0] = min_x_copy * -1;
+ }
+
+ /* convert back to the settings space */
+ mul_m4_v3(imat, min_vec);
+ mul_m4_v3(imat, max_vec);
+
+ limit->xmin = min_vec[0];
+ limit->ymin = min_vec[1];
+ limit->zmin = min_vec[2];
+
+ limit->xmax = max_vec[0];
+ limit->ymax = max_vec[1];
+ limit->zmax = max_vec[2];
+}
+
+static void updateDuplicateTransformConstraintSettings(Object *ob,
+ bPoseChannel *pchan,
+ bConstraint *curcon)
+{
+ bTransformConstraint *trans = (bTransformConstraint *)curcon->data;
+
+ float target_mat[4][4], own_mat[4][4], imat[4][4];
+
+ unit_m4(own_mat);
+ BKE_constraint_mat_convertspace(
+ ob, pchan, own_mat, curcon->ownspace, CONSTRAINT_SPACE_LOCAL, false);
+
+ /* ###Source map mirroring### */
+ float old_min, old_max;
+
+ /* Source location */
+ invert_m4_m4(imat, own_mat);
+
+ /* convert values into local object space */
+ mul_m4_v3(own_mat, trans->from_min);
+ mul_m4_v3(own_mat, trans->from_max);
+
+ old_min = trans->from_min[0];
+ old_max = trans->from_max[0];
+
+ trans->from_min[0] = -old_max;
+ trans->from_max[0] = -old_min;
+
+ /* convert back to the settings space */
+ mul_m4_v3(imat, trans->from_min);
+ mul_m4_v3(imat, trans->from_max);
+
+ /* Source rotation */
+
+ /* Zero out any location translation */
+ own_mat[3][0] = own_mat[3][1] = own_mat[3][2] = 0;
+
+ invert_m4_m4(imat, own_mat);
+
+ /* convert values into local object space */
+ mul_m4_v3(own_mat, trans->from_min_rot);
+ mul_m4_v3(own_mat, trans->from_max_rot);
+
+ old_min = trans->from_min_rot[1];
+ old_max = trans->from_max_rot[1];
+
+ trans->from_min_rot[1] = old_max * -1;
+ trans->from_max_rot[1] = old_min * -1;
+
+ old_min = trans->from_min_rot[2];
+ old_max = trans->from_max_rot[2];
+
+ trans->from_min_rot[2] = old_max * -1;
+ trans->from_max_rot[2] = old_min * -1;
+
+ /* convert back to the settings space */
+ mul_m4_v3(imat, trans->from_min_rot);
+ mul_m4_v3(imat, trans->from_max_rot);
+
+ /* Source scale does not require any mirroring */
+
+ /* ###Destination map mirroring### */
+ float temp_vec[3];
+ float imat_rot[4][4];
+
+ bPoseChannel *target_pchan = BKE_pose_channel_find_name(ob->pose, trans->subtarget);
+ unit_m4(target_mat);
+ BKE_constraint_mat_convertspace(
+ ob, target_pchan, target_mat, curcon->tarspace, CONSTRAINT_SPACE_LOCAL, false);
+
+ invert_m4_m4(imat, target_mat);
+ /* convert values into local object space */
+ mul_m4_v3(target_mat, trans->to_min);
+ mul_m4_v3(target_mat, trans->to_max);
+ mul_m4_v3(target_mat, trans->to_min_scale);
+ mul_m4_v3(target_mat, trans->to_max_scale);
+
+ /* Zero out any location translation */
+ target_mat[3][0] = target_mat[3][1] = target_mat[3][2] = 0;
+ invert_m4_m4(imat_rot, target_mat);
+
+ mul_m4_v3(target_mat, trans->to_min_rot);
+ mul_m4_v3(target_mat, trans->to_max_rot);
+
+ /* TODO(sebpa): This does not support euler order, but doing so will make this way more complex.
+ * For now we have decided to not support all corner cases and advanced setups. */
+
+ /* Helper variables to denote the axis in trans->map */
+ const char X = 0;
+ const char Y = 1;
+ const char Z = 2;
+
+ switch (trans->to) {
+ case TRANS_SCALE:
+ copy_v3_v3(temp_vec, trans->to_max_scale);
+
+ for (int i = 0; i < 3; i++) {
+ if ((trans->from == TRANS_LOCATION && trans->map[i] == X) ||
+ (trans->from == TRANS_ROTATION && trans->map[i] != X)) {
+ /* X Loc to X/Y/Z Scale: Min/Max Flipped */
+ /* Y Rot to X/Y/Z Scale: Min/Max Flipped */
+ /* Z Rot to X/Y/Z Scale: Min/Max Flipped */
+ trans->to_max_scale[i] = trans->to_min_scale[i];
+ trans->to_min_scale[i] = temp_vec[i];
+ }
+ }
+ break;
+ case TRANS_LOCATION:
+ /* Invert the X location */
+ trans->to_min[0] *= -1;
+ trans->to_max[0] *= -1;
+
+ copy_v3_v3(temp_vec, trans->to_max);
+
+ for (int i = 0; i < 3; i++) {
+ if ((trans->from == TRANS_LOCATION && trans->map[i] == X) ||
+ (trans->from == TRANS_ROTATION && trans->map[i] != X)) {
+ /* X Loc to X/Y/Z Loc: Min/Max Flipped (and Inverted)
+ * Y Rot to X/Y/Z Loc: Min/Max Flipped
+ * Z Rot to X/Y/Z Loc: Min/Max Flipped */
+ trans->to_max[i] = trans->to_min[i];
+ trans->to_min[i] = temp_vec[i];
+ }
+ }
+ break;
+ case TRANS_ROTATION:
+ /* Invert the Z rotation */
+ trans->to_min_rot[2] *= -1;
+ trans->to_max_rot[2] *= -1;
+
+ if ((trans->from == TRANS_LOCATION && trans->map[1] != X) ||
+ (trans->from == TRANS_ROTATION && trans->map[1] != Y) || trans->from == TRANS_SCALE) {
+ /* Invert the Y rotation */
+ trans->to_min_rot[1] *= -1;
+ trans->to_max_rot[1] *= -1;
+ }
+
+ copy_v3_v3(temp_vec, trans->to_max_rot);
+
+ for (int i = 0; i < 3; i++) {
+ if ((trans->from == TRANS_LOCATION && trans->map[i] == X && i != 1) ||
+ (trans->from == TRANS_ROTATION && trans->map[i] == Y && i != 1) ||
+ (trans->from == TRANS_ROTATION && trans->map[i] == Z)) {
+ /* X Loc to X/Z Rot: Flipped
+ * Y Rot to X/Z Rot: Flipped
+ * Z Rot to X/Y/Z rot: Flipped */
+ trans->to_max_rot[i] = trans->to_min_rot[i];
+ trans->to_min_rot[i] = temp_vec[i];
+ }
+ }
+ break;
+ }
+ /* convert back to the settings space */
+ mul_m4_v3(imat, trans->to_min);
+ mul_m4_v3(imat, trans->to_max);
+ mul_m4_v3(imat_rot, trans->to_min_rot);
+ mul_m4_v3(imat_rot, trans->to_max_rot);
+ mul_m4_v3(imat, trans->to_min_scale);
+ mul_m4_v3(imat, trans->to_max_scale);
+}
+
+static void updateDuplicateConstraintSettings(EditBone *dup_bone, EditBone *orig_bone, Object *ob)
+{
+ /* If an edit bone has been duplicated, lets update it's constraints if the
+ * subtarget they point to has also been duplicated.
+ */
+ bPoseChannel *pchan;
+ bConstraint *curcon;
+ ListBase *conlist;
+
+ if ((pchan = BKE_pose_channel_verify(ob->pose, dup_bone->name)) == NULL ||
+ (conlist = &pchan->constraints) == NULL) {
+ return;
+ }
+
+ for (curcon = conlist->first; curcon; curcon = curcon->next) {
+ switch (curcon->type) {
+ case CONSTRAINT_TYPE_ACTION:
+ updateDuplicateActionConstraintSettings(dup_bone, orig_bone, ob, curcon);
+ break;
+ case CONSTRAINT_TYPE_KINEMATIC:
+ updateDuplicateKinematicConstraintSettings(curcon);
+ break;
+ case CONSTRAINT_TYPE_LOCLIMIT:
+ case CONSTRAINT_TYPE_ROTLIMIT:
+ updateDuplicateLocRotConstraintSettings(ob, pchan, curcon);
+ break;
+ case CONSTRAINT_TYPE_TRANSFORM:
+ updateDuplicateTransformConstraintSettings(ob, pchan, curcon);
+ break;
+ }
+ }
+}
+
+static void updateDuplicateCustomBoneShapes(bContext *C, EditBone *dup_bone, Object *ob)
+{
+ if (ob->pose == NULL) {
+ return;
}
+ bPoseChannel *pchan;
+ pchan = BKE_pose_channel_verify(ob->pose, dup_bone->name);
- ED_armature_ebone_unique_name(editbones, eBone->name, NULL);
- BLI_addtail(editbones, eBone);
+ if (pchan->custom != NULL) {
+ Main *bmain = CTX_data_main(C);
+ char name_flip[MAX_ID_NAME - 2];
+ /* Skip the first two chars in the object name as those are used to store object type */
+ BLI_string_flip_side_name(name_flip, pchan->custom->id.name + 2, false, sizeof(name_flip));
+ Object *shape_ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, name_flip);
+
+ if (shape_ob != NULL) {
+ /* A flipped shape object exists, use it! */
+ pchan->custom = shape_ob;
+ }
+ }
+}
+
+static void copy_pchan(EditBone *src_bone, EditBone *dst_bone, Object *src_ob, Object *dst_ob)
+{
/* copy the ID property */
- if (curBone->prop) {
- eBone->prop = IDP_CopyProperty(curBone->prop);
+ if (src_bone->prop) {
+ dst_bone->prop = IDP_CopyProperty(src_bone->prop);
}
/* Lets duplicate the list of constraints that the
@@ -466,25 +878,46 @@ EditBone *duplicateEditBoneObjects(
if (src_ob->pose) {
bPoseChannel *chanold, *channew;
- chanold = BKE_pose_channel_verify(src_ob->pose, curBone->name);
+ chanold = BKE_pose_channel_verify(src_ob->pose, src_bone->name);
if (chanold) {
/* WARNING: this creates a new posechannel, but there will not be an attached bone
* yet as the new bones created here are still 'EditBones' not 'Bones'.
*/
- channew = BKE_pose_channel_verify(dst_ob->pose, eBone->name);
+ channew = BKE_pose_channel_verify(dst_ob->pose, dst_bone->name);
if (channew) {
BKE_pose_channel_copy_data(channew, chanold);
}
}
}
+}
- return eBone;
+EditBone *duplicateEditBoneObjects(
+ EditBone *cur_bone, const char *name, ListBase *editbones, Object *src_ob, Object *dst_ob)
+{
+ EditBone *e_bone = MEM_mallocN(sizeof(EditBone), "addup_editbone");
+
+ /* Copy data from old bone to new bone */
+ memcpy(e_bone, cur_bone, sizeof(EditBone));
+
+ cur_bone->temp.ebone = e_bone;
+ e_bone->temp.ebone = cur_bone;
+
+ if (name != NULL) {
+ BLI_strncpy(e_bone->name, name, sizeof(e_bone->name));
+ }
+
+ ED_armature_ebone_unique_name(editbones, e_bone->name, NULL);
+ BLI_addtail(editbones, e_bone);
+
+ copy_pchan(cur_bone, e_bone, src_ob, dst_ob);
+
+ return e_bone;
}
-EditBone *duplicateEditBone(EditBone *curBone, const char *name, ListBase *editbones, Object *ob)
+EditBone *duplicateEditBone(EditBone *cur_bone, const char *name, ListBase *editbones, Object *ob)
{
- return duplicateEditBoneObjects(curBone, name, editbones, ob, ob);
+ return duplicateEditBoneObjects(cur_bone, name, editbones, ob, ob);
}
static int armature_duplicate_selected_exec(bContext *C, wmOperator *op)
@@ -538,8 +971,8 @@ static int armature_duplicate_selected_exec(bContext *C, wmOperator *op)
BLI_string_flip_side_name(
new_bone_name_buff, ebone_iter->name, false, sizeof(new_bone_name_buff));
- /* Only use flipped name if not yet in use. Otherwise we'd get again inconsistent namings
- * (different numbers), better keep default behavior in this case. */
+ /* Only use flipped name if not yet in use. Otherwise we'd get again inconsistent
+ * namings (different numbers), better keep default behavior in this case. */
if (ED_armature_ebone_find_name(arm->edbo, new_bone_name_buff) == NULL) {
new_bone_name = new_bone_name_buff;
}
@@ -567,13 +1000,13 @@ static int armature_duplicate_selected_exec(bContext *C, wmOperator *op)
}
else if (ebone_iter->parent->temp.ebone) {
/* If this bone has a parent that was duplicated,
- * Set the duplicate->parent to the curBone->parent->temp
+ * Set the duplicate->parent to the cur_bone->parent->temp
*/
ebone->parent = ebone_iter->parent->temp.ebone;
}
else {
/* If this bone has a parent that IS not selected,
- * Set the duplicate->parent to the curBone->parent
+ * Set the duplicate->parent to the cur_bone->parent
*/
ebone->parent = (EditBone *)ebone_iter->parent;
ebone->flag &= ~BONE_CONNECTED;
@@ -590,7 +1023,7 @@ static int armature_duplicate_selected_exec(bContext *C, wmOperator *op)
/* Lets try to fix any constraint subtargets that might
* have been duplicated
*/
- updateDuplicateSubtarget(ebone, arm->edbo, ob);
+ updateDuplicateSubtarget(ebone, arm->edbo, ob, false);
}
}
@@ -745,9 +1178,21 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op)
/* Find the selected bones and duplicate them as needed, with mirrored name. */
for (ebone_iter = arm->edbo->first; ebone_iter && ebone_iter != ebone_first_dupe;
ebone_iter = ebone_iter->next) {
- if (EBONE_VISIBLE(arm, ebone_iter) && (ebone_iter->flag & BONE_SELECTED) &&
- /* will be set if the mirror bone already exists (no need to make a new one) */
- (ebone_iter->temp.ebone == NULL)) {
+ if (EBONE_VISIBLE(arm, ebone_iter) && (ebone_iter->flag & BONE_SELECTED)) {
+ if (ebone_iter->temp.ebone != NULL) {
+ /* This will be set if the mirror bone already exists (no need to make a new one)
+ * but we do need to make sure that the 'pchan' settings (constraints etc)
+ * is synchronized. */
+ bPoseChannel *pchan;
+ /* Make sure we clean up the old data before overwriting it */
+ pchan = BKE_pose_channel_verify(obedit->pose, ebone_iter->temp.ebone->name);
+ BKE_pose_channel_free(pchan);
+ /* Sync pchan data */
+ copy_pchan(ebone_iter, ebone_iter->temp.ebone, obedit, obedit);
+ /* Sync scale mode */
+ ebone_iter->temp.ebone->inherit_scale_mode = ebone_iter->inherit_scale_mode;
+ continue;
+ }
char name_flip[MAXBONENAME];
BLI_string_flip_side_name(name_flip, ebone_iter->name, false, sizeof(name_flip));
@@ -793,7 +1238,12 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op)
* then we can assume the parent has no L/R but is a center bone.
* So just use the same parent for both.
*/
- ebone->flag &= ~BONE_CONNECTED;
+
+ if (ebone->head[axis] != 0.0f) {
+ /* The mirrored bone doesn't start on the mirror axis, so assume that this one should
+ * not be connected to the old parent */
+ ebone->flag &= ~BONE_CONNECTED;
+ }
}
ebone->parent = ebone_parent;
@@ -803,10 +1253,19 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op)
ebone->bbone_prev = get_symmetrized_bone(arm, ebone_iter->bbone_prev);
ebone->bbone_next = get_symmetrized_bone(arm, ebone_iter->bbone_next);
+ /* Sync bbone handle types */
+ ebone->bbone_prev_type = ebone_iter->bbone_prev_type;
+ ebone->bbone_next_type = ebone_iter->bbone_next_type;
+
/* Lets try to fix any constraint subtargets that might
* have been duplicated
*/
- updateDuplicateSubtarget(ebone, arm->edbo, obedit);
+ updateDuplicateSubtarget(ebone, arm->edbo, obedit, true);
+ /* Try to update constraint options so that they are mirrored as well
+ * (need to supply bone_iter as well in case we are working with existing bones) */
+ updateDuplicateConstraintSettings(ebone, ebone_iter, obedit);
+ /* Mirror bone shapes if possible */
+ updateDuplicateCustomBoneShapes(C, ebone, obedit);
}
}
@@ -1232,7 +1691,7 @@ void ARMATURE_OT_subdivide(wmOperatorType *ot)
PropertyRNA *prop;
/* identifiers */
- ot->name = "Subdivide Multi";
+ ot->name = "Subdivide";
ot->idname = "ARMATURE_OT_subdivide";
ot->description = "Break selected bones into chains of smaller bones";