From 6bd93217daf7a8c8dbe9ef4140357c1d8c34974e Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 14 Feb 2023 15:08:08 +0000 Subject: [PATCH 01/50] Publish cbid with ass files. --- .../publish/extract_arnold_scene_source.py | 24 +++++++ .../publish/validate_arnold_scene_source.py | 2 +- .../validate_arnold_scene_source_cbid.py | 72 +++++++++++++++++++ 3 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py diff --git a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py index 924ac58c40..bb27705d2c 100644 --- a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py @@ -139,6 +139,30 @@ class ExtractArnoldSceneSource(publish.Extractor): duplicate_nodes.append(duplicate_transform) delete_bin.append(duplicate_transform) + # Copy cbId from original to mtoa_constant. + attr_name = "mtoa_constant_cbId" + duplicate_shapes = cmds.listRelatives( + duplicate_transform, shapes=True + ) + original_shapes = cmds.listRelatives(node, shapes=True) + for duplicate_shape in duplicate_shapes: + duplicate_path = ( + duplicate_transform + "|" + duplicate_shape + ) + for original_shape in original_shapes: + original_path = node + "|" + original_shape + if duplicate_shape == original_shape: + cmds.addAttr( + duplicate_path, + longName=attr_name, + dataType="string" + ) + cmds.setAttr( + duplicate_path + "." + attr_name, + cmds.getAttr(original_path + ".cbId"), + type="string" + ) + with attribute_values(attribute_data): with maintained_selection(): self.log.info( diff --git a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py index 3b0ffd52d7..e9f6d218f9 100644 --- a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py @@ -37,7 +37,7 @@ class ValidateArnoldSceneSource(pyblish.api.InstancePlugin): nodes_by_name[node_split[-1]] = node for shape in cmds.listRelatives(node, shapes=True): - nodes_by_name[shape.split("|")[-1]] = shape + nodes_by_name[shape.split("|")[-1]] = node + "|" + shape return ungrouped_nodes, nodes_by_name, parents diff --git a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py new file mode 100644 index 0000000000..bb8ea88453 --- /dev/null +++ b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py @@ -0,0 +1,72 @@ +import maya.cmds as cmds + +import pyblish.api +from openpype.pipeline.publish import ( + ValidateContentsOrder, PublishValidationError, RepairAction +) + + +class ValidateArnoldSceneSourceCbid(pyblish.api.InstancePlugin): + """Validate Arnold Scene Source Cbid. + + It is required for the proxy and content nodes to share the same cbid. + """ + + order = ValidateContentsOrder + hosts = ["maya"] + families = ["ass"] + label = "Validate Arnold Scene Source CBID" + actions = [RepairAction] + + @staticmethod + def _get_nodes_data(nodes): + nodes_by_name = {} + for node in nodes: + node_split = node.split("|") + nodes_by_name[node_split[-1]] = node + for shape in cmds.listRelatives(node, shapes=True): + nodes_by_name[shape.split("|")[-1]] = node + "|" + shape + + return nodes_by_name + + def get_invalid_couples(self, instance): + content_nodes_by_name = self._get_nodes_data( + instance.data["setMembers"] + ) + proxy_nodes_by_name = self._get_nodes_data( + instance.data.get("proxy", []) + ) + + invalid_couples = [] + for content_name, content_node in content_nodes_by_name.items(): + for proxy_name, proxy_node in proxy_nodes_by_name.items(): + if content_name == proxy_name: + content_value = cmds.getAttr(content_node + ".cbId") + proxy_value = cmds.getAttr(proxy_node + ".cbId") + if content_value != proxy_value: + invalid_couples.append((content_node, proxy_node)) + + return invalid_couples + + def process(self, instance): + # Proxy validation. + if not instance.data.get("proxy", []): + return + + # Validate for proxy nodes sharing the same cbId as content nodes. + invalid_couples = self.get_invalid_couples(instance) + if invalid_couples: + raise PublishValidationError( + "Found proxy nodes with mismatching cbid:\n{}".format( + invalid_couples + ) + ) + + @classmethod + def repair(cls, instance): + for content_node, proxy_node in cls.get_invalid_couples(cls, instance): + cmds.setAttr( + proxy_node + ".cbId", + cmds.getAttr(content_node + ".cbId"), + type="string" + ) From d4c001684c1f3319ecfe152a054fc84767c387db Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 15 Feb 2023 12:27:50 +0000 Subject: [PATCH 02/50] Working shader assignments --- openpype/tools/mayalookassigner/app.py | 23 +- .../tools/mayalookassigner/arnold_standin.py | 225 ++++++++++++++++++ openpype/tools/mayalookassigner/commands.py | 10 +- 3 files changed, 250 insertions(+), 8 deletions(-) create mode 100644 openpype/tools/mayalookassigner/arnold_standin.py diff --git a/openpype/tools/mayalookassigner/app.py b/openpype/tools/mayalookassigner/app.py index f9508657e5..e66f0d73e2 100644 --- a/openpype/tools/mayalookassigner/app.py +++ b/openpype/tools/mayalookassigner/app.py @@ -24,6 +24,7 @@ from .commands import ( remove_unused_looks ) from .vray_proxies import vrayproxy_assign_look +from . import arnold_standin module = sys.modules[__name__] module.window = None @@ -43,7 +44,7 @@ class MayaLookAssignerWindow(QtWidgets.QWidget): filename = get_workfile() self.setObjectName("lookManager") - self.setWindowTitle("Look Manager 1.3.0 - [{}]".format(filename)) + self.setWindowTitle("Look Manager 1.4.0 - [{}]".format(filename)) self.setWindowFlags(QtCore.Qt.Window) self.setParent(parent) @@ -240,18 +241,26 @@ class MayaLookAssignerWindow(QtWidgets.QWidget): )) nodes = item["nodes"] + # Assign Vray Proxy look. if cmds.pluginInfo('vrayformaya', query=True, loaded=True): self.echo("Getting vray proxy nodes ...") vray_proxies = set(cmds.ls(type="VRayProxy", long=True)) - if vray_proxies: - for vp in vray_proxies: - if vp in nodes: - vrayproxy_assign_look(vp, subset_name) + for vp in vray_proxies: + if vp in nodes: + vrayproxy_assign_look(vp, subset_name) - nodes = list(set(item["nodes"]).difference(vray_proxies)) + nodes = list(set(item["nodes"]).difference(vray_proxies)) - # Assign look + # Assign Arnold Standin look. + arnold_standins = set(cmds.ls(type="aiStandIn", long=True)) + for standin in arnold_standins: + if standin in nodes: + arnold_standin.assign_look(standin, subset_name) + + nodes = list(set(item["nodes"]).difference(arnold_standins)) + + # Assign look if nodes: assign_look_by_version(nodes, version_id=version["_id"]) diff --git a/openpype/tools/mayalookassigner/arnold_standin.py b/openpype/tools/mayalookassigner/arnold_standin.py new file mode 100644 index 0000000000..6b5ab10720 --- /dev/null +++ b/openpype/tools/mayalookassigner/arnold_standin.py @@ -0,0 +1,225 @@ +import os +import re +from collections import defaultdict +import json +import logging + +from maya import cmds + +from openpype.pipeline import ( + legacy_io, + get_representation_path, + registered_host, + discover_loader_plugins, + loaders_from_representation, + load_container +) +from openpype.client import ( + get_representation_by_name, + get_last_version_by_subset_name +) +from openpype.hosts.maya.api import lib + + +log = logging.getLogger(__name__) + + +def get_cbid_by_node(path): + """Get cbid from Arnold Scene Source. + + Args: + path (string): Path to Arnold Scene Source. + + Returns: + (dict): Dictionary with node full name/path and CBID. + """ + import arnold + results = {} + + arnold.AiBegin() + + arnold.AiMsgSetConsoleFlags(arnold.AI_LOG_ALL) + + arnold.AiSceneLoad(None, path, None) + + # Iterate over all shader nodes + iter = arnold.AiUniverseGetNodeIterator(arnold.AI_NODE_SHAPE) + while not arnold.AiNodeIteratorFinished(iter): + node = arnold.AiNodeIteratorGetNext(iter) + if arnold.AiNodeIs(node, "polymesh"): + node_name = arnold.AiNodeGetName(node) + try: + results[arnold.AiNodeGetStr(node, "cbId")].append(node_name) + except KeyError: + results[arnold.AiNodeGetStr(node, "cbId")] = [node_name] + + arnold.AiNodeIteratorDestroy(iter) + arnold.AiEnd() + + return results + + +def get_standin_path(node): + path = cmds.getAttr(node + ".dso") + + # Account for frame extension. + basename = os.path.basename(path) + current_frame = 1 + pattern = "(#+)" + matches = re.findall(pattern, basename) + if matches: + substring = "%{}d".format(str(len(matches[0])).zfill(2)) + path = path.replace(matches[0], substring) + path = path % current_frame + + return path + + +def assign_look(standin, subset): + log.info("Assigning {} to {}.".format(subset, standin)) + + nodes_by_id = get_cbid_by_node(get_standin_path(standin)) + + # Group by asset id so we run over the look per asset + node_ids_by_asset_id = defaultdict(set) + for node_id in nodes_by_id: + asset_id = node_id.split(":", 1)[0] + node_ids_by_asset_id[asset_id].add(node_id) + + project_name = legacy_io.active_project() + for asset_id, node_ids in node_ids_by_asset_id.items(): + + # Get latest look version + version = get_last_version_by_subset_name( + project_name, + subset_name=subset, + asset_id=asset_id, + fields=["_id"] + ) + if not version: + log.info("Didn't find last version for subset name {}".format( + subset + )) + continue + + # Relationships. + json_representation = get_representation_by_name( + project_name, representation_name="json", version_id=version["_id"] + ) + + # Load relationships + shader_relation = get_representation_path(json_representation) + with open(shader_relation, "r") as f: + relationships = json.load(f) + + # Load look. + # Get representations of shader file and relationships + look_representation = get_representation_by_name( + project_name, representation_name="ma", version_id=version["_id"] + ) + + # See if representation is already loaded, if so reuse it. + host = registered_host() + representation_id = str(look_representation['_id']) + for container in host.ls(): + if (container['loader'] == "LookLoader" and + container['representation'] == representation_id): + log.info("Reusing loaded look ...") + container_node = container['objectName'] + break + else: + log.info("Using look for the first time ...") + + # Load file + all_loaders = discover_loader_plugins() + loaders = loaders_from_representation( + all_loaders, representation_id + ) + loader = next( + (i for i in loaders if i.__name__ == "LookLoader"), None) + if loader is None: + raise RuntimeError("Could not find LookLoader, this is a bug") + + # Reference the look file + with lib.maintained_selection(): + container_node = load_container(loader, look_representation) + + # Get container members + shader_nodes = lib.get_container_members(container_node) + + # Get only the node ids and paths related to this asset + # And get the shader edits the look supplies + asset_nodes_by_id = { + node_id: nodes_by_id[node_id] for node_id in node_ids + } + edits = list( + lib.iter_shader_edits( + relationships, shader_nodes, asset_nodes_by_id + ) + ) + + # Create assignments + assignments = {} + for edit in edits: + if edit["action"] == "assign": + nodes = edit["nodes"] + shader = edit["shader"] + if not cmds.ls(shader, type="shadingEngine"): + log.info("Skipping non-shader: %s" % shader) + continue + + inputs = cmds.listConnections( + shader + ".surfaceShader", source=True) + if not inputs: + log.info("Shading engine missing material: %s" % shader) + + # Strip off component assignments + for i, node in enumerate(nodes): + if "." in node: + log.warning( + ("Converting face assignment to full object " + "assignment. This conversion can be lossy: " + "{}").format(node)) + nodes[i] = node.split(".")[0] + + material = inputs[0] + assignments[material] = nodes + + # Assign shader + # Clear all current shader assignments + plug = standin + ".operators" + num = cmds.getAttr(plug, size=True) + for i in reversed(range(num)): + cmds.removeMultiInstance("{}[{}]".format(plug, i), b=True) + + # Create new assignment overrides + index = 0 + for material, paths in assignments.items(): + for path in paths: + operator = cmds.createNode("aiSetParameter") + cmds.setAttr(operator + ".selection", path, type="string") + operator_assignments = { + "shader": { + "value": material, + "index": 0, + "enabled": True + } + } + for assignee, data in operator_assignments.items(): + cmds.setAttr( + "{}.assignment[{}]".format(operator, data["index"]), + "{}='{}'".format(assignee, data["value"]), + type="string" + ) + cmds.setAttr( + "{}.enableAssignment[{}]".format( + operator, data["index"] + ), + data["enabled"] + ) + + cmds.connectAttr( + operator + ".out", "{}[{}]".format(plug, index) + ) + + index += 1 diff --git a/openpype/tools/mayalookassigner/commands.py b/openpype/tools/mayalookassigner/commands.py index 2e7a51efde..69fcc77bce 100644 --- a/openpype/tools/mayalookassigner/commands.py +++ b/openpype/tools/mayalookassigner/commands.py @@ -1,6 +1,7 @@ from collections import defaultdict import logging import os +import re import maya.cmds as cmds @@ -13,6 +14,7 @@ from openpype.pipeline import ( from openpype.hosts.maya.api import lib from .vray_proxies import get_alembic_ids_cache +from . import arnold_standin log = logging.getLogger(__name__) @@ -107,6 +109,7 @@ def create_asset_id_hash(nodes): """ node_id_hash = defaultdict(list) for node in nodes: + shapes = cmds.ls(cmds.listRelatives(node, shapes=True), long=True) # iterate over content of reference node if cmds.nodeType(node) == "reference": ref_hashes = create_asset_id_hash( @@ -122,7 +125,12 @@ def create_asset_id_hash(nodes): pid = k.split(":")[0] if node not in node_id_hash[pid]: node_id_hash[pid].append(node) - + elif shapes and cmds.nodeType(shapes[0]) == "aiStandIn": + path = arnold_standin.get_standin_path(shapes[0]) + for id, _ in arnold_standin.get_cbid_by_node(path).items(): + pid = id.split(":")[0] + if shapes[0] not in node_id_hash[pid]: + node_id_hash[pid].append(shapes[0]) else: value = lib.get_id(node) if value is None: From fafd55cfb1cbcf797ce5a8fa747f2efbc4497236 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 16 Feb 2023 08:12:26 +0000 Subject: [PATCH 03/50] Better support for namespace when publishing ASS --- .../publish/extract_arnold_scene_source.py | 15 ++++++++++++--- .../publish/validate_arnold_scene_source.py | 5 +++-- .../publish/validate_arnold_scene_source_cbid.py | 5 +++-- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py index bb27705d2c..8c9d90e2e6 100644 --- a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py @@ -113,14 +113,22 @@ class ExtractArnoldSceneSource(publish.Extractor): instance.data["representations"].append(representation) def _extract(self, nodes, attribute_data, kwargs): - self.log.info("Writing: " + kwargs["filename"]) + self.log.info( + "Writing {} with:\n{}".format(kwargs["filename"], kwargs) + ) filenames = [] # Duplicating nodes so they are direct children of the world. This # makes the hierarchy of any exported ass file the same. with delete_after() as delete_bin: duplicate_nodes = [] for node in nodes: + parent = cmds.ls( + cmds.listRelatives(node, parent=True)[0], long=True + )[0] duplicate_transform = cmds.duplicate(node)[0] + duplicate_transform = "{}|{}".format( + parent, duplicate_transform + ) # Discard the children. shapes = cmds.listRelatives(duplicate_transform, shapes=True) @@ -133,8 +141,9 @@ class ExtractArnoldSceneSource(publish.Extractor): duplicate_transform, world=True )[0] - cmds.rename(duplicate_transform, node.split("|")[-1]) - duplicate_transform = "|" + node.split("|")[-1] + basename = node.split("|")[-1].split(":")[-1] + cmds.rename(duplicate_transform, basename) + duplicate_transform = "|" + basename duplicate_nodes.append(duplicate_transform) delete_bin.append(duplicate_transform) diff --git a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py index e9f6d218f9..84240e63e6 100644 --- a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py @@ -35,9 +35,10 @@ class ValidateArnoldSceneSource(pyblish.api.InstancePlugin): if parent: parents.append(parent) - nodes_by_name[node_split[-1]] = node + nodes_by_name[node_split[-1].split(":")[-1]] = node for shape in cmds.listRelatives(node, shapes=True): - nodes_by_name[shape.split("|")[-1]] = node + "|" + shape + basename = shape.split("|")[-1].split(":")[-1] + nodes_by_name[basename] = node + "|" + shape return ungrouped_nodes, nodes_by_name, parents diff --git a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py index bb8ea88453..056cc94edf 100644 --- a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py +++ b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py @@ -23,9 +23,10 @@ class ValidateArnoldSceneSourceCbid(pyblish.api.InstancePlugin): nodes_by_name = {} for node in nodes: node_split = node.split("|") - nodes_by_name[node_split[-1]] = node + nodes_by_name[node_split[-1].split(":")[-1]] = node for shape in cmds.listRelatives(node, shapes=True): - nodes_by_name[shape.split("|")[-1]] = node + "|" + shape + basename = shape.split("|")[-1].split(":")[-1] + nodes_by_name[basename] = node + "|" + shape return nodes_by_name From cbb04773335311c6af2d9311ad045ae12e42d778 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 16 Feb 2023 08:12:37 +0000 Subject: [PATCH 04/50] Fix loading ASS --- openpype/hosts/maya/plugins/load/load_arnold_standin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/load/load_arnold_standin.py b/openpype/hosts/maya/plugins/load/load_arnold_standin.py index 66e8b69639..5a216b930b 100644 --- a/openpype/hosts/maya/plugins/load/load_arnold_standin.py +++ b/openpype/hosts/maya/plugins/load/load_arnold_standin.py @@ -179,7 +179,7 @@ class ArnoldStandinLoader(load.LoaderPlugin): # If no proxy exists, the string operator wont replace anything. cmds.setAttr( string_replace_operator + ".match", - "resources/" + proxy_basename, + proxy_basename, type="string" ) cmds.setAttr( From cf4fd979cb6fd3431a365267661a3720ccb1f436 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 16 Feb 2023 08:13:29 +0000 Subject: [PATCH 05/50] Support attribute assignments --- .../tools/mayalookassigner/arnold_standin.py | 85 ++++++++++++------- 1 file changed, 52 insertions(+), 33 deletions(-) diff --git a/openpype/tools/mayalookassigner/arnold_standin.py b/openpype/tools/mayalookassigner/arnold_standin.py index 6b5ab10720..3290944df9 100644 --- a/openpype/tools/mayalookassigner/arnold_standin.py +++ b/openpype/tools/mayalookassigner/arnold_standin.py @@ -24,6 +24,12 @@ from openpype.hosts.maya.api import lib log = logging.getLogger(__name__) +ATTRIBUTE_MAPPING = { + "aiSubdivType": "subdiv_type", + "aiSubdivIterations": "subdiv_iterations" +} + + def get_cbid_by_node(path): """Get cbid from Arnold Scene Source. @@ -146,6 +152,7 @@ def assign_look(standin, subset): # Get container members shader_nodes = lib.get_container_members(container_node) + namespace = shader_nodes[0].split(":")[0] # Get only the node ids and paths related to this asset # And get the shader edits the look supplies @@ -159,31 +166,49 @@ def assign_look(standin, subset): ) # Create assignments - assignments = {} + node_assignments = {} for edit in edits: + for node in edit["nodes"]: + if node not in node_assignments: + node_assignments[node] = [] + if edit["action"] == "assign": - nodes = edit["nodes"] - shader = edit["shader"] - if not cmds.ls(shader, type="shadingEngine"): - log.info("Skipping non-shader: %s" % shader) + if not cmds.ls(edit["shader"], type="shadingEngine"): + log.info("Skipping non-shader: %s" % edit["shader"]) continue inputs = cmds.listConnections( - shader + ".surfaceShader", source=True) + edit["shader"] + ".surfaceShader", source=True) if not inputs: - log.info("Shading engine missing material: %s" % shader) + log.info( + "Shading engine missing material: %s" % edit["shader"] + ) # Strip off component assignments - for i, node in enumerate(nodes): + for i, node in enumerate(edit["nodes"]): if "." in node: log.warning( ("Converting face assignment to full object " "assignment. This conversion can be lossy: " "{}").format(node)) - nodes[i] = node.split(".")[0] + edit["nodes"][i] = node.split(".")[0] - material = inputs[0] - assignments[material] = nodes + assignment = "shader='{}'".format(inputs[0]) + for node in edit["nodes"]: + node_assignments[node].append(assignment) + + if edit["action"] == "setattr": + for attr, value in edit["attributes"].items(): + if attr not in ATTRIBUTE_MAPPING: + log.warning( + "Skipping setting attribute {} on {} because it is" + " not recognized.".format(attr, edit["nodes"]) + ) + continue + + assignment = "{}={}".format(ATTRIBUTE_MAPPING[attr], value) + for node in edit["nodes"]: + node_assignments[node].append(assignment) # Assign shader # Clear all current shader assignments @@ -194,32 +219,26 @@ def assign_look(standin, subset): # Create new assignment overrides index = 0 - for material, paths in assignments.items(): - for path in paths: + for node, assignments in node_assignments.items(): + if not assignments: + continue + + with lib.maintained_selection(): operator = cmds.createNode("aiSetParameter") - cmds.setAttr(operator + ".selection", path, type="string") - operator_assignments = { - "shader": { - "value": material, - "index": 0, - "enabled": True - } - } - for assignee, data in operator_assignments.items(): - cmds.setAttr( - "{}.assignment[{}]".format(operator, data["index"]), - "{}='{}'".format(assignee, data["value"]), - type="string" - ) - cmds.setAttr( - "{}.enableAssignment[{}]".format( - operator, data["index"] - ), - data["enabled"] - ) + operator = cmds.rename(operator, namespace + ":" + operator) + + cmds.setAttr(operator + ".selection", node, type="string") + for i, assignment in enumerate(assignments): + cmds.setAttr( + "{}.assignment[{}]".format(operator, i), + assignment, + type="string" + ) cmds.connectAttr( operator + ".out", "{}[{}]".format(plug, index) ) index += 1 + + cmds.sets(operator, edit=True, addElement=container_node[0]) From 2cb2ba00b788b1423d86098bf99d50b6f27ccc3b Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 16 Feb 2023 08:30:57 +0000 Subject: [PATCH 06/50] Fix collecting and assigning enum values --- openpype/hosts/maya/plugins/publish/collect_look.py | 2 +- openpype/tools/mayalookassigner/arnold_standin.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/collect_look.py b/openpype/hosts/maya/plugins/publish/collect_look.py index b01160a1c0..287ddc228b 100644 --- a/openpype/hosts/maya/plugins/publish/collect_look.py +++ b/openpype/hosts/maya/plugins/publish/collect_look.py @@ -556,7 +556,7 @@ class CollectLook(pyblish.api.InstancePlugin): continue if cmds.getAttr(attribute, type=True) == "message": continue - node_attributes[attr] = cmds.getAttr(attribute) + node_attributes[attr] = cmds.getAttr(attribute, asString=True) # Only include if there are any properties we care about if not node_attributes: continue diff --git a/openpype/tools/mayalookassigner/arnold_standin.py b/openpype/tools/mayalookassigner/arnold_standin.py index 3290944df9..392fe32148 100644 --- a/openpype/tools/mayalookassigner/arnold_standin.py +++ b/openpype/tools/mayalookassigner/arnold_standin.py @@ -206,7 +206,11 @@ def assign_look(standin, subset): ) continue + if isinstance(value, str): + value = "'{}'".format(value) + assignment = "{}={}".format(ATTRIBUTE_MAPPING[attr], value) + for node in edit["nodes"]: node_assignments[node].append(assignment) From 0e70a9559bd1238a586ce80bf178ee29ce0313f1 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 16 Feb 2023 09:03:55 +0000 Subject: [PATCH 07/50] Support displacement shader --- .../tools/mayalookassigner/arnold_standin.py | 56 ++++++++++++------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/openpype/tools/mayalookassigner/arnold_standin.py b/openpype/tools/mayalookassigner/arnold_standin.py index 392fe32148..0ba1526cda 100644 --- a/openpype/tools/mayalookassigner/arnold_standin.py +++ b/openpype/tools/mayalookassigner/arnold_standin.py @@ -81,6 +81,31 @@ def get_standin_path(node): return path +def shading_engine_assignments(shading_engine, attr, nodes, assignments): + shader_inputs = cmds.listConnections( + shading_engine + "." + attr, source=True) + if not shader_inputs: + log.info( + "Shading engine \"{}\" missing input \"{}\"".format( + shading_engine, attr + ) + ) + + # Strip off component assignments + for i, node in enumerate(nodes): + if "." in node: + log.warning( + ("Converting face assignment to full object " + "assignment. This conversion can be lossy: " + "{}").format(node)) + nodes[i] = node.split(".")[0] + + shader_type = "shader" if attr == "surfaceShader" else "disp_map" + assignment = "{}='{}'".format(shader_type, shader_inputs[0]) + for node in nodes: + assignments[node].append(assignment) + + def assign_look(standin, subset): log.info("Assigning {} to {}.".format(subset, standin)) @@ -177,25 +202,18 @@ def assign_look(standin, subset): log.info("Skipping non-shader: %s" % edit["shader"]) continue - inputs = cmds.listConnections( - edit["shader"] + ".surfaceShader", source=True) - if not inputs: - log.info( - "Shading engine missing material: %s" % edit["shader"] - ) - - # Strip off component assignments - for i, node in enumerate(edit["nodes"]): - if "." in node: - log.warning( - ("Converting face assignment to full object " - "assignment. This conversion can be lossy: " - "{}").format(node)) - edit["nodes"][i] = node.split(".")[0] - - assignment = "shader='{}'".format(inputs[0]) - for node in edit["nodes"]: - node_assignments[node].append(assignment) + shading_engine_assignments( + edit["shader"], + "surfaceShader", + edit["nodes"], + node_assignments + ) + shading_engine_assignments( + edit["shader"], + "displacementShader", + edit["nodes"], + node_assignments + ) if edit["action"] == "setattr": for attr, value in edit["attributes"].items(): From d13b74cb29b41d8fb94a7b6e3fbdd0146619ade9 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 16 Feb 2023 10:47:51 +0000 Subject: [PATCH 08/50] Support more parameters. --- .../tools/mayalookassigner/arnold_standin.py | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/openpype/tools/mayalookassigner/arnold_standin.py b/openpype/tools/mayalookassigner/arnold_standin.py index 0ba1526cda..b7568ec45f 100644 --- a/openpype/tools/mayalookassigner/arnold_standin.py +++ b/openpype/tools/mayalookassigner/arnold_standin.py @@ -25,11 +25,49 @@ log = logging.getLogger(__name__) ATTRIBUTE_MAPPING = { + "primaryVisibility": "visibility", # Camera + "castsShadows": "visibility", # Shadow + "receiveShadows": "receive_shadows", + "aiSelfShadows": "self_shadows", + "aiOpaque": "opaque", + "aiMatte": "matte", + "aiVisibleInDiffuseTransmission": "visibility", + "aiVisibleInSpecularTransmission": "visibility", + "aiVisibleInVolume": "visibility", + "aiVisibleInDiffuseReflection": "visibility", + "aiVisibleInSpecularReflection": "visibility", + "aiSubdivUvSmoothing": "subdiv_uv_smoothing", + "aiDispHeight": "disp_height", + "aiDispPadding": "disp_padding", + "aiDispZeroValue": "disp_zero_value", + "aiStepSize": "step_size", + "aiVolumePadding": "volume_padding", "aiSubdivType": "subdiv_type", "aiSubdivIterations": "subdiv_iterations" } +def calculate_visibility_mask(attributes): + # https://arnoldsupport.com/2018/11/21/backdoor-setting-visibility/ + mapping = { + "primaryVisibility": 1, # Camera + "castsShadows": 2, # Shadow + "aiVisibleInDiffuseTransmission": 4, + "aiVisibleInSpecularTransmission": 8, + "aiVisibleInVolume": 16, + "aiVisibleInDiffuseReflection": 32, + "aiVisibleInSpecularReflection": 64 + } + mask = 255 + for attr, value in mapping.items(): + if attributes.get(attr, True): + continue + + mask -= value + + return mask + + def get_cbid_by_node(path): """Get cbid from Arnold Scene Source. @@ -216,6 +254,7 @@ def assign_look(standin, subset): ) if edit["action"] == "setattr": + visibility = False for attr, value in edit["attributes"].items(): if attr not in ATTRIBUTE_MAPPING: log.warning( @@ -227,11 +266,37 @@ def assign_look(standin, subset): if isinstance(value, str): value = "'{}'".format(value) + if ATTRIBUTE_MAPPING[attr] == "visibility": + visibility = True + continue + assignment = "{}={}".format(ATTRIBUTE_MAPPING[attr], value) for node in edit["nodes"]: node_assignments[node].append(assignment) + if visibility: + # https://arnoldsupport.com/2018/11/21/backdoor-setting-visibility/ + mapping = { + "primaryVisibility": 1, # Camera + "castsShadows": 2, # Shadow + "aiVisibleInDiffuseTransmission": 4, + "aiVisibleInSpecularTransmission": 8, + "aiVisibleInVolume": 16, + "aiVisibleInDiffuseReflection": 32, + "aiVisibleInSpecularReflection": 64 + } + mask = 255 + for attr, value in mapping.items(): + if edit["attributes"].get(attr, True): + continue + mask -= value + + assignment = "visibility={}".format(mask) + + for node in edit["nodes"]: + node_assignments[node].append(assignment) + # Assign shader # Clear all current shader assignments plug = standin + ".operators" From 70b607bf15de2bbc35b7fff26ed710cd0f46c443 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 16 Feb 2023 11:05:57 +0000 Subject: [PATCH 09/50] Refactor to share code between vray and arnold --- .../tools/mayalookassigner/arnold_standin.py | 68 ++------------- openpype/tools/mayalookassigner/lib.py | 87 +++++++++++++++++++ 2 files changed, 95 insertions(+), 60 deletions(-) create mode 100644 openpype/tools/mayalookassigner/lib.py diff --git a/openpype/tools/mayalookassigner/arnold_standin.py b/openpype/tools/mayalookassigner/arnold_standin.py index b7568ec45f..7d3b7e59b3 100644 --- a/openpype/tools/mayalookassigner/arnold_standin.py +++ b/openpype/tools/mayalookassigner/arnold_standin.py @@ -1,24 +1,14 @@ import os import re from collections import defaultdict -import json import logging from maya import cmds -from openpype.pipeline import ( - legacy_io, - get_representation_path, - registered_host, - discover_loader_plugins, - loaders_from_representation, - load_container -) -from openpype.client import ( - get_representation_by_name, - get_last_version_by_subset_name -) -from openpype.hosts.maya.api import lib +from openpype.pipeline import legacy_io +from openpype.client import get_last_version_by_subset_name +from openpype.hosts.maya import api +from . import lib log = logging.getLogger(__name__) @@ -171,50 +161,8 @@ def assign_look(standin, subset): )) continue - # Relationships. - json_representation = get_representation_by_name( - project_name, representation_name="json", version_id=version["_id"] - ) - - # Load relationships - shader_relation = get_representation_path(json_representation) - with open(shader_relation, "r") as f: - relationships = json.load(f) - - # Load look. - # Get representations of shader file and relationships - look_representation = get_representation_by_name( - project_name, representation_name="ma", version_id=version["_id"] - ) - - # See if representation is already loaded, if so reuse it. - host = registered_host() - representation_id = str(look_representation['_id']) - for container in host.ls(): - if (container['loader'] == "LookLoader" and - container['representation'] == representation_id): - log.info("Reusing loaded look ...") - container_node = container['objectName'] - break - else: - log.info("Using look for the first time ...") - - # Load file - all_loaders = discover_loader_plugins() - loaders = loaders_from_representation( - all_loaders, representation_id - ) - loader = next( - (i for i in loaders if i.__name__ == "LookLoader"), None) - if loader is None: - raise RuntimeError("Could not find LookLoader, this is a bug") - - # Reference the look file - with lib.maintained_selection(): - container_node = load_container(loader, look_representation) - - # Get container members - shader_nodes = lib.get_container_members(container_node) + relationships = lib.get_look_relationships(version["_id"]) + shader_nodes, container_node = lib.load_look(version["_id"]) namespace = shader_nodes[0].split(":")[0] # Get only the node ids and paths related to this asset @@ -223,7 +171,7 @@ def assign_look(standin, subset): node_id: nodes_by_id[node_id] for node_id in node_ids } edits = list( - lib.iter_shader_edits( + api.lib.iter_shader_edits( relationships, shader_nodes, asset_nodes_by_id ) ) @@ -310,7 +258,7 @@ def assign_look(standin, subset): if not assignments: continue - with lib.maintained_selection(): + with api.lib.maintained_selection(): operator = cmds.createNode("aiSetParameter") operator = cmds.rename(operator, namespace + ":" + operator) diff --git a/openpype/tools/mayalookassigner/lib.py b/openpype/tools/mayalookassigner/lib.py new file mode 100644 index 0000000000..5594c53c33 --- /dev/null +++ b/openpype/tools/mayalookassigner/lib.py @@ -0,0 +1,87 @@ +import json +import logging + +from openpype.pipeline import ( + legacy_io, + get_representation_path, + registered_host, + discover_loader_plugins, + loaders_from_representation, + load_container +) +from openpype.client import get_representation_by_name +from openpype.hosts.maya.api import lib + + +log = logging.getLogger(__name__) + + +def get_look_relationships(version_id): + # type: (str) -> dict + """Get relations for the look. + + Args: + version_id (str): Parent version Id. + + Returns: + dict: Dictionary of relations. + """ + + project_name = legacy_io.active_project() + json_representation = get_representation_by_name( + project_name, representation_name="json", version_id=version_id + ) + + # Load relationships + shader_relation = get_representation_path(json_representation) + with open(shader_relation, "r") as f: + relationships = json.load(f) + + return relationships + + +def load_look(version_id): + # type: (str) -> list + """Load look from version. + + Get look from version and invoke Loader for it. + + Args: + version_id (str): Version ID + + Returns: + list of shader nodes. + + """ + + project_name = legacy_io.active_project() + # Get representations of shader file and relationships + look_representation = get_representation_by_name( + project_name, representation_name="ma", version_id=version_id + ) + + # See if representation is already loaded, if so reuse it. + host = registered_host() + representation_id = str(look_representation['_id']) + for container in host.ls(): + if (container['loader'] == "LookLoader" and + container['representation'] == representation_id): + log.info("Reusing loaded look ...") + container_node = container['objectName'] + break + else: + log.info("Using look for the first time ...") + + # Load file + all_loaders = discover_loader_plugins() + loaders = loaders_from_representation(all_loaders, representation_id) + loader = next( + (i for i in loaders if i.__name__ == "LookLoader"), None) + if loader is None: + raise RuntimeError("Could not find LookLoader, this is a bug") + + # Reference the look file + with lib.maintained_selection(): + container_node = load_container(loader, look_representation) + + return lib.get_container_members(container_node), container_node From c426e34761828bfb7540c041fc2b0398ab10acb4 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 16 Feb 2023 11:08:25 +0000 Subject: [PATCH 10/50] Missing refactor code --- .../tools/mayalookassigner/vray_proxies.py | 97 ++----------------- 1 file changed, 7 insertions(+), 90 deletions(-) diff --git a/openpype/tools/mayalookassigner/vray_proxies.py b/openpype/tools/mayalookassigner/vray_proxies.py index 889396e555..6ee618f37a 100644 --- a/openpype/tools/mayalookassigner/vray_proxies.py +++ b/openpype/tools/mayalookassigner/vray_proxies.py @@ -3,26 +3,16 @@ import os from collections import defaultdict import logging -import json import six import alembic.Abc from maya import cmds -from openpype.client import ( - get_representation_by_name, - get_last_version_by_subset_name, -) -from openpype.pipeline import ( - legacy_io, - load_container, - loaders_from_representation, - discover_loader_plugins, - get_representation_path, - registered_host, -) -from openpype.hosts.maya.api import lib +from openpype.client import get_last_version_by_subset_name +from openpype.pipeline import legacy_io +from openpype.hosts.maya import api +from . import lib log = logging.getLogger(__name__) @@ -149,79 +139,6 @@ def assign_vrayproxy_shaders(vrayproxy, assignments): index += 1 -def get_look_relationships(version_id): - # type: (str) -> dict - """Get relations for the look. - - Args: - version_id (str): Parent version Id. - - Returns: - dict: Dictionary of relations. - """ - - project_name = legacy_io.active_project() - json_representation = get_representation_by_name( - project_name, representation_name="json", version_id=version_id - ) - - # Load relationships - shader_relation = get_representation_path(json_representation) - with open(shader_relation, "r") as f: - relationships = json.load(f) - - return relationships - - -def load_look(version_id): - # type: (str) -> list - """Load look from version. - - Get look from version and invoke Loader for it. - - Args: - version_id (str): Version ID - - Returns: - list of shader nodes. - - """ - - project_name = legacy_io.active_project() - # Get representations of shader file and relationships - look_representation = get_representation_by_name( - project_name, representation_name="ma", version_id=version_id - ) - - # See if representation is already loaded, if so reuse it. - host = registered_host() - representation_id = str(look_representation['_id']) - for container in host.ls(): - if (container['loader'] == "LookLoader" and - container['representation'] == representation_id): - log.info("Reusing loaded look ...") - container_node = container['objectName'] - break - else: - log.info("Using look for the first time ...") - - # Load file - all_loaders = discover_loader_plugins() - loaders = loaders_from_representation(all_loaders, representation_id) - loader = next( - (i for i in loaders if i.__name__ == "LookLoader"), None) - if loader is None: - raise RuntimeError("Could not find LookLoader, this is a bug") - - # Reference the look file - with lib.maintained_selection(): - container_node = load_container(loader, look_representation) - - # Get container members - shader_nodes = lib.get_container_members(container_node) - return shader_nodes - - def vrayproxy_assign_look(vrayproxy, subset="lookDefault"): # type: (str, str) -> None """Assign look to vray proxy. @@ -263,8 +180,8 @@ def vrayproxy_assign_look(vrayproxy, subset="lookDefault"): )) continue - relationships = get_look_relationships(version["_id"]) - shadernodes = load_look(version["_id"]) + relationships = lib.get_look_relationships(version["_id"]) + shadernodes, _ = lib.load_look(version["_id"]) # Get only the node ids and paths related to this asset # And get the shader edits the look supplies @@ -272,7 +189,7 @@ def vrayproxy_assign_look(vrayproxy, subset="lookDefault"): node_id: nodes_by_id[node_id] for node_id in node_ids } edits = list( - lib.iter_shader_edits( + api.lib.iter_shader_edits( relationships, shadernodes, asset_nodes_by_id)) # Create assignments From 017d85d74b897a85d422e8b6770e4b789ad040e9 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 16 Feb 2023 11:11:29 +0000 Subject: [PATCH 11/50] Hound --- openpype/tools/mayalookassigner/commands.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/tools/mayalookassigner/commands.py b/openpype/tools/mayalookassigner/commands.py index 69fcc77bce..c22a8c9211 100644 --- a/openpype/tools/mayalookassigner/commands.py +++ b/openpype/tools/mayalookassigner/commands.py @@ -1,7 +1,6 @@ from collections import defaultdict import logging import os -import re import maya.cmds as cmds From d770547c292b2f286d5ba32ee851c336c4a4fe4a Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 1 Mar 2023 12:21:07 +0000 Subject: [PATCH 12/50] Get all descendents in collector. - refactor common code to lib. --- openpype/hosts/maya/api/lib.py | 41 +++++++++++++++++- .../publish/collect_arnold_scene_source.py | 11 +++-- .../maya/plugins/publish/collect_instances.py | 42 +------------------ .../publish/validate_arnold_scene_source.py | 3 -- .../validate_arnold_scene_source_cbid.py | 3 -- 5 files changed, 48 insertions(+), 52 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 4324d321dc..7e8bb5439f 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -4,7 +4,6 @@ import os import sys import platform import uuid -import math import re import json @@ -3562,3 +3561,43 @@ def get_color_management_output_transform(): if preferences["output_transform_enabled"]: colorspace = preferences["output_transform"] return colorspace + + +def get_all_children(nodes): + """Return all children of `nodes` including each instanced child. + Using maya.cmds.listRelatives(allDescendents=True) includes only the first + instance. As such, this function acts as an optimal replacement with a + focus on a fast query. + + """ + + sel = OpenMaya.MSelectionList() + traversed = set() + iterator = OpenMaya.MItDag(OpenMaya.MItDag.kDepthFirst) + for node in nodes: + + if node in traversed: + # Ignore if already processed as a child + # before + continue + + sel.clear() + sel.add(node) + dag = sel.getDagPath(0) + + iterator.reset(dag) + # ignore self + iterator.next() # noqa: B305 + while not iterator.isDone(): + + path = iterator.fullPathName() + + if path in traversed: + iterator.prune() + iterator.next() # noqa: B305 + continue + + traversed.add(path) + iterator.next() # noqa: B305 + + return list(traversed) diff --git a/openpype/hosts/maya/plugins/publish/collect_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/collect_arnold_scene_source.py index 0415808b7a..fd4993d09e 100644 --- a/openpype/hosts/maya/plugins/publish/collect_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/collect_arnold_scene_source.py @@ -1,6 +1,7 @@ from maya import cmds import pyblish.api +from openpype.hosts.maya.api.lib import get_all_children class CollectArnoldSceneSource(pyblish.api.InstancePlugin): @@ -21,11 +22,13 @@ class CollectArnoldSceneSource(pyblish.api.InstancePlugin): self.log.warning("Skipped empty instance: \"%s\" " % objset) continue if objset.endswith("content_SET"): - instance.data["setMembers"] = cmds.ls(members, long=True) - self.log.debug("content members: {}".format(members)) + set_members = get_all_children(cmds.ls(members, long=True)) + instance.data["setMembers"] = set_members + self.log.debug("content members: {}".format(set_members)) elif objset.endswith("proxy_SET"): - instance.data["proxy"] = cmds.ls(members, long=True) - self.log.debug("proxy members: {}".format(members)) + set_members = get_all_children(cmds.ls(members, long=True)) + instance.data["proxy"] = set_members + self.log.debug("proxy members: {}".format(set_members)) # Use camera in object set if present else default to render globals # camera. diff --git a/openpype/hosts/maya/plugins/publish/collect_instances.py b/openpype/hosts/maya/plugins/publish/collect_instances.py index 6c6819f0a2..6bf0756323 100644 --- a/openpype/hosts/maya/plugins/publish/collect_instances.py +++ b/openpype/hosts/maya/plugins/publish/collect_instances.py @@ -1,48 +1,8 @@ from maya import cmds -import maya.api.OpenMaya as om import pyblish.api import json - - -def get_all_children(nodes): - """Return all children of `nodes` including each instanced child. - Using maya.cmds.listRelatives(allDescendents=True) includes only the first - instance. As such, this function acts as an optimal replacement with a - focus on a fast query. - - """ - - sel = om.MSelectionList() - traversed = set() - iterator = om.MItDag(om.MItDag.kDepthFirst) - for node in nodes: - - if node in traversed: - # Ignore if already processed as a child - # before - continue - - sel.clear() - sel.add(node) - dag = sel.getDagPath(0) - - iterator.reset(dag) - # ignore self - iterator.next() # noqa: B305 - while not iterator.isDone(): - - path = iterator.fullPathName() - - if path in traversed: - iterator.prune() - iterator.next() # noqa: B305 - continue - - traversed.add(path) - iterator.next() # noqa: B305 - - return list(traversed) +from openpype.hosts.maya.api.lib import get_all_children class CollectInstances(pyblish.api.ContextPlugin): diff --git a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py index 84240e63e6..e582560e12 100644 --- a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py @@ -36,9 +36,6 @@ class ValidateArnoldSceneSource(pyblish.api.InstancePlugin): parents.append(parent) nodes_by_name[node_split[-1].split(":")[-1]] = node - for shape in cmds.listRelatives(node, shapes=True): - basename = shape.split("|")[-1].split(":")[-1] - nodes_by_name[basename] = node + "|" + shape return ungrouped_nodes, nodes_by_name, parents diff --git a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py index 056cc94edf..5d0ef79838 100644 --- a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py +++ b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py @@ -24,9 +24,6 @@ class ValidateArnoldSceneSourceCbid(pyblish.api.InstancePlugin): for node in nodes: node_split = node.split("|") nodes_by_name[node_split[-1].split(":")[-1]] = node - for shape in cmds.listRelatives(node, shapes=True): - basename = shape.split("|")[-1].split(":")[-1] - nodes_by_name[basename] = node + "|" + shape return nodes_by_name From 6b67508d405b18013502396d32fbf2cba4f41ec6 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Thu, 16 Mar 2023 17:03:11 +0000 Subject: [PATCH 13/50] Update openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py Co-authored-by: Roy Nieterau --- .../hosts/maya/plugins/publish/extract_arnold_scene_source.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py index 8c9d90e2e6..54b8a005b2 100644 --- a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py @@ -122,9 +122,7 @@ class ExtractArnoldSceneSource(publish.Extractor): with delete_after() as delete_bin: duplicate_nodes = [] for node in nodes: - parent = cmds.ls( - cmds.listRelatives(node, parent=True)[0], long=True - )[0] + parent = cmds.listRelatives(node, parent=True, fullPath=True)[0] duplicate_transform = cmds.duplicate(node)[0] duplicate_transform = "{}|{}".format( parent, duplicate_transform From b443b52d82fc5ea8246cb92e5484a31e73296016 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Thu, 16 Mar 2023 17:03:51 +0000 Subject: [PATCH 14/50] Update openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py Co-authored-by: Roy Nieterau --- .../hosts/maya/plugins/publish/extract_arnold_scene_source.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py index 54b8a005b2..f672c5a94b 100644 --- a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py @@ -140,8 +140,7 @@ class ExtractArnoldSceneSource(publish.Extractor): )[0] basename = node.split("|")[-1].split(":")[-1] - cmds.rename(duplicate_transform, basename) - duplicate_transform = "|" + basename + duplicate_transform = cmds.rename(duplicate_transform, basename) duplicate_nodes.append(duplicate_transform) delete_bin.append(duplicate_transform) From eaac0cb2e783e259de904c67278e60cb14e0ab88 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Thu, 16 Mar 2023 17:04:12 +0000 Subject: [PATCH 15/50] Update openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py Co-authored-by: Roy Nieterau --- .../hosts/maya/plugins/publish/extract_arnold_scene_source.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py index f672c5a94b..4de0f49de1 100644 --- a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py @@ -148,9 +148,9 @@ class ExtractArnoldSceneSource(publish.Extractor): # Copy cbId from original to mtoa_constant. attr_name = "mtoa_constant_cbId" duplicate_shapes = cmds.listRelatives( - duplicate_transform, shapes=True + duplicate_transform, shapes=True, fullPath=True ) - original_shapes = cmds.listRelatives(node, shapes=True) + original_shapes = cmds.listRelatives(node, shapes=True, fullPath=True) for duplicate_shape in duplicate_shapes: duplicate_path = ( duplicate_transform + "|" + duplicate_shape From 60de2b537fec0bb53c475854a015839b1f0836e8 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Thu, 16 Mar 2023 17:09:42 +0000 Subject: [PATCH 16/50] Update openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py Co-authored-by: Roy Nieterau --- .../publish/validate_arnold_scene_source_cbid.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py index 5d0ef79838..87c47978c8 100644 --- a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py +++ b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py @@ -37,12 +37,16 @@ class ValidateArnoldSceneSourceCbid(pyblish.api.InstancePlugin): invalid_couples = [] for content_name, content_node in content_nodes_by_name.items(): - for proxy_name, proxy_node in proxy_nodes_by_name.items(): - if content_name == proxy_name: - content_value = cmds.getAttr(content_node + ".cbId") - proxy_value = cmds.getAttr(proxy_node + ".cbId") - if content_value != proxy_value: - invalid_couples.append((content_node, proxy_node)) + proxy_node = proxy_nodes_by_name.get(content_name, None) + + if not proxy_node: + self.log.debug("Content node '{}' has no matching proxy node.".format(content_node)) + continue + + content_id = lib.get_id(content_node) + proxy_id = lib.get_id(proxy_node) + if content_id != proxy_id: + invalid_couples.append((content_node, proxy_node)) return invalid_couples From e599dcda0d5cee6f91239201dda6a2febb509888 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Thu, 16 Mar 2023 17:11:54 +0000 Subject: [PATCH 17/50] Update openpype/hosts/maya/tools/mayalookassigner/commands.py Co-authored-by: Roy Nieterau --- openpype/hosts/maya/tools/mayalookassigner/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/tools/mayalookassigner/commands.py b/openpype/hosts/maya/tools/mayalookassigner/commands.py index c22a8c9211..d7061f12e1 100644 --- a/openpype/hosts/maya/tools/mayalookassigner/commands.py +++ b/openpype/hosts/maya/tools/mayalookassigner/commands.py @@ -108,7 +108,7 @@ def create_asset_id_hash(nodes): """ node_id_hash = defaultdict(list) for node in nodes: - shapes = cmds.ls(cmds.listRelatives(node, shapes=True), long=True) + shapes = cmds.listRelatives(node, shapes=True, fullPath=True) # iterate over content of reference node if cmds.nodeType(node) == "reference": ref_hashes = create_asset_id_hash( From d22e0bb6fa27b719b34efc4b14adf1919457aa10 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 16 Mar 2023 17:29:08 +0000 Subject: [PATCH 18/50] setMembers > contentMembers --- .../maya/plugins/publish/collect_arnold_scene_source.py | 4 ++-- .../maya/plugins/publish/extract_arnold_scene_source.py | 2 +- .../maya/plugins/publish/validate_arnold_scene_source.py | 6 +++--- .../plugins/publish/validate_arnold_scene_source_cbid.py | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/collect_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/collect_arnold_scene_source.py index fd4993d09e..ab15d0419f 100644 --- a/openpype/hosts/maya/plugins/publish/collect_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/collect_arnold_scene_source.py @@ -23,7 +23,7 @@ class CollectArnoldSceneSource(pyblish.api.InstancePlugin): continue if objset.endswith("content_SET"): set_members = get_all_children(cmds.ls(members, long=True)) - instance.data["setMembers"] = set_members + instance.data["contentMembers"] = set_members self.log.debug("content members: {}".format(set_members)) elif objset.endswith("proxy_SET"): set_members = get_all_children(cmds.ls(members, long=True)) @@ -35,7 +35,7 @@ class CollectArnoldSceneSource(pyblish.api.InstancePlugin): cameras = cmds.ls(type="camera", long=True) renderable = [c for c in cameras if cmds.getAttr("%s.renderable" % c)] camera = renderable[0] - for node in instance.data["setMembers"]: + for node in instance.data["contentMembers"]: camera_shapes = cmds.listRelatives( node, shapes=True, type="camera" ) diff --git a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py index 4de0f49de1..0325c2518e 100644 --- a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py @@ -72,7 +72,7 @@ class ExtractArnoldSceneSource(publish.Extractor): } filenames = self._extract( - instance.data["setMembers"], attribute_data, kwargs + instance.data["contentMembers"], attribute_data, kwargs ) if "representations" not in instance.data: diff --git a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py index e582560e12..2a7eabe285 100644 --- a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py @@ -43,7 +43,7 @@ class ValidateArnoldSceneSource(pyblish.api.InstancePlugin): ungrouped_nodes = [] nodes, content_nodes_by_name, content_parents = self._get_nodes_data( - instance.data["setMembers"] + instance.data["contentMembers"] ) ungrouped_nodes.extend(nodes) @@ -64,11 +64,11 @@ class ValidateArnoldSceneSource(pyblish.api.InstancePlugin): return # Validate for content and proxy nodes amount being the same. - if len(instance.data["setMembers"]) != len(instance.data["proxy"]): + if len(instance.data["contentMembers"]) != len(instance.data["proxy"]): raise PublishValidationError( "Amount of content nodes ({}) and proxy nodes ({}) needs to " "be the same.".format( - len(instance.data["setMembers"]), + len(instance.data["contentMembers"]), len(instance.data["proxy"]) ) ) diff --git a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py index 87c47978c8..0cc2b482e4 100644 --- a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py +++ b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py @@ -29,7 +29,7 @@ class ValidateArnoldSceneSourceCbid(pyblish.api.InstancePlugin): def get_invalid_couples(self, instance): content_nodes_by_name = self._get_nodes_data( - instance.data["setMembers"] + instance.data["contentMembers"] ) proxy_nodes_by_name = self._get_nodes_data( instance.data.get("proxy", []) From 1e3862ca7a9aacc6d8e7d27602919a48146a813e Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 16 Mar 2023 17:40:57 +0000 Subject: [PATCH 19/50] Copy cbid from duplicated nodes. --- .../publish/extract_arnold_scene_source.py | 36 ++++--------------- 1 file changed, 7 insertions(+), 29 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py index 0325c2518e..1678f97627 100644 --- a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py @@ -4,9 +4,7 @@ from maya import cmds import arnold from openpype.pipeline import publish -from openpype.hosts.maya.api.lib import ( - maintained_selection, attribute_values, delete_after -) +from openpype.hosts.maya.api import lib class ExtractArnoldSceneSource(publish.Extractor): @@ -119,7 +117,7 @@ class ExtractArnoldSceneSource(publish.Extractor): filenames = [] # Duplicating nodes so they are direct children of the world. This # makes the hierarchy of any exported ass file the same. - with delete_after() as delete_bin: + with lib.delete_after() as delete_bin: duplicate_nodes = [] for node in nodes: parent = cmds.listRelatives(node, parent=True, fullPath=True)[0] @@ -145,32 +143,12 @@ class ExtractArnoldSceneSource(publish.Extractor): duplicate_nodes.append(duplicate_transform) delete_bin.append(duplicate_transform) - # Copy cbId from original to mtoa_constant. - attr_name = "mtoa_constant_cbId" - duplicate_shapes = cmds.listRelatives( - duplicate_transform, shapes=True, fullPath=True - ) - original_shapes = cmds.listRelatives(node, shapes=True, fullPath=True) - for duplicate_shape in duplicate_shapes: - duplicate_path = ( - duplicate_transform + "|" + duplicate_shape - ) - for original_shape in original_shapes: - original_path = node + "|" + original_shape - if duplicate_shape == original_shape: - cmds.addAttr( - duplicate_path, - longName=attr_name, - dataType="string" - ) - cmds.setAttr( - duplicate_path + "." + attr_name, - cmds.getAttr(original_path + ".cbId"), - type="string" - ) + # Copy cbId to mtoa_constant. + for node in duplicate_nodes: + lib.set_attribute("mtoa_constant_cbId", lib.get_id(node)) - with attribute_values(attribute_data): - with maintained_selection(): + with lib.attribute_values(attribute_data): + with lib.maintained_selection(): self.log.info( "Writing: {}".format(duplicate_nodes) ) From caf101bd7d461e9616904c17ec19e1540d18187b Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 17 Mar 2023 07:26:57 +0000 Subject: [PATCH 20/50] rsplit instead of split --- .../maya/plugins/publish/validate_arnold_scene_source_cbid.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py index 0cc2b482e4..457c3b7d52 100644 --- a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py +++ b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py @@ -22,8 +22,8 @@ class ValidateArnoldSceneSourceCbid(pyblish.api.InstancePlugin): def _get_nodes_data(nodes): nodes_by_name = {} for node in nodes: - node_split = node.split("|") - nodes_by_name[node_split[-1].split(":")[-1]] = node + node_name = node.rsplit("|", 1)[-1].rsplit(":", 1)[-1] + nodes_by_name[node_name] = node return nodes_by_name From baf60646730e34d8d9965472b8923d71144e1d30 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 17 Mar 2023 07:39:08 +0000 Subject: [PATCH 21/50] Validate against same named nodes in different hierarchies. --- .../publish/validate_arnold_scene_source.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py index 2a7eabe285..8f443f6963 100644 --- a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py @@ -26,6 +26,7 @@ class ValidateArnoldSceneSource(pyblish.api.InstancePlugin): ungrouped_nodes = [] nodes_by_name = {} parents = [] + same_named_nodes = [] for node in nodes: node_split = node.split("|") if len(node_split) == 2: @@ -35,7 +36,21 @@ class ValidateArnoldSceneSource(pyblish.api.InstancePlugin): if parent: parents.append(parent) - nodes_by_name[node_split[-1].split(":")[-1]] = node + node_name = node.rsplit("|", 1)[-1].rsplit(":", 1)[-1] + + # Check for same same nodes, which can happen in different + # hierarchies. + if node_name in nodes_by_name: + same_named_nodes.append((node, nodes_by_name[node_name])) + + nodes_by_name[node_name] = node + + if same_named_nodes: + raise PublishValidationError( + "Found nodes with the same name:\n{}".format( + "\n".join(["{}".format(n) for n in same_named_nodes]) + ) + ) return ungrouped_nodes, nodes_by_name, parents From 1048f58db3c72b2e061f0f6091143e210d13b3c0 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 17 Mar 2023 07:43:11 +0000 Subject: [PATCH 22/50] Use set_id and get_id --- .../plugins/publish/validate_arnold_scene_source_cbid.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py index 457c3b7d52..ad174404dc 100644 --- a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py +++ b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py @@ -67,8 +67,4 @@ class ValidateArnoldSceneSourceCbid(pyblish.api.InstancePlugin): @classmethod def repair(cls, instance): for content_node, proxy_node in cls.get_invalid_couples(cls, instance): - cmds.setAttr( - proxy_node + ".cbId", - cmds.getAttr(content_node + ".cbId"), - type="string" - ) + lib.set_id(proxy_node, lib.get_id(content_node), overwrite=False) From 214b76796c70aa1bb6608bb4f8510fcaba0e015f Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 17 Mar 2023 07:47:19 +0000 Subject: [PATCH 23/50] Failsafe for mtoa not loaded and user feedback. --- .../hosts/maya/tools/mayalookassigner/app.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/maya/tools/mayalookassigner/app.py b/openpype/hosts/maya/tools/mayalookassigner/app.py index e66f0d73e2..2a8775fff6 100644 --- a/openpype/hosts/maya/tools/mayalookassigner/app.py +++ b/openpype/hosts/maya/tools/mayalookassigner/app.py @@ -251,12 +251,23 @@ class MayaLookAssignerWindow(QtWidgets.QWidget): vrayproxy_assign_look(vp, subset_name) nodes = list(set(item["nodes"]).difference(vray_proxies)) + else: + self.echo( + "Could not assign to VRayProxy because vrayformaya plugin " + "is not loaded." + ) # Assign Arnold Standin look. - arnold_standins = set(cmds.ls(type="aiStandIn", long=True)) - for standin in arnold_standins: - if standin in nodes: - arnold_standin.assign_look(standin, subset_name) + if cmds.pluginInfo("mtoa", query=True, loaded=True): + arnold_standins = set(cmds.ls(type="aiStandIn", long=True)) + for standin in arnold_standins: + if standin in nodes: + arnold_standin.assign_look(standin, subset_name) + else: + self.echo( + "Could not assign to aiStandIn because mtoa plugin is not " + "loaded." + ) nodes = list(set(item["nodes"]).difference(arnold_standins)) From 14bd9b86b91876cb3be5d1c9d713b7a92931da80 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Fri, 17 Mar 2023 07:48:17 +0000 Subject: [PATCH 24/50] Update openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py Co-authored-by: Roy Nieterau --- .../tools/mayalookassigner/arnold_standin.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py b/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py index 7d3b7e59b3..d4f93aeca6 100644 --- a/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py +++ b/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py @@ -189,16 +189,16 @@ def assign_look(standin, subset): continue shading_engine_assignments( - edit["shader"], - "surfaceShader", - edit["nodes"], - node_assignments + shading_engine=edit["shader"], + attr="surfaceShader", + nodes=edit["nodes"], + assignments=node_assignments ) shading_engine_assignments( - edit["shader"], - "displacementShader", - edit["nodes"], - node_assignments + shading_engine=edit["shader"], + attr="displacementShader", + nodes=edit["nodes"], + assignments=node_assignments ) if edit["action"] == "setattr": From 54af7a82d5142d3f52575d7e86be3a827c450404 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 17 Mar 2023 07:49:48 +0000 Subject: [PATCH 25/50] attr > attribute --- .../maya/tools/mayalookassigner/arnold_standin.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py b/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py index d4f93aeca6..4f85d15108 100644 --- a/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py +++ b/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py @@ -109,13 +109,13 @@ def get_standin_path(node): return path -def shading_engine_assignments(shading_engine, attr, nodes, assignments): +def shading_engine_assignments(shading_engine, attribute, nodes, assignments): shader_inputs = cmds.listConnections( - shading_engine + "." + attr, source=True) + shading_engine + "." + attribute, source=True) if not shader_inputs: log.info( "Shading engine \"{}\" missing input \"{}\"".format( - shading_engine, attr + shading_engine, attribute ) ) @@ -128,7 +128,7 @@ def shading_engine_assignments(shading_engine, attr, nodes, assignments): "{}").format(node)) nodes[i] = node.split(".")[0] - shader_type = "shader" if attr == "surfaceShader" else "disp_map" + shader_type = "shader" if attribute == "surfaceShader" else "disp_map" assignment = "{}='{}'".format(shader_type, shader_inputs[0]) for node in nodes: assignments[node].append(assignment) @@ -190,13 +190,13 @@ def assign_look(standin, subset): shading_engine_assignments( shading_engine=edit["shader"], - attr="surfaceShader", + attribute="surfaceShader", nodes=edit["nodes"], assignments=node_assignments ) shading_engine_assignments( shading_engine=edit["shader"], - attr="displacementShader", + attribute="displacementShader", nodes=edit["nodes"], assignments=node_assignments ) From 6d1fd474a396f64b03ef999fda2b7761274ea317 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 17 Mar 2023 07:53:24 +0000 Subject: [PATCH 26/50] Use calculate_visibility_mask --- .../tools/mayalookassigner/arnold_standin.py | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py b/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py index 4f85d15108..6d7be80060 100644 --- a/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py +++ b/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py @@ -224,22 +224,7 @@ def assign_look(standin, subset): node_assignments[node].append(assignment) if visibility: - # https://arnoldsupport.com/2018/11/21/backdoor-setting-visibility/ - mapping = { - "primaryVisibility": 1, # Camera - "castsShadows": 2, # Shadow - "aiVisibleInDiffuseTransmission": 4, - "aiVisibleInSpecularTransmission": 8, - "aiVisibleInVolume": 16, - "aiVisibleInDiffuseReflection": 32, - "aiVisibleInSpecularReflection": 64 - } - mask = 255 - for attr, value in mapping.items(): - if edit["attributes"].get(attr, True): - continue - mask -= value - + mask = calculate_visibility_mask(edit["attributes"]) assignment = "visibility={}".format(mask) for node in edit["nodes"]: From e420883189ad06b8bcc76107fab0399e3e1f8465 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 17 Mar 2023 07:55:20 +0000 Subject: [PATCH 27/50] Hound --- .../maya/plugins/publish/extract_arnold_scene_source.py | 8 ++++++-- .../plugins/publish/validate_arnold_scene_source_cbid.py | 9 ++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py index 1678f97627..c2523d4d12 100644 --- a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py @@ -120,7 +120,9 @@ class ExtractArnoldSceneSource(publish.Extractor): with lib.delete_after() as delete_bin: duplicate_nodes = [] for node in nodes: - parent = cmds.listRelatives(node, parent=True, fullPath=True)[0] + parent = cmds.listRelatives( + node, parent=True, fullPath=True + )[0] duplicate_transform = cmds.duplicate(node)[0] duplicate_transform = "{}|{}".format( parent, duplicate_transform @@ -138,7 +140,9 @@ class ExtractArnoldSceneSource(publish.Extractor): )[0] basename = node.split("|")[-1].split(":")[-1] - duplicate_transform = cmds.rename(duplicate_transform, basename) + duplicate_transform = cmds.rename( + duplicate_transform, basename + ) duplicate_nodes.append(duplicate_transform) delete_bin.append(duplicate_transform) diff --git a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py index ad174404dc..dafad2f40a 100644 --- a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py +++ b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py @@ -1,6 +1,5 @@ -import maya.cmds as cmds - import pyblish.api +from openpype.hosts.maya.api import lib from openpype.pipeline.publish import ( ValidateContentsOrder, PublishValidationError, RepairAction ) @@ -40,7 +39,11 @@ class ValidateArnoldSceneSourceCbid(pyblish.api.InstancePlugin): proxy_node = proxy_nodes_by_name.get(content_name, None) if not proxy_node: - self.log.debug("Content node '{}' has no matching proxy node.".format(content_node)) + self.log.debug( + "Content node '{}' has no matching proxy node.".format( + content_node + ) + ) continue content_id = lib.get_id(content_node) From 0e8f251c5cbb58f4f67e18f8af8d9a7f06d8c0d6 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 17 Mar 2023 07:55:27 +0000 Subject: [PATCH 28/50] Hound --- .../hosts/maya/plugins/publish/validate_arnold_scene_source.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py index 8f443f6963..d09a8610c4 100644 --- a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py @@ -1,5 +1,3 @@ -import maya.cmds as cmds - import pyblish.api from openpype.pipeline.publish import ( ValidateContentsOrder, PublishValidationError From 7cd7b2daf97f8f9776888f40d7123db84a006377 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 17 Mar 2023 09:03:35 +0000 Subject: [PATCH 29/50] Fix extractor --- .../publish/extract_arnold_scene_source.py | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py index c2523d4d12..7348c2db4d 100644 --- a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py @@ -120,6 +120,10 @@ class ExtractArnoldSceneSource(publish.Extractor): with lib.delete_after() as delete_bin: duplicate_nodes = [] for node in nodes: + # Only interested in transforms: + if cmds.nodeType(node) != "transform": + continue + parent = cmds.listRelatives( node, parent=True, fullPath=True )[0] @@ -128,13 +132,6 @@ class ExtractArnoldSceneSource(publish.Extractor): parent, duplicate_transform ) - # Discard the children. - shapes = cmds.listRelatives(duplicate_transform, shapes=True) - children = cmds.listRelatives( - duplicate_transform, children=True - ) - cmds.delete(set(children) - set(shapes)) - duplicate_transform = cmds.parent( duplicate_transform, world=True )[0] @@ -144,12 +141,22 @@ class ExtractArnoldSceneSource(publish.Extractor): duplicate_transform, basename ) + # Discard the children. + shapes = cmds.listRelatives( + duplicate_transform, shapes=True, fullPath=True + ) + children = cmds.listRelatives( + duplicate_transform, children=True, fullPath=True + ) + cmds.delete(set(children) - set(shapes)) + duplicate_nodes.append(duplicate_transform) + duplicate_nodes.extend(shapes) delete_bin.append(duplicate_transform) # Copy cbId to mtoa_constant. for node in duplicate_nodes: - lib.set_attribute("mtoa_constant_cbId", lib.get_id(node)) + lib.set_attribute("mtoa_constant_cbId", lib.get_id(node), node) with lib.attribute_values(attribute_data): with lib.maintained_selection(): From b63b02687011d1771f6165754e280e062e0ff763 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 17 Mar 2023 15:38:44 +0000 Subject: [PATCH 30/50] Filter to transforms with shapes only. --- .../maya/plugins/publish/extract_arnold_scene_source.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py index 7348c2db4d..63e6ff0f36 100644 --- a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py @@ -124,6 +124,13 @@ class ExtractArnoldSceneSource(publish.Extractor): if cmds.nodeType(node) != "transform": continue + # Only interested in transforms with shapes. + shapes = cmds.listRelatives( + node, shapes=True, fullPath=True + ) or [] + if not shapes: + continue + parent = cmds.listRelatives( node, parent=True, fullPath=True )[0] From 81c6b91633de5b9973ac70a8b515636a5b532a17 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Mon, 20 Mar 2023 17:24:03 +0000 Subject: [PATCH 31/50] Update openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py Co-authored-by: Roy Nieterau --- .../maya/plugins/publish/extract_arnold_scene_source.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py index 63e6ff0f36..f7d059cdb6 100644 --- a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py @@ -139,9 +139,11 @@ class ExtractArnoldSceneSource(publish.Extractor): parent, duplicate_transform ) - duplicate_transform = cmds.parent( - duplicate_transform, world=True - )[0] + + if cmds.listRelatives(duplicate_transform, parent=True): + duplicate_transform = cmds.parent( + duplicate_transform, world=True + )[0] basename = node.split("|")[-1].split(":")[-1] duplicate_transform = cmds.rename( From b22fbf58dc4d93a21c98507bfe9fb2a60a5b8a75 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Mon, 20 Mar 2023 17:34:13 +0000 Subject: [PATCH 32/50] Update openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py Co-authored-by: Roy Nieterau --- .../hosts/maya/plugins/publish/extract_arnold_scene_source.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py index f7d059cdb6..225b8dbb28 100644 --- a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py @@ -150,7 +150,7 @@ class ExtractArnoldSceneSource(publish.Extractor): duplicate_transform, basename ) - # Discard the children. + # Discard children nodes that are not shapes shapes = cmds.listRelatives( duplicate_transform, shapes=True, fullPath=True ) From c2c963c703900e5587828cb756e83a28da940ed9 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 20 Mar 2023 17:49:26 +0000 Subject: [PATCH 33/50] Improve same named nodes error message. --- .../publish/validate_arnold_scene_source.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py index d09a8610c4..a0e2e84a00 100644 --- a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py @@ -24,7 +24,7 @@ class ValidateArnoldSceneSource(pyblish.api.InstancePlugin): ungrouped_nodes = [] nodes_by_name = {} parents = [] - same_named_nodes = [] + same_named_nodes = {} for node in nodes: node_split = node.split("|") if len(node_split) == 2: @@ -39,16 +39,21 @@ class ValidateArnoldSceneSource(pyblish.api.InstancePlugin): # Check for same same nodes, which can happen in different # hierarchies. if node_name in nodes_by_name: - same_named_nodes.append((node, nodes_by_name[node_name])) + try: + same_named_nodes[node_name].append(node) + except KeyError: + same_named_nodes[node_name] = [ + nodes_by_name[node_name], node + ] nodes_by_name[node_name] = node if same_named_nodes: - raise PublishValidationError( - "Found nodes with the same name:\n{}".format( - "\n".join(["{}".format(n) for n in same_named_nodes]) - ) - ) + message = "Found nodes with the same name:" + for name, nodes in same_named_nodes.items(): + message += "\n\n\"{}\":\n{}".format(name, "\n".join(nodes)) + + raise PublishValidationError(message) return ungrouped_nodes, nodes_by_name, parents From 5511b479ab84fd5dbeba822f213fa1a117cd8c10 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 20 Mar 2023 17:50:40 +0000 Subject: [PATCH 34/50] _get_nodes_data > _get_nodes_by_name --- .../maya/plugins/publish/validate_arnold_scene_source.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py index a0e2e84a00..7055dc145e 100644 --- a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py @@ -20,7 +20,7 @@ class ValidateArnoldSceneSource(pyblish.api.InstancePlugin): families = ["ass"] label = "Validate Arnold Scene Source" - def _get_nodes_data(self, nodes): + def _get_nodes_by_name(self, nodes): ungrouped_nodes = [] nodes_by_name = {} parents = [] @@ -60,12 +60,12 @@ class ValidateArnoldSceneSource(pyblish.api.InstancePlugin): def process(self, instance): ungrouped_nodes = [] - nodes, content_nodes_by_name, content_parents = self._get_nodes_data( - instance.data["contentMembers"] + nodes, content_nodes_by_name, content_parents = ( + self._get_nodes_by_name(instance.data["contentMembers"]) ) ungrouped_nodes.extend(nodes) - nodes, proxy_nodes_by_name, proxy_parents = self._get_nodes_data( + nodes, proxy_nodes_by_name, proxy_parents = self._get_nodes_by_name( instance.data.get("proxy", []) ) ungrouped_nodes.extend(nodes) From cd5493edea38ddf4680c137b0a07ab6eca389b93 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 20 Mar 2023 17:54:15 +0000 Subject: [PATCH 35/50] _get_nodes_data > _get_nodes_by_name --- .../plugins/publish/validate_arnold_scene_source_cbid.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py index dafad2f40a..6a4799f73f 100644 --- a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py +++ b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py @@ -18,7 +18,7 @@ class ValidateArnoldSceneSourceCbid(pyblish.api.InstancePlugin): actions = [RepairAction] @staticmethod - def _get_nodes_data(nodes): + def _get_nodes_by_name(nodes): nodes_by_name = {} for node in nodes: node_name = node.rsplit("|", 1)[-1].rsplit(":", 1)[-1] @@ -27,10 +27,10 @@ class ValidateArnoldSceneSourceCbid(pyblish.api.InstancePlugin): return nodes_by_name def get_invalid_couples(self, instance): - content_nodes_by_name = self._get_nodes_data( + content_nodes_by_name = self._get_nodes_by_name( instance.data["contentMembers"] ) - proxy_nodes_by_name = self._get_nodes_data( + proxy_nodes_by_name = self._get_nodes_by_name( instance.data.get("proxy", []) ) From 12c85bc46cdd4e5621c3305a01e608ea9dc0e7a9 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 20 Mar 2023 17:55:09 +0000 Subject: [PATCH 36/50] Make get_invalid_couples class method --- .../plugins/publish/validate_arnold_scene_source_cbid.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py index 6a4799f73f..e27723e104 100644 --- a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py +++ b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source_cbid.py @@ -26,11 +26,12 @@ class ValidateArnoldSceneSourceCbid(pyblish.api.InstancePlugin): return nodes_by_name - def get_invalid_couples(self, instance): - content_nodes_by_name = self._get_nodes_by_name( + @classmethod + def get_invalid_couples(cls, instance): + content_nodes_by_name = cls._get_nodes_by_name( instance.data["contentMembers"] ) - proxy_nodes_by_name = self._get_nodes_by_name( + proxy_nodes_by_name = cls._get_nodes_by_name( instance.data.get("proxy", []) ) @@ -39,7 +40,7 @@ class ValidateArnoldSceneSourceCbid(pyblish.api.InstancePlugin): proxy_node = proxy_nodes_by_name.get(content_name, None) if not proxy_node: - self.log.debug( + cls.log.debug( "Content node '{}' has no matching proxy node.".format( content_node ) From b7e87dc1e193c056750c65900b21fb8093da1da9 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 20 Mar 2023 18:04:32 +0000 Subject: [PATCH 37/50] Hound --- .../hosts/maya/plugins/publish/extract_arnold_scene_source.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py index 225b8dbb28..8344f02894 100644 --- a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py @@ -139,7 +139,6 @@ class ExtractArnoldSceneSource(publish.Extractor): parent, duplicate_transform ) - if cmds.listRelatives(duplicate_transform, parent=True): duplicate_transform = cmds.parent( duplicate_transform, world=True From 2d92deae1694fb7761032518b672b647890d5b5e Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 29 Mar 2023 11:31:49 +0100 Subject: [PATCH 38/50] Fixes --- openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py b/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py index 6d7be80060..449eacb40f 100644 --- a/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py +++ b/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py @@ -118,6 +118,7 @@ def shading_engine_assignments(shading_engine, attribute, nodes, assignments): shading_engine, attribute ) ) + return # Strip off component assignments for i, node in enumerate(nodes): @@ -261,4 +262,4 @@ def assign_look(standin, subset): index += 1 - cmds.sets(operator, edit=True, addElement=container_node[0]) + cmds.sets(operator, edit=True, addElement=container_node) From 57bbf946fda65a0e0b6ce3fce04de0f0f8dabf16 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 29 Mar 2023 11:31:55 +0100 Subject: [PATCH 39/50] Code cosmetics --- .../hosts/maya/tools/mayalookassigner/arnold_standin.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py b/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py index 449eacb40f..dfffbc5961 100644 --- a/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py +++ b/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py @@ -111,7 +111,8 @@ def get_standin_path(node): def shading_engine_assignments(shading_engine, attribute, nodes, assignments): shader_inputs = cmds.listConnections( - shading_engine + "." + attribute, source=True) + shading_engine + "." + attribute, source=True + ) if not shader_inputs: log.info( "Shading engine \"{}\" missing input \"{}\"".format( @@ -124,9 +125,9 @@ def shading_engine_assignments(shading_engine, attribute, nodes, assignments): for i, node in enumerate(nodes): if "." in node: log.warning( - ("Converting face assignment to full object " - "assignment. This conversion can be lossy: " - "{}").format(node)) + "Converting face assignment to full object assignment. This " + "conversion can be lossy: {}".format(node) + ) nodes[i] = node.split(".")[0] shader_type = "shader" if attribute == "surfaceShader" else "disp_map" From 73b369a32d26e2c01d3140b9074d1fd83402a772 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Wed, 29 Mar 2023 15:37:36 +0100 Subject: [PATCH 40/50] Update openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py Co-authored-by: Roy Nieterau --- .../hosts/maya/plugins/publish/extract_arnold_scene_source.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py index 8344f02894..ece53edc23 100644 --- a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py @@ -144,7 +144,7 @@ class ExtractArnoldSceneSource(publish.Extractor): duplicate_transform, world=True )[0] - basename = node.split("|")[-1].split(":")[-1] + basename = node.rsplit("|", 1)[-1].rsplit(":", 1)[-1] duplicate_transform = cmds.rename( duplicate_transform, basename ) From dea30d2f689a330488c9a5e0a91a7a2d769dbf89 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 29 Mar 2023 16:06:27 +0100 Subject: [PATCH 41/50] BigRoy feedback --- .../publish/collect_arnold_scene_source.py | 7 ++++--- .../publish/extract_arnold_scene_source.py | 8 +------- .../tools/mayalookassigner/arnold_standin.py | 18 ++++++++---------- .../maya/tools/mayalookassigner/commands.py | 12 +++++------- .../tools/mayalookassigner/vray_proxies.py | 8 +++++--- 5 files changed, 23 insertions(+), 30 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/collect_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/collect_arnold_scene_source.py index ab15d0419f..0845f653b1 100644 --- a/openpype/hosts/maya/plugins/publish/collect_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/collect_arnold_scene_source.py @@ -22,9 +22,10 @@ class CollectArnoldSceneSource(pyblish.api.InstancePlugin): self.log.warning("Skipped empty instance: \"%s\" " % objset) continue if objset.endswith("content_SET"): - set_members = get_all_children(cmds.ls(members, long=True)) - instance.data["contentMembers"] = set_members - self.log.debug("content members: {}".format(set_members)) + members = cmds.ls(members, long=True) + children = get_all_children(members) + instance.data["contentMembers"] = children + self.log.debug("content members: {}".format(children)) elif objset.endswith("proxy_SET"): set_members = get_all_children(cmds.ls(members, long=True)) instance.data["proxy"] = set_members diff --git a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py index 8344f02894..ce5dc27bbd 100644 --- a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py @@ -126,18 +126,12 @@ class ExtractArnoldSceneSource(publish.Extractor): # Only interested in transforms with shapes. shapes = cmds.listRelatives( - node, shapes=True, fullPath=True + node, shapes=True, noIntermediate=True ) or [] if not shapes: continue - parent = cmds.listRelatives( - node, parent=True, fullPath=True - )[0] duplicate_transform = cmds.duplicate(node)[0] - duplicate_transform = "{}|{}".format( - parent, duplicate_transform - ) if cmds.listRelatives(duplicate_transform, parent=True): duplicate_transform = cmds.parent( diff --git a/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py b/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py index dfffbc5961..771b256614 100644 --- a/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py +++ b/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py @@ -58,17 +58,17 @@ def calculate_visibility_mask(attributes): return mask -def get_cbid_by_node(path): - """Get cbid from Arnold Scene Source. +def get_id_by_node(path): + """Get node id from Arnold Scene Source. Args: path (string): Path to Arnold Scene Source. Returns: - (dict): Dictionary with node full name/path and CBID. + (dict): Dictionary with node full name/path and id. """ import arnold - results = {} + results = defaultdict(list) arnold.AiBegin() @@ -82,10 +82,7 @@ def get_cbid_by_node(path): node = arnold.AiNodeIteratorGetNext(iter) if arnold.AiNodeIs(node, "polymesh"): node_name = arnold.AiNodeGetName(node) - try: - results[arnold.AiNodeGetStr(node, "cbId")].append(node_name) - except KeyError: - results[arnold.AiNodeGetStr(node, "cbId")] = [node_name] + results[arnold.AiNodeGetStr(node, "cbId")].append(node_name) arnold.AiNodeIteratorDestroy(iter) arnold.AiEnd() @@ -139,7 +136,7 @@ def shading_engine_assignments(shading_engine, attribute, nodes, assignments): def assign_look(standin, subset): log.info("Assigning {} to {}.".format(subset, standin)) - nodes_by_id = get_cbid_by_node(get_standin_path(standin)) + nodes_by_id = get_id_by_node(get_standin_path(standin)) # Group by asset id so we run over the look per asset node_ids_by_asset_id = defaultdict(set) @@ -164,7 +161,8 @@ def assign_look(standin, subset): continue relationships = lib.get_look_relationships(version["_id"]) - shader_nodes, container_node = lib.load_look(version["_id"]) + shader_nodes, container_nodes = lib.load_look(version["_id"]) + container_node = container_nodes[0] namespace = shader_nodes[0].split(":")[0] # Get only the node ids and paths related to this asset diff --git a/openpype/hosts/maya/tools/mayalookassigner/commands.py b/openpype/hosts/maya/tools/mayalookassigner/commands.py index d7061f12e1..d78e31111d 100644 --- a/openpype/hosts/maya/tools/mayalookassigner/commands.py +++ b/openpype/hosts/maya/tools/mayalookassigner/commands.py @@ -121,15 +121,13 @@ def create_asset_id_hash(nodes): path = cmds.getAttr("{}.fileName".format(node)) ids = get_alembic_ids_cache(path) for k, _ in ids.items(): - pid = k.split(":")[0] - if node not in node_id_hash[pid]: - node_id_hash[pid].append(node) + id = k.split(":")[0] + node_id_hash[id].append(node) elif shapes and cmds.nodeType(shapes[0]) == "aiStandIn": path = arnold_standin.get_standin_path(shapes[0]) - for id, _ in arnold_standin.get_cbid_by_node(path).items(): - pid = id.split(":")[0] - if shapes[0] not in node_id_hash[pid]: - node_id_hash[pid].append(shapes[0]) + for id, _ in arnold_standin.get_id_by_node(path).items(): + id = id.split(":")[0] + node_id_hash[id].append(shapes[0]) else: value = lib.get_id(node) if value is None: diff --git a/openpype/hosts/maya/tools/mayalookassigner/vray_proxies.py b/openpype/hosts/maya/tools/mayalookassigner/vray_proxies.py index 6ee618f37a..1d2ec5fd87 100644 --- a/openpype/hosts/maya/tools/mayalookassigner/vray_proxies.py +++ b/openpype/hosts/maya/tools/mayalookassigner/vray_proxies.py @@ -11,7 +11,7 @@ from maya import cmds from openpype.client import get_last_version_by_subset_name from openpype.pipeline import legacy_io -from openpype.hosts.maya import api +import openpype.hosts.maya.lib as maya_lib from . import lib @@ -189,8 +189,10 @@ def vrayproxy_assign_look(vrayproxy, subset="lookDefault"): node_id: nodes_by_id[node_id] for node_id in node_ids } edits = list( - api.lib.iter_shader_edits( - relationships, shadernodes, asset_nodes_by_id)) + maya_lib.iter_shader_edits( + relationships, shadernodes, asset_nodes_by_id + ) + ) # Create assignments assignments = {} From 0d8a8af3711d6e696778ef8af611859886beebd6 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 29 Mar 2023 17:26:25 +0100 Subject: [PATCH 42/50] Use lib.get_all_children --- .../maya/tools/mayalookassigner/commands.py | 24 +------------------ 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/openpype/hosts/maya/tools/mayalookassigner/commands.py b/openpype/hosts/maya/tools/mayalookassigner/commands.py index d78e31111d..3fd367e860 100644 --- a/openpype/hosts/maya/tools/mayalookassigner/commands.py +++ b/openpype/hosts/maya/tools/mayalookassigner/commands.py @@ -45,33 +45,11 @@ def get_namespace_from_node(node): return parts[0] if len(parts) > 1 else u":" -def list_descendents(nodes): - """Include full descendant hierarchy of given nodes. - - This is a workaround to cmds.listRelatives(allDescendents=True) because - this way correctly keeps children instance paths (see Maya documentation) - - This fixes LKD-26: assignments not working as expected on instanced shapes. - - Return: - list: List of children descendents of nodes - - """ - result = [] - while True: - nodes = cmds.listRelatives(nodes, - fullPath=True) - if nodes: - result.extend(nodes) - else: - return result - - def get_selected_nodes(): """Get information from current selection""" selection = cmds.ls(selection=True, long=True) - hierarchy = list_descendents(selection) + hierarchy = lib.get_all_children(selection) return list(set(selection + hierarchy)) From 0d7b42957f56e637a32ee489cd56588ddc7f7627 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 30 Mar 2023 07:08:24 +0100 Subject: [PATCH 43/50] Add shape to container when loading. --- openpype/hosts/maya/plugins/load/load_arnold_standin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/load/load_arnold_standin.py b/openpype/hosts/maya/plugins/load/load_arnold_standin.py index 3097ba21aa..a527844f7b 100644 --- a/openpype/hosts/maya/plugins/load/load_arnold_standin.py +++ b/openpype/hosts/maya/plugins/load/load_arnold_standin.py @@ -84,7 +84,7 @@ class ArnoldStandinLoader(load.LoaderPlugin): sequence = is_sequence(os.listdir(os.path.dirname(self.fname))) cmds.setAttr(standin_shape + ".useFrameExtension", sequence) - nodes = [root, standin] + nodes = [root, standin, standin_shape] if operator is not None: nodes.append(operator) self[:] = nodes From 4889ffdd0b4f1f62ffb87418d5b903064e013bb2 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 30 Mar 2023 07:08:44 +0100 Subject: [PATCH 44/50] Dont query shapes when creating id hashes. --- openpype/hosts/maya/tools/mayalookassigner/commands.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/maya/tools/mayalookassigner/commands.py b/openpype/hosts/maya/tools/mayalookassigner/commands.py index 3fd367e860..a71cb361a4 100644 --- a/openpype/hosts/maya/tools/mayalookassigner/commands.py +++ b/openpype/hosts/maya/tools/mayalookassigner/commands.py @@ -86,7 +86,6 @@ def create_asset_id_hash(nodes): """ node_id_hash = defaultdict(list) for node in nodes: - shapes = cmds.listRelatives(node, shapes=True, fullPath=True) # iterate over content of reference node if cmds.nodeType(node) == "reference": ref_hashes = create_asset_id_hash( @@ -101,11 +100,11 @@ def create_asset_id_hash(nodes): for k, _ in ids.items(): id = k.split(":")[0] node_id_hash[id].append(node) - elif shapes and cmds.nodeType(shapes[0]) == "aiStandIn": - path = arnold_standin.get_standin_path(shapes[0]) + elif cmds.nodeType(node) == "aiStandIn": + path = arnold_standin.get_standin_path(node) for id, _ in arnold_standin.get_id_by_node(path).items(): id = id.split(":")[0] - node_id_hash[id].append(shapes[0]) + node_id_hash[id].append(node) else: value = lib.get_id(node) if value is None: From 10fdeb5243eafd09badd0d7ad868ee13bc8c0d99 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 31 Mar 2023 11:23:12 +0100 Subject: [PATCH 45/50] Use json sidecar instead of querying ass file for IDs. --- .../publish/extract_arnold_scene_source.py | 27 +++++++-- .../tools/mayalookassigner/arnold_standin.py | 57 +++++-------------- .../maya/tools/mayalookassigner/commands.py | 3 +- .../hosts/maya/tools/mayalookassigner/lib.py | 2 +- 4 files changed, 37 insertions(+), 52 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py index e12268282c..2ac9d4c6a2 100644 --- a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py @@ -1,4 +1,6 @@ import os +from collections import defaultdict +import json from maya import cmds import arnold @@ -17,8 +19,7 @@ class ExtractArnoldSceneSource(publish.Extractor): def process(self, instance): staging_dir = self.staging_dir(instance) - filename = "{}.ass".format(instance.name) - file_path = os.path.join(staging_dir, filename) + file_path = os.path.join(staging_dir, "{}.ass".format(instance.name)) # Mask mask = arnold.AI_NODE_ALL @@ -69,7 +70,7 @@ class ExtractArnoldSceneSource(publish.Extractor): "mask": mask } - filenames = self._extract( + filenames, nodes_by_id = self._extract( instance.data["contentMembers"], attribute_data, kwargs ) @@ -86,6 +87,19 @@ class ExtractArnoldSceneSource(publish.Extractor): instance.data["representations"].append(representation) + json_path = os.path.join(staging_dir, "{}.json".format(instance.name)) + with open(json_path, "w") as f: + json.dump(nodes_by_id, f) + + representation = { + "name": "json", + "ext": "json", + "files": os.path.basename(json_path), + "stagingDir": staging_dir + } + + instance.data["representations"].append(representation) + self.log.info( "Extracted instance {} to: {}".format(instance.name, staging_dir) ) @@ -95,7 +109,7 @@ class ExtractArnoldSceneSource(publish.Extractor): return kwargs["filename"] = file_path.replace(".ass", "_proxy.ass") - filenames = self._extract( + filenames, _ = self._extract( instance.data["proxy"], attribute_data, kwargs ) @@ -115,6 +129,7 @@ class ExtractArnoldSceneSource(publish.Extractor): "Writing {} with:\n{}".format(kwargs["filename"], kwargs) ) filenames = [] + nodes_by_id = defaultdict(list) # Duplicating nodes so they are direct children of the world. This # makes the hierarchy of any exported ass file the same. with lib.delete_after() as delete_bin: @@ -158,7 +173,7 @@ class ExtractArnoldSceneSource(publish.Extractor): # Copy cbId to mtoa_constant. for node in duplicate_nodes: - lib.set_attribute("mtoa_constant_cbId", lib.get_id(node), node) + nodes_by_id[lib.get_id(node)].append(node.replace("|", "/")) with lib.attribute_values(attribute_data): with lib.maintained_selection(): @@ -178,4 +193,4 @@ class ExtractArnoldSceneSource(publish.Extractor): self.log.info("Exported: {}".format(filenames)) - return filenames + return filenames, nodes_by_id diff --git a/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py b/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py index 771b256614..08e7746eda 100644 --- a/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py +++ b/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py @@ -1,5 +1,5 @@ import os -import re +import json from collections import defaultdict import logging @@ -58,52 +58,24 @@ def calculate_visibility_mask(attributes): return mask -def get_id_by_node(path): - """Get node id from Arnold Scene Source. +def get_nodes_by_id(standin): + """Get node id from aiStandIn via json sidecar. Args: - path (string): Path to Arnold Scene Source. + standin (string): aiStandIn node. Returns: (dict): Dictionary with node full name/path and id. """ - import arnold - results = defaultdict(list) + path = cmds.getAttr(standin + ".dso") + json_path = None + for f in os.listdir(os.path.dirname(path)): + if f.endswith(".json"): + json_path = os.path.join(os.path.dirname(path), f) + break - arnold.AiBegin() - - arnold.AiMsgSetConsoleFlags(arnold.AI_LOG_ALL) - - arnold.AiSceneLoad(None, path, None) - - # Iterate over all shader nodes - iter = arnold.AiUniverseGetNodeIterator(arnold.AI_NODE_SHAPE) - while not arnold.AiNodeIteratorFinished(iter): - node = arnold.AiNodeIteratorGetNext(iter) - if arnold.AiNodeIs(node, "polymesh"): - node_name = arnold.AiNodeGetName(node) - results[arnold.AiNodeGetStr(node, "cbId")].append(node_name) - - arnold.AiNodeIteratorDestroy(iter) - arnold.AiEnd() - - return results - - -def get_standin_path(node): - path = cmds.getAttr(node + ".dso") - - # Account for frame extension. - basename = os.path.basename(path) - current_frame = 1 - pattern = "(#+)" - matches = re.findall(pattern, basename) - if matches: - substring = "%{}d".format(str(len(matches[0])).zfill(2)) - path = path.replace(matches[0], substring) - path = path % current_frame - - return path + with open(json_path, "r") as f: + return json.load(f) def shading_engine_assignments(shading_engine, attribute, nodes, assignments): @@ -136,7 +108,7 @@ def shading_engine_assignments(shading_engine, attribute, nodes, assignments): def assign_look(standin, subset): log.info("Assigning {} to {}.".format(subset, standin)) - nodes_by_id = get_id_by_node(get_standin_path(standin)) + nodes_by_id = get_nodes_by_id(standin) # Group by asset id so we run over the look per asset node_ids_by_asset_id = defaultdict(set) @@ -161,8 +133,7 @@ def assign_look(standin, subset): continue relationships = lib.get_look_relationships(version["_id"]) - shader_nodes, container_nodes = lib.load_look(version["_id"]) - container_node = container_nodes[0] + shader_nodes, container_node = lib.load_look(version["_id"]) namespace = shader_nodes[0].split(":")[0] # Get only the node ids and paths related to this asset diff --git a/openpype/hosts/maya/tools/mayalookassigner/commands.py b/openpype/hosts/maya/tools/mayalookassigner/commands.py index 3e070c2efe..c5e6c973cf 100644 --- a/openpype/hosts/maya/tools/mayalookassigner/commands.py +++ b/openpype/hosts/maya/tools/mayalookassigner/commands.py @@ -87,8 +87,7 @@ def create_asset_id_hash(nodes): id = k.split(":")[0] node_id_hash[id].append(node) elif cmds.nodeType(node) == "aiStandIn": - path = arnold_standin.get_standin_path(node) - for id, _ in arnold_standin.get_id_by_node(path).items(): + for id, _ in arnold_standin.get_nodes_by_id(node).items(): id = id.split(":")[0] node_id_hash[id].append(node) else: diff --git a/openpype/hosts/maya/tools/mayalookassigner/lib.py b/openpype/hosts/maya/tools/mayalookassigner/lib.py index 5594c53c33..fddaf6112d 100644 --- a/openpype/hosts/maya/tools/mayalookassigner/lib.py +++ b/openpype/hosts/maya/tools/mayalookassigner/lib.py @@ -82,6 +82,6 @@ def load_look(version_id): # Reference the look file with lib.maintained_selection(): - container_node = load_container(loader, look_representation) + container_node = load_container(loader, look_representation)[0] return lib.get_container_members(container_node), container_node From 26d1c8df556facf9c03b5bc5eef9b5fb3f6a05b8 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Fri, 31 Mar 2023 17:25:19 +0100 Subject: [PATCH 46/50] Update openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py Co-authored-by: Roy Nieterau --- .../hosts/maya/plugins/publish/extract_arnold_scene_source.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py index 2ac9d4c6a2..83295683db 100644 --- a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py @@ -142,7 +142,7 @@ class ExtractArnoldSceneSource(publish.Extractor): # Only interested in transforms with shapes. shapes = cmds.listRelatives( node, shapes=True, noIntermediate=True - ) or [] + ) if not shapes: continue From 90113168cfc129953ab3924d0bd91fbc7d646130 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 31 Mar 2023 17:53:32 +0100 Subject: [PATCH 47/50] Doc string for shading_engine_assignments --- .../hosts/maya/tools/mayalookassigner/arnold_standin.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py b/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py index 08e7746eda..64fab2fa27 100644 --- a/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py +++ b/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py @@ -79,6 +79,14 @@ def get_nodes_by_id(standin): def shading_engine_assignments(shading_engine, attribute, nodes, assignments): + """Full assignments with shader or disp_map. + + Args: + shading_engine (string): Shading engine for material. + attribute (string): "surfaceShader" or "displacementShader" + nodes: (list): Nodes paths relative to aiStandIn. + assignments (dict): Assignments by nodes. + """ shader_inputs = cmds.listConnections( shading_engine + "." + attribute, source=True ) From 89f9003c91dd324c58857b124bd2237ef956203f Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Fri, 31 Mar 2023 19:31:54 +0100 Subject: [PATCH 48/50] Update openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py --- .../hosts/maya/plugins/publish/extract_arnold_scene_source.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py index 83295683db..7f063f1558 100644 --- a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py @@ -173,6 +173,7 @@ class ExtractArnoldSceneSource(publish.Extractor): # Copy cbId to mtoa_constant. for node in duplicate_nodes: + # Converting Maya hierarchy separator "|" to Arnold separator "/". nodes_by_id[lib.get_id(node)].append(node.replace("|", "/")) with lib.attribute_values(attribute_data): From e244778b3a399645b60f82f20ac48010467c6309 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 31 Mar 2023 19:41:22 +0100 Subject: [PATCH 49/50] Log --- openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py b/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py index 64fab2fa27..7eeeb72553 100644 --- a/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py +++ b/openpype/hosts/maya/tools/mayalookassigner/arnold_standin.py @@ -74,6 +74,10 @@ def get_nodes_by_id(standin): json_path = os.path.join(os.path.dirname(path), f) break + if not json_path: + log.warning("Could not find json file for {}.".format(standin)) + return {} + with open(json_path, "r") as f: return json.load(f) From b17f5218fe7c02885e8a163fe42ed7c2c031f815 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 31 Mar 2023 19:42:50 +0100 Subject: [PATCH 50/50] Hound --- .../hosts/maya/plugins/publish/extract_arnold_scene_source.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py index 7f063f1558..14bcc71da6 100644 --- a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py @@ -173,7 +173,8 @@ class ExtractArnoldSceneSource(publish.Extractor): # Copy cbId to mtoa_constant. for node in duplicate_nodes: - # Converting Maya hierarchy separator "|" to Arnold separator "/". + # Converting Maya hierarchy separator "|" to Arnold + # separator "/". nodes_by_id[lib.get_id(node)].append(node.replace("|", "/")) with lib.attribute_values(attribute_data):