diff --git a/openpype/hosts/blender/api/plugin.py b/openpype/hosts/blender/api/plugin.py index 7c7dfca2654..055684613e6 100644 --- a/openpype/hosts/blender/api/plugin.py +++ b/openpype/hosts/blender/api/plugin.py @@ -23,6 +23,7 @@ get_children_recursive, get_parent_collection, link_to_collection, + transfer_stack, unlink_from_collection, ) from openpype.pipeline import ( @@ -1248,6 +1249,25 @@ def replace_container( old_datablock.animation_data.action ) + # Ensure modifiers reassignation + if hasattr(old_datablock, "modifiers"): + transfer_stack( + old_datablock, "modifiers", new_datablock + ) + + # Ensure constraints reassignation + if hasattr(old_datablock, "constraints"): + transfer_stack( + old_datablock, "constraints", new_datablock + ) + + # Ensure bones constraints reassignation + if hasattr(old_datablock, "pose") and old_datablock.pose: + for bone in old_datablock.pose.bones: + new_bone = new_datablock.pose.bones.get(bone.name) + if new_bone: + transfer_stack(bone, "constraints", new_bone) + # Restore parent collection if existing if parent_collection: unlink_from_collection( diff --git a/openpype/hosts/blender/api/utils.py b/openpype/hosts/blender/api/utils.py index bbd350725ba..615f4d5fd6e 100644 --- a/openpype/hosts/blender/api/utils.py +++ b/openpype/hosts/blender/api/utils.py @@ -250,3 +250,52 @@ def assign_loader_to_datablocks(datablocks: List[bpy.types.ID]): container = bpy.context.scene.openpype_containers.get(datablock.name) if container and container.get(AVALON_PROPERTY): container[AVALON_PROPERTY]["loader"] = loader_name + + +def transfer_stack( + source_datablock: bpy.types.ID, + stack_name: str, + target_datablock: bpy.types.ID, +): + """Transfer stack of modifiers or constraints from a datablock to another one. + + New stack entities are created for each source stack entity. + If a stack entity in the target datablock has the same name of one + from the source datablock, it is skipped. No duplicate created, neither + attribute update. + + Args: + src_datablock (bpy.types.ID): Datablock to get stack from. + stack_name (str): Stack name to transfer (eg 'modifiers', 'constraints'...) + target_datablock (bpy.types.ID): Datablock to create stack entities to + """ + src_col = getattr(source_datablock, stack_name) + for stack_datablock in src_col: + target_col = getattr(target_datablock, stack_name) + target_data = target_col.get(stack_datablock.name) + if not target_data: + if stack_name == "modifiers": + target_data = target_col.new( + stack_datablock.name, stack_datablock.type + ) + else: + target_data = target_col.new(stack_datablock.type) + + # Transfer attributes + attributes = { + a + for a in dir(stack_datablock) + if not a.startswith("_") + and a + not in { + "type", + "error_location", + "rna_type", + "error_rotation", + "bl_rna", + "is_valid", + "is_override_data", + } + } + for attr in attributes: + setattr(target_data, attr, getattr(stack_datablock, attr))