From 893a84526c8308c12cdbc7f915470735a1d8faa0 Mon Sep 17 00:00:00 2001 From: Bo Zhou Date: Fri, 18 Mar 2022 19:15:07 +0900 Subject: [PATCH 01/27] add new render product for 3delight --- openpype/hosts/maya/api/lib_renderproducts.py | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/openpype/hosts/maya/api/lib_renderproducts.py b/openpype/hosts/maya/api/lib_renderproducts.py index 0c34998874..69c4eae18e 100644 --- a/openpype/hosts/maya/api/lib_renderproducts.py +++ b/openpype/hosts/maya/api/lib_renderproducts.py @@ -1107,6 +1107,81 @@ class RenderProductsRenderman(ARenderProducts): return new_files +class RenderProducts3Delight(ARenderProducts): + """Expected files for Renderman renderer. + + Warning: + This is very rudimentary and needs more love and testing. + """ + + renderer = "_3delight" + + def get_render_products(self): + """Get all AOVs. + + See Also: + :func:`ARenderProducts.get_render_products()` + + """ + cameras = [ + self.sanitize_camera_name(c) + for c in self.get_renderable_cameras() + ] + + if not cameras: + cameras = [ + self.sanitize_camera_name( + self.get_renderable_cameras()[0]) + ] + products = [] + + default_ext = "exr" + + nodes = cmds.listConnections('dlRenderGlobals1') + assert len(nodes) == 1 + node = nodes[0] + + num_layers = cmds.getAttr( + '{}.layerOutput'.format(node), + size=True) + for i in range(num_layers): + output = cmds.getAttr( + '{}.layerOutput[{}]'.format(node, i)) + if not output: + continue + + output_var = cmds.getAttr( + '{}.layerOutputVariables[{}]'.format(node, i)) + output_var_tokens = layerOutputVariable.split('|') + name = output_var_tokens[4] + + for camera in cameras: + product = RenderProduct(productName=name, + ext=default_ext, + camera=camera) + products.append(product) + + return products + + def get_files(self, product, camera): + """Get expected files. + + See Also: + :func:`ARenderProducts.get_files()` + """ + files = super(RenderProducts3Delight, self).get_files(product, camera) + + layer_data = self.layer_data + new_files = [] + for file in files: + new_file = "{}/{}/{}".format( + layer_data["sceneName"], layer_data["layerName"], file + ) + new_files.append(new_file) + + return new_files + + class AOVError(Exception): """Custom exception for determining AOVs.""" From e85ef95b443825badf6b30e598712ba24cd6aeb0 Mon Sep 17 00:00:00 2001 From: Bo Zhou Date: Fri, 18 Mar 2022 20:00:57 +0900 Subject: [PATCH 02/27] improve 3delight render product class and return it --- openpype/hosts/maya/api/lib_renderproducts.py | 35 ++++++------------- 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/openpype/hosts/maya/api/lib_renderproducts.py b/openpype/hosts/maya/api/lib_renderproducts.py index 69c4eae18e..97aa8e8957 100644 --- a/openpype/hosts/maya/api/lib_renderproducts.py +++ b/openpype/hosts/maya/api/lib_renderproducts.py @@ -77,6 +77,7 @@ IMAGE_PREFIXES = { "arnold": "defaultRenderGlobals.imageFilePrefix", "renderman": "rmanGlobals.imageFileFormat", "redshift": "defaultRenderGlobals.imageFilePrefix", + "_3delight": "defaultRenderGlobals.imageFilePrefix", } @@ -154,7 +155,8 @@ def get(layer, render_instance=None): "arnold": RenderProductsArnold, "vray": RenderProductsVray, "redshift": RenderProductsRedshift, - "renderman": RenderProductsRenderman + "renderman": RenderProductsRenderman, + "_3delight": RenderProducts3Delight }.get(renderer_name.lower(), None) if renderer is None: raise UnsupportedRendererException( @@ -1137,13 +1139,16 @@ class RenderProducts3Delight(ARenderProducts): default_ext = "exr" - nodes = cmds.listConnections('dlRenderGlobals1') + nodes = cmds.listConnections( + 'dlRenderGlobals1', + type='dlRenderSettings') assert len(nodes) == 1 node = nodes[0] num_layers = cmds.getAttr( - '{}.layerOutput'.format(node), + '{}.layerOutputVariables'.format(node), size=True) + assert num_layers > 0 for i in range(num_layers): output = cmds.getAttr( '{}.layerOutput[{}]'.format(node, i)) @@ -1152,35 +1157,17 @@ class RenderProducts3Delight(ARenderProducts): output_var = cmds.getAttr( '{}.layerOutputVariables[{}]'.format(node, i)) - output_var_tokens = layerOutputVariable.split('|') - name = output_var_tokens[4] + output_var_tokens = output_var.split('|') + aov_name = output_var_tokens[4] for camera in cameras: - product = RenderProduct(productName=name, + product = RenderProduct(productName=aov_name, ext=default_ext, camera=camera) products.append(product) return products - def get_files(self, product, camera): - """Get expected files. - - See Also: - :func:`ARenderProducts.get_files()` - """ - files = super(RenderProducts3Delight, self).get_files(product, camera) - - layer_data = self.layer_data - new_files = [] - for file in files: - new_file = "{}/{}/{}".format( - layer_data["sceneName"], layer_data["layerName"], file - ) - new_files.append(new_file) - - return new_files - class AOVError(Exception): """Custom exception for determining AOVs.""" From 3ec621a8efa97bc33c63f33fd87a62a76fa7d0c2 Mon Sep 17 00:00:00 2001 From: Bo Zhou Date: Fri, 18 Mar 2022 20:02:29 +0900 Subject: [PATCH 03/27] add method _set_3delight_settings to render creator --- .../maya/plugins/create/create_render.py | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_render.py b/openpype/hosts/maya/plugins/create/create_render.py index 9002ae3876..c06fe8a76d 100644 --- a/openpype/hosts/maya/plugins/create/create_render.py +++ b/openpype/hosts/maya/plugins/create/create_render.py @@ -77,7 +77,8 @@ class CreateRender(plugin.Creator): 'vray': 'vraySettings.fileNamePrefix', 'arnold': 'defaultRenderGlobals.imageFilePrefix', 'renderman': 'defaultRenderGlobals.imageFilePrefix', - 'redshift': 'defaultRenderGlobals.imageFilePrefix' + 'redshift': 'defaultRenderGlobals.imageFilePrefix', + '_3delight': 'defaultRenderGlobals.imageFilePrefix' } _image_prefixes = { @@ -85,7 +86,8 @@ class CreateRender(plugin.Creator): 'vray': 'maya///', 'arnold': 'maya///{aov_separator}', # noqa 'renderman': 'maya///{aov_separator}', - 'redshift': 'maya///' # noqa + 'redshift': 'maya///', # noqa + '_3delight': 'maya///' # noqa } _aov_chars = { @@ -462,6 +464,8 @@ class CreateRender(plugin.Creator): asset["data"].get("resolutionHeight")) self._set_global_output_settings() + if renderer == "_3delight": + self._set_3delight_settings(asset) def _set_vray_settings(self, asset): # type: (dict) -> None @@ -507,6 +511,38 @@ class CreateRender(plugin.Creator): "{}.height".format(node), asset["data"].get("resolutionHeight")) + def _set_3delight_settings(self, asset): + # type: (dict) -> None + """Sets important settings for 3Delight.""" + nodes = cmds.listConnections( + 'dlRenderGlobals1', + type='dlRenderSettings') + assert len(nodes) == 1 + node = nodes[0] + + # frame range + start_frame = int(cmds.playbackOptions(query=True, + animationStartTime=True)) + end_frame = int(cmds.playbackOptions(query=True, + animationEndTime=True)) + + cmds.setAttr( + "{}.startFrame".format(node), start_frame) + cmds.setAttr( + "{}.endFrame".format(node), end_frame) + + # outputOptionsDefault + cmds.setAttr( + "{}.outputOptionsDefault".format(node), 2) + + # resolution + cmds.setAttr( + "defaultResolution.width", + asset["data"].get("resolutionWidth")) + cmds.setAttr( + "defaultResolution.height", + asset["data"].get("resolutionHeight")) + @staticmethod def _set_global_output_settings(): # enable animation From 87c697b51bd42655ef3936187240b4a2681efaac Mon Sep 17 00:00:00 2001 From: DMO Date: Wed, 22 Jun 2022 13:52:02 +0900 Subject: [PATCH 04/27] Rewrote large portions of the file to handle different nodes that may contain texture files. Currently supported are: - file (maya) - aiImage (Arnold) - RedshiftNormalMap (Redshift) - dlTexture (3Delight) - dlTriplanar (3Delight) --- .../publish/collect_multiverse_look.py | 250 +++++++++++------- 1 file changed, 148 insertions(+), 102 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/collect_multiverse_look.py b/openpype/hosts/maya/plugins/publish/collect_multiverse_look.py index edf40a27a6..4bd2476feb 100644 --- a/openpype/hosts/maya/plugins/publish/collect_multiverse_look.py +++ b/openpype/hosts/maya/plugins/publish/collect_multiverse_look.py @@ -21,37 +21,68 @@ COLOUR_SPACES = ['sRGB', 'linear', 'auto'] MIPMAP_EXTENSIONS = ['tdl'] -def get_look_attrs(node): - """Returns attributes of a node that are important for the look. +class _NodeTypeAttrib(object): + """docstring for _NodeType""" - These are the "changed" attributes (those that have edits applied - in the current scene). + def __init__(self, name, fname, computed_fname=None, colour_space=None): + self.name = name + self.fname = fname + self.computed_fname = computed_fname or fname + self.colour_space = colour_space or "colorSpace" - Returns: - list: Attribute names to extract + def get_fname(self, node): + return "{}.{}".format(node, self.fname) + def get_computed_fname(self, node): + return "{}.{}".format(node, self.computed_fname) + + def get_colour_space(self, node): + return "{}.{}".format(node, self.colour_space) + + def __str__(self): + return "_NodeTypeAttrib(name={}, fname={}, " + "computed_fname={}, colour_space={})".format( + self.name, self.fname, self.computed_fname, self.colour_space) + + +NODETYPES = { + "file": [_NodeTypeAttrib("file", "fileTextureName", + "computedFileTextureNamePattern")], + "aiImage": [_NodeTypeAttrib("aiImage", "filename")], + "RedshiftNormalMap": [_NodeTypeAttrib("RedshiftNormalMap", "tex0")], + "dlTexture": [_NodeTypeAttrib("dlTexture", "textureFile", + None, "textureFile_meta_colorspace")], + "dlTriplanar": [_NodeTypeAttrib("dlTriplanar", "colorTexture", + None, "colorTexture_meta_colorspace"), + _NodeTypeAttrib("dlTriplanar", "floatTexture", + None, "floatTexture_meta_colorspace"), + _NodeTypeAttrib("dlTriplanar", "heightTexture", + None, "heightTexture_meta_colorspace")] +} + + +def get_file_paths_for_node(node): + """Gets all the file paths in this node. + + Returns all filepaths that this node references. Some node types only + reference one, but others, like dlTriplanar, can reference 3. + + Args: + node (str): Name of the Maya node + + Returns + list(str): A list with all evaluated maya attributes for filepaths. """ - # When referenced get only attributes that are "changed since file open" - # which includes any reference edits, otherwise take *all* user defined - # attributes - is_referenced = cmds.referenceQuery(node, isNodeReferenced=True) - result = cmds.listAttr(node, userDefined=True, - changedSinceFileOpen=is_referenced) or [] - # `cbId` is added when a scene is saved, ignore by default - if "cbId" in result: - result.remove("cbId") + node_type = cmds.nodeType(node) + if node_type not in NODETYPES: + return [] - # For shapes allow render stat changes - if cmds.objectType(node, isAType="shape"): - attrs = cmds.listAttr(node, changedSinceFileOpen=True) or [] - for attr in attrs: - if attr in SHAPE_ATTRS: - result.append(attr) - elif attr.startswith('ai'): - result.append(attr) - - return result + paths = [] + for node_type_attr in NODETYPES[node_type]: + fname = cmds.getAttr("{}.{}".format(node, node_type_attr.fname)) + paths.append(fname) + return paths def node_uses_image_sequence(node): @@ -69,13 +100,29 @@ def node_uses_image_sequence(node): """ # useFrameExtension indicates an explicit image sequence - node_path = get_file_node_path(node).lower() + paths = get_file_node_paths(node) + paths = [path.lower() for path in paths] # The following tokens imply a sequence patterns = ["", "", "", "u_v", ""] lower = texture_pattern.lower() if any(pattern in lower for pattern in patterns): - return texture_pattern + return [texture_pattern] - if cmds.nodeType(node) == 'aiImage': - return cmds.getAttr('{0}.filename'.format(node)) - if cmds.nodeType(node) == 'RedshiftNormalMap': - return cmds.getAttr('{}.tex0'.format(node)) - - # otherwise use fileTextureName - return cmds.getAttr('{0}.fileTextureName'.format(node)) + return get_file_paths_for_node(node) def get_file_node_files(node): @@ -181,15 +222,13 @@ def get_file_node_files(node): """ - path = get_file_node_path(node) - path = cmds.workspace(expandName=path) + paths = get_file_node_paths(node) + paths = [cmds.workspace(expandName=path) for path in paths] if node_uses_image_sequence(node): - glob_pattern = seq_to_glob(path) - return glob.glob(glob_pattern) - elif os.path.exists(path): - return [path] + globs = [glob.glob(seq_to_glob(path)) for path in paths] + return globs else: - return [] + return list(filter(lambda x: os.path.exists(x), paths)) def get_mipmap(fname): @@ -211,6 +250,11 @@ def is_mipmap(fname): class CollectMultiverseLookData(pyblish.api.InstancePlugin): """Collect Multiverse Look + Searches through the overrides finding all material overrides. From there + it extracts the shading group and then finds all texture files in the + shading group network. It also checks for mipmap versions of texture files + and adds them to the resouces to get published. + """ order = pyblish.api.CollectorOrder + 0.2 @@ -258,12 +302,20 @@ class CollectMultiverseLookData(pyblish.api.InstancePlugin): shadingGroup), "members": list()} # The SG may reference files, add those too! - history = cmds.listHistory(shadingGroup) - files = cmds.ls(history, type="file", long=True) + history = cmds.listHistory( + shadingGroup, allConnections=True) + + # We need to iterate over node_types since `cmds.ls` may + # error out if we don't have the appropriate plugin loaded. + files = [] + for node_type in NODETYPES.keys(): + files += cmds.ls(history, + type=node_type, + long=True) for f in files: resources = self.collect_resource(f, publishMipMap) - instance.data["resources"].append(resources) + instance.data["resources"] += resources elif isinstance(matOver, multiverse.MaterialSourceUsdPath): # TODO: Handle this later. @@ -284,69 +336,63 @@ class CollectMultiverseLookData(pyblish.api.InstancePlugin): dict """ - self.log.debug("processing: {}".format(node)) - if cmds.nodeType(node) not in ["file", "aiImage", "RedshiftNormalMap"]: - self.log.error( - "Unsupported file node: {}".format(cmds.nodeType(node))) + node_type = cmds.nodeType(node) + self.log.debug("processing: {}/{}".format(node, node_type)) + + if not node_type in NODETYPES: + self.log.error("Unsupported file node: {}".format(node_type)) raise AssertionError("Unsupported file node") - if cmds.nodeType(node) == 'file': - self.log.debug(" - file node") - attribute = "{}.fileTextureName".format(node) - computed_attribute = "{}.computedFileTextureNamePattern".format( - node) - elif cmds.nodeType(node) == 'aiImage': - self.log.debug("aiImage node") - attribute = "{}.filename".format(node) - computed_attribute = attribute - elif cmds.nodeType(node) == 'RedshiftNormalMap': - self.log.debug("RedshiftNormalMap node") - attribute = "{}.tex0".format(node) - computed_attribute = attribute + resources = [] + for node_type_attr in NODETYPES[node_type]: + fname_attrib = node_type_attr.get_fname(node) + computed_fname_attrib = node_type_attr.get_computed_fname(node) + colour_space_attrib = node_type_attr.get_colour_space(node) - source = cmds.getAttr(attribute) - self.log.info(" - file source: {}".format(source)) - color_space_attr = "{}.colorSpace".format(node) - try: - color_space = cmds.getAttr(color_space_attr) - except ValueError: - # node doesn't have colorspace attribute + source = cmds.getAttr(fname_attrib) color_space = "Raw" - # Compare with the computed file path, e.g. the one with the - # pattern in it, to generate some logging information about this - # difference - # computed_attribute = "{}.computedFileTextureNamePattern".format(node) - computed_source = cmds.getAttr(computed_attribute) - if source != computed_source: - self.log.debug("Detected computed file pattern difference " - "from original pattern: {0} " - "({1} -> {2})".format(node, - source, - computed_source)) + try: + color_space = cmds.getAttr(colour_space_attrib) + except ValueError: + # node doesn't have colorspace attribute, use "Raw" from before + pass + # Compare with the computed file path, e.g. the one with the + # pattern in it, to generate some logging information about this + # difference + # computed_attribute = "{}.computedFileTextureNamePattern".format(node) + computed_source = cmds.getAttr(computed_fname_attrib) + if source != computed_source: + self.log.debug("Detected computed file pattern difference " + "from original pattern: {0} " + "({1} -> {2})".format(node, + source, + computed_source)) - # We replace backslashes with forward slashes because V-Ray - # can't handle the UDIM files with the backslashes in the - # paths as the computed patterns - source = source.replace("\\", "/") + # We replace backslashes with forward slashes because V-Ray + # can't handle the UDIM files with the backslashes in the + # paths as the computed patterns + source = source.replace("\\", "/") - files = get_file_node_files(node) - files = self.handle_files(files, publishMipMap) - if len(files) == 0: - self.log.error("No valid files found from node `%s`" % node) + files = get_file_node_files(node) + files = self.handle_files(files, publishMipMap) + if len(files) == 0: + self.log.error("No valid files found from node `%s`" % node) - self.log.info("collection of resource done:") - self.log.info(" - node: {}".format(node)) - self.log.info(" - attribute: {}".format(attribute)) - self.log.info(" - source: {}".format(source)) - self.log.info(" - file: {}".format(files)) - self.log.info(" - color space: {}".format(color_space)) + self.log.info("collection of resource done:") + self.log.info(" - node: {}".format(node)) + self.log.info(" - attribute: {}".format(fname_attrib)) + self.log.info(" - source: {}".format(source)) + self.log.info(" - file: {}".format(files)) + self.log.info(" - color space: {}".format(color_space)) - # Define the resource - return {"node": node, - "attribute": attribute, - "source": source, # required for resources - "files": files, - "color_space": color_space} # required for resources + # Define the resource + resource = {"node": node, + "attribute": fname_attrib, + "source": source, # required for resources + "files": files, + "color_space": color_space} # required for resources + resources.append(resource) + return resources def handle_files(self, files, publishMipMap): """This will go through all the files and make sure that they are From 2007c759478b08b002a0d0a91816c5cf0e769c3f Mon Sep 17 00:00:00 2001 From: DMO Date: Fri, 24 Jun 2022 15:22:31 +0900 Subject: [PATCH 05/27] Reverting `mvUsd` family back to `usd` so that other software can create standard Usd files and still be imported directly by Multiverse. --- openpype/hosts/maya/plugins/create/create_multiverse_usd.py | 2 +- openpype/hosts/maya/plugins/load/load_multiverse_usd.py | 2 +- openpype/hosts/maya/plugins/load/load_reference.py | 3 ++- openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py | 2 +- openpype/plugins/publish/integrate_new.py | 1 - 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_multiverse_usd.py b/openpype/hosts/maya/plugins/create/create_multiverse_usd.py index 5290d5143f..8cd76b5f40 100644 --- a/openpype/hosts/maya/plugins/create/create_multiverse_usd.py +++ b/openpype/hosts/maya/plugins/create/create_multiverse_usd.py @@ -6,7 +6,7 @@ class CreateMultiverseUsd(plugin.Creator): name = "mvUsdMain" label = "Multiverse USD Asset" - family = "mvUsd" + family = "usd" icon = "cubes" def __init__(self, *args, **kwargs): diff --git a/openpype/hosts/maya/plugins/load/load_multiverse_usd.py b/openpype/hosts/maya/plugins/load/load_multiverse_usd.py index 3350dc6ac9..76d7c306a0 100644 --- a/openpype/hosts/maya/plugins/load/load_multiverse_usd.py +++ b/openpype/hosts/maya/plugins/load/load_multiverse_usd.py @@ -16,7 +16,7 @@ from openpype.hosts.maya.api.pipeline import containerise class MultiverseUsdLoader(load.LoaderPlugin): """Read USD data in a Multiverse Compound""" - families = ["model", "mvUsd", "mvUsdComposition", "mvUsdOverride", + families = ["model", "usd", "mvUsdComposition", "mvUsdOverride", "pointcache", "animation"] representations = ["usd", "usda", "usdc", "usdz", "abc"] diff --git a/openpype/hosts/maya/plugins/load/load_reference.py b/openpype/hosts/maya/plugins/load/load_reference.py index e4355ed3d4..0a2640014c 100644 --- a/openpype/hosts/maya/plugins/load/load_reference.py +++ b/openpype/hosts/maya/plugins/load/load_reference.py @@ -25,7 +25,8 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): "rig", "camerarig", "xgen", - "staticMesh"] + "staticMesh", + "mvLook"] representations = ["ma", "abc", "fbx", "mb"] label = "Reference" diff --git a/openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py b/openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py index 3654be7b34..b1aaf9d9ba 100644 --- a/openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py +++ b/openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py @@ -26,7 +26,7 @@ class ExtractMultiverseUsd(openpype.api.Extractor): label = "Extract Multiverse USD Asset" hosts = ["maya"] - families = ["mvUsd"] + families = ["usd"] scene_type = "usd" file_formats = ["usd", "usda", "usdz"] diff --git a/openpype/plugins/publish/integrate_new.py b/openpype/plugins/publish/integrate_new.py index 2471105250..f5ca125189 100644 --- a/openpype/plugins/publish/integrate_new.py +++ b/openpype/plugins/publish/integrate_new.py @@ -110,7 +110,6 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): "staticMesh", "skeletalMesh", "mvLook", - "mvUsd", "mvUsdComposition", "mvUsdOverride", "simpleUnrealTexture" From ab1c86a96dda5eb92281e810027d8952c91b0a31 Mon Sep 17 00:00:00 2001 From: DMO Date: Tue, 28 Jun 2022 15:40:45 +0900 Subject: [PATCH 06/27] Do not lock nodes, it's not needed and it makes things much harder. --- openpype/hosts/maya/plugins/load/load_multiverse_usd.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_multiverse_usd.py b/openpype/hosts/maya/plugins/load/load_multiverse_usd.py index 76d7c306a0..24b97db365 100644 --- a/openpype/hosts/maya/plugins/load/load_multiverse_usd.py +++ b/openpype/hosts/maya/plugins/load/load_multiverse_usd.py @@ -47,9 +47,6 @@ class MultiverseUsdLoader(load.LoaderPlugin): transform = cmds.listRelatives( shape, parent=True, fullPath=True)[0] - # Lock the shape node so the user cannot delete it. - cmds.lockNode(shape, lock=True) - nodes = [transform, shape] self[:] = nodes From c5fd2a970eab972b87468d5c3f7e796b3c11c2e1 Mon Sep 17 00:00:00 2001 From: DMO Date: Tue, 28 Jun 2022 17:13:18 +0900 Subject: [PATCH 07/27] I was incorrectly globing into an array of arrays instead of single long array. --- .../hosts/maya/plugins/publish/collect_multiverse_look.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/collect_multiverse_look.py b/openpype/hosts/maya/plugins/publish/collect_multiverse_look.py index 4bd2476feb..b11dbaeba6 100644 --- a/openpype/hosts/maya/plugins/publish/collect_multiverse_look.py +++ b/openpype/hosts/maya/plugins/publish/collect_multiverse_look.py @@ -225,7 +225,9 @@ def get_file_node_files(node): paths = get_file_node_paths(node) paths = [cmds.workspace(expandName=path) for path in paths] if node_uses_image_sequence(node): - globs = [glob.glob(seq_to_glob(path)) for path in paths] + globs = [] + for path in paths: + globs += glob.glob(seq_to_glob(path)) return globs else: return list(filter(lambda x: os.path.exists(x), paths)) From 044946e8c5f65a8a4ae308862b2ed5d57494e501 Mon Sep 17 00:00:00 2001 From: DMO Date: Tue, 28 Jun 2022 20:47:28 +0900 Subject: [PATCH 08/27] Strip UsdComp namespaces. Write attributes from mvLook. Formatting. --- .../hosts/maya/plugins/create/create_multiverse_usd_comp.py | 2 +- openpype/hosts/maya/plugins/publish/collect_look.py | 3 ++- .../hosts/maya/plugins/publish/collect_multiverse_look.py | 2 +- .../hosts/maya/plugins/publish/extract_multiverse_look.py | 2 +- .../hosts/maya/plugins/publish/validate_mvlook_contents.py | 5 +++-- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_multiverse_usd_comp.py b/openpype/hosts/maya/plugins/create/create_multiverse_usd_comp.py index ed466a8068..a92969eb9a 100644 --- a/openpype/hosts/maya/plugins/create/create_multiverse_usd_comp.py +++ b/openpype/hosts/maya/plugins/create/create_multiverse_usd_comp.py @@ -17,7 +17,7 @@ class CreateMultiverseUsdComp(plugin.Creator): # Order of `fileFormat` must match extract_multiverse_usd_comp.py self.data["fileFormat"] = ["usda", "usd"] - self.data["stripNamespaces"] = False + self.data["stripNamespaces"] = True self.data["mergeTransformAndShape"] = False self.data["flattenContent"] = False self.data["writeAsCompoundLayers"] = False diff --git a/openpype/hosts/maya/plugins/publish/collect_look.py b/openpype/hosts/maya/plugins/publish/collect_look.py index e8ada57f8f..28c57e04b5 100644 --- a/openpype/hosts/maya/plugins/publish/collect_look.py +++ b/openpype/hosts/maya/plugins/publish/collect_look.py @@ -440,7 +440,8 @@ class CollectLook(pyblish.api.InstancePlugin): for res in self.collect_resources(n): instance.data["resources"].append(res) - self.log.info("Collected resources: {}".format(instance.data["resources"])) + self.log.info("Collected resources: {}".format( + instance.data["resources"])) # Log warning when no relevant sets were retrieved for the look. if ( diff --git a/openpype/hosts/maya/plugins/publish/collect_multiverse_look.py b/openpype/hosts/maya/plugins/publish/collect_multiverse_look.py index b11dbaeba6..4c50e4df27 100644 --- a/openpype/hosts/maya/plugins/publish/collect_multiverse_look.py +++ b/openpype/hosts/maya/plugins/publish/collect_multiverse_look.py @@ -256,7 +256,7 @@ class CollectMultiverseLookData(pyblish.api.InstancePlugin): it extracts the shading group and then finds all texture files in the shading group network. It also checks for mipmap versions of texture files and adds them to the resouces to get published. - + """ order = pyblish.api.CollectorOrder + 0.2 diff --git a/openpype/hosts/maya/plugins/publish/extract_multiverse_look.py b/openpype/hosts/maya/plugins/publish/extract_multiverse_look.py index 82e2b41929..b97314d5a1 100644 --- a/openpype/hosts/maya/plugins/publish/extract_multiverse_look.py +++ b/openpype/hosts/maya/plugins/publish/extract_multiverse_look.py @@ -73,7 +73,7 @@ class ExtractMultiverseLook(openpype.api.Extractor): "writeAll": False, "writeTransforms": False, "writeVisibility": False, - "writeAttributes": False, + "writeAttributes": True, "writeMaterials": True, "writeVariants": False, "writeVariantsDefinition": False, diff --git a/openpype/hosts/maya/plugins/publish/validate_mvlook_contents.py b/openpype/hosts/maya/plugins/publish/validate_mvlook_contents.py index bac2c030c8..a755ec1da9 100644 --- a/openpype/hosts/maya/plugins/publish/validate_mvlook_contents.py +++ b/openpype/hosts/maya/plugins/publish/validate_mvlook_contents.py @@ -80,13 +80,14 @@ class ValidateMvLookContents(pyblish.api.InstancePlugin): def is_or_has_mipmap(self, fname, files): ext = os.path.splitext(fname)[1][1:] if ext in MIPMAP_EXTENSIONS: - self.log.debug("Is a mipmap '{}'".format(fname)) + self.log.debug(" - Is a mipmap '{}'".format(fname)) return True for colour_space in COLOUR_SPACES: for mipmap_ext in MIPMAP_EXTENSIONS: mipmap_fname = '.'.join([fname, colour_space, mipmap_ext]) if mipmap_fname in files: - self.log.debug("Has a mipmap '{}'".format(fname)) + self.log.debug( + " - Has a mipmap '{}'".format(mipmap_fname)) return True return False From cb82e66d01c506bd9038acec6904a0a7b39cbb37 Mon Sep 17 00:00:00 2001 From: DMO Date: Wed, 6 Jul 2022 11:30:47 +0900 Subject: [PATCH 09/27] Naming suffixes can be overridden in project settings, lets print out what they *actually* are. --- .../publish/validate_transform_naming_suffix.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_transform_naming_suffix.py b/openpype/hosts/maya/plugins/publish/validate_transform_naming_suffix.py index 6f5ff24b9c..51561b35e1 100644 --- a/openpype/hosts/maya/plugins/publish/validate_transform_naming_suffix.py +++ b/openpype/hosts/maya/plugins/publish/validate_transform_naming_suffix.py @@ -20,6 +20,7 @@ class ValidateTransformNamingSuffix(pyblish.api.InstancePlugin): - nurbsSurface: _NRB - locator: _LOC - null/group: _GRP + Suffices can also be overriden by project settings. .. warning:: This grabs the first child shape as a reference and doesn't use the @@ -43,6 +44,13 @@ class ValidateTransformNamingSuffix(pyblish.api.InstancePlugin): ALLOW_IF_NOT_IN_SUFFIX_TABLE = True + @classmethod + def get_table_for_invalid(cls): + ss = [] + for k,v in cls.SUFFIX_NAMING_TABLE.items(): + ss.append(" - {}: {}".format(k,", ".join(v))) + return "\n".join(ss) + @staticmethod def is_valid_name(node_name, shape_type, SUFFIX_NAMING_TABLE, ALLOW_IF_NOT_IN_SUFFIX_TABLE): @@ -105,5 +113,7 @@ class ValidateTransformNamingSuffix(pyblish.api.InstancePlugin): """ invalid = self.get_invalid(instance) if invalid: + valid = self.get_table_for_invalid() raise ValueError("Incorrectly named geometry " - "transforms: {0}".format(invalid)) + "transforms: {0}, accepted suffixes are: " + "\n{1}".format(invalid, valid)) From 56f13bf388ad42a4a34e533fe5ea529ffd7f6668 Mon Sep 17 00:00:00 2001 From: DMO Date: Wed, 6 Jul 2022 12:39:40 +0900 Subject: [PATCH 10/27] Move alembic publishing options around. Adding more publishing options to the schema and defaults. --- .../defaults/project_settings/maya.json | 22 +++--- .../schemas/schema_maya_publish.json | 72 ++++++++++++------- 2 files changed, 62 insertions(+), 32 deletions(-) diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index cdd3a62d00..82d5ab20cb 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -401,14 +401,6 @@ "optional": true, "active": true }, - "ExtractAlembic": { - "enabled": true, - "families": [ - "pointcache", - "model", - "vrayproxy" - ] - }, "ValidateRigContents": { "enabled": false, "optional": true, @@ -561,6 +553,20 @@ "optional": true, "active": true, "bake_attributes": [] + }, + "ExtractAlembic": { + "enabled": true, + "families": [ + "pointcache", + "model", + "vrayproxy" + ] + }, + "ExtractAnimation": { + "enabled": true + }, + "ExtractMultiverseUsdAnim": { + "enabled": true } }, "load": { diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json index 41b681d893..e77bb1a6f8 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json @@ -504,30 +504,6 @@ "label": "ValidateUniqueNames" } ] - }, - { - "type": "label", - "label": "Extractors" - }, - { - "type": "dict", - "collapsible": true, - "key": "ExtractAlembic", - "label": "Extract Alembic", - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "key": "families", - "label": "Families", - "type": "list", - "object_type": "text" - } - ] } ] }, @@ -686,6 +662,54 @@ "is_list": true } ] + }, + { + "type": "dict", + "collapsible": true, + "key": "ExtractAlembic", + "label": "Extract Alembic", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "key": "families", + "label": "Families", + "type": "list", + "object_type": "text" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "ExtractAnimation", + "label": "Extract Alembic Animation", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "ExtractMultiverseUsdAnim", + "label": "Extract USD Animation", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + } + ] } ] } From 53f56853d9519b96115ab92e10aa610d776cd5be Mon Sep 17 00:00:00 2001 From: DMO Date: Wed, 6 Jul 2022 12:43:24 +0900 Subject: [PATCH 11/27] Subclass ExtractMultiverseUsd as ExtractMultiverseUsdAnim to enable animation-specific USD extraction. Adding noNormals to settings to allow skipping normals for meshes that will be sub-divided later. --- .../maya/plugins/create/create_animation.py | 3 + .../maya/plugins/publish/extract_animation.py | 4 +- .../plugins/publish/extract_multiverse_usd.py | 70 ++++++++++++++++--- 3 files changed, 65 insertions(+), 12 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_animation.py b/openpype/hosts/maya/plugins/create/create_animation.py index 5cd1f7090a..b9e1f3b7a2 100644 --- a/openpype/hosts/maya/plugins/create/create_animation.py +++ b/openpype/hosts/maya/plugins/create/create_animation.py @@ -42,3 +42,6 @@ class CreateAnimation(plugin.Creator): # Default to not send to farm. self.data["farm"] = False self.data["priority"] = 50 + + # Default to write normals. + self.data["writeNormals"] = True diff --git a/openpype/hosts/maya/plugins/publish/extract_animation.py b/openpype/hosts/maya/plugins/publish/extract_animation.py index abe5ed3bf5..7ce80b4679 100644 --- a/openpype/hosts/maya/plugins/publish/extract_animation.py +++ b/openpype/hosts/maya/plugins/publish/extract_animation.py @@ -63,7 +63,9 @@ class ExtractAnimation(openpype.api.Extractor): "selection": True, "worldSpace": instance.data.get("worldSpace", True), "writeColorSets": instance.data.get("writeColorSets", False), - "writeFaceSets": instance.data.get("writeFaceSets", False) + "writeFaceSets": instance.data.get("writeFaceSets", False), + # 'noNormals' is the standard alembic option name. + "noNormals": not instance.data.get("writeNormals", True) } if not instance.data.get("includeParentHierarchy", True): diff --git a/openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py b/openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py index b1aaf9d9ba..2a99dffa8d 100644 --- a/openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py +++ b/openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py @@ -2,6 +2,7 @@ import os import six from maya import cmds +from maya import mel import openpype.api from openpype.hosts.maya.api.lib import maintained_selection @@ -87,7 +88,7 @@ class ExtractMultiverseUsd(openpype.api.Extractor): return { "stripNamespaces": False, "mergeTransformAndShape": False, - "writeAncestors": True, + "writeAncestors": False, "flattenParentXforms": False, "writeSparseOverrides": False, "useMetaPrimPath": False, @@ -147,6 +148,13 @@ class ExtractMultiverseUsd(openpype.api.Extractor): return options + def get_default_options(self): + self.log.info("ExtractMultiverseUsd get_default_options") + return self.default_options + + def filter_members(self, members): + return members + def process(self, instance): # Load plugin first cmds.loadPlugin("MultiverseForMaya", quiet=True) @@ -161,7 +169,7 @@ class ExtractMultiverseUsd(openpype.api.Extractor): file_path = file_path.replace('\\', '/') # Parse export options - options = self.default_options + options = self.get_default_options() options = self.parse_overrides(instance, options) self.log.info("Export options: {0}".format(options)) @@ -170,27 +178,35 @@ class ExtractMultiverseUsd(openpype.api.Extractor): with maintained_selection(): members = instance.data("setMembers") - self.log.info('Collected object {}'.format(members)) + self.log.info('Collected objects: {}'.format(members)) + members = self.filter_members(members) + if not members: + self.log.error('No members!') + return + self.log.info(' - filtered: {}'.format(members)) import multiverse time_opts = None frame_start = instance.data['frameStart'] frame_end = instance.data['frameEnd'] - handle_start = instance.data['handleStart'] - handle_end = instance.data['handleEnd'] - step = instance.data['step'] - fps = instance.data['fps'] if frame_end != frame_start: time_opts = multiverse.TimeOptions() time_opts.writeTimeRange = True + + handle_start = instance.data['handleStart'] + handle_end = instance.data['handleEnd'] + time_opts.frameRange = ( frame_start - handle_start, frame_end + handle_end) - time_opts.frameIncrement = step - time_opts.numTimeSamples = instance.data["numTimeSamples"] - time_opts.timeSamplesSpan = instance.data["timeSamplesSpan"] - time_opts.framePerSecond = fps + time_opts.frameIncrement = instance.data['step'] + time_opts.numTimeSamples = instance.data.get( + 'numTimeSamples', options['numTimeSamples']) + time_opts.timeSamplesSpan = instance.data.get( + 'timeSamplesSpan', options['timeSamplesSpan']) + time_opts.framePerSecond = instance.data.get( + 'fps', mel.eval('currentTimeUnitToFPS()')) asset_write_opts = multiverse.AssetWriteOptions(time_opts) options_discard_keys = { @@ -203,11 +219,15 @@ class ExtractMultiverseUsd(openpype.api.Extractor): 'step', 'fps' } + self.log.debug("Write Options:") for key, value in options.items(): if key in options_discard_keys: continue + + self.log.debug(" - {}={}".format(key, value)) setattr(asset_write_opts, key, value) + self.log.info('WriteAsset: {} / {}'.format(file_path, members)) multiverse.WriteAsset(file_path, members, asset_write_opts) if "representations" not in instance.data: @@ -223,3 +243,31 @@ class ExtractMultiverseUsd(openpype.api.Extractor): self.log.info("Extracted instance {} to {}".format( instance.name, file_path)) + + +class ExtractMultiverseUsdAnim(ExtractMultiverseUsd): + """Extractor for Multiverse USD Animation Sparse Cache data. + + This will extract the sparse cache data from the scene and generate a + USD file with all the animation data. + + Upon publish a .usd sparse cache will be written. + """ + label = "Extract Multiverse USD Animation Sparse Cache" + families = ["animation"] + + def get_default_options(self): + anim_options = self.default_options + anim_options["writeSparseOverrides"] = True + anim_options["stripNamespaces"] = True + return anim_options + + def filter_members(self, members): + out_set = next((i for i in members if i.endswith("out_SET")), None) + + if out_set is None: + self.log.warning("Expecting out_SET") + return None + + members = cmds.ls(cmds.sets(out_set, query=True), long=True) + return members From cdf165ed79c7b9f3939d3b199d0757f9bac74e5b Mon Sep 17 00:00:00 2001 From: DMO Date: Wed, 6 Jul 2022 12:43:46 +0900 Subject: [PATCH 12/27] The mvLook should skip namespaces. --- openpype/hosts/maya/plugins/publish/extract_multiverse_look.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_multiverse_look.py b/openpype/hosts/maya/plugins/publish/extract_multiverse_look.py index b97314d5a1..8a5d7e4e53 100644 --- a/openpype/hosts/maya/plugins/publish/extract_multiverse_look.py +++ b/openpype/hosts/maya/plugins/publish/extract_multiverse_look.py @@ -78,7 +78,7 @@ class ExtractMultiverseLook(openpype.api.Extractor): "writeVariants": False, "writeVariantsDefinition": False, "writeActiveState": False, - "writeNamespaces": False, + "writeNamespaces": True, "numTimeSamples": 1, "timeSamplesSpan": 0.0 } From 559210ffb6ffacd68be8227d56b90c036a70c184 Mon Sep 17 00:00:00 2001 From: DMO Date: Wed, 6 Jul 2022 12:44:24 +0900 Subject: [PATCH 13/27] If there's a mixed-attribute on the node, this will fail, just skip it with a warning. --- openpype/hosts/maya/plugins/publish/collect_look.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openpype/hosts/maya/plugins/publish/collect_look.py b/openpype/hosts/maya/plugins/publish/collect_look.py index 28c57e04b5..40edd3b2f4 100644 --- a/openpype/hosts/maya/plugins/publish/collect_look.py +++ b/openpype/hosts/maya/plugins/publish/collect_look.py @@ -549,6 +549,11 @@ class CollectLook(pyblish.api.InstancePlugin): if not cmds.attributeQuery(attr, node=node, exists=True): continue attribute = "{}.{}".format(node, attr) + # We don't support mixed-type attributes yet. + if cmds.attributeQuery(attr, node=node, multi=True): + self.log.warning("Attribute '{}' is mixed-type and is " + "not supported yet.".format(attribute)) + continue if cmds.getAttr(attribute, type=True) == "message": continue node_attributes[attr] = cmds.getAttr(attribute) From 77c45e2b7f110e6939b685b03c613f7feef7f718 Mon Sep 17 00:00:00 2001 From: DMO Date: Wed, 20 Jul 2022 11:04:17 +0900 Subject: [PATCH 14/27] Animation should also write out UsdAttributes. --- openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py b/openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py index 2a99dffa8d..40dd5dfe50 100644 --- a/openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py +++ b/openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py @@ -259,6 +259,7 @@ class ExtractMultiverseUsdAnim(ExtractMultiverseUsd): def get_default_options(self): anim_options = self.default_options anim_options["writeSparseOverrides"] = True + anim_options["writeUsdAttributes"] = True anim_options["stripNamespaces"] = True return anim_options From aeee2a491821133549672a98a0939e402fb7ce12 Mon Sep 17 00:00:00 2001 From: DMO Date: Mon, 31 Oct 2022 14:51:44 +0900 Subject: [PATCH 15/27] Fixed commit for adding proper layered support for USD overrides. --- .../create/create_multiverse_usd_comp.py | 2 +- .../maya/plugins/load/load_multiverse_usd.py | 32 ++++- .../plugins/load/load_multiverse_usd_over.py | 136 ++++++++++++++++++ 3 files changed, 164 insertions(+), 6 deletions(-) create mode 100644 openpype/hosts/maya/plugins/load/load_multiverse_usd_over.py diff --git a/openpype/hosts/maya/plugins/create/create_multiverse_usd_comp.py b/openpype/hosts/maya/plugins/create/create_multiverse_usd_comp.py index a92969eb9a..ed466a8068 100644 --- a/openpype/hosts/maya/plugins/create/create_multiverse_usd_comp.py +++ b/openpype/hosts/maya/plugins/create/create_multiverse_usd_comp.py @@ -17,7 +17,7 @@ class CreateMultiverseUsdComp(plugin.Creator): # Order of `fileFormat` must match extract_multiverse_usd_comp.py self.data["fileFormat"] = ["usda", "usd"] - self.data["stripNamespaces"] = True + self.data["stripNamespaces"] = False self.data["mergeTransformAndShape"] = False self.data["flattenContent"] = False self.data["writeAsCompoundLayers"] = False diff --git a/openpype/hosts/maya/plugins/load/load_multiverse_usd.py b/openpype/hosts/maya/plugins/load/load_multiverse_usd.py index 24b97db365..13915aa2a9 100644 --- a/openpype/hosts/maya/plugins/load/load_multiverse_usd.py +++ b/openpype/hosts/maya/plugins/load/load_multiverse_usd.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- import maya.cmds as cmds +from maya import mel +import os from openpype.pipeline import ( load, @@ -11,6 +13,7 @@ from openpype.hosts.maya.api.lib import ( unique_namespace ) from openpype.hosts.maya.api.pipeline import containerise +from openpype.client import get_representations, get_representation_by_id class MultiverseUsdLoader(load.LoaderPlugin): @@ -26,7 +29,6 @@ class MultiverseUsdLoader(load.LoaderPlugin): color = "orange" def load(self, context, name=None, namespace=None, options=None): - asset = context['asset']['name'] namespace = namespace or unique_namespace( asset + "_", @@ -34,15 +36,16 @@ class MultiverseUsdLoader(load.LoaderPlugin): suffix="_", ) - # Create the shape + # Make sure we can load the plugin cmds.loadPlugin("MultiverseForMaya", quiet=True) + import multiverse + # Create the shape shape = None transform = None with maintained_selection(): cmds.namespace(addNamespace=namespace) with namespaced(namespace, new=False): - import multiverse shape = multiverse.CreateUsdCompound(self.fname) transform = cmds.listRelatives( shape, parent=True, fullPath=True)[0] @@ -67,15 +70,34 @@ class MultiverseUsdLoader(load.LoaderPlugin): shapes = cmds.ls(members, type="mvUsdCompoundShape") assert shapes, "Cannot find mvUsdCompoundShape in container" - path = get_representation_path(representation) + project_name = representation["context"]["project"]["name"] + prev_representation_id = cmds.getAttr("{}.representation".format(node)) + prev_representation = get_representation_by_id(project_name, + prev_representation_id) + prev_path = os.path.normpath(prev_representation["data"]["path"]) + # Make sure we can load the plugin + cmds.loadPlugin("MultiverseForMaya", quiet=True) import multiverse + for shape in shapes: - multiverse.SetUsdCompoundAssetPaths(shape, [path]) + + asset_paths = multiverse.GetUsdCompoundAssetPaths(shape) + asset_paths = [os.path.normpath(p) for p in asset_paths] + + assert asset_paths.count(prev_path) == 1, \ + "Couldn't find matching path (or too many)" + prev_path_idx = asset_paths.index(prev_path) + + path = get_representation_path(representation) + asset_paths[prev_path_idx] = path + + multiverse.SetUsdCompoundAssetPaths(shape, asset_paths) cmds.setAttr("{}.representation".format(node), str(representation["_id"]), type="string") + mel.eval('refreshEditorTemplates;') def switch(self, container, representation): self.update(container, representation) diff --git a/openpype/hosts/maya/plugins/load/load_multiverse_usd_over.py b/openpype/hosts/maya/plugins/load/load_multiverse_usd_over.py new file mode 100644 index 0000000000..080475461b --- /dev/null +++ b/openpype/hosts/maya/plugins/load/load_multiverse_usd_over.py @@ -0,0 +1,136 @@ +# -*- coding: utf-8 -*- +import maya.cmds as cmds +from maya import mel +import os + +import qargparse + +from openpype.pipeline import ( + load, + get_representation_path +) +from openpype.hosts.maya.api.lib import ( + maintained_selection, + namespaced, + unique_namespace +) +from openpype.hosts.maya.api.pipeline import containerise +from openpype.client import get_representations, get_representation_by_id + + +class MultiverseUsdOverLoader(load.LoaderPlugin): + """Reference file""" + + families = ["mvUsdOverride"] + representations = ["usda", "usd", "udsz"] + + label = "Load Usd Override into Compound" + order = -10 + icon = "code-fork" + color = "orange" + + options = [ + qargparse.String( + "Which Compound", + label="Compound", + help="Select which compound to add this as a layer to." + ) + ] + + def load(self, context, name=None, namespace=None, options=None): + asset = context['asset']['name'] + + current_usd = cmds.ls(selection=True, + type="mvUsdCompoundShape", + dag=True, + long=True) + if len(current_usd) != 1: + self.log.error("Current selection invalid: '{}', " + "must contain exactly 1 mvUsdCompoundShape." + "".format(current_usd)) + return + + # Make sure we can load the plugin + cmds.loadPlugin("MultiverseForMaya", quiet=True) + import multiverse + + nodes = current_usd + with maintained_selection(): + multiverse.AddUsdCompoundAssetPath(current_usd[0], self.fname) + + namespace = current_usd[0].split("|")[1].split(":")[0] + + container = containerise( + name=name, + namespace=namespace, + nodes=nodes, + context=context, + loader=self.__class__.__name__) + + cmds.addAttr(container, longName="mvUsdCompoundShape", + niceName="mvUsdCompoundShape", dataType="string") + cmds.setAttr(container + ".mvUsdCompoundShape", + current_usd[0], type="string") + + return container + + def update(self, container, representation): + # type: (dict, dict) -> None + """Update container with specified representation.""" + + cmds.loadPlugin("MultiverseForMaya", quiet=True) + import multiverse + + node = container['objectName'] + assert cmds.objExists(node), "Missing container" + + members = cmds.sets(node, query=True) or [] + shapes = cmds.ls(members, type="mvUsdCompoundShape") + assert shapes, "Cannot find mvUsdCompoundShape in container" + + mvShape = container['mvUsdCompoundShape'] + assert mvShape, "Missing mv source" + + project_name = representation["context"]["project"]["name"] + prev_representation_id = cmds.getAttr("{}.representation".format(node)) + prev_representation = get_representation_by_id(project_name, + prev_representation_id) + prev_path = os.path.normpath(prev_representation["data"]["path"]) + + path = get_representation_path(representation) + + for shape in shapes: + asset_paths = multiverse.GetUsdCompoundAssetPaths(shape) + asset_paths = [os.path.normpath(p) for p in asset_paths] + + assert asset_paths.count(prev_path) == 1, \ + "Couldn't find matching path (or too many)" + prev_path_idx = asset_paths.index(prev_path) + asset_paths[prev_path_idx] = path + multiverse.SetUsdCompoundAssetPaths(shape, asset_paths) + + cmds.setAttr("{}.representation".format(node), + str(representation["_id"]), + type="string") + mel.eval('refreshEditorTemplates;') + + def switch(self, container, representation): + self.update(container, representation) + + def remove(self, container): + # type: (dict) -> None + """Remove loaded container.""" + # Delete container and its contents + if cmds.objExists(container['objectName']): + members = cmds.sets(container['objectName'], query=True) or [] + cmds.delete([container['objectName']] + members) + + # Remove the namespace, if empty + namespace = container['namespace'] + if cmds.namespace(exists=namespace): + members = cmds.namespaceInfo(namespace, listNamespace=True) + if not members: + cmds.namespace(removeNamespace=namespace) + else: + self.log.warning("Namespace not deleted because it " + "still has members: %s", namespace) From c62527a678d97f9bf0e6be6f4ecec3d30433104b Mon Sep 17 00:00:00 2001 From: DMO Date: Mon, 31 Oct 2022 15:15:46 +0900 Subject: [PATCH 16/27] Removing bad merge. --- openpype/hosts/maya/api/lib_renderproducts.py | 58 ------------------- 1 file changed, 58 deletions(-) diff --git a/openpype/hosts/maya/api/lib_renderproducts.py b/openpype/hosts/maya/api/lib_renderproducts.py index 7b9601cda8..cd204445b7 100644 --- a/openpype/hosts/maya/api/lib_renderproducts.py +++ b/openpype/hosts/maya/api/lib_renderproducts.py @@ -77,7 +77,6 @@ IMAGE_PREFIXES = { "arnold": "defaultRenderGlobals.imageFilePrefix", "renderman": "rmanGlobals.imageFileFormat", "redshift": "defaultRenderGlobals.imageFilePrefix", - "_3delight": "defaultRenderGlobals.imageFilePrefix", "mayahardware2": "defaultRenderGlobals.imageFilePrefix" } @@ -173,7 +172,6 @@ def get(layer, render_instance=None): "redshift": RenderProductsRedshift, "renderman": RenderProductsRenderman, "mayahardware2": RenderProductsMayaHardware - "_3delight": RenderProducts3Delight }.get(renderer_name.lower(), None) if renderer is None: raise UnsupportedRendererException( @@ -1288,62 +1286,6 @@ class RenderProductsMayaHardware(ARenderProducts): for cam in self.get_renderable_cameras(): product = RenderProduct(productName="beauty", ext=ext, camera=cam) products.append(product) -class RenderProducts3Delight(ARenderProducts): - """Expected files for Renderman renderer. - - Warning: - This is very rudimentary and needs more love and testing. - """ - - renderer = "_3delight" - - def get_render_products(self): - """Get all AOVs. - - See Also: - :func:`ARenderProducts.get_render_products()` - - """ - cameras = [ - self.sanitize_camera_name(c) - for c in self.get_renderable_cameras() - ] - - if not cameras: - cameras = [ - self.sanitize_camera_name( - self.get_renderable_cameras()[0]) - ] - products = [] - - default_ext = "exr" - - nodes = cmds.listConnections( - 'dlRenderGlobals1', - type='dlRenderSettings') - assert len(nodes) == 1 - node = nodes[0] - - num_layers = cmds.getAttr( - '{}.layerOutputVariables'.format(node), - size=True) - assert num_layers > 0 - for i in range(num_layers): - output = cmds.getAttr( - '{}.layerOutput[{}]'.format(node, i)) - if not output: - continue - - output_var = cmds.getAttr( - '{}.layerOutputVariables[{}]'.format(node, i)) - output_var_tokens = output_var.split('|') - aov_name = output_var_tokens[4] - - for camera in cameras: - product = RenderProduct(productName=aov_name, - ext=default_ext, - camera=camera) - products.append(product) return products From 422a3c4f8ad9e7cbdb590d4c9f5aeb694cbcc698 Mon Sep 17 00:00:00 2001 From: DMO Date: Mon, 31 Oct 2022 15:33:50 +0900 Subject: [PATCH 17/27] Removed wrongly-staged `extract_animation.py` --- .../maya/plugins/publish/extract_animation.py | 113 ------------------ 1 file changed, 113 deletions(-) delete mode 100644 openpype/hosts/maya/plugins/publish/extract_animation.py diff --git a/openpype/hosts/maya/plugins/publish/extract_animation.py b/openpype/hosts/maya/plugins/publish/extract_animation.py deleted file mode 100644 index 3d1f30b640..0000000000 --- a/openpype/hosts/maya/plugins/publish/extract_animation.py +++ /dev/null @@ -1,113 +0,0 @@ -import os - -from maya import cmds - -import openpype.api -from openpype.hosts.maya.api.lib import ( - extract_alembic, - suspended_refresh, - maintained_selection, - iter_visible_nodes_in_range -) - - -class ExtractAnimation(openpype.api.Extractor): - """Produce an alembic of just point positions and normals. - - Positions and normals, uvs, creases are preserved, but nothing more, - for plain and predictable point caches. - - Plugin can run locally or remotely (on a farm - if instance is marked with - "farm" it will be skipped in local processing, but processed on farm) - """ - - label = "Extract Animation" - hosts = ["maya"] - families = ["animation"] - targets = ["local", "remote"] - - def process(self, instance): - if instance.data.get("farm"): - self.log.debug("Should be processed on farm, skipping.") - return - - # Collect the out set nodes - out_sets = [node for node in instance if node.endswith("out_SET")] - if len(out_sets) != 1: - raise RuntimeError("Couldn't find exactly one out_SET: " - "{0}".format(out_sets)) - out_set = out_sets[0] - roots = cmds.sets(out_set, query=True) - - # Include all descendants - nodes = roots + cmds.listRelatives(roots, - allDescendents=True, - fullPath=True) or [] - - # Collect the start and end including handles - start = instance.data["frameStartHandle"] - end = instance.data["frameEndHandle"] - - self.log.info("Extracting animation..") - dirname = self.staging_dir(instance) - - parent_dir = self.staging_dir(instance) - filename = "{name}.abc".format(**instance.data) - path = os.path.join(parent_dir, filename) - - options = { - "step": instance.data.get("step", 1.0) or 1.0, - "attr": ["cbId"], - "writeVisibility": True, - "writeCreases": True, - "uvWrite": True, - "selection": True, - "worldSpace": instance.data.get("worldSpace", True), - "writeColorSets": instance.data.get("writeColorSets", False), - "writeFaceSets": instance.data.get("writeFaceSets", False), - # 'noNormals' is the standard alembic option name. - "noNormals": not instance.data.get("writeNormals", True) - } - - if not instance.data.get("includeParentHierarchy", True): - # Set the root nodes if we don't want to include parents - # The roots are to be considered the ones that are the actual - # direct members of the set - options["root"] = roots - - if int(cmds.about(version=True)) >= 2017: - # Since Maya 2017 alembic supports multiple uv sets - write them. - options["writeUVSets"] = True - - if instance.data.get("visibleOnly", False): - # If we only want to include nodes that are visible in the frame - # range then we need to do our own check. Alembic's `visibleOnly` - # flag does not filter out those that are only hidden on some - # frames as it counts "animated" or "connected" visibilities as - # if it's always visible. - nodes = list(iter_visible_nodes_in_range(nodes, - start=start, - end=end)) - - with suspended_refresh(): - with maintained_selection(): - cmds.select(nodes, noExpand=True) - extract_alembic(file=path, - startFrame=float(start), - endFrame=float(end), - **options) - - if "representations" not in instance.data: - instance.data["representations"] = [] - - representation = { - 'name': 'abc', - 'ext': 'abc', - 'files': filename, - "stagingDir": dirname, - } - instance.data["representations"].append(representation) - - instance.context.data["cleanupFullPaths"].append(path) - - self.log.info("Extracted {} to {}".format(instance, dirname)) From 046ed724c802101ad7a4190ae7fb9f3765b907d3 Mon Sep 17 00:00:00 2001 From: DMO Date: Mon, 31 Oct 2022 15:40:25 +0900 Subject: [PATCH 18/27] Addressing valid hound concerns. --- openpype/hosts/maya/plugins/load/load_multiverse_usd.py | 2 +- .../hosts/maya/plugins/load/load_multiverse_usd_over.py | 8 ++------ .../hosts/maya/plugins/publish/collect_multiverse_look.py | 6 +++--- .../plugins/publish/validate_transform_naming_suffix.py | 4 ++-- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_multiverse_usd.py b/openpype/hosts/maya/plugins/load/load_multiverse_usd.py index 13915aa2a9..9e0d38df46 100644 --- a/openpype/hosts/maya/plugins/load/load_multiverse_usd.py +++ b/openpype/hosts/maya/plugins/load/load_multiverse_usd.py @@ -13,7 +13,7 @@ from openpype.hosts.maya.api.lib import ( unique_namespace ) from openpype.hosts.maya.api.pipeline import containerise -from openpype.client import get_representations, get_representation_by_id +from openpype.client import get_representation_by_id class MultiverseUsdLoader(load.LoaderPlugin): diff --git a/openpype/hosts/maya/plugins/load/load_multiverse_usd_over.py b/openpype/hosts/maya/plugins/load/load_multiverse_usd_over.py index 080475461b..8a25508ac2 100644 --- a/openpype/hosts/maya/plugins/load/load_multiverse_usd_over.py +++ b/openpype/hosts/maya/plugins/load/load_multiverse_usd_over.py @@ -10,12 +10,10 @@ from openpype.pipeline import ( get_representation_path ) from openpype.hosts.maya.api.lib import ( - maintained_selection, - namespaced, - unique_namespace + maintained_selection ) from openpype.hosts.maya.api.pipeline import containerise -from openpype.client import get_representations, get_representation_by_id +from openpype.client import get_representation_by_id class MultiverseUsdOverLoader(load.LoaderPlugin): @@ -38,8 +36,6 @@ class MultiverseUsdOverLoader(load.LoaderPlugin): ] def load(self, context, name=None, namespace=None, options=None): - asset = context['asset']['name'] - current_usd = cmds.ls(selection=True, type="mvUsdCompoundShape", dag=True, diff --git a/openpype/hosts/maya/plugins/publish/collect_multiverse_look.py b/openpype/hosts/maya/plugins/publish/collect_multiverse_look.py index 4c50e4df27..a7cb14855b 100644 --- a/openpype/hosts/maya/plugins/publish/collect_multiverse_look.py +++ b/openpype/hosts/maya/plugins/publish/collect_multiverse_look.py @@ -253,7 +253,7 @@ class CollectMultiverseLookData(pyblish.api.InstancePlugin): """Collect Multiverse Look Searches through the overrides finding all material overrides. From there - it extracts the shading group and then finds all texture files in the + it extracts the shading group and then finds all texture files in the shading group network. It also checks for mipmap versions of texture files and adds them to the resouces to get published. @@ -341,7 +341,7 @@ class CollectMultiverseLookData(pyblish.api.InstancePlugin): node_type = cmds.nodeType(node) self.log.debug("processing: {}/{}".format(node, node_type)) - if not node_type in NODETYPES: + if node_type not in NODETYPES: self.log.error("Unsupported file node: {}".format(node_type)) raise AssertionError("Unsupported file node") @@ -361,7 +361,7 @@ class CollectMultiverseLookData(pyblish.api.InstancePlugin): # Compare with the computed file path, e.g. the one with the # pattern in it, to generate some logging information about this # difference - # computed_attribute = "{}.computedFileTextureNamePattern".format(node) + # computed_attribute = "{}.computedFileTextureNamePattern".format(node) # noqa computed_source = cmds.getAttr(computed_fname_attrib) if source != computed_source: self.log.debug("Detected computed file pattern difference " diff --git a/openpype/hosts/maya/plugins/publish/validate_transform_naming_suffix.py b/openpype/hosts/maya/plugins/publish/validate_transform_naming_suffix.py index 4f2a400d91..65551c8d5e 100644 --- a/openpype/hosts/maya/plugins/publish/validate_transform_naming_suffix.py +++ b/openpype/hosts/maya/plugins/publish/validate_transform_naming_suffix.py @@ -48,8 +48,8 @@ class ValidateTransformNamingSuffix(pyblish.api.InstancePlugin): @classmethod def get_table_for_invalid(cls): ss = [] - for k,v in cls.SUFFIX_NAMING_TABLE.items(): - ss.append(" - {}: {}".format(k,", ".join(v))) + for k, v in cls.SUFFIX_NAMING_TABLE.items(): + ss.append(" - {}: {}".format(k, ", ".join(v))) return "\n".join(ss) @staticmethod From 843f6e8cd036452fd8f98dc88b70abdf6b06fdd3 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Thu, 22 Dec 2022 16:10:07 +0100 Subject: [PATCH 19/27] :art: improve online family functionality --- .../plugins/create/create_online.py | 31 ++++++++++++++++--- .../plugins/publish/collect_online_file.py | 8 ++++- .../plugins/publish/validate_online_file.py | 2 ++ openpype/plugins/publish/extract_thumbnail.py | 2 +- 4 files changed, 37 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/traypublisher/plugins/create/create_online.py b/openpype/hosts/traypublisher/plugins/create/create_online.py index 19f956a50e..096172d581 100644 --- a/openpype/hosts/traypublisher/plugins/create/create_online.py +++ b/openpype/hosts/traypublisher/plugins/create/create_online.py @@ -7,13 +7,14 @@ exists under selected asset. """ from pathlib import Path -from openpype.client import get_subset_by_name, get_asset_by_name -from openpype.lib.attribute_definitions import FileDef +# from openpype.client import get_subset_by_name, get_asset_by_name +from openpype.lib.attribute_definitions import FileDef, BoolDef, UILabelDef from openpype.pipeline import ( CreatedInstance, CreatorError ) from openpype.hosts.traypublisher.api.plugin import TrayPublishCreator +from typing import Union class OnlineCreator(TrayPublishCreator): @@ -23,7 +24,11 @@ class OnlineCreator(TrayPublishCreator): label = "Online" family = "online" description = "Publish file retaining its original file name" - extensions = [".mov", ".mp4", ".mxf", ".m4v", ".mpg"] + extensions = [".mov", ".mp4", ".mxf", ".m4v", ".mpg", ".exr", ".dpx", ".tif", ".png", ".jpg"] + + def __init__(self, *args, **kwargs): + super(OnlineCreator, self).__init__(*args, **kwargs) + self._original_path: Union[str, None] = None def get_detail_description(self): return """# Create file retaining its original file name. @@ -49,13 +54,17 @@ class OnlineCreator(TrayPublishCreator): origin_basename = Path(files[0]).stem + # disable check for existing subset with the same name + """ asset = get_asset_by_name( self.project_name, instance_data["asset"], fields=["_id"]) + if get_subset_by_name( self.project_name, origin_basename, asset["_id"], fields=["_id"]): raise CreatorError(f"subset with {origin_basename} already " "exists in selected asset") + """ instance_data["originalBasename"] = origin_basename subset_name = origin_basename @@ -69,15 +78,29 @@ class OnlineCreator(TrayPublishCreator): instance_data, self) self._store_new_instance(new_instance) + def get_instance_attr_defs(self): + return [ + BoolDef( + "add_review_family", + default=True, + label="Review" + ) + ] + def get_pre_create_attr_defs(self): return [ FileDef( "representation_file", folders=False, extensions=self.extensions, - allow_sequences=False, + allow_sequences=True, single_item=True, label="Representation", + ), + BoolDef( + "add_review_family", + default=True, + label="Review" ) ] diff --git a/openpype/hosts/traypublisher/plugins/publish/collect_online_file.py b/openpype/hosts/traypublisher/plugins/publish/collect_online_file.py index a3f86afa13..05b00e9516 100644 --- a/openpype/hosts/traypublisher/plugins/publish/collect_online_file.py +++ b/openpype/hosts/traypublisher/plugins/publish/collect_online_file.py @@ -12,12 +12,18 @@ class CollectOnlineFile(pyblish.api.InstancePlugin): def process(self, instance): file = Path(instance.data["creator_attributes"]["path"]) + review = instance.data["creator_attributes"]["add_review_family"] + instance.data["review"] = review + if "review" not in instance.data["families"]: + instance.data["families"].append("review") + self.log.info(f"Adding review: {review}") instance.data["representations"].append( { "name": file.suffix.lstrip("."), "ext": file.suffix.lstrip("."), "files": file.name, - "stagingDir": file.parent.as_posix() + "stagingDir": file.parent.as_posix(), + "tags": ["review"] if review else [] } ) diff --git a/openpype/hosts/traypublisher/plugins/publish/validate_online_file.py b/openpype/hosts/traypublisher/plugins/publish/validate_online_file.py index 12b2e72ced..2db865ca2b 100644 --- a/openpype/hosts/traypublisher/plugins/publish/validate_online_file.py +++ b/openpype/hosts/traypublisher/plugins/publish/validate_online_file.py @@ -20,6 +20,8 @@ class ValidateOnlineFile(OptionalPyblishPluginMixin, optional = True def process(self, instance): + if not self.is_active(instance.data): + return project_name = instance.context.data["projectName"] asset_id = instance.data["assetEntity"]["_id"] subset = get_subset_by_name( diff --git a/openpype/plugins/publish/extract_thumbnail.py b/openpype/plugins/publish/extract_thumbnail.py index 14b43beae8..14c6a21ed0 100644 --- a/openpype/plugins/publish/extract_thumbnail.py +++ b/openpype/plugins/publish/extract_thumbnail.py @@ -19,7 +19,7 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): order = pyblish.api.ExtractorOrder families = [ "imagesequence", "render", "render2d", "prerender", - "source", "clip", "take" + "source", "clip", "take", "online" ] hosts = ["shell", "fusion", "resolve", "traypublisher"] enabled = False From 64afc35cd2ea98a2c340a23c6a2a2c0158a2e216 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Thu, 22 Dec 2022 16:13:37 +0100 Subject: [PATCH 20/27] :rotating_light: some hound fixes --- .../hosts/traypublisher/plugins/create/create_online.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/traypublisher/plugins/create/create_online.py b/openpype/hosts/traypublisher/plugins/create/create_online.py index 096172d581..1a366bcff5 100644 --- a/openpype/hosts/traypublisher/plugins/create/create_online.py +++ b/openpype/hosts/traypublisher/plugins/create/create_online.py @@ -8,7 +8,7 @@ exists under selected asset. from pathlib import Path # from openpype.client import get_subset_by_name, get_asset_by_name -from openpype.lib.attribute_definitions import FileDef, BoolDef, UILabelDef +from openpype.lib.attribute_definitions import FileDef, BoolDef from openpype.pipeline import ( CreatedInstance, CreatorError @@ -24,7 +24,8 @@ class OnlineCreator(TrayPublishCreator): label = "Online" family = "online" description = "Publish file retaining its original file name" - extensions = [".mov", ".mp4", ".mxf", ".m4v", ".mpg", ".exr", ".dpx", ".tif", ".png", ".jpg"] + extensions = [".mov", ".mp4", ".mxf", ".m4v", ".mpg", ".exr", + ".dpx", ".tif", ".png", ".jpg"] def __init__(self, *args, **kwargs): super(OnlineCreator, self).__init__(*args, **kwargs) @@ -58,7 +59,7 @@ class OnlineCreator(TrayPublishCreator): """ asset = get_asset_by_name( self.project_name, instance_data["asset"], fields=["_id"]) - + if get_subset_by_name( self.project_name, origin_basename, asset["_id"], fields=["_id"]): From e37bff0eb5389c99d7e1e9ba04853a398ce73d8b Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Mon, 9 Jan 2023 12:44:21 +0100 Subject: [PATCH 21/27] :recycle: remove unused method --- openpype/hosts/traypublisher/plugins/create/create_online.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/openpype/hosts/traypublisher/plugins/create/create_online.py b/openpype/hosts/traypublisher/plugins/create/create_online.py index 1a366bcff5..c751801340 100644 --- a/openpype/hosts/traypublisher/plugins/create/create_online.py +++ b/openpype/hosts/traypublisher/plugins/create/create_online.py @@ -27,10 +27,6 @@ class OnlineCreator(TrayPublishCreator): extensions = [".mov", ".mp4", ".mxf", ".m4v", ".mpg", ".exr", ".dpx", ".tif", ".png", ".jpg"] - def __init__(self, *args, **kwargs): - super(OnlineCreator, self).__init__(*args, **kwargs) - self._original_path: Union[str, None] = None - def get_detail_description(self): return """# Create file retaining its original file name. From ba2646580916fca6224c7b72c1a43ee776bf3dd5 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Mon, 9 Jan 2023 12:45:05 +0100 Subject: [PATCH 22/27] :recycle: remove unused import --- openpype/hosts/traypublisher/plugins/create/create_online.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/traypublisher/plugins/create/create_online.py b/openpype/hosts/traypublisher/plugins/create/create_online.py index c751801340..199fae6d2c 100644 --- a/openpype/hosts/traypublisher/plugins/create/create_online.py +++ b/openpype/hosts/traypublisher/plugins/create/create_online.py @@ -14,7 +14,6 @@ from openpype.pipeline import ( CreatorError ) from openpype.hosts.traypublisher.api.plugin import TrayPublishCreator -from typing import Union class OnlineCreator(TrayPublishCreator): From 6cfab9bd312ede9aa08cece6fffa0bb0e03c74f3 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 9 Jan 2023 17:12:46 +0100 Subject: [PATCH 23/27] OP-4706 - add vn tag for ffmpeg This tag ignores any graphic data from secondary audio input. This solves an issue where audio stream should be used from mp4/mov. Without it ffmpeg uses graphical data from this input too. --- openpype/plugins/publish/extract_review.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index 9310923a9f..dcb43d7fa2 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -1038,6 +1038,9 @@ class ExtractReview(pyblish.api.InstancePlugin): # Set audio duration audio_in_args.append("-to {:0.10f}".format(audio_duration)) + # Ignore video data from audio input + audio_in_args.append("-vn") + # Add audio input path audio_in_args.append("-i {}".format( path_to_subprocess_arg(audio["filename"]) From 48d43f7fea8817af9bfbf906be451cff2d562b5a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 10 Jan 2023 18:16:31 +0100 Subject: [PATCH 24/27] OP-4679 - check comment on context Check for context must be preserved until old Pyblish is completely eradicated as comment could be filled after collection phase, therefore not caught by collector. --- .../modules/ftrack/plugins/publish/integrate_ftrack_note.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_note.py b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_note.py index 6776509dda..2aecd97591 100644 --- a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_note.py +++ b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_note.py @@ -45,7 +45,8 @@ class IntegrateFtrackNote(pyblish.api.InstancePlugin): host_name = context.data["hostName"] app_name = context.data["appName"] app_label = context.data["appLabel"] - comment = instance.data["comment"] + # context comment is fallback until old Pyblish is removed + comment = instance.data["comment"] or context.data.get("comment") if not comment: self.log.info("Comment is not set.") else: From 59e2bd2bdf0c738aca7fcfdd76473753c16c97b0 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 10 Jan 2023 18:17:15 +0100 Subject: [PATCH 25/27] OP-4679 - updated logging Printing what is actually missing is more helpful than only full data. --- .../modules/ftrack/plugins/publish/integrate_ftrack_note.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_note.py b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_note.py index 2aecd97591..994df304ad 100644 --- a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_note.py +++ b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_note.py @@ -130,8 +130,8 @@ class IntegrateFtrackNote(pyblish.api.InstancePlugin): if not note_text.solved: self.log.warning(( "Note template require more keys then can be provided." - "\nTemplate: {}\nData: {}" - ).format(template, format_data)) + "\nTemplate: {}\nMissing values for keys:{}\nData: {}" + ).format(template, note_text.missing_keys, format_data)) continue if not note_text: From 12e29805249bdb31a77c8c9249e2f96c0df56080 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 11 Jan 2023 15:03:55 +0100 Subject: [PATCH 26/27] OP-4679 - push adding comment to instance to Extractor phase Pyblish allows modifying comment after collect phase, eg. collector wouldn't collect it. Should be pushed back to Collect phase after Pyblish is eradicated. --- openpype/plugins/publish/collect_comment.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/plugins/publish/collect_comment.py b/openpype/plugins/publish/collect_comment.py index 12579cd957..5be04731ac 100644 --- a/openpype/plugins/publish/collect_comment.py +++ b/openpype/plugins/publish/collect_comment.py @@ -73,7 +73,9 @@ class CollectComment( """ label = "Collect Instance Comment" - order = pyblish.api.CollectorOrder + 0.49 + # TODO change to CollectorOrder after Pyblish is purged + # Pyblish allows modifying comment after collect phase + order = pyblish.api.ExtractorOrder - 0.49 def process(self, context): context_comment = self.cleanup_comment(context.data.get("comment")) From f854de46295da4d96c007335421647f78b2e6b36 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 11 Jan 2023 15:06:03 +0100 Subject: [PATCH 27/27] OP-4679 - revert back fallback Availability of comment on instance has been resolved by bumping up order of CollectComment. --- .../modules/ftrack/plugins/publish/integrate_ftrack_note.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_note.py b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_note.py index 994df304ad..6e82897d89 100644 --- a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_note.py +++ b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_note.py @@ -45,8 +45,7 @@ class IntegrateFtrackNote(pyblish.api.InstancePlugin): host_name = context.data["hostName"] app_name = context.data["appName"] app_label = context.data["appLabel"] - # context comment is fallback until old Pyblish is removed - comment = instance.data["comment"] or context.data.get("comment") + comment = instance.data["comment"] if not comment: self.log.info("Comment is not set.") else: