Fix T56622: crash and other bugs deleting scenes.
Simplify library remapping code to handle special collection/object links in postprocess. Previously base contained the actual object link which needed special handling in preprocess, now objects are linked through collection and the base cache can be updated in postprocess.
This commit is contained in:
@@ -611,29 +611,36 @@ bool BKE_scene_collections_object_remove(Main *bmain, Scene *scene, Object *ob,
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove all NULL objects from non-scene collections.
|
||||
* Remove all NULL objects from collections.
|
||||
* This is used for library remapping, where these pointers have been set to NULL.
|
||||
* Otherwise this should never happen.
|
||||
*/
|
||||
static void collection_object_remove_nulls(Collection *collection)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
for (CollectionObject *cob = collection->gobject.first, *cob_next = NULL; cob; cob = cob_next) {
|
||||
cob_next = cob->next;
|
||||
|
||||
if (cob->ob == NULL) {
|
||||
BLI_freelinkN(&collection->gobject, cob);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
BKE_collection_object_cache_free(collection);
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_collections_object_remove_nulls(Main *bmain)
|
||||
{
|
||||
for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
|
||||
collection_object_remove_nulls(scene->master_collection);
|
||||
}
|
||||
|
||||
for (Collection *collection = bmain->collection.first; collection; collection = collection->id.next) {
|
||||
if (!BKE_collection_is_in_scene(collection)) {
|
||||
bool changed = false;
|
||||
|
||||
for (CollectionObject *cob = collection->gobject.first, *cob_next = NULL; cob; cob = cob_next) {
|
||||
cob_next = cob->next;
|
||||
|
||||
if (cob->ob == NULL) {
|
||||
BLI_freelinkN(&collection->gobject, cob);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
BKE_collection_object_cache_free(collection);
|
||||
}
|
||||
}
|
||||
collection_object_remove_nulls(collection);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -646,15 +653,9 @@ void BKE_collections_child_remove_nulls(Main *bmain, Collection *old_collection)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
for (CollectionChild *child = old_collection->children.first; child; child = child->next) {
|
||||
CollectionParent *cparent = collection_find_parent(child->collection, old_collection);
|
||||
if (cparent) {
|
||||
BLI_freelinkN(&child->collection->parents, cparent);
|
||||
}
|
||||
}
|
||||
|
||||
for (CollectionParent *cparent = old_collection->parents.first; cparent; cparent = cparent->next) {
|
||||
for (CollectionParent *cparent = old_collection->parents.first, *cnext; cparent; cparent = cnext) {
|
||||
Collection *parent = cparent->collection;
|
||||
cnext = cparent->next;
|
||||
|
||||
for (CollectionChild *child = parent->children.first, *child_next = NULL; child; child = child_next) {
|
||||
child_next = child->next;
|
||||
@@ -664,9 +665,12 @@ void BKE_collections_child_remove_nulls(Main *bmain, Collection *old_collection)
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BLI_freelistN(&old_collection->parents);
|
||||
if (!collection_find_child(parent, old_collection)) {
|
||||
BLI_freelinkN(&old_collection->parents, cparent);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
BKE_main_collection_sync(bmain);
|
||||
|
||||
@@ -80,7 +80,7 @@ static LayerCollection *layer_collection_add(ListBase *lb_parent, Collection *co
|
||||
static void layer_collection_free(ViewLayer *view_layer, LayerCollection *lc)
|
||||
{
|
||||
if (lc == view_layer->active_collection) {
|
||||
view_layer->active_collection = view_layer->layer_collections.first;
|
||||
view_layer->active_collection = NULL;
|
||||
}
|
||||
|
||||
for (LayerCollection *nlc = lc->layer_collections.first; nlc; nlc = nlc->next) {
|
||||
@@ -311,7 +311,9 @@ static void view_layer_bases_hash_create(ViewLayer *view_layer)
|
||||
view_layer->object_bases_hash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__);
|
||||
|
||||
for (Base *base = view_layer->object_bases.first; base; base = base->next) {
|
||||
BLI_ghash_insert(view_layer->object_bases_hash, base->object, base);
|
||||
if (base->object) {
|
||||
BLI_ghash_insert(view_layer->object_bases_hash, base->object, base);
|
||||
}
|
||||
}
|
||||
|
||||
BLI_mutex_unlock(&hash_lock);
|
||||
@@ -786,7 +788,9 @@ void BKE_layer_collection_sync(const Scene *scene, ViewLayer *view_layer)
|
||||
view_layer->basact = NULL;
|
||||
}
|
||||
|
||||
BLI_ghash_remove(view_layer->object_bases_hash, base->object, NULL, NULL);
|
||||
if (base->object) {
|
||||
BLI_ghash_remove(view_layer->object_bases_hash, base->object, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
BLI_freelistN(&view_layer->object_bases);
|
||||
|
||||
@@ -263,81 +263,9 @@ static int foreach_libblock_remap_callback(void *user_data, ID *id_self, ID **id
|
||||
return IDWALK_RET_NOP;
|
||||
}
|
||||
|
||||
/* Some remapping unfortunately require extra and/or specific handling, tackle those here. */
|
||||
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)
|
||||
{
|
||||
if (skip_indirect && is_indirect) {
|
||||
r_id_remap_data->skipped_indirect++;
|
||||
r_id_remap_data->skipped_refcounted++;
|
||||
}
|
||||
else {
|
||||
/* 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) {
|
||||
r_id_remap_data->status |= ID_REMAP_IS_LINKED_DIRECT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void libblock_remap_data_preprocess_collection_unlink(
|
||||
IDRemap *r_id_remap_data, Object *ob, const bool skip_indirect, const bool is_indirect)
|
||||
{
|
||||
Main *bmain = r_id_remap_data->bmain;
|
||||
for (Collection *collection = bmain->collection.first; collection; collection = collection->id.next) {
|
||||
if (!BKE_collection_is_in_scene(collection) && BKE_collection_has_object(collection, ob)) {
|
||||
if (skip_indirect && is_indirect) {
|
||||
r_id_remap_data->skipped_indirect++;
|
||||
r_id_remap_data->skipped_refcounted++;
|
||||
}
|
||||
else {
|
||||
BKE_collection_object_remove(bmain, collection, ob, false);
|
||||
if (!is_indirect) {
|
||||
r_id_remap_data->status |= ID_REMAP_IS_LINKED_DIRECT;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void libblock_remap_data_preprocess(IDRemap *r_id_remap_data)
|
||||
{
|
||||
switch (GS(r_id_remap_data->id->name)) {
|
||||
case ID_SCE:
|
||||
{
|
||||
Scene *sce = (Scene *)r_id_remap_data->id;
|
||||
|
||||
if (!r_id_remap_data->new_id) {
|
||||
const bool is_indirect = (sce->id.lib != NULL);
|
||||
const bool skip_indirect = (r_id_remap_data->flag & ID_REMAP_SKIP_INDIRECT_USAGE) != 0;
|
||||
|
||||
/* In case we are unlinking... */
|
||||
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. */
|
||||
FOREACH_SCENE_OBJECT_BEGIN(sce, ob_iter)
|
||||
{
|
||||
libblock_remap_data_preprocess_scene_object_unlink(
|
||||
r_id_remap_data, sce, ob_iter, skip_indirect, is_indirect);
|
||||
libblock_remap_data_preprocess_collection_unlink(
|
||||
r_id_remap_data, ob_iter, skip_indirect, is_indirect);
|
||||
}
|
||||
FOREACH_SCENE_OBJECT_END;
|
||||
}
|
||||
else if (GS(r_id_remap_data->old_id->name) == ID_OB) {
|
||||
/* ... a specific object from scene. */
|
||||
Object *old_ob = (Object *)r_id_remap_data->old_id;
|
||||
libblock_remap_data_preprocess_scene_object_unlink(
|
||||
r_id_remap_data, sce, old_ob, skip_indirect, is_indirect);
|
||||
libblock_remap_data_preprocess_collection_unlink(
|
||||
r_id_remap_data, old_ob, skip_indirect, is_indirect);
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ID_OB:
|
||||
{
|
||||
ID *old_id = r_id_remap_data->old_id;
|
||||
|
||||
Reference in New Issue
Block a user