From 3df93d063e2d52a2c2e73cfe31801c15682bf0b4 Mon Sep 17 00:00:00 2001 From: Joshua Leung Date: Thu, 22 Jan 2015 02:08:29 +1300 Subject: [PATCH] Fix: Joining armatures loses drivers Currently, when joining two armatures, the drivers of the armatures being merged in are lost. This commit introduces a new AnimData API function for merging animation data into another AnimData block. NOTE: * For now, this only copies the drivers over. As a result, manual effort will still be needed to go through and fix the drivers. I am working on automating that process, but it's more important that the drivers don't have to be created from scratch for now (since this is needed for the Goosberry rigging work). --- source/blender/blenkernel/BKE_animsys.h | 14 ++++ source/blender/blenkernel/intern/anim_sys.c | 71 +++++++++++++++++++ .../editors/armature/armature_naming.c | 1 + .../editors/armature/armature_relations.c | 28 ++++++++ 4 files changed, 114 insertions(+) diff --git a/source/blender/blenkernel/BKE_animsys.h b/source/blender/blenkernel/BKE_animsys.h index a5109acfa56..24abd114a90 100644 --- a/source/blender/blenkernel/BKE_animsys.h +++ b/source/blender/blenkernel/BKE_animsys.h @@ -73,6 +73,20 @@ bool BKE_copy_animdata_id(struct ID *id_to, struct ID *id_from, const bool do_ac /* Copy AnimData Actions */ void BKE_copy_animdata_id_action(struct ID *id); +/* Merge copies of data from source AnimData block */ +typedef enum eAnimData_MergeCopy_Modes { + /* Keep destination action */ + ADT_MERGECOPY_KEEP_DST = 0, + + /* Use src action (make a new copy) */ + ADT_MERGECOPY_SRC_COPY = 1, + + /* Use src action (but just reference the existing version) */ + ADT_MERGECOPY_SRC_REF = 2 +} eAnimData_MergeCopy_Modes; + +void BKE_animdata_merge_copy(struct ID *dst_id, struct ID *src_id, eAnimData_MergeCopy_Modes action_mode, bool fix_drivers); + /* Make Local */ void BKE_animdata_make_local(struct AnimData *adt); diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index 8e36449f214..00e5c1be48e 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -318,6 +318,77 @@ void BKE_copy_animdata_id_action(ID *id) } } +/* Merge copies of the data from the src AnimData into the destination AnimData */ +void BKE_animdata_merge_copy(ID *dst_id, ID *src_id, eAnimData_MergeCopy_Modes action_mode, bool fix_drivers) +{ + AnimData *src = BKE_animdata_from_id(src_id); + AnimData *dst = BKE_animdata_from_id(dst_id); + + /* sanity checks */ + if (ELEM(NULL, dst, src)) + return; + + // TODO: we must unset all "tweakmode" flags + if ((src->flag & ADT_NLA_EDIT_ON) || (dst->flag & ADT_NLA_EDIT_ON)) { + printf("ERROR: Merging AnimData blocks while editing NLA is dangerous as it may cause data corruption\n"); + return; + } + + /* handle actions... */ + if (action_mode == ADT_MERGECOPY_SRC_COPY) { + /* make a copy of the actions */ + dst->action = BKE_action_copy(src->action); + dst->tmpact = BKE_action_copy(src->tmpact); + } + else if (action_mode == ADT_MERGECOPY_SRC_REF) { + /* make a reference to it */ + dst->action = src->action; + id_us_plus((ID *)dst->action); + + dst->tmpact = src->tmpact; + id_us_plus((ID *)dst->tmpact); + } + + /* duplicate NLA data */ + if (src->nla_tracks.first) { + ListBase tracks = {NULL, NULL}; + + copy_nladata(&tracks, &src->nla_tracks); + BLI_movelisttolist(&dst->nla_tracks, &tracks); + } + + /* duplicate drivers (F-Curves) */ + if (src->drivers.first) { + ListBase drivers = {NULL, NULL}; + + copy_fcurves(&drivers, &src->drivers); + + /* Fix up all driver targets using the old target id + * - This assumes that the src ID is being merged into the dst ID + */ + if (fix_drivers) { + FCurve *fcu; + + for (fcu = drivers.first; fcu; fcu = fcu->next) { + ChannelDriver *driver = fcu->driver; + DriverVar *dvar; + + for (dvar = driver->variables.first; dvar; dvar = dvar->next) { + DRIVER_TARGETS_USED_LOOPER(dvar) + { + if (dtar->id == src_id) { + dtar->id = dst_id; + } + } + DRIVER_TARGETS_LOOPER_END + } + } + } + + BLI_movelisttolist(&dst->drivers, &drivers); + } +} + /* Make Local -------------------------------------------- */ static void make_local_strips(ListBase *strips) diff --git a/source/blender/editors/armature/armature_naming.c b/source/blender/editors/armature/armature_naming.c index 9afc45955bd..f984bf071fa 100644 --- a/source/blender/editors/armature/armature_naming.c +++ b/source/blender/editors/armature/armature_naming.c @@ -267,6 +267,7 @@ void ED_armature_bone_rename(bArmature *arm, const char *oldnamep, const char *n /* Fix all animdata that may refer to this bone - we can't just do the ones attached to objects, since * other ID-blocks may have drivers referring to this bone [#29822] */ + // XXX: the ID here is for armatures, but most bone drivers are actually on the object instead... { BKE_all_animdata_fix_paths_rename(&arm->id, "pose.bones", oldname, newname); diff --git a/source/blender/editors/armature/armature_relations.c b/source/blender/editors/armature/armature_relations.c index 3e226c39c8c..2a19d7a0bec 100644 --- a/source/blender/editors/armature/armature_relations.c +++ b/source/blender/editors/armature/armature_relations.c @@ -40,6 +40,7 @@ #include "BLF_translation.h" #include "BKE_action.h" +#include "BKE_animsys.h" #include "BKE_constraint.h" #include "BKE_context.h" #include "BKE_depsgraph.h" @@ -200,6 +201,33 @@ int join_armature_exec(bContext *C, wmOperator *op) if ((base->object->type == OB_ARMATURE) && (base->object != ob)) { bArmature *curarm = base->object->data; + /* we assume that each armature datablock is only used in a single place */ + BLI_assert(ob->data != base->object->data); + + /* copy over animdata first, so that the link fixing can access and fix the links */ + if (base->object->adt) { + if (ob->adt == NULL) { + /* no animdata, so just use a copy of the whole thing */ + ob->adt = BKE_copy_animdata(base->object->adt, false); + } + else { + /* merge in data - we'll fix the drivers manually */ + BKE_animdata_merge_copy(&ob->id, &base->object->id, ADT_MERGECOPY_KEEP_DST, false); + } + } + + if (curarm->adt) { + if (arm->adt == NULL) { + /* no animdata, so just use a copy of the whole thing */ + arm->adt = BKE_copy_animdata(curarm->adt, false); + } + else { + /* merge in data - we'll fix the drivers manually */ + BKE_animdata_merge_copy(&arm->id, &curarm->id, ADT_MERGECOPY_KEEP_DST, false); + } + } + + /* Make a list of editbones in current armature */ ED_armature_to_edit(base->object->data);