Skip to content

extract_blend

ExtractBlend

Bases: BlenderExtractor, OptionalPyblishPluginMixin

Extract a blend file.

Source code in client/ayon_blender/plugins/publish/extract_blend.py
 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
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
    pack_textures = True

    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)
        asset_group = instance.data["transientData"]["instance_node"]
        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_instance_data(asset_group))
            stack.enter_context(strip_namespace(containers))
            if self.pack_textures:
                stack.enter_context(
                    packed_images(data_blocks, logger=self.log)
                )
            # make sure all texture files are absolute paths
            if instance.data["productBaseType"] == "look":
                stack.enter_context(make_material_image_paths_absolute(data_blocks))

            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.
        """
        return set(instance)

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
122
123
124
125
126
127
128
129
130
131
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 set(instance)

ExtractBlendAction

Bases: ExtractBlend

Extract a blend file from the current scene.

Source code in client/ayon_blender/plugins/publish/extract_blend.py
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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
142
143
144
145
146
147
148
149
150
151
152
153
154
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"]
    }

ExtractBlendLook

Bases: ExtractBlend

Extract a blend file from the current scene.

Source code in client/ayon_blender/plugins/publish/extract_blend.py
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
class ExtractBlendLook(ExtractBlend):
    """Extract a blend file from the current scene."""
    families = ["look"]
    label = "Extract Blend (Look)"
    optional = False
    # From settings
    compress = False

    def add_datablock(self, instance: pyblish.api.Instance) -> set:
        """Add the material data block to the blend file.
        The datablock needs to be exported along with its linked objects
        to preserve the material's node tree and texture links.

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

        Returns:
            set: A set of data blocks added.
        """
        datablock_to_be_exported = set()
        for obj in instance:
            if not isinstance(obj, bpy.types.Object):
                continue
            if not hasattr(obj.data, "materials"):
                continue
            datablock_to_be_exported.update(obj.data.materials)
        return datablock_to_be_exported

add_datablock(instance)

Add the material data block to the blend file. The datablock needs to be exported along with its linked objects to preserve the material's node tree and texture links.

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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
def add_datablock(self, instance: pyblish.api.Instance) -> set:
    """Add the material data block to the blend file.
    The datablock needs to be exported along with its linked objects
    to preserve the material's node tree and texture links.

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

    Returns:
        set: A set of data blocks added.
    """
    datablock_to_be_exported = set()
    for obj in instance:
        if not isinstance(obj, bpy.types.Object):
            continue
        if not hasattr(obj.data, "materials"):
            continue
        datablock_to_be_exported.update(obj.data.materials)
    return datablock_to_be_exported

Link objects to a collection during context

Source code in client/ayon_blender/plugins/publish/extract_blend.py
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@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)