Fix T66751: Symmetrizing armature does not symmetrize constraints.

The symmetrize operator now tries to make sure that the armature
constraints are correctly mirrored.

Before it would only mirror the subtargets for the constraints (and that
failed too in some cases).

Reviewed By: Sybren

Differential Revision: http://developer.blender.org/D6009
This commit is contained in:
Sebastian Parborg 2020-04-07 13:50:57 +02:00
parent 6feeede47f
commit ee43cf5722
Notes: blender-bot 2023-03-24 17:05:22 +01:00
Referenced by issue #66751, Mirroring armature and symmetrizing acts weird
3 changed files with 508 additions and 56 deletions

View File

@ -925,6 +925,7 @@ void BKE_pose_channel_free_ex(bPoseChannel *pchan, bool do_id_user)
if (pchan->prop) {
IDP_FreeProperty(pchan->prop);
pchan->prop = NULL;
}
/* Cached data, for new draw manager rendering code. */

View File

@ -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,435 @@ 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;
EditBone *duplicateEditBoneObjects(
EditBone *curBone, const char *name, ListBase *editbones, Object *src_ob, Object *dst_ob)
{
EditBone *eBone = MEM_mallocN(sizeof(EditBone), "addup_editbone");
float mat[4][4];
/* Copy data from old bone to new bone */
memcpy(eBone, curBone, sizeof(EditBone));
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);
curBone->temp.ebone = eBone;
eBone->temp.ebone = curBone;
if (name != NULL) {
BLI_strncpy(eBone->name, name, sizeof(eBone->name));
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;
}
}
ED_armature_ebone_unique_name(editbones, eBone->name, NULL);
BLI_addtail(editbones, eBone);
/* 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);
}
static void updateDuplicateKinematicConstraintSettings(bConstraint *curcon)
{
/* 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);
}
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));
bRotLimitConstraint *limit = (bRotLimitConstraint *)curcon->data;
float local_mat[4][4], imat[4][4];
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 This does not support euler order, but doing so will make this way more complex.
* For now we have decided to not support all cornercases and advanced setups. (sebpa)
*/
/* 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);
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 +879,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 *duplicateEditBone(EditBone *curBone, const char *name, ListBase *editbones, Object *ob)
EditBone *duplicateEditBoneObjects(
EditBone *cur_bone, const char *name, ListBase *editbones, Object *src_ob, Object *dst_ob)
{
return duplicateEditBoneObjects(curBone, name, editbones, ob, 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 *cur_bone, const char *name, ListBase *editbones, Object *ob)
{
return duplicateEditBoneObjects(cur_bone, name, editbones, ob, ob);
}
static int armature_duplicate_selected_exec(bContext *C, wmOperator *op)
@ -538,8 +972,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 +1001,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 +1024,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 +1179,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) {
/* 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 syncronized
*/
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 +1239,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 +1254,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);
}
}

View File

@ -232,9 +232,6 @@ struct EditBone *duplicateEditBone(struct EditBone *curBone,
const char *name,
struct ListBase *editbones,
struct Object *ob);
void updateDuplicateSubtarget(struct EditBone *dupBone,
struct ListBase *editbones,
struct Object *ob);
/* duplicate method (cross objects) */
/* editbones is the target list */
@ -244,12 +241,6 @@ struct EditBone *duplicateEditBoneObjects(struct EditBone *curBone,
struct Object *src_ob,
struct Object *dst_ob);
/* editbones is the source list */
void updateDuplicateSubtargetObjects(struct EditBone *dupBone,
struct ListBase *editbones,
struct Object *src_ob,
struct Object *dst_ob);
EditBone *add_points_bone(struct Object *obedit, float head[3], float tail[3]);
void bone_free(struct bArmature *arm, struct EditBone *bone);