Skip to content

api

Unreal Editor Ayon host API.

Loader

Bases: LoaderPlugin, ABC

This serves as skeleton for future Ayon specific functionality

Source code in client/ayon_unreal/api/plugin.py
272
273
274
class Loader(LoaderPlugin, ABC):
    """This serves as skeleton for future Ayon specific functionality"""
    pass

UnrealActorCreator

Bases: UnrealBaseCreator

Base class for Unreal creator plugins based on actors.

Source code in client/ayon_unreal/api/plugin.py
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
class UnrealActorCreator(UnrealBaseCreator):
    """Base class for Unreal creator plugins based on actors."""

    def create(self, product_name, instance_data, pre_create_data):
        """Create instance of the asset.

        Args:
            product_name (str): Name of the product.
            instance_data (dict): Data for the instance.
            pre_create_data (dict): Data for the instance.

        Returns:
            CreatedInstance: Created instance.
        """
        try:
            if UNREAL_VERSION.major == 5:
                world = unreal.UnrealEditorSubsystem().get_editor_world()
            else:
                world = unreal.EditorLevelLibrary.get_editor_world()

            # Check if the level is saved
            if world.get_path_name().startswith("/Temp/"):
                raise CreatorError(
                    "Level must be saved before creating instances.")

            # Check if instance data has members, filled by the plugin.
            # If not, use selection.
            if not instance_data.get("members"):
                actor_subsystem = unreal.EditorActorSubsystem()
                sel_actors = actor_subsystem.get_selected_level_actors()
                selection = [a.get_path_name() for a in sel_actors]

                instance_data["members"] = selection
            instance_data["level"] = world.get_path_name()

            super(UnrealActorCreator, self).create(
                product_name,
                instance_data,
                pre_create_data)

        except Exception as exc:
            raise CreatorError(f"Creator error: {exc}") from exc

    def get_pre_create_attr_defs(self):
        return [
            UILabelDef("Select actors to create instance from them."),
        ]

create(product_name, instance_data, pre_create_data)

Create instance of the asset.

Parameters:

Name Type Description Default
product_name str

Name of the product.

required
instance_data dict

Data for the instance.

required
pre_create_data dict

Data for the instance.

required

Returns:

Name Type Description
CreatedInstance

Created instance.

Source code in client/ayon_unreal/api/plugin.py
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
def create(self, product_name, instance_data, pre_create_data):
    """Create instance of the asset.

    Args:
        product_name (str): Name of the product.
        instance_data (dict): Data for the instance.
        pre_create_data (dict): Data for the instance.

    Returns:
        CreatedInstance: Created instance.
    """
    try:
        if UNREAL_VERSION.major == 5:
            world = unreal.UnrealEditorSubsystem().get_editor_world()
        else:
            world = unreal.EditorLevelLibrary.get_editor_world()

        # Check if the level is saved
        if world.get_path_name().startswith("/Temp/"):
            raise CreatorError(
                "Level must be saved before creating instances.")

        # Check if instance data has members, filled by the plugin.
        # If not, use selection.
        if not instance_data.get("members"):
            actor_subsystem = unreal.EditorActorSubsystem()
            sel_actors = actor_subsystem.get_selected_level_actors()
            selection = [a.get_path_name() for a in sel_actors]

            instance_data["members"] = selection
        instance_data["level"] = world.get_path_name()

        super(UnrealActorCreator, self).create(
            product_name,
            instance_data,
            pre_create_data)

    except Exception as exc:
        raise CreatorError(f"Creator error: {exc}") from exc

UnrealAssetCreator

Bases: UnrealBaseCreator

Base class for Unreal creator plugins based on assets.

Source code in client/ayon_unreal/api/plugin.py
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
class UnrealAssetCreator(UnrealBaseCreator):
    """Base class for Unreal creator plugins based on assets."""

    def create(self, product_name, instance_data, pre_create_data):
        """Create instance of the asset.

        Args:
            product_name (str): Name of the product.
            instance_data (dict): Data for the instance.
            pre_create_data (dict): Data for the instance.

        Returns:
            CreatedInstance: Created instance.
        """
        try:
            # Check if instance data has members, filled by the plugin.
            # If not, use selection.
            if not pre_create_data.get("members"):
                pre_create_data["members"] = []

                if pre_create_data.get("use_selection"):
                    utilib = unreal.EditorUtilityLibrary
                    sel_objects = utilib.get_selected_assets()
                    pre_create_data["members"] = [
                        a.get_path_name() for a in sel_objects]

            super(UnrealAssetCreator, self).create(
                product_name,
                instance_data,
                pre_create_data)

        except Exception as exc:
            raise CreatorError(f"Creator error: {exc}") from exc

    def get_pre_create_attr_defs(self):
        return [
            BoolDef("use_selection", label="Use selection", default=True)
        ]

create(product_name, instance_data, pre_create_data)

Create instance of the asset.

Parameters:

Name Type Description Default
product_name str

Name of the product.

required
instance_data dict

Data for the instance.

required
pre_create_data dict

Data for the instance.

required

Returns:

Name Type Description
CreatedInstance

Created instance.

Source code in client/ayon_unreal/api/plugin.py
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
def create(self, product_name, instance_data, pre_create_data):
    """Create instance of the asset.

    Args:
        product_name (str): Name of the product.
        instance_data (dict): Data for the instance.
        pre_create_data (dict): Data for the instance.

    Returns:
        CreatedInstance: Created instance.
    """
    try:
        # Check if instance data has members, filled by the plugin.
        # If not, use selection.
        if not pre_create_data.get("members"):
            pre_create_data["members"] = []

            if pre_create_data.get("use_selection"):
                utilib = unreal.EditorUtilityLibrary
                sel_objects = utilib.get_selected_assets()
                pre_create_data["members"] = [
                    a.get_path_name() for a in sel_objects]

        super(UnrealAssetCreator, self).create(
            product_name,
            instance_data,
            pre_create_data)

    except Exception as exc:
        raise CreatorError(f"Creator error: {exc}") from exc

UnrealHost

Bases: HostBase, ILoadHost, IPublishHost

Unreal host implementation.

For some time this class will re-use functions from module based implementation for backwards compatibility of older unreal projects.

Source code in client/ayon_unreal/api/pipeline.py
 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
class UnrealHost(HostBase, ILoadHost, IPublishHost):
    """Unreal host implementation.

    For some time this class will re-use functions from module based
    implementation for backwards compatibility of older unreal projects.
    """

    name = "unreal"

    def install(self):
        install()

    def get_containers(self):
        return ls()

    @staticmethod
    def show_tools_popup():
        """Show tools popup with actions leading to show other tools."""
        show_tools_popup()

    @staticmethod
    def show_tools_dialog():
        """Show tools dialog with actions leading to show other tools."""
        show_tools_dialog()

    def update_context_data(self, data, changes):
        content_path = unreal.Paths.project_content_dir()
        op_ctx = content_path + CONTEXT_CONTAINER
        attempts = 3
        for i in range(attempts):
            try:
                with open(op_ctx, "w+") as f:
                    json.dump(data, f)
                break
            except IOError as e:
                if i == attempts - 1:
                    raise Exception(
                        "Failed to write context data. Aborting.") from e
                unreal.log_warning("Failed to write context data. Retrying...")
                i += 1
                time.sleep(3)
                continue

    def get_context_data(self):
        content_path = unreal.Paths.project_content_dir()
        op_ctx = content_path + CONTEXT_CONTAINER
        if not os.path.isfile(op_ctx):
            return {}
        with open(op_ctx, "r") as fp:
            data = json.load(fp)
        return data

show_tools_dialog() staticmethod

Show tools dialog with actions leading to show other tools.

Source code in client/ayon_unreal/api/pipeline.py
74
75
76
77
@staticmethod
def show_tools_dialog():
    """Show tools dialog with actions leading to show other tools."""
    show_tools_dialog()

show_tools_popup() staticmethod

Show tools popup with actions leading to show other tools.

Source code in client/ayon_unreal/api/pipeline.py
69
70
71
72
@staticmethod
def show_tools_popup():
    """Show tools popup with actions leading to show other tools."""
    show_tools_popup()

containerise(name, namespace, nodes, context, loader=None, suffix='_CON')

Bundles nodes (assets) into a container and add metadata to it.

Unreal doesn't support groups of assets that you can add metadata to. But it does support folders that helps to organize asset. Unfortunately those folders are just that - you cannot add any additional information to them. Ayon Integration Plugin is providing way out - Implementing AssetContainer Blueprint class. This class when added to folder can handle metadata on it using standard :func:unreal.EditorAssetLibrary.set_metadata_tag() and :func:unreal.EditorAssetLibrary.get_metadata_tag_values(). It also stores and monitor all changes in assets in path where it resides. List of those assets is available as assets property.

This is list of strings starting with asset type and ending with its path: Material /Game/Ayon/Test/TestMaterial.TestMaterial

Source code in client/ayon_unreal/api/pipeline.py
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
def containerise(name, namespace, nodes, context, loader=None, suffix="_CON"):
    """Bundles *nodes* (assets) into a *container* and add metadata to it.

    Unreal doesn't support *groups* of assets that you can add metadata to.
    But it does support folders that helps to organize asset. Unfortunately
    those folders are just that - you cannot add any additional information
    to them. Ayon Integration Plugin is providing way out - Implementing
    `AssetContainer` Blueprint class. This class when added to folder can
    handle metadata on it using standard
    :func:`unreal.EditorAssetLibrary.set_metadata_tag()` and
    :func:`unreal.EditorAssetLibrary.get_metadata_tag_values()`. It also
    stores and monitor all changes in assets in path where it resides. List of
    those assets is available as `assets` property.

    This is list of strings starting with asset type and ending with its path:
    `Material /Game/Ayon/Test/TestMaterial.TestMaterial`

    """
    # 1 - create directory for container
    root = "/Game"
    container_name = f"{name}{suffix}"
    new_name = move_assets_to_path(root, container_name, nodes)

    # 2 - create Asset Container there
    path = f"{root}/{new_name}"
    create_container(container=container_name, path=path)

    namespace = path

    data = {
        "schema": "ayon:container-2.0",
        "id": AYON_CONTAINER_ID,
        "name": new_name,
        "namespace": namespace,
        "loader": str(loader),
        "representation": context["representation"]["id"],
    }
    # 3 - imprint data
    imprint(f"{path}/{container_name}", data)
    return path

install()

Install Unreal configuration for AYON.

Source code in client/ayon_unreal/api/pipeline.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
140
141
142
def install():
    """Install Unreal configuration for AYON."""
    print("-=" * 40)
    logo = '''.
.
                    ·

                   ·∙/
                 ·-∙•∙-·
              / \\  /∙·  / \\
\\  │  /   ∙
              \\   \\ · /   /
              \\\\   ∙ ∙  //
                \\\\/   \\//
                   ___
                  │   │
                  │   │
                  │   │
                  │___│


         ·-─═─-∙ A Y O N ∙-─═─-·
                by  YNPUT
.
'''
    print(logo)
    print("installing Ayon for Unreal ...")
    print("-=" * 40)
    logger.info("installing Ayon for Unreal")
    pyblish.api.register_host("unreal")
    pyblish.api.register_plugin_path(str(PUBLISH_PATH))
    register_loader_plugin_path(str(LOAD_PATH))
    register_creator_plugin_path(str(CREATE_PATH))
    register_inventory_action_path(str(INVENTORY_PATH))
    _register_callbacks()
    _register_events()

instantiate(root, name, data, assets=None, suffix='_INS')

Bundles nodes into container.

Marking it with metadata as publishable instance. If assets are provided, they are moved to new path where AyonPublishInstance class asset is created and imprinted with metadata.

This can then be collected for publishing by Pyblish for example.

Parameters:

Name Type Description Default
root str

root path where to create instance container

required
name str

name of the container

required
data dict

data to imprint on container

required
assets list of str

list of asset paths to include in publish instance

None
suffix str

suffix string to append to instance name

'_INS'
Source code in client/ayon_unreal/api/pipeline.py
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
def instantiate(root, name, data, assets=None, suffix="_INS"):
    """Bundles *nodes* into *container*.

    Marking it with metadata as publishable instance. If assets are provided,
    they are moved to new path where `AyonPublishInstance` class asset is
    created and imprinted with metadata.

    This can then be collected for publishing by Pyblish for example.

    Args:
        root (str): root path where to create instance container
        name (str): name of the container
        data (dict): data to imprint on container
        assets (list of str): list of asset paths to include in publish
                              instance
        suffix (str): suffix string to append to instance name

    """
    container_name = f"{name}{suffix}"

    # if we specify assets, create new folder and move them there. If not,
    # just create empty folder
    if assets:
        new_name = move_assets_to_path(root, container_name, assets)
    else:
        new_name = create_folder(root, name)

    path = f"{root}/{new_name}"
    create_publish_instance(instance=container_name, path=path)

    imprint(f"{path}/{container_name}", data)

ls()

List all containers.

List all found in Content Manager of Unreal and return metadata from them. Adding objectName to set.

Source code in client/ayon_unreal/api/pipeline.py
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
def ls():
    """List all containers.

    List all found in *Content Manager* of Unreal and return
    metadata from them. Adding `objectName` to set.

    """
    ar = unreal.AssetRegistryHelpers.get_asset_registry()
    # UE 5.1 changed how class name is specified
    class_name = ["/Script/Ayon", "AyonAssetContainer"] if UNREAL_VERSION.major == 5 and UNREAL_VERSION.minor > 0 else "AyonAssetContainer"  # noqa
    ayon_containers = ar.get_assets_by_class(class_name, True)

    # get_asset_by_class returns AssetData. To get all metadata we need to
    # load asset. get_tag_values() work only on metadata registered in
    # Asset Registry Project settings (and there is no way to set it with
    # python short of editing ini configuration file).
    for asset_data in ayon_containers:
        asset = asset_data.get_asset()
        data = unreal.EditorAssetLibrary.get_metadata_tag_values(asset)
        data["objectName"] = asset_data.asset_name
        yield cast_map_to_str_dict(data)

maintained_selection()

Stub to be either implemented or replaced.

This is needed for old publisher implementation, but it is not supported (yet) in UE.

Source code in client/ayon_unreal/api/pipeline.py
814
815
816
817
818
819
820
821
822
823
824
@contextmanager
def maintained_selection():
    """Stub to be either implemented or replaced.

    This is needed for old publisher implementation, but
    it is not supported (yet) in UE.
    """
    try:
        yield
    finally:
        pass

publish()

Shorthand to publish from within host.

Source code in client/ayon_unreal/api/pipeline.py
230
231
232
233
234
def publish():
    """Shorthand to publish from within host."""
    import pyblish.util

    return pyblish.util.publish()

show_tools_dialog()

Show dialog with tools.

Dialog will stay visible.

Source code in client/ayon_unreal/api/pipeline.py
339
340
341
342
343
344
345
346
def show_tools_dialog():
    """Show dialog with tools.

    Dialog will stay visible.
    """
    from ayon_unreal.api import tools_ui

    tools_ui.show_tools_dialog()

show_tools_popup()

Show popup with tools.

Popup will disappear on click or losing focus.

Source code in client/ayon_unreal/api/pipeline.py
329
330
331
332
333
334
335
336
def show_tools_popup():
    """Show popup with tools.

    Popup will disappear on click or losing focus.
    """
    from ayon_unreal.api import tools_ui

    tools_ui.show_tools_popup()

uninstall()

Uninstall Unreal configuration for Ayon.

Source code in client/ayon_unreal/api/pipeline.py
145
146
147
148
149
150
def uninstall():
    """Uninstall Unreal configuration for Ayon."""
    pyblish.api.deregister_plugin_path(str(PUBLISH_PATH))
    deregister_loader_plugin_path(str(LOAD_PATH))
    deregister_creator_plugin_path(str(CREATE_PATH))
    deregister_inventory_action_path(str(INVENTORY_PATH))