Skip to content

extract_blend

ExtractBlend

Bases: BlenderExtractor, OptionalPyblishPluginMixin

Extract a blend file.

Source code in client/ayon_blender/plugins/publish/extract_blend.py
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
class ExtractBlend(
    plugin.BlenderExtractor, publish.OptionalPyblishPluginMixin
):
    """Extract a blend file."""

    label = "Extract Blend"
    hosts = ["blender"]
    families = ["model", "camera", "rig", "layout", "blendScene"]
    optional = True

    # From settings
    compress = False

    def process(self, instance):
        if not self.is_active(instance.data):
            return

        # Define extract output file path

        stagingdir = self.staging_dir(instance)
        folder_name = instance.data["folderEntity"]["name"]
        product_name = instance.data["productName"]
        instance_name = f"{folder_name}_{product_name}"
        filename = f"{instance_name}.blend"
        filepath = os.path.join(stagingdir, filename)

        # Perform extraction
        self.log.debug("Performing extraction..")

        data_blocks = self.add_datablock(instance)

        containers = list(ls())
        with contextlib.ExitStack() as stack:
            # If the instance node is a Collection, we want to enforce the
            # full child hierarchies to be included in the written collections.
            instance_node = instance.data["transientData"]["instance_node"]
            if isinstance(instance_node, bpy.types.Collection):
                # We only link children nodes to the 'parent' collection it is
                # in so that the full children hierarchy is preserved for the
                # main collection, and all its child collections.
                collections = [instance_node]
                collections.extend(instance_node.children_recursive)
                for collection in set(collections):
                    missing_child_hierarchy = set()
                    for obj in collection.objects:
                        for child in obj.children_recursive:
                            if collection not in child.users_collection:
                                missing_child_hierarchy.add(child)

                    if missing_child_hierarchy:
                        stack.enter_context(link_to_collection(
                            collection, list(missing_child_hierarchy)))

            stack.enter_context(strip_container_data(containers))
            stack.enter_context(strip_namespace(containers))
            self.log.debug("Datablocks: %s", data_blocks)
            bpy.data.libraries.write(
                filepath, data_blocks, compress=self.compress
            )

        if "representations" not in instance.data:
            instance.data["representations"] = []

        representation = {
            'name': 'blend',
            'ext': 'blend',
            'files': filename,
            "stagingDir": stagingdir,
        }
        instance.data["representations"].append(representation)

        self.log.debug("Extracted instance '%s' to: %s",
                       instance.name, representation)

    def add_datablock(self, instance: pyblish.api.Instance) -> set:
        """Add a data block to the blend file.

        Args:
            instance (pyblish.api.Instance): The instance to add.

        Returns:
            set: A set of data blocks added.
        """
        data_blocks = set()

        for data in instance:
            data_blocks.add(data)
            # Pack used images in the blend files.
            if not (
                isinstance(data, bpy.types.Object) and data.type == 'MESH'
            ):
                continue
            for material_slot in data.material_slots:
                mat = material_slot.material
                if not (mat and mat.use_nodes):
                    continue
                tree = mat.node_tree
                if tree.type != 'SHADER':
                    continue
                for node in tree.nodes:
                    if node.bl_idname != 'ShaderNodeTexImage':
                        continue
                    # Check if image is not packed already
                    # and pack it if not.
                    if node.image and node.image.packed_file is None:
                        node.image.pack()
        return data_blocks

add_datablock(instance)

Add a data block to the blend file.

Parameters:

Name Type Description Default
instance Instance

The instance to add.

required

Returns:

Name Type Description
set set

A set of data blocks added.

Source code in client/ayon_blender/plugins/publish/extract_blend.py
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
def add_datablock(self, instance: pyblish.api.Instance) -> set:
    """Add a data block to the blend file.

    Args:
        instance (pyblish.api.Instance): The instance to add.

    Returns:
        set: A set of data blocks added.
    """
    data_blocks = set()

    for data in instance:
        data_blocks.add(data)
        # Pack used images in the blend files.
        if not (
            isinstance(data, bpy.types.Object) and data.type == 'MESH'
        ):
            continue
        for material_slot in data.material_slots:
            mat = material_slot.material
            if not (mat and mat.use_nodes):
                continue
            tree = mat.node_tree
            if tree.type != 'SHADER':
                continue
            for node in tree.nodes:
                if node.bl_idname != 'ShaderNodeTexImage':
                    continue
                # Check if image is not packed already
                # and pack it if not.
                if node.image and node.image.packed_file is None:
                    node.image.pack()
    return data_blocks

ExtractBlendAction

Bases: ExtractBlend

Extract a blend file from the current scene.

Source code in client/ayon_blender/plugins/publish/extract_blend.py
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
class ExtractBlendAction(ExtractBlend):
    """Extract a blend file from the current scene.
    """
    families = ["action"]
    label = "Extract Blend (Action)"
    optional = False
    # From settings
    compress = False

    def add_datablock(self, instance: pyblish.api.Instance) -> set:
        """Add a data block to the blend file.

        Args:
            instance (pyblish.api.Instance): The instance to add.

        Returns:
            set: A set of data blocks added.
        """
        return {
            action for action in bpy.data.actions
            if action.name == instance.data["productName"]
        }

add_datablock(instance)

Add a data block to the blend file.

Parameters:

Name Type Description Default
instance Instance

The instance to add.

required

Returns:

Name Type Description
set set

A set of data blocks added.

Source code in client/ayon_blender/plugins/publish/extract_blend.py
151
152
153
154
155
156
157
158
159
160
161
162
163
def add_datablock(self, instance: pyblish.api.Instance) -> set:
    """Add a data block to the blend file.

    Args:
        instance (pyblish.api.Instance): The instance to add.

    Returns:
        set: A set of data blocks added.
    """
    return {
        action for action in bpy.data.actions
        if action.name == instance.data["productName"]
    }

Link objects to a collection during context

Source code in client/ayon_blender/plugins/publish/extract_blend.py
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@contextlib.contextmanager
def link_to_collection(collection, objects):
    """Link objects to a collection during context"""
    unlink_after = []
    try:
        for obj in objects:
            if not isinstance(obj, bpy.types.Object):
                continue
            if collection not in obj.users_collection:
                unlink_after.append(obj)
                collection.objects.link(obj)
        yield
    finally:
        for obj in unlink_after:
            collection.objects.unlink(obj)