Merge pull request #951 from pypeclub/3.0/arnold-referenced-aovs

Maya: Handling Arnold referenced AOVs - Pype 3
This commit is contained in:
Milan Kolar 2021-02-01 18:37:30 +01:00 committed by GitHub
commit 8b1897a020
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 78 additions and 104 deletions

View file

@ -29,8 +29,8 @@ Attributes:
token in image prefixes.
RENDERER_NAMES (dict): Renderer names mapping between reported name and
*human readable* name.
ImagePrefixes (dict): Mapping between renderers and their respective
image prefix atrribute names.
IMAGE_PREFIXES (dict): Mapping between renderers and their respective
image prefix attribute names.
Todo:
Determine `multipart` from render instance.
@ -78,7 +78,7 @@ RENDERER_NAMES = {
}
# not sure about the renderman image prefix
ImagePrefixes = {
IMAGE_PREFIXES = {
"mentalray": "defaultRenderGlobals.imageFilePrefix",
"vray": "vraySettings.fileNamePrefix",
"arnold": "defaultRenderGlobals.imageFilePrefix",
@ -123,22 +123,22 @@ class ExpectedFiles:
if renderer.lower() == "arnold":
return self._get_files(ExpectedFilesArnold(layer,
self._render_instance))
elif renderer.lower() == "vray":
if renderer.lower() == "vray":
return self._get_files(ExpectedFilesVray(
layer, self._render_instance))
elif renderer.lower() == "redshift":
if renderer.lower() == "redshift":
return self._get_files(ExpectedFilesRedshift(
layer, self._render_instance))
elif renderer.lower() == "mentalray":
if renderer.lower() == "mentalray":
return self._get_files(ExpectedFilesMentalray(
layer, self._render_instance))
elif renderer.lower() == "renderman":
if renderer.lower() == "renderman":
return self._get_files(ExpectedFilesRenderman(
layer, self._render_instance))
else:
raise UnsupportedRendererException(
"unsupported {}".format(renderer)
)
raise UnsupportedRendererException(
"unsupported {}".format(renderer)
)
def _get_files(self, renderer):
files = renderer.get_files()
@ -169,9 +169,9 @@ class AExpectedFiles:
@abstractmethod
def get_aovs(self):
"""To be implemented by renderer class."""
pass
def sanitize_camera_name(self, camera):
@staticmethod
def sanitize_camera_name(camera):
"""Sanitize camera name.
Remove Maya illegal characters from camera name.
@ -187,8 +187,7 @@ class AExpectedFiles:
test_camera_01
"""
sanitized = re.sub('[^0-9a-zA-Z_]+', '_', camera)
return sanitized
return re.sub('[^0-9a-zA-Z_]+', '_', camera)
def get_renderer_prefix(self):
"""Return prefix for specific renderer.
@ -201,12 +200,12 @@ class AExpectedFiles:
Raises:
:exc:`UnsupportedRendererException`: If we requested image
prefix for renderer we know nothing about.
See :data:`ImagePrefixes` for mapping of renderers and
See :data:`IMAGE_PREFIXES` for mapping of renderers and
image prefixes.
"""
try:
file_prefix = cmds.getAttr(ImagePrefixes[self.renderer])
file_prefix = cmds.getAttr(IMAGE_PREFIXES[self.renderer])
except KeyError:
raise UnsupportedRendererException(
"Unsupported renderer {}".format(self.renderer)
@ -218,62 +217,32 @@ class AExpectedFiles:
# ____________________/ ____________________________________________/
# 1 - get scene name /__________________/
# ____________________/
scene_dir, scene_basename = os.path.split(cmds.file(q=True, loc=True))
_, scene_basename = os.path.split(cmds.file(q=True, loc=True))
scene_name, _ = os.path.splitext(scene_basename)
# ______________________________________________
# ____________________/ ____________________________________________/
# 2 - detect renderer /__________________/
# ____________________/
renderer = self.renderer
# ________________________________________________
# __________________/ ______________________________________________/
# 3 - image prefix /__________________/
# __________________/
file_prefix = self.get_renderer_prefix()
if not file_prefix:
raise RuntimeError("Image prefix not set")
default_ext = cmds.getAttr("defaultRenderGlobals.imfPluginKey")
# ________________________________________________
# __________________/ ______________________________________________/
# 4 - get renderable cameras_____________/
# __________________/
# if we have <camera> token in prefix path we'll expect output for
# every renderable camera in layer.
renderable_cameras = self.get_renderable_cameras()
# ________________________________________________
# __________________/ ______________________________________________/
# 5 - get AOVs /____________________/
# __________________/
enabled_aovs = self.get_aovs()
layer_name = self.layer
if self.layer.startswith("rs_"):
layer_name = self.layer[3:]
start_frame = int(self.get_render_attribute("startFrame"))
end_frame = int(self.get_render_attribute("endFrame"))
frame_step = int(self.get_render_attribute("byFrameStep"))
padding = int(self.get_render_attribute("extensionPadding"))
scene_data = {
"frameStart": start_frame,
"frameEnd": end_frame,
"frameStep": frame_step,
"padding": padding,
"cameras": renderable_cameras,
"frameStart": int(self.get_render_attribute("startFrame")),
"frameEnd": int(self.get_render_attribute("endFrame")),
"frameStep": int(self.get_render_attribute("byFrameStep")),
"padding": int(self.get_render_attribute("extensionPadding")),
# if we have <camera> token in prefix path we'll expect output for
# every renderable camera in layer.
"cameras": self.get_renderable_cameras(),
"sceneName": scene_name,
"layerName": layer_name,
"renderer": renderer,
"defaultExt": default_ext,
"renderer": self.renderer,
"defaultExt": cmds.getAttr("defaultRenderGlobals.imfPluginKey"),
"filePrefix": file_prefix,
"enabledAOVs": enabled_aovs,
"enabledAOVs": self.get_aovs(),
}
return scene_data
@ -296,9 +265,9 @@ class AExpectedFiles:
file_prefix = re.sub(regex, value, file_prefix)
for frame in range(
int(layer_data["frameStart"]),
int(layer_data["frameEnd"]) + 1,
int(layer_data["frameStep"]),
int(layer_data["frameStart"]),
int(layer_data["frameEnd"]) + 1,
int(layer_data["frameStep"]),
):
expected_files.append(
"{}.{}.{}".format(
@ -331,9 +300,9 @@ class AExpectedFiles:
aov_files = []
for frame in range(
int(layer_data["frameStart"]),
int(layer_data["frameEnd"]) + 1,
int(layer_data["frameStep"]),
int(layer_data["frameStart"]),
int(layer_data["frameEnd"]) + 1,
int(layer_data["frameStep"]),
):
aov_files.append(
"{}.{}.{}".format(
@ -388,14 +357,13 @@ class AExpectedFiles:
renderable_cameras = []
for cam in cam_parents:
renderable = False
if self.maya_is_true(cmds.getAttr("{}.renderable".format(cam))):
renderable = True
renderable_cameras.append(cam)
return renderable_cameras
def maya_is_true(self, attr_val):
@staticmethod
def maya_is_true(attr_val):
"""Whether a Maya attr evaluates to True.
When querying an attribute value from an ambiguous object the
@ -411,12 +379,13 @@ class AExpectedFiles:
"""
if isinstance(attr_val, types.BooleanType):
return attr_val
elif isinstance(attr_val, (types.ListType, types.GeneratorType)):
if isinstance(attr_val, (types.ListType, types.GeneratorType)):
return any(attr_val)
else:
return bool(attr_val)
def get_layer_overrides(self, attr, layer):
return bool(attr_val)
@staticmethod
def get_layer_overrides(attr):
"""Get overrides for attribute on given render layer.
Args:
@ -491,8 +460,8 @@ class ExpectedFilesArnold(AExpectedFiles):
enabled_aovs = []
try:
if not (
cmds.getAttr("defaultArnoldRenderOptions.aovMode")
and not cmds.getAttr("defaultArnoldDriver.mergeAOVs") # noqa: W503, E501
cmds.getAttr("defaultArnoldRenderOptions.aovMode")
and not cmds.getAttr("defaultArnoldDriver.mergeAOVs") # noqa: W503, E501
):
# AOVs are merged in mutli-channel file
self.multipart = True
@ -508,7 +477,14 @@ class ExpectedFilesArnold(AExpectedFiles):
# AOVs are set to be rendered separately. We should expect
# <RenderPass> token in path.
ai_aovs = [n for n in cmds.ls(type="aiAOV")]
# handle aovs from references
use_ref_aovs = self.render_instance.data.get(
"useReferencedAovs", False) or False
ai_aovs = cmds.ls(type="aiAOV")
if not use_ref_aovs:
ref_aovs = cmds.ls(type="aiAOV", referencedNodes=True)
ai_aovs = list(set(ai_aovs) - set(ref_aovs))
for aov in ai_aovs:
enabled = self.maya_is_true(cmds.getAttr("{}.enabled".format(aov)))
@ -523,7 +499,7 @@ class ExpectedFilesArnold(AExpectedFiles):
raise AOVError(msg)
for override in self.get_layer_overrides(
"{}.enabled".format(aov), self.layer
"{}.enabled".format(aov)
):
enabled = self.maya_is_true(override)
if enabled:
@ -567,7 +543,7 @@ class ExpectedFilesVray(AExpectedFiles):
"""Override to get vray specific extension."""
layer_data = super(ExpectedFilesVray, self)._get_layer_data()
default_ext = cmds.getAttr("vraySettings.imageFormatStr")
if default_ext == "exr (multichannel)" or default_ext == "exr (deep)":
if default_ext in ["exr (multichannel)", "exr (deep)"]:
default_ext = "exr"
layer_data["defaultExt"] = default_ext
layer_data["padding"] = cmds.getAttr("vraySettings.fileNamePadding")
@ -587,11 +563,11 @@ class ExpectedFilesVray(AExpectedFiles):
# remove 'beauty' from filenames as vray doesn't output it
update = {}
if layer_data.get("enabledAOVs"):
for aov, seq in expected_files[0].items():
for aov, seqs in expected_files[0].items():
if aov.startswith("beauty"):
new_list = []
for f in seq:
new_list.append(f.replace("_beauty", ""))
for seq in seqs:
new_list.append(seq.replace("_beauty", ""))
update[aov] = new_list
expected_files[0].update(update)
@ -609,8 +585,8 @@ class ExpectedFilesVray(AExpectedFiles):
try:
# really? do we set it in vray just by selecting multichannel exr?
if (
cmds.getAttr("vraySettings.imageFormatStr")
== "exr (multichannel)" # noqa: W503
cmds.getAttr("vraySettings.imageFormatStr")
== "exr (multichannel)" # noqa: W503
):
# AOVs are merged in mutli-channel file
self.multipart = True
@ -624,7 +600,7 @@ class ExpectedFilesVray(AExpectedFiles):
return enabled_aovs
default_ext = cmds.getAttr("vraySettings.imageFormatStr")
if default_ext == "exr (multichannel)" or default_ext == "exr (deep)":
if default_ext in ["exr (multichannel)", "exr (deep)"]:
default_ext = "exr"
# add beauty as default
@ -634,7 +610,7 @@ class ExpectedFilesVray(AExpectedFiles):
# handle aovs from references
use_ref_aovs = self.render_instance.data.get(
"vrayUseReferencedAovs", False) or False
"useReferencedAovs", False) or False
# this will have list of all aovs no matter if they are coming from
# reference or not.
@ -650,18 +626,18 @@ class ExpectedFilesVray(AExpectedFiles):
for aov in vr_aovs:
enabled = self.maya_is_true(cmds.getAttr("{}.enabled".format(aov)))
for override in self.get_layer_overrides(
"{}.enabled".format(aov), "rs_{}".format(self.layer)
"{}.enabled".format(aov)
):
enabled = self.maya_is_true(override)
if enabled:
# todo: find how vray set format for AOVs
enabled_aovs.append(
(self._get_vray_aov_name(aov), default_ext))
return enabled_aovs
def _get_vray_aov_name(self, node):
@staticmethod
def _get_vray_aov_name(node):
"""Get AOVs name from Vray.
Args:
@ -762,8 +738,8 @@ class ExpectedFilesRedshift(AExpectedFiles):
if aov[0].lower() == "cryptomatte":
aov_name = aov[0]
expected_files.append(
{aov_name: self._generate_single_file_sequence(
layer_data, aov_name=aov_name)})
{aov_name: self._generate_single_file_sequence(layer_data)}
)
return expected_files
@ -778,7 +754,7 @@ class ExpectedFilesRedshift(AExpectedFiles):
try:
if self.maya_is_true(
cmds.getAttr("redshiftOptions.exrForceMultilayer")
cmds.getAttr("redshiftOptions.exrForceMultilayer")
):
# AOVs are merged in mutli-channel file
self.multipart = True
@ -794,13 +770,13 @@ class ExpectedFilesRedshift(AExpectedFiles):
default_ext = self.ext_mapping[
cmds.getAttr("redshiftOptions.imageFormat")
]
rs_aovs = [n for n in cmds.ls(type="RedshiftAOV")]
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(
"{}.enabled".format(aov), self.layer
"{}.enabled".format(aov)
):
enabled = self.maya_is_true(override)
@ -809,7 +785,7 @@ class ExpectedFilesRedshift(AExpectedFiles):
# is in the list of AOVs that renderer cannot (or will not)
# merge into final exr.
if self.maya_is_true(
cmds.getAttr("redshiftOptions.exrForceMultilayer")
cmds.getAttr("redshiftOptions.exrForceMultilayer")
):
if cmds.getAttr("%s.name" % aov) in self.unmerged_aovs:
enabled_aovs.append(
@ -821,7 +797,7 @@ class ExpectedFilesRedshift(AExpectedFiles):
)
if self.maya_is_true(
cmds.getAttr("redshiftOptions.exrForceMultilayer")
cmds.getAttr("redshiftOptions.exrForceMultilayer")
):
# AOVs are merged in mutli-channel file
self.multipart = True
@ -859,7 +835,7 @@ class ExpectedFilesRenderman(AExpectedFiles):
enabled = self.maya_is_true(cmds.getAttr("{}.enable".format(aov)))
for override in self.get_layer_overrides(
"{}.enable".format(aov), self.layer
"{}.enable".format(aov)
):
enabled = self.maya_is_true(override)
@ -908,6 +884,7 @@ class ExpectedFilesMentalray(AExpectedFiles):
:exc:`UnimplementedRendererException`: as it is not implemented.
"""
super(ExpectedFilesMentalray, self).__init__(layer, render_instance)
raise UnimplementedRendererException("Mentalray not implemented")
def get_aovs(self):
@ -923,8 +900,6 @@ class ExpectedFilesMentalray(AExpectedFiles):
class AOVError(Exception):
"""Custom exception for determining AOVs."""
pass
class UnsupportedRendererException(Exception):
"""Custom exception.
@ -932,13 +907,9 @@ class UnsupportedRendererException(Exception):
Raised when requesting data from unsupported renderer.
"""
pass
class UnimplementedRendererException(Exception):
"""Custom exception.
Raised when requesting data from renderer that is not implemented yet.
"""
pass

View file

@ -191,7 +191,7 @@ class CreateRender(avalon.maya.Creator):
self.data["tilesX"] = 2
self.data["tilesY"] = 2
self.data["convertToScanline"] = False
self.data["vrayUseReferencedAovs"] = False
self.data["useReferencedAovs"] = False
# Disable for now as this feature is not working yet
# self.data["assScene"] = False

View file

@ -2,7 +2,7 @@
"""Collect render data.
This collector will go through render layers in maya and prepare all data
needed to create instances and their representations for submition and
needed to create instances and their representations for submission and
publishing on farm.
Requires:
@ -248,8 +248,11 @@ class CollectMayaRender(pyblish.api.ContextPlugin):
"tilesX": render_instance.data.get("tilesX") or 2,
"tilesY": render_instance.data.get("tilesY") or 2,
"priority": render_instance.data.get("priority"),
"convertToScanline": render_instance.data.get("convertToScanline") or False, # noqa: E501
"vrayUseReferencedAovs": render_instance.data.get("vrayUseReferencedAovs") or False # noqa: E501
"convertToScanline": render_instance.data.get(
"convertToScanline") or False,
"useReferencedAovs": render_instance.data.get(
"useReferencedAovs") or render_instance.data.get(
"vrayUseReferencedAovs") or False
}
if self.sync_workfile_version: