Merge pull request #1496 from pypeclub/feature/1159-maya-safer-handling-of-expected-render-output-names

This commit is contained in:
Milan Kolar 2021-05-17 13:32:38 +02:00 committed by GitHub
commit d303c23eb7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 316 additions and 33 deletions

View file

@ -184,7 +184,7 @@ class AExpectedFiles:
(str): sanitized camera name
Example:
>>> sanizite_camera_name('test:camera_01')
>>> AExpectedFiles.sanizite_camera_name('test:camera_01')
test_camera_01
"""
@ -230,7 +230,7 @@ class AExpectedFiles:
if self.layer.startswith("rs_"):
layer_name = self.layer[3:]
scene_data = {
return {
"frameStart": int(self.get_render_attribute("startFrame")),
"frameEnd": int(self.get_render_attribute("endFrame")),
"frameStep": int(self.get_render_attribute("byFrameStep")),
@ -245,7 +245,6 @@ class AExpectedFiles:
"filePrefix": file_prefix,
"enabledAOVs": self.get_aovs(),
}
return scene_data
def _generate_single_file_sequence(
self, layer_data, force_aov_name=None):
@ -685,8 +684,6 @@ class ExpectedFilesRedshift(AExpectedFiles):
"""Expected files for Redshift renderer.
Attributes:
ext_mapping (list): Mapping redshift extension dropdown values
to strings.
unmerged_aovs (list): Name of aovs that are not merged into resulting
exr and we need them specified in expectedFiles output.
@ -695,8 +692,6 @@ class ExpectedFilesRedshift(AExpectedFiles):
unmerged_aovs = ["Cryptomatte"]
ext_mapping = ["iff", "exr", "tif", "png", "tga", "jpg"]
def __init__(self, layer, render_instance):
"""Construtor."""
super(ExpectedFilesRedshift, self).__init__(layer, render_instance)
@ -785,12 +780,10 @@ class ExpectedFilesRedshift(AExpectedFiles):
# anyway.
return enabled_aovs
default_ext = self.ext_mapping[
cmds.getAttr("redshiftOptions.imageFormat")
]
default_ext = cmds.getAttr(
"redshiftOptions.imageFormat", asString=True)
rs_aovs = cmds.ls(type="RedshiftAOV", referencedNodes=False)
# todo: find out how to detect multichannel exr for redshift
for aov in rs_aovs:
enabled = self.maya_is_true(cmds.getAttr("{}.enabled".format(aov)))
for override in self.get_layer_overrides(

View file

@ -12,7 +12,7 @@ from openpype.hosts.maya.api import (
lib,
plugin
)
from openpype.api import get_system_settings
from openpype.api import (get_system_settings, get_asset)
class CreateRender(plugin.Creator):
@ -104,7 +104,7 @@ class CreateRender(plugin.Creator):
# namespace is not empty, so we leave it untouched
pass
while(cmds.namespace(exists=namespace_name)):
while cmds.namespace(exists=namespace_name):
namespace_name = "_{}{}".format(str(instance), index)
index += 1
@ -125,7 +125,7 @@ class CreateRender(plugin.Creator):
cmds.sets(sets, forceElement=instance)
# if no render layers are present, create default one with
# asterix selector
# asterisk selector
if not layers:
render_layer = self._rs.createRenderLayer('Main')
collection = render_layer.createCollection("defaultCollection")
@ -137,9 +137,7 @@ class CreateRender(plugin.Creator):
if renderer.startswith('renderman'):
renderer = 'renderman'
cmds.setAttr(self._image_prefix_nodes[renderer],
self._image_prefixes[renderer],
type="string")
self._set_default_renderer_settings(renderer)
def _create_render_settings(self):
# get pools
@ -318,3 +316,86 @@ class CreateRender(plugin.Creator):
False if os.getenv("OPENPYPE_DONT_VERIFY_SSL", True) else True
) # noqa
return requests.get(*args, **kwargs)
def _set_default_renderer_settings(self, renderer):
"""Set basic settings based on renderer.
Args:
renderer (str): Renderer name.
"""
cmds.setAttr(self._image_prefix_nodes[renderer],
self._image_prefixes[renderer],
type="string")
asset = get_asset()
if renderer == "arnold":
# set format to exr
cmds.setAttr(
"defaultArnoldDriver.ai_translator", "exr", type="string")
# enable animation
cmds.setAttr("defaultRenderGlobals.outFormatControl", 0)
cmds.setAttr("defaultRenderGlobals.animation", 1)
cmds.setAttr("defaultRenderGlobals.putFrameBeforeExt", 1)
cmds.setAttr("defaultRenderGlobals.extensionPadding", 4)
# resolution
cmds.setAttr(
"defaultResolution.width",
asset["data"].get("resolutionWidth"))
cmds.setAttr(
"defaultResolution.height",
asset["data"].get("resolutionHeight"))
if renderer == "vray":
vray_settings = cmds.ls(type="VRaySettingsNode")
if not vray_settings:
node = cmds.createNode("VRaySettingsNode")
else:
node = vray_settings[0]
# set underscore as element separator instead of default `.`
cmds.setAttr(
"{}.fileNameRenderElementSeparator".format(
node),
"_"
)
# set format to exr
cmds.setAttr(
"{}.imageFormatStr".format(node), 5)
# animType
cmds.setAttr(
"{}.animType".format(node), 1)
# resolution
cmds.setAttr(
"{}.width".format(node),
asset["data"].get("resolutionWidth"))
cmds.setAttr(
"{}.height".format(node),
asset["data"].get("resolutionHeight"))
if renderer == "redshift":
redshift_settings = cmds.ls(type="RedshiftOptions")
if not redshift_settings:
node = cmds.createNode("RedshiftOptions")
else:
node = redshift_settings[0]
# set exr
cmds.setAttr("{}.imageFormat".format(node), 1)
# resolution
cmds.setAttr(
"defaultResolution.width",
asset["data"].get("resolutionWidth"))
cmds.setAttr(
"defaultResolution.height",
asset["data"].get("resolutionHeight"))
# enable animation
cmds.setAttr("defaultRenderGlobals.outFormatControl", 0)
cmds.setAttr("defaultRenderGlobals.animation", 1)
cmds.setAttr("defaultRenderGlobals.putFrameBeforeExt", 1)
cmds.setAttr("defaultRenderGlobals.extensionPadding", 4)

View file

@ -1,8 +1,9 @@
import os
# -*- coding: utf-8 -*-
"""Maya validator for render settings."""
import re
from collections import OrderedDict
from maya import cmds, mel
import pymel.core as pm
import pyblish.api
import openpype.api
@ -60,6 +61,8 @@ class ValidateRenderSettings(pyblish.api.InstancePlugin):
'renderman': '<layer>_<aov>.<f4>.<ext>'
}
redshift_AOV_prefix = "<BeautyPath>/<BeautyFile>_<RenderPass>"
# WARNING: There is bug? in renderman, translating <scene> token
# to something left behind mayas default image prefix. So instead
# `SceneName_v01` it translates to:
@ -120,25 +123,59 @@ class ValidateRenderSettings(pyblish.api.InstancePlugin):
"doesn't have: '<renderlayer>' or "
"'<layer>' token".format(prefix))
if len(cameras) > 1:
if not re.search(cls.R_CAMERA_TOKEN, prefix):
invalid = True
cls.log.error("Wrong image prefix [ {} ] - "
"doesn't have: '<camera>' token".format(prefix))
if len(cameras) > 1 and not re.search(cls.R_CAMERA_TOKEN, prefix):
invalid = True
cls.log.error("Wrong image prefix [ {} ] - "
"doesn't have: '<camera>' token".format(prefix))
# renderer specific checks
if renderer == "vray":
# no vray checks implemented yet
pass
elif renderer == "redshift":
vray_settings = cmds.ls(type="VRaySettingsNode")
if not vray_settings:
node = cmds.createNode("VRaySettingsNode")
else:
node = vray_settings[0]
if cmds.getAttr(
"{}.fileNameRenderElementSeparator".format(node)) != "_":
invalid = False
cls.log.error("AOV separator is not set correctly.")
if renderer == "redshift":
if re.search(cls.R_AOV_TOKEN, prefix):
invalid = True
cls.log.error("Do not use AOV token [ {} ] - "
"Redshift automatically append AOV name and "
"it doesn't make much sense with "
"Multipart EXR".format(prefix))
cls.log.error(("Do not use AOV token [ {} ] - "
"Redshift is using image prefixes per AOV so "
"it doesn't make much sense using it in global"
"image prefix").format(prefix))
# get redshift AOVs
rs_aovs = cmds.ls(type="RedshiftAOV", referencedNodes=False)
for aov in rs_aovs:
aov_prefix = cmds.getAttr("{}.filePrefix".format(aov))
# check their image prefix
if aov_prefix != cls.redshift_AOV_prefix:
cls.log.error(("AOV ({}) image prefix is not set "
"correctly {} != {}").format(
cmds.getAttr("{}.name".format(aov)),
cmds.getAttr("{}.filePrefix".format(aov)),
aov_prefix
))
invalid = True
# get aov format
aov_ext = cmds.getAttr(
"{}.fileFormat".format(aov), asString=True)
elif renderer == "renderman":
default_ext = cmds.getAttr(
"redshiftOptions.imageFormat", asString=True)
if default_ext != aov_ext:
cls.log.error(("AOV file format is not the same "
"as the one set globally "
"{} != {}").format(default_ext,
aov_ext))
invalid = True
if renderer == "renderman":
file_prefix = cmds.getAttr("rmanGlobals.imageFileFormat")
dir_prefix = cmds.getAttr("rmanGlobals.imageOutputDir")
@ -151,7 +188,7 @@ class ValidateRenderSettings(pyblish.api.InstancePlugin):
cls.log.error("Wrong directory prefix [ {} ]".format(
dir_prefix))
else:
if renderer == "arnold":
multipart = cmds.getAttr("defaultArnoldDriver.mergeAOVs")
if multipart:
if re.search(cls.R_AOV_TOKEN, prefix):
@ -177,6 +214,43 @@ class ValidateRenderSettings(pyblish.api.InstancePlugin):
cls.log.error("Expecting padding of {} ( {} )".format(
cls.DEFAULT_PADDING, "0" * cls.DEFAULT_PADDING))
# load validation definitions from settings
validation_settings = (
instance.context.data["project_settings"]["maya"]["publish"]["ValidateRenderSettings"].get( # noqa: E501
"{}_render_attributes".format(renderer))
)
# go through definitions and test if such node.attribute exists.
# if so, compare its value from the one required.
for attr, value in OrderedDict(validation_settings).items():
# first get node of that type
cls.log.debug("{}: {}".format(attr, value))
node_type = attr.split(".")[0]
attribute_name = ".".join(attr.split(".")[1:])
nodes = cmds.ls(type=node_type)
if not isinstance(nodes, list):
cls.log.warning("No nodes of '{}' found.".format(node_type))
continue
for node in nodes:
try:
render_value = cmds.getAttr(
"{}.{}".format(node, attribute_name))
except RuntimeError:
invalid = True
cls.log.error(
"Cannot get value of {}.{}".format(
node, attribute_name))
else:
if value != render_value:
invalid = True
cls.log.error(
("Invalid value {} set on {}.{}. "
"Expecting {}").format(
render_value, node, attribute_name, value)
)
return invalid
@classmethod
@ -210,3 +284,29 @@ class ValidateRenderSettings(pyblish.api.InstancePlugin):
cmds.setAttr("rmanGlobals.imageOutputDir",
cls.RendermanDirPrefix,
type="string")
if renderer == "vray":
vray_settings = cmds.ls(type="VRaySettingsNode")
if not vray_settings:
node = cmds.createNode("VRaySettingsNode")
else:
node = vray_settings[0]
cmds.setAttr(
"{}.fileNameRenderElementSeparator".format(
node),
"_"
)
if renderer == "redshift":
# get redshift AOVs
rs_aovs = cmds.ls(type="RedshiftAOV", referencedNodes=False)
for aov in rs_aovs:
# fix AOV prefixes
cmds.setAttr(
"{}.filePrefix".format(aov), cls.redshift_AOV_prefix)
# fix AOV file format
default_ext = cmds.getAttr(
"redshiftOptions.imageFormat", asString=True)
cmds.setAttr(
"{}.fileFormat".format(aov), default_ext)

View file

@ -135,6 +135,12 @@
"enabled": false,
"attributes": {}
},
"ValidateRenderSettings": {
"arnold_render_attributes": [],
"vray_render_attributes": [],
"redshift_render_attributes": [],
"renderman_render_attributes": []
},
"ValidateModelName": {
"enabled": false,
"material_file": {

View file

@ -72,6 +72,56 @@
}
]
},
{
"type": "dict",
"collapsible": true,
"key": "ValidateRenderSettings",
"label": "ValidateRenderSettings",
"children": [
{
"type": "dict-modifiable",
"store_as_list": true,
"key": "arnold_render_attributes",
"label": "Arnold Render Attributes",
"use_label_wrap": true,
"object_type": {
"type": "text"
}
},
{
"type": "dict-modifiable",
"store_as_list": true,
"key": "vray_render_attributes",
"label": "Vray Render Attributes",
"use_label_wrap": true,
"object_type": {
"type": "text"
}
},
{
"type": "dict-modifiable",
"store_as_list": true,
"key": "redshift_render_attributes",
"label": "Redshift Render Attributes",
"use_label_wrap": true,
"object_type": {
"type": "text"
}
},
{
"type": "dict-modifiable",
"store_as_list": true,
"key": "renderman_render_attributes",
"label": "Renderman Render Attributes",
"use_label_wrap": true,
"object_type": {
"type": "text"
}
}
]
},
{
"type": "collapsible-wrap",
"label": "Model",

View file

@ -0,0 +1,52 @@
---
id: admin_hosts_maya
title: Maya
sidebar_label: Maya
---
## Maya
### Publish Plugins
#### Render Settings Validator (`ValidateRenderSettings`)
Render Settings Validator is here to make sure artists will submit renders
we correct settings. Some of these settings are needed by OpenPype but some
can be defined by TD using [OpenPype Settings UI](admin_settings).
OpenPype enforced settings include:
- animation must be enabled in output
- render prefix must start with `maya/<scene>` to make sure renders are in
correct directory
- there must be `<renderlayer>` or its equivalent in different renderers in
file prefix
- if multiple cameras are to be rendered, `<camera>` token must be in file prefix
For **Vray**:
- AOV separator must be set to `_` (underscore)
For **Redshift**:
- all AOVs must follow `<BeautyPath>/<BeautyFile>_<RenderPass>` image file prefix
- AOV image format must be same as the one set in Output settings
For **Renderman**:
- both image and directory prefixes must comply to `<layer>_<aov>.<f4>.<ext>` and `<ws>/renders/maya/<scene>/<layer>` respectively
For **Arnold**:
- there shouldn't be `<renderpass>` token when merge AOVs option is turned on
Additional check can be added via Settings - **Project Settings > Maya > Publish plugin > ValidateRenderSettings**.
You can add as many options as you want for every supported renderer. In first field put node type and attribute
and in the second required value.
![Settings example](assets/maya-admin_render_settings_validator.png)
In this example we've put `aiOptions.AA_samples` in first one and `6` to second to enforce
Arnolds Camera (AA) samples to 6.
Note that `aiOptions` is not the name of node but rather its type. For renderers there is usually
just one instance of this node type but if that is not so, validator will go through all its
instances and check the value there. Node type for **VRay** settings is `VRaySettingsNode`, for **Renderman**
it is `rmanGlobals`, for **Redshift** it is `RedshiftOptions`.

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -84,6 +84,7 @@ module.exports = {
label: "Integrations",
items: [
"admin_hosts_blender",
"admin_hosts_maya",
"admin_hosts_resolve",
"admin_hosts_harmony",
"admin_hosts_aftereffects"