Fix T54136: Crash when deleting an object that is in an instanced group
We were not cleaning up groups after deleting objects, leaving groups with Bases that had no object. It includes a unittest. Reviewers: mont29
This commit is contained in:
parent
e7c4a9d1ef
commit
d7ba1ada82
Notes:
blender-bot
2023-02-14 11:28:39 +01:00
Referenced by issue #54136, Crash when deleting an object that is in an instanced group
|
@ -279,6 +279,26 @@ static void libblock_remap_data_preprocess_scene_object_unlink(
|
|||
}
|
||||
}
|
||||
|
||||
static void libblock_remap_data_preprocess_group_unlink(
|
||||
IDRemap *r_id_remap_data, Object *ob, const bool skip_indirect, const bool is_indirect)
|
||||
{
|
||||
Main *bmain = r_id_remap_data->bmain;
|
||||
for (Group *group = bmain->group.first; group; group = group->id.next) {
|
||||
if (BKE_group_object_exists(group, ob)) {
|
||||
if (skip_indirect && is_indirect) {
|
||||
r_id_remap_data->skipped_indirect++;
|
||||
r_id_remap_data->skipped_refcounted++;
|
||||
}
|
||||
else {
|
||||
BKE_collections_object_remove(bmain, &group->id, 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)) {
|
||||
|
@ -297,6 +317,8 @@ static void libblock_remap_data_preprocess(IDRemap *r_id_remap_data)
|
|||
{
|
||||
libblock_remap_data_preprocess_scene_object_unlink(
|
||||
r_id_remap_data, sce, ob_iter, skip_indirect, is_indirect);
|
||||
libblock_remap_data_preprocess_group_unlink(
|
||||
r_id_remap_data, ob_iter, skip_indirect, is_indirect);
|
||||
}
|
||||
FOREACH_SCENE_OBJECT_END
|
||||
}
|
||||
|
@ -305,7 +327,10 @@ static void libblock_remap_data_preprocess(IDRemap *r_id_remap_data)
|
|||
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_group_unlink(
|
||||
r_id_remap_data, old_ob, skip_indirect, is_indirect);
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -94,6 +94,7 @@ VIEW_LAYER_TEST(group_a)
|
|||
VIEW_LAYER_TEST(group_b)
|
||||
VIEW_LAYER_TEST(group_c)
|
||||
VIEW_LAYER_TEST(group_d)
|
||||
VIEW_LAYER_TEST(group_e)
|
||||
VIEW_LAYER_TEST(object_add_cylinder)
|
||||
VIEW_LAYER_TEST(object_add_empty)
|
||||
VIEW_LAYER_TEST(object_add_torus)
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
# ############################################################
|
||||
# Importing - Same For All Render Layer Tests
|
||||
# ############################################################
|
||||
|
||||
import unittest
|
||||
import os
|
||||
import sys
|
||||
|
||||
from view_layer_common import *
|
||||
|
||||
|
||||
# ############################################################
|
||||
# Testing
|
||||
# ############################################################
|
||||
|
||||
class UnitTesting(ViewLayerTesting):
|
||||
def test_group_delete_object(self):
|
||||
"""
|
||||
See if we can safely remove instanced objects
|
||||
"""
|
||||
import bpy
|
||||
scene = bpy.context.scene
|
||||
view_layer = bpy.context.view_layer
|
||||
ob = bpy.context.object
|
||||
|
||||
# clean up the scene a bit
|
||||
for o in (o for o in view_layer.objects if o != ob):
|
||||
view_layer.collections[0].collection.objects.unlink(o)
|
||||
|
||||
for v in (v for v in scene.view_layers if v != view_layer):
|
||||
scene.view_layers.remove(v)
|
||||
|
||||
# update depsgraph
|
||||
scene.update()
|
||||
|
||||
# create group
|
||||
group = bpy.data.groups.new("Switch")
|
||||
group.objects.link(ob)
|
||||
|
||||
# update depsgraph
|
||||
scene.update()
|
||||
|
||||
# instance the group
|
||||
empty = bpy.data.objects.new("Empty", None)
|
||||
bpy.context.scene_collection.objects.link(empty)
|
||||
layer_collection = bpy.context.layer_collection
|
||||
empty.dupli_type = 'GROUP'
|
||||
empty.dupli_group = group
|
||||
|
||||
# prepare to delete the original object
|
||||
# we could just pass an overridden context
|
||||
# but let's do it the old fashion way
|
||||
view_layer.objects.active = ob
|
||||
ob.select_set('SELECT')
|
||||
self.assertTrue(ob.select_get())
|
||||
empty.select_set('DESELECT')
|
||||
self.assertFalse(empty.select_get())
|
||||
|
||||
# update depsgraph
|
||||
scene.update()
|
||||
|
||||
# delete the original object
|
||||
bpy.ops.object.delete()
|
||||
|
||||
|
||||
# ############################################################
|
||||
# Main - Same For All Render Layer Tests
|
||||
# ############################################################
|
||||
|
||||
if __name__ == '__main__':
|
||||
UnitTesting._extra_arguments = setup_extra_arguments(__file__)
|
||||
unittest.main()
|
Loading…
Reference in New Issue