Skip to content

Instantly share code, notes, and snippets.

@BigRoy
Created August 28, 2019 16:55
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save BigRoy/653379bd3b0b1c2f6d85d50d05addf29 to your computer and use it in GitHub Desktop.
Save BigRoy/653379bd3b0b1c2f6d85d50d05addf29 to your computer and use it in GitHub Desktop.
Fix Maya legacy renderlayer being unable to duplicate (it doesn't show up correctly, but it does create a new renderLayer node visible when Show DAG nodes only is disabled)
# Fix Maya bug where you cannot duplicate a renderlayer correctly bug
# This is with Legacy Renderlayers, not Render Setup.
# Bug has been found in both Maya 2018 + 2019
import maya.cmds as mc
# Recreate all renderlayer overrides (.adjustments)
for layer in mc.ls(type="renderLayer"):
attr = layer + ".adjustments"
indices = mc.getAttr(attr, multiIndices=True) or []
connections = []
for index in indices:
# Collect current connection
index_attr = "{plug}[{index}]".format(plug=attr, index=index)
src = cmds.listConnections(index_attr + ".plug",
source=True,
plugs=True)[0]
value = cmds.getAttr(index_attr + ".value")
connections.append((src, value))
# Remove the entry and break any connections
mc.removeMultiInstance(index_attr, b=True)
# Recreate the overrides
for i, (src, value) in enumerate(connections):
cmds.connectAttr(src, layer + ".adjustments[{0}].plug".format(i))
@BigRoy
Copy link
Author

BigRoy commented Sep 4, 2020

This is what Maya uses to display the renderlayers in the Layer Editor:

global proc string[] buildRenderLayerArray()
//
//	Description
//		Get the render layers in order by displayOrder and
//		reverse to put in the list with defaultRenderLayer at
//		the bottom
{
	string $layers[];
  
	if (`objExists renderLayerManager`) {
		$layers = `listConnections renderLayerManager.renderLayerId`;

		$layers = sortLayers($layers);
		$layers = reverseArray($layers);
	}
	return $layers;
}

So the invalid layers can be found by finding those that are not connected to renderLayerManager.renderLayerId

@BigRoy
Copy link
Author

BigRoy commented Sep 4, 2020

Also had this Fix script that helped to connect and show the missing layers. However this still fails to set the right identification numbers for all layers, as sometimes Maya just did not allow to apply a specific identification number.

# Fix maya duplicated renderlayer not showing up in Render Layer editor
from colorbleed.lib import pairwise
from maya import cmds
from maya import mel
import re
from collections import defaultdict

layers = cmds.ls(type="renderLayer")
for layer in layers:
    
    # Check for missing connection for renderLayer.renderInfo.identification
    id_input = cmds.listConnections(layer + ".identification",
                                    source=True,
                                    destination=False) or []
    if not id_input:
        # Connect it to the render layer manager to show up
        # in the render layer editor as that defines "all
        # layers" by those connected to the renderlayer 
        # manager node.
        layer_manager = cmds.ls(type="renderLayerManager")[0]
        connections = cmds.listConnections(layer_manager + ".renderLayerId",
                                           connections=True,
                                           plugs=True) or []
        sources = [src for src, dest in pairwise(connections)]
        indices = []
        for source in sources:
            match = re.match(r".*\[([0-9]+)\]$", source)
            index = int(match.group(1)) + 1
            indices.append(index)
            indices.sort()
        
        next_index = indices[-1] if indices else 0
            
        src = "{0}.renderLayerId[{1}]".format(layer_manager, next_index)
        dest = "{0}.identification".format(layer)
        print("Fixing missing renderLayerId connection for: %s" % layer)
        print("\t{0} -> {1}".format(src, dest))
        cmds.connectAttr(src, dest, force=True)
                                        
    # Check for missing connection to defaultRenderingList
    # (not sure if required)
    outputs = cmds.listConnections(layer + ".message",
                                   source=False,
                                   destination=True)
    outputs = cmds.ls(outputs, type="defaultRenderingList")
    if not outputs:
        src = layer + ".message"
        dest = "defaultRenderingList1.rendering"
        print("Fixing missing connection to defaultRenderingList for: %s" % layer)
        print("\t{0} -> {1}".format(src, dest))
        cmds.connectAttr(src, dest, nextAvailable=True)
        
# Remove all non-connected renderlayer manager entries
layer_manager = cmds.ls(type="renderLayerManager")[0]
id_plug = layer_manager + ".renderLayerId"       

for index in reversed(cmds.getAttr(id_plug, multiIndices=True)):
    id_plug_index = "{0}[{1}]".format(id_plug, index)
    if not cmds.listConnections(id_plug_index,
                                destination=True,
                                source=False):
        print("Removing non-connected index %s" % id_plug_index)
        cmds.removeMultiInstance(id_plug_index)
             
# Fix all numberings for renderlayer manager entries
connections = cmds.listConnections(layer_manager + ".renderLayerId",
                                   connections=True,
                                   plugs=True) or []
for src, dest in pairwise(connections):
    index = int(re.match(r".*\[([0-9]+)\]$", src).group(1))
    if cmds.getAttr(src) != index:
        cmds.setAttr(src, index)
        
# Force renderlayer editor refresh
mel.eval('updateEditorRenderLayer "RenderLayerTab"')

@RocketeerVFX
Copy link

RocketeerVFX commented Sep 27, 2020

Hey Big Roy. Man I am so excited to see someone tackle this bug.

Tried the last script and get this error:

Error: ImportError: file line 2: No module named colorbleed.lib

Think the colorbleed.lib file is something local to your setup? I guess its part of this https://github.com/Colorbleed/colorbleed-config but no idea how to set this up.

Running legacy render layers in maya 2018

@BigRoy
Copy link
Author

BigRoy commented Sep 28, 2020

@RocketeerVFX Here's the script without those dependencies:

# Fix maya duplicated renderlayer not showing up in Render Layer editor

from maya import cmds
from maya import mel
import re
import itertools
from collections import defaultdict


def pairwise(iterable):
    """s -> (s0,s1), (s2,s3), (s4, s5), ..."""
    a = iter(iterable)
    return itertools.izip(a, a)


layers = cmds.ls(type="renderLayer")
for layer in layers:
    
    # Check for missing connection for renderLayer.renderInfo.identification
    id_input = cmds.listConnections(layer + ".identification",
                                    source=True,
                                    destination=False) or []
    if not id_input:
        # Connect it to the render layer manager to show up
        # in the render layer editor as that defines "all
        # layers" by those connected to the renderlayer 
        # manager node.
        layer_manager = cmds.ls(type="renderLayerManager")[0]
        connections = cmds.listConnections(layer_manager + ".renderLayerId",
                                           connections=True,
                                           plugs=True) or []
        sources = [src for src, dest in pairwise(connections)]
        indices = []
        for source in sources:
            match = re.match(r".*\[([0-9]+)\]$", source)
            index = int(match.group(1)) + 1
            indices.append(index)
            indices.sort()
        
        next_index = indices[-1] if indices else 0
            
        src = "{0}.renderLayerId[{1}]".format(layer_manager, next_index)
        dest = "{0}.identification".format(layer)
        print("Fixing missing renderLayerId connection for: %s" % layer)
        print("\t{0} -> {1}".format(src, dest))
        cmds.connectAttr(src, dest, force=True)
                                        
    # Check for missing connection to defaultRenderingList
    # (not sure if required)
    outputs = cmds.listConnections(layer + ".message",
                                   source=False,
                                   destination=True)
    outputs = cmds.ls(outputs, type="defaultRenderingList")
    if not outputs:
        src = layer + ".message"
        dest = "defaultRenderingList1.rendering"
        print("Fixing missing connection to defaultRenderingList for: %s" % layer)
        print("\t{0} -> {1}".format(src, dest))
        cmds.connectAttr(src, dest, nextAvailable=True)
        
# Remove all non-connected renderlayer manager entries
layer_manager = cmds.ls(type="renderLayerManager")[0]
id_plug = layer_manager + ".renderLayerId"       

for index in reversed(cmds.getAttr(id_plug, multiIndices=True)):
    id_plug_index = "{0}[{1}]".format(id_plug, index)
    if not cmds.listConnections(id_plug_index,
                                destination=True,
                                source=False):
        print("Removing non-connected index %s" % id_plug_index)
        cmds.removeMultiInstance(id_plug_index)
             
# Fix all numberings for renderlayer manager entries
connections = cmds.listConnections(layer_manager + ".renderLayerId",
                                   connections=True,
                                   plugs=True) or []
for src, dest in pairwise(connections):
    index = int(re.match(r".*\[([0-9]+)\]$", src).group(1))
    if cmds.getAttr(src) != index:
        cmds.setAttr(src, index)
        
# Force renderlayer editor refresh
mel.eval('updateEditorRenderLayer "RenderLayerTab"')

Let me know if that works for you. Unfortunately this hasn't worked for me in all cases - in particular I had a scene recently that kept bugging out. I'd be happy to get some more faulty scenes to investigate where they overlap so if you can share anything definitely pass it on and I'll see if I can reproduce it and potentially fix it if either of these scripts don't work for you.

@RocketeerVFX
Copy link

RocketeerVFX commented Sep 30, 2020 via email

@RocketeerVFX
Copy link

Didn't work :/

Error: TypeError: file line 63: argument to reversed() must be a sequence

@BigRoy
Copy link
Author

BigRoy commented Oct 2, 2020

@RocketeerVFX That's interesting. As if the renderLayerManager.renderLayerId has zero indices, odd.

Anyway, this would work around that particular error:

# Fix maya duplicated renderlayer not showing up in Render Layer editor

from maya import cmds
from maya import mel
import re
import itertools
from collections import defaultdict


def pairwise(iterable):
    """s -> (s0,s1), (s2,s3), (s4, s5), ..."""
    a = iter(iterable)
    return itertools.izip(a, a)


layers = cmds.ls(type="renderLayer")
for layer in layers:
    
    # Check for missing connection for renderLayer.renderInfo.identification
    id_input = cmds.listConnections(layer + ".identification",
                                    source=True,
                                    destination=False) or []
    if not id_input:
        # Connect it to the render layer manager to show up
        # in the render layer editor as that defines "all
        # layers" by those connected to the renderlayer 
        # manager node.
        layer_manager = cmds.ls(type="renderLayerManager")[0]
        connections = cmds.listConnections(layer_manager + ".renderLayerId",
                                           connections=True,
                                           plugs=True) or []
        sources = [src for src, dest in pairwise(connections)]
        indices = []
        for source in sources:
            match = re.match(r".*\[([0-9]+)\]$", source)
            index = int(match.group(1)) + 1
            indices.append(index)
            indices.sort()
        
        next_index = indices[-1] if indices else 0
            
        src = "{0}.renderLayerId[{1}]".format(layer_manager, next_index)
        dest = "{0}.identification".format(layer)
        print("Fixing missing renderLayerId connection for: %s" % layer)
        print("\t{0} -> {1}".format(src, dest))
        cmds.connectAttr(src, dest, force=True)
                                        
    # Check for missing connection to defaultRenderingList
    # (not sure if required)
    outputs = cmds.listConnections(layer + ".message",
                                   source=False,
                                   destination=True)
    outputs = cmds.ls(outputs, type="defaultRenderingList")
    if not outputs:
        src = layer + ".message"
        dest = "defaultRenderingList1.rendering"
        print("Fixing missing connection to defaultRenderingList for: %s" % layer)
        print("\t{0} -> {1}".format(src, dest))
        cmds.connectAttr(src, dest, nextAvailable=True)
        
# Remove all non-connected renderlayer manager entries
layer_manager = cmds.ls(type="renderLayerManager")[0]
id_plug = layer_manager + ".renderLayerId"       

indices = cmds.getAttr(id_plug, multiIndices=True) or []
for index in reversed(indices):
    id_plug_index = "{0}[{1}]".format(id_plug, index)
    if not cmds.listConnections(id_plug_index,
                                destination=True,
                                source=False):
        print("Removing non-connected index %s" % id_plug_index)
        cmds.removeMultiInstance(id_plug_index)
             
# Fix all numberings for renderlayer manager entries
connections = cmds.listConnections(layer_manager + ".renderLayerId",
                                   connections=True,
                                   plugs=True) or []
for src, dest in pairwise(connections):
    index = int(re.match(r".*\[([0-9]+)\]$", src).group(1))
    if cmds.getAttr(src) != index:
        cmds.setAttr(src, index)
        
# Force renderlayer editor refresh
mel.eval('updateEditorRenderLayer "RenderLayerTab"')

But not sure if it would actually end up perofrming what you'd need to fix the scene. You are using legacy renderlayers, right?
Anyway, if you have a simple reproducible scene I'd be happy to investigate and take a look.

@RocketeerVFX
Copy link

RocketeerVFX commented Oct 2, 2020

Thanks I'll try that :)
Yeah its legacy renderlayers.
I wish I could share it, but its all on pipe with ftrack dependencies. Which is partly what I'm thinking is causing the issues anyways. Some reference or similar.

@RocketeerVFX
Copy link

RocketeerVFX commented Oct 2, 2020

It ran this time but sadly no joy. :/

Force renderlayer editor refresh

mel.eval('updateEditorRenderLayer "RenderLayerTab"')
Fixing missing renderLayerId connection for: BTY_ASIA1
prop_bridge:renderLayerManager.renderLayerId[2] -> BTY_ASIA1.identification
Fixing missing connection to defaultRenderingList for: BTY_ASIA1
BTY_ASIA1.message -> defaultRenderingList1.rendering

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment