From d4fdf8530605ca4f03957415f48d99eef292d11b Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 17 Apr 2024 22:45:47 +0800 Subject: [PATCH] Add Qt dialog to support users to choose their templates for project creation --- .../hosts/substancepainter/api/lib.py | 83 ----------- .../plugins/load/load_mesh.py | 136 +++++++++++++++--- .../server/settings/load_plugins.py | 36 +++-- 3 files changed, 145 insertions(+), 110 deletions(-) diff --git a/client/ayon_core/hosts/substancepainter/api/lib.py b/client/ayon_core/hosts/substancepainter/api/lib.py index f95e47f99d..64c39943ce 100644 --- a/client/ayon_core/hosts/substancepainter/api/lib.py +++ b/client/ayon_core/hosts/substancepainter/api/lib.py @@ -640,86 +640,3 @@ def prompt_new_file_with_mesh(mesh_filepath): return return project_mesh - - -def convert_substance_object_to_python(subst_proj_option="default"): - """Function to convert substance C++ objects to python instance. - It is made to avoid any possible ValueError when C++ objects casting - as python instance. - - Args: - subst_proj_option (str, optional): Substance project option. - Defaults to "default". - - Raises: - ValueError: Raise Error when unsupported Substance - Project was detected - - Returns: - python instance: converted python instance of the C++ objects. - """ - if subst_proj_option == "default": - return substance_painter.project.ProjectWorkflow.Default - elif subst_proj_option == "uvTile": - return substance_painter.project.ProjectWorkflow.UVTile - elif subst_proj_option == "textureSetPerUVTile": - return substance_painter.project.ProjectWorkflow.TextureSetPerUVTile - elif subst_proj_option == "PerFragment": - return substance_painter.project.TangentSpace.PerFragment - elif subst_proj_option == "PerVertex": - return substance_painter.project.TangentSpace.PerVertex - elif subst_proj_option == "DirectX": - return substance_painter.project.NormalMapFormat.DirectX - elif subst_proj_option == "OpenGL": - return substance_painter.project.NormalMapFormat.OpenGL - else: - raise ValueError( - f"Unsupported Substance Objects: {subst_proj_option}") - - -def parse_substance_attributes_setting(template_name, project_templates): - """Function to parse the dictionary from the AYON setting to be used - as the attributes for Substance Project Creation - - Args: - template_name (str): name of the template from the setting - project_templates (dict): project template data from the setting - - Returns: - dict: data to be used as attributes for Substance Project Creation - """ - attributes_data = {} - for template in project_templates: - if template["name"] == template_name: - attributes_data.update(template) - attributes_data["normal_map_format"] = convert_substance_object_to_python( - subst_proj_option=attributes_data["normal_map_format"]) - attributes_data["project_workflow"] = convert_substance_object_to_python( - subst_proj_option=attributes_data["project_workflow"]) - attributes_data["tangent_space_mode"] = convert_substance_object_to_python( - subst_proj_option=attributes_data["tangent_space_mode"]) - attributes_data.pop("name") - attributes_data.pop("preserve_strokes") - return attributes_data - - -def parse_subst_attrs_reloading_mesh(template_name, project_templates): - """Function to parse the substances attributes ('import_cameras' - and 'preserve_strokes') for reloading mesh - with the existing projects. - - Args: - template_name (str): name of the template from the setting - project_templates (dict): project template data from the setting - - Returns: - dict: data to be used as attributes for reloading mesh with the - existing project - """ - attributes_data = {} - for template in project_templates: - if template["name"] == template_name: - for key, value in template.items(): - if isinstance(value, bool): - attributes_data.update({key: value}) - return attributes_data diff --git a/client/ayon_core/hosts/substancepainter/plugins/load/load_mesh.py b/client/ayon_core/hosts/substancepainter/plugins/load/load_mesh.py index 49f11251c9..b377cf9a1d 100644 --- a/client/ayon_core/hosts/substancepainter/plugins/load/load_mesh.py +++ b/client/ayon_core/hosts/substancepainter/plugins/load/load_mesh.py @@ -1,23 +1,130 @@ +from qtpy import QtWidgets, QtCore from ayon_core.pipeline import ( load, get_representation_path, ) -from ayon_core.lib import EnumDef from ayon_core.pipeline.load import LoadError from ayon_core.hosts.substancepainter.api.pipeline import ( imprint_container, set_container_metadata, remove_container_metadata ) -from ayon_core.hosts.substancepainter.api.lib import ( - parse_substance_attributes_setting, - parse_subst_attrs_reloading_mesh -) - import substance_painter.project +def _convert(subst_attr): + """Function to convert substance C++ objects to python instance. + It is made to avoid any possible ValueError when C++ objects casting + as python instance. + + Args: + subst_attr (str): Substance attributes + + Raises: + ValueError: Raise Error when unsupported Substance + Project was detected + + Returns: + python instance: converted python instance of the C++ objects. + """ + if subst_attr in {"Default", "UVTile", "TextureSetPerUVTile"}: + return getattr(substance_painter.project.ProjectWorkflow, subst_attr) + elif subst_attr in {"PerFragment", "PerVertex"}: + return getattr(substance_painter.project.TangentSpace, subst_attr) + elif subst_attr in {"DirectX", "OpenGL"}: + return getattr(substance_painter.project.NormalMapFormat, subst_attr) + else: + raise ValueError( + f"Unsupported Substance Objects: {subst_attr}") + + +def parse_substance_attributes_setting(template_name, project_templates): + """Function to parse the dictionary from the AYON setting to be used + as the attributes for Substance Project Creation + + Args: + template_name (str): name of the template from the setting + project_templates (dict): project template data from the setting + + Returns: + dict: data to be used as attributes for Substance Project Creation + """ + attributes_data = {} + for template in project_templates: + if template["name"] == template_name: + attributes_data.update(template) + attributes_data["normal_map_format"] = _convert( + attributes_data["normal_map_format"]) + attributes_data["project_workflow"] = _convert( + attributes_data["project_workflow"]) + attributes_data["tangent_space_mode"] = _convert( + attributes_data["tangent_space_mode"]) + attributes_data.pop("name") + attributes_data.pop("preserve_strokes") + return attributes_data + + +def parse_subst_attrs_reloading_mesh(template_name, project_templates): + """Function to parse the substances attributes ('import_cameras' + and 'preserve_strokes') for reloading mesh + with the existing projects. + + Args: + template_name (str): name of the template from the setting + project_templates (dict): project template data from the setting + + Returns: + dict: data to be used as attributes for reloading mesh with the + existing project + """ + attributes_data = {} + for template in project_templates: + if template["name"] == template_name: + for key, value in template.items(): + if isinstance(value, bool): + attributes_data.update({key: value}) + return attributes_data + + +class SubstanceProjectConfigurationWindow(QtWidgets.QDialog): + """The pop-up dialog allows users to choose material + duplicate options for importing Max objects when updating + or switching assets. + """ + def __init__(self, project_templates): + super(SubstanceProjectConfigurationWindow, self).__init__() + self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint) + + self.template_name = None + self.project_templates = project_templates + + self.widgets = { + "label": QtWidgets.QLabel("Project Configuration"), + "template_options": QtWidgets.QComboBox(), + "buttons": QtWidgets.QWidget(), + "okButton": QtWidgets.QPushButton("Ok"), + } + for template in project_templates: + self.widgets["template_options"].addItem(template) + # Build buttons. + layout = QtWidgets.QHBoxLayout(self.widgets["buttons"]) + layout.addWidget(self.widgets["template_options"]) + layout.addWidget(self.widgets["okButton"]) + # Build layout. + layout = QtWidgets.QVBoxLayout(self) + layout.addWidget(self.widgets["label"]) + layout.addWidget(self.widgets["buttons"]) + + self.widgets["okButton"].pressed.connect(self.on_ok_pressed) + + def on_ok_pressed(self): + self.template_name = ( + self.widgets["template_options"].currentText() + ) + self.close() + + class SubstanceLoadProjectMesh(load.LoaderPlugin): """Load mesh for project""" @@ -30,20 +137,13 @@ class SubstanceLoadProjectMesh(load.LoaderPlugin): color = "orange" project_templates = [] - @classmethod - def get_options(cls, contexts): - template_enum = [template["name"] for template in cls.project_templates] - return [ - EnumDef("project_template", - items=template_enum, - default="default", - label="Project Template") - ] - def load(self, context, name, namespace, options=None): - # Get user inputs - template_name = options.get("project_template", "default") + template_enum = [template["name"] for template in self.project_templates] + window = SubstanceProjectConfigurationWindow(template_enum) + window.exec_() + template_name = window.template_name + template_settings = parse_substance_attributes_setting( template_name, self.project_templates) sp_settings = substance_painter.project.Settings(**template_settings) diff --git a/server_addon/substancepainter/server/settings/load_plugins.py b/server_addon/substancepainter/server/settings/load_plugins.py index 4d3e64f0b6..294ecfcef6 100644 --- a/server_addon/substancepainter/server/settings/load_plugins.py +++ b/server_addon/substancepainter/server/settings/load_plugins.py @@ -10,17 +10,17 @@ def normal_map_format_enum(): def tangent_space_enum(): return [ - {"label": "PerFragment", "value": "PerFragment"}, - {"label": "PerVertex", "value": "PerVertex"}, + {"label": "Per Fragment", "value": "PerFragment"}, + {"label": "Per Vertex", "value": "PerVertex"}, ] def uv_workflow_enum(): return [ - {"label": "Default", "value": "default"}, - {"label": "UV Tile", "value": "uvTile"}, + {"label": "Default", "value": "Default"}, + {"label": "UV Tile", "value": "UVTile"}, {"label": "Texture Set Per UV Tile", - "value": "textureSetPerUVTile"} + "value": "TextureSetPerUVTile"} ] @@ -54,7 +54,7 @@ class ProjectTemplatesModel(BaseSettingsModel): "creating new project.") ) project_workflow: str = SettingsField( - "default", enum_resolver=uv_workflow_enum, + "Default", enum_resolver=uv_workflow_enum, title="UV Tile Settings", description=("Set UV workflow when " "creating new project.") @@ -90,11 +90,29 @@ class LoadersModel(BaseSettingsModel): DEFAULT_LOADER_SETTINGS = { "SubstanceLoadProjectMesh":{ "project_templates": [{ - "name": "default", - "default_texture_resolution": 1024, + "name": "2K(Default)", + "default_texture_resolution": 2048, "import_cameras": True, "normal_map_format": "DirectX", - "project_workflow": "default", + "project_workflow": "Default", + "tangent_space_mode": "PerFragment", + "preserve_strokes": True + }, + { + "name": "2K(UV tile)", + "default_texture_resolution": 2048, + "import_cameras": True, + "normal_map_format": "DirectX", + "project_workflow": "UVTile", + "tangent_space_mode": "PerFragment", + "preserve_strokes": True + }, + { + "name": "4K(Custom)", + "default_texture_resolution": 4096, + "import_cameras": True, + "normal_map_format": "OpenGL", + "project_workflow": "UVTile", "tangent_space_mode": "PerFragment", "preserve_strokes": True }]