diff --git a/client/ayon_core/hosts/unreal/api/hierarchy.py b/client/ayon_core/hosts/unreal/api/hierarchy.py new file mode 100644 index 0000000000..c8e40bd73c --- /dev/null +++ b/client/ayon_core/hosts/unreal/api/hierarchy.py @@ -0,0 +1,79 @@ +import os + +from ayon_api import get_folders_hierarchy +from ayon_core.settings import get_project_settings +from ayon_core.hosts.unreal.api.pipeline import ( + generate_sequence, + set_sequence_hierarchy, +) + + +def get_default_sequence_path(settings): + """Get default render folder from blender settings.""" + + sequence_path = settings['unreal']['sequence_path'] + if sequence_path.endswith("/"): + sequence_path = sequence_path.rstrip("/") + + return f"/Game/{sequence_path}" + + +def _create_sequence( + element, sequence_path, parent_path="", parent_seq=None, + parent_fr=None +): + path = f"{parent_path}/{element['name']}" + hierarchy_dir = f"{sequence_path}{path}" + + # Create sequence for the current element + sequence, frame_range = generate_sequence(element["name"], hierarchy_dir) + + # Add the sequence to the parent element if provided + if parent_seq: + set_sequence_hierarchy( + parent_seq, sequence, + parent_fr[1], + frame_range[0], frame_range[1], + []) + + if not element["children"]: + return + + # Traverse the children and create sequences recursively + for child in element["children"]: + _create_sequence( + child, sequence_path, parent_path=path, + parent_seq=sequence, parent_fr=frame_range) + + +def build_sequence_hierarchy(): + """ + Builds the sequence hierarchy by creating sequences from the root element. + + Raises: + ValueError: If the sequence root element is not found in the hierarchy. + """ + print("Building sequence hierarchy...") + + project = os.environ.get("AVALON_PROJECT") + + settings = get_project_settings(project) + sequence_path = get_default_sequence_path(settings) + + sequence_root_name = "shots" + + hierarchy = get_folders_hierarchy(project_name=project)["hierarchy"] + + # Find the sequence root element in the hierarchy + sequence_root = next(( + element + for element in hierarchy + if element["name"] == sequence_root_name + ), None) + + # Raise an error if the sequence root element is not found + if not sequence_root: + raise ValueError(f"Could not find {sequence_root_name} in hierarchy") + + # Start creating sequences from the root element + _create_sequence(sequence_root, sequence_path) diff --git a/client/ayon_core/hosts/unreal/api/pipeline.py b/client/ayon_core/hosts/unreal/api/pipeline.py index 922fc8abd8..b7d9b55362 100644 --- a/client/ayon_core/hosts/unreal/api/pipeline.py +++ b/client/ayon_core/hosts/unreal/api/pipeline.py @@ -11,13 +11,13 @@ import pyblish.api from ayon_core.client import get_asset_by_name, get_assets from ayon_core.pipeline import ( + AYON_CONTAINER_ID, register_loader_plugin_path, register_creator_plugin_path, register_inventory_action_path, deregister_loader_plugin_path, deregister_creator_plugin_path, deregister_inventory_action_path, - AYON_CONTAINER_ID, get_current_project_name, ) from ayon_core.tools.utils import host_tools @@ -590,12 +590,12 @@ def set_sequence_hierarchy( hid_section.set_level_names(maps) -def generate_sequence(h, h_dir): +def generate_sequence(name, hierarchy_dir): tools = unreal.AssetToolsHelpers().get_asset_tools() sequence = tools.create_asset( - asset_name=h, - package_path=h_dir, + asset_name=name, + package_path=hierarchy_dir, asset_class=unreal.LevelSequence, factory=unreal.LevelSequenceFactoryNew() ) @@ -603,8 +603,8 @@ def generate_sequence(h, h_dir): project_name = get_current_project_name() asset_data = get_asset_by_name( project_name, - h_dir.split('/')[-1], - fields=["_id", "data.fps"] + name, + fields=["_id", "data.fps", "data.clipIn", "data.clipOut"] ) start_frames = [] @@ -625,8 +625,12 @@ def generate_sequence(h, h_dir): fields=["_id", "data.clipIn", "data.clipOut"] )) - min_frame = min(start_frames) - max_frame = max(end_frames) + if elements: + min_frame = min(start_frames) + max_frame = max(end_frames) + else: + min_frame = asset_data.get('data').get("clipIn") + max_frame = asset_data.get('data').get("clipOut") fps = asset_data.get('data').get("fps") diff --git a/client/ayon_core/hosts/unreal/api/tools_ui.py b/client/ayon_core/hosts/unreal/api/tools_ui.py index 084da9a0f0..2e200e6c8d 100644 --- a/client/ayon_core/hosts/unreal/api/tools_ui.py +++ b/client/ayon_core/hosts/unreal/api/tools_ui.py @@ -8,6 +8,7 @@ from ayon_core import ( from ayon_core.tools.utils import host_tools from ayon_core.tools.utils.lib import qt_app_context from ayon_core.hosts.unreal.api import rendering +from ayon_core.hosts.unreal.api import hierarchy class ToolsBtnsWidget(QtWidgets.QWidget): @@ -21,6 +22,8 @@ class ToolsBtnsWidget(QtWidgets.QWidget): publish_btn = QtWidgets.QPushButton("Publisher...", self) manage_btn = QtWidgets.QPushButton("Manage...", self) render_btn = QtWidgets.QPushButton("Render...", self) + sequence_btn = QtWidgets.QPushButton( + "Build sequence hierarchy...", self) experimental_tools_btn = QtWidgets.QPushButton( "Experimental tools...", self ) @@ -31,6 +34,7 @@ class ToolsBtnsWidget(QtWidgets.QWidget): layout.addWidget(publish_btn, 0) layout.addWidget(manage_btn, 0) layout.addWidget(render_btn, 0) + layout.addWidget(sequence_btn, 0) layout.addWidget(experimental_tools_btn, 0) layout.addStretch(1) @@ -38,6 +42,7 @@ class ToolsBtnsWidget(QtWidgets.QWidget): publish_btn.clicked.connect(self._on_publish) manage_btn.clicked.connect(self._on_manage) render_btn.clicked.connect(self._on_render) + sequence_btn.clicked.connect(self._on_sequence) experimental_tools_btn.clicked.connect(self._on_experimental) def _on_create(self): @@ -55,6 +60,9 @@ class ToolsBtnsWidget(QtWidgets.QWidget): def _on_render(self): rendering.start_rendering() + def _on_sequence(self): + hierarchy.build_sequence_hierarchy() + def _on_experimental(self): self.tool_required.emit("experimental_tools") diff --git a/server_addon/unreal/server/settings.py b/server_addon/unreal/server/settings.py index 5f54fb6c75..3574c718b7 100644 --- a/server_addon/unreal/server/settings.py +++ b/server_addon/unreal/server/settings.py @@ -32,7 +32,15 @@ class UnrealSettings(BaseSettingsModel): False, title="Delete assets that are not matched" ) - render_config_path: str = SettingsField( + asset_path: str = Field( + "Ayon/Assets/", + title="Assets Path" + ) + sequence_path: str = Field( + "Ayon/Sequences/", + title="Sequences Path" + ) + render_config_path: str = Field( "", title="Render Config Path" ) @@ -54,6 +62,8 @@ class UnrealSettings(BaseSettingsModel): DEFAULT_VALUES = { "level_sequences_for_layouts": True, "delete_unmatched_assets": False, + "asset_path": "Ayon/Assets/", + "sequence_path": "Ayon/Sequences/", "render_config_path": "", "preroll_frames": 0, "render_format": "exr", diff --git a/server_addon/unreal/server/version.py b/server_addon/unreal/server/version.py index 3dc1f76bc6..485f44ac21 100644 --- a/server_addon/unreal/server/version.py +++ b/server_addon/unreal/server/version.py @@ -1 +1 @@ -__version__ = "0.1.0" +__version__ = "0.1.1"