mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-25 05:14:40 +01:00
Merge pull request #2642 from BigRoy/maya_lib_rendersetup_cleanup
Maya: cleanup duplicate rendersetup code
This commit is contained in:
commit
06709b03da
3 changed files with 48 additions and 375 deletions
|
|
@ -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_<layername>` 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)
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue