Changeset View
Changeset View
Standalone View
Standalone View
source/blender/blenkernel/intern/library_remap.c
| Show First 20 Lines • Show All 71 Lines • ▼ Show 20 Lines | |||||
| #include "BKE_armature.h" | #include "BKE_armature.h" | ||||
| #include "BKE_brush.h" | #include "BKE_brush.h" | ||||
| #include "BKE_camera.h" | #include "BKE_camera.h" | ||||
| #include "BKE_cachefile.h" | #include "BKE_cachefile.h" | ||||
| #include "BKE_collection.h" | #include "BKE_collection.h" | ||||
| #include "BKE_curve.h" | #include "BKE_curve.h" | ||||
| #include "BKE_fcurve.h" | #include "BKE_fcurve.h" | ||||
| #include "BKE_font.h" | #include "BKE_font.h" | ||||
| #include "BKE_group.h" | |||||
| #include "BKE_gpencil.h" | #include "BKE_gpencil.h" | ||||
| #include "BKE_idprop.h" | #include "BKE_idprop.h" | ||||
| #include "BKE_image.h" | #include "BKE_image.h" | ||||
| #include "BKE_ipo.h" | #include "BKE_ipo.h" | ||||
| #include "BKE_key.h" | #include "BKE_key.h" | ||||
| #include "BKE_lamp.h" | #include "BKE_lamp.h" | ||||
| #include "BKE_lattice.h" | #include "BKE_lattice.h" | ||||
| #include "BKE_layer.h" | #include "BKE_layer.h" | ||||
| ▲ Show 20 Lines • Show All 177 Lines • ▼ Show 20 Lines | |||||
| static void libblock_remap_data_preprocess_scene_object_unlink( | static void libblock_remap_data_preprocess_scene_object_unlink( | ||||
| IDRemap *r_id_remap_data, Scene *sce, Object *ob, const bool skip_indirect, const bool is_indirect) | IDRemap *r_id_remap_data, Scene *sce, Object *ob, const bool skip_indirect, const bool is_indirect) | ||||
| { | { | ||||
| if (skip_indirect && is_indirect) { | if (skip_indirect && is_indirect) { | ||||
| r_id_remap_data->skipped_indirect++; | r_id_remap_data->skipped_indirect++; | ||||
| r_id_remap_data->skipped_refcounted++; | r_id_remap_data->skipped_refcounted++; | ||||
| } | } | ||||
| else { | else { | ||||
| BKE_collections_object_remove(r_id_remap_data->bmain, &sce->id, ob, false); | /* Remove object from all collections in the scene. free_use is false | ||||
| * to avoid recursively calling object free again. */ | |||||
| BKE_scene_collections_object_remove(r_id_remap_data->bmain, sce, ob, false); | |||||
| if (!is_indirect) { | if (!is_indirect) { | ||||
| r_id_remap_data->status |= ID_REMAP_IS_LINKED_DIRECT; | r_id_remap_data->status |= ID_REMAP_IS_LINKED_DIRECT; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| static void libblock_remap_data_preprocess_group_unlink( | static void libblock_remap_data_preprocess_collection_unlink( | ||||
| IDRemap *r_id_remap_data, Object *ob, const bool skip_indirect, const bool is_indirect) | IDRemap *r_id_remap_data, Object *ob, const bool skip_indirect, const bool is_indirect) | ||||
| { | { | ||||
| Main *bmain = r_id_remap_data->bmain; | Main *bmain = r_id_remap_data->bmain; | ||||
| for (Group *group = bmain->group.first; group; group = group->id.next) { | for (Collection *collection = bmain->collection.first; collection; collection = collection->id.next) { | ||||
| if (BKE_group_object_exists(group, ob)) { | if (!BKE_collection_is_in_scene(collection) && BKE_collection_has_object(collection, ob)) { | ||||
| if (skip_indirect && is_indirect) { | if (skip_indirect && is_indirect) { | ||||
| r_id_remap_data->skipped_indirect++; | r_id_remap_data->skipped_indirect++; | ||||
| r_id_remap_data->skipped_refcounted++; | r_id_remap_data->skipped_refcounted++; | ||||
| } | } | ||||
| else { | else { | ||||
| BKE_collections_object_remove(bmain, &group->id, ob, false); | BKE_collection_object_remove(bmain, collection, ob, false); | ||||
| if (!is_indirect) { | if (!is_indirect) { | ||||
| r_id_remap_data->status |= ID_REMAP_IS_LINKED_DIRECT; | r_id_remap_data->status |= ID_REMAP_IS_LINKED_DIRECT; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| static void libblock_remap_data_preprocess(IDRemap *r_id_remap_data) | static void libblock_remap_data_preprocess(IDRemap *r_id_remap_data) | ||||
| { | { | ||||
| switch (GS(r_id_remap_data->id->name)) { | switch (GS(r_id_remap_data->id->name)) { | ||||
| case ID_SCE: | case ID_SCE: | ||||
| { | { | ||||
| Scene *sce = (Scene *)r_id_remap_data->id; | Scene *sce = (Scene *)r_id_remap_data->id; | ||||
| if (!r_id_remap_data->new_id) { | if (!r_id_remap_data->new_id) { | ||||
| const bool is_indirect = (sce->id.lib != NULL); | const bool is_indirect = (sce->id.lib != NULL); | ||||
| const bool skip_indirect = (r_id_remap_data->flag & ID_REMAP_SKIP_INDIRECT_USAGE) != 0; | const bool skip_indirect = (r_id_remap_data->flag & ID_REMAP_SKIP_INDIRECT_USAGE) != 0; | ||||
| /* In case we are unlinking... */ | /* In case we are unlinking... */ | ||||
| if (!r_id_remap_data->old_id) { | if (!r_id_remap_data->old_id) { | ||||
| /* TODO: how is it valid to iterator over a scene while | |||||
| * removing objects from it? can't this crash? */ | |||||
| /* ... everything from scene. */ | /* ... everything from scene. */ | ||||
| FOREACH_SCENE_OBJECT_BEGIN(sce, ob_iter) | FOREACH_SCENE_OBJECT_BEGIN(sce, ob_iter) | ||||
| { | { | ||||
| libblock_remap_data_preprocess_scene_object_unlink( | libblock_remap_data_preprocess_scene_object_unlink( | ||||
| r_id_remap_data, sce, ob_iter, skip_indirect, is_indirect); | r_id_remap_data, sce, ob_iter, skip_indirect, is_indirect); | ||||
| libblock_remap_data_preprocess_group_unlink( | libblock_remap_data_preprocess_collection_unlink( | ||||
| r_id_remap_data, ob_iter, skip_indirect, is_indirect); | r_id_remap_data, ob_iter, skip_indirect, is_indirect); | ||||
| } | } | ||||
| FOREACH_SCENE_OBJECT_END; | FOREACH_SCENE_OBJECT_END; | ||||
| } | } | ||||
| else if (GS(r_id_remap_data->old_id->name) == ID_OB) { | else if (GS(r_id_remap_data->old_id->name) == ID_OB) { | ||||
| /* ... a specific object from scene. */ | /* ... a specific object from scene. */ | ||||
| Object *old_ob = (Object *)r_id_remap_data->old_id; | Object *old_ob = (Object *)r_id_remap_data->old_id; | ||||
| libblock_remap_data_preprocess_scene_object_unlink( | libblock_remap_data_preprocess_scene_object_unlink( | ||||
| r_id_remap_data, sce, old_ob, skip_indirect, is_indirect); | r_id_remap_data, sce, old_ob, skip_indirect, is_indirect); | ||||
| libblock_remap_data_preprocess_group_unlink( | libblock_remap_data_preprocess_collection_unlink( | ||||
| r_id_remap_data, old_ob, skip_indirect, is_indirect); | r_id_remap_data, old_ob, skip_indirect, is_indirect); | ||||
| } | } | ||||
| } | } | ||||
| break; | break; | ||||
| } | } | ||||
| case ID_OB: | case ID_OB: | ||||
| { | { | ||||
| Show All 17 Lines | switch (GS(r_id_remap_data->id->name)) { | ||||
| } | } | ||||
| default: | default: | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| static void libblock_remap_data_postprocess_object_update(Main *bmain, Object *old_ob, Object *new_ob) | static void libblock_remap_data_postprocess_object_update(Main *bmain, Object *old_ob, Object *new_ob) | ||||
| { | { | ||||
| if (old_ob->flag & OB_FROMGROUP) { | if (new_ob == NULL) { | ||||
| /* Note that for Scene's BaseObject->flag, either we: | /* In case we unlinked old_ob (new_ob is NULL), the object has already | ||||
| * - unlinked old_ob (i.e. new_ob is NULL), in which case scenes' bases have been removed already. | * been removed from the scenes and their collections. We still have | ||||
| * - remapped old_ob by new_ob, in which case scenes' bases are still valid as is. | * to remove the NULL children from collections not used in any scene. */ | ||||
| * So in any case, no need to update them here. */ | BKE_collections_object_remove_nulls(bmain); | ||||
| if (BKE_group_object_find(NULL, old_ob) == NULL) { | |||||
| old_ob->flag &= ~OB_FROMGROUP; | |||||
| } | |||||
| if (new_ob == NULL) { /* We need to remove NULL-ified groupobjects... */ | |||||
| for (Group *group = bmain->group.first; group; group = group->id.next) { | |||||
| BKE_group_object_unlink(group, NULL); | |||||
| } | |||||
| } | } | ||||
| else { | else { | ||||
| new_ob->flag |= OB_FROMGROUP; | BKE_main_collection_sync_remap(bmain); | ||||
| } | |||||
| } | } | ||||
| if (old_ob->type == OB_MBALL) { | if (old_ob->type == OB_MBALL) { | ||||
| for (Object *ob = bmain->object.first; ob; ob = ob->id.next) { | for (Object *ob = bmain->object.first; ob; ob = ob->id.next) { | ||||
| if (ob->type == OB_MBALL && BKE_mball_is_basis_for(ob, old_ob)) { | if (ob->type == OB_MBALL && BKE_mball_is_basis_for(ob, old_ob)) { | ||||
| DEG_id_tag_update(&ob->id, OB_RECALC_DATA); | DEG_id_tag_update(&ob->id, OB_RECALC_DATA); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| static void libblock_remap_data_postprocess_group_scene_unlink(Main *UNUSED(bmain), Scene *sce, ID *old_id) | static void libblock_remap_data_postprocess_collection_update(Main *bmain, Collection *old_collection, Collection *new_collection) | ||||
| { | |||||
| /* Note that here we assume no object has no base (i.e. all objects are assumed instanced | |||||
| * in one scene...). */ | |||||
| FOREACH_SCENE_OBJECT_BEGIN(sce, ob) | |||||
| { | { | ||||
| if (ob->flag & OB_FROMGROUP) { | if (new_collection == NULL) { | ||||
| Group *grp = BKE_group_object_find(NULL, ob); | /* In case we unlinked old_collection (new_collection is NULL), we need | ||||
| * to remove any collection children that have been set to NULL in the | |||||
| /* Unlinked group (old_id) is still in bmain... */ | * because of pointer replacement. */ | ||||
| if (grp && (&grp->id == old_id || grp->id.us == 0)) { | BKE_collections_child_remove_nulls(bmain, old_collection); | ||||
| grp = BKE_group_object_find(grp, ob); | |||||
| } | |||||
| if (!grp) { | |||||
| ob->flag &= ~OB_FROMGROUP; | |||||
| } | |||||
| } | } | ||||
| else { | |||||
| BKE_main_collection_sync_remap(bmain); | |||||
| } | } | ||||
| FOREACH_SCENE_OBJECT_END; | |||||
| } | } | ||||
| static void libblock_remap_data_postprocess_obdata_relink(Main *UNUSED(bmain), Object *ob, ID *new_id) | static void libblock_remap_data_postprocess_obdata_relink(Main *UNUSED(bmain), Object *ob, ID *new_id) | ||||
| { | { | ||||
| if (ob->data == new_id) { | if (ob->data == new_id) { | ||||
| switch (GS(new_id->name)) { | switch (GS(new_id->name)) { | ||||
| case ID_ME: | case ID_ME: | ||||
| multires_force_update(ob); | multires_force_update(ob); | ||||
| break; | break; | ||||
| case ID_CU: | case ID_CU: | ||||
| ▲ Show 20 Lines • Show All 165 Lines • ▼ Show 20 Lines | void BKE_libblock_remap_locked( | ||||
| /* Some after-process updates. | /* Some after-process updates. | ||||
| * This is a bit ugly, but cannot see a way to avoid it. Maybe we should do a per-ID callback for this instead? | * This is a bit ugly, but cannot see a way to avoid it. Maybe we should do a per-ID callback for this instead? | ||||
| */ | */ | ||||
| switch (GS(old_id->name)) { | switch (GS(old_id->name)) { | ||||
| case ID_OB: | case ID_OB: | ||||
| libblock_remap_data_postprocess_object_update(bmain, (Object *)old_id, (Object *)new_id); | libblock_remap_data_postprocess_object_update(bmain, (Object *)old_id, (Object *)new_id); | ||||
| break; | break; | ||||
| case ID_GR: | case ID_GR: | ||||
| if (!new_id) { /* Only affects us in case group was unlinked. */ | libblock_remap_data_postprocess_collection_update(bmain, (Collection *)old_id, (Collection *)new_id); | ||||
| for (Scene *sce = bmain->scene.first; sce; sce = sce->id.next) { | |||||
| libblock_remap_data_postprocess_group_scene_unlink(bmain, sce, old_id); | |||||
| } | |||||
| } | |||||
| break; | break; | ||||
| case ID_ME: | case ID_ME: | ||||
| case ID_CU: | case ID_CU: | ||||
| case ID_MB: | case ID_MB: | ||||
| if (new_id) { /* Only affects us in case obdata was relinked (changed). */ | if (new_id) { /* Only affects us in case obdata was relinked (changed). */ | ||||
| for (Object *ob = bmain->object.first; ob; ob = ob->id.next) { | for (Object *ob = bmain->object.first; ob; ob = ob->id.next) { | ||||
| libblock_remap_data_postprocess_obdata_relink(bmain, ob, new_id); | libblock_remap_data_postprocess_obdata_relink(bmain, ob, new_id); | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 80 Lines • ▼ Show 20 Lines | void BKE_libblock_relink_ex( | ||||
| libblock_remap_data(bmain, id, old_id, new_id, remap_flags, NULL); | libblock_remap_data(bmain, id, old_id, new_id, remap_flags, NULL); | ||||
| /* Some after-process updates. | /* Some after-process updates. | ||||
| * This is a bit ugly, but cannot see a way to avoid it. Maybe we should do a per-ID callback for this instead? | * This is a bit ugly, but cannot see a way to avoid it. Maybe we should do a per-ID callback for this instead? | ||||
| */ | */ | ||||
| switch (GS(id->name)) { | switch (GS(id->name)) { | ||||
| case ID_SCE: | case ID_SCE: | ||||
| { | { | ||||
| Scene *sce = (Scene *)id; | |||||
| if (old_id) { | if (old_id) { | ||||
| switch (GS(old_id->name)) { | switch (GS(old_id->name)) { | ||||
| case ID_OB: | case ID_OB: | ||||
| { | { | ||||
| libblock_remap_data_postprocess_object_update(bmain, (Object *)old_id, (Object *)new_id); | libblock_remap_data_postprocess_object_update(bmain, (Object *)old_id, (Object *)new_id); | ||||
| break; | break; | ||||
| } | } | ||||
| case ID_GR: | case ID_GR: | ||||
| if (!new_id) { /* Only affects us in case group was unlinked. */ | libblock_remap_data_postprocess_collection_update(bmain, (Collection *)old_id, (Collection *)new_id); | ||||
| libblock_remap_data_postprocess_group_scene_unlink(bmain, sce, old_id); | |||||
| } | |||||
| break; | break; | ||||
| default: | default: | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| /* No choice but to check whole objects/groups. */ | /* No choice but to check whole objects/collections. */ | ||||
| for (Object *ob = bmain->object.first; ob; ob = ob->id.next) { | for (Object *ob = bmain->object.first; ob; ob = ob->id.next) { | ||||
| libblock_remap_data_postprocess_object_update(bmain, ob, NULL); | libblock_remap_data_postprocess_object_update(bmain, ob, NULL); | ||||
| } | } | ||||
| for (Group *grp = bmain->group.first; grp; grp = grp->id.next) { | for (Collection *collection = bmain->collection.first; collection; collection = collection->id.next) { | ||||
| libblock_remap_data_postprocess_group_scene_unlink(bmain, sce, NULL); | libblock_remap_data_postprocess_collection_update(bmain, collection, NULL); | ||||
| } | } | ||||
| } | } | ||||
| break; | break; | ||||
| } | } | ||||
| case ID_OB: | case ID_OB: | ||||
| if (new_id) { /* Only affects us in case obdata was relinked (changed). */ | if (new_id) { /* Only affects us in case obdata was relinked (changed). */ | ||||
| libblock_remap_data_postprocess_obdata_relink(bmain, (Object *)id, new_id); | libblock_remap_data_postprocess_obdata_relink(bmain, (Object *)id, new_id); | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 113 Lines • ▼ Show 20 Lines | case ID_SPK: | ||||
| break; | break; | ||||
| case ID_LP: | case ID_LP: | ||||
| BKE_lightprobe_free((LightProbe *)id); | BKE_lightprobe_free((LightProbe *)id); | ||||
| break; | break; | ||||
| case ID_SO: | case ID_SO: | ||||
| BKE_sound_free((bSound *)id); | BKE_sound_free((bSound *)id); | ||||
| break; | break; | ||||
| case ID_GR: | case ID_GR: | ||||
| BKE_group_free((Group *)id); | BKE_collection_free((Collection *)id); | ||||
| break; | break; | ||||
| case ID_AR: | case ID_AR: | ||||
| BKE_armature_free((bArmature *)id); | BKE_armature_free((bArmature *)id); | ||||
| break; | break; | ||||
| case ID_AC: | case ID_AC: | ||||
| BKE_action_free((bAction *)id); | BKE_action_free((bAction *)id); | ||||
| break; | break; | ||||
| case ID_NT: | case ID_NT: | ||||
| ▲ Show 20 Lines • Show All 178 Lines • ▼ Show 20 Lines | |||||
| } | } | ||||
| void BKE_libblock_free_us(Main *bmain, void *idv) /* test users */ | void BKE_libblock_free_us(Main *bmain, void *idv) /* test users */ | ||||
| { | { | ||||
| ID *id = idv; | ID *id = idv; | ||||
| id_us_min(id); | id_us_min(id); | ||||
| /* XXX This is a temp (2.77) hack so that we keep same behavior as in 2.76 regarding groups when deleting an object. | /* XXX This is a temp (2.77) hack so that we keep same behavior as in 2.76 regarding collections when deleting an object. | ||||
| * Since only 'user_one' usage of objects is groups, and only 'real user' usage of objects is scenes, | * Since only 'user_one' usage of objects is collections, and only 'real user' usage of objects is scenes, | ||||
| * removing that 'user_one' tag when there is no more real (scene) users of an object ensures it gets | * removing that 'user_one' tag when there is no more real (scene) users of an object ensures it gets | ||||
| * fully unlinked. | * fully unlinked. | ||||
| * But only for local objects, not linked ones! | * But only for local objects, not linked ones! | ||||
| * Otherwise, there is no real way to get rid of an object anymore - better handling of this is TODO. | * Otherwise, there is no real way to get rid of an object anymore - better handling of this is TODO. | ||||
| */ | */ | ||||
| if ((GS(id->name) == ID_OB) && (id->us == 1) && (id->lib == NULL)) { | if ((GS(id->name) == ID_OB) && (id->us == 1) && (id->lib == NULL)) { | ||||
| id_us_clear_real(id); | id_us_clear_real(id); | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 58 Lines • Show Last 20 Lines | |||||