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
This commit is contained in:
Bastien Montagne 2020-11-25 11:20:12 +01:00
parent e4e7dfc1d8
commit fbdf1af355
Notes: blender-bot 2023-02-14 10:09:24 +01:00
Referenced by issue #82758, Convert Proxy to Override: Local constraints aren't saved
Referenced by issue #82718, Tablet no longer working correctly in Sculpting - Blender 2.92
Referenced by issue #77314, Glare node bug when using NLM denoising
2 changed files with 38 additions and 4 deletions

View File

@ -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 */

View File

@ -49,6 +49,7 @@
#include "DNA_workspace_types.h"
#include "BKE_animsys.h"
#include "BKE_armature.h"
#include "BKE_collection.h"
#include "BKE_colortools.h"
#include "BKE_fcurve.h"
@ -472,8 +473,15 @@ void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports))
* \note Keep this message at the bottom of the function.
*/
{
/* Keep this block, even when empty. */
/* Systematically rebuild posebones to ensure consistent ordering matching the one of bones in
* Armature obdata. */
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
if (ob->type == OB_ARMATURE) {
BKE_pose_rebuild(bmain, ob, ob->data, true);
}
}
}
}