fixed validators to support multiple cameras, deleting obsolete collector

This commit is contained in:
Ondrej Samohel 2020-02-08 01:25:23 +01:00
parent b02eb6c4c9
commit 67306bfb66
No known key found for this signature in database
GPG key ID: 8A29C663C672C2B7
3 changed files with 85 additions and 222 deletions

View file

@ -1,202 +0,0 @@
from maya import cmds
import pyblish.api
from avalon import maya, api
import pype.maya.lib as lib
class CollectMayaRenderlayers(pyblish.api.ContextPlugin):
"""Gather instances by active render layers"""
order = pyblish.api.CollectorOrder + 0.01
hosts = ["maya"]
label = "Render Layers"
active = False
def process(self, context):
asset = api.Session["AVALON_ASSET"]
filepath = context.data["currentFile"].replace("\\", "/")
# Get render globals node
try:
render_globals = cmds.ls("renderglobalsMain")[0]
for instance in context:
self.log.debug(instance.name)
if instance.data['family'] == 'workfile':
instance.data['publish'] = True
except IndexError:
self.log.info("Skipping renderlayer collection, no "
"renderGlobalsDefault found..")
return
# Get all valid renderlayers
# This is how Maya populates the renderlayer display
rlm_attribute = "renderLayerManager.renderLayerId"
connected_layers = cmds.listConnections(rlm_attribute) or []
valid_layers = set(connected_layers)
# Get all renderlayers and check their state
renderlayers = [i for i in cmds.ls(type="renderLayer") if
cmds.getAttr("{}.renderable".format(i)) and not
cmds.referenceQuery(i, isNodeReferenced=True)]
# Sort by displayOrder
def sort_by_display_order(layer):
return cmds.getAttr("%s.displayOrder" % layer)
renderlayers = sorted(renderlayers, key=sort_by_display_order)
for layer in renderlayers:
# Check if layer is in valid (linked) layers
if layer not in valid_layers:
self.log.warning("%s is invalid, skipping" % layer)
continue
if layer.endswith("defaultRenderLayer"):
continue
else:
# Remove Maya render setup prefix `rs_`
layername = layer.split("rs_", 1)[-1]
# Get layer specific settings, might be overrides
data = {
"subset": layername,
"setMembers": layer,
"publish": True,
"frameStart": self.get_render_attribute("startFrame",
layer=layer),
"frameEnd": self.get_render_attribute("endFrame",
layer=layer),
"byFrameStep": self.get_render_attribute("byFrameStep",
layer=layer),
"renderer": self.get_render_attribute("currentRenderer",
layer=layer),
# instance subset
"family": "Render Layers",
"families": ["renderlayer"],
"asset": asset,
"time": api.time(),
"author": context.data["user"],
# Add source to allow tracing back to the scene from
# which was submitted originally
"source": filepath
}
# Apply each user defined attribute as data
for attr in cmds.listAttr(layer, userDefined=True) or list():
try:
value = cmds.getAttr("{}.{}".format(layer, attr))
except Exception:
# Some attributes cannot be read directly,
# such as mesh and color attributes. These
# are considered non-essential to this
# particular publishing pipeline.
value = None
data[attr] = value
# Include (optional) global settings
# TODO(marcus): Take into account layer overrides
# Get global overrides and translate to Deadline values
overrides = self.parse_options(render_globals)
data.update(**overrides)
# Define nice label
label = "{0} ({1})".format(layername, data["asset"])
label += " [{0}-{1}]".format(int(data["frameStart"]),
int(data["frameEnd"]))
instance = context.create_instance(layername)
instance.data["label"] = label
instance.data.update(data)
def get_render_attribute(self, attr, layer):
return lib.get_attr_in_layer("defaultRenderGlobals.{}".format(attr),
layer=layer)
def parse_options(self, render_globals):
"""Get all overrides with a value, skip those without
Here's the kicker. These globals override defaults in the submission
integrator, but an empty value means no overriding is made.
Otherwise, Frames would override the default frames set under globals.
Args:
render_globals (str): collection of render globals
Returns:
dict: only overrides with values
"""
attributes = maya.read(render_globals)
options = {"renderGlobals": {}}
options["renderGlobals"]["Priority"] = attributes["priority"]
# Check for specific pools
pool_a, pool_b = self._discover_pools(attributes)
options["renderGlobals"].update({"Pool": pool_a})
if pool_b:
options["renderGlobals"].update({"SecondaryPool": pool_b})
legacy = attributes["useLegacyRenderLayers"]
options["renderGlobals"]["UseLegacyRenderLayers"] = legacy
# Machine list
machine_list = attributes["machineList"]
if machine_list:
key = "Whitelist" if attributes["whitelist"] else "Blacklist"
options['renderGlobals'][key] = machine_list
# Suspend publish job
state = "Suspended" if attributes["suspendPublishJob"] else "Active"
options["publishJobState"] = state
chunksize = attributes.get("framesPerTask", 1)
options["renderGlobals"]["ChunkSize"] = chunksize
# Override frames should be False if extendFrames is False. This is
# to ensure it doesn't go off doing crazy unpredictable things
override_frames = False
extend_frames = attributes.get("extendFrames", False)
if extend_frames:
override_frames = attributes.get("overrideExistingFrame", False)
options["extendFrames"] = extend_frames
options["overrideExistingFrame"] = override_frames
maya_render_plugin = "MayaBatch"
if not attributes.get("useMayaBatch", True):
maya_render_plugin = "MayaCmd"
options["mayaRenderPlugin"] = maya_render_plugin
return options
def _discover_pools(self, attributes):
pool_a = None
pool_b = None
# Check for specific pools
pool_b = []
if "primaryPool" in attributes:
pool_a = attributes["primaryPool"]
if "secondaryPool" in attributes:
pool_b = attributes["secondaryPool"]
else:
# Backwards compatibility
pool_str = attributes.get("pools", None)
if pool_str:
pool_a, pool_b = pool_str.split(";")
# Ensure empty entry token is caught
if pool_b == "-":
pool_b = None
return pool_a, pool_b

View file

@ -1,17 +1,26 @@
import re
import pyblish.api
import pype.api
import pype.maya.action
from maya import cmds
ImagePrefixes = {
'mentalray': 'defaultRenderGlobals.imageFilePrefix',
'vray': 'vraySettings.fileNamePrefix',
'arnold': 'defaultRenderGlobals.imageFilePrefix',
'renderman': 'defaultRenderGlobals.imageFilePrefix',
'redshift': 'defaultRenderGlobals.imageFilePrefix'
}
class ValidateRenderSingleCamera(pyblish.api.InstancePlugin):
"""Only one camera may be renderable in a layer.
Currently the pipeline supports only a single camera per layer.
This is because when multiple cameras are rendered the output files
automatically get different names because the <Camera> render token
is not in the output path. As such the output files conflict with how
our pipeline expects the output.
"""Validate renderable camera count for layer and <Camera> token.
Pipeline is supporting multiple renderable cameras per layer, but image
prefix must contain <Camera> token.
"""
order = pype.api.ValidateContentsOrder
@ -21,6 +30,8 @@ class ValidateRenderSingleCamera(pyblish.api.InstancePlugin):
"vrayscene"]
actions = [pype.maya.action.SelectInvalidAction]
R_CAMERA_TOKEN = re.compile(r'%c|<camera>', re.IGNORECASE)
def process(self, instance):
"""Process all the cameras in the instance"""
invalid = self.get_invalid(instance)
@ -31,8 +42,17 @@ class ValidateRenderSingleCamera(pyblish.api.InstancePlugin):
def get_invalid(cls, instance):
cameras = instance.data.get("cameras", [])
renderer = cmds.getAttr('defaultRenderGlobals.currentRenderer').lower()
# handle various renderman names
if renderer.startswith('renderman'):
renderer = 'renderman'
file_prefix = cmds.getAttr(ImagePrefixes[renderer])
if len(cameras) > 1:
if re.search(cls.R_CAMERA_TOKEN, file_prefix):
# if there is <Camera> token in prefix and we have more then
# 1 camera, all is ok.
return
cls.log.error("Multiple renderable cameras found for %s: %s " %
(instance.data["setMembers"], cameras))
return [instance.data["setMembers"]] + cameras

View file

@ -1,4 +1,5 @@
import os
import re
from maya import cmds, mel
import pymel.core as pm
@ -11,9 +12,13 @@ import pype.maya.lib as lib
class ValidateRenderSettings(pyblish.api.InstancePlugin):
"""Validates the global render settings
* File Name Prefix must be as followed:
* vray: maya/<Scene>/<Layer>/<Layer>
* default: maya/<Scene>/<RenderLayer>/<RenderLayer>_<RenderPass>
* File Name Prefix must start with: `maya/<Scene>`
all other token are customizable but sane values are:
`maya/<Scene>/<RenderLayer>/<RenderLayer>_<RenderPass>`
<Camera> token is supported also, usefull for multiple renderable
cameras per render layer.
* Frame Padding must be:
* default: 4
@ -35,16 +40,30 @@ class ValidateRenderSettings(pyblish.api.InstancePlugin):
families = ["renderlayer"]
actions = [pype.api.RepairAction]
ImagePrefixes = {
'mentalray': 'defaultRenderGlobals.imageFilePrefix',
'vray': 'vraySettings.fileNamePrefix',
'arnold': 'defaultRenderGlobals.imageFilePrefix',
'renderman': 'defaultRenderGlobals.imageFilePrefix',
'redshift': 'defaultRenderGlobals.imageFilePrefix'
}
R_AOV_TOKEN = re.compile(
r'%a|<aov>|<renderpass>', re.IGNORECASE)
R_LAYER_TOKEN = re.compile(
r'%l|<layer>|<renderlayer>', re.IGNORECASE)
R_CAMERA_TOKEN = re.compile(r'%c|<camera>', re.IGNORECASE)
R_SCENE_TOKEN = re.compile(r'%s|<scene>', re.IGNORECASE)
DEFAULT_PADDING = 4
RENDERER_PREFIX = {"vray": "maya/<scene>/<Layer>/<Layer>"}
VRAY_PREFIX = "maya/<scene>/<Layer>/<Layer>"
DEFAULT_PREFIX = "maya/<Scene>/<RenderLayer>/<RenderLayer>_<RenderPass>"
def process(self, instance):
invalid = self.get_invalid(instance)
if invalid:
raise ValueError("Invalid render settings found for '%s'!"
% instance.name)
assert invalid is False, ("Invalid render settings "
"found for '{}'!".format(instance.name))
@classmethod
def get_invalid(cls, instance):
@ -53,10 +72,11 @@ class ValidateRenderSettings(pyblish.api.InstancePlugin):
renderer = instance.data['renderer']
layer = instance.data['setMembers']
cameras = instance.data.get("cameras", [])
# Get the node attributes for current renderer
attrs = lib.RENDER_ATTRS.get(renderer, lib.RENDER_ATTRS['default'])
prefix = lib.get_attr_in_layer("{node}.{prefix}".format(**attrs),
prefix = lib.get_attr_in_layer(cls.ImagePrefixes[renderer],
layer=layer)
padding = lib.get_attr_in_layer("{node}.{padding}".format(**attrs),
layer=layer)
@ -68,12 +88,37 @@ class ValidateRenderSettings(pyblish.api.InstancePlugin):
cls.log.error("Animation needs to be enabled. Use the same "
"frame for start and end to render single frame")
fname_prefix = cls.get_prefix(renderer)
if prefix != fname_prefix:
if not prefix.lower().startswith("maya/<scene>"):
invalid = True
cls.log.error("Wrong file name prefix: %s (expected: %s)"
% (prefix, fname_prefix))
cls.log.error("Wrong image prefix [ {} ] - "
"doesn't start with: 'maya/<scene>'".format(prefix))
if not re.search(cls.R_LAYER_TOKEN, prefix):
invalid = True
cls.log.error("Wrong image prefix [ {} ] - "
"doesn't have: '<renderlayer>' or "
"'<layer>' token".format(prefix))
if not re.search(cls.R_AOV_TOKEN, prefix):
invalid = True
cls.log.error("Wrong image prefix [ {} ] - "
"doesn't have: '<renderpass>' or "
"'<aov>' 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 renderer == "vray":
if prefix.lower() != cls.VRAY_PREFIX.lower():
cls.log.warning("warning: prefix differs from "
"recommended {}".format(cls.VRAY_PREFIX))
else:
if prefix.lower() != cls.DEFAULT_PREFIX.lower():
cls.log.warning("warning: prefix differs from "
"recommended {}".format(cls.DEFAULT_PREFIX))
if padding != cls.DEFAULT_PADDING:
invalid = True