From 1f08a2734339ae7c40424df778d7402807beed10 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 30 Jan 2023 10:15:04 +0000 Subject: [PATCH] Workaround for motion blur --- .../plugins/publish/extract_workfile_xgen.py | 147 +++++++++++++----- .../maya/plugins/publish/extract_xgen.py | 62 ++++---- 2 files changed, 146 insertions(+), 63 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py b/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py index d37a03d1f6..5847563e5b 100644 --- a/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py +++ b/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py @@ -1,11 +1,14 @@ import os import shutil +import copy from maya import cmds import pyblish.api from openpype.hosts.maya.api import current_file +from openpype.hosts.maya.api.lib import extract_alembic from openpype.pipeline import publish +from openpype.lib import StringTemplate class ExtractWorkfileXgen(publish.Extractor): @@ -18,9 +21,22 @@ class ExtractWorkfileXgen(publish.Extractor): hosts = ["maya"] def process(self, instance): + transfers = [] + + # Validate there is any palettes in the scene. + if not cmds.ls(type="xgmPalette"): + self.log.debug( + "No collections found in the scene. Abort Xgen extraction." + ) + return + else: + import xgenm + # Validate to extract only when we are publishing a renderlayer as # well. renderlayer = False + start_frame = None + end_frame = None for i in instance.context: is_renderlayer = ( "renderlayer" in i.data.get("families", []) or @@ -28,6 +44,17 @@ class ExtractWorkfileXgen(publish.Extractor): ) if is_renderlayer and i.data["publish"]: renderlayer = True + + if start_frame is None: + start_frame = i.data["frameStart"] + if end_frame is None: + end_frame = i.data["frameEnd"] + + if i.data["frameStart"] < start_frame: + start_frame = i.data["frameStart"] + if i.data["frameEnd"] > end_frame: + end_frame = i.data["frameEnd"] + break if not renderlayer: @@ -37,6 +64,51 @@ class ExtractWorkfileXgen(publish.Extractor): ) return + # We decrement start frame and increment end frame so motion blur will + # render correctly. + start_frame -= 1 + end_frame += 1 + + # Extract patches alembic. + basename, _ = os.path.splitext(current_file()) + dirname = os.path.dirname(current_file()) + kwargs = {"attrPrefix": ["xgen"], "stripNamespaces": True} + alembic_files = [] + for palette in cmds.ls(type="xgmPalette"): + patch_names = [] + for description in xgenm.descriptions(palette): + for name in xgenm.boundGeometry(palette, description): + patch_names.append(name) + + alembic_file = os.path.join( + dirname, + "{}__{}.abc".format(basename, palette.replace(":", "__ns__")) + ) + extract_alembic( + alembic_file, + root=patch_names, + selection=False, + startFrame=float(start_frame), + endFrame=float(end_frame), + verbose=True, + **kwargs + ) + alembic_files.append(alembic_file) + + template_data = copy.deepcopy(instance.data["anatomyData"]) + published_maya_path = StringTemplate( + instance.context.data["anatomy"].templates["publish"]["file"] + ).format(template_data) + published_basename, _ = os.path.splitext(published_maya_path) + + for source in alembic_files: + destination = os.path.join( + os.path.dirname(instance.data["resourcesDir"]), + os.path.basename(source.replace(basename, published_basename)) + ) + transfers.append((source, destination)) + + # Validate that we are using the published workfile. deadline_settings = instance.context.get("deadline") if deadline_settings: publish_settings = deadline_settings["publish"] @@ -76,8 +148,9 @@ class ExtractWorkfileXgen(publish.Extractor): with open(destination, "r") as f: for line in [line.rstrip() for line in f]: if line.startswith("\txgProjectPath"): + path = os.path.dirname(instance.data["resourcesDir"]) line = "\txgProjectPath\t\t{}/".format( - instance.data["resourcesDir"].replace("\\", "/") + path.replace("\\", "/") ) lines.append(line) @@ -88,31 +161,30 @@ class ExtractWorkfileXgen(publish.Extractor): sources.append(destination) # Add resource files to workfile instance. - transfers = [] for source in sources: basename = os.path.basename(source) - destination = os.path.join(instance.data["resourcesDir"], basename) + destination = os.path.join( + os.path.dirname(instance.data["resourcesDir"]), basename + ) transfers.append((source, destination)) - import xgenm + destination_dir = os.path.join( + instance.data["resourcesDir"], "collections" + ) for palette in cmds.ls(type="xgmPalette"): - relative_data_path = xgenm.getAttr( - "xgDataPath", palette.replace("|", "") - ).split(os.pathsep)[0] - absolute_data_path = relative_data_path.replace( - "${PROJECT}", - xgenm.getAttr("xgProjectPath", palette.replace("|", "")) - ) - - for root, _, files in os.walk(absolute_data_path): - for file in files: - source = os.path.join(root, file).replace("\\", "/") - destination = os.path.join( - instance.data["resourcesDir"], - relative_data_path.replace("${PROJECT}", ""), - source.replace(absolute_data_path, "")[1:] - ) - transfers.append((source, destination.replace("\\", "/"))) + project_path = xgenm.getAttr("xgProjectPath", palette) + data_path = xgenm.getAttr("xgDataPath", palette) + data_path = data_path.replace("${PROJECT}", project_path) + for path in data_path.split(os.pathsep): + for root, _, files in os.walk(path): + for f in files: + source = os.path.join(root, f) + destination = "{}/{}{}".format( + destination_dir, + palette.replace(":", "__ns__"), + source.replace(path, "") + ) + transfers.append((source, destination)) for source, destination in transfers: self.log.debug("Transfer: {} > {}".format(source, destination)) @@ -120,21 +192,26 @@ class ExtractWorkfileXgen(publish.Extractor): instance.data["transfers"] = transfers # Set palette attributes in preparation for workfile publish. - attrs = ["xgFileName", "xgBaseFile"] + attrs = {"xgFileName": None, "xgBaseFile": ""} data = {} for palette in cmds.ls(type="xgmPalette"): - for attr in attrs: - value = cmds.getAttr(palette + "." + attr) - if value: - new_value = "resources/{}".format(value) - node_attr = "{}.{}".format(palette, attr) - self.log.info( - "Setting \"{}\" on \"{}\"".format(new_value, node_attr) - ) - cmds.setAttr(node_attr, new_value, type="string") - try: - data[palette][attr] = value - except KeyError: - data[palette] = {attr: value} + attrs["xgFileName"] = "resources/{}.xgen".format( + palette.replace(":", "__ns__") + ) + for attr, value in attrs.items(): + node_attr = palette + "." + attr + + old_value = cmds.getAttr(node_attr) + try: + data[palette][attr] = old_value + except KeyError: + data[palette] = {attr: old_value} + + cmds.setAttr(node_attr, value, type="string") + self.log.info( + "Setting \"{}\" on \"{}\"".format(value, node_attr) + ) + + cmds.setAttr(palette + "." + "xgExportAsDelta", False) instance.data["xgenAttributes"] = data diff --git a/openpype/hosts/maya/plugins/publish/extract_xgen.py b/openpype/hosts/maya/plugins/publish/extract_xgen.py index 80b62275cd..fd85cadcac 100644 --- a/openpype/hosts/maya/plugins/publish/extract_xgen.py +++ b/openpype/hosts/maya/plugins/publish/extract_xgen.py @@ -1,5 +1,6 @@ import os import copy +import tempfile from maya import cmds import xgenm @@ -16,6 +17,7 @@ class ExtractXgenCache(publish.Extractor): - Duplicate nodes used for patches. - Export palette and import onto duplicate nodes. - Export/Publish duplicate nodes and palette. + - Export duplicate palette to .xgen file and add to publish. - Publish all xgen files as resources. """ @@ -32,29 +34,6 @@ class ExtractXgenCache(publish.Extractor): maya_filename = "{}.{}".format(instance.data["name"], self.scene_type) maya_filepath = os.path.join(staging_dir, maya_filename) - # Get published xgen file name. - template_data = copy.deepcopy(instance.data["anatomyData"]) - template_data.update({"ext": "xgen"}) - templates = instance.context.data["anatomy"].templates["publish"] - xgen_filename = StringTemplate(templates["file"]).format(template_data) - name = instance.data["xgmPalette"].replace(":", "__").replace("|", "") - xgen_filename = xgen_filename.replace(".xgen", "__" + name + ".xgen") - - # Export xgen palette files. - xgen_path = os.path.join(staging_dir, xgen_filename).replace("\\", "/") - xgenm.exportPalette( - instance.data["xgmPalette"].replace("|", ""), xgen_path - ) - self.log.info("Extracted to {}".format(xgen_path)) - - representation = { - "name": name, - "ext": "xgen", - "files": xgen_filename, - "stagingDir": staging_dir, - } - instance.data["representations"].append(representation) - # Collect nodes to export. duplicate_nodes = [] for node, connections in instance.data["xgenConnections"].items(): @@ -74,17 +53,43 @@ class ExtractXgenCache(publish.Extractor): duplicate_nodes.append(duplicate_transform) + # Export temp xgen palette files. + temp_xgen_path = os.path.join( + tempfile.gettempdir(), "temp.xgen" + ).replace("\\", "/") + xgenm.exportPalette( + instance.data["xgmPalette"].replace("|", ""), temp_xgen_path + ) + self.log.info("Extracted to {}".format(temp_xgen_path)) + # Import xgen onto the duplicate. with maintained_selection(): cmds.select(duplicate_nodes) - palette = xgenm.importPalette(xgen_path, []) + palette = xgenm.importPalette(temp_xgen_path, []) - attribute_data = { - "{}.xgFileName".format(palette): xgen_filename + # Get published xgen file name. + template_data = copy.deepcopy(instance.data["anatomyData"]) + template_data.update({"ext": "xgen"}) + templates = instance.context.data["anatomy"].templates["publish"] + xgen_filename = StringTemplate(templates["file"]).format(template_data) + + # Export duplicated palette. + xgen_path = os.path.join(staging_dir, xgen_filename).replace("\\", "/") + xgenm.exportPalette(palette, xgen_path) + + representation = { + "name": "xgen", + "ext": "xgen", + "files": xgen_filename, + "stagingDir": staging_dir, } + instance.data["representations"].append(representation) # Export Maya file. type = "mayaAscii" if self.scene_type == "ma" else "mayaBinary" + attribute_data = { + "{}.xgFileName".format(palette): xgen_filename + } with attribute_values(attribute_data): with maintained_selection(): cmds.select(duplicate_nodes + [palette]) @@ -106,12 +111,13 @@ class ExtractXgenCache(publish.Extractor): "name": self.scene_type, "ext": self.scene_type, "files": maya_filename, - "stagingDir": staging_dir, - "data": {"xgenName": palette} + "stagingDir": staging_dir } instance.data["representations"].append(representation) + # Clean up. cmds.delete(duplicate_nodes + [palette]) + os.remove(temp_xgen_path) # Collect all files under palette root as resources. data_path = xgenm.getAttr(