From 7a164032e031b8425aacdd2b998bed7617d5ba85 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 28 Jun 2023 18:35:41 +0100 Subject: [PATCH 01/18] Working callback for managing Xgen sidecar files. --- openpype/hosts/maya/api/pipeline.py | 103 ++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/openpype/hosts/maya/api/pipeline.py b/openpype/hosts/maya/api/pipeline.py index 5323717fa7..27d489418f 100644 --- a/openpype/hosts/maya/api/pipeline.py +++ b/openpype/hosts/maya/api/pipeline.py @@ -2,6 +2,7 @@ import os import errno import logging import contextlib +import shutil from maya import utils, cmds, OpenMaya import maya.api.OpenMaya as om @@ -113,6 +114,9 @@ class MayaHost(HostBase, IWorkfileHost, ILoadHost): register_event_callback("taskChanged", on_task_changed) register_event_callback("workfile.open.before", before_workfile_open) register_event_callback("workfile.save.before", before_workfile_save) + register_event_callback( + "workfile.save.before", workfile_save_before_xgen + ) register_event_callback("workfile.save.before", after_workfile_save) def open_workfile(self, filepath): @@ -681,6 +685,105 @@ def before_workfile_save(event): create_workspace_mel(workdir_path, project_name) +def display_warning(message, show_cancel=False): + """Show feedback to user. + + Returns: + bool + """ + + from qtpy import QtWidgets + + accept = QtWidgets.QMessageBox.Ok + if show_cancel: + buttons = accept | QtWidgets.QMessageBox.Cancel + else: + buttons = accept + + state = QtWidgets.QMessageBox.warning( + None, + "", + message, + buttons=buttons, + defaultButton=accept + ) + + return state == accept + + +def workfile_save_before_xgen(event): + current_work_dir = legacy_io.Session["AVALON_WORKDIR"].replace("\\", "/") + expected_work_dir = event.data["workdir_path"].replace("\\", "/") + if current_work_dir == expected_work_dir: + return + + palettes = cmds.ls(type="xgmPalette", long=True) + if not palettes: + return + + import xgenm + + transfers = [] + overwrites = [] + attribute_changes = {} + attrs = ["xgFileName", "xgBaseFile"] + for palette in palettes: + project_path = xgenm.getAttr("xgProjectPath", palette.replace("|", "")) + _, maya_extension = os.path.splitext(event.data["filename"]) + + for attr in attrs: + node_attr = "{}.{}".format(palette, attr) + attr_value = cmds.getAttr(node_attr) + + if not attr_value: + continue + + source = os.path.join(project_path, attr_value) + + attr_value = event.data["filename"].replace( + maya_extension, + "__{}{}".format( + palette.replace("|", "").replace(":", "__"), + os.path.splitext(attr_value)[1] + ) + ) + target = os.path.join(expected_work_dir, attr_value) + + transfers.append((source, target)) + attribute_changes[node_attr] = attr_value + + relative_path = xgenm.getAttr( + "xgDataPath", palette.replace("|", "") + ).split(os.pathsep)[0] + absolute_path = relative_path.replace("${PROJECT}", project_path) + for root, _, files in os.walk(absolute_path): + for f in files: + source = os.path.join(root, f).replace("\\", "/") + target = source.replace(project_path, expected_work_dir + "/") + transfers.append((source, target)) + if os.path.exists(target): + overwrites.append(target) + + # Ask user about overwriting files. + msg = ( + "WARNING! Potential loss of data.\n\n" + "Found duplicate Xgen files in new context.\n" + "Do you want to overwrite?\n\n{}".format("\n".join(overwrites)) + ) + if overwrites: + accept = display_warning(msg, show_cancel=True) + if not accept: + return + + for attribute, value in attribute_changes.items(): + cmds.setAttr(attribute, value, type="string") + + for source, destination in transfers: + if not os.path.exists(os.path.dirname(destination)): + os.makedirs(os.path.dirname(destination)) + shutil.copy(source, destination) + + def after_workfile_save(event): workfile_name = event["filename"] if ( From 25a03628c949df07af76dbedb1b30767f8ffc37f Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 29 Jun 2023 08:49:43 +0100 Subject: [PATCH 02/18] BigRoy feedback --- openpype/hosts/maya/api/pipeline.py | 39 +++++++++++++++++++---------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/openpype/hosts/maya/api/pipeline.py b/openpype/hosts/maya/api/pipeline.py index 27d489418f..45064d53f9 100644 --- a/openpype/hosts/maya/api/pipeline.py +++ b/openpype/hosts/maya/api/pipeline.py @@ -712,6 +712,20 @@ def display_warning(message, show_cancel=False): def workfile_save_before_xgen(event): + """Manage Xgen external files when switching context. + + Xgen has various external files that needs to be unique and relative to the + workfile, so we need to copy and potentially overwrite these files when + switching context. + + Args: + event (Event) - openpype/lib/events.py + """ + if not cmds.pluginInfo("xgenToolkit", query=True, loaded=True): + return + + import xgenm + current_work_dir = legacy_io.Session["AVALON_WORKDIR"].replace("\\", "/") expected_work_dir = event.data["workdir_path"].replace("\\", "/") if current_work_dir == expected_work_dir: @@ -721,14 +735,13 @@ def workfile_save_before_xgen(event): if not palettes: return - import xgenm - transfers = [] overwrites = [] attribute_changes = {} attrs = ["xgFileName", "xgBaseFile"] for palette in palettes: - project_path = xgenm.getAttr("xgProjectPath", palette.replace("|", "")) + sanitized_palette = palette.replace("|", "") + project_path = xgenm.getAttr("xgProjectPath", sanitized_palette) _, maya_extension = os.path.splitext(event.data["filename"]) for attr in attrs: @@ -743,7 +756,7 @@ def workfile_save_before_xgen(event): attr_value = event.data["filename"].replace( maya_extension, "__{}{}".format( - palette.replace("|", "").replace(":", "__"), + sanitized_palette.replace(":", "__"), os.path.splitext(attr_value)[1] ) ) @@ -753,7 +766,7 @@ def workfile_save_before_xgen(event): attribute_changes[node_attr] = attr_value relative_path = xgenm.getAttr( - "xgDataPath", palette.replace("|", "") + "xgDataPath", sanitized_palette ).split(os.pathsep)[0] absolute_path = relative_path.replace("${PROJECT}", project_path) for root, _, files in os.walk(absolute_path): @@ -765,24 +778,24 @@ def workfile_save_before_xgen(event): overwrites.append(target) # Ask user about overwriting files. - msg = ( - "WARNING! Potential loss of data.\n\n" - "Found duplicate Xgen files in new context.\n" - "Do you want to overwrite?\n\n{}".format("\n".join(overwrites)) - ) if overwrites: + msg = ( + "WARNING! Potential loss of data.\n\n" + "Found duplicate Xgen files in new context.\n" + "Do you want to overwrite?\n\n{}".format("\n".join(overwrites)) + ) accept = display_warning(msg, show_cancel=True) if not accept: return - for attribute, value in attribute_changes.items(): - cmds.setAttr(attribute, value, type="string") - for source, destination in transfers: if not os.path.exists(os.path.dirname(destination)): os.makedirs(os.path.dirname(destination)) shutil.copy(source, destination) + for attribute, value in attribute_changes.items(): + cmds.setAttr(attribute, value, type="string") + def after_workfile_save(event): workfile_name = event["filename"] From 7ca847ce247d7278190143885d4cb5a3a6ba5267 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 29 Jun 2023 10:36:41 +0100 Subject: [PATCH 03/18] Change name of warning method. --- openpype/hosts/maya/api/pipeline.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/api/pipeline.py b/openpype/hosts/maya/api/pipeline.py index 8e5c2f9fb0..98ebd9f028 100644 --- a/openpype/hosts/maya/api/pipeline.py +++ b/openpype/hosts/maya/api/pipeline.py @@ -685,7 +685,7 @@ def before_workfile_save(event): create_workspace_mel(workdir_path, project_name) -def display_warning(message, show_cancel=False): +def prompt_warning(message, show_cancel=False): """Show feedback to user. Returns: @@ -784,7 +784,7 @@ def workfile_save_before_xgen(event): "Found duplicate Xgen files in new context.\n" "Do you want to overwrite?\n\n{}".format("\n".join(overwrites)) ) - accept = display_warning(msg, show_cancel=True) + accept = prompt_warning(msg, show_cancel=True) if not accept: return From aa5236128b5a8f63cfa0e2495c28ad1adaf61693 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Fri, 30 Jun 2023 08:15:07 +0100 Subject: [PATCH 04/18] Update openpype/hosts/maya/api/pipeline.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/hosts/maya/api/pipeline.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/maya/api/pipeline.py b/openpype/hosts/maya/api/pipeline.py index 98ebd9f028..c2e4ffaba0 100644 --- a/openpype/hosts/maya/api/pipeline.py +++ b/openpype/hosts/maya/api/pipeline.py @@ -688,9 +688,9 @@ def before_workfile_save(event): def prompt_warning(message, show_cancel=False): """Show feedback to user. - Returns: - bool - """ + Returns: + bool + """ from qtpy import QtWidgets From 2a40984858d20d47a7f99f5b8ca070d0e63159f4 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 30 Jun 2023 08:16:37 +0100 Subject: [PATCH 05/18] import at top --- openpype/hosts/maya/api/pipeline.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/openpype/hosts/maya/api/pipeline.py b/openpype/hosts/maya/api/pipeline.py index 98ebd9f028..33ab2ac71e 100644 --- a/openpype/hosts/maya/api/pipeline.py +++ b/openpype/hosts/maya/api/pipeline.py @@ -8,6 +8,7 @@ from maya import utils, cmds, OpenMaya import maya.api.OpenMaya as om import pyblish.api +from qtpy import QtWidgets from openpype.settings import get_project_settings from openpype.host import ( @@ -691,9 +692,6 @@ def prompt_warning(message, show_cancel=False): Returns: bool """ - - from qtpy import QtWidgets - accept = QtWidgets.QMessageBox.Ok if show_cancel: buttons = accept | QtWidgets.QMessageBox.Cancel From 0033efe5b181f78e4fe13216305933ecaa1fb447 Mon Sep 17 00:00:00 2001 From: "clement.hector" Date: Fri, 30 Jun 2023 15:08:38 +0200 Subject: [PATCH 06/18] catch assertion error --- openpype/hosts/maya/api/workfile_template_builder.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/api/workfile_template_builder.py b/openpype/hosts/maya/api/workfile_template_builder.py index e2f30f46d0..ba3435b60d 100644 --- a/openpype/hosts/maya/api/workfile_template_builder.py +++ b/openpype/hosts/maya/api/workfile_template_builder.py @@ -272,7 +272,12 @@ class MayaPlaceholderLoadPlugin(PlaceholderPlugin, PlaceholderLoadMixin): return roots = cmds.sets(container, q=True) - ref_node = get_reference_node(roots) + ref_node = None + try: + ref_node = get_reference_node(roots) + except AssertionError as e: + self.log.info(e.args[0]) + nodes_to_parent = [] for root in roots: if ref_node: From 13820bf99b54dd42e702da8df027497e9932b1f0 Mon Sep 17 00:00:00 2001 From: "clement.hector" Date: Fri, 30 Jun 2023 15:15:06 +0200 Subject: [PATCH 07/18] indent correction --- openpype/hosts/maya/api/workfile_template_builder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/api/workfile_template_builder.py b/openpype/hosts/maya/api/workfile_template_builder.py index ba3435b60d..865f497710 100644 --- a/openpype/hosts/maya/api/workfile_template_builder.py +++ b/openpype/hosts/maya/api/workfile_template_builder.py @@ -274,7 +274,7 @@ class MayaPlaceholderLoadPlugin(PlaceholderPlugin, PlaceholderLoadMixin): roots = cmds.sets(container, q=True) ref_node = None try: - ref_node = get_reference_node(roots) + ref_node = get_reference_node(roots) except AssertionError as e: self.log.info(e.args[0]) From 8e9f4eb40e4d5595c9786f4b6dc7ffd9a4dcfdc6 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 30 Jun 2023 15:40:31 +0100 Subject: [PATCH 08/18] Log message about overwrites and continue --- openpype/hosts/maya/api/pipeline.py | 35 +++++------------------------ 1 file changed, 5 insertions(+), 30 deletions(-) diff --git a/openpype/hosts/maya/api/pipeline.py b/openpype/hosts/maya/api/pipeline.py index 8cc4359205..9fab825105 100644 --- a/openpype/hosts/maya/api/pipeline.py +++ b/openpype/hosts/maya/api/pipeline.py @@ -686,30 +686,6 @@ def before_workfile_save(event): create_workspace_mel(workdir_path, project_name) -def prompt_warning(message, show_cancel=False): - """Show feedback to user. - - Returns: - bool - """ - - accept = QtWidgets.QMessageBox.Ok - if show_cancel: - buttons = accept | QtWidgets.QMessageBox.Cancel - else: - buttons = accept - - state = QtWidgets.QMessageBox.warning( - None, - "", - message, - buttons=buttons, - defaultButton=accept - ) - - return state == accept - - def workfile_save_before_xgen(event): """Manage Xgen external files when switching context. @@ -778,14 +754,13 @@ def workfile_save_before_xgen(event): # Ask user about overwriting files. if overwrites: - msg = ( + log.warning( "WARNING! Potential loss of data.\n\n" - "Found duplicate Xgen files in new context.\n" - "Do you want to overwrite?\n\n{}".format("\n".join(overwrites)) + "Found duplicate Xgen files in new context.\n{}".format( + "\n".join(overwrites) + ) ) - accept = prompt_warning(msg, show_cancel=True) - if not accept: - return + return for source, destination in transfers: if not os.path.exists(os.path.dirname(destination)): From e7bdf25b9a126bf0e4cffbcc6a2529db1f15af37 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 30 Jun 2023 15:41:39 +0100 Subject: [PATCH 09/18] HOund --- openpype/hosts/maya/api/pipeline.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/maya/api/pipeline.py b/openpype/hosts/maya/api/pipeline.py index 9fab825105..b4042fd3d7 100644 --- a/openpype/hosts/maya/api/pipeline.py +++ b/openpype/hosts/maya/api/pipeline.py @@ -8,7 +8,6 @@ from maya import utils, cmds, OpenMaya import maya.api.OpenMaya as om import pyblish.api -from qtpy import QtWidgets from openpype.settings import get_project_settings from openpype.host import ( From 8713759c18ba8e5b617ada2add27bbc154cc6069 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 30 Jun 2023 15:53:08 +0100 Subject: [PATCH 10/18] Hound --- openpype/hosts/maya/api/pipeline.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/hosts/maya/api/pipeline.py b/openpype/hosts/maya/api/pipeline.py index b4042fd3d7..9fab825105 100644 --- a/openpype/hosts/maya/api/pipeline.py +++ b/openpype/hosts/maya/api/pipeline.py @@ -8,6 +8,7 @@ from maya import utils, cmds, OpenMaya import maya.api.OpenMaya as om import pyblish.api +from qtpy import QtWidgets from openpype.settings import get_project_settings from openpype.host import ( From aaf3a9acfefa4f7f9663c4d96ec273e09a7d4f0c Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 30 Jun 2023 15:53:34 +0100 Subject: [PATCH 11/18] Hound --- openpype/hosts/maya/api/pipeline.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/maya/api/pipeline.py b/openpype/hosts/maya/api/pipeline.py index 9fab825105..b4042fd3d7 100644 --- a/openpype/hosts/maya/api/pipeline.py +++ b/openpype/hosts/maya/api/pipeline.py @@ -8,7 +8,6 @@ from maya import utils, cmds, OpenMaya import maya.api.OpenMaya as om import pyblish.api -from qtpy import QtWidgets from openpype.settings import get_project_settings from openpype.host import ( From a2ef4e00138a9e5613516fcba3c10dc45e45f206 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 30 Jun 2023 22:05:45 +0200 Subject: [PATCH 12/18] Restructure `set_colorspace` - Only set `configFilePath` when OCIO env var is not set since it doesn't do anything if OCIO var is set anyway. - Set the Maya 2022+ default OCIO path using the resources path instead of "" to avoid Maya Save File on new file after launch (this also fixes the Save prompt on open last workfile feature with Global color management enabled) - Move all code related to applying the maya settings together after querying the settings - Swap around the `if use_workfile_settings` since the check was reversed - Use `get_current_project_name()` instead of environment vars --- openpype/hosts/maya/api/lib.py | 92 ++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 42 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index ce851d2dbe..f7ddab9f1e 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -3238,36 +3238,21 @@ def iter_shader_edits(relationships, shader_nodes, nodes_by_id, label=None): def set_colorspace(): - """Set Colorspace from project configuration - """ + """Set Colorspace from project configuration""" - # set color spaces for rendering space and view transforms - def _colormanage(**kwargs): - """Wrapper around `cmds.colorManagementPrefs`. - - This logs errors instead of raising an error so color management - settings get applied as much as possible. - - """ - assert len(kwargs) == 1, "Must receive one keyword argument" - try: - cmds.colorManagementPrefs(edit=True, **kwargs) - log.debug("Setting Color Management Preference: {}".format(kwargs)) - except RuntimeError as exc: - log.error(exc) - - project_name = os.getenv("AVALON_PROJECT") + project_name = get_current_project_name() imageio = get_project_settings(project_name)["maya"]["imageio"] # ocio compatibility variables ocio_v2_maya_version = 2022 maya_version = int(cmds.about(version=True)) ocio_v2_support = use_ocio_v2 = maya_version >= ocio_v2_maya_version + is_ocio_set = bool(os.environ.get("OCIO")) - root_dict = {} use_workfile_settings = imageio.get("workfile", {}).get("enabled") - if use_workfile_settings: + root_dict = imageio["workfile"] + else: # TODO: deprecated code from 3.15.5 - remove # Maya 2022+ introduces new OCIO v2 color management settings that # can override the old color management preferences. OpenPype has @@ -3290,40 +3275,63 @@ def set_colorspace(): if not isinstance(root_dict, dict): msg = "set_colorspace(): argument should be dictionary" log.error(msg) + return - else: - root_dict = imageio["workfile"] + # backward compatibility + # TODO: deprecated code from 3.15.5 - remove with deprecated code above + view_name = root_dict.get("viewTransform") + if view_name is None: + view_name = root_dict.get("viewName") log.debug(">> root_dict: {}".format(pformat(root_dict))) + if not root_dict: + return - if root_dict: - # enable color management - cmds.colorManagementPrefs(e=True, cmEnabled=True) - cmds.colorManagementPrefs(e=True, ocioRulesEnabled=True) + # set color spaces for rendering space and view transforms + def _colormanage(**kwargs): + """Wrapper around `cmds.colorManagementPrefs`. - # backward compatibility - # TODO: deprecated code from 3.15.5 - refactor to use new settings - view_name = root_dict.get("viewTransform") - if view_name is None: - view_name = root_dict.get("viewName") + This logs errors instead of raising an error so color management + settings get applied as much as possible. - if use_ocio_v2: - # Use Maya 2022+ default OCIO v2 config + """ + assert len(kwargs) == 1, "Must receive one keyword argument" + try: + cmds.colorManagementPrefs(edit=True, **kwargs) + log.debug("Setting Color Management Preference: {}".format(kwargs)) + except RuntimeError as exc: + log.error(exc) + + # enable color management + cmds.colorManagementPrefs(edit=True, cmEnabled=True) + cmds.colorManagementPrefs(edit=True, ocioRulesEnabled=True) + + if use_ocio_v2: + log.info("Using Maya OCIO v2") + if not is_ocio_set: + # Set the Maya 2022+ default OCIO v2 config file path log.info("Setting default Maya OCIO v2 config") - cmds.colorManagementPrefs(edit=True, configFilePath="") + # Note: Setting "" as value also sets this default however + # introduces a bug where launching a file on startup will prompt + # to save the empty scene before it, so we set using the path. + # This value has been the same for 2022, 2023 and 2024 + path = "/OCIO-configs/Maya2022-default/config.ocio" + cmds.colorManagementPrefs(edit=True, configFilePath=path) - # set rendering space and view transform - _colormanage(renderingSpaceName=root_dict["renderSpace"]) - _colormanage(viewName=view_name) - _colormanage(displayName=root_dict["displayName"]) - else: + # set rendering space and view transform + _colormanage(renderingSpaceName=root_dict["renderSpace"]) + _colormanage(viewName=view_name) + _colormanage(displayName=root_dict["displayName"]) + else: + log.info("Using Maya OCIO v1 (legacy)") + if not is_ocio_set: # Set the Maya default config file path log.info("Setting default Maya OCIO v1 legacy config") cmds.colorManagementPrefs(edit=True, configFilePath="legacy") - # set rendering space and view transform - _colormanage(renderingSpaceName=root_dict["renderSpace"]) - _colormanage(viewTransformName=view_name) + # set rendering space and view transform + _colormanage(renderingSpaceName=root_dict["renderSpace"]) + _colormanage(viewTransformName=view_name) @contextlib.contextmanager From d792ca37e3e1f53d48facab858774d7b406df8de Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 1 Jul 2023 03:34:07 +0000 Subject: [PATCH 13/18] chore(): update bug report / version --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 5d464d0532..652dbb8597 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -35,6 +35,7 @@ body: label: Version description: What version are you running? Look to OpenPype Tray options: + - 3.15.12-nightly.2 - 3.15.12-nightly.1 - 3.15.11 - 3.15.11-nightly.5 @@ -134,7 +135,6 @@ body: - 3.14.4-nightly.4 - 3.14.4-nightly.3 - 3.14.4-nightly.2 - - 3.14.4-nightly.1 validations: required: true - type: dropdown From fe58cb3c7cc4520b61c8b73c166f6694c5d29e91 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 3 Jul 2023 13:42:00 +0200 Subject: [PATCH 14/18] Enhancement: More descriptive error messages for Loaders (#5227) * More descriptive error messages for Loaders * Restructure error reporting --- openpype/pipeline/load/__init__.py | 4 ++ openpype/pipeline/load/utils.py | 37 +++++++++++++++---- .../tools/sceneinventory/switch_dialog.py | 24 +++++++++--- 3 files changed, 52 insertions(+), 13 deletions(-) diff --git a/openpype/pipeline/load/__init__.py b/openpype/pipeline/load/__init__.py index e9ac0df924..7320a9f0e8 100644 --- a/openpype/pipeline/load/__init__.py +++ b/openpype/pipeline/load/__init__.py @@ -4,6 +4,8 @@ from .utils import ( LoadError, IncompatibleLoaderError, InvalidRepresentationContext, + LoaderSwitchNotImplementedError, + LoaderNotFoundError, get_repres_contexts, get_contexts_for_repre_docs, @@ -55,6 +57,8 @@ __all__ = ( "LoadError", "IncompatibleLoaderError", "InvalidRepresentationContext", + "LoaderSwitchNotImplementedError", + "LoaderNotFoundError", "get_repres_contexts", "get_contexts_for_repre_docs", diff --git a/openpype/pipeline/load/utils.py b/openpype/pipeline/load/utils.py index fefdb8537b..2c40280ccd 100644 --- a/openpype/pipeline/load/utils.py +++ b/openpype/pipeline/load/utils.py @@ -79,6 +79,16 @@ class InvalidRepresentationContext(ValueError): pass +class LoaderSwitchNotImplementedError(NotImplementedError): + """Error when `switch` is used with Loader that has no implementation.""" + pass + + +class LoaderNotFoundError(RuntimeError): + """Error when Loader plugin for a loader name is not found.""" + pass + + def get_repres_contexts(representation_ids, dbcon=None): """Return parenthood context for representation. @@ -432,7 +442,10 @@ def remove_container(container): Loader = _get_container_loader(container) if not Loader: - raise RuntimeError("Can't remove container. See log for details.") + raise LoaderNotFoundError( + "Can't remove container because loader '{}' was not found." + .format(container.get("loader")) + ) loader = Loader(get_representation_context(container["representation"])) return loader.remove(container) @@ -480,7 +493,10 @@ def update_container(container, version=-1): # Run update on the Loader for this container Loader = _get_container_loader(container) if not Loader: - raise RuntimeError("Can't update container. See log for details.") + raise LoaderNotFoundError( + "Can't update container because loader '{}' was not found." + .format(container.get("loader")) + ) loader = Loader(get_representation_context(container["representation"])) return loader.update(container, new_representation) @@ -502,15 +518,18 @@ def switch_container(container, representation, loader_plugin=None): loader_plugin = _get_container_loader(container) if not loader_plugin: - raise RuntimeError("Can't switch container. See log for details.") + raise LoaderNotFoundError( + "Can't switch container because loader '{}' was not found." + .format(container.get("loader")) + ) if not hasattr(loader_plugin, "switch"): # Backwards compatibility (classes without switch support # might be better to just have "switch" raise NotImplementedError # on the base class of Loader\ - raise RuntimeError("Loader '{}' does not support 'switch'".format( - loader_plugin.label - )) + raise LoaderSwitchNotImplementedError( + "Loader {} does not support 'switch'".format(loader_plugin.label) + ) # Get the new representation to switch to project_name = legacy_io.active_project() @@ -520,7 +539,11 @@ def switch_container(container, representation, loader_plugin=None): new_context = get_representation_context(new_representation) if not is_compatible_loader(loader_plugin, new_context): - raise AssertionError("Must be compatible Loader") + raise IncompatibleLoaderError( + "Loader {} is incompatible with {}".format( + loader_plugin.__name__, new_context["subset"]["name"] + ) + ) loader = loader_plugin(new_context) diff --git a/openpype/tools/sceneinventory/switch_dialog.py b/openpype/tools/sceneinventory/switch_dialog.py index 4aaad38bbc..ce2272df57 100644 --- a/openpype/tools/sceneinventory/switch_dialog.py +++ b/openpype/tools/sceneinventory/switch_dialog.py @@ -19,6 +19,9 @@ from openpype.pipeline.load import ( switch_container, get_repres_contexts, loaders_from_repre_context, + LoaderSwitchNotImplementedError, + IncompatibleLoaderError, + LoaderNotFoundError ) from .widgets import ( @@ -1298,19 +1301,28 @@ class SwitchAssetDialog(QtWidgets.QDialog): else: repre_doc = repres_by_name[container_repre_name] + error = None try: switch_container(container, repre_doc, loader) + except ( + LoaderSwitchNotImplementedError, + IncompatibleLoaderError, + LoaderNotFoundError, + ) as exc: + error = str(exc) except Exception: - msg = ( + error = ( + "Switch asset failed. " + "Search console log for more details." + ) + if error is not None: + log.warning(( "Couldn't switch asset." "See traceback for more information." - ) - log.warning(msg, exc_info=True) + ), exc_info=True) dialog = QtWidgets.QMessageBox(self) dialog.setWindowTitle("Switch asset failed") - dialog.setText( - "Switch asset failed. Search console log for more details" - ) + dialog.setText(error) dialog.exec_() self.switched.emit() From f2315c6fd812308a605a9aba21d00473a8bb8c79 Mon Sep 17 00:00:00 2001 From: Mustafa-Zarkash Date: Mon, 3 Jul 2023 16:40:35 +0300 Subject: [PATCH 15/18] add geometry check --- .../plugins/publish/validate_abc_primitive_to_detail.py | 8 ++++++++ .../plugins/publish/validate_primitive_hierarchy_paths.py | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/openpype/hosts/houdini/plugins/publish/validate_abc_primitive_to_detail.py b/openpype/hosts/houdini/plugins/publish/validate_abc_primitive_to_detail.py index 86e92a052f..bef8db45a4 100644 --- a/openpype/hosts/houdini/plugins/publish/validate_abc_primitive_to_detail.py +++ b/openpype/hosts/houdini/plugins/publish/validate_abc_primitive_to_detail.py @@ -73,6 +73,14 @@ class ValidateAbcPrimitiveToDetail(pyblish.api.InstancePlugin): cls.log.debug("Checking Primitive to Detail pattern: %s" % pattern) cls.log.debug("Checking with path attribute: %s" % path_attr) + if not hasattr(output_node, "geometry"): + # In the case someone has explicitly set an Object + # node instead of a SOP node in Geometry context + # then for now we ignore - this allows us to also + # export object transforms. + cls.log.warning("No geometry output node found, skipping check..") + return + # Check if the primitive attribute exists frame = instance.data.get("frameStart", 0) geo = output_node.geometryAtFrame(frame) diff --git a/openpype/hosts/houdini/plugins/publish/validate_primitive_hierarchy_paths.py b/openpype/hosts/houdini/plugins/publish/validate_primitive_hierarchy_paths.py index d3a4c0cfbf..cd5e724ab3 100644 --- a/openpype/hosts/houdini/plugins/publish/validate_primitive_hierarchy_paths.py +++ b/openpype/hosts/houdini/plugins/publish/validate_primitive_hierarchy_paths.py @@ -60,6 +60,14 @@ class ValidatePrimitiveHierarchyPaths(pyblish.api.InstancePlugin): cls.log.debug("Checking for attribute: %s" % path_attr) + if not hasattr(output_node, "geometry"): + # In the case someone has explicitly set an Object + # node instead of a SOP node in Geometry context + # then for now we ignore - this allows us to also + # export object transforms. + cls.log.warning("No geometry output node found, skipping check..") + return + # Check if the primitive attribute exists frame = instance.data.get("frameStart", 0) geo = output_node.geometryAtFrame(frame) From 744095eb161939dec966f6e52c215b72c775a521 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 3 Jul 2023 16:39:40 +0200 Subject: [PATCH 16/18] maya: allign default settings to distributed aces 1.2 config --- openpype/settings/defaults/project_settings/maya.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 19c3da13e6..e3fc5f0723 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -421,9 +421,9 @@ }, "workfile": { "enabled": false, - "renderSpace": "ACEScg", - "displayName": "sRGB", - "viewName": "ACES 1.0 SDR-video" + "renderSpace": "ACES - ACEScg", + "displayName": "ACES", + "viewName": "sRGB" }, "colorManagementPreference_v2": { "enabled": true, From 39385995785f353db4c1d9521760cae46c637035 Mon Sep 17 00:00:00 2001 From: Mustafa-Zarkash Date: Mon, 3 Jul 2023 18:07:34 +0300 Subject: [PATCH 17/18] add geometry check --- .../hosts/houdini/plugins/publish/validate_vdb_output_node.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py b/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py index 674782179c..ee46b746a2 100644 --- a/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py +++ b/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py @@ -55,6 +55,8 @@ def get_geometry_at_frame(sop_node, frame, force=True): """Return geometry at frame but force a cooked value.""" with update_mode_context(hou.updateMode.AutoUpdate): sop_node.cook(force=force, frame_range=(frame, frame)) + if not hasattr(sop_node, "geometry"): + return return sop_node.geometryAtFrame(frame) From b6d4f1b142bdc73aeb6d6f402b5897d06b7a31f7 Mon Sep 17 00:00:00 2001 From: Mustafa-Zarkash Date: Tue, 4 Jul 2023 10:53:28 +0300 Subject: [PATCH 18/18] move check outside with --- .../hosts/houdini/plugins/publish/validate_vdb_output_node.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py b/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py index ee46b746a2..b51e1007f0 100644 --- a/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py +++ b/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py @@ -53,10 +53,10 @@ def update_mode_context(mode): def get_geometry_at_frame(sop_node, frame, force=True): """Return geometry at frame but force a cooked value.""" + if not hasattr(sop_node, "geometry"): + return with update_mode_context(hou.updateMode.AutoUpdate): sop_node.cook(force=force, frame_range=(frame, frame)) - if not hasattr(sop_node, "geometry"): - return return sop_node.geometryAtFrame(frame)