From dd67b355eeed10cd51da30974975a0df90cdcfcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 27 Jan 2025 16:29:18 +0100 Subject: [PATCH] Anim: do not set slot ID type when Action is linked When an action slot does not have an ID type, and it is assigned to some ID, the slot is bound to that ID's type. This now no longer happens when the Action is linked, because linked data should not be modified. Pull Request: https://projects.blender.org/blender/blender/pulls/133670 --- source/blender/animrig/intern/action.cc | 7 ++++ tests/data | 2 +- tests/python/bl_animation_action.py | 53 +++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/source/blender/animrig/intern/action.cc b/source/blender/animrig/intern/action.cc index 0d307fddf8b..08cb02f8633 100644 --- a/source/blender/animrig/intern/action.cc +++ b/source/blender/animrig/intern/action.cc @@ -697,6 +697,13 @@ void Action::slot_identifier_ensure_prefix(Slot &slot) void Action::slot_setup_for_id(Slot &slot, const ID &animated_id) { + if (!ID_IS_EDITABLE(this) || ID_IS_OVERRIDE_LIBRARY(this)) { + /* Do not write to linked data. For now, also avoid changing the slot identifier on an + * override. Actions cannot have library overrides at the moment, and when they do, this should + * actually get designed. For now, it's better to avoid editing data than editing too much. */ + return; + } + if (slot.has_idtype()) { BLI_assert(slot.idtype == GS(animated_id.name)); return; diff --git a/tests/data b/tests/data index 02bedf6aa5b..7330388f9a7 160000 --- a/tests/data +++ b/tests/data @@ -1 +1 @@ -Subproject commit 02bedf6aa5bf8ece10006a07b380edd3f2843191 +Subproject commit 7330388f9a7a457f9031b26fbf18bced9afc8430 diff --git a/tests/python/bl_animation_action.py b/tests/python/bl_animation_action.py index 261cd2489a7..3ba20a59397 100644 --- a/tests/python/bl_animation_action.py +++ b/tests/python/bl_animation_action.py @@ -162,6 +162,59 @@ class ActionSlotAssignmentTest(unittest.TestCase): cube_adt.action_slot = slot self.assertEqual(cube_adt.action_slot, slot_cube, "The slot should not have changed") + def test_untyped_slot_assignment_local(self): + """Test untyped slot assignment, with a local Action.""" + + action = self._load_legacy_action(link=False) + + # Assign the Action to a Mesh data-block. This should set the ID type of the Slot to 'MESH'. + mesh = bpy.data.meshes['Cube'] + mesh.animation_data_create().action = action + + slot = action.slots[0] + self.assertEqual('MESH', slot.target_id_type, "After assignment, the ID type should be specified.") + self.assertEqual("MELegacy Slot", slot.identifier) + + def test_untyped_slot_assignment_linked(self): + """Test untyped slot assignment, with a linked Action.""" + + action = self._load_legacy_action(link=True) + + # Assign the Action to a Mesh data-block. This should set the ID type of the Slot to 'MESH'. + mesh = bpy.data.meshes['Cube'] + mesh.animation_data_create().action = action + + slot = action.slots[0] + self.assertEqual( + 'UNSPECIFIED', + slot.target_id_type, + "After assignment, the ID type should remain UNSPECIFIED when the Action is linked.") + self.assertEqual("XXLegacy Slot", slot.identifier) + + @staticmethod + def _load_legacy_action(*, link: bool) -> bpy.types.Action: + # At the moment of writing, the only way to create an untyped slot is to + # load a legacy Action that has `id_root=0` and let the versioning code + # create the untyped slot. + blendpath = args.testdir / "legacy-action-without-idroot.blend" + + # Append or link the one Action from the legacy file. + with bpy.data.libraries.load(str(blendpath), link=link) as (data_in, data_out): + data_out.actions = data_in.actions + + # Using plain asserts here, because these are not part of the unit test. + # They're here to test that the test code itself is doing the right thing. + assert len(data_out.actions) == 1 + assert isinstance(data_out.actions[0], bpy.types.Action) + + # Check that the state of things is as expected. + action = data_out.actions[0] + slot = action.slots[0] + assert slot.target_id_type == 'UNSPECIFIED' + assert slot.identifier == "XXLegacy Slot" + + return action + class LimitationsTest(unittest.TestCase): """Test artificial limitations for the layered Action.