Animation: Armature symmetrize ebones refactor #106487

Manually merged
Sybren A. Stüvel merged 5 commits from cgtinker/blender:symmetrize-refactoring into main 2023-05-15 11:09:36 +02:00
1 changed files with 83 additions and 66 deletions

View File

@ -327,6 +327,7 @@ void postEditBoneDuplicate(struct ListBase *editbones, Object *ob)
if (!ebone_dst) { if (!ebone_dst) {
ebone_dst = ED_armature_ebone_get_mirrored(editbones, ebone_src); ebone_dst = ED_armature_ebone_get_mirrored(editbones, ebone_src);
} }
if (ebone_dst) { if (ebone_dst) {
BLI_ghash_insert(name_map, ebone_src->name, ebone_dst->name); BLI_ghash_insert(name_map, ebone_src->name, ebone_dst->name);
} }
@ -334,22 +335,28 @@ void postEditBoneDuplicate(struct ListBase *editbones, Object *ob)
LISTBASE_FOREACH (EditBone *, ebone_src, editbones) { LISTBASE_FOREACH (EditBone *, ebone_src, editbones) {
EditBone *ebone_dst = ebone_src->temp.ebone; EditBone *ebone_dst = ebone_src->temp.ebone;
if (ebone_dst) { if (!ebone_dst) {
bPoseChannel *pchan_src = BKE_pose_channel_find_name(ob->pose, ebone_src->name); continue;
if (pchan_src) { }
bPoseChannel *pchan_dst = BKE_pose_channel_find_name(ob->pose, ebone_dst->name);
if (pchan_dst) { bPoseChannel *pchan_src = BKE_pose_channel_find_name(ob->pose, ebone_src->name);
if (pchan_src->custom_tx) { if (!pchan_src) {
pchan_dst->custom_tx = pchan_duplicate_map(ob->pose, name_map, pchan_src->custom_tx); continue;
} }
if (pchan_src->bbone_prev) {
pchan_dst->bbone_prev = pchan_duplicate_map(ob->pose, name_map, pchan_src->bbone_prev); bPoseChannel *pchan_dst = BKE_pose_channel_find_name(ob->pose, ebone_dst->name);
} if (!pchan_dst) {
if (pchan_src->bbone_next) { continue;
pchan_dst->bbone_next = pchan_duplicate_map(ob->pose, name_map, pchan_src->bbone_next); }
}
} if (pchan_src->custom_tx) {
} pchan_dst->custom_tx = pchan_duplicate_map(ob->pose, name_map, pchan_src->custom_tx);
}
if (pchan_src->bbone_prev) {
pchan_dst->bbone_prev = pchan_duplicate_map(ob->pose, name_map, pchan_src->bbone_prev);
}
if (pchan_src->bbone_next) {
pchan_dst->bbone_next = pchan_duplicate_map(ob->pose, name_map, pchan_src->bbone_next);
} }
} }
@ -1120,71 +1127,80 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op)
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
scene, view_layer, CTX_wm_view3d(C), &objects_len); scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) { for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
bArmature *arm = obedit->data;
EditBone *ebone_iter; EditBone *ebone_iter;
/* The beginning of the duplicated mirrored bones in the edbo list */ /* The beginning of the duplicated mirrored bones in the edbo list */
EditBone *ebone_first_dupe = NULL; EditBone *ebone_first_dupe = NULL;
Object *obedit = objects[ob_index];
bArmature *arm = obedit->data;
ED_armature_edit_sync_selection(arm->edbo); /* XXX why is this needed? */ ED_armature_edit_sync_selection(arm->edbo); /* XXX why is this needed? */
preEditBoneDuplicate(arm->edbo); preEditBoneDuplicate(arm->edbo);
/* Select mirrored bones */ /* Deselect ebones depending on input axis and direction.
* A symmetrizable selection contains selected ebones of the input direction
* and unique selected bones with an unique flippable name.
*
* Storing temp ptrs to mirrored unselected ebones. */
for (ebone_iter = arm->edbo->first; ebone_iter; ebone_iter = ebone_iter->next) { for (ebone_iter = arm->edbo->first; ebone_iter; ebone_iter = ebone_iter->next) {
if (EBONE_VISIBLE(arm, ebone_iter) && (ebone_iter->flag & BONE_SELECTED)) { if (!(EBONE_VISIBLE(arm, ebone_iter) && (ebone_iter->flag & BONE_SELECTED))) {
char name_flip[MAXBONENAME]; /* Skipping invisible selected bones. */
continue;
}
BLI_string_flip_side_name(name_flip, ebone_iter->name, false, sizeof(name_flip)); char name_flip[MAXBONENAME];
if (ebone_iter == NULL) {
continue;
}
if (STREQ(name_flip, ebone_iter->name)) { BLI_string_flip_side_name(name_flip, ebone_iter->name, false, sizeof(name_flip));
/* if the name matches, we don't have the potential to be mirrored, just skip */
ebone_iter->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
}
else {
EditBone *ebone = ED_armature_ebone_find_name(arm->edbo, name_flip);
if (ebone) { if (STREQ(name_flip, ebone_iter->name)) {
if ((ebone->flag & BONE_SELECTED) == 0) { /* Skipping ebones without flippable as they don't have the potential to be mirrored. */
/* simple case, we're selected, the other bone isn't! */ ebone_iter->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
ebone_iter->temp.ebone = ebone; continue;
} }
else {
/* complicated - choose which direction to copy */
float axis_delta;
axis_delta = ebone->head[axis] - ebone_iter->head[axis]; EditBone *ebone = ED_armature_ebone_find_name(arm->edbo, name_flip);
if (axis_delta == 0.0f) {
axis_delta = ebone->tail[axis] - ebone_iter->tail[axis];
}
if (axis_delta == 0.0f) { if (!ebone) {
/* Both mirrored bones exist and point to each other and overlap exactly. /* The ebone_iter is unique and mirrorable. */
* continue;
* in this case there's no well defined solution, so de-select both and skip. }
*/
ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
ebone_iter->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
}
else {
EditBone *ebone_src, *ebone_dst;
if (((axis_delta < 0.0f) ? -1 : 1) == direction) {
ebone_src = ebone;
ebone_dst = ebone_iter;
}
else {
ebone_src = ebone_iter;
ebone_dst = ebone;
}
ebone_src->temp.ebone = ebone_dst; if (ebone->flag & BONE_SELECTED) {
ebone_dst->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); /* The mirrored ebone and the ebone_iter are selected.
} * Deselect based on the input direction and axis. */
} float axis_delta;
axis_delta = ebone->head[axis] - ebone_iter->head[axis];
if (axis_delta == 0.0f) {
/* The ebone heads are overlapping. */
axis_delta = ebone->tail[axis] - ebone_iter->tail[axis];
if (axis_delta == 0.0f) {
/* Both mirrored bones point to each other and overlap exactly.
* In this case there's no well defined solution, so de-select both and skip. */
ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
ebone_iter->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
continue;
} }
} }
/* Deselect depending on direction. */
if (((axis_delta < 0.0f) ? -1 : 1) == direction) {

When you're here in the code, ebone is guaranteed to be non-NULL.
And even if it were to be NULL, setting ebone_iter->temp.ebone = NULL in that case also wouldn't hurt.

When you're here in the code, `ebone` is guaranteed to be non-`NULL`. And even if it were to be `NULL`, setting `ebone_iter->temp.ebone = NULL` in that case also wouldn't hurt.

Right, I've overlooked that, thanks.

Right, I've overlooked that, thanks.
/* Don't store temp ptr if the iter_bone gets deselected.
* In this case, the ebone.temp should point to the ebone_iter. */
ebone_iter->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
continue;
}
ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
} }
/* Set temp pointer to mirrored ebones */
ebone_iter->temp.ebone = ebone;
} }
/* Find the selected bones and duplicate them as needed, with mirrored name. */ /* Find the selected bones and duplicate them as needed, with mirrored name. */
@ -1206,11 +1222,12 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op)
ebone_iter->temp.ebone->inherit_scale_mode = ebone_iter->inherit_scale_mode; ebone_iter->temp.ebone->inherit_scale_mode = ebone_iter->inherit_scale_mode;
continue; continue;
} }
char name_flip[MAXBONENAME]; char name_flip[MAXBONENAME];
BLI_string_flip_side_name(name_flip, ebone_iter->name, false, sizeof(name_flip)); BLI_string_flip_side_name(name_flip, ebone_iter->name, false, sizeof(name_flip));
/* bones must have a side-suffix */ /* mirrored bones must have a side-suffix */
if (!STREQ(name_flip, ebone_iter->name)) { if (!STREQ(name_flip, ebone_iter->name)) {

Comments nowadays should be a full sentence, so start with a capital and end with a period. Touching is fixing ;-)

Comments nowadays should be a full sentence, so start with a capital and end with a period. Touching is fixing ;-)
EditBone *ebone; EditBone *ebone;
@ -1254,8 +1271,8 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op)
*/ */
if (ebone->head[axis] != 0.0f) { if (ebone->head[axis] != 0.0f) {
/* The mirrored bone doesn't start on the mirror axis, so assume that this one should /* The mirrored bone doesn't start on the mirror axis, so assume that this one
* not be connected to the old parent */ * should not be connected to the old parent */
ebone->flag &= ~BONE_CONNECTED; ebone->flag &= ~BONE_CONNECTED;
} }
} }