mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-25 05:14:40 +01:00
Merge pull request #1496 from pypeclub/feature/1159-maya-safer-handling-of-expected-render-output-names
This commit is contained in:
commit
d303c23eb7
8 changed files with 316 additions and 33 deletions
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
52
website/docs/admin_hosts_maya.md
Normal file
52
website/docs/admin_hosts_maya.md
Normal 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.
|
||||
|
||||

|
||||
|
||||
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`.
|
||||
BIN
website/docs/assets/maya-admin_render_settings_validator.png
Normal file
BIN
website/docs/assets/maya-admin_render_settings_validator.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
|
|
@ -84,6 +84,7 @@ module.exports = {
|
|||
label: "Integrations",
|
||||
items: [
|
||||
"admin_hosts_blender",
|
||||
"admin_hosts_maya",
|
||||
"admin_hosts_resolve",
|
||||
"admin_hosts_harmony",
|
||||
"admin_hosts_aftereffects"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue