diff --git a/openpype/hosts/maya/api/attributes.py b/openpype/hosts/maya/api/attributes.py deleted file mode 100644 index 84d1e1391f..0000000000 --- a/openpype/hosts/maya/api/attributes.py +++ /dev/null @@ -1,334 +0,0 @@ -# -*- coding: utf-8 -*- -"""Code to get attributes from render layer without switching to it. - -https://github.com/Colorbleed/colorbleed-config/blob/acre/colorbleed/maya/lib_rendersetup.py -Credits: Roy Nieterau (BigRoy) / Colorbleed -Modified for use in OpenPype - -""" - -from maya import cmds -import maya.api.OpenMaya as om -import pymel.core as pm - -import maya.app.renderSetup.model.utils as utils -from maya.app.renderSetup.model import renderSetup -from maya.app.renderSetup.model.override import ( - AbsOverride, - RelOverride, - UniqueOverride -) - -EXACT_MATCH = 0 -PARENT_MATCH = 1 -CLIENT_MATCH = 2 - -DEFAULT_RENDER_LAYER = "defaultRenderLayer" - - -def get_rendersetup_layer(layer): - """Return render setup layer name. - - This also converts names from legacy renderLayer node name to render setup - name. - - Note: `DEFAULT_RENDER_LAYER` is not a renderSetupLayer node but it is - however the valid layer name for Render Setup - so we return that as - is. - - Example: - >>> for legacy_layer in cmds.ls(type="renderLayer"): - >>> layer = get_rendersetup_layer(legacy_layer) - - Returns: - str or None: Returns renderSetupLayer node name if `layer` is a valid - layer name in legacy renderlayers or render setup layers. - Returns None if the layer can't be found or Render Setup is - currently disabled. - """ - if layer == DEFAULT_RENDER_LAYER: - # DEFAULT_RENDER_LAYER doesn't have a `renderSetupLayer` - return layer - - if not cmds.mayaHasRenderSetup(): - return None - - if not cmds.objExists(layer): - return None - - if cmds.nodeType(layer) == "renderSetupLayer": - return layer - - # By default Render Setup renames the legacy renderlayer - # to `rs_` but lets not rely on that as the - # layer node can be renamed manually - connections = cmds.listConnections(layer + ".message", - type="renderSetupLayer", - exactType=True, - source=False, - destination=True, - plugs=True) or [] - return next((conn.split(".", 1)[0] for conn in connections - if conn.endswith(".legacyRenderLayer")), None) - - -def get_attr_in_layer(node_attr, layer): - """Return attribute value in Render Setup layer. - - This will only work for attributes which can be - retrieved with `maya.cmds.getAttr` and for which - Relative and Absolute overrides are applicable. - - Examples: - >>> get_attr_in_layer("defaultResolution.width", layer="layer1") - >>> get_attr_in_layer("defaultRenderGlobals.startFrame", layer="layer") - >>> get_attr_in_layer("transform.translate", layer="layer3") - - Args: - attr (str): attribute name as 'node.attribute' - layer (str): layer name - - Returns: - object: attribute value in layer - - """ - def _layer_needs_update(layer): - """Return whether layer needs updating.""" - # Use `getattr` as e.g. DEFAULT_RENDER_LAYER does not have - # the attribute - return getattr(layer, "needsMembershipUpdate", False) or \ - getattr(layer, "needsApplyUpdate", False) - - def get_default_layer_value(node_attr_): - """Return attribute value in `DEFAULT_RENDER_LAYER`.""" - inputs = cmds.listConnections(node_attr_, - source=True, - destination=False, - type="applyOverride") or [] - if inputs: - override = inputs[0] - history_overrides = cmds.ls(cmds.listHistory(override, - pruneDagObjects=True), - type="applyOverride") - node = history_overrides[-1] if history_overrides else override - node_attr_ = node + ".original" - - return pm.getAttr(node_attr_, asString=True) - - layer = get_rendersetup_layer(layer) - rs = renderSetup.instance() - current_layer = rs.getVisibleRenderLayer() - if current_layer.name() == layer: - - # Ensure layer is up-to-date - if _layer_needs_update(current_layer): - try: - rs.switchToLayer(current_layer) - except RuntimeError: - # Some cases can cause errors on switching - # the first time with Render Setup layers - # e.g. different overrides to compounds - # and its children plugs. So we just force - # it another time. If it then still fails - # we will let it error out. - rs.switchToLayer(current_layer) - - return pm.getAttr(node_attr, asString=True) - - overrides = get_attr_overrides(node_attr, layer) - default_layer_value = get_default_layer_value(node_attr) - if not overrides: - return default_layer_value - - value = default_layer_value - for match, layer_override, index in overrides: - if isinstance(layer_override, AbsOverride): - # Absolute override - value = pm.getAttr(layer_override.name() + ".attrValue") - if match == EXACT_MATCH: - # value = value - pass - if match == PARENT_MATCH: - value = value[index] - if match == CLIENT_MATCH: - value[index] = value - - elif isinstance(layer_override, RelOverride): - # Relative override - # Value = Original * Multiply + Offset - multiply = pm.getAttr(layer_override.name() + ".multiply") - offset = pm.getAttr(layer_override.name() + ".offset") - - if match == EXACT_MATCH: - value = value * multiply + offset - if match == PARENT_MATCH: - value = value * multiply[index] + offset[index] - if match == CLIENT_MATCH: - value[index] = value[index] * multiply + offset - - else: - raise TypeError("Unsupported override: %s" % layer_override) - - return value - - -def get_attr_overrides(node_attr, layer, - skip_disabled=True, - skip_local_render=True, - stop_at_absolute_override=True): - """Return all Overrides applicable to the attribute. - - Overrides are returned as a 3-tuple: - (Match, Override, Index) - Match: - This is any of EXACT_MATCH, PARENT_MATCH, CLIENT_MATCH - and defines whether the override is exactly on the - plug, on the parent or on a child plug. - Override: - This is the RenderSetup Override instance. - Index: - This is the Plug index under the parent or for - the child that matches. The EXACT_MATCH index will - always be None. For PARENT_MATCH the index is which - index the plug is under the parent plug. For CLIENT_MATCH - the index is which child index matches the plug. - - Args: - node_attr (str): attribute name as 'node.attribute' - layer (str): layer name - skip_disabled (bool): exclude disabled overrides - skip_local_render (bool): exclude overrides marked - as local render. - stop_at_absolute_override: exclude overrides prior - to the last absolute override as they have - no influence on the resulting value. - - Returns: - list: Ordered Overrides in order of strength - - """ - def get_mplug_children(plug): - """Return children MPlugs of compound `MPlug`.""" - children = [] - if plug.isCompound: - for i in range(plug.numChildren()): - children.append(plug.child(i)) - return children - - def get_mplug_names(mplug): - """Return long and short name of `MPlug`.""" - long_name = mplug.partialName(useLongNames=True) - short_name = mplug.partialName(useLongNames=False) - return {long_name, short_name} - - def iter_override_targets(override): - try: - for target in override._targets(): - yield target - except AssertionError: - # Workaround: There is a bug where the private `_targets()` method - # fails on some attribute plugs. For example overrides - # to the defaultRenderGlobals.endFrame - # (Tested in Maya 2020.2) - print("Workaround for %s" % override) - from maya.app.renderSetup.common.utils import findPlug - - attr = override.attributeName() - if isinstance(override, UniqueOverride): - node = override.targetNodeName() - yield findPlug(node, attr) - else: - nodes = override.parent().selector().nodes() - for node in nodes: - if cmds.attributeQuery(attr, node=node, exists=True): - yield findPlug(node, attr) - - # Get the MPlug for the node.attr - sel = om.MSelectionList() - sel.add(node_attr) - plug = sel.getPlug(0) - - layer = get_rendersetup_layer(layer) - if layer == DEFAULT_RENDER_LAYER: - # DEFAULT_RENDER_LAYER will never have overrides - # since it's the default layer - return [] - - rs_layer = renderSetup.instance().getRenderLayer(layer) - if rs_layer is None: - # Renderlayer does not exist - return - - # Get any parent or children plugs as we also - # want to include them in the attribute match - # for overrides - parent = plug.parent() if plug.isChild else None - parent_index = None - if parent: - parent_index = get_mplug_children(parent).index(plug) - - children = get_mplug_children(plug) - - # Create lookup for the attribute by both long - # and short names - attr_names = get_mplug_names(plug) - for child in children: - attr_names.update(get_mplug_names(child)) - if parent: - attr_names.update(get_mplug_names(parent)) - - # Get all overrides of the layer - # And find those that are relevant to the attribute - plug_overrides = [] - - # Iterate over the overrides in reverse so we get the last - # overrides first and can "break" whenever an absolute - # override is reached - layer_overrides = list(utils.getOverridesRecursive(rs_layer)) - for layer_override in reversed(layer_overrides): - - if skip_disabled and not layer_override.isEnabled(): - # Ignore disabled overrides - continue - - if skip_local_render and layer_override.isLocalRender(): - continue - - # The targets list can be very large so we'll do - # a quick filter by attribute name to detect whether - # it matches the attribute name, or its parent or child - if layer_override.attributeName() not in attr_names: - continue - - override_match = None - for override_plug in iter_override_targets(layer_override): - - override_match = None - if plug == override_plug: - override_match = (EXACT_MATCH, layer_override, None) - - elif parent and override_plug == parent: - override_match = (PARENT_MATCH, layer_override, parent_index) - - elif children and override_plug in children: - child_index = children.index(override_plug) - override_match = (CLIENT_MATCH, layer_override, child_index) - - if override_match: - plug_overrides.append(override_match) - break - - if ( - override_match and - stop_at_absolute_override and - isinstance(layer_override, AbsOverride) and - # When the override is only on a child plug then it doesn't - # override the entire value so we not stop at this override - not override_match[0] == CLIENT_MATCH - ): - # If override is absolute override, then BREAK out - # of parent loop we don't need to look any further as - # this is the absolute override - break - - return reversed(plug_overrides) diff --git a/openpype/hosts/maya/api/lib_rendersetup.py b/openpype/hosts/maya/api/lib_rendersetup.py index 0736febe9c..0fdc54a068 100644 --- a/openpype/hosts/maya/api/lib_rendersetup.py +++ b/openpype/hosts/maya/api/lib_rendersetup.py @@ -1,24 +1,29 @@ # -*- coding: utf-8 -*- -"""Library for handling Render Setup in Maya.""" +"""Code to get attributes from render layer without switching to it. + +https://github.com/Colorbleed/colorbleed-config/blob/acre/colorbleed/maya/lib_rendersetup.py +Credits: Roy Nieterau (BigRoy) / Colorbleed +Modified for use in OpenPype + +""" + from maya import cmds import maya.api.OpenMaya as om import logging import maya.app.renderSetup.model.utils as utils -from maya.app.renderSetup.model import ( - renderSetup -) +from maya.app.renderSetup.model import renderSetup from maya.app.renderSetup.model.override import ( AbsOverride, RelOverride, UniqueOverride ) -ExactMatch = 0 -ParentMatch = 1 -ChildMatch = 2 +EXACT_MATCH = 0 +PARENT_MATCH = 1 +CLIENT_MATCH = 2 -DefaultRenderLayer = "defaultRenderLayer" +DEFAULT_RENDER_LAYER = "defaultRenderLayer" log = logging.getLogger(__name__) @@ -44,7 +49,7 @@ def get_rendersetup_layer(layer): """ - if layer == DefaultRenderLayer: + if layer == DEFAULT_RENDER_LAYER: # defaultRenderLayer doesn't have a `renderSetupLayer` return layer @@ -96,12 +101,13 @@ def get_attr_in_layer(node_attr, layer): def _layer_needs_update(layer): """Return whether layer needs updating.""" - # Use `getattr` as e.g. DefaultRenderLayer does not have the attribute + # Use `getattr` as e.g. DEFAULT_RENDER_LAYER does not have + # the attribute return getattr(layer, "needsMembershipUpdate", False) or \ getattr(layer, "needsApplyUpdate", False) def get_default_layer_value(node_attr_): - """Return attribute value in defaultRenderLayer""" + """Return attribute value in `DEFAULT_RENDER_LAYER`.""" inputs = cmds.listConnections(node_attr_, source=True, destination=False, @@ -112,11 +118,11 @@ def get_attr_in_layer(node_attr, layer): skipConversionNodes=True, type="applyOverride") or [] if inputs: - _override = inputs[0] - history_overrides = cmds.ls(cmds.listHistory(_override, + override = inputs[0] + history_overrides = cmds.ls(cmds.listHistory(override, pruneDagObjects=True), type="applyOverride") - node = history_overrides[-1] if history_overrides else _override + node = history_overrides[-1] if history_overrides else override node_attr_ = node + ".original" return pm.getAttr(node_attr_, asString=True) @@ -151,11 +157,12 @@ def get_attr_in_layer(node_attr, layer): if isinstance(layer_override, AbsOverride): # Absolute override value = pm.getAttr(layer_override.name() + ".attrValue") - if match == ExactMatch: - value = value - if match == ParentMatch: + if match == EXACT_MATCH: + # value = value + pass + elif match == PARENT_MATCH: value = value[index] - if match == ChildMatch: + elif match == CLIENT_MATCH: value[index] = value elif isinstance(layer_override, RelOverride): @@ -164,11 +171,11 @@ def get_attr_in_layer(node_attr, layer): multiply = pm.getAttr(layer_override.name() + ".multiply") offset = pm.getAttr(layer_override.name() + ".offset") - if match == ExactMatch: + if match == EXACT_MATCH: value = value * multiply + offset - if match == ParentMatch: + elif match == PARENT_MATCH: value = value * multiply[index] + offset[index] - if match == ChildMatch: + elif match == CLIENT_MATCH: value[index] = value[index] * multiply + offset else: @@ -187,7 +194,7 @@ def get_attr_overrides(node_attr, layer, (Match, Override, Index) Match: - This is any of ExactMatch, ParentMatch, ChildMatch + This is any of EXACT_MATCH, PARENT_MATCH, CLIENT_MATCH and defines whether the override is exactly on the plug, on the parent or on a child plug. @@ -196,9 +203,9 @@ def get_attr_overrides(node_attr, layer, Index: This is the Plug index under the parent or for - the child that matches. The ExactMatch index will - always be None. For ParentMatch the index is which - index the plug is under the parent plug. For ChildMatch + the child that matches. The EXACT_MATCH index will + always be None. For PARENT_MATCH the index is which + index the plug is under the parent plug. For CLIENT_MATCH the index is which child index matches the plug. Args: @@ -217,7 +224,7 @@ def get_attr_overrides(node_attr, layer, """ def get_mplug_children(plug): - """Return children MPlugs of compound MPlug""" + """Return children MPlugs of compound `MPlug`.""" children = [] if plug.isCompound: for i in range(plug.numChildren()): @@ -225,29 +232,29 @@ def get_attr_overrides(node_attr, layer, return children def get_mplug_names(mplug): - """Return long and short name of MPlug""" + """Return long and short name of `MPlug`.""" long_name = mplug.partialName(useLongNames=True) short_name = mplug.partialName(useLongNames=False) return {long_name, short_name} - def iter_override_targets(_override): + def iter_override_targets(override): try: - for target in _override._targets(): + for target in override._targets(): yield target except AssertionError: # Workaround: There is a bug where the private `_targets()` method # fails on some attribute plugs. For example overrides # to the defaultRenderGlobals.endFrame # (Tested in Maya 2020.2) - log.debug("Workaround for %s" % _override) + log.debug("Workaround for %s" % override) from maya.app.renderSetup.common.utils import findPlug - attr = _override.attributeName() - if isinstance(_override, UniqueOverride): - node = _override.targetNodeName() + attr = override.attributeName() + if isinstance(override, UniqueOverride): + node = override.targetNodeName() yield findPlug(node, attr) else: - nodes = _override.parent().selector().nodes() + nodes = override.parent().selector().nodes() for node in nodes: if cmds.attributeQuery(attr, node=node, exists=True): yield findPlug(node, attr) @@ -258,8 +265,8 @@ def get_attr_overrides(node_attr, layer, plug = sel.getPlug(0) layer = get_rendersetup_layer(layer) - if layer == DefaultRenderLayer: - # DefaultRenderLayer will never have overrides + if layer == DEFAULT_RENDER_LAYER: + # DEFAULT_RENDER_LAYER will never have overrides # since it's the default layer return [] @@ -314,14 +321,14 @@ def get_attr_overrides(node_attr, layer, override_match = None if plug == override_plug: - override_match = (ExactMatch, layer_override, None) + override_match = (EXACT_MATCH, layer_override, None) elif parent and override_plug == parent: - override_match = (ParentMatch, layer_override, parent_index) + override_match = (PARENT_MATCH, layer_override, parent_index) elif children and override_plug in children: child_index = children.index(override_plug) - override_match = (ChildMatch, layer_override, child_index) + override_match = (CLIENT_MATCH, layer_override, child_index) if override_match: plug_overrides.append(override_match) @@ -333,7 +340,7 @@ def get_attr_overrides(node_attr, layer, isinstance(layer_override, AbsOverride) and # When the override is only on a child plug then it doesn't # override the entire value so we not stop at this override - not override_match[0] == ChildMatch + not override_match[0] == CLIENT_MATCH ): # If override is absolute override, then BREAK out # of parent loop we don't need to look any further as diff --git a/openpype/hosts/maya/plugins/publish/collect_renderable_camera.py b/openpype/hosts/maya/plugins/publish/collect_renderable_camera.py index 0bf06aa273..93a37d8693 100644 --- a/openpype/hosts/maya/plugins/publish/collect_renderable_camera.py +++ b/openpype/hosts/maya/plugins/publish/collect_renderable_camera.py @@ -2,7 +2,7 @@ import pyblish.api from maya import cmds -from openpype.hosts.maya.api.attributes import get_attr_in_layer +from openpype.hosts.maya.api.lib_rendersetup import get_attr_in_layer class CollectRenderableCamera(pyblish.api.InstancePlugin):