diff --git a/pype/maya/__init__.py b/pype/maya/__init__.py index b4dbc52bc8..fcc557e7bc 100644 --- a/pype/maya/__init__.py +++ b/pype/maya/__init__.py @@ -8,7 +8,6 @@ from avalon import api as avalon, pipeline, maya from avalon.maya.pipeline import IS_HEADLESS from avalon.tools import workfiles from pyblish import api as pyblish -from pypeapp import config from ..lib import ( any_outdated @@ -156,6 +155,12 @@ def on_open(_): from avalon.vendor.Qt import QtWidgets from ..widgets import popup + cmds.evalDeferred( + "from pype.maya import lib;lib.remove_render_layer_observer()") + cmds.evalDeferred( + "from pype.maya import lib;lib.add_render_layer_observer()") + cmds.evalDeferred( + "from pype.maya import lib;lib.add_render_layer_change_observer()") # # Update current task for the current scene # update_task_from_path(cmds.file(query=True, sceneName=True)) @@ -193,6 +198,12 @@ def on_new(_): """Set project resolution and fps when create a new file""" avalon.logger.info("Running callback on new..") with maya.suspended_refresh(): + cmds.evalDeferred( + "from pype.maya import lib;lib.remove_render_layer_observer()") + cmds.evalDeferred( + "from pype.maya import lib;lib.add_render_layer_observer()") + cmds.evalDeferred( + "from pype.maya import lib;lib.add_render_layer_change_observer()") lib.set_context_settings() diff --git a/pype/maya/lib.py b/pype/maya/lib.py index 0890d3863e..74bae96abb 100644 --- a/pype/maya/lib.py +++ b/pype/maya/lib.py @@ -2422,3 +2422,136 @@ class shelf(): cmds.deleteUI(each) else: cmds.shelfLayout(self.name, p="ShelfLayout") + + +def _get_render_instance(): + objectset = cmds.ls("*.id", long=True, type="objectSet", + recursive=True, objectsOnly=True) + + for objset in objectset: + + if not cmds.attributeQuery("id", node=objset, exists=True): + continue + + id_attr = "{}.id".format(objset) + if cmds.getAttr(id_attr) != "pyblish.avalon.instance": + continue + + has_family = cmds.attributeQuery("family", + node=objset, + exists=True) + if not has_family: + continue + + if cmds.getAttr("{}.family".format(objset)) == 'render': + return objset + + return None + + +renderItemObserverList = [] + + +class RenderSetupListObserver: + + def listItemAdded(self, item): + print("--- adding ...") + self._add_render_layer(item) + + def listItemRemoved(self, item): + print("--- removing ...") + self._remove_render_layer(item.name()) + + def _add_render_layer(self, item): + render_set = _get_render_instance() + layer_name = item.name() + + if not render_set: + return + + members = cmds.sets(render_set, query=True) + if not "LAYER_{}".format(layer_name) in members: + print(" - creating set for {}".format(layer_name)) + set = cmds.sets(n="LAYER_{}".format(layer_name)) + cmds.sets(set, forceElement=render_set) + rio = RenderSetupItemObserver(item) + print("- adding observer for {}".format(item.name())) + item.addItemObserver(rio.itemChanged) + renderItemObserverList.append(rio) + + def _remove_render_layer(self, layer_name): + render_set = _get_render_instance() + + if not render_set: + return + + members = cmds.sets(render_set, query=True) + if "LAYER_{}".format(layer_name) in members: + print(" - removing set for {}".format(layer_name)) + cmds.delete(n="LAYER_{}".format(layer_name)) + + +class RenderSetupItemObserver(): + + def __init__(self, item): + self.item = item + self.original_name = item.name() + + def itemChanged(self, *args, **kwargs): + if self.item.name() == self.original_name: + return + + render_set = _get_render_instance() + + if not render_set: + return + + members = cmds.sets(render_set, query=True) + if "LAYER_{}".format(self.original_name) in members: + print(" <> renaming {} to {}".format(self.original_name, + self.item.name())) + cmds.rename("LAYER_{}".format(self.original_name), + "LAYER_{}".format(self.item.name())) + self.original_name = self.item.name() + + +renderListObserver = RenderSetupListObserver() + + +def add_render_layer_change_observer(): + import maya.app.renderSetup.model.renderSetup as renderSetup + + rs = renderSetup.instance() + render_set = _get_render_instance() + if not render_set: + return + + members = cmds.sets(render_set, query=True) + layers = rs.getRenderLayers() + for layer in layers: + if "LAYER_{}".format(layer.name()) in members: + rio = RenderSetupItemObserver(layer) + print("- adding observer for {}".format(layer.name())) + layer.addItemObserver(rio.itemChanged) + renderItemObserverList.append(rio) + + +def add_render_layer_observer(): + import maya.app.renderSetup.model.renderSetup as renderSetup + + print("> adding renderSetup observer ...") + rs = renderSetup.instance() + rs.addListObserver(renderListObserver) + pass + + +def remove_render_layer_observer(): + import maya.app.renderSetup.model.renderSetup as renderSetup + + print("< removing renderSetup observer ...") + rs = renderSetup.instance() + try: + rs.removeListObserver(renderListObserver) + except ValueError: + # no observer set yet + pass diff --git a/pype/plugins/maya/create/create_render.py b/pype/plugins/maya/create/create_render.py index c54aaaa76c..f847b8add5 100644 --- a/pype/plugins/maya/create/create_render.py +++ b/pype/plugins/maya/create/create_render.py @@ -119,7 +119,7 @@ class CreateRender(avalon.maya.Creator): self.data["suspendPublishJob"] = False self.data["extendFrames"] = False self.data["overrideExistingFrame"] = True - self.data["useLegacyRenderLayers"] = True + # self.data["useLegacyRenderLayers"] = True self.data["priority"] = 50 self.data["framesPerTask"] = 1 self.data["whitelist"] = False diff --git a/pype/plugins/maya/publish/collect_render.py b/pype/plugins/maya/publish/collect_render.py index fee6581dd8..5436fbd7e4 100644 --- a/pype/plugins/maya/publish/collect_render.py +++ b/pype/plugins/maya/publish/collect_render.py @@ -2,6 +2,7 @@ import re from maya import cmds from maya import OpenMaya as om +from pprint import pprint import maya.app.renderSetup.model.renderSetup as renderSetup @@ -11,7 +12,7 @@ from avalon import maya, api import pype.maya.lib as lib -class CollectMayaRender(pyblish.api.InstancePlugin): +class CollectMayaRender(pyblish.api.ContextPlugin): """Gather all publishable render layers from renderSetup""" order = pyblish.api.CollectorOrder + 0.01 @@ -19,8 +20,21 @@ class CollectMayaRender(pyblish.api.InstancePlugin): label = "Collect Render Layers" families = ["render"] - def process(self, instance): - collected_render_layers = instance.data['setMembers'] + def process(self, context): + render_instance = None + for instance in context: + if 'render' in instance.data['families']: + render_instance = instance + + if not render_instance: + self.log.info("No render instance found, skipping render " + "layer collection.") + return + + render_globals = render_instance + collected_render_layers = render_instance.data['setMembers'] + filepath = context.data["currentFile"].replace("\\", "/") + asset = api.Session["AVALON_ASSET"] self._rs = renderSetup.instance() maya_render_layers = {l.name(): l for l in self._rs.getRenderLayers()} @@ -36,6 +50,7 @@ class CollectMayaRender(pyblish.api.InstancePlugin): self.log.warnig(msg) continue + self.log.info("processing %s" % layer) # check if layer is part of renderSetup if expected_layer_name not in maya_render_layers: msg = ("Render layer [ {} ] is not in " @@ -52,21 +67,23 @@ class CollectMayaRender(pyblish.api.InstancePlugin): # test if there are sets (subsets) to attach render to sets = cmds.ls(expected_layer_name, long=True, dag=True, sets=True) - self.log.debug(sets) + self.log.debug("marked subsets: {}".format(sets)) + layer_name = "rs_{}".format(expected_layer_name) + self.log.info(" - %s" % layer_name) # Get layer specific settings, might be overrides data = { "subset": expected_layer_name, - "setMembers": layer, + "setMembers": expected_layer_name, "publish": True, "frameStart": self.get_render_attribute("startFrame", - layer=layer), + layer=layer_name), "frameEnd": self.get_render_attribute("endFrame", - layer=layer), + layer=layer_name), "byFrameStep": self.get_render_attribute("byFrameStep", - layer=layer), + layer=layer_name), "renderer": self.get_render_attribute("currentRenderer", - layer=layer), + layer=layer_name), # instance subset "family": "Render Layers", @@ -96,23 +113,108 @@ class CollectMayaRender(pyblish.api.InstancePlugin): # 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) + overrides = self.parse_options(str(render_globals)) data.update(**overrides) # Define nice label - label = "{0} ({1})".format(layername, data["asset"]) + label = "{0} ({1})".format(expected_layer_name, data["asset"]) label += " [{0}-{1}]".format(int(data["frameStart"]), int(data["frameEnd"])) - instance = context.create_instance(layername) + instance = context.create_instance(expected_layer_name) instance.data["label"] = label instance.data.update(data) pass - def get_attributes(self, layer, attribute): + def parse_options(self, render_globals): + """Get all overrides with a value, skip those without - pass + 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) + + self.log.info(attributes) + + 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 def _get_overrides(self, layer): rset = self.maya_layers[layer].renderSettingsCollectionInstance() return rset.getOverrides() + + def get_render_attribute(self, attr, layer): + return lib.get_attr_in_layer("defaultRenderGlobals.{}".format(attr), + layer=layer) diff --git a/pype/plugins/maya/publish/collect_renderable_camera.py b/pype/plugins/maya/publish/collect_renderable_camera.py index 6b1732c3cb..707d52ef69 100644 --- a/pype/plugins/maya/publish/collect_renderable_camera.py +++ b/pype/plugins/maya/publish/collect_renderable_camera.py @@ -16,7 +16,7 @@ class CollectRenderableCamera(pyblish.api.InstancePlugin): "renderlayer"] def process(self, instance): - layer = instance.data["setMembers"] + layer = "rs_%s" % instance.data["setMembers"] cameras = cmds.ls(type="camera", long=True) renderable = [c for c in cameras if diff --git a/pype/plugins/maya/publish/collect_renderlayers.py b/pype/plugins/maya/publish/collect_renderlayers.py index 73a4d237ab..0012b28ac9 100644 --- a/pype/plugins/maya/publish/collect_renderlayers.py +++ b/pype/plugins/maya/publish/collect_renderlayers.py @@ -12,6 +12,7 @@ class CollectMayaRenderlayers(pyblish.api.ContextPlugin): order = pyblish.api.CollectorOrder + 0.01 hosts = ["maya"] label = "Render Layers" + active = False def process(self, context):