From 90d80dcfabeb758eec9ffa212deac9a1c68815cb Mon Sep 17 00:00:00 2001 From: David Lai Date: Sun, 22 Aug 2021 04:36:51 +0800 Subject: [PATCH 01/10] allow publishing model with render sets --- .../hosts/maya/plugins/load/load_reference.py | 12 + .../maya/plugins/publish/collect_look.py | 71 ++++ .../maya/plugins/publish/extract_look.py | 340 +++++++++++++----- 3 files changed, 326 insertions(+), 97 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_reference.py b/openpype/hosts/maya/plugins/load/load_reference.py index 96269f2771..77c9f28d10 100644 --- a/openpype/hosts/maya/plugins/load/load_reference.py +++ b/openpype/hosts/maya/plugins/load/load_reference.py @@ -152,3 +152,15 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): options={"useSelection": True}, data={"dependencies": dependency} ) + + +class AugmentedModelLoader(ReferenceLoader): + """Load augmented model via Maya referencing""" + + families = ["model"] + representations = ["fried.ma", "fried.mb"] + + label = "Fried Model" + order = -9 + icon = "code-fork" + color = "yellow" diff --git a/openpype/hosts/maya/plugins/publish/collect_look.py b/openpype/hosts/maya/plugins/publish/collect_look.py index 0dde52447d..fa48874f0e 100644 --- a/openpype/hosts/maya/plugins/publish/collect_look.py +++ b/openpype/hosts/maya/plugins/publish/collect_look.py @@ -357,6 +357,17 @@ class CollectLook(pyblish.api.InstancePlugin): for vray_node in vray_plugin_nodes: history.extend(cmds.listHistory(vray_node)) + # handling render attribute sets + render_set_types = [ + "VRayDisplacement", + ] + render_sets = cmds.ls(look_sets, type=render_set_types) + if render_sets: + history.extend( + cmds.listHistory(render_sets, future=False, pruneDagObjects=True) + or [] + ) + files = cmds.ls(history, type="file", long=True) files.extend(cmds.ls(history, type="aiImage", long=True)) files.extend(cmds.ls(history, type="RedshiftNormalMap", long=True)) @@ -550,3 +561,63 @@ class CollectLook(pyblish.api.InstancePlugin): "source": source, # required for resources "files": files, "color_space": color_space} # required for resources + + +class CollectModelRenderSets(CollectLook): + """Collect render attribute sets for model instance. + + This enables additional render attributes be published with model. + + """ + + order = pyblish.api.CollectorOrder + 0.21 + families = ["model"] + label = "Collect Model Render Sets" + hosts = ["maya"] + maketx = True + + def process(self, instance): + """Collect the Look in the instance with the correct layer settings""" + model_nodes = instance[:] + + with lib.renderlayer(instance.data.get("renderlayer", "defaultRenderLayer")): + self.collect(instance) + + set_nodes = [m for m in instance if m not in model_nodes] + instance[:] = model_nodes + + if set_nodes: + instance.data["modelRenderSets"] = set_nodes + instance.data["modelRenderSetsHistory"] = \ + cmds.listHistory(set_nodes, future=False, pruneDagObjects=True) + + self.log.info("Model render sets collected.") + else: + self.log.info("No model render sets.") + + def collect_sets(self, instance): + """Collect all related objectSets except shadingEngines + + Args: + instance (list): all nodes to be published + + Returns: + dict + """ + + sets = {} + for node in instance: + related_sets = lib.get_related_sets(node) + if not related_sets: + continue + + for objset in related_sets: + if objset in sets: + continue + + if "shadingEngine" in cmds.nodeType(objset, inherited=True): + continue + + sets[objset] = {"uuid": lib.get_id(objset), "members": list()} + + return sets diff --git a/openpype/hosts/maya/plugins/publish/extract_look.py b/openpype/hosts/maya/plugins/publish/extract_look.py index f09d50d714..8ede62d84f 100644 --- a/openpype/hosts/maya/plugins/publish/extract_look.py +++ b/openpype/hosts/maya/plugins/publish/extract_look.py @@ -201,103 +201,11 @@ class ExtractLook(openpype.api.Extractor): relationships = lookdata["relationships"] sets = relationships.keys() - # Extract the textures to transfer, possibly convert with maketx and - # remap the node paths to the destination path. Note that a source - # might be included more than once amongst the resources as they could - # be the input file to multiple nodes. - resources = instance.data["resources"] - do_maketx = instance.data.get("maketx", False) - - # Collect all unique files used in the resources - files = set() - files_metadata = {} - for resource in resources: - # Preserve color space values (force value after filepath change) - # This will also trigger in the same order at end of context to - # ensure after context it's still the original value. - color_space = resource.get("color_space") - - for f in resource["files"]: - - files_metadata[os.path.normpath(f)] = { - "color_space": color_space} - # files.update(os.path.normpath(f)) - - # Process the resource files - transfers = [] - hardlinks = [] - hashes = {} - force_copy = instance.data.get("forceCopy", False) - - self.log.info(files) - for filepath in files_metadata: - - linearize = False - if do_maketx and files_metadata[filepath]["color_space"].lower() == "srgb": # noqa: E501 - linearize = True - # set its file node to 'raw' as tx will be linearized - files_metadata[filepath]["color_space"] = "raw" - - if do_maketx: - color_space = "raw" - - source, mode, texture_hash = self._process_texture( - filepath, - do_maketx, - staging=dir_path, - linearize=linearize, - force=force_copy - ) - destination = self.resource_destination(instance, - source, - do_maketx) - - # Force copy is specified. - if force_copy: - mode = COPY - - if mode == COPY: - transfers.append((source, destination)) - self.log.info('copying') - elif mode == HARDLINK: - hardlinks.append((source, destination)) - self.log.info('hardlinking') - - # Store the hashes from hash to destination to include in the - # database - hashes[texture_hash] = destination - - # Remap the resources to the destination path (change node attributes) - destinations = {} - remap = OrderedDict() # needs to be ordered, see color space values - for resource in resources: - source = os.path.normpath(resource["source"]) - if source not in destinations: - # Cache destination as source resource might be included - # multiple times - destinations[source] = self.resource_destination( - instance, source, do_maketx - ) - - # Preserve color space values (force value after filepath change) - # This will also trigger in the same order at end of context to - # ensure after context it's still the original value. - color_space_attr = resource["node"] + ".colorSpace" - try: - color_space = cmds.getAttr(color_space_attr) - except ValueError: - # node doesn't have color space attribute - color_space = "raw" - else: - if files_metadata[source]["color_space"] == "raw": - # set color space to raw if we linearized it - color_space = "raw" - # Remap file node filename to destination - remap[color_space_attr] = color_space - attr = resource["attribute"] - remap[attr] = destinations[source] - - self.log.info("Finished remapping destinations ...") + results = self.process_resources(instance, staging_dir=dir_path) + transfers = results["fileTransfers"] + hardlinks = results["fileHardlinks"] + hashes = results["fileHashes"] + remap = results["attrRemap"] # Extract in correct render layer layer = instance.data.get("renderlayer", "defaultRenderLayer") @@ -378,6 +286,112 @@ class ExtractLook(openpype.api.Extractor): self.log.info("Extracted instance '%s' to: %s" % (instance.name, maya_path)) + def process_resources(self, instance, staging_dir): + + # Extract the textures to transfer, possibly convert with maketx and + # remap the node paths to the destination path. Note that a source + # might be included more than once amongst the resources as they could + # be the input file to multiple nodes. + resources = instance.data["resources"] + do_maketx = instance.data.get("maketx", False) + + # Collect all unique files used in the resources + files = set() + files_metadata = {} + for resource in resources: + # Preserve color space values (force value after filepath change) + # This will also trigger in the same order at end of context to + # ensure after context it's still the original value. + color_space = resource.get("color_space") + + for f in resource["files"]: + files_metadata[os.path.normpath(f)] = { + "color_space": color_space} + # files.update(os.path.normpath(f)) + + # Process the resource files + transfers = [] + hardlinks = [] + hashes = {} + force_copy = instance.data.get("forceCopy", False) + + self.log.info(files) + for filepath in files_metadata: + + linearize = False + if do_maketx and files_metadata[filepath]["color_space"].lower() == "srgb": # noqa: E501 + linearize = True + # set its file node to 'raw' as tx will be linearized + files_metadata[filepath]["color_space"] = "raw" + + if do_maketx: + color_space = "raw" + + source, mode, texture_hash = self._process_texture( + filepath, + do_maketx, + staging=staging_dir, + linearize=linearize, + force=force_copy + ) + destination = self.resource_destination(instance, + source, + do_maketx) + + # Force copy is specified. + if force_copy: + mode = COPY + + if mode == COPY: + transfers.append((source, destination)) + self.log.info('copying') + elif mode == HARDLINK: + hardlinks.append((source, destination)) + self.log.info('hardlinking') + + # Store the hashes from hash to destination to include in the + # database + hashes[texture_hash] = destination + + # Remap the resources to the destination path (change node attributes) + destinations = {} + remap = OrderedDict() # needs to be ordered, see color space values + for resource in resources: + source = os.path.normpath(resource["source"]) + if source not in destinations: + # Cache destination as source resource might be included + # multiple times + destinations[source] = self.resource_destination( + instance, source, do_maketx + ) + + # Preserve color space values (force value after filepath change) + # This will also trigger in the same order at end of context to + # ensure after context it's still the original value. + color_space_attr = resource["node"] + ".colorSpace" + try: + color_space = cmds.getAttr(color_space_attr) + except ValueError: + # node doesn't have color space attribute + color_space = "raw" + else: + if files_metadata[source]["color_space"] == "raw": + # set color space to raw if we linearized it + color_space = "raw" + # Remap file node filename to destination + remap[color_space_attr] = color_space + attr = resource["attribute"] + remap[attr] = destinations[source] + + self.log.info("Finished remapping destinations ...") + + return { + "fileTransfers": transfers, + "fileHardlinks": hardlinks, + "fileHashes": hashes, + "attrRemap": remap, + } + def resource_destination(self, instance, filepath, do_maketx): """Get resource destination path. @@ -467,3 +481,135 @@ class ExtractLook(openpype.api.Extractor): return converted, COPY, texture_hash return filepath, COPY, texture_hash + + +class ExtractAugmentedModel(ExtractLook): + """Extract as Augmented Model (Maya Scene). + + Rendering attrs augmented model. + + Only extracts contents based on the original "setMembers" data to ensure + publishing the least amount of required shapes. From that it only takes + the shapes that are not intermediateObjects + + During export it sets a temporary context to perform a clean extraction. + The context ensures: + - Smooth preview is turned off for the geometry + - Default shader is assigned (no materials are exported) + - Remove display layers + + """ + + label = "Augmented Model (Maya Scene)" + hosts = ["maya"] + families = ["model"] + scene_type = "ma" + augmented = "fried" + + def process(self, instance): + """Plugin entry point. + + Args: + instance: Instance to process. + + """ + render_sets = instance.data.get("modelRenderSetsHistory") + if not render_sets: + self.log.info("Model is not render augmented, skip extraction.") + return + + ext_mapping = ( + instance.context.data["project_settings"]["maya"]["ext_mapping"] + ) + if ext_mapping: + self.log.info("Looking in settings for scene type ...") + # use extension mapping for first family found + for family in self.families: + try: + self.scene_type = ext_mapping[family] + self.log.info( + "Using {} as scene type".format(self.scene_type)) + break + except KeyError: + # no preset found + pass + + if "representations" not in instance.data: + instance.data["representations"] = [] + + # Define extract output file path + stagingdir = self.staging_dir(instance) + ext = "{0}.{1}".format(self.augmented, self.scene_type) + filename = "{0}.{1}".format(instance.name, ext) + path = os.path.join(stagingdir, filename) + + # Perform extraction + self.log.info("Performing extraction ...") + + results = self.process_resources(instance, staging_dir=stagingdir) + transfers = results["fileTransfers"] + hardlinks = results["fileHardlinks"] + hashes = results["fileHashes"] + remap = results["attrRemap"] + + self.log.info(remap) + + # Get only the shape contents we need in such a way that we avoid + # taking along intermediateObjects + members = instance.data("setMembers") + members = cmds.ls(members, + dag=True, + shapes=True, + type=("mesh", "nurbsCurve"), + noIntermediate=True, + long=True) + members += instance.data.get("modelRenderSetsHistory") + + with lib.no_display_layers(instance): + with lib.displaySmoothness(members, + divisionsU=0, + divisionsV=0, + pointsWire=4, + pointsShaded=1, + polygonObject=1): + with lib.shader(members, + shadingEngine="initialShadingGroup"): + # To avoid Maya trying to automatically remap the file + # textures relative to the `workspace -directory` we force + # it to a fake temporary workspace. This fixes textures + # getting incorrectly remapped. (LKD-17, PLN-101) + with no_workspace_dir(): + with lib.attribute_values(remap): + with avalon.maya.maintained_selection(): + + cmds.select(members, noExpand=True) + cmds.file(path, + force=True, + typ="mayaAscii" if self.scene_type == "ma" else "mayaBinary", # noqa: E501 + exportSelected=True, + preserveReferences=False, + channels=False, + constraints=False, + expressions=False, + constructionHistory=False) + + if "hardlinks" not in instance.data: + instance.data["hardlinks"] = [] + if "transfers" not in instance.data: + instance.data["transfers"] = [] + + # Set up the resources transfers/links for the integrator + instance.data["transfers"].extend(transfers) + instance.data["hardlinks"].extend(hardlinks) + + # Source hash for the textures + instance.data["sourceHashes"] = hashes + + instance.data["representations"].append({ + 'name': ext, + 'ext': ext, + 'files': filename, + "stagingDir": stagingdir, + }) + + self.log.info("Extracted instance '%s' to: %s" % (instance.name, path)) From d9b5242dc177554527043f91e1d018d7ae5fcf7e Mon Sep 17 00:00:00 2001 From: David Lai Date: Tue, 24 Aug 2021 01:49:46 +0800 Subject: [PATCH 02/10] fix linter --- openpype/hosts/maya/plugins/publish/collect_look.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/collect_look.py b/openpype/hosts/maya/plugins/publish/collect_look.py index fa48874f0e..d0c5ff203c 100644 --- a/openpype/hosts/maya/plugins/publish/collect_look.py +++ b/openpype/hosts/maya/plugins/publish/collect_look.py @@ -364,7 +364,9 @@ class CollectLook(pyblish.api.InstancePlugin): render_sets = cmds.ls(look_sets, type=render_set_types) if render_sets: history.extend( - cmds.listHistory(render_sets, future=False, pruneDagObjects=True) + cmds.listHistory(render_sets, + future=False, + pruneDagObjects=True) or [] ) @@ -579,8 +581,9 @@ class CollectModelRenderSets(CollectLook): def process(self, instance): """Collect the Look in the instance with the correct layer settings""" model_nodes = instance[:] + renderlayer = instance.data.get("renderlayer", "defaultRenderLayer") - with lib.renderlayer(instance.data.get("renderlayer", "defaultRenderLayer")): + with lib.renderlayer(renderlayer): self.collect(instance) set_nodes = [m for m in instance if m not in model_nodes] From 6b276015e8af8a7c5cb15458d9ea689d66d35bd3 Mon Sep 17 00:00:00 2001 From: David Lai Date: Tue, 24 Aug 2021 01:57:34 +0800 Subject: [PATCH 03/10] update doc string --- openpype/hosts/maya/plugins/publish/collect_look.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/collect_look.py b/openpype/hosts/maya/plugins/publish/collect_look.py index d0c5ff203c..ecc89e9032 100644 --- a/openpype/hosts/maya/plugins/publish/collect_look.py +++ b/openpype/hosts/maya/plugins/publish/collect_look.py @@ -568,7 +568,8 @@ class CollectLook(pyblish.api.InstancePlugin): class CollectModelRenderSets(CollectLook): """Collect render attribute sets for model instance. - This enables additional render attributes be published with model. + Collects additional render attribute sets so they can be + published with model. """ From 9f11165e87153d61612e824d50175bfdd2b0c732 Mon Sep 17 00:00:00 2001 From: David Lai Date: Tue, 24 Aug 2021 02:00:28 +0800 Subject: [PATCH 04/10] remove duplicated code --- .../hosts/maya/plugins/publish/extract_look.py | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_look.py b/openpype/hosts/maya/plugins/publish/extract_look.py index 8ede62d84f..121c99fc47 100644 --- a/openpype/hosts/maya/plugins/publish/extract_look.py +++ b/openpype/hosts/maya/plugins/publish/extract_look.py @@ -518,21 +518,7 @@ class ExtractAugmentedModel(ExtractLook): self.log.info("Model is not render augmented, skip extraction.") return - ext_mapping = ( - instance.context.data["project_settings"]["maya"]["ext_mapping"] - ) - if ext_mapping: - self.log.info("Looking in settings for scene type ...") - # use extension mapping for first family found - for family in self.families: - try: - self.scene_type = ext_mapping[family] - self.log.info( - "Using {} as scene type".format(self.scene_type)) - break - except KeyError: - # no preset found - pass + self.get_maya_scene_type(instance) if "representations" not in instance.data: instance.data["representations"] = [] From 7eed3da7c3c2d525a5dc5e5784749c05b8e7a4bd Mon Sep 17 00:00:00 2001 From: David Lai Date: Wed, 8 Sep 2021 05:57:12 +0800 Subject: [PATCH 05/10] refactor, publishing render sets as model metadata Instead of loading whole render(augmented) model from Loader, *import* those render sets from scene inventory as model's metadata which allow lookDev artist to modify it and optionally publish a look from there. --- openpype/hosts/maya/api/__init__.py | 2 + .../plugins/inventory/import_modelrender.py | 85 +++++++++++ .../hosts/maya/plugins/load/load_reference.py | 12 -- .../maya/plugins/publish/collect_look.py | 24 +--- .../maya/plugins/publish/extract_look.py | 133 +++--------------- 5 files changed, 106 insertions(+), 150 deletions(-) create mode 100644 openpype/hosts/maya/plugins/inventory/import_modelrender.py diff --git a/openpype/hosts/maya/api/__init__.py b/openpype/hosts/maya/api/__init__.py index 9219da407f..b48027ddba 100644 --- a/openpype/hosts/maya/api/__init__.py +++ b/openpype/hosts/maya/api/__init__.py @@ -35,6 +35,7 @@ def install(): pyblish.register_plugin_path(PUBLISH_PATH) avalon.register_plugin_path(avalon.Loader, LOAD_PATH) avalon.register_plugin_path(avalon.Creator, CREATE_PATH) + avalon.register_plugin_path(avalon.InventoryAction, INVENTORY_PATH) log.info(PUBLISH_PATH) menu.install() @@ -97,6 +98,7 @@ def uninstall(): pyblish.deregister_plugin_path(PUBLISH_PATH) avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH) avalon.deregister_plugin_path(avalon.Creator, CREATE_PATH) + avalon.deregister_plugin_path(avalon.InventoryAction, INVENTORY_PATH) menu.uninstall() diff --git a/openpype/hosts/maya/plugins/inventory/import_modelrender.py b/openpype/hosts/maya/plugins/inventory/import_modelrender.py new file mode 100644 index 0000000000..2737901b51 --- /dev/null +++ b/openpype/hosts/maya/plugins/inventory/import_modelrender.py @@ -0,0 +1,85 @@ +from avalon import api, io + + +class ImportModelRender(api.InventoryAction): + + label = "Import Model Render Sets" + icon = "industry" + color = "#55DDAA" + + scene_type = "meta.render.ma" + look_data_type = "meta.render.json" + + @staticmethod + def is_compatible(container): + return container.get("loader") == "ReferenceLoader" \ + and container.get("name", "").startswith("model") + + def process(self, containers): + from maya import cmds + + for container in containers: + container_name = container["objectName"] + nodes = [] + for n in cmds.sets(container_name, query=True, nodesOnly=True) or []: + if cmds.nodeType(n) == "reference": + nodes += cmds.referenceQuery(n, nodes=True) + else: + nodes.append(n) + + repr_doc = io.find_one({"_id": io.ObjectId(container["representation"])}) + version_id = repr_doc["parent"] + + print("Importing render sets for model %r" % container_name) + self.assign_model_render_by_version(nodes, version_id) + + def assign_model_render_by_version(self, nodes, version_id): + """Assign nodes a specific published model render data version by id. + + This assumes the nodes correspond with the asset. + + Args: + nodes(list): nodes to assign render data to + version_id (bson.ObjectId): database id of the version of model + + Returns: + None + """ + import json + from maya import cmds + from avalon import maya, io, pipeline + from openpype.hosts.maya.api import lib + + # Get representations of shader file and relationships + look_representation = io.find_one({"type": "representation", + "parent": version_id, + "name": self.scene_type}) + + if not look_representation: + print("No model render sets for this model version..") + return + + json_representation = io.find_one({"type": "representation", + "parent": version_id, + "name": self.look_data_type}) + + context = pipeline.get_representation_context(look_representation['_id']) + maya_file = pipeline.get_representation_path_from_context(context) + + context = pipeline.get_representation_context(json_representation['_id']) + json_file = pipeline.get_representation_path_from_context(context) + + # Import the look file + with maya.maintained_selection(): + shader_nodes = cmds.file(maya_file, + i=True, # import + returnNewNodes=True) + # imprint context data + + # Load relationships + shader_relation = json_file + with open(shader_relation, "r") as f: + relationships = json.load(f) + + # Assign relationships + lib.apply_shaders(relationships, shader_nodes, nodes) diff --git a/openpype/hosts/maya/plugins/load/load_reference.py b/openpype/hosts/maya/plugins/load/load_reference.py index 77c9f28d10..96269f2771 100644 --- a/openpype/hosts/maya/plugins/load/load_reference.py +++ b/openpype/hosts/maya/plugins/load/load_reference.py @@ -152,15 +152,3 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): options={"useSelection": True}, data={"dependencies": dependency} ) - - -class AugmentedModelLoader(ReferenceLoader): - """Load augmented model via Maya referencing""" - - families = ["model"] - representations = ["fried.ma", "fried.mb"] - - label = "Fried Model" - order = -9 - icon = "code-fork" - color = "yellow" diff --git a/openpype/hosts/maya/plugins/publish/collect_look.py b/openpype/hosts/maya/plugins/publish/collect_look.py index ecc89e9032..712c7f19ff 100644 --- a/openpype/hosts/maya/plugins/publish/collect_look.py +++ b/openpype/hosts/maya/plugins/publish/collect_look.py @@ -223,8 +223,8 @@ class CollectLook(pyblish.api.InstancePlugin): def process(self, instance): """Collect the Look in the instance with the correct layer settings""" - - with lib.renderlayer(instance.data["renderlayer"]): + renderlayer = instance.data.get("renderlayer", "defaultRenderLayer") + with lib.renderlayer(renderlayer): self.collect(instance) def collect(self, instance): @@ -579,26 +579,6 @@ class CollectModelRenderSets(CollectLook): hosts = ["maya"] maketx = True - def process(self, instance): - """Collect the Look in the instance with the correct layer settings""" - model_nodes = instance[:] - renderlayer = instance.data.get("renderlayer", "defaultRenderLayer") - - with lib.renderlayer(renderlayer): - self.collect(instance) - - set_nodes = [m for m in instance if m not in model_nodes] - instance[:] = model_nodes - - if set_nodes: - instance.data["modelRenderSets"] = set_nodes - instance.data["modelRenderSetsHistory"] = \ - cmds.listHistory(set_nodes, future=False, pruneDagObjects=True) - - self.log.info("Model render sets collected.") - else: - self.log.info("No model render sets.") - def collect_sets(self, instance): """Collect all related objectSets except shadingEngines diff --git a/openpype/hosts/maya/plugins/publish/extract_look.py b/openpype/hosts/maya/plugins/publish/extract_look.py index 121c99fc47..62b58623e7 100644 --- a/openpype/hosts/maya/plugins/publish/extract_look.py +++ b/openpype/hosts/maya/plugins/publish/extract_look.py @@ -135,6 +135,7 @@ class ExtractLook(openpype.api.Extractor): families = ["look"] order = pyblish.api.ExtractorOrder + 0.2 scene_type = "ma" + look_data_type = "json" @staticmethod def get_renderer_name(): @@ -186,7 +187,7 @@ class ExtractLook(openpype.api.Extractor): # Define extract output file path dir_path = self.staging_dir(instance) maya_fname = "{0}.{1}".format(instance.name, self.scene_type) - json_fname = "{0}.json".format(instance.name) + json_fname = "{0}.{1}".format(instance.name, self.look_data_type) # Make texture dump folder maya_path = os.path.join(dir_path, maya_fname) @@ -252,19 +253,21 @@ class ExtractLook(openpype.api.Extractor): instance.data["files"].append(maya_fname) instance.data["files"].append(json_fname) - instance.data["representations"] = [] + if instance.data.get("representations") is None: + instance.data["representations"] = [] + instance.data["representations"].append( { - "name": "ma", - "ext": "ma", + "name": self.scene_type, + "ext": self.scene_type, "files": os.path.basename(maya_fname), "stagingDir": os.path.dirname(maya_fname), } ) instance.data["representations"].append( { - "name": "json", - "ext": "json", + "name": self.look_data_type, + "ext": self.look_data_type, "files": os.path.basename(json_fname), "stagingDir": os.path.dirname(json_fname), } @@ -483,119 +486,17 @@ class ExtractLook(openpype.api.Extractor): return filepath, COPY, texture_hash -class ExtractAugmentedModel(ExtractLook): - """Extract as Augmented Model (Maya Scene). +class ExtractModelRenderSets(ExtractLook): + """Extract model render attribute sets as model metadata - Rendering attrs augmented model. - - Only extracts contents based on the original "setMembers" data to ensure - publishing the least amount of required shapes. From that it only takes - the shapes that are not intermediateObjects - - During export it sets a temporary context to perform a clean extraction. - The context ensures: - - Smooth preview is turned off for the geometry - - Default shader is assigned (no materials are exported) - - Remove display layers + Only extracts the render attrib sets (NO shadingEngines) alongside a .json file + that stores it relationships for the sets and "attribute" data for the + instance members. """ - label = "Augmented Model (Maya Scene)" + label = "Model Render Sets" hosts = ["maya"] families = ["model"] - scene_type = "ma" - augmented = "fried" - - def process(self, instance): - """Plugin entry point. - - Args: - instance: Instance to process. - - """ - render_sets = instance.data.get("modelRenderSetsHistory") - if not render_sets: - self.log.info("Model is not render augmented, skip extraction.") - return - - self.get_maya_scene_type(instance) - - if "representations" not in instance.data: - instance.data["representations"] = [] - - # Define extract output file path - stagingdir = self.staging_dir(instance) - ext = "{0}.{1}".format(self.augmented, self.scene_type) - filename = "{0}.{1}".format(instance.name, ext) - path = os.path.join(stagingdir, filename) - - # Perform extraction - self.log.info("Performing extraction ...") - - results = self.process_resources(instance, staging_dir=stagingdir) - transfers = results["fileTransfers"] - hardlinks = results["fileHardlinks"] - hashes = results["fileHashes"] - remap = results["attrRemap"] - - self.log.info(remap) - - # Get only the shape contents we need in such a way that we avoid - # taking along intermediateObjects - members = instance.data("setMembers") - members = cmds.ls(members, - dag=True, - shapes=True, - type=("mesh", "nurbsCurve"), - noIntermediate=True, - long=True) - members += instance.data.get("modelRenderSetsHistory") - - with lib.no_display_layers(instance): - with lib.displaySmoothness(members, - divisionsU=0, - divisionsV=0, - pointsWire=4, - pointsShaded=1, - polygonObject=1): - with lib.shader(members, - shadingEngine="initialShadingGroup"): - # To avoid Maya trying to automatically remap the file - # textures relative to the `workspace -directory` we force - # it to a fake temporary workspace. This fixes textures - # getting incorrectly remapped. (LKD-17, PLN-101) - with no_workspace_dir(): - with lib.attribute_values(remap): - with avalon.maya.maintained_selection(): - - cmds.select(members, noExpand=True) - cmds.file(path, - force=True, - typ="mayaAscii" if self.scene_type == "ma" else "mayaBinary", # noqa: E501 - exportSelected=True, - preserveReferences=False, - channels=False, - constraints=False, - expressions=False, - constructionHistory=False) - - if "hardlinks" not in instance.data: - instance.data["hardlinks"] = [] - if "transfers" not in instance.data: - instance.data["transfers"] = [] - - # Set up the resources transfers/links for the integrator - instance.data["transfers"].extend(transfers) - instance.data["hardlinks"].extend(hardlinks) - - # Source hash for the textures - instance.data["sourceHashes"] = hashes - - instance.data["representations"].append({ - 'name': ext, - 'ext': ext, - 'files': filename, - "stagingDir": stagingdir, - }) - - self.log.info("Extracted instance '%s' to: %s" % (instance.name, path)) + scene_type = "meta.render.ma" + look_data_type = "meta.render.json" From 84ca6a591c5aa1d15cb16cde3e05318eb5b71915 Mon Sep 17 00:00:00 2001 From: David Lai Date: Wed, 8 Sep 2021 23:19:23 +0800 Subject: [PATCH 06/10] fix linter --- .../plugins/inventory/import_modelrender.py | 33 +++++++++++-------- .../maya/plugins/publish/extract_look.py | 6 ++-- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/openpype/hosts/maya/plugins/inventory/import_modelrender.py b/openpype/hosts/maya/plugins/inventory/import_modelrender.py index 2737901b51..c13b4d6a1c 100644 --- a/openpype/hosts/maya/plugins/inventory/import_modelrender.py +++ b/openpype/hosts/maya/plugins/inventory/import_modelrender.py @@ -19,18 +19,20 @@ class ImportModelRender(api.InventoryAction): from maya import cmds for container in containers: - container_name = container["objectName"] + con_name = container["objectName"] nodes = [] - for n in cmds.sets(container_name, query=True, nodesOnly=True) or []: + for n in cmds.sets(con_name, query=True, nodesOnly=True) or []: if cmds.nodeType(n) == "reference": nodes += cmds.referenceQuery(n, nodes=True) else: nodes.append(n) - repr_doc = io.find_one({"_id": io.ObjectId(container["representation"])}) + repr_doc = io.find_one({ + "_id": io.ObjectId(container["representation"]), + }) version_id = repr_doc["parent"] - print("Importing render sets for model %r" % container_name) + print("Importing render sets for model %r" % con_name) self.assign_model_render_by_version(nodes, version_id) def assign_model_render_by_version(self, nodes, version_id): @@ -51,22 +53,25 @@ class ImportModelRender(api.InventoryAction): from openpype.hosts.maya.api import lib # Get representations of shader file and relationships - look_representation = io.find_one({"type": "representation", - "parent": version_id, - "name": self.scene_type}) - - if not look_representation: + look_repr = io.find_one({ + "type": "representation", + "parent": version_id, + "name": self.scene_type, + }) + if not look_repr: print("No model render sets for this model version..") return - json_representation = io.find_one({"type": "representation", - "parent": version_id, - "name": self.look_data_type}) + json_repr = io.find_one({ + "type": "representation", + "parent": version_id, + "name": self.look_data_type, + }) - context = pipeline.get_representation_context(look_representation['_id']) + context = pipeline.get_representation_context(look_repr["_id"]) maya_file = pipeline.get_representation_path_from_context(context) - context = pipeline.get_representation_context(json_representation['_id']) + context = pipeline.get_representation_context(json_repr["_id"]) json_file = pipeline.get_representation_path_from_context(context) # Import the look file diff --git a/openpype/hosts/maya/plugins/publish/extract_look.py b/openpype/hosts/maya/plugins/publish/extract_look.py index 62b58623e7..0a3a8d2e79 100644 --- a/openpype/hosts/maya/plugins/publish/extract_look.py +++ b/openpype/hosts/maya/plugins/publish/extract_look.py @@ -489,9 +489,9 @@ class ExtractLook(openpype.api.Extractor): class ExtractModelRenderSets(ExtractLook): """Extract model render attribute sets as model metadata - Only extracts the render attrib sets (NO shadingEngines) alongside a .json file - that stores it relationships for the sets and "attribute" data for the - instance members. + Only extracts the render attrib sets (NO shadingEngines) alongside + a .json file that stores it relationships for the sets and "attribute" + data for the instance members. """ From 316ee85f7b8635a2cd4266376eee88836d164b44 Mon Sep 17 00:00:00 2001 From: David Lai Date: Wed, 8 Sep 2021 23:21:35 +0800 Subject: [PATCH 07/10] fix linter --- openpype/hosts/maya/plugins/inventory/import_modelrender.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/inventory/import_modelrender.py b/openpype/hosts/maya/plugins/inventory/import_modelrender.py index c13b4d6a1c..3675b757ea 100644 --- a/openpype/hosts/maya/plugins/inventory/import_modelrender.py +++ b/openpype/hosts/maya/plugins/inventory/import_modelrender.py @@ -12,8 +12,10 @@ class ImportModelRender(api.InventoryAction): @staticmethod def is_compatible(container): - return container.get("loader") == "ReferenceLoader" \ - and container.get("name", "").startswith("model") + return ( + container.get("loader") == "ReferenceLoader" + and container.get("name", "").startswith("model") + ) def process(self, containers): from maya import cmds From f96555d9e95d34d9e7f3160f674a11e3276d4955 Mon Sep 17 00:00:00 2001 From: David Lai Date: Fri, 17 Sep 2021 02:27:52 +0800 Subject: [PATCH 08/10] modelrender-sets add vray light-mesh, obj-properties --- openpype/hosts/maya/plugins/publish/collect_look.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/hosts/maya/plugins/publish/collect_look.py b/openpype/hosts/maya/plugins/publish/collect_look.py index 712c7f19ff..8ebdfa1b67 100644 --- a/openpype/hosts/maya/plugins/publish/collect_look.py +++ b/openpype/hosts/maya/plugins/publish/collect_look.py @@ -360,6 +360,8 @@ class CollectLook(pyblish.api.InstancePlugin): # handling render attribute sets render_set_types = [ "VRayDisplacement", + "VRayLightMesh", + "VRayObjectProperties", ] render_sets = cmds.ls(look_sets, type=render_set_types) if render_sets: From da3d4d02039084658a618084d5ce1ba9374b27ca Mon Sep 17 00:00:00 2001 From: David Lai Date: Fri, 17 Sep 2021 02:28:50 +0800 Subject: [PATCH 09/10] modelrender-sets add redshift object-id, mesh-parameters --- openpype/hosts/maya/plugins/publish/collect_look.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/hosts/maya/plugins/publish/collect_look.py b/openpype/hosts/maya/plugins/publish/collect_look.py index 8ebdfa1b67..9c047b252f 100644 --- a/openpype/hosts/maya/plugins/publish/collect_look.py +++ b/openpype/hosts/maya/plugins/publish/collect_look.py @@ -362,6 +362,8 @@ class CollectLook(pyblish.api.InstancePlugin): "VRayDisplacement", "VRayLightMesh", "VRayObjectProperties", + "RedshiftObjectId", + "RedshiftMeshParameters", ] render_sets = cmds.ls(look_sets, type=render_set_types) if render_sets: From da7dda6b6a8b7d8cd240292c73bb7eec641490e5 Mon Sep 17 00:00:00 2001 From: David Lai Date: Fri, 24 Sep 2021 02:35:59 +0800 Subject: [PATCH 10/10] read studio scene type config on extracting look --- .../plugins/inventory/import_modelrender.py | 4 ++-- .../maya/plugins/publish/extract_look.py | 21 ++++++++++++++----- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/openpype/hosts/maya/plugins/inventory/import_modelrender.py b/openpype/hosts/maya/plugins/inventory/import_modelrender.py index 3675b757ea..e3cad4cf2e 100644 --- a/openpype/hosts/maya/plugins/inventory/import_modelrender.py +++ b/openpype/hosts/maya/plugins/inventory/import_modelrender.py @@ -7,7 +7,7 @@ class ImportModelRender(api.InventoryAction): icon = "industry" color = "#55DDAA" - scene_type = "meta.render.ma" + scene_type_regex = "meta.render.m[ab]" look_data_type = "meta.render.json" @staticmethod @@ -58,7 +58,7 @@ class ImportModelRender(api.InventoryAction): look_repr = io.find_one({ "type": "representation", "parent": version_id, - "name": self.scene_type, + "name": {"$regex": self.scene_type_regex}, }) if not look_repr: print("No model render sets for this model version..") diff --git a/openpype/hosts/maya/plugins/publish/extract_look.py b/openpype/hosts/maya/plugins/publish/extract_look.py index 0a3a8d2e79..bbf25ebdc7 100644 --- a/openpype/hosts/maya/plugins/publish/extract_look.py +++ b/openpype/hosts/maya/plugins/publish/extract_look.py @@ -122,7 +122,7 @@ def no_workspace_dir(): class ExtractLook(openpype.api.Extractor): - """Extract Look (Maya Ascii + JSON) + """Extract Look (Maya Scene + JSON) Only extracts the sets (shadingEngines and alike) alongside a .json file that stores it relationships for the sets and "attribute" data for the @@ -130,7 +130,7 @@ class ExtractLook(openpype.api.Extractor): """ - label = "Extract Look (Maya ASCII + JSON)" + label = "Extract Look (Maya Scene + JSON)" hosts = ["maya"] families = ["look"] order = pyblish.api.ExtractorOrder + 0.2 @@ -177,6 +177,8 @@ class ExtractLook(openpype.api.Extractor): # no preset found pass + return "mayaAscii" if self.scene_type == "ma" else "mayaBinary" + def process(self, instance): """Plugin entry point. @@ -184,6 +186,8 @@ class ExtractLook(openpype.api.Extractor): instance: Instance to process. """ + _scene_type = self.get_maya_scene_type(instance) + # Define extract output file path dir_path = self.staging_dir(instance) maya_fname = "{0}.{1}".format(instance.name, self.scene_type) @@ -197,7 +201,7 @@ class ExtractLook(openpype.api.Extractor): # Remove all members of the sets so they are not included in the # exported file by accident - self.log.info("Extract sets (Maya ASCII) ...") + self.log.info("Extract sets (%s) ..." % _scene_type) lookdata = instance.data["lookData"] relationships = lookdata["relationships"] sets = relationships.keys() @@ -224,7 +228,7 @@ class ExtractLook(openpype.api.Extractor): cmds.file( maya_path, force=True, - typ="mayaAscii", + typ=_scene_type, exportSelected=True, preserveReferences=False, channels=True, @@ -498,5 +502,12 @@ class ExtractModelRenderSets(ExtractLook): label = "Model Render Sets" hosts = ["maya"] families = ["model"] - scene_type = "meta.render.ma" + scene_type_prefix = "meta.render." look_data_type = "meta.render.json" + + def get_maya_scene_type(self, instance): + typ = super(ExtractModelRenderSets, self).get_maya_scene_type(instance) + # add prefix + self.scene_type = self.scene_type_prefix + self.scene_type + + return typ