Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBastien Montagne <bastien@blender.org>2022-06-29 19:05:25 +0300
committerBastien Montagne <bastien@blender.org>2022-06-30 11:33:44 +0300
commitb544225202793435c54f63080a2bbd098727654d (patch)
tree490a9daeae50a1f427d5725d1659ad8c4fc0f5b6
parentacdc043c30a6741229536eb9ff70d3ec94ef7b25 (diff)
Fix (unreported) liboverride resync creating garbage data in some cases.
Regression caused by the introduction of partial resync in February 2022 (rB1695d38989fd482d3c). Code was missing adding some existing overrides to the mapping in some specific cases, causing resync to create 'new' ones instead of re-using existing ones. This commit also adds a basic resync testcase that illustrates this issue.
-rw-r--r--source/blender/blenkernel/intern/lib_override.cc9
-rw-r--r--tests/python/bl_blendfile_library_overrides.py109
2 files changed, 116 insertions, 2 deletions
diff --git a/source/blender/blenkernel/intern/lib_override.cc b/source/blender/blenkernel/intern/lib_override.cc
index 7bde609913b..feb6fb95f5c 100644
--- a/source/blender/blenkernel/intern/lib_override.cc
+++ b/source/blender/blenkernel/intern/lib_override.cc
@@ -1749,11 +1749,16 @@ static bool lib_override_library_resync(Main *bmain,
id->tag |= LIB_TAG_MISSING;
}
- if (id->tag & LIB_TAG_DOIT && (id->lib == id_root->lib) && ID_IS_OVERRIDE_LIBRARY(id)) {
+ if ((id->lib == id_root->lib) && ID_IS_OVERRIDE_LIBRARY(id)) {
/* While this should not happen in typical cases (and won't be properly supported here),
* user is free to do all kind of very bad things, including having different local
* overrides of a same linked ID in a same hierarchy. */
IDOverrideLibrary *id_override_library = lib_override_get(bmain, id, nullptr);
+
+ if (id_override_library->hierarchy_root != id_root->override_library->hierarchy_root) {
+ continue;
+ }
+
ID *reference_id = id_override_library->reference;
if (GS(reference_id->name) != GS(id->name)) {
switch (GS(id->name)) {
@@ -1775,7 +1780,7 @@ static bool lib_override_library_resync(Main *bmain,
if (!BLI_ghash_haskey(linkedref_to_old_override, reference_id)) {
BLI_ghash_insert(linkedref_to_old_override, reference_id, id);
- if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id) || (id->tag & LIB_TAG_DOIT) == 0) {
continue;
}
if ((id->override_library->reference->tag & LIB_TAG_DOIT) == 0) {
diff --git a/tests/python/bl_blendfile_library_overrides.py b/tests/python/bl_blendfile_library_overrides.py
index 1acc1e4d862..46954c08b8c 100644
--- a/tests/python/bl_blendfile_library_overrides.py
+++ b/tests/python/bl_blendfile_library_overrides.py
@@ -181,9 +181,118 @@ class TestLibraryTemplate(TestHelper, unittest.TestCase):
assert(operation.operation == 'NOOP')
+class TestLibraryOverridesResync(TestHelper, unittest.TestCase):
+ DATA_NAME_CONTAINER = "LibCollection"
+ DATA_NAME_RIGGED = "LibRigged"
+ DATA_NAME_RIG = "LibRig"
+ DATA_NAME_CONTROLLER_1 = "LibController1"
+ DATA_NAME_CONTROLLER_2 = "LibController2"
+
+ def __init__(self, args):
+ self.args = args
+
+ output_dir = pathlib.Path(self.args.output_dir)
+ self.ensure_path(str(output_dir))
+ self.output_path = output_dir / "blendlib_overrides.blend"
+ self.test_output_path = output_dir / "blendlib_overrides_test.blend"
+
+ bpy.ops.wm.read_homefile(use_empty=True, use_factory_startup=True)
+
+ collection_container = bpy.data.collections.new(TestLibraryOverridesResync.DATA_NAME_CONTAINER)
+ bpy.context.collection.children.link(collection_container)
+
+ mesh = bpy.data.meshes.new(TestLibraryOverridesResync.DATA_NAME_RIGGED)
+ obj_child = bpy.data.objects.new(TestLibraryOverridesResync.DATA_NAME_RIGGED, object_data=mesh)
+ collection_container.objects.link(obj_child)
+ armature = bpy.data.armatures.new(TestLibraryOverridesResync.DATA_NAME_RIG)
+ obj_armature = bpy.data.objects.new(TestLibraryOverridesResync.DATA_NAME_RIG, object_data=armature)
+ obj_child.parent = obj_armature
+ collection_container.objects.link(obj_armature)
+
+ obj_child_modifier = obj_child.modifiers.new("", 'ARMATURE')
+ obj_child_modifier.object = obj_armature
+
+ obj_ctrl1 = bpy.data.objects.new(TestLibraryOverridesResync.DATA_NAME_CONTROLLER_1, object_data=None)
+ collection_container.objects.link(obj_ctrl1)
+
+ obj_armature_constraint = obj_armature.constraints.new('COPY_LOCATION')
+ obj_armature_constraint.target = obj_ctrl1
+
+ collection_sub = bpy.data.collections.new(TestLibraryOverridesResync.DATA_NAME_CONTROLLER_2)
+ collection_container.children.link(collection_sub)
+ obj_ctrl2 = bpy.data.objects.new(TestLibraryOverridesResync.DATA_NAME_CONTROLLER_2, object_data=None)
+ collection_sub.objects.link(obj_ctrl2)
+
+ bpy.ops.wm.save_as_mainfile(filepath=str(self.output_path), check_existing=False, compress=False)
+
+ def test_link_and_override_resync(self):
+ bpy.ops.wm.read_homefile(use_empty=True, use_factory_startup=True)
+ bpy.data.orphans_purge()
+
+ link_dir = self.output_path / "Collection"
+ bpy.ops.wm.link(directory=str(link_dir), filename=TestLibraryOverridesResync.DATA_NAME_CONTAINER, instance_collections=False)
+
+ linked_collection_container = bpy.data.collections[TestLibraryOverridesResync.DATA_NAME_CONTAINER]
+ assert(linked_collection_container.library is not None)
+ assert(linked_collection_container.override_library is None)
+ assert(len(bpy.data.collections) == 2)
+ assert(all(id_.library is not None for id_ in bpy.data.collections))
+ assert(len(bpy.data.objects) == 4)
+ assert(all(id_.library is not None for id_ in bpy.data.objects))
+ assert(len(bpy.data.meshes) == 1)
+ assert(all(id_.library is not None for id_ in bpy.data.meshes))
+ assert(len(bpy.data.armatures) == 1)
+ assert(all(id_.library is not None for id_ in bpy.data.armatures))
+
+ override_collection_container = linked_collection_container.override_hierarchy_create(bpy.context.scene, bpy.context.view_layer)
+ assert(override_collection_container.library is None)
+ assert(override_collection_container.override_library is not None)
+ # Objects and collections are duplicated as overrides, but meshes and armatures remain only linked data.
+ assert(len(bpy.data.collections) == 4)
+ assert(all((id_.library is None and id_.override_library is not None) for id_ in bpy.data.collections[:2]))
+ assert(len(bpy.data.objects) == 8)
+ assert(all((id_.library is None and id_.override_library is not None) for id_ in bpy.data.objects[:4]))
+ assert(len(bpy.data.meshes) == 1)
+ assert(len(bpy.data.armatures) == 1)
+
+ bpy.ops.wm.save_as_mainfile(filepath=str(self.test_output_path), check_existing=False, compress=False)
+
+ # Re-open the lib file, and change its ID relationships.
+ bpy.ops.wm.open_mainfile(filepath=str(self.output_path))
+
+ obj_armature = bpy.data.objects[TestLibraryOverridesResync.DATA_NAME_RIG]
+ obj_armature_constraint = obj_armature.constraints[0]
+ obj_ctrl2 = bpy.data.objects[TestLibraryOverridesResync.DATA_NAME_CONTROLLER_2]
+ obj_armature_constraint.target = obj_ctrl2
+
+ bpy.ops.wm.save_as_mainfile(filepath=str(self.output_path), check_existing=False, compress=False)
+
+ # Re-open the main file, and check that automatic resync did its work correctly, remapping the target of the
+ # armature constraint to controller 2, without creating unexpected garbage IDs along the line.
+ bpy.ops.wm.open_mainfile(filepath=str(self.test_output_path))
+
+ override_collection_container = bpy.data.collections[TestLibraryOverridesResync.DATA_NAME_CONTAINER]
+ assert(override_collection_container.library is None)
+ assert(override_collection_container.override_library is not None)
+ # Objects and collections are duplicated as overrides, but meshes and armatures remain only linked data.
+ assert(len(bpy.data.collections) == 4)
+ assert(all((id_.library is None and id_.override_library is not None) for id_ in bpy.data.collections[:2]))
+ assert(len(bpy.data.objects) == 8)
+ assert(all((id_.library is None and id_.override_library is not None) for id_ in bpy.data.objects[:4]))
+ assert(len(bpy.data.meshes) == 1)
+ assert(len(bpy.data.armatures) == 1)
+
+ obj_armature = bpy.data.objects[TestLibraryOverridesResync.DATA_NAME_RIG]
+ obj_ctrl2 = bpy.data.objects[TestLibraryOverridesResync.DATA_NAME_CONTROLLER_2]
+ assert(obj_armature.library is None and obj_armature.override_library is not None)
+ assert(obj_ctrl2.library is None and obj_ctrl2.override_library is not None)
+ assert(obj_armature.constraints[0].target == obj_ctrl2)
+
+
TESTS = (
TestLibraryOverrides,
TestLibraryTemplate,
+ TestLibraryOverridesResync,
)