Skip to content

validate_renderpasses

ValidateRenderPasses

Bases: OptionalPyblishPluginMixin, InstancePlugin

Validates Render Passes before farm submission

Source code in client/ayon_max/plugins/publish/validate_renderpasses.py
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
class ValidateRenderPasses(OptionalPyblishPluginMixin,
                           pyblish.api.InstancePlugin):
    """Validates Render Passes before farm submission
    """

    order = ValidateContentsOrder
    families = ["maxrender"]
    hosts = ["max"]
    label = "Validate Render Passes"
    actions = [RepairAction]

    settings_category = "max"

    def process(self, instance):
        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.

        1. Check Render Output Folder matches the name of
           the current Max Scene, e.g.
             The name of the current Max scene:
               John_Doe.max
             The expected render output directory:
               {root[work]}/{project[name]}/{hierarchy}/{asset}/
               work/{task[name]}/render/3dsmax/John_Doe/

        2. Check image extension(s) of the render output(s)
           matches the image format in OP/AYON setting, e.g.
               The current image format in settings: png
               The expected render outputs: John_Doe.png

        3. Check filename of render element ends with the name of
           render element from the 3dsMax Render Element Manager.
           e.g. The name of render element: RsCryptomatte
            The expected filename: {InstanceName}_RsCryptomatte.png

        Args:
            instance (pyblish.api.Instance): instance
            workfile_name (str): filename of the Max scene

        Returns:
            list: list of invalid filename which doesn't match
                with the project name
        """
        invalid = []
        file = rt.maxFileName
        workfile_name, ext = os.path.splitext(file)
        if workfile_name not in rt.rendOutputFilename:
            cls.log.error(
                "Render output folder must include"
                f" the max scene name {workfile_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)
        beauty_name, ext = os.path.splitext(beauty_fname)
        invalid_filenames = cls.get_invalid_filenames(
            instance, beauty_name)
        invalid.extend(invalid_filenames)
        invalid_image_format = cls.get_invalid_image_format(
            instance, ext.lstrip("."))
        invalid.extend(invalid_image_format)
        renderer = instance.data["renderer"]
        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).rsplit(":", 1)[-1]
                rend_file = render_elem.GetRenderElementFilename(i)
                if not rend_file:
                    continue

                rend_fname, ext = os.path.splitext(
                    os.path.basename(rend_file))
                invalid_filenames = cls.get_invalid_filenames(
                    instance, rend_fname, renderpass=renderpass)
                invalid.extend(invalid_filenames)
                invalid_image_format = cls.get_invalid_image_format(
                    instance, ext)
                invalid.extend(invalid_image_format)
        elif renderer == "Arnold":
            cls.log.debug(
                "Renderpass validation does not support Arnold yet,"
                " validation skipped...")
        else:
            cls.log.debug(
                "Skipping render element validation "
                f"for renderer: {renderer}")
        return invalid

    @classmethod
    def get_invalid_filenames(cls, instance, file_name, renderpass=None):
        """Function to get invalid filenames from render outputs.

        Args:
            instance (pyblish.api.Instance): instance
            file_name (str): name of the file
            renderpass (str, optional): name of the renderpass.
                Defaults to None.

        Returns:
            list: invalid filenames
        """
        invalid = []
        if instance.name not in file_name:
            cls.log.error("The renderpass filename should contain the instance name.")
            invalid.append(("Invalid instance name",
                            file_name))
        if renderpass is not None:
            if not file_name.rstrip(".").endswith(renderpass):
                cls.log.error(
                    f"Filename for {renderpass} should "
                    f"end with {renderpass}: {file_name}"
                )
                invalid.append((f"Invalid {renderpass}",
                                os.path.basename(file_name)))
        return invalid

    @classmethod
    def get_invalid_image_format(cls, 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"]
        ext = ext.lstrip(".")
        if ext != image_format:
            msg = (
                f"Invalid image format {ext} for render outputs.\n"
                f"Should be: {image_format}")
            cls.log.error(msg)
            invalid.append((msg, ext))
        return invalid

    @classmethod
    def repair(cls, instance):
        container = instance.data.get("instance_node")
        # TODO: need to rename the function of render_output
        RenderSettings().render_output(container)
        cls.log.debug("Finished repairing the render output "
                      "folder and filenames.")

get_invalid(instance) classmethod

Function to get invalid beauty render outputs and render elements.

  1. Check Render Output Folder matches the name of the current Max Scene, e.g. The name of the current Max scene: John_Doe.max The expected render output directory: {root[work]}/{project[name]}/{hierarchy}/{asset}/ work/{task[name]}/render/3dsmax/John_Doe/

  2. Check image extension(s) of the render output(s) matches the image format in OP/AYON setting, e.g. The current image format in settings: png The expected render outputs: John_Doe.png

  3. Check filename of render element ends with the name of render element from the 3dsMax Render Element Manager. e.g. The name of render element: RsCryptomatte The expected filename: {InstanceName}_RsCryptomatte.png

Parameters:

Name Type Description Default
instance Instance

instance

required
workfile_name str

filename of the Max scene

required

Returns:

Name Type Description
list

list of invalid filename which doesn't match with the project name

Source code in client/ayon_max/plugins/publish/validate_renderpasses.py
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
@classmethod
def get_invalid(cls, instance):
    """Function to get invalid beauty render outputs and
    render elements.

    1. Check Render Output Folder matches the name of
       the current Max Scene, e.g.
         The name of the current Max scene:
           John_Doe.max
         The expected render output directory:
           {root[work]}/{project[name]}/{hierarchy}/{asset}/
           work/{task[name]}/render/3dsmax/John_Doe/

    2. Check image extension(s) of the render output(s)
       matches the image format in OP/AYON setting, e.g.
           The current image format in settings: png
           The expected render outputs: John_Doe.png

    3. Check filename of render element ends with the name of
       render element from the 3dsMax Render Element Manager.
       e.g. The name of render element: RsCryptomatte
        The expected filename: {InstanceName}_RsCryptomatte.png

    Args:
        instance (pyblish.api.Instance): instance
        workfile_name (str): filename of the Max scene

    Returns:
        list: list of invalid filename which doesn't match
            with the project name
    """
    invalid = []
    file = rt.maxFileName
    workfile_name, ext = os.path.splitext(file)
    if workfile_name not in rt.rendOutputFilename:
        cls.log.error(
            "Render output folder must include"
            f" the max scene name {workfile_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)
    beauty_name, ext = os.path.splitext(beauty_fname)
    invalid_filenames = cls.get_invalid_filenames(
        instance, beauty_name)
    invalid.extend(invalid_filenames)
    invalid_image_format = cls.get_invalid_image_format(
        instance, ext.lstrip("."))
    invalid.extend(invalid_image_format)
    renderer = instance.data["renderer"]
    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).rsplit(":", 1)[-1]
            rend_file = render_elem.GetRenderElementFilename(i)
            if not rend_file:
                continue

            rend_fname, ext = os.path.splitext(
                os.path.basename(rend_file))
            invalid_filenames = cls.get_invalid_filenames(
                instance, rend_fname, renderpass=renderpass)
            invalid.extend(invalid_filenames)
            invalid_image_format = cls.get_invalid_image_format(
                instance, ext)
            invalid.extend(invalid_image_format)
    elif renderer == "Arnold":
        cls.log.debug(
            "Renderpass validation does not support Arnold yet,"
            " validation skipped...")
    else:
        cls.log.debug(
            "Skipping render element validation "
            f"for renderer: {renderer}")
    return invalid

get_invalid_filenames(instance, file_name, renderpass=None) classmethod

Function to get invalid filenames from render outputs.

Parameters:

Name Type Description Default
instance Instance

instance

required
file_name str

name of the file

required
renderpass str

name of the renderpass. Defaults to None.

None

Returns:

Name Type Description
list

invalid filenames

Source code in client/ayon_max/plugins/publish/validate_renderpasses.py
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
@classmethod
def get_invalid_filenames(cls, instance, file_name, renderpass=None):
    """Function to get invalid filenames from render outputs.

    Args:
        instance (pyblish.api.Instance): instance
        file_name (str): name of the file
        renderpass (str, optional): name of the renderpass.
            Defaults to None.

    Returns:
        list: invalid filenames
    """
    invalid = []
    if instance.name not in file_name:
        cls.log.error("The renderpass filename should contain the instance name.")
        invalid.append(("Invalid instance name",
                        file_name))
    if renderpass is not None:
        if not file_name.rstrip(".").endswith(renderpass):
            cls.log.error(
                f"Filename for {renderpass} should "
                f"end with {renderpass}: {file_name}"
            )
            invalid.append((f"Invalid {renderpass}",
                            os.path.basename(file_name)))
    return invalid

get_invalid_image_format(instance, ext) classmethod

Function to check if the image format of the render outputs aligns with that in the setting.

Parameters:

Name Type Description Default
instance Instance

instance

required
ext str

image extension

required

Returns:

Name Type Description
list

list of files with invalid image format

Source code in client/ayon_max/plugins/publish/validate_renderpasses.py
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
@classmethod
def get_invalid_image_format(cls, 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"]
    ext = ext.lstrip(".")
    if ext != image_format:
        msg = (
            f"Invalid image format {ext} for render outputs.\n"
            f"Should be: {image_format}")
        cls.log.error(msg)
        invalid.append((msg, ext))
    return invalid