From 908b8e3fb69a2347d6b6208f32c546dc55ba061a Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 17 Oct 2023 12:22:01 +0200 Subject: [PATCH 1/7] Add MayaUsdReferenceLoader to reference USD as Maya native geometry using `mayaUSDImport` file translator --- openpype/hosts/maya/api/plugin.py | 3 +- .../hosts/maya/plugins/load/load_reference.py | 100 +++++++++++++++++- 2 files changed, 101 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/api/plugin.py b/openpype/hosts/maya/api/plugin.py index 3b54954c8a..07167a9a32 100644 --- a/openpype/hosts/maya/api/plugin.py +++ b/openpype/hosts/maya/api/plugin.py @@ -771,7 +771,8 @@ class ReferenceLoader(Loader): "ma": "mayaAscii", "mb": "mayaBinary", "abc": "Alembic", - "fbx": "FBX" + "fbx": "FBX", + "usd": "USD Import" }.get(representation["name"]) assert file_type, "Unsupported representation: %s" % representation diff --git a/openpype/hosts/maya/plugins/load/load_reference.py b/openpype/hosts/maya/plugins/load/load_reference.py index 4b704fa706..0d7f08d3c3 100644 --- a/openpype/hosts/maya/plugins/load/load_reference.py +++ b/openpype/hosts/maya/plugins/load/load_reference.py @@ -1,7 +1,9 @@ import os import difflib import contextlib + from maya import cmds +import qargparse from openpype.settings import get_project_settings import openpype.hosts.maya.api.plugin @@ -128,6 +130,12 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): if not attach_to_root: group_name = namespace + kwargs = {} + if "file_options" in options: + kwargs["options"] = options["file_options"] + if "file_type" in options: + kwargs["type"] = options["file_type"] + path = self.filepath_from_context(context) with maintained_selection(): cmds.loadPlugin("AbcImport.mll", quiet=True) @@ -139,7 +147,8 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): reference=True, returnNewNodes=True, groupReference=attach_to_root, - groupName=group_name) + groupName=group_name, + **kwargs) shapes = cmds.ls(nodes, shapes=True, long=True) @@ -251,3 +260,92 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): else: self.log.warning("This version of Maya does not support locking of" " transforms of cameras.") + + +class MayaUSDReferenceLoader(ReferenceLoader): + """Reference USD file to native Maya nodes using MayaUSDImport reference""" + + families = ["usd"] + representations = ["usd"] + extensions = {"usd", "usda", "usdc"} + + options = ReferenceLoader.options + [ + qargparse.Boolean( + "readAnimData", + label="Load anim data", + default=True, + help="Load animation data from USD file" + ), + qargparse.Boolean( + "useAsAnimationCache", + label="Use as animation cache", + default=True, + help=( + "Imports geometry prims with time-sampled point data using a " + "point-based deformer that references the imported " + "USD file.\n" + "This provides better import and playback performance when " + "importing time-sampled geometry from USD, and should " + "reduce the weight of the resulting Maya scene." + ) + ), + qargparse.Boolean( + "importInstances", + label="Import instances", + default=True, + help=( + "Import USD instanced geometries as Maya instanced shapes. " + "Will flatten the scene otherwise." + ) + ), + qargparse.String( + "primPath", + label="Prim Path", + default="/", + help=( + "Name of the USD scope where traversing will begin.\n" + "The prim at the specified primPath (including the prim) will " + "be imported.\n" + "Specifying the pseudo-root (/) means you want " + "to import everything in the file.\n" + "If the passed prim path is empty, it will first try to " + "import the defaultPrim for the rootLayer if it exists.\n" + "Otherwise, it will behave as if the pseudo-root was passed " + "in." + ) + ) + ] + + file_type = "USD Import" + + def process_reference(self, context, name, namespace, options): + cmds.loadPlugin("mayaUsdPlugin", quiet=True) + + def bool_option(key, default): + # Shorthand for getting optional boolean file option from options + value = int(bool(options.get(key, default))) + return "{}={}".format(key, value) + + def string_option(key, default): + # Shorthand for getting optional string file option from options + value = str(options.get(key, default)) + return "{}={}".format(key, value) + + options["file_options"] = ";".join([ + string_option("primPath", default="/"), + bool_option("importInstances", default=True), + bool_option("useAsAnimationCache", default=True), + bool_option("readAnimData", default=True), + # TODO: Expose more parameters + # "preferredMaterial=none", + # "importRelativeTextures=Automatic", + # "useCustomFrameRange=0", + # "startTime=0", + # "endTime=0", + # "importUSDZTextures=0" + ]) + options["file_type"] = self.file_type + + return super(MayaUSDReferenceLoader, self).process_reference( + context, name, namespace, options + ) From bf96b15b90e04b0721af853adeb161707ebd5b8c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Nov 2023 13:50:44 +0100 Subject: [PATCH 2/7] center publisher window on first show --- openpype/tools/publisher/window.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/tools/publisher/window.py b/openpype/tools/publisher/window.py index 312cf1dd5c..2416763c27 100644 --- a/openpype/tools/publisher/window.py +++ b/openpype/tools/publisher/window.py @@ -15,6 +15,7 @@ from openpype.tools.utils import ( MessageOverlayObject, PixmapLabel, ) +from openpype.tools.utils.lib import center_window from .constants import ResetKeySequence from .publish_report_viewer import PublishReportViewerWidget @@ -529,6 +530,7 @@ class PublisherWindow(QtWidgets.QDialog): def _on_first_show(self): self.resize(self.default_width, self.default_height) self.setStyleSheet(style.load_stylesheet()) + center_window(self) self._reset_on_show = self._reset_on_first_show def _on_show_timer(self): From 63af150dd8f08b280bb6cb1ebe918dd4025e50a5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Nov 2023 17:22:02 +0100 Subject: [PATCH 3/7] confirm of instance context changes reset origin of input fields --- openpype/tools/publisher/widgets/widgets.py | 29 +++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/openpype/tools/publisher/widgets/widgets.py b/openpype/tools/publisher/widgets/widgets.py index 1bbe73381f..77ebc3f0bb 100644 --- a/openpype/tools/publisher/widgets/widgets.py +++ b/openpype/tools/publisher/widgets/widgets.py @@ -579,6 +579,10 @@ class AssetsField(BaseClickableFrame): """Change to asset names set with last `set_selected_items` call.""" self.set_selected_items(self._origin_value) + def confirm_value(self): + self._origin_value = copy.deepcopy(self._selected_items) + self._has_value_changed = False + class TasksComboboxProxy(QtCore.QSortFilterProxyModel): def __init__(self, *args, **kwargs): @@ -785,6 +789,18 @@ class TasksCombobox(QtWidgets.QComboBox): self._set_is_valid(is_valid) + def confirm_value(self): + new_task_name = self._selected_items[0] + origin_value = copy.deepcopy(self._origin_value) + new_origin_value = [ + (asset_name, new_task_name) + for (asset_name, task_name) in origin_value + ] + + self._origin_value = new_origin_value + self._origin_selection = copy.deepcopy(self._selected_items) + self._has_value_changed = False + def set_selected_items(self, asset_task_combinations=None): """Set items for selected instances. @@ -919,6 +935,10 @@ class VariantInputWidget(PlaceholderLineEdit): """Change text of multiselection.""" self._multiselection_text = text + def confirm_value(self): + self._origin_value = copy.deepcopy(self._current_value) + self._has_value_changed = False + def _set_is_valid(self, valid): if valid == self._is_valid: return @@ -1210,6 +1230,15 @@ class GlobalAttrsWidget(QtWidgets.QWidget): self._set_btns_enabled(False) self._set_btns_visible(invalid_tasks) + if variant_value is not None: + self.variant_input.confirm_value() + + if asset_name is not None: + self.asset_value_widget.confirm_value() + + if task_name is not None: + self.task_value_widget.confirm_value() + self.instance_context_changed.emit() def _on_cancel(self): From e4ed21623a57618fa6889750c5cf97ec9cc1872d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Nov 2023 17:35:48 +0100 Subject: [PATCH 4/7] fix task combinations --- openpype/tools/publisher/widgets/widgets.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/openpype/tools/publisher/widgets/widgets.py b/openpype/tools/publisher/widgets/widgets.py index 77ebc3f0bb..9b31697749 100644 --- a/openpype/tools/publisher/widgets/widgets.py +++ b/openpype/tools/publisher/widgets/widgets.py @@ -789,15 +789,12 @@ class TasksCombobox(QtWidgets.QComboBox): self._set_is_valid(is_valid) - def confirm_value(self): + def confirm_value(self, asset_names): new_task_name = self._selected_items[0] - origin_value = copy.deepcopy(self._origin_value) - new_origin_value = [ + self._origin_value = [ (asset_name, new_task_name) - for (asset_name, task_name) in origin_value + for asset_name in asset_names ] - - self._origin_value = new_origin_value self._origin_selection = copy.deepcopy(self._selected_items) self._has_value_changed = False @@ -1180,6 +1177,7 @@ class GlobalAttrsWidget(QtWidgets.QWidget): subset_names = set() invalid_tasks = False + asset_names = [] for instance in self._current_instances: new_variant_value = instance.get("variant") new_asset_name = instance.get("asset") @@ -1193,6 +1191,7 @@ class GlobalAttrsWidget(QtWidgets.QWidget): if task_name is not None: new_task_name = task_name + asset_names.append(new_asset_name) try: new_subset_name = self._controller.get_subset_name( instance.creator_identifier, @@ -1237,7 +1236,7 @@ class GlobalAttrsWidget(QtWidgets.QWidget): self.asset_value_widget.confirm_value() if task_name is not None: - self.task_value_widget.confirm_value() + self.task_value_widget.confirm_value(asset_names) self.instance_context_changed.emit() From b8ed125569bdfd7c343d08483bfd70431d38f11d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Nov 2023 17:44:33 +0100 Subject: [PATCH 5/7] set spacing between buttons --- openpype/tools/publisher/widgets/widgets.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/tools/publisher/widgets/widgets.py b/openpype/tools/publisher/widgets/widgets.py index 9b31697749..6dbeaad821 100644 --- a/openpype/tools/publisher/widgets/widgets.py +++ b/openpype/tools/publisher/widgets/widgets.py @@ -1127,6 +1127,7 @@ class GlobalAttrsWidget(QtWidgets.QWidget): btns_layout = QtWidgets.QHBoxLayout() btns_layout.setContentsMargins(0, 0, 0, 0) btns_layout.addStretch(1) + btns_layout.setSpacing(5) btns_layout.addWidget(submit_btn) btns_layout.addWidget(cancel_btn) From 8c8c083395b77e920254e8dd5d83ad06eb0c636d Mon Sep 17 00:00:00 2001 From: Ynbot Date: Wed, 8 Nov 2023 03:24:51 +0000 Subject: [PATCH 6/7] [Automated] Bump version --- openpype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/version.py b/openpype/version.py index 9832c77291..8500b78966 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.17.5" +__version__ = "3.17.6-nightly.1" From 3cd8fc6a0a8162a73d647f17a66294b0c79b2724 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 8 Nov 2023 03:25:29 +0000 Subject: [PATCH 7/7] 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 bdfc2ad46f..5d4db81a77 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.17.6-nightly.1 - 3.17.5 - 3.17.5-nightly.3 - 3.17.5-nightly.2 @@ -134,7 +135,6 @@ body: - 3.15.1 - 3.15.1-nightly.6 - 3.15.1-nightly.5 - - 3.15.1-nightly.4 validations: required: true - type: dropdown