diff --git a/openpype/hosts/max/plugins/publish/validate_deadline_publish.py b/openpype/hosts/max/plugins/publish/validate_deadline_publish.py deleted file mode 100644 index b2f0e863f4..0000000000 --- a/openpype/hosts/max/plugins/publish/validate_deadline_publish.py +++ /dev/null @@ -1,43 +0,0 @@ -import os -import pyblish.api -from pymxs import runtime as rt -from openpype.pipeline.publish import ( - RepairAction, - ValidateContentsOrder, - PublishValidationError, - OptionalPyblishPluginMixin -) -from openpype.hosts.max.api.lib_rendersettings import RenderSettings - - -class ValidateDeadlinePublish(pyblish.api.InstancePlugin, - OptionalPyblishPluginMixin): - """Validates Render File Directory is - not the same in every submission - """ - - order = ValidateContentsOrder - families = ["maxrender"] - hosts = ["max"] - label = "Render Output for Deadline" - optional = True - actions = [RepairAction] - - def process(self, instance): - if not self.is_active(instance.data): - return - file = rt.maxFileName - filename, ext = os.path.splitext(file) - if filename not in rt.rendOutputFilename: - raise PublishValidationError( - "Render output folder " - "doesn't match the max scene name! " - "Use Repair action to " - "fix the folder file path.." - ) - - @classmethod - def repair(cls, instance): - container = instance.data.get("instance_node") - RenderSettings().render_output(container) - cls.log.debug("Reset the render output folder...") diff --git a/openpype/hosts/max/plugins/publish/validate_renderpasses.py b/openpype/hosts/max/plugins/publish/validate_renderpasses.py new file mode 100644 index 0000000000..9e65f305a2 --- /dev/null +++ b/openpype/hosts/max/plugins/publish/validate_renderpasses.py @@ -0,0 +1,137 @@ +import os +import pyblish.api +from pymxs import runtime as rt +from openpype.pipeline.publish import ( + RepairAction, + ValidateContentsOrder, + PublishValidationError, + OptionalPyblishPluginMixin +) +from openpype.hosts.max.api.lib import get_current_renderer +from openpype.hosts.max.api.lib_rendersettings import RenderSettings + + +class ValidateRenderPasses(OptionalPyblishPluginMixin, + pyblish.api.InstancePlugin): + """Validates Render Passes before Deadline Submission + """ + + order = ValidateContentsOrder + families = ["maxrender"] + hosts = ["max"] + label = "Validate Render Passes" + optional = True + actions = [RepairAction] + + def process(self, instance): + if not self.is_active(instance.data): + return + invalid = self.get_invalid(instance) + if invalid: + bullet_point_invalid_statement = "\n".join( + f"- {err_type}: {filepath}" for err_type, filepath + in invalid + ) + report = ( + "Invalid render passes found.\n\n" + f"{bullet_point_invalid_statement}\n\n" + "You can use repair action to fix the invalid filepath." + ) + raise PublishValidationError( + report, title="Invalid Render Passes") + + @classmethod + def get_invalid(cls, instance): + """Function to get invalid beauty render outputs and + render elements + + Args: + instance (pyblish.api.Instance): instance + filename (str): filename of the Max scene + + Returns: + list: list of invalid filename which doesn't match + with the project name + """ + invalid = [] + file = rt.maxFileName + filename, ext = os.path.splitext(file) + if filename not in rt.rendOutputFilename: + cls.log.error( + "Render output folder " + "doesn't match the max scene name! " + ) + invalid_folder_name = os.path.dirname( + rt.rendOutputFilename).replace( + "\\", "/").split("/")[-1] + invalid.append(("Invalid Render Output Folder", + invalid_folder_name)) + beauty_fname = os.path.basename(rt.rendOutputFilename) + ext = os.path.splitext(beauty_fname)[-1].lstrip(".") + invalid_image_format = cls.get_invalid_image_format( + cls, instance, ext) + invalid.extend(invalid_image_format) + renderer_class = get_current_renderer() + renderer = str(renderer_class).split(":")[0] + if renderer in [ + "ART_Renderer", + "Redshift_Renderer", + "V_Ray_6_Hotfix_3", + "V_Ray_GPU_6_Hotfix_3", + "Default_Scanline_Renderer", + "Quicksilver_Hardware_Renderer", + ]: + render_elem = rt.maxOps.GetCurRenderElementMgr() + render_elem_num = render_elem.NumRenderElements() + for i in range(render_elem_num): + renderlayer_name = render_elem.GetRenderElement(i) + renderpass = str(renderlayer_name).split(":")[-1] + rend_file = render_elem.GetRenderElementFilename(i) + if not rend_file: + cls.log.error(f"No filepath for {renderpass}") + invalid.append((f"Invalid {renderpass}", + "No filepath")) + rend_fname, ext = os.path.splitext( + os.path.basename(rend_file)) + if not rend_fname.lstrip(".").endswith(renderpass): + err_msg = ( + f"Filename for {renderpass} should be " + f"ended with {renderpass}" + ) + cls.log.error(err_msg) + invalid.append((f"Invalid {renderpass}", + os.path.basename(rend_file))) + invalid_image_format = cls.get_invalid_image_format( + cls, instance, ext) + invalid.extend(invalid_image_format) + elif renderer == "Arnold": + cls.log.debug( + "Temporarily not support to check on Arnold render.") + + return invalid + + def get_invalid_image_format(self, instance, ext): + """Function to check if the image format of the render outputs + aligns with that in the setting. + + Args: + instance (pyblish.api.Instance): instance + ext (str): image extension + + Returns: + list: list of files with invalid image format + """ + invalid = [] + settings = instance.context.data["project_settings"].get("max") + image_format = settings["RenderSettings"]["image_format"] + if ext.lstrip(".") != image_format: + msg = "Invalid image format for render outputs" + self.log.error(msg) + invalid.append((msg, ext.lstrip("."))) + return invalid + + @classmethod + def repair(cls, instance): + container = instance.data.get("instance_node") + RenderSettings().render_output(container) + cls.log.debug("Reset the render output folder...") diff --git a/openpype/settings/defaults/project_settings/max.json b/openpype/settings/defaults/project_settings/max.json index d1610610dc..293515f1a2 100644 --- a/openpype/settings/defaults/project_settings/max.json +++ b/openpype/settings/defaults/project_settings/max.json @@ -61,6 +61,11 @@ "optional": true, "family_plugins_mapping": [] }, + "ValidateRenderPasses": { + "enabled": true, + "optional": true, + "active": true + }, "ExtractModelObj": { "enabled": true, "optional": true, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json index b4d85bda98..4dd3d519e8 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json @@ -88,6 +88,31 @@ } ] } + }, + { + "type": "dict", + "collapsible": true, + "checkbox_key": "enabled", + "key": "ValidateRenderPasses", + "label": "Validate Render Passes", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "optional", + "label": "Optional" + }, + { + "type": "boolean", + "key": "active", + "label": "Active" + } + ] } ] }, diff --git a/server_addon/max/server/settings/publishers.py b/server_addon/max/server/settings/publishers.py index d40d85a99b..a10e72b36a 100644 --- a/server_addon/max/server/settings/publishers.py +++ b/server_addon/max/server/settings/publishers.py @@ -68,6 +68,10 @@ class PublishersModel(BaseSettingsModel): default_factory=ValidateLoadedPluginModel, title="Validate Loaded Plugin" ) + ValidateRenderPasses: BasicValidateModel = Field( + default_factory=BasicValidateModel, + title="Validate Render Passes" + ) ExtractModelObj: BasicValidateModel = Field( default_factory=BasicValidateModel, title="Extract OBJ", @@ -106,6 +110,11 @@ DEFAULT_PUBLISH_SETTINGS = { "optional": True, "family_plugins_mapping": [] }, + "ValidateRenderPasses": { + "enabled": True, + "optional": True, + "active": True + }, "ExtractModelObj": { "enabled": True, "optional": True, diff --git a/server_addon/max/server/version.py b/server_addon/max/server/version.py index bbab0242f6..1276d0254f 100644 --- a/server_addon/max/server/version.py +++ b/server_addon/max/server/version.py @@ -1 +1 @@ -__version__ = "0.1.4" +__version__ = "0.1.5"