diff --git a/openpype/hosts/maya/api/fbx.py b/openpype/hosts/maya/api/fbx.py index 5bd375362b..2dd4f5a73d 100644 --- a/openpype/hosts/maya/api/fbx.py +++ b/openpype/hosts/maya/api/fbx.py @@ -203,6 +203,9 @@ class FBXExtractor: path (str): Path to use for export. """ + # The export requires forward slashes because we need + # to format it into a string in a mel expression + path = path.replace("\\", "/") with maintained_selection(): cmds.select(members, r=True, noExpand=True) mel.eval('FBXExport -f "{}" -s'.format(path)) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index b246a77512..6019aec37c 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -922,7 +922,7 @@ def no_display_layers(nodes): @contextlib.contextmanager -def namespaced(namespace, new=True): +def namespaced(namespace, new=True, relative_names=None): """Work inside namespace during context Args: @@ -934,6 +934,7 @@ def namespaced(namespace, new=True): """ original = cmds.namespaceInfo(cur=True, absoluteName=True) + original_relative_names = cmds.namespace(query=True, relativeNames=True) if new: namespace = unique_namespace(namespace) cmds.namespace(add=namespace) @@ -943,6 +944,8 @@ def namespaced(namespace, new=True): yield namespace finally: cmds.namespace(set=original) + if relative_names is not None: + cmds.namespace(relativeNames=original_relative_names) @contextlib.contextmanager diff --git a/openpype/hosts/maya/plugins/create/create_rig.py b/openpype/hosts/maya/plugins/create/create_rig.py index 22a94ed4fd..acd5c98f89 100644 --- a/openpype/hosts/maya/plugins/create/create_rig.py +++ b/openpype/hosts/maya/plugins/create/create_rig.py @@ -20,7 +20,7 @@ class CreateRig(plugin.MayaCreator): instance_node = instance.get("instance_node") self.log.info("Creating Rig instance set up ...") - # TODO:change name (_controls_set -> _rigs_SET) + # TODO:change name (_controls_SET -> _rigs_SET) controls = cmds.sets(name=subset_name + "_controls_SET", empty=True) # TODO:change name (_out_SET -> _geo_SET) pointcache = cmds.sets(name=subset_name + "_out_SET", empty=True) diff --git a/openpype/hosts/maya/plugins/load/_load_animation.py b/openpype/hosts/maya/plugins/load/_load_animation.py index 6d67383909..2432184151 100644 --- a/openpype/hosts/maya/plugins/load/_load_animation.py +++ b/openpype/hosts/maya/plugins/load/_load_animation.py @@ -1,4 +1,46 @@ import openpype.hosts.maya.api.plugin +import maya.cmds as cmds + + +def _process_reference(file_url, name, namespace, options): + """_summary_ + + Args: + file_url (str): fileapth of the objects to be loaded + name (str): subset name + namespace (str): namespace + options (dict): dict of storing the param + + Returns: + list: list of object nodes + """ + from openpype.hosts.maya.api.lib import unique_namespace + # Get name from asset being loaded + # Assuming name is subset name from the animation, we split the number + # suffix from the name to ensure the namespace is unique + name = name.split("_")[0] + ext = file_url.split(".")[-1] + namespace = unique_namespace( + "{}_".format(name), + format="%03d", + suffix="_{}".format(ext) + ) + + attach_to_root = options.get("attach_to_root", True) + group_name = options["group_name"] + + # no group shall be created + if not attach_to_root: + group_name = namespace + + nodes = cmds.file(file_url, + namespace=namespace, + sharedReferenceFile=False, + groupReference=attach_to_root, + groupName=group_name, + reference=True, + returnNewNodes=True) + return nodes class AbcLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): @@ -7,7 +49,7 @@ class AbcLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): families = ["animation", "camera", "pointcache"] - representations = ["abc", "fbx"] + representations = ["abc"] label = "Reference animation" order = -10 @@ -16,44 +58,42 @@ class AbcLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): def process_reference(self, context, name, namespace, options): - import maya.cmds as cmds - from openpype.hosts.maya.api.lib import unique_namespace - cmds.loadPlugin("AbcImport.mll", quiet=True) - # Prevent identical alembic nodes from being shared - # Create unique namespace for the cameras - - # Get name from asset being loaded - # Assuming name is subset name from the animation, we split the number - # suffix from the name to ensure the namespace is unique - name = name.split("_")[0] - namespace = unique_namespace( - "{}_".format(name), - format="%03d", - suffix="_abc" - ) - - attach_to_root = options.get("attach_to_root", True) - group_name = options["group_name"] - - # no group shall be created - if not attach_to_root: - group_name = namespace - # hero_001 (abc) # asset_counter{optional} path = self.filepath_from_context(context) file_url = self.prepare_root_value(path, context["project"]["name"]) - nodes = cmds.file(file_url, - namespace=namespace, - sharedReferenceFile=False, - groupReference=attach_to_root, - groupName=group_name, - reference=True, - returnNewNodes=True) + nodes = _process_reference(file_url, name, namespace, options) # load colorbleed ID attribute self[:] = nodes return nodes + + +class FbxLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): + """Loader to reference an Fbx files""" + + families = ["animation", + "camera"] + representations = ["fbx"] + + label = "Reference animation" + order = -10 + icon = "code-fork" + color = "orange" + + def process_reference(self, context, name, namespace, options): + + cmds.loadPlugin("fbx4maya.mll", quiet=True) + + path = self.filepath_from_context(context) + file_url = self.prepare_root_value(path, + context["project"]["name"]) + + nodes = _process_reference(file_url, name, namespace, options) + + self[:] = nodes + + return nodes diff --git a/openpype/hosts/maya/plugins/publish/collect_fbx_animation.py b/openpype/hosts/maya/plugins/publish/collect_fbx_animation.py index 9347936e63..ee5ac741c8 100644 --- a/openpype/hosts/maya/plugins/publish/collect_fbx_animation.py +++ b/openpype/hosts/maya/plugins/publish/collect_fbx_animation.py @@ -29,7 +29,8 @@ class CollectFbxAnimation(pyblish.api.InstancePlugin, for skeleton_set in skeleton_sets: skeleton_content = cmds.sets(skeleton_set, query=True) self.log.debug( - "Collected animated " - f"skeleton data: {skeleton_content}") + "Collected animated skeleton data: {}".format( + skeleton_content + )) if skeleton_content: instance.data["animated_skeleton"] += skeleton_content diff --git a/openpype/hosts/maya/plugins/publish/collect_skeleton_mesh.py b/openpype/hosts/maya/plugins/publish/collect_skeleton_mesh.py index a22901357b..9169e3dc28 100644 --- a/openpype/hosts/maya/plugins/publish/collect_skeleton_mesh.py +++ b/openpype/hosts/maya/plugins/publish/collect_skeleton_mesh.py @@ -35,5 +35,6 @@ class CollectSkeletonMesh(pyblish.api.InstancePlugin): if skeleton_mesh_content: instance.data["skeleton_mesh"] += skeleton_mesh_content self.log.debug( - "Collected skeleton " - f"mesh Set: {skeleton_mesh_content}") + "Collected skeletonmesh Set: {}".format( + skeleton_mesh_content + )) diff --git a/openpype/hosts/maya/plugins/publish/extract_fbx_animation.py b/openpype/hosts/maya/plugins/publish/extract_fbx_animation.py index d281e01779..fbc1d5176c 100644 --- a/openpype/hosts/maya/plugins/publish/extract_fbx_animation.py +++ b/openpype/hosts/maya/plugins/publish/extract_fbx_animation.py @@ -40,18 +40,15 @@ class ExtractFBXAnimation(publish.Extractor): fbx_exporter.set_options_from_instance(instance) out_set_name = next(out for out in out_set) - # temporarily disable namespace - namespace = out_set_name.split(":")[0] - new_out_set = out_set_name.replace( - f"{namespace}:", "") + # Export from the rig's namespace so that the exported + # FBX does not include the namespace but preserves the node + # names as existing in the rig workfile + namespace, relative_out_set = out_set_name.split(":", 1) cmds.namespace(relativeNames=True) - with namespaced(":" + namespace, new=False) as namespace: - path = path.replace("\\", "/") - fbx_exporter.export(new_out_set, path) - original_relative_names = cmds.namespace( - query=True, relativeNames=True) - if original_relative_names: - cmds.namespace(relativeNames=original_relative_names) + with namespaced( + ":" + namespace, + new=False, relative_names=True) as namespace: + fbx_exporter.export(relative_out_set, path) representations = instance.data.setdefault("representations", []) representations.append({ @@ -62,4 +59,4 @@ class ExtractFBXAnimation(publish.Extractor): }) self.log.debug( - "Extracted Fbx animation successful to: {0}".format(path)) + "Extracted Fbx animation to: {0}".format(path)) diff --git a/openpype/hosts/maya/plugins/publish/extract_skeleton_mesh.py b/openpype/hosts/maya/plugins/publish/extract_skeleton_mesh.py index 42cbb33013..cecdf282e2 100644 --- a/openpype/hosts/maya/plugins/publish/extract_skeleton_mesh.py +++ b/openpype/hosts/maya/plugins/publish/extract_skeleton_mesh.py @@ -32,8 +32,6 @@ class ExtractSkeletonMesh(publish.Extractor, filename = "{0}.fbx".format(instance.name) path = os.path.join(staging_dir, filename) - # The export requires forward slashes because we need - # to format it into a string in a mel expression fbx_exporter = fbx.FBXExtractor(log=self.log) out_set = instance.data.get("skeleton_mesh", []) @@ -43,7 +41,6 @@ class ExtractSkeletonMesh(publish.Extractor, fbx_exporter.set_options_from_instance(instance) # Export - path = path.replace("\\", "/") fbx_exporter.export(out_set, path) representations = instance.data.setdefault("representations", []) diff --git a/openpype/hosts/maya/plugins/publish/validate_skeleton_rig_output_ids.py b/openpype/hosts/maya/plugins/publish/validate_skeleton_rig_output_ids.py index 0d1e702749..735ca27b39 100644 --- a/openpype/hosts/maya/plugins/publish/validate_skeleton_rig_output_ids.py +++ b/openpype/hosts/maya/plugins/publish/validate_skeleton_rig_output_ids.py @@ -68,9 +68,7 @@ class ValidateSkeletonRigOutputIds(pyblish.api.InstancePlugin): if shapes: instance_nodes.extend(shapes) - scene_nodes = cmds.ls(type="transform", long=True) - scene_nodes += cmds.ls(type="mesh", long=True) - scene_nodes = set(scene_nodes) - set(instance_nodes) + scene_nodes = cmds.ls(type=("transform", "mesh"), long=True) scene_nodes_by_basename = defaultdict(list) for node in scene_nodes: @@ -109,7 +107,7 @@ class ValidateSkeletonRigOutputIds(pyblish.api.InstancePlugin): for instance_node, matches in invalid_matches.items(): ids = set(get_id(node) for node in matches) - # If there are multiple scene ids matched, and error needs to be + # If there are multiple scene ids matched, an error needs to be # raised for manual correction. if len(ids) > 1: multiple_ids_match.append({"node": instance_node, diff --git a/openpype/hosts/maya/plugins/publish/validate_skeleton_top_group_hierarchy.py b/openpype/hosts/maya/plugins/publish/validate_skeleton_top_group_hierarchy.py index 541efee9a9..553618aa50 100644 --- a/openpype/hosts/maya/plugins/publish/validate_skeleton_top_group_hierarchy.py +++ b/openpype/hosts/maya/plugins/publish/validate_skeleton_top_group_hierarchy.py @@ -24,19 +24,17 @@ class ValidateSkeletonTopGroupHierarchy(pyblish.api.InstancePlugin, def process(self, instance): invalid = [] - skeleton_mesh_data = instance.data(("skeleton_mesh"), []) + skeleton_mesh_data = instance.data("skeleton_mesh", []) if skeleton_mesh_data: invalid = self.get_top_hierarchy(skeleton_mesh_data) if invalid: raise PublishValidationError( "The skeletonMesh_SET includes the object which " - f"is not at the top hierarchy: {invalid}") + "is not at the top hierarchy: {}".format(invalid)) def get_top_hierarchy(self, targets): - non_top_hierarchy_list = [] - for target in targets: - long_names = cmds.ls(target, long=True) - for name in long_names: - if len(name.split["|"]) > 2: - non_top_hierarchy_list.append(name) + targets = cmds.ls(targets, long=True) # ensure long names + non_top_hierarchy_list = [ + target for target in targets if target.count("|") > 2 + ] return non_top_hierarchy_list