Merge branch 'develop' into enhancement/OP-5548_Fusion-saver-settings

This commit is contained in:
Jakub Ježek 2023-05-15 21:24:56 +02:00 committed by GitHub
commit 9a52475075
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
224 changed files with 4398 additions and 1436 deletions

View file

@ -35,6 +35,9 @@ body:
label: Version
description: What version are you running? Look to OpenPype Tray
options:
- 3.15.7-nightly.3
- 3.15.7-nightly.2
- 3.15.7-nightly.1
- 3.15.6
- 3.15.6-nightly.3
- 3.15.6-nightly.2
@ -132,9 +135,6 @@ body:
- 3.14.1-nightly.4
- 3.14.1-nightly.3
- 3.14.1-nightly.2
- 3.14.1-nightly.1
- 3.14.0
- 3.14.0-nightly.1
validations:
required: true
- type: dropdown

6
.gitignore vendored
View file

@ -112,3 +112,9 @@ tools/run_eventserver.*
tools/dev_*
.github_changelog_generator
# Addons
########
/openpype/addons/*
!/openpype/addons/README.md

View file

@ -0,0 +1,3 @@
This directory is for storing external addons that needs to be included in the pipeline when distributed.
The directory is ignored by Git, but included in the zip and installation files.

View file

@ -1,29 +1,39 @@
import pyblish.api
from openpype.pipeline import OptionalPyblishPluginMixin
from openpype.pipeline import KnownPublishError
class FusionIncrementCurrentFile(pyblish.api.ContextPlugin):
class FusionIncrementCurrentFile(
pyblish.api.ContextPlugin, OptionalPyblishPluginMixin
):
"""Increment the current file.
Saves the current file with an increased version number.
"""
label = "Increment current file"
label = "Increment workfile version"
order = pyblish.api.IntegratorOrder + 9.0
hosts = ["fusion"]
families = ["workfile"]
optional = True
def process(self, context):
if not self.is_active(context.data):
return
from openpype.lib import version_up
from openpype.pipeline.publish import get_errored_plugins_from_context
errored_plugins = get_errored_plugins_from_context(context)
if any(plugin.__name__ == "FusionSubmitDeadline"
for plugin in errored_plugins):
raise RuntimeError("Skipping incrementing current file because "
"submission to render farm failed.")
if any(
plugin.__name__ == "FusionSubmitDeadline"
for plugin in errored_plugins
):
raise KnownPublishError(
"Skipping incrementing current file because "
"submission to render farm failed."
)
comp = context.data.get("currentComp")
assert comp, "Must have comp"

View file

@ -1,12 +1,17 @@
import pyblish.api
from openpype.pipeline.publish import RepairAction
from openpype.pipeline import PublishValidationError
from openpype.pipeline import (
publish,
OptionalPyblishPluginMixin,
PublishValidationError,
)
from openpype.hosts.fusion.api.action import SelectInvalidAction
class ValidateBackgroundDepth(pyblish.api.InstancePlugin):
class ValidateBackgroundDepth(
pyblish.api.InstancePlugin, OptionalPyblishPluginMixin
):
"""Validate if all Background tool are set to float32 bit"""
order = pyblish.api.ValidatorOrder
@ -15,11 +20,10 @@ class ValidateBackgroundDepth(pyblish.api.InstancePlugin):
families = ["render"]
optional = True
actions = [SelectInvalidAction, RepairAction]
actions = [SelectInvalidAction, publish.RepairAction]
@classmethod
def get_invalid(cls, instance):
context = instance.context
comp = context.data.get("currentComp")
assert comp, "Must have Comp object"
@ -31,12 +35,16 @@ class ValidateBackgroundDepth(pyblish.api.InstancePlugin):
return [i for i in backgrounds if i.GetInput("Depth") != 4.0]
def process(self, instance):
if not self.is_active(instance.data):
return
invalid = self.get_invalid(instance)
if invalid:
raise PublishValidationError(
"Found {} Backgrounds tools which"
" are not set to float32".format(len(invalid)),
title=self.label)
title=self.label,
)
@classmethod
def repair(cls, instance):

View file

@ -81,7 +81,13 @@ class HoudiniHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost):
# TODO: make sure this doesn't trigger when
# opening with last workfile.
_set_context_settings()
shelves.generate_shelves()
if not IS_HEADLESS:
import hdefereval # noqa, hdefereval is only available in ui mode
# Defer generation of shelves due to issue on Windows where shelf
# initialization during start up delays Houdini UI by minutes
# making it extremely slow to launch.
hdefereval.executeDeferred(shelves.generate_shelves)
if not IS_HEADLESS:
import hdefereval # noqa, hdefereval is only available in ui mode

View file

@ -138,7 +138,7 @@ def get_default_render_folder(project_setting=None):
["default_render_image_folder"])
def set_framerange(start_frame, end_frame):
def set_render_frame_range(start_frame, end_frame):
"""
Note:
Frame range can be specified in different types. Possible values are:
@ -150,10 +150,10 @@ def set_framerange(start_frame, end_frame):
Todo:
Current type is hard-coded, there should be a custom setting for this.
"""
rt.rendTimeType = 4
rt.rendTimeType = 3
if start_frame is not None and end_frame is not None:
frame_range = "{0}-{1}".format(start_frame, end_frame)
rt.rendPickupFrames = frame_range
rt.rendStart = int(start_frame)
rt.rendEnd = int(end_frame)
def get_multipass_setting(project_setting=None):
@ -173,10 +173,16 @@ def set_scene_resolution(width: int, height: int):
None
"""
# make sure the render dialog is closed
# for the update of resolution
# Changing the Render Setup dialog settingsshould be done
# with the actual Render Setup dialog in a closed state.
if rt.renderSceneDialog.isOpen():
rt.renderSceneDialog.close()
rt.renderWidth = width
rt.renderHeight = height
def reset_scene_resolution():
"""Apply the scene resolution from the project definition
@ -243,6 +249,7 @@ def reset_frame_range(fps: bool = True):
frame_end = frame_range["frameEnd"] + int(frame_range["handleEnd"])
frange_cmd = f"animationRange = interval {frame_start} {frame_end}"
rt.execute(frange_cmd)
set_render_frame_range(frame_start, frame_end)
def set_context_setting():
@ -259,6 +266,7 @@ def set_context_setting():
None
"""
reset_scene_resolution()
reset_frame_range()
def get_max_version():

View file

@ -36,8 +36,9 @@ class RenderProducts(object):
container)
context = get_current_project_asset()
startFrame = context["data"].get("frameStart")
endFrame = context["data"].get("frameEnd") + 1
# TODO: change the frame range follows the current render setting
startFrame = int(rt.rendStart)
endFrame = int(rt.rendEnd) + 1
img_fmt = self._project_settings["max"]["RenderSettings"]["image_format"] # noqa
full_render_list = self.beauty_render_product(output_file,

View file

@ -6,7 +6,7 @@ from openpype.pipeline import legacy_io
from openpype.pipeline.context_tools import get_current_project_asset
from openpype.hosts.max.api.lib import (
set_framerange,
set_render_frame_range,
get_current_renderer,
get_default_render_folder
)
@ -68,7 +68,7 @@ class RenderSettings(object):
# Set Frame Range
frame_start = context["data"].get("frame_start")
frame_end = context["data"].get("frame_end")
set_framerange(frame_start, frame_end)
set_render_frame_range(frame_start, frame_end)
# get the production render
renderer_class = get_current_renderer()
renderer = str(renderer_class).split(":")[0]
@ -105,6 +105,9 @@ class RenderSettings(object):
rt.rendSaveFile = True
if rt.renderSceneDialog.isOpen():
rt.renderSceneDialog.close()
def arnold_setup(self):
# get Arnold RenderView run in the background
# for setting up renderable camera

View file

@ -52,6 +52,7 @@ class MaxHost(HostBase, IWorkfileHost, ILoadHost, INewPublisher):
def context_setting():
return lib.set_context_setting()
rt.callbacks.addScript(rt.Name('systemPostNew'),
context_setting)

View file

@ -27,6 +27,11 @@ class CreateRender(plugin.MaxCreator):
# for additional work on the node:
# instance_node = rt.getNodeByName(instance.get("instance_node"))
# make sure the render dialog is closed
# for the update of resolution
# Changing the Render Setup dialog settings should be done
# with the actual Render Setup dialog in a closed state.
# set viewport camera for rendering(mandatory for deadline)
RenderSettings().set_render_camera(sel_obj)
# set output paths for rendering(mandatory for deadline)

View file

@ -46,7 +46,6 @@ class CollectRender(pyblish.api.InstancePlugin):
self.log.debug(f"Setting {version_int} to context.")
context.data["version"] = version_int
# setup the plugin as 3dsmax for the internal renderer
data = {
"subset": instance.name,
@ -59,8 +58,8 @@ class CollectRender(pyblish.api.InstancePlugin):
"source": filepath,
"expectedFiles": render_layer_files,
"plugin": "3dsmax",
"frameStart": context.data['frameStart'],
"frameEnd": context.data['frameEnd'],
"frameStart": int(rt.rendStart),
"frameEnd": int(rt.rendEnd),
"version": version_int,
"farm": True
}

View file

@ -0,0 +1,64 @@
import pyblish.api
from pymxs import runtime as rt
from openpype.pipeline import (
OptionalPyblishPluginMixin
)
from openpype.pipeline.publish import (
RepairAction,
ValidateContentsOrder,
PublishValidationError
)
class ValidateFrameRange(pyblish.api.InstancePlugin,
OptionalPyblishPluginMixin):
"""Validates the frame ranges.
This is an optional validator checking if the frame range on instance
matches the frame range specified for the asset.
It also validates render frame ranges of render layers.
Repair action will change everything to match the asset frame range.
This can be turned off by the artist to allow custom ranges.
"""
label = "Validate Frame Range"
order = ValidateContentsOrder
families = ["maxrender"]
hosts = ["max"]
optional = True
actions = [RepairAction]
def process(self, instance):
if not self.is_active(instance.data):
self.log.info("Skipping validation...")
return
context = instance.context
frame_start = int(context.data.get("frameStart"))
frame_end = int(context.data.get("frameEnd"))
inst_frame_start = int(instance.data.get("frameStart"))
inst_frame_end = int(instance.data.get("frameEnd"))
errors = []
if frame_start != inst_frame_start:
errors.append(
f"Start frame ({inst_frame_start}) on instance does not match " # noqa
f"with the start frame ({frame_start}) set on the asset data. ") # noqa
if frame_end != inst_frame_end:
errors.append(
f"End frame ({inst_frame_end}) on instance does not match "
f"with the end frame ({frame_start}) from the asset data. ")
if errors:
errors.append("You can use repair action to fix it.")
raise PublishValidationError("\n".join(errors))
@classmethod
def repair(cls, instance):
rt.rendStart = instance.context.data.get("frameStart")
rt.rendEnd = instance.context.data.get("frameEnd")

View file

@ -0,0 +1,65 @@
import pyblish.api
from openpype.pipeline import (
PublishValidationError,
OptionalPyblishPluginMixin
)
from pymxs import runtime as rt
from openpype.hosts.max.api.lib import reset_scene_resolution
from openpype.pipeline.context_tools import (
get_current_project_asset,
get_current_project
)
class ValidateResolutionSetting(pyblish.api.InstancePlugin,
OptionalPyblishPluginMixin):
"""Validate the resolution setting aligned with DB"""
order = pyblish.api.ValidatorOrder - 0.01
families = ["maxrender"]
hosts = ["max"]
label = "Validate Resolution Setting"
optional = True
def process(self, instance):
if not self.is_active(instance.data):
return
width, height = self.get_db_resolution(instance)
current_width = rt.renderwidth
current_height = rt.renderHeight
if current_width != width and current_height != height:
raise PublishValidationError("Resolution Setting "
"not matching resolution "
"set on asset or shot.")
if current_width != width:
raise PublishValidationError("Width in Resolution Setting "
"not matching resolution set "
"on asset or shot.")
if current_height != height:
raise PublishValidationError("Height in Resolution Setting "
"not matching resolution set "
"on asset or shot.")
def get_db_resolution(self, instance):
data = ["data.resolutionWidth", "data.resolutionHeight"]
project_resolution = get_current_project(fields=data)
project_resolution_data = project_resolution["data"]
asset_resolution = get_current_project_asset(fields=data)
asset_resolution_data = asset_resolution["data"]
# Set project resolution
project_width = int(
project_resolution_data.get("resolutionWidth", 1920))
project_height = int(
project_resolution_data.get("resolutionHeight", 1080))
width = int(
asset_resolution_data.get("resolutionWidth", project_width))
height = int(
asset_resolution_data.get("resolutionHeight", project_height))
return width, height
@classmethod
def repair(cls, instance):
reset_scene_resolution()

View file

@ -43,7 +43,24 @@ class MayaTemplateBuilder(AbstractTemplateBuilder):
))
cmds.sets(name=PLACEHOLDER_SET, empty=True)
new_nodes = cmds.file(path, i=True, returnNewNodes=True)
new_nodes = cmds.file(
path,
i=True,
returnNewNodes=True,
preserveReferences=True,
loadReferenceDepth="all",
)
# make default cameras non-renderable
default_cameras = [cam for cam in cmds.ls(cameras=True)
if cmds.camera(cam, query=True, startupCamera=True)]
for cam in default_cameras:
if not cmds.attributeQuery("renderable", node=cam, exists=True):
self.log.debug(
"Camera {} has no attribute 'renderable'".format(cam)
)
continue
cmds.setAttr("{}.renderable".format(cam), 0)
cmds.setAttr(PLACEHOLDER_SET + ".hiddenInOutliner", True)

View file

@ -50,7 +50,8 @@ class ValidateShaderName(pyblish.api.InstancePlugin):
asset_name = instance.data.get("asset", None)
# Check the number of connected shadingEngines per shape
r = re.compile(cls.regex)
regex_compile = re.compile(cls.regex)
error_message = "object {0} has invalid shader name {1}"
for shape in shapes:
shading_engines = cmds.listConnections(shape,
destination=True,
@ -60,19 +61,18 @@ class ValidateShaderName(pyblish.api.InstancePlugin):
)
for shader in shaders:
m = r.match(cls.regex, shader)
m = regex_compile.match(shader)
if m is None:
invalid.append(shape)
cls.log.error(
"object {0} has invalid shader name {1}".format(shape,
shader)
)
cls.log.error(error_message.format(shape, shader))
else:
if 'asset' in r.groupindex:
if 'asset' in regex_compile.groupindex:
if m.group('asset') != asset_name:
invalid.append(shape)
cls.log.error(("object {0} has invalid "
"shader name {1}").format(shape,
shader))
message = error_message
message += " with missing asset name \"{2}\""
cls.log.error(
message.format(shape, shader, asset_name)
)
return invalid

View file

@ -4,6 +4,6 @@ Supported Unreal Engine version is 4.26+ (mainly because of major Python changes
### Project naming
Unreal doesn't support project names starting with non-alphabetic character. So names like `123_myProject` are
invalid. If OpenPype detects such name it automatically prepends letter **P** to make it valid name, so `123_myProject`
invalid. If Ayon detects such name it automatically prepends letter **P** to make it valid name, so `123_myProject`
will become `P123_myProject`. There is also soft-limit on project name length to be shorter than 20 characters.
Longer names will issue warning in Unreal Editor that there might be possible side effects.
Longer names will issue warning in Unreal Editor that there might be possible side effects.

View file

@ -1,5 +1,8 @@
import os
from openpype.modules import OpenPypeModule, IHostAddon
from pathlib import Path
from openpype.modules import IHostAddon, OpenPypeModule
from .lib import get_compatible_integration
UNREAL_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
@ -13,15 +16,23 @@ class UnrealAddon(OpenPypeModule, IHostAddon):
def add_implementation_envs(self, env, app):
"""Modify environments to contain all required for implementation."""
# Set OPENPYPE_UNREAL_PLUGIN required for Unreal implementation
# Set AYON_UNREAL_PLUGIN required for Unreal implementation
ue_plugin = "UE_5.0" if app.name[:1] == "5" else "UE_4.7"
ue_version = app.name.replace("-", ".")
unreal_plugin_path = os.path.join(
UNREAL_ROOT_DIR, "integration", ue_plugin, "OpenPype"
UNREAL_ROOT_DIR, "integration", f"UE_{ue_version}", "Ayon"
)
if not env.get("OPENPYPE_UNREAL_PLUGIN") or \
env.get("OPENPYPE_UNREAL_PLUGIN") != unreal_plugin_path:
env["OPENPYPE_UNREAL_PLUGIN"] = unreal_plugin_path
if not Path(unreal_plugin_path).exists():
compatible_versions = get_compatible_integration(
ue_version, Path(UNREAL_ROOT_DIR) / "integration"
)
if compatible_versions:
unreal_plugin_path = compatible_versions[-1] / "Ayon"
unreal_plugin_path = unreal_plugin_path.as_posix()
if not env.get("AYON_UNREAL_PLUGIN") or \
env.get("AYON_UNREAL_PLUGIN") != unreal_plugin_path:
env["AYON_UNREAL_PLUGIN"] = unreal_plugin_path
# Set default environments if are not set via settings
defaults = {

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
"""Unreal Editor OpenPype host API."""
"""Unreal Editor Ayon host API."""
from .plugin import (
UnrealActorCreator,

View file

@ -2,15 +2,15 @@
import unreal # noqa
class OpenPypeUnrealException(Exception):
class AyonUnrealException(Exception):
pass
@unreal.uclass()
class OpenPypeHelpers(unreal.OpenPypeLib):
"""Class wrapping some useful functions for OpenPype.
class AyonHelpers(unreal.AyonLib):
"""Class wrapping some useful functions for Ayon.
This class is extending native BP class in OpenPype Integration Plugin.
This class is extending native BP class in Ayon Integration Plugin.
"""
@ -29,13 +29,13 @@ class OpenPypeHelpers(unreal.OpenPypeLib):
Example:
OpenPypeHelpers().set_folder_color(
AyonHelpers().set_folder_color(
"/Game/Path", unreal.LinearColor(a=1.0, r=1.0, g=0.5, b=0)
)
Note:
This will take effect only after Editor is restarted. I couldn't
find a way to refresh it. Also this saves the color definition
find a way to refresh it. Also, this saves the color definition
into the project config, binding this path with color. So if you
delete this path and later re-create, it will set this color
again.

View file

@ -14,7 +14,7 @@ from openpype.pipeline import (
register_creator_plugin_path,
deregister_loader_plugin_path,
deregister_creator_plugin_path,
AVALON_CONTAINER_ID,
AYON_CONTAINER_ID,
)
from openpype.tools.utils import host_tools
import openpype.hosts.unreal
@ -22,12 +22,13 @@ from openpype.host import HostBase, ILoadHost, IPublishHost
import unreal # noqa
# Rename to Ayon once parent module renames
logger = logging.getLogger("openpype.hosts.unreal")
OPENPYPE_CONTAINERS = "OpenPypeContainers"
CONTEXT_CONTAINER = "OpenPype/context.json"
AYON_CONTAINERS = "AyonContainers"
CONTEXT_CONTAINER = "Ayon/context.json"
UNREAL_VERSION = semver.VersionInfo(
*os.getenv("OPENPYPE_UNREAL_VERSION").split(".")
*os.getenv("AYON_UNREAL_VERSION").split(".")
)
HOST_DIR = os.path.dirname(os.path.abspath(openpype.hosts.unreal.__file__))
@ -53,14 +54,14 @@ class UnrealHost(HostBase, ILoadHost, IPublishHost):
def get_containers(self):
return ls()
def show_tools_popup(self):
@staticmethod
def show_tools_popup():
"""Show tools popup with actions leading to show other tools."""
show_tools_popup()
def show_tools_dialog(self):
@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):
@ -72,9 +73,10 @@ class UnrealHost(HostBase, ILoadHost, IPublishHost):
with open(op_ctx, "w+") as f:
json.dump(data, f)
break
except IOError:
except IOError as e:
if i == attempts - 1:
raise Exception("Failed to write context data. Aborting.")
raise Exception(
"Failed to write context data. Aborting.") from e
unreal.log_warning("Failed to write context data. Retrying...")
i += 1
time.sleep(3)
@ -95,19 +97,30 @@ def install():
print("-=" * 40)
logo = '''.
.
____________
/ \\ __ \\
\\ \\ \\/_\\ \\
\\ \\ _____/ ______
\\ \\ \\___// \\ \\
\\ \\____\\ \\ \\_____\\
\\/_____/ \\/______/ PYPE Club .
·
·/
·--·
/ \\ /· / \\
\\ /
\\ \\ · / /
\\\\ //
\\\\/ \\//
___
___
-·
·-- A Y O N --·
by YNPUT
.
'''
print(logo)
print("installing OpenPype for Unreal ...")
print("installing Ayon for Unreal ...")
print("-=" * 40)
logger.info("installing OpenPype for Unreal")
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))
@ -117,7 +130,7 @@ def install():
def uninstall():
"""Uninstall Unreal configuration for Avalon."""
"""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))
@ -125,14 +138,14 @@ def uninstall():
def _register_callbacks():
"""
TODO: Implement callbacks if supported by UE4
TODO: Implement callbacks if supported by UE
"""
pass
def _register_events():
"""
TODO: Implement callbacks if supported by UE4
TODO: Implement callbacks if supported by UE
"""
pass
@ -146,32 +159,30 @@ def ls():
"""
ar = unreal.AssetRegistryHelpers.get_asset_registry()
# UE 5.1 changed how class name is specified
class_name = ["/Script/OpenPype", "AssetContainer"] if UNREAL_VERSION.major == 5 and UNREAL_VERSION.minor > 0 else "AssetContainer" # noqa
openpype_containers = ar.get_assets_by_class(class_name, True)
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 openpype_containers:
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
data = cast_map_to_str_dict(data)
yield data
yield cast_map_to_str_dict(data)
def ls_inst():
ar = unreal.AssetRegistryHelpers.get_asset_registry()
# UE 5.1 changed how class name is specified
class_name = [
"/Script/OpenPype",
"OpenPypePublishInstance"
"/Script/Ayon",
"AyonPublishInstance"
] if (
UNREAL_VERSION.major == 5
and UNREAL_VERSION.minor > 0
) else "OpenPypePublishInstance" # noqa
) else "AyonPublishInstance" # noqa
instances = ar.get_assets_by_class(class_name, True)
# get_asset_by_class returns AssetData. To get all metadata we need to
@ -182,13 +193,11 @@ def ls_inst():
asset = asset_data.get_asset()
data = unreal.EditorAssetLibrary.get_metadata_tag_values(asset)
data["objectName"] = asset_data.asset_name
data = cast_map_to_str_dict(data)
yield data
yield cast_map_to_str_dict(data)
def parse_container(container):
"""To get data from container, AssetContainer must be loaded.
"""To get data from container, AyonAssetContainer must be loaded.
Args:
container(str): path to container
@ -217,7 +226,7 @@ def containerise(name, namespace, nodes, context, loader=None, suffix="_CON"):
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. OpenPype Integration Plugin is providing way out - Implementing
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
@ -226,30 +235,30 @@ def containerise(name, namespace, nodes, context, loader=None, suffix="_CON"):
those assets is available as `assets` property.
This is list of strings starting with asset type and ending with its path:
`Material /Game/OpenPype/Test/TestMaterial.TestMaterial`
`Material /Game/Ayon/Test/TestMaterial.TestMaterial`
"""
# 1 - create directory for container
root = "/Game"
container_name = "{}{}".format(name, suffix)
container_name = f"{name}{suffix}"
new_name = move_assets_to_path(root, container_name, nodes)
# 2 - create Asset Container there
path = "{}/{}".format(root, new_name)
path = f"{root}/{new_name}"
create_container(container=container_name, path=path)
namespace = path
data = {
"schema": "openpype:container-2.0",
"id": AVALON_CONTAINER_ID,
"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("{}/{}".format(path, container_name), data)
imprint(f"{path}/{container_name}", data)
return path
@ -257,7 +266,7 @@ 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 `OpenPypePublishInstance` class asset is
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.
@ -271,7 +280,7 @@ def instantiate(root, name, data, assets=None, suffix="_INS"):
suffix (str): suffix string to append to instance name
"""
container_name = "{}{}".format(name, suffix)
container_name = f"{name}{suffix}"
# if we specify assets, create new folder and move them there. If not,
# just create empty folder
@ -280,10 +289,10 @@ def instantiate(root, name, data, assets=None, suffix="_INS"):
else:
new_name = create_folder(root, name)
path = "{}/{}".format(root, new_name)
path = f"{root}/{new_name}"
create_publish_instance(instance=container_name, path=path)
imprint("{}/{}".format(path, container_name), data)
imprint(f"{path}/{container_name}", data)
def imprint(node, data):
@ -299,7 +308,7 @@ def imprint(node, data):
loaded_asset, key, str(value)
)
with unreal.ScopedEditorTransaction("OpenPype containerising"):
with unreal.ScopedEditorTransaction("Ayon containerising"):
unreal.EditorAssetLibrary.save_asset(node)
@ -366,11 +375,11 @@ def create_folder(root: str, name: str) -> str:
eal = unreal.EditorAssetLibrary
index = 1
while True:
if eal.does_directory_exist("{}/{}".format(root, name)):
name = "{}{}".format(name, index)
if eal.does_directory_exist(f"{root}/{name}"):
name = f"{name}{index}"
index += 1
else:
eal.make_directory("{}/{}".format(root, name))
eal.make_directory(f"{root}/{name}")
break
return name
@ -403,9 +412,7 @@ def move_assets_to_path(root: str, name: str, assets: List[str]) -> str:
unreal.log(assets)
for asset in assets:
loaded = eal.load_asset(asset)
eal.rename_asset(
asset, "{}/{}/{}".format(root, name, loaded.get_name())
)
eal.rename_asset(asset, f"{root}/{name}/{loaded.get_name()}")
return name
@ -432,17 +439,16 @@ def create_container(container: str, path: str) -> unreal.Object:
)
"""
factory = unreal.AssetContainerFactory()
factory = unreal.AyonAssetContainerFactory()
tools = unreal.AssetToolsHelpers().get_asset_tools()
asset = tools.create_asset(container, path, None, factory)
return asset
return tools.create_asset(container, path, None, factory)
def create_publish_instance(instance: str, path: str) -> unreal.Object:
"""Helper function to create OpenPype Publish Instance on given path.
"""Helper function to create Ayon Publish Instance on given path.
This behaves similarly as :func:`create_openpype_container`.
This behaves similarly as :func:`create_ayon_container`.
Args:
path (str): Path where to create Publish Instance.
@ -460,10 +466,9 @@ def create_publish_instance(instance: str, path: str) -> unreal.Object:
)
"""
factory = unreal.OpenPypePublishInstanceFactory()
factory = unreal.AyonPublishInstanceFactory()
tools = unreal.AssetToolsHelpers().get_asset_tools()
asset = tools.create_asset(instance, path, None, factory)
return asset
return tools.create_asset(instance, path, None, factory)
def cast_map_to_str_dict(umap) -> dict:
@ -494,11 +499,14 @@ def get_subsequences(sequence: unreal.LevelSequence):
"""
tracks = sequence.get_master_tracks()
subscene_track = None
for t in tracks:
if t.get_class() == unreal.MovieSceneSubTrack.static_class():
subscene_track = t
break
subscene_track = next(
(
t
for t in tracks
if t.get_class() == unreal.MovieSceneSubTrack.static_class()
),
None,
)
if subscene_track is not None and subscene_track.get_sections():
return subscene_track.get_sections()
return []

View file

@ -31,7 +31,7 @@ from openpype.pipeline import (
@six.add_metaclass(ABCMeta)
class UnrealBaseCreator(Creator):
"""Base class for Unreal creator plugins."""
root = "/Game/OpenPype/PublishInstances"
root = "/Game/Ayon/AyonPublishInstances"
suffix = "_INS"
@staticmethod
@ -243,5 +243,5 @@ class UnrealActorCreator(UnrealBaseCreator):
class Loader(LoaderPlugin, ABC):
"""This serves as skeleton for future OpenPype specific functionality"""
"""This serves as skeleton for future Ayon specific functionality"""
pass

View file

@ -51,7 +51,7 @@ def start_rendering():
# instances = pipeline.ls_inst()
instances = [
a for a in assets
if a.get_class().get_name() == "OpenPypePublishInstance"]
if a.get_class().get_name() == "AyonPublishInstance"]
inst_data = []
@ -64,8 +64,9 @@ def start_rendering():
project = os.environ.get("AVALON_PROJECT")
anatomy = Anatomy(project)
root = anatomy.roots['renders']
except Exception:
raise Exception("Could not find render root in anatomy settings.")
except Exception as e:
raise Exception(
"Could not find render root in anatomy settings.") from e
render_dir = f"{root}/{project}"
@ -121,7 +122,7 @@ def start_rendering():
job = queue.allocate_new_job(unreal.MoviePipelineExecutorJob)
job.sequence = unreal.SoftObjectPath(i["master_sequence"])
job.map = unreal.SoftObjectPath(i["master_level"])
job.author = "OpenPype"
job.author = "Ayon"
# If we have a saved configuration, copy it to the job.
if config:
@ -129,7 +130,7 @@ def start_rendering():
# User data could be used to pass data to the job, that can be
# read in the job's OnJobFinished callback. We could,
# for instance, pass the AvalonPublishInstance's path to the job.
# for instance, pass the AyonPublishInstance's path to the job.
# job.user_data = ""
output_dir = render_setting.get('output')

View file

@ -64,7 +64,7 @@ class ToolsDialog(QtWidgets.QDialog):
def __init__(self, *args, **kwargs):
super(ToolsDialog, self).__init__(*args, **kwargs)
self.setWindowTitle("OpenPype tools")
self.setWindowTitle("Ayon tools")
icon = QtGui.QIcon(resources.get_openpype_icon_filepath())
self.setWindowIcon(icon)

View file

@ -186,15 +186,15 @@ class UnrealPrelaunchHook(PreLaunchHook):
project_path.mkdir(parents=True, exist_ok=True)
# Set "OPENPYPE_UNREAL_PLUGIN" to current process environment for
# Set "AYON_UNREAL_PLUGIN" to current process environment for
# execution of `create_unreal_project`
if self.launch_context.env.get("OPENPYPE_UNREAL_PLUGIN"):
if self.launch_context.env.get("AYON_UNREAL_PLUGIN"):
self.log.info((
f"{self.signature} using OpenPype plugin from "
f"{self.launch_context.env.get('OPENPYPE_UNREAL_PLUGIN')}"
f"{self.signature} using Ayon plugin from "
f"{self.launch_context.env.get('AYON_UNREAL_PLUGIN')}"
))
env_key = "OPENPYPE_UNREAL_PLUGIN"
env_key = "AYON_UNREAL_PLUGIN"
if self.launch_context.env.get(env_key):
os.environ[env_key] = self.launch_context.env[env_key]
@ -213,7 +213,7 @@ class UnrealPrelaunchHook(PreLaunchHook):
engine_path,
project_path)
self.launch_context.env["OPENPYPE_UNREAL_VERSION"] = engine_version
self.launch_context.env["AYON_UNREAL_VERSION"] = engine_version
# Append project file to launch arguments
self.launch_context.launch_args.append(
f"\"{project_file.as_posix()}\"")

View file

@ -0,0 +1,10 @@
# Building the plugin
In order to successfully build the plugin, make sure that the path to the UnrealBuildTool.exe is specified correctly.
After the UBT path specify for which platform it will be compiled. in the -Project parameter, specify the path to the
CommandletProject.uproject file. Next the build type has to be specified (DebugGame, Development, Package, etc.) and then the -TargetType (Editor, Runtime, etc.)
`BuildPlugin_[Ver].bat` runs the building process in the background. If you want to show the progress inside the
command prompt, use the `BuildPlugin_[Ver]_Window.bat` file.

View file

@ -0,0 +1,23 @@
{
"FileVersion": 3,
"Version": 1,
"VersionName": "1.0",
"FriendlyName": "Ayon",
"Description": "Ayon Integration",
"Category": "Ayon.Integration",
"CreatedBy": "Ondrej Samohel",
"CreatedByURL": "https://ayon.ynput.io",
"DocsURL": "https://ayon.ynput.io/docs/artist_hosts_unreal",
"MarketplaceURL": "",
"SupportURL": "https://ynput.io/",
"EngineVersion": "4.27",
"CanContainContent": true,
"Installed": true,
"Modules": [
{
"Name": "Ayon",
"Type": "Editor",
"LoadingPhase": "Default"
}
]
}

View file

@ -0,0 +1,2 @@
[/Script/Ayon.AyonSettings]
FolderColor=(R=91,G=197,B=220,A=255)

View file

@ -0,0 +1,30 @@
import unreal
ayon_detected = True
try:
from openpype.pipeline import install_host
from openpype.hosts.unreal.api import UnrealHost
ayon_host = UnrealHost()
except ImportError as exc:
ayon_host = None
ayon_detected = False
unreal.log_error(f"OpenPype: cannot load Ayon [ {exc} ]")
if ayon_detected:
install_host(ayon_host)
@unreal.uclass()
class AyonIntegration(unreal.AyonPythonBridge):
@unreal.ufunction(override=True)
def RunInPython_Popup(self):
unreal.log_warning("Ayon: showing tools popup")
if ayon_detected:
ayon_host.show_tools_popup()
@unreal.ufunction(override=True)
def RunInPython_Dialog(self):
unreal.log_warning("Ayon: showing tools dialog")
if ayon_detected:
ayon_host.show_tools_dialog()

View file

@ -0,0 +1,3 @@
# Ayon Unreal Integration plugin - UE 4.x
This is plugin for Unreal Editor, creating menu for [Ayon](https://github.com/ynput/OpenPype) tools to run.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -2,35 +2,37 @@
using UnrealBuildTool;
public class OpenPype : ModuleRules
public class Ayon : ModuleRules
{
public OpenPype(ReadOnlyTargetRules Target) : base(Target)
public Ayon(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicIncludePaths.AddRange(
new string[] {
new string[]
{
// ... add public include paths required here ...
}
);
);
PrivateIncludePaths.AddRange(
new string[] {
new string[]
{
// ... add other private include paths required here ...
}
);
);
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
// ... add other public dependencies that you statically link with here ...
}
);
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
@ -46,14 +48,14 @@ public class OpenPype : ModuleRules
"AssetTools"
// ... add private dependencies that you statically link with here ...
}
);
);
DynamicallyLoadedModuleNames.AddRange(
new string[]
{
// ... add any modules that your module loads dynamically here ...
}
);
);
}
}
}

View file

@ -1,25 +1,26 @@
// Copyright 2023, Ayon, All rights reserved.
#include "OpenPype.h"
#include "Ayon.h"
#include "ISettingsContainer.h"
#include "ISettingsModule.h"
#include "ISettingsSection.h"
#include "LevelEditor.h"
#include "OpenPypePythonBridge.h"
#include "OpenPypeSettings.h"
#include "OpenPypeStyle.h"
#include "AyonPythonBridge.h"
#include "AyonSettings.h"
#include "AyonStyle.h"
#include "Modules/ModuleManager.h"
static const FName OpenPypeTabName("OpenPype");
static const FName AyonTabName("Ayon");
#define LOCTEXT_NAMESPACE "FOpenPypeModule"
#define LOCTEXT_NAMESPACE "FAyonModule"
// This function is triggered when the plugin is staring up
void FOpenPypeModule::StartupModule()
void FAyonModule::StartupModule()
{
if (!IsRunningCommandlet()) {
FOpenPypeStyle::Initialize();
FOpenPypeStyle::SetIcon("Logo", "openpype40");
FAyonStyle::Initialize();
FAyonStyle::SetIcon("Logo", "ayon40");
// Create the Extender that will add content to the menu
FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor");
@ -31,13 +32,13 @@ void FOpenPypeModule::StartupModule()
"LevelEditor",
EExtensionHook::After,
NULL,
FMenuExtensionDelegate::CreateRaw(this, &FOpenPypeModule::AddMenuEntry)
FMenuExtensionDelegate::CreateRaw(this, &FAyonModule::AddMenuEntry)
);
ToolbarExtender->AddToolBarExtension(
"Settings",
EExtensionHook::After,
NULL,
FToolBarExtensionDelegate::CreateRaw(this, &FOpenPypeModule::AddToobarEntry));
FToolBarExtensionDelegate::CreateRaw(this, &FAyonModule::AddToobarEntry));
LevelEditorModule.GetMenuExtensibilityManager()->AddExtender(MenuExtender);
@ -47,56 +48,56 @@ void FOpenPypeModule::StartupModule()
}
}
void FOpenPypeModule::ShutdownModule()
void FAyonModule::ShutdownModule()
{
FOpenPypeStyle::Shutdown();
FAyonStyle::Shutdown();
}
void FOpenPypeModule::AddMenuEntry(FMenuBuilder& MenuBuilder)
void FAyonModule::AddMenuEntry(FMenuBuilder& MenuBuilder)
{
// Create Section
MenuBuilder.BeginSection("OpenPype", TAttribute<FText>(FText::FromString("OpenPype")));
MenuBuilder.BeginSection("Ayon", TAttribute<FText>(FText::FromString("Ayon")));
{
// Create a Submenu inside of the Section
MenuBuilder.AddMenuEntry(
FText::FromString("Tools..."),
FText::FromString("Pipeline tools"),
FSlateIcon(FOpenPypeStyle::GetStyleSetName(), "OpenPype.Logo"),
FUIAction(FExecuteAction::CreateRaw(this, &FOpenPypeModule::MenuPopup))
FSlateIcon(FAyonStyle::GetStyleSetName(), "Ayon.Logo"),
FUIAction(FExecuteAction::CreateRaw(this, &FAyonModule::MenuPopup))
);
MenuBuilder.AddMenuEntry(
FText::FromString("Tools dialog..."),
FText::FromString("Pipeline tools dialog"),
FSlateIcon(FOpenPypeStyle::GetStyleSetName(), "OpenPype.Logo"),
FUIAction(FExecuteAction::CreateRaw(this, &FOpenPypeModule::MenuDialog))
FSlateIcon(FAyonStyle::GetStyleSetName(), "Ayon.Logo"),
FUIAction(FExecuteAction::CreateRaw(this, &FAyonModule::MenuDialog))
);
}
MenuBuilder.EndSection();
}
void FOpenPypeModule::AddToobarEntry(FToolBarBuilder& ToolbarBuilder)
void FAyonModule::AddToobarEntry(FToolBarBuilder& ToolbarBuilder)
{
ToolbarBuilder.BeginSection(TEXT("OpenPype"));
ToolbarBuilder.BeginSection(TEXT("Ayon"));
{
ToolbarBuilder.AddToolBarButton(
FUIAction(
FExecuteAction::CreateRaw(this, &FOpenPypeModule::MenuPopup),
FExecuteAction::CreateRaw(this, &FAyonModule::MenuPopup),
NULL,
FIsActionChecked()
),
NAME_None,
LOCTEXT("OpenPype_label", "OpenPype"),
LOCTEXT("OpenPype_tooltip", "OpenPype Tools"),
FSlateIcon(FOpenPypeStyle::GetStyleSetName(), "OpenPype.Logo")
LOCTEXT("Ayon_label", "Ayon"),
LOCTEXT("Ayon_tooltip", "Ayon Tools"),
FSlateIcon(FAyonStyle::GetStyleSetName(), "Ayon.Logo")
);
}
ToolbarBuilder.EndSection();
}
void FOpenPypeModule::RegisterSettings()
void FAyonModule::RegisterSettings()
{
ISettingsModule& SettingsModule = FModuleManager::LoadModuleChecked<ISettingsModule>("Settings");
@ -104,10 +105,10 @@ void FOpenPypeModule::RegisterSettings()
// TODO: After the movement of the plugin from the game to editor, it might be necessary to move this!
ISettingsContainerPtr SettingsContainer = SettingsModule.GetContainer("Project");
UOpenPypeSettings* Settings = GetMutableDefault<UOpenPypeSettings>();
UAyonSettings* Settings = GetMutableDefault<UAyonSettings>();
// Register the settings
ISettingsSectionPtr SettingsSection = SettingsModule.RegisterSettings("Project", "OpenPype", "General",
ISettingsSectionPtr SettingsSection = SettingsModule.RegisterSettings("Project", "Ayon", "General",
LOCTEXT("RuntimeGeneralSettingsName",
"General"),
LOCTEXT("RuntimeGeneralSettingsDescription",
@ -119,13 +120,13 @@ void FOpenPypeModule::RegisterSettings()
// validate those or just act to settings changes.
if (SettingsSection.IsValid())
{
SettingsSection->OnModified().BindRaw(this, &FOpenPypeModule::HandleSettingsSaved);
SettingsSection->OnModified().BindRaw(this, &FAyonModule::HandleSettingsSaved);
}
}
bool FOpenPypeModule::HandleSettingsSaved()
bool FAyonModule::HandleSettingsSaved()
{
UOpenPypeSettings* Settings = GetMutableDefault<UOpenPypeSettings>();
UAyonSettings* Settings = GetMutableDefault<UAyonSettings>();
bool ResaveSettings = false;
// You can put any validation code in here and resave the settings in case an invalid
@ -140,16 +141,16 @@ bool FOpenPypeModule::HandleSettingsSaved()
}
void FOpenPypeModule::MenuPopup()
void FAyonModule::MenuPopup()
{
UOpenPypePythonBridge* bridge = UOpenPypePythonBridge::Get();
UAyonPythonBridge* bridge = UAyonPythonBridge::Get();
bridge->RunInPython_Popup();
}
void FOpenPypeModule::MenuDialog()
void FAyonModule::MenuDialog()
{
UOpenPypePythonBridge* bridge = UOpenPypePythonBridge::Get();
UAyonPythonBridge* bridge = UAyonPythonBridge::Get();
bridge->RunInPython_Dialog();
}
IMPLEMENT_MODULE(FOpenPypeModule, OpenPype)
IMPLEMENT_MODULE(FAyonModule, Ayon)

View file

@ -0,0 +1,114 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "AyonAssetContainer.h"
#include "AssetRegistryModule.h"
#include "Misc/PackageName.h"
#include "Containers/UnrealString.h"
UAyonAssetContainer::UAyonAssetContainer(const FObjectInitializer& ObjectInitializer)
: UAssetUserData(ObjectInitializer)
{
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
FString path = UAyonAssetContainer::GetPathName();
UE_LOG(LogTemp, Warning, TEXT("UAyonAssetContainer %s"), *path);
FARFilter Filter;
Filter.PackagePaths.Add(FName(*path));
AssetRegistryModule.Get().OnAssetAdded().AddUObject(this, &UAyonAssetContainer::OnAssetAdded);
AssetRegistryModule.Get().OnAssetRemoved().AddUObject(this, &UAyonAssetContainer::OnAssetRemoved);
AssetRegistryModule.Get().OnAssetRenamed().AddUObject(this, &UAyonAssetContainer::OnAssetRenamed);
}
void UAyonAssetContainer::OnAssetAdded(const FAssetData& AssetData)
{
TArray<FString> split;
// get directory of current container
FString selfFullPath = UAyonAssetContainer::GetPathName();
FString selfDir = FPackageName::GetLongPackagePath(*selfFullPath);
// get asset path and class
FString assetPath = AssetData.GetFullName();
FString assetFName = AssetData.AssetClass.ToString();
// split path
assetPath.ParseIntoArray(split, TEXT(" "), true);
FString assetDir = FPackageName::GetLongPackagePath(*split[1]);
// take interest only in paths starting with path of current container
if (assetDir.StartsWith(*selfDir))
{
// exclude self
if (assetFName != "AyonAssetContainer")
{
assets.Add(assetPath);
assetsData.Add(AssetData);
UE_LOG(LogTemp, Log, TEXT("%s: asset added to %s"), *selfFullPath, *selfDir);
}
}
}
void UAyonAssetContainer::OnAssetRemoved(const FAssetData& AssetData)
{
TArray<FString> split;
// get directory of current container
FString selfFullPath = UAyonAssetContainer::GetPathName();
FString selfDir = FPackageName::GetLongPackagePath(*selfFullPath);
// get asset path and class
FString assetPath = AssetData.GetFullName();
FString assetFName = AssetData.AssetClass.ToString();
// split path
assetPath.ParseIntoArray(split, TEXT(" "), true);
FString assetDir = FPackageName::GetLongPackagePath(*split[1]);
// take interest only in paths starting with path of current container
FString path = UAyonAssetContainer::GetPathName();
FString lpp = FPackageName::GetLongPackagePath(*path);
if (assetDir.StartsWith(*selfDir))
{
// exclude self
if (assetFName != "AyonAssetContainer")
{
// UE_LOG(LogTemp, Warning, TEXT("%s: asset removed"), *lpp);
assets.Remove(assetPath);
assetsData.Remove(AssetData);
}
}
}
void UAyonAssetContainer::OnAssetRenamed(const FAssetData& AssetData, const FString& str)
{
TArray<FString> split;
// get directory of current container
FString selfFullPath = UAyonAssetContainer::GetPathName();
FString selfDir = FPackageName::GetLongPackagePath(*selfFullPath);
// get asset path and class
FString assetPath = AssetData.GetFullName();
FString assetFName = AssetData.AssetClass.ToString();
// split path
assetPath.ParseIntoArray(split, TEXT(" "), true);
FString assetDir = FPackageName::GetLongPackagePath(*split[1]);
if (assetDir.StartsWith(*selfDir))
{
// exclude self
if (assetFName != "AyonAssetContainer")
{
assets.Remove(str);
assets.Add(assetPath);
assetsData.Remove(AssetData);
// UE_LOG(LogTemp, Warning, TEXT("%s: asset renamed %s"), *lpp, *str);
}
}
}

View file

@ -0,0 +1,20 @@
#include "AyonAssetContainerFactory.h"
#include "AyonAssetContainer.h"
UAyonAssetContainerFactory::UAyonAssetContainerFactory(const FObjectInitializer& ObjectInitializer)
: UFactory(ObjectInitializer)
{
SupportedClass = UAyonAssetContainer::StaticClass();
bCreateNew = false;
bEditorImport = true;
}
UObject* UAyonAssetContainerFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn)
{
UAyonAssetContainer* AssetContainer = NewObject<UAyonAssetContainer>(InParent, Class, Name, Flags);
return AssetContainer;
}
bool UAyonAssetContainerFactory::ShouldShowInNewMenu() const {
return false;
}

View file

@ -1,5 +1,5 @@
// Copyright 2023, Ayon, All rights reserved.
#include "OpenPypeLib.h"
#include "AyonLib.h"
#include "AssetViewUtils.h"
#include "Misc/Paths.h"
@ -13,7 +13,7 @@
* @warning This color will appear only after Editor restart. Is there a better way?
*/
bool UOpenPypeLib::SetFolderColor(const FString& FolderPath, const FLinearColor& FolderColor, const bool& bForceAdd)
bool UAyonLib::SetFolderColor(const FString& FolderPath, const FLinearColor& FolderColor, const bool& bForceAdd)
{
if (AssetViewUtils::DoesFolderExist(FolderPath))
{
@ -31,11 +31,11 @@ bool UOpenPypeLib::SetFolderColor(const FString& FolderPath, const FLinearColor&
}
/**
* Returns all properties on given object
* Returns all poperties on given object
* @param cls - class
* @return TArray of properties
*/
TArray<FString> UOpenPypeLib::GetAllProperties(UClass* cls)
TArray<FString> UAyonLib::GetAllProperties(UClass* cls)
{
TArray<FString> Ret;
if (cls != nullptr)

View file

@ -0,0 +1,203 @@
// Copyright 2023, Ayon, All rights reserved.
// Deprecation warning: this is left here just for backwards compatibility
// and will be removed in next versions of Ayon.
#pragma once
#include "AyonPublishInstance.h"
#include "AssetRegistryModule.h"
#include "AyonLib.h"
#include "AyonSettings.h"
#include "Framework/Notifications/NotificationManager.h"
#include "Widgets/Notifications/SNotificationList.h"
//Moves all the invalid pointers to the end to prepare them for the shrinking
#define REMOVE_INVALID_ENTRIES(VAR) VAR.CompactStable(); \
VAR.Shrink();
UAyonPublishInstance::UAyonPublishInstance(const FObjectInitializer& ObjectInitializer)
: UPrimaryDataAsset(ObjectInitializer)
{
const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<
FAssetRegistryModule>("AssetRegistry");
const FPropertyEditorModule& PropertyEditorModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>(
"PropertyEditor");
FString Left, Right;
GetPathName().Split("/" + GetName(), &Left, &Right);
FARFilter Filter;
Filter.PackagePaths.Emplace(FName(Left));
TArray<FAssetData> FoundAssets;
AssetRegistryModule.GetRegistry().GetAssets(Filter, FoundAssets);
for (const FAssetData& AssetData : FoundAssets)
OnAssetCreated(AssetData);
REMOVE_INVALID_ENTRIES(AssetDataInternal)
REMOVE_INVALID_ENTRIES(AssetDataExternal)
AssetRegistryModule.Get().OnAssetAdded().AddUObject(this, &UAyonPublishInstance::OnAssetCreated);
AssetRegistryModule.Get().OnAssetRemoved().AddUObject(this, &UAyonPublishInstance::OnAssetRemoved);
AssetRegistryModule.Get().OnAssetUpdated().AddUObject(this, &UAyonPublishInstance::OnAssetUpdated);
#ifdef WITH_EDITOR
ColorAyonDirs();
#endif
}
void UAyonPublishInstance::OnAssetCreated(const FAssetData& InAssetData)
{
TArray<FString> split;
UObject* Asset = InAssetData.GetAsset();
if (!IsValid(Asset))
{
UE_LOG(LogAssetData, Warning, TEXT("Asset \"%s\" is not valid! Skipping the addition."),
*InAssetData.ObjectPath.ToString());
return;
}
const bool result = IsUnderSameDir(Asset) && Cast<UAyonPublishInstance>(Asset) == nullptr;
if (result)
{
if (AssetDataInternal.Emplace(Asset).IsValidId())
{
UE_LOG(LogTemp, Log, TEXT("Added an Asset to PublishInstance - Publish Instance: %s, Asset %s"),
*this->GetName(), *Asset->GetName());
}
}
}
void UAyonPublishInstance::OnAssetRemoved(const FAssetData& InAssetData)
{
if (Cast<UAyonPublishInstance>(InAssetData.GetAsset()) == nullptr)
{
if (AssetDataInternal.Contains(nullptr))
{
AssetDataInternal.Remove(nullptr);
REMOVE_INVALID_ENTRIES(AssetDataInternal)
}
else
{
AssetDataExternal.Remove(nullptr);
REMOVE_INVALID_ENTRIES(AssetDataExternal)
}
}
}
void UAyonPublishInstance::OnAssetUpdated(const FAssetData& InAssetData)
{
REMOVE_INVALID_ENTRIES(AssetDataInternal);
REMOVE_INVALID_ENTRIES(AssetDataExternal);
}
bool UAyonPublishInstance::IsUnderSameDir(const UObject* InAsset) const
{
FString ThisLeft, ThisRight;
this->GetPathName().Split(this->GetName(), &ThisLeft, &ThisRight);
return InAsset->GetPathName().StartsWith(ThisLeft);
}
#ifdef WITH_EDITOR
void UAyonPublishInstance::ColorAyonDirs()
{
FString PathName = this->GetPathName();
//Check whether the path contains the defined Ayon folder
if (!PathName.Contains(TEXT("Ayon"))) return;
//Get the base path for open pype
FString PathLeft, PathRight;
PathName.Split(FString("Ayon"), &PathLeft, &PathRight);
if (PathLeft.IsEmpty() || PathRight.IsEmpty())
{
UE_LOG(LogAssetData, Error, TEXT("Failed to retrieve the base Ayon directory!"))
return;
}
PathName.RemoveFromEnd(PathRight, ESearchCase::CaseSensitive);
//Get the current settings
const UAyonSettings* Settings = GetMutableDefault<UAyonSettings>();
//Color the base folder
UAyonLib::SetFolderColor(PathName, Settings->GetFolderFColor(), false);
//Get Sub paths, iterate through them and color them according to the folder color in UAyonSettings
const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(
"AssetRegistry");
TArray<FString> PathList;
AssetRegistryModule.Get().GetSubPaths(PathName, PathList, true);
if (PathList.Num() > 0)
{
for (const FString& Path : PathList)
{
UAyonLib::SetFolderColor(Path, Settings->GetFolderFColor(), false);
}
}
}
void UAyonPublishInstance::SendNotification(const FString& Text) const
{
FNotificationInfo Info{FText::FromString(Text)};
Info.bFireAndForget = true;
Info.bUseLargeFont = false;
Info.bUseThrobber = false;
Info.bUseSuccessFailIcons = false;
Info.ExpireDuration = 4.f;
Info.FadeOutDuration = 2.f;
FSlateNotificationManager::Get().AddNotification(Info);
UE_LOG(LogAssetData, Warning,
TEXT(
"Removed duplicated asset from the AssetsDataExternal in Container \"%s\", Asset is already included in the AssetDataInternal!"
), *GetName()
)
}
void UAyonPublishInstance::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);
if (PropertyChangedEvent.ChangeType == EPropertyChangeType::ValueSet &&
PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_CHECKED(
UAyonPublishInstance, AssetDataExternal))
{
// Check for duplicated assets
for (const auto& Asset : AssetDataInternal)
{
if (AssetDataExternal.Contains(Asset))
{
AssetDataExternal.Remove(Asset);
return SendNotification(
"You are not allowed to add assets into AssetDataExternal which are already included in AssetDataInternal!");
}
}
// Check if no UAyonPublishInstance type assets are included
for (const auto& Asset : AssetDataExternal)
{
if (Cast<UAyonPublishInstance>(Asset.Get()) != nullptr)
{
AssetDataExternal.Remove(Asset);
return SendNotification("You are not allowed to add publish instances!");
}
}
}
}
#endif

View file

@ -0,0 +1,23 @@
// Copyright 2023, Ayon, All rights reserved.
// Deprecation warning: this is left here just for backwards compatibility
// and will be removed in next versions of Ayon.
#include "AyonPublishInstanceFactory.h"
#include "AyonPublishInstance.h"
UAyonPublishInstanceFactory::UAyonPublishInstanceFactory(const FObjectInitializer& ObjectInitializer)
: UFactory(ObjectInitializer)
{
SupportedClass = UAyonPublishInstance::StaticClass();
bCreateNew = false;
bEditorImport = true;
}
UObject* UAyonPublishInstanceFactory::FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn)
{
check(InClass->IsChildOf(UAyonPublishInstance::StaticClass()));
return NewObject<UAyonPublishInstance>(InParent, InClass, InName, Flags);
}
bool UAyonPublishInstanceFactory::ShouldShowInNewMenu() const {
return false;
}

View file

@ -0,0 +1,14 @@
// Copyright 2023, Ayon, All rights reserved.
#include "AyonPythonBridge.h"
UAyonPythonBridge* UAyonPythonBridge::Get()
{
TArray<UClass*> AyonPythonBridgeClasses;
GetDerivedClasses(UAyonPythonBridge::StaticClass(), AyonPythonBridgeClasses);
int32 NumClasses = AyonPythonBridgeClasses.Num();
if (NumClasses > 0)
{
return Cast<UAyonPythonBridge>(AyonPythonBridgeClasses[NumClasses - 1]->GetDefaultObject());
}
return nullptr;
};

View file

@ -0,0 +1,20 @@
// Copyright 2023, Ayon, All rights reserved.
#include "AyonSettings.h"
#include "Interfaces/IPluginManager.h"
/**
* Mainly is used for initializing default values if the DefaultAyonSettings.ini file does not exist in the saved config
*/
UAyonSettings::UAyonSettings(const FObjectInitializer& ObjectInitializer)
{
const FString ConfigFilePath = AYON_SETTINGS_FILEPATH;
// This has to be probably in the future set using the UE Reflection system
FColor Color;
GConfig->GetColor(TEXT("/Script/Ayon.AyonSettings"), TEXT("FolderColor"), Color, ConfigFilePath);
FolderColor = Color;
}

View file

@ -0,0 +1,70 @@
// Copyright 2023, Ayon, All rights reserved.
#include "AyonStyle.h"
#include "Framework/Application/SlateApplication.h"
#include "Styling/SlateStyle.h"
#include "Styling/SlateStyleRegistry.h"
TUniquePtr< FSlateStyleSet > FAyonStyle::AyonStyleInstance = nullptr;
void FAyonStyle::Initialize()
{
if (!AyonStyleInstance.IsValid())
{
AyonStyleInstance = Create();
FSlateStyleRegistry::RegisterSlateStyle(*AyonStyleInstance);
}
}
void FAyonStyle::Shutdown()
{
if (AyonStyleInstance.IsValid())
{
FSlateStyleRegistry::UnRegisterSlateStyle(*AyonStyleInstance);
AyonStyleInstance.Reset();
}
}
FName FAyonStyle::GetStyleSetName()
{
static FName StyleSetName(TEXT("AyonStyle"));
return StyleSetName;
}
FName FAyonStyle::GetContextName()
{
static FName ContextName(TEXT("Ayon"));
return ContextName;
}
#define IMAGE_BRUSH(RelativePath, ...) FSlateImageBrush( Style->RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ )
const FVector2D Icon40x40(40.0f, 40.0f);
TUniquePtr< FSlateStyleSet > FAyonStyle::Create()
{
TUniquePtr< FSlateStyleSet > Style = MakeUnique<FSlateStyleSet>(GetStyleSetName());
Style->SetContentRoot(FPaths::EnginePluginsDir() / TEXT("Marketplace/Ayon/Resources"));
return Style;
}
void FAyonStyle::SetIcon(const FString& StyleName, const FString& ResourcePath)
{
FSlateStyleSet* Style = AyonStyleInstance.Get();
FString Name(GetContextName().ToString());
Name = Name + "." + StyleName;
Style->Set(*Name, new FSlateImageBrush(Style->RootToContentDir(ResourcePath, TEXT(".png")), Icon40x40));
FSlateApplication::Get().GetRenderer()->ReloadTextureResources();
}
#undef IMAGE_BRUSH
const ISlateStyle& FAyonStyle::Get()
{
check(AyonStyleInstance);
return *AyonStyleInstance;
}

View file

@ -0,0 +1,41 @@
// Copyright 2023, Ayon, All rights reserved.
#include "Commandlets/AyonActionResult.h"
#include "Logging/Ayon_Log.h"
EAyon_ActionResult::Type& FAyon_ActionResult::GetStatus()
{
return Status;
}
FText& FAyon_ActionResult::GetReason()
{
return Reason;
}
FAyon_ActionResult::FAyon_ActionResult():Status(EAyon_ActionResult::Type::Ok)
{
}
FAyon_ActionResult::FAyon_ActionResult(const EAyon_ActionResult::Type& InEnum):Status(InEnum)
{
TryLog();
}
FAyon_ActionResult::FAyon_ActionResult(const EAyon_ActionResult::Type& InEnum, const FText& InReason):Status(InEnum), Reason(InReason)
{
TryLog();
};
bool FAyon_ActionResult::IsProblem() const
{
return Status != EAyon_ActionResult::Ok;
}
void FAyon_ActionResult::TryLog() const
{
if(IsProblem())
UE_LOG(LogCommandletOPGenerateProject, Error, TEXT("%s"), *Reason.ToString());
}

View file

@ -0,0 +1,141 @@
// Copyright 2023, Ayon, All rights reserved.
#include "Commandlets/Implementations/AyonGenerateProjectCommandlet.h"
#include "Editor.h"
#include "GameProjectUtils.h"
#include "AyonConstants.h"
#include "Commandlets/AyonActionResult.h"
#include "ProjectDescriptor.h"
int32 UAyonGenerateProjectCommandlet::Main(const FString& CommandLineParams)
{
//Parses command line parameters & creates structure FProjectInformation
const FAyonGenerateProjectParams ParsedParams = FAyonGenerateProjectParams(CommandLineParams);
ProjectInformation = ParsedParams.GenerateUEProjectInformation();
//Creates .uproject & other UE files
EVALUATE_AYON_ACTION_RESULT(TryCreateProject());
//Loads created .uproject
EVALUATE_AYON_ACTION_RESULT(TryLoadProjectDescriptor());
//Adds needed plugin to .uproject
AttachPluginsToProjectDescriptor();
//Saves .uproject
EVALUATE_AYON_ACTION_RESULT(TrySave());
//When we are here, there should not be problems in generating Unreal Project for Ayon
return 0;
}
FAyonGenerateProjectParams::FAyonGenerateProjectParams(): FAyonGenerateProjectParams("")
{
}
FAyonGenerateProjectParams::FAyonGenerateProjectParams(const FString& CommandLineParams): CommandLineParams(
CommandLineParams)
{
UCommandlet::ParseCommandLine(*CommandLineParams, Tokens, Switches);
}
FProjectInformation FAyonGenerateProjectParams::GenerateUEProjectInformation() const
{
FProjectInformation ProjectInformation = FProjectInformation();
ProjectInformation.ProjectFilename = GetProjectFileName();
ProjectInformation.bShouldGenerateCode = IsSwitchPresent("GenerateCode");
return ProjectInformation;
}
FString FAyonGenerateProjectParams::TryGetToken(const int32 Index) const
{
return Tokens.IsValidIndex(Index) ? Tokens[Index] : "";
}
FString FAyonGenerateProjectParams::GetProjectFileName() const
{
return TryGetToken(0);
}
bool FAyonGenerateProjectParams::IsSwitchPresent(const FString& Switch) const
{
return INDEX_NONE != Switches.IndexOfByPredicate([&Switch](const FString& Item) -> bool
{
return Item.Equals(Switch);
}
);
}
UAyonGenerateProjectCommandlet::UAyonGenerateProjectCommandlet()
{
LogToConsole = true;
}
FAyon_ActionResult UAyonGenerateProjectCommandlet::TryCreateProject() const
{
FText FailReason;
FText FailLog;
TArray<FString> OutCreatedFiles;
if (!GameProjectUtils::CreateProject(ProjectInformation, FailReason, FailLog, &OutCreatedFiles))
return FAyon_ActionResult(EAyon_ActionResult::ProjectNotCreated, FailReason);
return FAyon_ActionResult();
}
FAyon_ActionResult UAyonGenerateProjectCommandlet::TryLoadProjectDescriptor()
{
FText FailReason;
const bool bLoaded = ProjectDescriptor.Load(ProjectInformation.ProjectFilename, FailReason);
return FAyon_ActionResult(bLoaded ? EAyon_ActionResult::Ok : EAyon_ActionResult::ProjectNotLoaded, FailReason);
}
void UAyonGenerateProjectCommandlet::AttachPluginsToProjectDescriptor()
{
FPluginReferenceDescriptor AyonPluginDescriptor;
AyonPluginDescriptor.bEnabled = true;
AyonPluginDescriptor.Name = AyonConstants::Ayon_PluginName;
ProjectDescriptor.Plugins.Add(AyonPluginDescriptor);
FPluginReferenceDescriptor PythonPluginDescriptor;
PythonPluginDescriptor.bEnabled = true;
PythonPluginDescriptor.Name = AyonConstants::PythonScript_PluginName;
ProjectDescriptor.Plugins.Add(PythonPluginDescriptor);
FPluginReferenceDescriptor SequencerScriptingPluginDescriptor;
SequencerScriptingPluginDescriptor.bEnabled = true;
SequencerScriptingPluginDescriptor.Name = AyonConstants::SequencerScripting_PluginName;
ProjectDescriptor.Plugins.Add(SequencerScriptingPluginDescriptor);
FPluginReferenceDescriptor MovieRenderPipelinePluginDescriptor;
MovieRenderPipelinePluginDescriptor.bEnabled = true;
MovieRenderPipelinePluginDescriptor.Name = AyonConstants::MovieRenderPipeline_PluginName;
ProjectDescriptor.Plugins.Add(MovieRenderPipelinePluginDescriptor);
FPluginReferenceDescriptor EditorScriptingPluginDescriptor;
EditorScriptingPluginDescriptor.bEnabled = true;
EditorScriptingPluginDescriptor.Name = AyonConstants::EditorScriptingUtils_PluginName;
ProjectDescriptor.Plugins.Add(EditorScriptingPluginDescriptor);
}
FAyon_ActionResult UAyonGenerateProjectCommandlet::TrySave()
{
FText FailReason;
const bool bSaved = ProjectDescriptor.Save(ProjectInformation.ProjectFilename, FailReason);
return FAyon_ActionResult(bSaved ? EAyon_ActionResult::Ok : EAyon_ActionResult::ProjectNotSaved, FailReason);
}
FAyonGenerateProjectParams UAyonGenerateProjectCommandlet::ParseParameters(const FString& Params) const
{
FAyonGenerateProjectParams ParamsResult;
TArray<FString> Tokens, Switches;
ParseCommandLine(*Params, Tokens, Switches);
return ParamsResult;
}

View file

@ -1,10 +1,12 @@
// Copyright 2023, Ayon, All rights reserved.
// Deprecation warning: this is left here just for backwards compatibility
// and will be removed in next versions of Ayon.
#pragma once
#include "OpenPypePublishInstance.h"
#include "AssetRegistryModule.h"
#include "OpenPypeLib.h"
#include "OpenPypeSettings.h"
#include "AyonLib.h"
#include "AyonSettings.h"
#include "Framework/Notifications/NotificationManager.h"
#include "Widgets/Notifications/SNotificationList.h"
@ -43,7 +45,7 @@ UOpenPypePublishInstance::UOpenPypePublishInstance(const FObjectInitializer& Obj
#ifdef WITH_EDITOR
ColorOpenPypeDirs();
#endif
}
void UOpenPypePublishInstance::OnAssetCreated(const FAssetData& InAssetData)
@ -124,12 +126,12 @@ void UOpenPypePublishInstance::ColorOpenPypeDirs()
PathName.RemoveFromEnd(PathRight, ESearchCase::CaseSensitive);
//Get the current settings
const UOpenPypeSettings* Settings = GetMutableDefault<UOpenPypeSettings>();
const UAyonSettings* Settings = GetMutableDefault<UAyonSettings>();
//Color the base folder
UOpenPypeLib::SetFolderColor(PathName, Settings->GetFolderFColor(), false);
UAyonLib::SetFolderColor(PathName, Settings->GetFolderFColor(), false);
//Get Sub paths, iterate through them and color them according to the folder color in UOpenPypeSettings
//Get Sub paths, iterate through them and color them according to the folder color in UAyonSettings
const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(
"AssetRegistry");
@ -141,7 +143,7 @@ void UOpenPypePublishInstance::ColorOpenPypeDirs()
{
for (const FString& Path : PathList)
{
UOpenPypeLib::SetFolderColor(Path, Settings->GetFolderFColor(), false);
UAyonLib::SetFolderColor(Path, Settings->GetFolderFColor(), false);
}
}
}

View file

@ -2,10 +2,8 @@
#pragma once
#include "Engine.h"
class FOpenPypeModule : public IModuleInterface
class FAyonModule : public IModuleInterface
{
public:
virtual void StartupModule() override;

View file

@ -0,0 +1,39 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "Engine/AssetUserData.h"
#include "AssetData.h"
#include "AyonAssetContainer.generated.h"
/**
*
*/
UCLASS(Blueprintable)
class AYON_API UAyonAssetContainer : public UAssetUserData
{
GENERATED_BODY()
public:
UAyonAssetContainer(const FObjectInitializer& ObjectInitalizer);
// ~UAyonAssetContainer();
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Assets")
TArray<FString> assets;
// There seems to be no reflection option to expose array of FAssetData
/*
UPROPERTY(Transient, BlueprintReadOnly, Category = "Python", meta=(DisplayName="Assets Data"))
TArray<FAssetData> assetsData;
*/
private:
TArray<FAssetData> assetsData;
void OnAssetAdded(const FAssetData& AssetData);
void OnAssetRemoved(const FAssetData& AssetData);
void OnAssetRenamed(const FAssetData& AssetData, const FString& str);
};

View file

@ -0,0 +1,21 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Factories/Factory.h"
#include "AyonAssetContainerFactory.generated.h"
/**
*
*/
UCLASS()
class AYON_API UAyonAssetContainerFactory : public UFactory
{
GENERATED_BODY()
public:
UAyonAssetContainerFactory(const FObjectInitializer& ObjectInitializer);
virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override;
virtual bool ShouldShowInNewMenu() const override;
};

View file

@ -0,0 +1,15 @@
// Copyright 2023, Ayon, All rights reserved.
#pragma once
#include "CoreMinimal.h"
namespace AyonConstants
{
const FString Ayon_PluginName = "Ayon";
const FString PythonScript_PluginName = "PythonScriptPlugin";
const FString SequencerScripting_PluginName = "SequencerScripting";
const FString MovieRenderPipeline_PluginName = "MovieRenderPipeline";
const FString EditorScriptingUtils_PluginName = "EditorScriptingUtilities";
}

View file

@ -1,12 +1,11 @@
// Copyright 2023, Ayon, All rights reserved.
#pragma once
#include "Engine.h"
#include "OpenPypeLib.generated.h"
#include "AyonLib.generated.h"
UCLASS(Blueprintable)
class OPENPYPE_API UOpenPypeLib : public UBlueprintFunctionLibrary
class AYON_API UAyonLib : public UBlueprintFunctionLibrary
{
GENERATED_BODY()

View file

@ -0,0 +1,103 @@
// Copyright 2023, Ayon, All rights reserved.
// Deprecation warning: this is left here just for backwards compatibility
// and will be removed in next versions of Ayon.
#pragma once
#include "AyonPublishInstance.generated.h"
UCLASS(Blueprintable)
class AYON_API UAyonPublishInstance : public UPrimaryDataAsset
{
GENERATED_UCLASS_BODY()
public:
/**
* Retrieves all the assets which are monitored by the Publish Instance (Monitors assets in the directory which is
* placed in)
*
* @return - Set of UObjects. Careful! They are returning raw pointers. Seems like an issue in UE5
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category="Python")
TSet<UObject*> GetInternalAssets() const
{
//For some reason it can only return Raw Pointers? Seems like an issue which they haven't fixed.
TSet<UObject*> ResultSet;
for (const auto& Asset : AssetDataInternal)
ResultSet.Add(Asset.LoadSynchronous());
return ResultSet;
}
/**
* Retrieves all the assets which have been added manually by the Publish Instance
*
* @return - TSet of assets (UObjects). Careful! They are returning raw pointers. Seems like an issue in UE5
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category="Python")
TSet<UObject*> GetExternalAssets() const
{
//For some reason it can only return Raw Pointers? Seems like an issue which they haven't fixed.
TSet<UObject*> ResultSet;
for (const auto& Asset : AssetDataExternal)
ResultSet.Add(Asset.LoadSynchronous());
return ResultSet;
}
/**
* Function for returning all the assets in the container combined.
*
* @return Returns all the internal and externally added assets into one set (TSet of UObjects). Careful! They are
* returning raw pointers. Seems like an issue in UE5
*
* @attention If the bAddExternalAssets variable is false, external assets won't be included!
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category="Python")
TSet<UObject*> GetAllAssets() const
{
const TSet<TSoftObjectPtr<UObject>>& IteratedSet = bAddExternalAssets
? AssetDataInternal.Union(AssetDataExternal)
: AssetDataInternal;
//Create a new TSet only with raw pointers.
TSet<UObject*> ResultSet;
for (auto& Asset : IteratedSet)
ResultSet.Add(Asset.LoadSynchronous());
return ResultSet;
}
private:
UPROPERTY(VisibleAnywhere, Category="Assets")
TSet<TSoftObjectPtr<UObject>> AssetDataInternal;
/**
* This property allows exposing the array to include other assets from any other directory than what it's currently
* monitoring. NOTE: that these assets have to be added manually! They are not automatically registered or added!
*/
UPROPERTY(EditAnywhere, Category = "Assets")
bool bAddExternalAssets = false;
UPROPERTY(EditAnywhere, meta=(EditCondition="bAddExternalAssets"), Category="Assets")
TSet<TSoftObjectPtr<UObject>> AssetDataExternal;
void OnAssetCreated(const FAssetData& InAssetData);
void OnAssetRemoved(const FAssetData& InAssetData);
void OnAssetUpdated(const FAssetData& InAssetData);
bool IsUnderSameDir(const UObject* InAsset) const;
#ifdef WITH_EDITOR
void ColorAyonDirs();
void SendNotification(const FString& Text) const;
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
#endif
};

View file

@ -1,20 +1,22 @@
// Copyright 2023, Ayon, All rights reserved.
// Deprecation warning: this is left here just for backwards compatibility
// and will be removed in next versions of Ayon.
#pragma once
#include "CoreMinimal.h"
#include "Factories/Factory.h"
#include "OpenPypePublishInstanceFactory.generated.h"
#include "AyonPublishInstanceFactory.generated.h"
/**
*
*/
UCLASS()
class OPENPYPE_API UOpenPypePublishInstanceFactory : public UFactory
class AYON_API UAyonPublishInstanceFactory : public UFactory
{
GENERATED_BODY()
public:
UOpenPypePublishInstanceFactory(const FObjectInitializer& ObjectInitializer);
UAyonPublishInstanceFactory(const FObjectInitializer& ObjectInitializer);
virtual UObject* FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override;
virtual bool ShouldShowInNewMenu() const override;
};

View file

@ -1,16 +1,15 @@
// Copyright 2023, Ayon, All rights reserved.
#pragma once
#include "Engine.h"
#include "OpenPypePythonBridge.generated.h"
#include "AyonPythonBridge.generated.h"
UCLASS(Blueprintable)
class UOpenPypePythonBridge : public UObject
class UAyonPythonBridge : public UObject
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category = Python)
static UOpenPypePythonBridge* Get();
static UAyonPythonBridge* Get();
UFUNCTION(BlueprintImplementableEvent, Category = Python)
void RunInPython_Popup() const;

View file

@ -0,0 +1,31 @@
// Copyright 2023, Ayon, All rights reserved.
#pragma once
#include "CoreMinimal.h"
#include "AyonSettings.generated.h"
#define AYON_SETTINGS_FILEPATH IPluginManager::Get().FindPlugin("Ayon")->GetBaseDir() / TEXT("Config") / TEXT("DefaultAyonSettings.ini")
UCLASS(Config=AyonSettings, DefaultConfig)
class AYON_API UAyonSettings : public UObject
{
GENERATED_UCLASS_BODY()
UFUNCTION(BlueprintCallable, BlueprintPure, Category = Settings)
FColor GetFolderFColor() const
{
return FolderColor;
}
UFUNCTION(BlueprintCallable, BlueprintPure, Category = Settings)
FLinearColor GetFolderFLinearColor() const
{
return FLinearColor(FolderColor);
}
protected:
UPROPERTY(config, EditAnywhere, Category = Folders)
FColor FolderColor = FColor(25,45,223);
};

View file

@ -6,7 +6,7 @@ class FSlateStyleSet;
class ISlateStyle;
class FOpenPypeStyle
class FAyonStyle
{
public:
static void Initialize();
@ -19,5 +19,5 @@ public:
private:
static TUniquePtr< FSlateStyleSet > Create();
static TUniquePtr< FSlateStyleSet > OpenPypeStyleInstance;
static TUniquePtr< FSlateStyleSet > AyonStyleInstance;
};

View file

@ -3,23 +3,23 @@
#pragma once
#include "CoreMinimal.h"
#include "OPActionResult.generated.h"
#include "AyonActionResult.generated.h"
/**
* @brief This macro returns error code when is problem or does nothing when there is no problem.
* @param ActionResult FOP_ActionResult structure
* @param ActionResult FAyon_ActionResult structure
*/
#define EVALUATE_OP_ACTION_RESULT(ActionResult) \
#define EVALUATE_AYON_ACTION_RESULT(ActionResult) \
if(ActionResult.IsProblem()) \
return ActionResult.GetStatus();
/**
* @brief This enum values are humanly readable mapping of error codes.
* Here should be all error codes to be possible find what went wrong.
* TODO: In the future a web document should exists with the mapped error code & what problem occurred & how to repair it...
* TODO: In the future should exists an web document where is mapped error code & what problem occured & how to repair it...
*/
UENUM()
namespace EOP_ActionResult
namespace EAyon_ActionResult
{
enum Type
{
@ -27,11 +27,11 @@ namespace EOP_ActionResult
ProjectNotCreated,
ProjectNotLoaded,
ProjectNotSaved,
//....Here insert another values
//....Here insert another values
//Do not remove!
//Usable for looping through enum values
__Last UMETA(Hidden)
__Last UMETA(Hidden)
};
}
@ -40,44 +40,44 @@ namespace EOP_ActionResult
* @brief This struct holds action result enum and optionally reason of fail
*/
USTRUCT()
struct FOP_ActionResult
struct FAyon_ActionResult
{
GENERATED_BODY()
public:
/** @brief Default constructor usable when there is no problem */
FOP_ActionResult();
FAyon_ActionResult();
/**
* @brief This constructor initializes variables & attempts to log when is error
* @param InEnum Status
*/
FOP_ActionResult(const EOP_ActionResult::Type& InEnum);
FAyon_ActionResult(const EAyon_ActionResult::Type& InEnum);
/**
* @brief This constructor initializes variables & attempts to log when is error
* @param InEnum Status
* @param InReason Reason of potential fail
*/
FOP_ActionResult(const EOP_ActionResult::Type& InEnum, const FText& InReason);
FAyon_ActionResult(const EAyon_ActionResult::Type& InEnum, const FText& InReason);
private:
/** @brief Action status */
EOP_ActionResult::Type Status;
EAyon_ActionResult::Type Status;
/** @brief Optional reason of fail */
FText Reason;
FText Reason;
public:
/**
* @brief Checks if there is problematic state
* @return true when status is not equal to EOP_ActionResult::Ok
* @return true when status is not equal to EAyon_ActionResult::Ok
*/
bool IsProblem() const;
EOP_ActionResult::Type& GetStatus();
EAyon_ActionResult::Type& GetStatus();
FText& GetReason();
private:
private:
void TryLog() const;
};

View file

@ -2,10 +2,10 @@
#pragma once
#include "GameProjectUtils.h"
#include "Commandlets/OPActionResult.h"
#include "Commandlets/AyonActionResult.h"
#include "ProjectDescriptor.h"
#include "Commandlets/Commandlet.h"
#include "OPGenerateProjectCommandlet.generated.h"
#include "AyonGenerateProjectCommandlet.generated.h"
struct FProjectDescriptor;
struct FProjectInformation;
@ -14,7 +14,7 @@ struct FProjectInformation;
* @brief Structure which parses command line parameters and generates FProjectInformation
*/
USTRUCT()
struct FOPGenerateProjectParams
struct FAyonGenerateProjectParams
{
GENERATED_BODY()
@ -24,8 +24,8 @@ private:
TArray<FString> Switches;
public:
FOPGenerateProjectParams();
FOPGenerateProjectParams(const FString& CommandLineParams);
FAyonGenerateProjectParams();
FAyonGenerateProjectParams(const FString& CommandLineParams);
FProjectInformation GenerateUEProjectInformation() const;
@ -37,7 +37,7 @@ private:
};
UCLASS()
class OPENPYPE_API UOPGenerateProjectCommandlet : public UCommandlet
class AYON_API UAyonGenerateProjectCommandlet : public UCommandlet
{
GENERATED_BODY()
@ -46,15 +46,15 @@ private:
FProjectDescriptor ProjectDescriptor;
public:
UOPGenerateProjectCommandlet();
UAyonGenerateProjectCommandlet();
virtual int32 Main(const FString& CommandLineParams) override;
private:
FOPGenerateProjectParams ParseParameters(const FString& Params) const;
FOP_ActionResult TryCreateProject() const;
FOP_ActionResult TryLoadProjectDescriptor();
FAyonGenerateProjectParams ParseParameters(const FString& Params) const;
FAyon_ActionResult TryCreateProject() const;
FAyon_ActionResult TryLoadProjectDescriptor();
void AttachPluginsToProjectDescriptor();
FOP_ActionResult TrySave();
FAyon_ActionResult TrySave();
};

View file

@ -1,4 +1,4 @@
// Copyright 2023, Ayon, All rights reserved.
#pragma once
DEFINE_LOG_CATEGORY_STATIC(LogCommandletOPGenerateProject, Log, All);
DEFINE_LOG_CATEGORY_STATIC(LogCommandletOPGenerateProject, Log, All);

View file

@ -1,12 +1,13 @@
// Copyright 2023, Ayon, All rights reserved.
// Deprecation warning: this is left here just for backwards compatibility
// and will be removed in next versions of Ayon.
#pragma once
#include "Engine.h"
#include "OpenPypePublishInstance.generated.h"
UCLASS(Blueprintable)
class OPENPYPE_API UOpenPypePublishInstance : public UPrimaryDataAsset
class AYON_API UOpenPypePublishInstance : public UPrimaryDataAsset
{
GENERATED_UCLASS_BODY()
@ -48,7 +49,7 @@ public:
/**
* Function for returning all the assets in the container combined.
*
*
* @return Returns all the internal and externally added assets into one set (TSet of UObjects). Careful! They are
* returning raw pointers. Seems like an issue in UE5
*
@ -94,7 +95,7 @@ private:
#ifdef WITH_EDITOR
void ColorOpenPypeDirs();
void SendNotification(const FString& Text) const;
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;

View file

@ -0,0 +1 @@
D:\UE4\UE_4.27\Engine\Build\BatchFiles\RunUAT.bat BuildPlugin -plugin="D:\OpenPype\openpype\hosts\unreal\integration\UE_4.27\Ayon\Ayon.uplugin" -Package="D:\BuiltPlugins\4.27"

View file

@ -0,0 +1 @@
cmd /k "BuildPlugin_4-27.bat"

View file

@ -5,7 +5,7 @@
"Description": "",
"Plugins": [
{
"Name": "OpenPype",
"Name": "Ayon",
"Enabled": true
}
]

View file

@ -1,2 +0,0 @@
[/Script/OpenPype.OpenPypeSettings]
FolderColor=(R=91,G=197,B=220,A=255)

View file

@ -1,30 +0,0 @@
import unreal
openpype_detected = True
try:
from openpype.pipeline import install_host
from openpype.hosts.unreal.api import UnrealHost
openpype_host = UnrealHost()
except ImportError as exc:
openpype_host = None
openpype_detected = False
unreal.log_error("OpenPype: cannot load OpenPype [ {} ]".format(exc))
if openpype_detected:
install_host(openpype_host)
@unreal.uclass()
class OpenPypeIntegration(unreal.OpenPypePythonBridge):
@unreal.ufunction(override=True)
def RunInPython_Popup(self):
unreal.log_warning("OpenPype: showing tools popup")
if openpype_detected:
openpype_host.show_tools_popup()
@unreal.ufunction(override=True)
def RunInPython_Dialog(self):
unreal.log_warning("OpenPype: showing tools dialog")
if openpype_detected:
openpype_host.show_tools_dialog()

View file

@ -1,23 +0,0 @@
{
"FileVersion": 3,
"Version": 1,
"VersionName": "1.0",
"FriendlyName": "OpenPype",
"Description": "OpenPype Integration",
"Category": "OpenPype.Integration",
"CreatedBy": "Ondrej Samohel",
"CreatedByURL": "https://openpype.io",
"DocsURL": "https://openpype.io/docs/artist_hosts_unreal",
"MarketplaceURL": "",
"SupportURL": "https://pype.club/",
"EngineVersion": "4.27",
"CanContainContent": true,
"Installed": true,
"Modules": [
{
"Name": "OpenPype",
"Type": "Editor",
"LoadingPhase": "Default"
}
]
}

View file

@ -1,11 +0,0 @@
# OpenPype Unreal Integration plugin - UE 4.x
This is plugin for Unreal Editor, creating menu for [OpenPype](https://github.com/getavalon) tools to run.
## How does this work
Plugin is creating basic menu items in **Window/OpenPype** section of Unreal Editor main menu and a button
on the main toolbar with associated menu. Clicking on those menu items is calling callbacks that are
declared in c++ but needs to be implemented during Unreal Editor
startup in `Plugins/OpenPype/Content/Python/init_unreal.py` - this should be executed by Unreal Editor
automatically.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

View file

@ -1,141 +0,0 @@
// Copyright 2023, Ayon, All rights reserved.
#include "Commandlets/Implementations/OPGenerateProjectCommandlet.h"
#include "Editor.h"
#include "GameProjectUtils.h"
#include "OPConstants.h"
#include "Commandlets/OPActionResult.h"
#include "ProjectDescriptor.h"
int32 UOPGenerateProjectCommandlet::Main(const FString& CommandLineParams)
{
//Parses command line parameters & creates structure FProjectInformation
const FOPGenerateProjectParams ParsedParams = FOPGenerateProjectParams(CommandLineParams);
ProjectInformation = ParsedParams.GenerateUEProjectInformation();
//Creates .uproject & other UE files
EVALUATE_OP_ACTION_RESULT(TryCreateProject());
//Loads created .uproject
EVALUATE_OP_ACTION_RESULT(TryLoadProjectDescriptor());
//Adds needed plugin to .uproject
AttachPluginsToProjectDescriptor();
//Saves .uproject
EVALUATE_OP_ACTION_RESULT(TrySave());
//When we are here, there should not be problems in generating Unreal Project for OpenPype
return 0;
}
FOPGenerateProjectParams::FOPGenerateProjectParams(): FOPGenerateProjectParams("")
{
}
FOPGenerateProjectParams::FOPGenerateProjectParams(const FString& CommandLineParams): CommandLineParams(
CommandLineParams)
{
UCommandlet::ParseCommandLine(*CommandLineParams, Tokens, Switches);
}
FProjectInformation FOPGenerateProjectParams::GenerateUEProjectInformation() const
{
FProjectInformation ProjectInformation = FProjectInformation();
ProjectInformation.ProjectFilename = GetProjectFileName();
ProjectInformation.bShouldGenerateCode = IsSwitchPresent("GenerateCode");
return ProjectInformation;
}
FString FOPGenerateProjectParams::TryGetToken(const int32 Index) const
{
return Tokens.IsValidIndex(Index) ? Tokens[Index] : "";
}
FString FOPGenerateProjectParams::GetProjectFileName() const
{
return TryGetToken(0);
}
bool FOPGenerateProjectParams::IsSwitchPresent(const FString& Switch) const
{
return INDEX_NONE != Switches.IndexOfByPredicate([&Switch](const FString& Item) -> bool
{
return Item.Equals(Switch);
}
);
}
UOPGenerateProjectCommandlet::UOPGenerateProjectCommandlet()
{
LogToConsole = true;
}
FOP_ActionResult UOPGenerateProjectCommandlet::TryCreateProject() const
{
FText FailReason;
FText FailLog;
TArray<FString> OutCreatedFiles;
if (!GameProjectUtils::CreateProject(ProjectInformation, FailReason, FailLog, &OutCreatedFiles))
return FOP_ActionResult(EOP_ActionResult::ProjectNotCreated, FailReason);
return FOP_ActionResult();
}
FOP_ActionResult UOPGenerateProjectCommandlet::TryLoadProjectDescriptor()
{
FText FailReason;
const bool bLoaded = ProjectDescriptor.Load(ProjectInformation.ProjectFilename, FailReason);
return FOP_ActionResult(bLoaded ? EOP_ActionResult::Ok : EOP_ActionResult::ProjectNotLoaded, FailReason);
}
void UOPGenerateProjectCommandlet::AttachPluginsToProjectDescriptor()
{
FPluginReferenceDescriptor OPPluginDescriptor;
OPPluginDescriptor.bEnabled = true;
OPPluginDescriptor.Name = OPConstants::OP_PluginName;
ProjectDescriptor.Plugins.Add(OPPluginDescriptor);
FPluginReferenceDescriptor PythonPluginDescriptor;
PythonPluginDescriptor.bEnabled = true;
PythonPluginDescriptor.Name = OPConstants::PythonScript_PluginName;
ProjectDescriptor.Plugins.Add(PythonPluginDescriptor);
FPluginReferenceDescriptor SequencerScriptingPluginDescriptor;
SequencerScriptingPluginDescriptor.bEnabled = true;
SequencerScriptingPluginDescriptor.Name = OPConstants::SequencerScripting_PluginName;
ProjectDescriptor.Plugins.Add(SequencerScriptingPluginDescriptor);
FPluginReferenceDescriptor MovieRenderPipelinePluginDescriptor;
MovieRenderPipelinePluginDescriptor.bEnabled = true;
MovieRenderPipelinePluginDescriptor.Name = OPConstants::MovieRenderPipeline_PluginName;
ProjectDescriptor.Plugins.Add(MovieRenderPipelinePluginDescriptor);
FPluginReferenceDescriptor EditorScriptingPluginDescriptor;
EditorScriptingPluginDescriptor.bEnabled = true;
EditorScriptingPluginDescriptor.Name = OPConstants::EditorScriptingUtils_PluginName;
ProjectDescriptor.Plugins.Add(EditorScriptingPluginDescriptor);
}
FOP_ActionResult UOPGenerateProjectCommandlet::TrySave()
{
FText FailReason;
const bool bSaved = ProjectDescriptor.Save(ProjectInformation.ProjectFilename, FailReason);
return FOP_ActionResult(bSaved ? EOP_ActionResult::Ok : EOP_ActionResult::ProjectNotSaved, FailReason);
}
FOPGenerateProjectParams UOPGenerateProjectCommandlet::ParseParameters(const FString& Params) const
{
FOPGenerateProjectParams ParamsResult;
TArray<FString> Tokens, Switches;
ParseCommandLine(*Params, Tokens, Switches);
return ParamsResult;
}

View file

@ -1,41 +0,0 @@
// Copyright 2023, Ayon, All rights reserved.
#include "Commandlets/OPActionResult.h"
#include "Logging/OP_Log.h"
EOP_ActionResult::Type& FOP_ActionResult::GetStatus()
{
return Status;
}
FText& FOP_ActionResult::GetReason()
{
return Reason;
}
FOP_ActionResult::FOP_ActionResult():Status(EOP_ActionResult::Type::Ok)
{
}
FOP_ActionResult::FOP_ActionResult(const EOP_ActionResult::Type& InEnum):Status(InEnum)
{
TryLog();
}
FOP_ActionResult::FOP_ActionResult(const EOP_ActionResult::Type& InEnum, const FText& InReason):Status(InEnum), Reason(InReason)
{
TryLog();
};
bool FOP_ActionResult::IsProblem() const
{
return Status != EOP_ActionResult::Ok;
}
void FOP_ActionResult::TryLog() const
{
if(IsProblem())
UE_LOG(LogCommandletOPGenerateProject, Error, TEXT("%s"), *Reason.ToString());
}

View file

@ -1,21 +0,0 @@
// Copyright 2023, Ayon, All rights reserved.
#include "OpenPypePublishInstanceFactory.h"
#include "OpenPypePublishInstance.h"
UOpenPypePublishInstanceFactory::UOpenPypePublishInstanceFactory(const FObjectInitializer& ObjectInitializer)
: UFactory(ObjectInitializer)
{
SupportedClass = UOpenPypePublishInstance::StaticClass();
bCreateNew = false;
bEditorImport = true;
}
UObject* UOpenPypePublishInstanceFactory::FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn)
{
check(InClass->IsChildOf(UOpenPypePublishInstance::StaticClass()));
return NewObject<UOpenPypePublishInstance>(InParent, InClass, InName, Flags);
}
bool UOpenPypePublishInstanceFactory::ShouldShowInNewMenu() const {
return false;
}

View file

@ -1,14 +0,0 @@
// Copyright 2023, Ayon, All rights reserved.
#include "OpenPypePythonBridge.h"
UOpenPypePythonBridge* UOpenPypePythonBridge::Get()
{
TArray<UClass*> OpenPypePythonBridgeClasses;
GetDerivedClasses(UOpenPypePythonBridge::StaticClass(), OpenPypePythonBridgeClasses);
int32 NumClasses = OpenPypePythonBridgeClasses.Num();
if (NumClasses > 0)
{
return Cast<UOpenPypePythonBridge>(OpenPypePythonBridgeClasses[NumClasses - 1]->GetDefaultObject());
}
return nullptr;
};

View file

@ -1,20 +0,0 @@
// Copyright 2023, Ayon, All rights reserved.
#include "OpenPypeSettings.h"
#include "Interfaces/IPluginManager.h"
/**
* Mainly is used for initializing default values if the DefaultOpenPypeSettings.ini file does not exist in the saved config
*/
UOpenPypeSettings::UOpenPypeSettings(const FObjectInitializer& ObjectInitializer)
{
const FString ConfigFilePath = OPENPYPE_SETTINGS_FILEPATH;
// This has to be probably in the future set using the UE Reflection system
FColor Color;
GConfig->GetColor(TEXT("/Script/OpenPype.OpenPypeSettings"), TEXT("FolderColor"), Color, ConfigFilePath);
FolderColor = Color;
}

View file

@ -1,70 +0,0 @@
// Copyright 2023, Ayon, All rights reserved.
#include "OpenPypeStyle.h"
#include "Framework/Application/SlateApplication.h"
#include "Styling/SlateStyle.h"
#include "Styling/SlateStyleRegistry.h"
TUniquePtr< FSlateStyleSet > FOpenPypeStyle::OpenPypeStyleInstance = nullptr;
void FOpenPypeStyle::Initialize()
{
if (!OpenPypeStyleInstance.IsValid())
{
OpenPypeStyleInstance = Create();
FSlateStyleRegistry::RegisterSlateStyle(*OpenPypeStyleInstance);
}
}
void FOpenPypeStyle::Shutdown()
{
if (OpenPypeStyleInstance.IsValid())
{
FSlateStyleRegistry::UnRegisterSlateStyle(*OpenPypeStyleInstance);
OpenPypeStyleInstance.Reset();
}
}
FName FOpenPypeStyle::GetStyleSetName()
{
static FName StyleSetName(TEXT("OpenPypeStyle"));
return StyleSetName;
}
FName FOpenPypeStyle::GetContextName()
{
static FName ContextName(TEXT("OpenPype"));
return ContextName;
}
#define IMAGE_BRUSH(RelativePath, ...) FSlateImageBrush( Style->RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ )
const FVector2D Icon40x40(40.0f, 40.0f);
TUniquePtr< FSlateStyleSet > FOpenPypeStyle::Create()
{
TUniquePtr< FSlateStyleSet > Style = MakeUnique<FSlateStyleSet>(GetStyleSetName());
Style->SetContentRoot(FPaths::EnginePluginsDir() / TEXT("Marketplace/OpenPype/Resources"));
return Style;
}
void FOpenPypeStyle::SetIcon(const FString& StyleName, const FString& ResourcePath)
{
FSlateStyleSet* Style = OpenPypeStyleInstance.Get();
FString Name(GetContextName().ToString());
Name = Name + "." + StyleName;
Style->Set(*Name, new FSlateImageBrush(Style->RootToContentDir(ResourcePath, TEXT(".png")), Icon40x40));
FSlateApplication::Get().GetRenderer()->ReloadTextureResources();
}
#undef IMAGE_BRUSH
const ISlateStyle& FOpenPypeStyle::Get()
{
check(OpenPypeStyleInstance);
return *OpenPypeStyleInstance;
}

View file

@ -2,23 +2,23 @@
"FileVersion": 3,
"Version": 1,
"VersionName": "1.0",
"FriendlyName": "OpenPype",
"Description": "OpenPype Integration",
"Category": "OpenPype.Integration",
"FriendlyName": "Ayon",
"Description": "Ayon Integration",
"Category": "Ayon.Integration",
"CreatedBy": "Ondrej Samohel",
"CreatedByURL": "https://openpype.io",
"DocsURL": "https://openpype.io/docs/artist_hosts_unreal",
"CreatedByURL": "https://ayon.ynput.io",
"DocsURL": "https://ayon.ynput.io/docs/artist_hosts_unreal",
"MarketplaceURL": "",
"SupportURL": "https://pype.club/",
"SupportURL": "https://ynput.io/",
"CanContainContent": true,
"EngineVersion": "5.0",
"IsExperimentalVersion": false,
"Installed": true,
"Modules": [
{
"Name": "OpenPype",
"Name": "Ayon",
"Type": "Editor",
"LoadingPhase": "Default"
}
]
}
}

View file

@ -0,0 +1,2 @@
[/Script/Ayon.AyonSettings]
FolderColor=(R=91,G=197,B=220,A=255)

View file

@ -0,0 +1,30 @@
import unreal
ayon_detected = True
try:
from openpype.pipeline import install_host
from openpype.hosts.unreal.api import UnrealHost
ayon_host = UnrealHost()
except ImportError as exc:
ayon_host = None
ayon_detected = False
unreal.log_error(f"Ayon: cannot load Ayon integration [ {exc} ]")
if ayon_detected:
install_host(ayon_host)
@unreal.uclass()
class AyonIntegration(unreal.AyonPythonBridge):
@unreal.ufunction(override=True)
def RunInPython_Popup(self):
unreal.log_warning("Ayon: showing tools popup")
if ayon_detected:
ayon_host.show_tools_popup()
@unreal.ufunction(override=True)
def RunInPython_Dialog(self):
unreal.log_warning("Ayon: showing tools dialog")
if ayon_detected:
ayon_host.show_tools_dialog()

View file

@ -0,0 +1,3 @@
# Ayon Unreal Integration plugin - UE 5.0
This is plugin for Unreal Editor, creating menu for [Ayon](https://github.com/ynput/OpenPype) tools to run.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -2,9 +2,9 @@
using UnrealBuildTool;
public class OpenPype : ModuleRules
public class Ayon : ModuleRules
{
public OpenPype(ReadOnlyTargetRules Target) : base(Target)
public Ayon(ReadOnlyTargetRules Target) : base(Target)
{
DefaultBuildSettings = BuildSettingsVersion.V2;
bLegacyPublicIncludePaths = false;

View file

@ -1,58 +1,57 @@
// Copyright 2023, Ayon, All rights reserved.
#include "OpenPype.h"
#include "Ayon.h"
#include "ISettingsContainer.h"
#include "ISettingsModule.h"
#include "ISettingsSection.h"
#include "OpenPypeStyle.h"
#include "OpenPypeCommands.h"
#include "OpenPypePythonBridge.h"
#include "OpenPypeSettings.h"
#include "Misc/MessageDialog.h"
#include "AyonStyle.h"
#include "AyonCommands.h"
#include "AyonPythonBridge.h"
#include "AyonSettings.h"
#include "ToolMenus.h"
static const FName OpenPypeTabName("OpenPype");
static const FName AyonTabName("Ayon");
#define LOCTEXT_NAMESPACE "FOpenPypeModule"
#define LOCTEXT_NAMESPACE "FAyonModule"
// This function is triggered when the plugin is staring up
void FOpenPypeModule::StartupModule()
void FAyonModule::StartupModule()
{
FOpenPypeStyle::Initialize();
FOpenPypeStyle::ReloadTextures();
FOpenPypeCommands::Register();
FAyonStyle::Initialize();
FAyonStyle::ReloadTextures();
FAyonCommands::Register();
PluginCommands = MakeShareable(new FUICommandList);
PluginCommands->MapAction(
FOpenPypeCommands::Get().OpenPypeTools,
FExecuteAction::CreateRaw(this, &FOpenPypeModule::MenuPopup),
FAyonCommands::Get().AyonTools,
FExecuteAction::CreateRaw(this, &FAyonModule::MenuPopup),
FCanExecuteAction());
PluginCommands->MapAction(
FOpenPypeCommands::Get().OpenPypeToolsDialog,
FExecuteAction::CreateRaw(this, &FOpenPypeModule::MenuDialog),
FAyonCommands::Get().AyonToolsDialog,
FExecuteAction::CreateRaw(this, &FAyonModule::MenuDialog),
FCanExecuteAction());
UToolMenus::RegisterStartupCallback(
FSimpleMulticastDelegate::FDelegate::CreateRaw(this, &FOpenPypeModule::RegisterMenus));
FSimpleMulticastDelegate::FDelegate::CreateRaw(this, &FAyonModule::RegisterMenus));
RegisterSettings();
}
void FOpenPypeModule::ShutdownModule()
void FAyonModule::ShutdownModule()
{
UToolMenus::UnRegisterStartupCallback(this);
UToolMenus::UnregisterOwner(this);
FOpenPypeStyle::Shutdown();
FAyonStyle::Shutdown();
FOpenPypeCommands::Unregister();
FAyonCommands::Unregister();
}
void FOpenPypeModule::RegisterSettings()
void FAyonModule::RegisterSettings()
{
ISettingsModule& SettingsModule = FModuleManager::LoadModuleChecked<ISettingsModule>("Settings");
@ -60,10 +59,10 @@ void FOpenPypeModule::RegisterSettings()
// TODO: After the movement of the plugin from the game to editor, it might be necessary to move this!
ISettingsContainerPtr SettingsContainer = SettingsModule.GetContainer("Project");
UOpenPypeSettings* Settings = GetMutableDefault<UOpenPypeSettings>();
UAyonSettings* Settings = GetMutableDefault<UAyonSettings>();
// Register the settings
ISettingsSectionPtr SettingsSection = SettingsModule.RegisterSettings("Project", "OpenPype", "General",
ISettingsSectionPtr SettingsSection = SettingsModule.RegisterSettings("Project", "Ayon", "General",
LOCTEXT("RuntimeGeneralSettingsName",
"General"),
LOCTEXT("RuntimeGeneralSettingsDescription",
@ -75,13 +74,13 @@ void FOpenPypeModule::RegisterSettings()
// validate those or just act to settings changes.
if (SettingsSection.IsValid())
{
SettingsSection->OnModified().BindRaw(this, &FOpenPypeModule::HandleSettingsSaved);
SettingsSection->OnModified().BindRaw(this, &FAyonModule::HandleSettingsSaved);
}
}
bool FOpenPypeModule::HandleSettingsSaved()
bool FAyonModule::HandleSettingsSaved()
{
UOpenPypeSettings* Settings = GetMutableDefault<UOpenPypeSettings>();
UAyonSettings* Settings = GetMutableDefault<UAyonSettings>();
bool ResaveSettings = false;
// You can put any validation code in here and resave the settings in case an invalid
@ -95,7 +94,7 @@ bool FOpenPypeModule::HandleSettingsSaved()
return true;
}
void FOpenPypeModule::RegisterMenus()
void FAyonModule::RegisterMenus()
{
// Owner will be used for cleanup in call to UToolMenus::UnregisterOwner
FToolMenuOwnerScoped OwnerScoped(this);
@ -103,21 +102,21 @@ void FOpenPypeModule::RegisterMenus()
{
UToolMenu* Menu = UToolMenus::Get()->ExtendMenu("LevelEditor.MainMenu.Tools");
{
// FToolMenuSection& Section = Menu->FindOrAddSection("OpenPype");
// FToolMenuSection& Section = Menu->FindOrAddSection("Ayon");
FToolMenuSection& Section = Menu->AddSection(
"OpenPype",
TAttribute<FText>(FText::FromString("OpenPype")),
"Ayon",
TAttribute<FText>(FText::FromString("Ayon")),
FToolMenuInsert("Programming", EToolMenuInsertType::Before)
);
Section.AddMenuEntryWithCommandList(FOpenPypeCommands::Get().OpenPypeTools, PluginCommands);
Section.AddMenuEntryWithCommandList(FOpenPypeCommands::Get().OpenPypeToolsDialog, PluginCommands);
Section.AddMenuEntryWithCommandList(FAyonCommands::Get().AyonTools, PluginCommands);
Section.AddMenuEntryWithCommandList(FAyonCommands::Get().AyonToolsDialog, PluginCommands);
}
UToolMenu* ToolbarMenu = UToolMenus::Get()->ExtendMenu("LevelEditor.LevelEditorToolBar.PlayToolBar");
{
FToolMenuSection& Section = ToolbarMenu->FindOrAddSection("PluginTools");
{
FToolMenuEntry& Entry = Section.AddEntry(
FToolMenuEntry::InitToolBarButton(FOpenPypeCommands::Get().OpenPypeTools));
FToolMenuEntry::InitToolBarButton(FAyonCommands::Get().AyonTools));
Entry.SetCommandList(PluginCommands);
}
}
@ -125,16 +124,16 @@ void FOpenPypeModule::RegisterMenus()
}
void FOpenPypeModule::MenuPopup()
void FAyonModule::MenuPopup()
{
UOpenPypePythonBridge* bridge = UOpenPypePythonBridge::Get();
UAyonPythonBridge* bridge = UAyonPythonBridge::Get();
bridge->RunInPython_Popup();
}
void FOpenPypeModule::MenuDialog()
void FAyonModule::MenuDialog()
{
UOpenPypePythonBridge* bridge = UOpenPypePythonBridge::Get();
UAyonPythonBridge* bridge = UAyonPythonBridge::Get();
bridge->RunInPython_Dialog();
}
IMPLEMENT_MODULE(FOpenPypeModule, OpenPype)
IMPLEMENT_MODULE(FAyonModule, Ayon)

View file

@ -1,31 +1,30 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "AssetContainer.h"
#include "AyonAssetContainer.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "Misc/PackageName.h"
#include "Engine.h"
#include "Containers/UnrealString.h"
UAssetContainer::UAssetContainer(const FObjectInitializer& ObjectInitializer)
UAyonAssetContainer::UAyonAssetContainer(const FObjectInitializer& ObjectInitializer)
: UAssetUserData(ObjectInitializer)
{
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
FString path = UAssetContainer::GetPathName();
UE_LOG(LogTemp, Warning, TEXT("UAssetContainer %s"), *path);
FString path = UAyonAssetContainer::GetPathName();
UE_LOG(LogTemp, Warning, TEXT("UAyonAssetContainer %s"), *path);
FARFilter Filter;
Filter.PackagePaths.Add(FName(*path));
AssetRegistryModule.Get().OnAssetAdded().AddUObject(this, &UAssetContainer::OnAssetAdded);
AssetRegistryModule.Get().OnAssetRemoved().AddUObject(this, &UAssetContainer::OnAssetRemoved);
AssetRegistryModule.Get().OnAssetRenamed().AddUObject(this, &UAssetContainer::OnAssetRenamed);
AssetRegistryModule.Get().OnAssetAdded().AddUObject(this, &UAyonAssetContainer::OnAssetAdded);
AssetRegistryModule.Get().OnAssetRemoved().AddUObject(this, &UAyonAssetContainer::OnAssetRemoved);
AssetRegistryModule.Get().OnAssetRenamed().AddUObject(this, &UAyonAssetContainer::OnAssetRenamed);
}
void UAssetContainer::OnAssetAdded(const FAssetData& AssetData)
void UAyonAssetContainer::OnAssetAdded(const FAssetData& AssetData)
{
TArray<FString> split;
// get directory of current container
FString selfFullPath = UAssetContainer::GetPathName();
FString selfFullPath = UAyonAssetContainer::GetPathName();
FString selfDir = FPackageName::GetLongPackagePath(*selfFullPath);
// get asset path and class
@ -50,12 +49,12 @@ void UAssetContainer::OnAssetAdded(const FAssetData& AssetData)
}
}
void UAssetContainer::OnAssetRemoved(const FAssetData& AssetData)
void UAyonAssetContainer::OnAssetRemoved(const FAssetData& AssetData)
{
TArray<FString> split;
// get directory of current container
FString selfFullPath = UAssetContainer::GetPathName();
FString selfFullPath = UAyonAssetContainer::GetPathName();
FString selfDir = FPackageName::GetLongPackagePath(*selfFullPath);
// get asset path and class
@ -68,7 +67,7 @@ void UAssetContainer::OnAssetRemoved(const FAssetData& AssetData)
FString assetDir = FPackageName::GetLongPackagePath(*split[1]);
// take interest only in paths starting with path of current container
FString path = UAssetContainer::GetPathName();
FString path = UAyonAssetContainer::GetPathName();
FString lpp = FPackageName::GetLongPackagePath(*path);
if (assetDir.StartsWith(*selfDir))
@ -83,12 +82,12 @@ void UAssetContainer::OnAssetRemoved(const FAssetData& AssetData)
}
}
void UAssetContainer::OnAssetRenamed(const FAssetData& AssetData, const FString& str)
void UAyonAssetContainer::OnAssetRenamed(const FAssetData& AssetData, const FString& str)
{
TArray<FString> split;
// get directory of current container
FString selfFullPath = UAssetContainer::GetPathName();
FString selfFullPath = UAyonAssetContainer::GetPathName();
FString selfDir = FPackageName::GetLongPackagePath(*selfFullPath);
// get asset path and class

View file

@ -0,0 +1,20 @@
#include "AyonAssetContainerFactory.h"
#include "AyonAssetContainer.h"
UAyonAssetContainerFactory::UAyonAssetContainerFactory(const FObjectInitializer& ObjectInitializer)
: UFactory(ObjectInitializer)
{
SupportedClass = UAyonAssetContainer::StaticClass();
bCreateNew = false;
bEditorImport = true;
}
UObject* UAyonAssetContainerFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn)
{
UAyonAssetContainer* AssetContainer = NewObject<UAyonAssetContainer>(InParent, Class, Name, Flags);
return AssetContainer;
}
bool UAyonAssetContainerFactory::ShouldShowInNewMenu() const {
return false;
}

View file

@ -0,0 +1,13 @@
// Copyright 2023, Ayon, All rights reserved.
#include "AyonCommands.h"
#define LOCTEXT_NAMESPACE "FAyonModule"
void FAyonCommands::RegisterCommands()
{
UI_COMMAND(AyonTools, "Ayon Tools", "Pipeline tools", EUserInterfaceActionType::Button, FInputChord());
UI_COMMAND(AyonToolsDialog, "Ayon Tools Dialog", "Pipeline tools dialog", EUserInterfaceActionType::Button, FInputChord());
}
#undef LOCTEXT_NAMESPACE

View file

@ -1,9 +1,7 @@
// Copyright 2023, Ayon, All rights reserved.
#include "OpenPypeLib.h"
#include "AyonLib.h"
#include "AssetViewUtils.h"
#include "Misc/Paths.h"
#include "Misc/ConfigCacheIni.h"
#include "UObject/UnrealType.h"
/**
@ -13,7 +11,7 @@
* @warning This color will appear only after Editor restart. Is there a better way?
*/
bool UOpenPypeLib::SetFolderColor(const FString& FolderPath, const FLinearColor& FolderColor, const bool& bForceAdd)
bool UAyonLib::SetFolderColor(const FString& FolderPath, const FLinearColor& FolderColor, const bool& bForceAdd)
{
if (AssetViewUtils::DoesFolderExist(FolderPath))
{
@ -31,11 +29,11 @@ bool UOpenPypeLib::SetFolderColor(const FString& FolderPath, const FLinearColor&
}
/**
* Returns all properties on given object
* Returns all poperties on given object
* @param cls - class
* @return TArray of properties
*/
TArray<FString> UOpenPypeLib::GetAllProperties(UClass* cls)
TArray<FString> UAyonLib::GetAllProperties(UClass* cls)
{
TArray<FString> Ret;
if (cls != nullptr)

View file

@ -0,0 +1,204 @@
// Copyright 2023, Ayon, All rights reserved.
// Deprecation warning: this is left here just for backwards compatibility
// and will be removed in next versions of Ayon.
#pragma once
#include "AyonPublishInstance.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "AssetToolsModule.h"
#include "Framework/Notifications/NotificationManager.h"
#include "AyonLib.h"
#include "AyonSettings.h"
#include "Widgets/Notifications/SNotificationList.h"
//Moves all the invalid pointers to the end to prepare them for the shrinking
#define REMOVE_INVALID_ENTRIES(VAR) VAR.CompactStable(); \
VAR.Shrink();
UAyonPublishInstance::UAyonPublishInstance(const FObjectInitializer& ObjectInitializer)
: UPrimaryDataAsset(ObjectInitializer)
{
const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<
FAssetRegistryModule>("AssetRegistry");
const FPropertyEditorModule& PropertyEditorModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>(
"PropertyEditor");
FString Left, Right;
GetPathName().Split("/" + GetName(), &Left, &Right);
FARFilter Filter;
Filter.PackagePaths.Emplace(FName(Left));
TArray<FAssetData> FoundAssets;
AssetRegistryModule.GetRegistry().GetAssets(Filter, FoundAssets);
for (const FAssetData& AssetData : FoundAssets)
OnAssetCreated(AssetData);
REMOVE_INVALID_ENTRIES(AssetDataInternal)
REMOVE_INVALID_ENTRIES(AssetDataExternal)
AssetRegistryModule.Get().OnAssetAdded().AddUObject(this, &UAyonPublishInstance::OnAssetCreated);
AssetRegistryModule.Get().OnAssetRemoved().AddUObject(this, &UAyonPublishInstance::OnAssetRemoved);
AssetRegistryModule.Get().OnAssetUpdated().AddUObject(this, &UAyonPublishInstance::OnAssetUpdated);
#ifdef WITH_EDITOR
ColorAyonDirs();
#endif
}
void UAyonPublishInstance::OnAssetCreated(const FAssetData& InAssetData)
{
TArray<FString> split;
UObject* Asset = InAssetData.GetAsset();
if (!IsValid(Asset))
{
UE_LOG(LogAssetData, Warning, TEXT("Asset \"%s\" is not valid! Skipping the addition."),
*InAssetData.ObjectPath.ToString());
return;
}
const bool result = IsUnderSameDir(Asset) && Cast<UAyonPublishInstance>(Asset) == nullptr;
if (result)
{
if (AssetDataInternal.Emplace(Asset).IsValidId())
{
UE_LOG(LogTemp, Log, TEXT("Added an Asset to PublishInstance - Publish Instance: %s, Asset %s"),
*this->GetName(), *Asset->GetName());
}
}
}
void UAyonPublishInstance::OnAssetRemoved(const FAssetData& InAssetData)
{
if (Cast<UAyonPublishInstance>(InAssetData.GetAsset()) == nullptr)
{
if (AssetDataInternal.Contains(nullptr))
{
AssetDataInternal.Remove(nullptr);
REMOVE_INVALID_ENTRIES(AssetDataInternal)
}
else
{
AssetDataExternal.Remove(nullptr);
REMOVE_INVALID_ENTRIES(AssetDataExternal)
}
}
}
void UAyonPublishInstance::OnAssetUpdated(const FAssetData& InAssetData)
{
REMOVE_INVALID_ENTRIES(AssetDataInternal);
REMOVE_INVALID_ENTRIES(AssetDataExternal);
}
bool UAyonPublishInstance::IsUnderSameDir(const UObject* InAsset) const
{
FString ThisLeft, ThisRight;
this->GetPathName().Split(this->GetName(), &ThisLeft, &ThisRight);
return InAsset->GetPathName().StartsWith(ThisLeft);
}
#ifdef WITH_EDITOR
void UAyonPublishInstance::ColorAyonDirs()
{
FString PathName = this->GetPathName();
//Check whether the path contains the defined Ayon folder
if (!PathName.Contains(TEXT("Ayon"))) return;
//Get the base path for open pype
FString PathLeft, PathRight;
PathName.Split(FString("Ayon"), &PathLeft, &PathRight);
if (PathLeft.IsEmpty() || PathRight.IsEmpty())
{
UE_LOG(LogAssetData, Error, TEXT("Failed to retrieve the base Ayon directory!"))
return;
}
PathName.RemoveFromEnd(PathRight, ESearchCase::CaseSensitive);
//Get the current settings
const UAyonSettings* Settings = GetMutableDefault<UAyonSettings>();
//Color the base folder
UAyonLib::SetFolderColor(PathName, Settings->GetFolderFColor(), false);
//Get Sub paths, iterate through them and color them according to the folder color in UAyonSettings
const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(
"AssetRegistry");
TArray<FString> PathList;
AssetRegistryModule.Get().GetSubPaths(PathName, PathList, true);
if (PathList.Num() > 0)
{
for (const FString& Path : PathList)
{
UAyonLib::SetFolderColor(Path, Settings->GetFolderFColor(), false);
}
}
}
void UAyonPublishInstance::SendNotification(const FString& Text) const
{
FNotificationInfo Info{FText::FromString(Text)};
Info.bFireAndForget = true;
Info.bUseLargeFont = false;
Info.bUseThrobber = false;
Info.bUseSuccessFailIcons = false;
Info.ExpireDuration = 4.f;
Info.FadeOutDuration = 2.f;
FSlateNotificationManager::Get().AddNotification(Info);
UE_LOG(LogAssetData, Warning,
TEXT(
"Removed duplicated asset from the AssetsDataExternal in Container \"%s\", Asset is already included in the AssetDataInternal!"
), *GetName()
)
}
void UAyonPublishInstance::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);
if (PropertyChangedEvent.ChangeType == EPropertyChangeType::ValueSet &&
PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_CHECKED(
UAyonPublishInstance, AssetDataExternal))
{
// Check for duplicated assets
for (const auto& Asset : AssetDataInternal)
{
if (AssetDataExternal.Contains(Asset))
{
AssetDataExternal.Remove(Asset);
return SendNotification(
"You are not allowed to add assets into AssetDataExternal which are already included in AssetDataInternal!");
}
}
// Check if no UAyonPublishInstance type assets are included
for (const auto& Asset : AssetDataExternal)
{
if (Cast<UAyonPublishInstance>(Asset.Get()) != nullptr)
{
AssetDataExternal.Remove(Asset);
return SendNotification("You are not allowed to add publish instances!");
}
}
}
}
#endif

View file

@ -0,0 +1,23 @@
// Copyright 2023, Ayon, All rights reserved.
// Deprecation warning: this is left here just for backwards compatibility
// and will be removed in next versions of Ayon.
#include "AyonPublishInstanceFactory.h"
#include "AyonPublishInstance.h"
UAyonPublishInstanceFactory::UAyonPublishInstanceFactory(const FObjectInitializer& ObjectInitializer)
: UFactory(ObjectInitializer)
{
SupportedClass = UAyonPublishInstance::StaticClass();
bCreateNew = false;
bEditorImport = true;
}
UObject* UAyonPublishInstanceFactory::FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn)
{
check(InClass->IsChildOf(UAyonPublishInstance::StaticClass()));
return NewObject<UAyonPublishInstance>(InParent, InClass, InName, Flags);
}
bool UAyonPublishInstanceFactory::ShouldShowInNewMenu() const {
return false;
}

View file

@ -0,0 +1,14 @@
// Copyright 2023, Ayon, All rights reserved.
#include "AyonPythonBridge.h"
UAyonPythonBridge* UAyonPythonBridge::Get()
{
TArray<UClass*> AyonPythonBridgeClasses;
GetDerivedClasses(UAyonPythonBridge::StaticClass(), AyonPythonBridgeClasses);
int32 NumClasses = AyonPythonBridgeClasses.Num();
if (NumClasses > 0)
{
return Cast<UAyonPythonBridge>(AyonPythonBridgeClasses[NumClasses - 1]->GetDefaultObject());
}
return nullptr;
};

View file

@ -0,0 +1,21 @@
// Copyright 2023, Ayon, All rights reserved.
#include "AyonSettings.h"
#include "Interfaces/IPluginManager.h"
#include "UObject/UObjectGlobals.h"
/**
* Mainly is used for initializing default values if the DefaultAyonSettings.ini file does not exist in the saved config
*/
UAyonSettings::UAyonSettings(const FObjectInitializer& ObjectInitializer)
{
const FString ConfigFilePath = AYON_SETTINGS_FILEPATH;
// This has to be probably in the future set using the UE Reflection system
FColor Color;
GConfig->GetColor(TEXT("/Script/Ayon.AyonSettings"), TEXT("FolderColor"), Color, ConfigFilePath);
FolderColor = Color;
}

View file

@ -0,0 +1,62 @@
// Copyright 2023, Ayon, All rights reserved.
#include "AyonStyle.h"
#include "Framework/Application/SlateApplication.h"
#include "Styling/SlateStyleRegistry.h"
#include "Slate/SlateGameResources.h"
#include "Interfaces/IPluginManager.h"
#include "Styling/SlateStyleMacros.h"
#define RootToContentDir Style->RootToContentDir
TSharedPtr<FSlateStyleSet> FAyonStyle::AyonStyleInstance = nullptr;
void FAyonStyle::Initialize()
{
if (!AyonStyleInstance.IsValid())
{
AyonStyleInstance = Create();
FSlateStyleRegistry::RegisterSlateStyle(*AyonStyleInstance);
}
}
void FAyonStyle::Shutdown()
{
FSlateStyleRegistry::UnRegisterSlateStyle(*AyonStyleInstance);
ensure(AyonStyleInstance.IsUnique());
AyonStyleInstance.Reset();
}
FName FAyonStyle::GetStyleSetName()
{
static FName StyleSetName(TEXT("AyonStyle"));
return StyleSetName;
}
const FVector2D Icon16x16(16.0f, 16.0f);
const FVector2D Icon20x20(20.0f, 20.0f);
const FVector2D Icon40x40(40.0f, 40.0f);
TSharedRef< FSlateStyleSet > FAyonStyle::Create()
{
TSharedRef< FSlateStyleSet > Style = MakeShareable(new FSlateStyleSet("AyonStyle"));
Style->SetContentRoot(IPluginManager::Get().FindPlugin("Ayon")->GetBaseDir() / TEXT("Resources"));
Style->Set("Ayon.AyonTools", new IMAGE_BRUSH(TEXT("ayon40"), Icon40x40));
Style->Set("Ayon.AyonToolsDialog", new IMAGE_BRUSH(TEXT("ayon40"), Icon40x40));
return Style;
}
void FAyonStyle::ReloadTextures()
{
if (FSlateApplication::IsInitialized())
{
FSlateApplication::Get().GetRenderer()->ReloadTextureResources();
}
}
const ISlateStyle& FAyonStyle::Get()
{
return *AyonStyleInstance;
}

Some files were not shown because too many files have changed in this diff Show more