diff --git a/openpype/hosts/maya/plugins/create/create_look.py b/openpype/hosts/maya/plugins/create/create_look.py index 0f960e5ff5..ead1e6f93f 100644 --- a/openpype/hosts/maya/plugins/create/create_look.py +++ b/openpype/hosts/maya/plugins/create/create_look.py @@ -17,6 +17,7 @@ class CreateLook(plugin.MayaCreator): icon = "paint-brush" make_tx = True + rs_tex = False def get_instance_attr_defs(self): @@ -32,6 +33,11 @@ class CreateLook(plugin.MayaCreator): label="MakeTX", tooltip="Whether to generate .tx files for your textures", default=self.make_tx), + BoolDef("rstex", + label="Convert textures to .rstex", + tooltip="Whether to generate Redshift .rstex files for " + "your textures", + default=self.make_tx), BoolDef("forceCopy", label="Force Copy", tooltip="Enable users to force a copy instead of hardlink." diff --git a/openpype/hosts/maya/plugins/create/create_review.py b/openpype/hosts/maya/plugins/create/create_review.py index 13187e6375..f787ba5dd5 100644 --- a/openpype/hosts/maya/plugins/create/create_review.py +++ b/openpype/hosts/maya/plugins/create/create_review.py @@ -26,14 +26,15 @@ class CreateReview(plugin.MayaCreator): family = "review" icon = "video-camera" - use_maya_timeline = True + useMayaTimeline = True + panZoom = False def get_instance_attr_defs(self): defs = lib.collect_animation_defs() # Option for using Maya or asset frame range in settings. - if not self.use_maya_timeline: + if not self.useMayaTimeline: # Update the defaults to be the asset frame range frame_range = lib.get_frame_range() defs_by_key = {attr_def.key: attr_def for attr_def in defs} @@ -72,7 +73,10 @@ class CreateReview(plugin.MayaCreator): default=True), EnumDef("transparency", label="Transparency", - items=TRANSPARENCIES) + items=TRANSPARENCIES), + BoolDef("panZoom", + label="Enable camera pan/zoom", + default=True), ]) return defs diff --git a/openpype/hosts/maya/plugins/publish/collect_instances.py b/openpype/hosts/maya/plugins/publish/collect_instances.py index ee02d3efc0..aa14527170 100644 --- a/openpype/hosts/maya/plugins/publish/collect_instances.py +++ b/openpype/hosts/maya/plugins/publish/collect_instances.py @@ -1,47 +1,7 @@ from maya import cmds -import maya.api.OpenMaya as om import pyblish.api - - -def get_all_children(nodes): - """Return all children of `nodes` including each instanced child. - Using maya.cmds.listRelatives(allDescendents=True) includes only the first - instance. As such, this function acts as an optimal replacement with a - focus on a fast query. - - """ - - sel = om.MSelectionList() - traversed = set() - iterator = om.MItDag(om.MItDag.kDepthFirst) - for node in nodes: - - if node in traversed: - # Ignore if already processed as a child - # before - continue - - sel.clear() - sel.add(node) - dag = sel.getDagPath(0) - - iterator.reset(dag) - # ignore self - iterator.next() # noqa: B305 - while not iterator.isDone(): - - path = iterator.fullPathName() - - if path in traversed: - iterator.prune() - iterator.next() # noqa: B305 - continue - - traversed.add(path) - iterator.next() # noqa: B305 - - return list(traversed) +from openpype.hosts.maya.api.lib import get_all_children class CollectNewInstances(pyblish.api.InstancePlugin): diff --git a/openpype/hosts/maya/plugins/publish/validate_attributes.py b/openpype/hosts/maya/plugins/publish/validate_attributes.py index 7a55d1ba41..733ec9d1de 100644 --- a/openpype/hosts/maya/plugins/publish/validate_attributes.py +++ b/openpype/hosts/maya/plugins/publish/validate_attributes.py @@ -1,6 +1,10 @@ -import pymel.core as pm +from collections import defaultdict + +from maya import cmds import pyblish.api + +from openpype.hosts.maya.api.lib import set_attribute from openpype.pipeline.publish import ( RepairContextAction, ValidateContentsOrder, @@ -8,7 +12,7 @@ from openpype.pipeline.publish import ( ) -class ValidateAttributes(pyblish.api.ContextPlugin, +class ValidateAttributes(pyblish.api.InstancePlugin, OptionalPyblishPluginMixin): """Ensure attributes are consistent. @@ -29,88 +33,83 @@ class ValidateAttributes(pyblish.api.ContextPlugin, attributes = None - def process(self, context): - # Check for preset existence. - if not self.is_active(context.data): + def process(self, instance): + if not self.is_active(instance.data): return + # Check for preset existence. if not self.attributes: return - invalid = self.get_invalid(context, compute=True) + invalid = self.get_invalid(instance, compute=True) if invalid: raise RuntimeError( "Found attributes with invalid values: {}".format(invalid) ) @classmethod - def get_invalid(cls, context, compute=False): - invalid = context.data.get("invalid_attributes", []) + def get_invalid(cls, instance, compute=False): if compute: - invalid = cls.get_invalid_attributes(context) - - return invalid + return cls.get_invalid_attributes(instance) + else: + return instance.data.get("invalid_attributes", []) @classmethod - def get_invalid_attributes(cls, context): + def get_invalid_attributes(cls, instance): invalid_attributes = [] - for instance in context: - # Filter publisable instances. - if not instance.data["publish"]: + + # Filter families. + families = [instance.data["family"]] + families += instance.data.get("families", []) + families = set(families) & set(cls.attributes.keys()) + if not families: + return [] + + # Get all attributes to validate. + attributes = defaultdict(dict) + for family in families: + if family not in cls.attributes: + # No attributes to validate for family continue - # Filter families. - families = [instance.data["family"]] - families += instance.data.get("families", []) - families = list(set(families) & set(cls.attributes.keys())) - if not families: + for preset_attr, preset_value in cls.attributes[family].items(): + node_name, attribute_name = preset_attr.split(".", 1) + attributes[node_name][attribute_name] = preset_value + + if not attributes: + return [] + + # Get invalid attributes. + nodes = cmds.ls(long=True) + for node in nodes: + node_name = node.rsplit("|", 1)[-1].rsplit(":", 1)[-1] + if node_name not in attributes: continue - # Get all attributes to validate. - attributes = {} - for family in families: - for preset in cls.attributes[family]: - [node_name, attribute_name] = preset.split(".") - try: - attributes[node_name].update( - {attribute_name: cls.attributes[family][preset]} - ) - except KeyError: - attributes.update({ - node_name: { - attribute_name: cls.attributes[family][preset] - } - }) + for attr_name, expected in attributes.items(): - # Get invalid attributes. - nodes = pm.ls() - for node in nodes: - name = node.name(stripNamespace=True) - if name not in attributes.keys(): + # Skip if attribute does not exist + if not cmds.attributeQuery(attr_name, node=node, exists=True): continue - presets_to_validate = attributes[name] - for attribute in node.listAttr(): - names = [attribute.shortName(), attribute.longName()] - attribute_name = list( - set(names) & set(presets_to_validate.keys()) + plug = "{}.{}".format(node, attr_name) + value = cmds.getAttr(plug) + if value != expected: + invalid_attributes.append( + { + "attribute": plug, + "expected": expected, + "current": value + } ) - if attribute_name: - expected = presets_to_validate[attribute_name[0]] - if attribute.get() != expected: - invalid_attributes.append( - { - "attribute": attribute, - "expected": expected, - "current": attribute.get() - } - ) - context.data["invalid_attributes"] = invalid_attributes + instance.data["invalid_attributes"] = invalid_attributes return invalid_attributes @classmethod def repair(cls, instance): invalid = cls.get_invalid(instance) for data in invalid: - data["attribute"].set(data["expected"]) + node, attr = data["attribute"].split(".", 1) + value = data["expected"] + set_attribute(node=node, attribute=attr, value=value) diff --git a/openpype/hosts/maya/plugins/publish/validate_frame_range.py b/openpype/hosts/maya/plugins/publish/validate_frame_range.py index 278be75e5e..b9622e9632 100644 --- a/openpype/hosts/maya/plugins/publish/validate_frame_range.py +++ b/openpype/hosts/maya/plugins/publish/validate_frame_range.py @@ -54,7 +54,6 @@ class ValidateFrameRange(pyblish.api.InstancePlugin, frame_start_handle = int(context.data.get("frameStartHandle")) frame_end_handle = int(context.data.get("frameEndHandle")) - handles = int(context.data.get("handles")) handle_start = int(context.data.get("handleStart")) handle_end = int(context.data.get("handleEnd")) frame_start = int(context.data.get("frameStart")) @@ -71,8 +70,6 @@ class ValidateFrameRange(pyblish.api.InstancePlugin, assert frame_start_handle <= frame_end_handle, ( "start frame is lower then end frame") - assert handles >= 0, ("handles cannot have negative values") - # compare with data on instance errors = [] if [ef for ef in self.exclude_families diff --git a/openpype/hosts/maya/plugins/publish/validate_maya_units.py b/openpype/hosts/maya/plugins/publish/validate_maya_units.py index d28d51248e..1d5619795f 100644 --- a/openpype/hosts/maya/plugins/publish/validate_maya_units.py +++ b/openpype/hosts/maya/plugins/publish/validate_maya_units.py @@ -127,7 +127,7 @@ class ValidateMayaUnits(pyblish.api.ContextPlugin): cls.log.debug(current_linear) cls.log.info("Setting time unit to match project") - # TODO repace query with using 'context.data["assetEntity"]' + # TODO replace query with using 'context.data["assetEntity"]' asset_doc = get_current_project_asset() asset_fps = asset_doc["data"]["fps"] mayalib.set_scene_fps(asset_fps) diff --git a/openpype/hosts/maya/plugins/publish/validate_mesh_overlapping_uvs.py b/openpype/hosts/maya/plugins/publish/validate_mesh_overlapping_uvs.py index 0c63158f3e..ed26e57c87 100644 --- a/openpype/hosts/maya/plugins/publish/validate_mesh_overlapping_uvs.py +++ b/openpype/hosts/maya/plugins/publish/validate_mesh_overlapping_uvs.py @@ -1,10 +1,11 @@ -import pyblish.api -import openpype.hosts.maya.api.action import math -import maya.api.OpenMaya as om -import pymel.core as pm - from six.moves import xrange + +from maya import cmds +import maya.api.OpenMaya as om +import pyblish.api + +import openpype.hosts.maya.api.action from openpype.pipeline.publish import ( ValidateMeshOrder, OptionalPyblishPluginMixin @@ -188,8 +189,7 @@ class GetOverlappingUVs(object): center, radius = self._createBoundingCircle(meshfn) for i in xrange(meshfn.numPolygons): # noqa: F821 - rayb1, face1Orig, face1Vec = self._createRayGivenFace( - meshfn, i) + rayb1, face1Orig, face1Vec = self._createRayGivenFace(meshfn, i) if not rayb1: continue cui = center[2*i] @@ -209,8 +209,8 @@ class GetOverlappingUVs(object): if (dsqr >= (ri + rj) * (ri + rj)): continue - rayb2, face2Orig, face2Vec = self._createRayGivenFace( - meshfn, j) + rayb2, face2Orig, face2Vec = self._createRayGivenFace(meshfn, + j) if not rayb2: continue # Exclude the degenerate face @@ -244,37 +244,45 @@ class ValidateMeshHasOverlappingUVs(pyblish.api.InstancePlugin, optional = True @classmethod - def _get_overlapping_uvs(cls, node): - """ Check if mesh has overlapping UVs. + def _get_overlapping_uvs(cls, mesh): + """Return overlapping UVs of mesh. + + Args: + mesh (str): Mesh node name + + Returns: + list: Overlapping uvs for the input mesh in all uv sets. - :param node: node to check - :type node: str - :returns: True is has overlapping UVs, False otherwise - :rtype: bool """ ovl = GetOverlappingUVs() + # Store original uv set + original_current_uv_set = cmds.polyUVSet(mesh, + query=True, + currentUVSet=True) + overlapping_faces = [] - for i, uv in enumerate(pm.polyUVSet(node, q=1, auv=1)): - pm.polyUVSet(node, cuv=1, uvSet=uv) - overlapping_faces.extend(ovl._getOverlapUVFaces(str(node))) + for uv_set in cmds.polyUVSet(mesh, query=True, allUVSets=True): + cmds.polyUVSet(mesh, currentUVSet=True, uvSet=uv_set) + overlapping_faces.extend(ovl._getOverlapUVFaces(mesh)) + + # Restore original uv set + cmds.polyUVSet(mesh, currentUVSet=True, uvSet=original_current_uv_set) return overlapping_faces @classmethod def get_invalid(cls, instance, compute=False): - invalid = [] + if compute: - instance.data["overlapping_faces"] = [] - for node in pm.ls(instance, type="mesh"): + invalid = [] + for node in cmds.ls(instance, type="mesh"): faces = cls._get_overlapping_uvs(node) invalid.extend(faces) - # Store values for later. - instance.data["overlapping_faces"].extend(faces) - else: - invalid.extend(instance.data["overlapping_faces"]) - return invalid + instance.data["overlapping_faces"] = invalid + + return instance.data.get("overlapping_faces", []) def process(self, instance): if not self.is_active(instance.data): diff --git a/openpype/modules/deadline/plugins/publish/collect_pools.py b/openpype/modules/deadline/plugins/publish/collect_pools.py index f4f588bf31..e221eb00ea 100644 --- a/openpype/modules/deadline/plugins/publish/collect_pools.py +++ b/openpype/modules/deadline/plugins/publish/collect_pools.py @@ -3,7 +3,6 @@ """ import pyblish.api - from openpype.lib import TextDef from openpype.pipeline.publish import OpenPypePyblishPluginMixin @@ -14,7 +13,11 @@ class CollectDeadlinePools(pyblish.api.InstancePlugin, order = pyblish.api.CollectorOrder + 0.420 label = "Collect Deadline Pools" - families = ["rendering", "render.farm", "renderFarm", "renderlayer"] + families = ["rendering", + "render.farm", + "renderFarm", + "renderlayer", + "maxrender"] primary_pool = None secondary_pool = None @@ -29,7 +32,6 @@ class CollectDeadlinePools(pyblish.api.InstancePlugin, def process(self, instance): attr_values = self.get_attr_values_from_data(instance.data) - if not instance.data.get("primaryPool"): instance.data["primaryPool"] = ( attr_values.get("primaryPool") or self.primary_pool or "none" @@ -42,7 +44,6 @@ class CollectDeadlinePools(pyblish.api.InstancePlugin, @classmethod def get_attribute_defs(cls): - # TODO: Preferably this would be an enum for the user # but the Deadline server URL can be dynamic and # can be set per render instance. Since get_attribute_defs