Commit: d7ba1ada8208f6f846b3705f5c6d9933261a625f
Author: Dalai Felinto
Date:   Thu Feb 22 17:16:39 2018 -0300
Branches: blender2.8
https://developer.blender.org/rBd7ba1ada8208f6f846b3705f5c6d9933261a625f

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

===================================================================

M       source/blender/blenkernel/intern/library_remap.c
M       tests/python/view_layer/CMakeLists.txt
A       tests/python/view_layer/test_group_e.py

===================================================================

diff --git a/source/blender/blenkernel/intern/library_remap.c 
b/source/blender/blenkernel/intern/library_remap.c
index 6d4c4082810..e689461636d 100644
--- a/source/blender/blenkernel/intern/library_remap.c
+++ b/source/blender/blenkernel/intern/library_remap.c
@@ -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;
                }
diff --git a/tests/python/view_layer/CMakeLists.txt 
b/tests/python/view_layer/CMakeLists.txt
index cc5a3ba54b1..77a56fb47f9 100644
--- a/tests/python/view_layer/CMakeLists.txt
+++ b/tests/python/view_layer/CMakeLists.txt
@@ -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)
diff --git a/tests/python/view_layer/test_group_e.py 
b/tests/python/view_layer/test_group_e.py
new file mode 100644
index 00000000000..566c043572e
--- /dev/null
+++ b/tests/python/view_layer/test_group_e.py
@@ -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()

_______________________________________________
Bf-blender-cvs mailing list
Bf-blender-cvs@blender.org
https://lists.blender.org/mailman/listinfo/bf-blender-cvs

Reply via email to