diff --git a/pype/plugins/maya/create/create_redshift_proxy.py b/pype/plugins/maya/create/create_redshift_proxy.py new file mode 100644 index 00000000000..8713022aac8 --- /dev/null +++ b/pype/plugins/maya/create/create_redshift_proxy.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +"""Creator of Redshift proxy subset types.""" + +from pype.hosts.maya import plugin, lib + + +class CreateRedshiftProxy(plugin.Creator): + """Create instance of Redshift Proxy subset.""" + + name = "redshiftproxy" + label = "Redshift Proxy" + family = "redshiftproxy" + icon = "gears" + + def __init__(self, *args, **kwargs): + super(CreateRedshiftProxy, self).__init__(*args, **kwargs) + + animation_data = lib.collect_animation_data() + + self.data["animation"] = False + self.data["proxyFrameStart"] = animation_data["frameStart"] + self.data["proxyFrameEnd"] = animation_data["frameEnd"] + self.data["proxyFrameStep"] = animation_data["step"] diff --git a/pype/plugins/maya/load/load_redshift_proxy.py b/pype/plugins/maya/load/load_redshift_proxy.py new file mode 100644 index 00000000000..b4f54cd8a2e --- /dev/null +++ b/pype/plugins/maya/load/load_redshift_proxy.py @@ -0,0 +1,147 @@ +# -*- coding: utf-8 -*- +"""Loader for Redshift proxy.""" +import os +from avalon.maya import lib +from avalon import api +from pype.api import config + +import maya.cmds as cmds +import clique + + +class RedshiftProxyLoader(api.Loader): + """Load Redshift proxy""" + + families = ["redshiftproxy"] + representations = ["rs"] + + label = "Import Redshift Proxy" + order = -10 + icon = "code-fork" + color = "orange" + + def load(self, context, name=None, namespace=None, options=None): + """Plugin entry point.""" + + from avalon.maya.pipeline import containerise + from pype.hosts.maya.lib import namespaced + + try: + family = context["representation"]["context"]["family"] + except ValueError: + family = "redshiftproxy" + + asset_name = context['asset']["name"] + namespace = namespace or lib.unique_namespace( + asset_name + "_", + prefix="_" if asset_name[0].isdigit() else "", + suffix="_", + ) + + # Ensure Redshift for Maya is loaded. + cmds.loadPlugin("redshift4maya", quiet=True) + + with lib.maintained_selection(): + cmds.namespace(addNamespace=namespace) + with namespaced(namespace, new=False): + nodes, group_node = self.create_rs_proxy( + name, self.fname) + + self[:] = nodes + if not nodes: + return + + # colour the group node + presets = config.get_presets(project=os.environ['AVALON_PROJECT']) + colors = presets['plugins']['maya']['load']['colors'] + c = colors.get(family) + if c is not None: + cmds.setAttr("{0}.useOutlinerColor".format(group_node), 1) + cmds.setAttr("{0}.outlinerColor".format(group_node), + c[0], c[1], c[2]) + + return containerise( + name=name, + namespace=namespace, + nodes=nodes, + context=context, + loader=self.__class__.__name__) + + def update(self, container, representation): + + node = container['objectName'] + assert cmds.objExists(node), "Missing container" + + members = cmds.sets(node, query=True) or [] + rs_meshes = cmds.ls(members, type="RedshiftProxyMesh") + assert rs_meshes, "Cannot find RedshiftProxyMesh in container" + + filename = api.get_representation_path(representation) + + for rs_mesh in rs_meshes: + cmds.setAttr("{}.fileName".format(rs_mesh), + filename, + type="string") + + # Update metadata + cmds.setAttr("{}.representation".format(node), + str(representation["_id"]), + type="string") + + def remove(self, 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) + + def switch(self, container, representation): + self.update(container, representation) + + def create_rs_proxy(self, name, path): + """Creates Redshift Proxies showing a proxy object. + + Args: + name (str): Proxy name. + path (str): Path to proxy file. + + Returns: + (str, str): Name of mesh with Redshift proxy and its parent + transform. + + """ + rs_mesh = cmds.createNode( + 'RedshiftProxyMesh', name="{}_RS".format(name)) + mesh_shape = cmds.createNode("mesh", name="{}_GEOShape".format(name)) + + cmds.setAttr("{}.fileName".format(rs_mesh), + path, + type="string") + + cmds.connectAttr("{}.outMesh".format(rs_mesh), + "{}.inMesh".format(mesh_shape)) + + group_node = cmds.group(empty=True, name="{}_GRP".format(name)) + mesh_transform = cmds.listRelatives(mesh_shape, + parent=True, fullPath=True) + cmds.parent(mesh_transform, group_node) + nodes = [rs_mesh, mesh_shape, group_node] + + # determine if we need to enable animation support + files_in_folder = os.listdir(os.path.dirname(path)) + collections, remainder = clique.assemble(files_in_folder) + + if collections: + cmds.setAttr("{}.useFrameExtension".format(rs_mesh), 1) + + return nodes, group_node diff --git a/pype/plugins/maya/publish/extract_redshift_proxy.py b/pype/plugins/maya/publish/extract_redshift_proxy.py new file mode 100644 index 00000000000..56255eaad43 --- /dev/null +++ b/pype/plugins/maya/publish/extract_redshift_proxy.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- +"""Redshift Proxy extractor.""" +import os +import math + +import avalon.maya +import pype.api + +from maya import cmds + + +class ExtractRedshiftProxy(pype.api.Extractor): + """Extract the content of the instance to a redshift proxy file.""" + + label = "Redshift Proxy (.rs)" + hosts = ["maya"] + families = ["redshiftproxy"] + + def process(self, instance): + """Extractor entry point.""" + + staging_dir = self.staging_dir(instance) + file_name = "{}.rs".format(instance.name) + file_path = os.path.join(staging_dir, file_name) + + anim_on = instance.data["animation"] + rs_options = "exportConnectivity=0;enableCompression=1;keepUnused=0;" + repr_files = file_name + + if not anim_on: + # Remove animation information because it is not required for + # non-animated subsets + instance.data.pop("proxyFrameStart", None) + instance.data.pop("proxyFrameEnd", None) + + else: + start_frame = instance.data["proxyFrameStart"] + end_frame = instance.data["proxyFrameEnd"] + rs_options = "{}startFrame={};endFrame={};frameStep={};".format( + rs_options, start_frame, + end_frame, instance.data["proxyFrameStep"] + ) + + root, ext = os.path.splitext(file_path) + # Padding is taken from number of digits of the end_frame. + # Not sure where Redshift is taking it. + repr_files = [ + "{}.{}{}".format(root, str(frame).rjust(int(math.log10(int(end_frame)) + 1), "0"), ext) # noqa: E501 + for frame in range( + int(start_frame), + int(end_frame) + 1, + int(instance.data["proxyFrameStep"]))] + # vertex_colors = instance.data.get("vertexColors", False) + + # Write out rs file + self.log.info("Writing: '%s'" % file_path) + with avalon.maya.maintained_selection(): + cmds.select(instance.data["setMembers"], noExpand=True) + cmds.file(file_path, + pr=False, + force=True, + type="Redshift Proxy", + exportSelected=True, + options=rs_options) + + if "representations" not in instance.data: + instance.data["representations"] = [] + + self.log.debug("Files: {}".format(repr_files)) + + representation = { + 'name': 'rs', + 'ext': 'rs', + 'files': repr_files, + "stagingDir": staging_dir, + } + instance.data["representations"].append(representation) + + self.log.info("Extracted instance '%s' to: %s" + % (instance.name, staging_dir))