From fbdf1af3555027696df5b687c7fe8d345e4c39e9 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 25 Nov 2020 11:20:12 +0100 Subject: Fix T82758: Convert Proxy to Override: Local constraints aren't saved. Ensure consistent order of pose bones. Now it should always match the one from bones in armature obdata (as exposed by e.g. RNA, i.e. children-first). Previously when some pose bones would need to be added or removed from a pose due to changes in the bone armature, or if bones in armature were re-ordered, the bones order in pose would not match anymore the one from armature, and could even become different between e.g. a proxy and its linked source. This was not really nice, but not a big issue before either. But with diffing process of override, consistent order of items between reference linked collection and local override one is crucial. Reviewed By: #animation_rigging, sybren Maniphest Tasks: T82758 Differential Revision: https://developer.blender.org/D9646 --- source/blender/blenkernel/intern/armature.c | 32 ++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) (limited to 'source/blender/blenkernel/intern/armature.c') diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index b5d3a04acb8..92146082557 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -2440,17 +2440,42 @@ static void pose_proxy_sync(Object *ob, Object *from, int layer_protected) } } -static int rebuild_pose_bone(bPose *pose, Bone *bone, bPoseChannel *parchan, int counter) +/** + * \param r_last_visited_bone_p the last bone handled by the last call to this function. + */ +static int rebuild_pose_bone( + bPose *pose, Bone *bone, bPoseChannel *parchan, int counter, Bone **r_last_visited_bone_p) { bPoseChannel *pchan = BKE_pose_channel_verify(pose, bone->name); /* verify checks and/or adds */ pchan->bone = bone; pchan->parent = parchan; + /* We ensure the current pchan is immediately after the one we just generated/updated in the + * previous call to `rebuild_pose_bone`. + * + * It may be either the parent, the previous sibling, or the last + * (grand-(grand-(...)))-child (as processed by the recursive, depth-first nature of this + * function) of the previous sibling. + * + * Note: In most cases there is nothing to do here, but pose list may get out of order when some + * bones are added, removed or moved in the armature data. */ + bPoseChannel *pchan_prev = pchan->prev; + const Bone *last_visited_bone = *r_last_visited_bone_p; + if ((pchan_prev == NULL && last_visited_bone != NULL) || + (pchan_prev != NULL && pchan_prev->bone != last_visited_bone)) { + pchan_prev = last_visited_bone != NULL ? + BKE_pose_channel_find_name(pose, last_visited_bone->name) : + NULL; + BLI_remlink(&pose->chanbase, pchan); + BLI_insertlinkafter(&pose->chanbase, pchan_prev, pchan); + } + + *r_last_visited_bone_p = pchan->bone; counter++; for (bone = bone->childbase.first; bone; bone = bone->next) { - counter = rebuild_pose_bone(pose, bone, pchan, counter); + counter = rebuild_pose_bone(pose, bone, pchan, counter, r_last_visited_bone_p); /* for quick detecting of next bone in chain, only b-bone uses it now */ if (bone->flag & BONE_CONNECTED) { pchan->child = BKE_pose_channel_find_name(pose, bone->name); @@ -2520,8 +2545,9 @@ void BKE_pose_rebuild(Main *bmain, Object *ob, bArmature *arm, const bool do_id_ BKE_pose_clear_pointers(pose); /* first step, check if all channels are there */ + Bone *prev_bone = NULL; for (bone = arm->bonebase.first; bone; bone = bone->next) { - counter = rebuild_pose_bone(pose, bone, NULL, counter); + counter = rebuild_pose_bone(pose, bone, NULL, counter, &prev_bone); } /* and a check for garbage */ -- cgit v1.2.3