diff options
author | Bastien Montagne <montagne29@wanadoo.fr> | 2017-06-09 16:58:32 +0300 |
---|---|---|
committer | Bastien Montagne <montagne29@wanadoo.fr> | 2017-06-09 17:30:59 +0300 |
commit | f097e73a2aa08234c6fbffc3af39620ab523dbd8 (patch) | |
tree | 440dca4c1411388b19bd8471b88304b55f363ff2 /source/blender/editors | |
parent | d7f33668d6e69f22e0b0ee28be0039df00f63ad3 (diff) |
Fix crash when making local object+obdata with linked armature.
Reported by Andy Goralczyk (@eyecandy) over IRC, thanks!
Simply nuke all that poor broken custom one-by-one handling in
object_relations.c code, and use highly complex but powerful and
well-tested BKE_library_make_local() in all cases of MakeLocal!
ID management, especially related to linking, is very hairy matters,
better to have as few as possible core functions managing all the dirty
details. ;)
Diffstat (limited to 'source/blender/editors')
-rw-r--r-- | source/blender/editors/object/object_relations.c | 185 |
1 files changed, 93 insertions, 92 deletions
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 7ec43b02b38..3284af2df69 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -2149,24 +2149,6 @@ void ED_object_single_users(Main *bmain, Scene *scene, const bool full, const bo /******************************* Make Local ***********************************/ -/* helper for below, ma was checked to be not NULL */ -static void make_local_makelocalmaterial(Material *ma) -{ - AnimData *adt; - int b; - - id_make_local(G.main, &ma->id, false, false); - - for (b = 0; b < MAX_MTEX; b++) - if (ma->mtex[b] && ma->mtex[b]->tex) - id_make_local(G.main, &ma->mtex[b]->tex->id, false, false); - - adt = BKE_animdata_from_id(&ma->id); - if (adt) BKE_animdata_make_local(adt); - - /* nodetree? XXX */ -} - enum { MAKE_LOCAL_SELECT_OB = 1, MAKE_LOCAL_SELECT_OBDATA = 2, @@ -2252,123 +2234,142 @@ static bool make_local_all__instance_indirect_unused(Main *bmain, Scene *scene) return changed; } -static int make_local_exec(bContext *C, wmOperator *op) +static void make_local_animdata_tag_strips(ListBase *strips) { - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - AnimData *adt; - ParticleSystem *psys; - Material *ma, ***matarar; - Lamp *la; - ID *id; - const int mode = RNA_enum_get(op->ptr, "type"); - int a, b; + NlaStrip *strip; - if (mode == MAKE_LOCAL_ALL) { - /* de-select so the user can differentiate newly instanced from existing objects */ - BKE_scene_base_deselect_all(scene); - - if (make_local_all__instance_indirect_unused(bmain, scene)) { - BKE_report(op->reports, RPT_INFO, - "Orphan library objects added to the current scene to avoid loss"); + for (strip = strips->first; strip; strip = strip->next) { + if (strip->act) { + strip->act->id.tag &= ~LIB_TAG_PRE_EXISTING; + } + if (strip->remap && strip->remap->target) { + strip->remap->target->id.tag &= ~LIB_TAG_PRE_EXISTING; } - BKE_library_make_local(bmain, NULL, NULL, false, false); /* NULL is all libs */ - WM_event_add_notifier(C, NC_WINDOW, NULL); - return OPERATOR_FINISHED; + make_local_animdata_tag_strips(&strip->strips); } +} - tag_localizable_objects(C, mode); - - CTX_DATA_BEGIN (C, Object *, ob, selected_objects) - { - if ((ob->id.tag & LIB_TAG_DOIT) == 0) { - continue; +/* Tag all actions used by given animdata to be made local. */ +static void make_local_animdata_tag(AnimData *adt) +{ + if (adt) { + /* Actions - Active and Temp */ + if (adt->action) { + adt->action->id.tag &= ~LIB_TAG_PRE_EXISTING; + } + if (adt->tmpact) { + adt->tmpact->id.tag &= ~LIB_TAG_PRE_EXISTING; + } + /* Remaps */ + if (adt->remap && adt->remap->target) { + adt->remap->target->id.tag &= ~LIB_TAG_PRE_EXISTING; } - if (ob->id.lib) - id_make_local(bmain, &ob->id, false, false); - } - CTX_DATA_END; + /* Drivers */ + /* TODO: need to handle the ID-targets too? */ - /* maybe object pointers */ - CTX_DATA_BEGIN (C, Object *, ob, selected_objects) - { - if (ob->id.lib == NULL) { - ID_NEW_REMAP(ob->parent); + /* NLA Data */ + for (NlaTrack *nlt = adt->nla_tracks.first; nlt; nlt = nlt->next) { + make_local_animdata_tag_strips(&nlt->strips); } } - CTX_DATA_END; - - CTX_DATA_BEGIN (C, Object *, ob, selected_objects) - { - if ((ob->id.tag & LIB_TAG_DOIT) == 0) { - continue; - } +} - id = ob->data; +static void make_local_material_tag(Material *ma) +{ + if (ma) { + ma->id.tag &= ~LIB_TAG_PRE_EXISTING; + make_local_animdata_tag(BKE_animdata_from_id(&ma->id)); - if (id && (ELEM(mode, MAKE_LOCAL_SELECT_OBDATA, MAKE_LOCAL_SELECT_OBDATA_MATERIAL))) { - id_make_local(bmain, id, false, false); - adt = BKE_animdata_from_id(id); - if (adt) BKE_animdata_make_local(adt); + /* About nodetrees: root one is made local together with material, others we keep linked for now... */ - /* tag indirect data direct */ - matarar = give_matarar(ob); - if (matarar) { - for (a = 0; a < ob->totcol; a++) { - ma = (*matarar)[a]; - if (ma) - id_lib_extern(&ma->id); - } + for (int a = 0; a < MAX_MTEX; a++) { + if (ma->mtex[a] && ma->mtex[a]->tex) { + ma->mtex[a]->tex->id.tag &= ~LIB_TAG_PRE_EXISTING; } } + } +} - for (psys = ob->particlesystem.first; psys; psys = psys->next) - id_make_local(bmain, &psys->part->id, false, false); +static int make_local_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + ParticleSystem *psys; + Material *ma, ***matarar; + Lamp *la; + const int mode = RNA_enum_get(op->ptr, "type"); + int a; + + /* Note: we (ab)use LIB_TAG_PRE_EXISTING to cherry pick which ID to make local... */ + if (mode == MAKE_LOCAL_ALL) { + BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); + + /* de-select so the user can differentiate newly instanced from existing objects */ + BKE_scene_base_deselect_all(scene); - adt = BKE_animdata_from_id(&ob->id); - if (adt) BKE_animdata_make_local(adt); + if (make_local_all__instance_indirect_unused(bmain, scene)) { + BKE_report(op->reports, RPT_INFO, "Orphan library objects added to the current scene to avoid loss"); + } } - CTX_DATA_END; + else { + BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true); + tag_localizable_objects(C, mode); - if (mode == MAKE_LOCAL_SELECT_OBDATA_MATERIAL) { CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { if ((ob->id.tag & LIB_TAG_DOIT) == 0) { continue; } - if (ob->type == OB_LAMP) { - la = ob->data; - - for (b = 0; b < MAX_MTEX; b++) - if (la->mtex[b] && la->mtex[b]->tex) - id_make_local(bmain, &la->mtex[b]->tex->id, false, false); + ob->id.tag &= ~LIB_TAG_PRE_EXISTING; + make_local_animdata_tag(BKE_animdata_from_id(&ob->id)); + for (psys = ob->particlesystem.first; psys; psys = psys->next) { + psys->part->id.tag &= ~LIB_TAG_PRE_EXISTING; } - else { + + if (mode == MAKE_LOCAL_SELECT_OBDATA_MATERIAL) { for (a = 0; a < ob->totcol; a++) { ma = ob->mat[a]; - if (ma) - make_local_makelocalmaterial(ma); + if (ma) { + make_local_material_tag(ma); + } } matarar = (Material ***)give_matarar(ob); if (matarar) { for (a = 0; a < ob->totcol; a++) { ma = (*matarar)[a]; - if (ma) - make_local_makelocalmaterial(ma); + if (ma) { + make_local_material_tag(ma); + } + } + } + + if (ob->type == OB_LAMP) { + BLI_assert(ob->data != NULL); + la = ob->data; + for (a = 0; a < MAX_MTEX; a++) { + if (la->mtex[a] && la->mtex[a]->tex) { + la->id.tag &= ~LIB_TAG_PRE_EXISTING; + } } } } + + if (ELEM(mode, MAKE_LOCAL_SELECT_OBDATA, MAKE_LOCAL_SELECT_OBDATA_MATERIAL) && ob->data != NULL) { + ID *ob_data = ob->data; + ob_data->tag &= ~LIB_TAG_PRE_EXISTING; + make_local_animdata_tag(BKE_animdata_from_id(ob_data)); + } } CTX_DATA_END; } - BKE_main_id_clear_newpoins(bmain); - WM_event_add_notifier(C, NC_WINDOW, NULL); + BKE_library_make_local(bmain, NULL, NULL, true, false); /* NULL is all libs */ + WM_event_add_notifier(C, NC_WINDOW, NULL); return OPERATOR_FINISHED; } |