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 101 additions and 71 deletions
Showing only changes of commit 186e50441f - Show all commits

View File

@ -327,6 +327,7 @@ void postEditBoneDuplicate(struct ListBase *editbones, Object *ob)
if (!ebone_dst) {
ebone_dst = ED_armature_ebone_get_mirrored(editbones, ebone_src);
}
if (ebone_dst) {
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) {
EditBone *ebone_dst = ebone_src->temp.ebone;
if (ebone_dst) {
bPoseChannel *pchan_src = BKE_pose_channel_find_name(ob->pose, ebone_src->name);
if (pchan_src) {
bPoseChannel *pchan_dst = BKE_pose_channel_find_name(ob->pose, ebone_dst->name);
if (pchan_dst) {
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);
}
}
}
if (!ebone_dst) {
continue;
}
bPoseChannel *pchan_src = BKE_pose_channel_find_name(ob->pose, ebone_src->name);
if (!pchan_src) {
continue;
}
bPoseChannel *pchan_dst = BKE_pose_channel_find_name(ob->pose, ebone_dst->name);
if (!pchan_dst) {
continue;
}
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);
}
}
@ -1089,6 +1096,71 @@ static EditBone *get_symmetrized_bone(bArmature *arm, EditBone *bone)
return (mirror != NULL) ? mirror : bone;
}
static void ED_armature_set_symmetrizable_ebone_selection(bArmature *arm, int axis, int direction)

No need to have an ED_armature prefix, those are only for functions that are declared in the header files (and thus part of Blender's internal API).

Also axis and direction can be declared as const int.

No need to have an `ED_armature` prefix, those are only for functions that are declared in the header files (and thus part of Blender's internal API). Also `axis` and `direction` can be declared as `const int`.
{
/* Deselects 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.*/
EditBone *ebone_iter;
for (ebone_iter = arm->edbo->first; ebone_iter; ebone_iter = ebone_iter->next) {
if (!(EBONE_VISIBLE(arm, ebone_iter) && (ebone_iter->flag & BONE_SELECTED))) {
/* ignore selected bones which are invisble */
continue;
}
char name_flip[MAXBONENAME];
if (ebone_iter == NULL) {
continue;
}
BLI_string_flip_side_name(name_flip, ebone_iter->name, false, sizeof(name_flip));
if (STREQ(name_flip, ebone_iter->name)) {
/* if the name matches, we don't have the potential to be mirrored, just skip */
ebone_iter->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
continue;
}
EditBone *ebone = ED_armature_ebone_find_name(arm->edbo, name_flip);
if (!ebone) {
continue;
}
if ((ebone->flag & BONE_SELECTED) == 0) {
/* ebone is only once selected per side */
continue;
}
/* complicated - choose which direction to copy */
float axis_delta;
axis_delta = ebone->head[axis] - ebone_iter->head[axis];
if (axis_delta == 0.0f) {
axis_delta = ebone->tail[axis] - ebone_iter->tail[axis];
if (axis_delta == 0.0f) {
/* Both mirrored bones exist and 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) {
ebone_iter->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
}
else {
ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
}
}
}
/**
* near duplicate of #armature_duplicate_selected_exec,
* except for parenting part (keep in sync)
@ -1109,69 +1181,26 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op)
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
bArmature *arm = obedit->data;
EditBone *ebone_iter;
/* The beginning of the duplicated mirrored bones in the edbo list */
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? */

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.
preEditBoneDuplicate(arm->edbo);
/* Select mirrored bones */
ED_armature_set_symmetrizable_ebone_selection(arm, axis, direction);
for (ebone_iter = arm->edbo->first; ebone_iter; ebone_iter = ebone_iter->next) {
if (EBONE_VISIBLE(arm, ebone_iter) && (ebone_iter->flag & BONE_SELECTED)) {
char name_flip[MAXBONENAME];
BLI_string_flip_side_name(name_flip, ebone_iter->name, false, sizeof(name_flip));
if (STREQ(name_flip, ebone_iter->name)) {
/* 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 ((ebone->flag & BONE_SELECTED) == 0) {
/* simple case, we're selected, the other bone isn't! */
ebone_iter->temp.ebone = ebone;
}
else {
/* complicated - choose which direction to copy */
float axis_delta;
axis_delta = ebone->head[axis] - ebone_iter->head[axis];
if (axis_delta == 0.0f) {
axis_delta = ebone->tail[axis] - ebone_iter->tail[axis];
}
if (axis_delta == 0.0f) {
/* Both mirrored bones exist and 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);
}
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;
ebone_dst->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
}
}
}
EditBone *ebone;
/* Set temp pointer to mirrored ebones */
ebone = ED_armature_ebone_get_mirrored(arm->edbo, ebone_iter);
if (ebone) {
ebone_iter->temp.ebone = ebone;
}
}
}
@ -1194,11 +1223,12 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op)
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));
/* bones must have a side-suffix */
/* mirrored bones must have a side-suffix */

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 ;-)
if (!STREQ(name_flip, ebone_iter->name)) {
EditBone *ebone;
@ -1241,8 +1271,8 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op)
*/
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 */
/* 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;
}
}