From 128b2856543078ba8a25824ad6895e4ca98bc4ff Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 9 Oct 2018 12:57:50 +0200 Subject: [PATCH] Embed all internal Colorbleed library (cb) code into config --- colorbleed/lib.py | 19 + colorbleed/maya/lib.py | 493 ++++++++++++++++++ .../plugins/maya/create/colorbleed_camera.py | 2 +- .../plugins/maya/publish/collect_look.py | 142 ++++- .../maya/publish/extract_camera_alembic.py | 10 +- .../maya/publish/extract_camera_mayaAscii.py | 36 +- .../plugins/maya/publish/extract_look.py | 11 +- .../plugins/maya/publish/extract_model.py | 21 +- .../maya/publish/validate_look_sets.py | 4 +- .../maya/publish/validate_rig_controllers.py | 4 +- ...idate_rig_controllers_arnold_attributes.py | 4 +- 11 files changed, 689 insertions(+), 57 deletions(-) diff --git a/colorbleed/lib.py b/colorbleed/lib.py index bc98cf0cc5..edd911a461 100644 --- a/colorbleed/lib.py +++ b/colorbleed/lib.py @@ -2,6 +2,7 @@ import os import re import logging import importlib +import itertools from .vendor import pather from .vendor.pather.error import ParseError @@ -12,6 +13,24 @@ import avalon.api log = logging.getLogger(__name__) +def pairwise(iterable): + """s -> (s0,s1), (s2,s3), (s4, s5), ...""" + a = iter(iterable) + return itertools.izip(a, a) + + +def grouper(iterable, n, fillvalue=None): + """Collect data into fixed-length chunks or blocks + + Examples: + grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx + + """ + + args = [iter(iterable)] * n + return itertools.izip_longest(fillvalue=fillvalue, *args) + + def is_latest(representation): """Return whether the representation is from latest version diff --git a/colorbleed/maya/lib.py b/colorbleed/maya/lib.py index d4b0b8aa73..dcb9a91620 100644 --- a/colorbleed/maya/lib.py +++ b/colorbleed/maya/lib.py @@ -94,6 +94,11 @@ INT_FPS = {15, 24, 25, 30, 48, 50, 60, 44100, 48000} FLOAT_FPS = {23.976, 29.97, 47.952, 59.94} +def _get_mel_global(name): + """Return the value of a mel global variable""" + return mel.eval("$%s = $%s;" % (name, name)) + + def matrix_equals(a, b, tolerance=1e-10): """ Compares two matrices with an imperfection tolerance @@ -306,6 +311,33 @@ def attribute_values(attr_values): cmds.setAttr(attr, value) +@contextlib.contextmanager +def keytangent_default(in_tangent_type='auto', + out_tangent_type='auto'): + """Set the default keyTangent for new keys during this context""" + + original_itt = cmds.keyTangent(query=True, g=True, itt=True)[0] + original_ott = cmds.keyTangent(query=True, g=True, ott=True)[0] + cmds.keyTangent(g=True, itt=in_tangent_type) + cmds.keyTangent(g=True, ott=out_tangent_type) + try: + yield + finally: + cmds.keyTangent(g=True, itt=original_itt) + cmds.keyTangent(g=True, ott=original_ott) + + +@contextlib.contextmanager +def undo_chunk(): + """Open a undo chunk during context.""" + + try: + cmds.undoInfo(openChunk=True) + yield + finally: + cmds.undoInfo(closeChunk=True) + + @contextlib.contextmanager def renderlayer(layer): """Set the renderlayer during the context""" @@ -339,6 +371,126 @@ def evaluation(mode="off"): cmds.evaluationManager(mode=original) +@contextlib.contextmanager +def no_refresh(): + """Temporarily disables Maya's UI updates + + Note: + This only disabled the main pane and will sometimes still + trigger updates in torn off panels. + + """ + + pane = _get_mel_global('gMainPane') + state = cmds.paneLayout(pane, query=True, manage=True) + cmds.paneLayout(pane, edit=True, manage=False) + + try: + yield + finally: + cmds.paneLayout(pane, edit=True, manage=state) + + +@contextlib.contextmanager +def empty_sets(sets, force=False): + """Remove all members of the sets during the context""" + + assert isinstance(sets, (list, tuple)) + + original = dict() + original_connections = [] + + # Store original state + for obj_set in sets: + members = cmds.sets(obj_set, query=True) + original[obj_set] = members + + try: + for obj_set in sets: + cmds.sets(clear=obj_set) + if force: + # Break all connections if force is enabled, this way we + # prevent Maya from exporting any reference nodes which are + # connected with placeHolder[x] attributes + plug = "%s.dagSetMembers" % obj_set + connections = cmds.listConnections(plug, + source=True, + destination=False, + plugs=True, + connections=True) or [] + original_connections.extend(connections) + for dest, src in pairwise(connections): + cmds.disconnectAttr(src, dest) + yield + finally: + + for dest, src in pairwise(original_connections): + cmds.connectAttr(src, dest) + + # Restore original members + for origin_set, members in original.iteritems(): + cmds.sets(members, forceElement=origin_set) + + +@contextlib.contextmanager +def renderlayer(layer): + """Set the renderlayer during the context + + Arguments: + layer (str): Name of layer to switch to. + + """ + + original = cmds.editRenderLayerGlobals(query=True, + currentRenderLayer=True) + + try: + cmds.editRenderLayerGlobals(currentRenderLayer=layer) + yield + finally: + cmds.editRenderLayerGlobals(currentRenderLayer=original) + + +class delete_after(object): + """Context Manager that will delete collected nodes after exit. + + This allows to ensure the nodes added to the context are deleted + afterwards. This is useful if you want to ensure nodes are deleted + even if an error is raised. + + Examples: + with delete_after() as delete_bin: + cube = maya.cmds.polyCube() + delete_bin.extend(cube) + # cube exists + # cube deleted + + """ + + def __init__(self, nodes=None): + + self._nodes = list() + + if nodes: + self.extend(nodes) + + def append(self, node): + self._nodes.append(node) + + def extend(self, nodes): + self._nodes.extend(nodes) + + def __iter__(self): + return iter(self._nodes) + + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + if self._nodes: + cmds.delete(self._nodes) + + def get_renderer(layer): with renderlayer(layer): return cmds.getAttr("defaultRenderGlobals.currentRenderer") @@ -367,6 +519,157 @@ def no_undo(flush=False): cmds.undoInfo(**{keyword: original}) +def get_shader_assignments_from_shapes(shapes): + """Return the shape assignment per related shading engines. + + Returns a dictionary where the keys are shadingGroups and the values are + lists of assigned shapes or shape-components. + + For the 'shapes' this will return a dictionary like: + { + "shadingEngineX": ["nodeX", "nodeY"], + "shadingEngineY": ["nodeA", "nodeB"] + } + + Args: + shapes (list): The shapes to collect the assignments for. + + Returns: + dict: The {shadingEngine: shapes} relationships + + """ + + shapes = cmds.ls(shapes, + long=True, + selection=True, + shapes=True, + objectsOnly=True) + if not shapes: + return {} + + # Collect shading engines and their shapes + assignments = defaultdict(list) + for shape in shapes: + + # Get unique shading groups for the shape + shading_groups = cmds.listConnections(shape, + type="shadingEngine") or [] + shading_groups = list(set(shading_groups)) + for shading_group in shading_groups: + assignments[shading_group].add(shape) + + return dict(assignments) + + +@contextlib.contextmanager +def shader(nodes, shadingEngine="initialShadingGroup"): + """Assign a shader to nodes during the context""" + + shapes = cmds.ls(nodes, dag=1, o=1, shapes=1, long=1) + original = get_shader_assignments_from_shapes(shapes) + + try: + # Assign override shader + if shapes: + cmds.sets(shapes, edit=True, forceElement=shadingEngine) + yield + finally: + + # Assign original shaders + for sg, members in original.items(): + if members: + cmds.sets(shapes, edit=True, forceElement=shadingEngine) + + +@contextlib.contextmanager +def displaySmoothness(nodes, + divisionsU=0, + divisionsV=0, + pointsWire=4, + pointsShaded=1, + polygonObject=1): + """Set the displaySmoothness during the context""" + + # Ensure only non-intermediate shapes + nodes = cmds.ls(nodes, + dag=1, + shapes=1, + long=1, + noIntermediate=True) + + def parse(node): + """Parse the current state of a node""" + state = {} + for key in ["divisionsU", + "divisionsV", + "pointsWire", + "pointsShaded", + "polygonObject"]: + value = cmds.displaySmoothness(node, query=1, **{key: True}) + if value is not None: + state[key] = value[0] + return state + + originals = dict((node, parse(node)) for node in nodes) + + try: + # Apply current state + cmds.displaySmoothness(nodes, + divisionsU=divisionsU, + divisionsV=divisionsV, + pointsWire=pointsWire, + pointsShaded=pointsShaded, + polygonObject=polygonObject) + yield + finally: + # Revert state + for node, state in originals.iteritems(): + if state: + cmds.displaySmoothness(node, **state) + + +@contextlib.contextmanager +def no_display_layers(nodes): + """Ensure nodes are not in a displayLayer during context. + + Arguments: + nodes (list): The nodes to remove from any display layer. + + """ + + # Ensure long names + nodes = cmds.ls(nodes, long=True) + + # Get the original state + lookup = set(nodes) + original = {} + for layer in cmds.ls(type='displayLayer'): + + # Skip default layer + if layer == "defaultLayer": + continue + + members = cmds.editDisplayLayerMembers(layer, + query=True, + fullNames=True) + if not members: + continue + members = set(members) + + included = lookup.intersection(members) + if included: + original[layer] = list(included) + + try: + # Add all nodes to default layer + cmds.editDisplayLayerMembers("defaultLayer", nodes, noRecurse=True) + yield + finally: + # Restore original members + for layer, members in original.iteritems(): + cmds.editDisplayLayerMembers(layer, members, noRecurse=True) + + @contextlib.contextmanager def namespaced(namespace, new=True): """Work inside namespace during context @@ -1534,3 +1837,193 @@ def validate_fps(): return False return True + + +def bake(nodes, + frame_range=None, + step=1.0, + simulation=True, + preserve_outside_keys=False, + disable_implicit_control=True, + shape=True): + """Bake the given nodes over the time range. + + This will bake all attributes of the node, including custom attributes. + + Args: + nodes (list): Names of transform nodes, eg. camera, light. + frame_range (tuple): frame range with start and end frame. + or if None then takes timeSliderRange + simulation (bool): Whether to perform a full simulation of the + attributes over time. + preserve_outside_keys (bool): Keep keys that are outside of the baked + range. + disable_implicit_control (bool): When True will disable any + constraints to the object. + shape (bool): When True also bake attributes on the children shapes. + step (float): The step size to sample by. + + Returns: + None + + """ + + # Parse inputs + if not nodes: + return + + assert isinstance(nodes, (list, tuple)), "Nodes must be a list or tuple" + + # If frame range is None fall back to time slider range + if frame_range is None: + frame_range = getTimeSliderRange() + + # If frame range is single frame bake one frame more, + # otherwise maya.cmds.bakeResults gets confused + if frame_range[1] == frame_range[0]: + frame_range[1] += 1 + + # Bake it + with keytangent_default(in_tangent_type='auto', + out_tangent_type='auto'): + cmds.bakeResults(nodes, + simulation=simulation, + preserveOutsideKeys=preserve_outside_keys, + disableImplicitControl=disable_implicit_control, + shape=shape, + sampleBy=step, + time=(frame_range[0], frame_range[1])) + + +def bake_to_world_space(nodes, + frameRange=None, + simulation=True, + preserveOutsideKeys=False, + disableImplicitControl=True, + shape=True, + step=1.0): + """Bake the nodes to world space transformation (incl. other attributes) + + Bakes the transforms to world space (while maintaining all its animated + attributes and settings) by duplicating the node. Then parents it to world + and constrains to the original. + + Other attributes are also baked by connecting all attributes directly. + Baking is then done using Maya's bakeResults command. + + See `bake` for the argument documentation. + + Returns: + list: The newly created and baked node names. + + """ + + def _get_attrs(node): + """Workaround for buggy shape attribute listing with listAttr""" + attrs = cmds.listAttr(node, + write=True, + scalar=True, + settable=True, + connectable=True, + keyable=True, + shortNames=True) or [] + valid_attrs = [] + for attr in attrs: + node_attr = '{0}.{1}'.format(node, attr) + + # Sometimes Maya returns 'non-existent' attributes for shapes + # so we filter those out + if not cmds.attributeQuery(attr, node=node, exists=True): + continue + + # We only need those that have a connection, just to be safe + # that it's actually keyable/connectable anyway. + if cmds.connectionInfo(node_attr, + isDestination=True): + valid_attrs.append(attr) + + return valid_attrs + + transform_attrs = set(["t", "r", "s", + "tx", "ty", "tz", + "rx", "ry", "rz", + "sx", "sy", "sz"]) + + world_space_nodes = [] + with delete_after() as delete_bin: + + # Create the duplicate nodes that are in world-space connected to + # the originals + for node in nodes: + + # Duplicate the node + short_name = node.rsplit("|", 1)[-1] + new_name = "{0}_baked".format(short_name) + new_node = cmds.duplicate(node, + name=new_name, + renameChildren=True)[0] + + # Connect all attributes on the node except for transform + # attributes + attrs = _get_attrs(node) + attrs = set(attrs) - transform_attrs if attrs else [] + + for attr in attrs: + orig_node_attr = '{0}.{1}'.format(node, attr) + new_node_attr = '{0}.{1}'.format(new_node, attr) + + # unlock to avoid connection errors + cmds.setAttr(new_node_attr, lock=False) + + cmds.connectAttr(orig_node_attr, + new_node_attr, + force=True) + + # If shapes are also baked then connect those keyable attributes + if shape: + children_shapes = cmds.listRelatives(new_node, + children=True, + fullPath=True, + shapes=True) + if children_shapes: + orig_children_shapes = cmds.listRelatives(node, + children=True, + fullPath=True, + shapes=True) + for orig_shape, new_shape in zip(orig_children_shapes, + children_shapes): + attrs = _get_attrs(orig_shape) + for attr in attrs: + orig_node_attr = '{0}.{1}'.format(orig_shape, attr) + new_node_attr = '{0}.{1}'.format(new_shape, attr) + + # unlock to avoid connection errors + cmds.setAttr(new_node_attr, lock=False) + + cmds.connectAttr(orig_node_attr, + new_node_attr, + force=True) + + # Parent to world + if cmds.listRelatives(new_node, parent=True): + new_node = cmds.parent(new_node, world=True)[0] + + # Unlock transform attributes so constraint can be created + for attr in transform_attrs: + cmds.setAttr('{0}.{1}'.format(new_node, attr), lock=False) + + # Constraints + delete_bin.extend(cmds.parentConstraint(node, new_node, mo=False)) + delete_bin.extend(cmds.scaleConstraint(node, new_node, mo=False)) + + world_space_nodes.append(new_node) + + bake(world_space_nodes, + frame_range=frameRange, + step=step, + simulation=simulation, + preserve_outside_keys=preserveOutsideKeys, + disable_implicit_control=disableImplicitControl, + shape=shape) + + return world_space_nodes diff --git a/colorbleed/plugins/maya/create/colorbleed_camera.py b/colorbleed/plugins/maya/create/colorbleed_camera.py index 94c1a82225..1a87744d38 100644 --- a/colorbleed/plugins/maya/create/colorbleed_camera.py +++ b/colorbleed/plugins/maya/create/colorbleed_camera.py @@ -22,6 +22,6 @@ class CreateCamera(avalon.maya.Creator): # Bake to world space by default, when this is False it will also # include the parent hierarchy in the baked results - data['bakeToWorldSpace'] = True + data['bake_to_world_space'] = True self.data = data diff --git a/colorbleed/plugins/maya/publish/collect_look.py b/colorbleed/plugins/maya/publish/collect_look.py index 93080b5312..1decca6e2e 100644 --- a/colorbleed/plugins/maya/publish/collect_look.py +++ b/colorbleed/plugins/maya/publish/collect_look.py @@ -1,7 +1,10 @@ +import re +import os +import glob + from maya import cmds import pyblish.api import colorbleed.maya.lib as lib -from cb.utils.maya import context, shaders SHAPE_ATTRS = ["castsShadows", "receiveShadows", @@ -48,6 +51,139 @@ def get_look_attrs(node): return result +def node_uses_image_sequence(node): + """Return whether file node uses an image sequence or single image. + + Determine if a node uses an image sequence or just a single image, + not always obvious from its file path alone. + + Args: + node (str): Name of the Maya node + + Returns: + bool: True if node uses an image sequence + + """ + + # useFrameExtension indicates an explicit image sequence + node_path = get_file_node_path(node).lower() + + # The following tokens imply a sequence + patterns = ["", "", "", "u_v", ".tif will return as /path/to/texture.*.tif. + + Args: + path (str): the image sequence path + + Returns: + str: Return glob string that matches the filename pattern. + + """ + + if path is None: + return path + + # If any of the patterns, convert the pattern + patterns = { + "": "", + "": "", + "": "", + "#": "#", + "u_v": "|", + "", + "": "" + } + + lower = path.lower() + has_pattern = False + for pattern, regex_pattern in patterns.items(): + if pattern in lower: + path = re.sub(regex_pattern, "*", path, flags=re.IGNORECASE) + has_pattern = True + + if has_pattern: + return path + + base = os.path.basename(path) + matches = list(re.finditer(r'\d+', base)) + if matches: + match = matches[-1] + new_base = '{0}*{1}'.format(base[:match.start()], + base[match.end():]) + head = os.path.dirname(path) + return os.path.join(head, new_base) + else: + return path + + +def get_file_node_path(node): + """Get the file path used by a Maya file node. + + Args: + node (str): Name of the Maya file node + + Returns: + str: the file path in use + + """ + # if the path appears to be sequence, use computedFileTextureNamePattern, + # this preserves the <> tag + if cmds.attributeQuery('computedFileTextureNamePattern', + node=node, + exists=True): + plug = '{0}.computedFileTextureNamePattern'.format(node) + texture_pattern = cmds.getAttr(plug) + + patterns = ["", + "", + "u_v", + "", + ""] + lower = texture_pattern.lower() + if any(pattern in lower for pattern in patterns): + return texture_pattern + + # otherwise use fileTextureName + return cmds.getAttr('{0}.fileTextureName'.format(node)) + + +def get_file_node_files(node): + """Return the file paths related to the file node + + Note: + Will only return existing files. Returns an empty list + if not valid existing files are linked. + + Returns: + list: List of full file paths. + + """ + + path = get_file_node_path(node) + path = cmds.workspace(expandName=path) + if node_uses_image_sequence(node): + glob_pattern = seq_to_glob(path) + return glob.glob(glob_pattern) + elif os.path.exists(path): + return [path] + else: + return [] + + class CollectLook(pyblish.api.InstancePlugin): """Collect look data for instance. @@ -74,7 +210,7 @@ class CollectLook(pyblish.api.InstancePlugin): def process(self, instance): """Collect the Look in the instance with the correct layer settings""" - with context.renderlayer(instance.data["renderlayer"]): + with lib.renderlayer(instance.data["renderlayer"]): self.collect(instance) def collect(self, instance): @@ -268,7 +404,7 @@ class CollectLook(pyblish.api.InstancePlugin): # paths as the computed patterns source = source.replace("\\", "/") - files = shaders.get_file_node_files(node) + files = get_file_node_files(node) if len(files) == 0: self.log.error("No valid files found from node `%s`" % node) diff --git a/colorbleed/plugins/maya/publish/extract_camera_alembic.py b/colorbleed/plugins/maya/publish/extract_camera_alembic.py index 2b18ced96f..efacf7b528 100644 --- a/colorbleed/plugins/maya/publish/extract_camera_alembic.py +++ b/colorbleed/plugins/maya/publish/extract_camera_alembic.py @@ -5,14 +5,14 @@ from maya import cmds import avalon.maya import colorbleed.api -import cb.utils.maya.context as context +import colorbleed.maya.lib as lib class ExtractCameraAlembic(colorbleed.api.Extractor): """Extract a Camera as Alembic. The cameras gets baked to world space by default. Only when the instance's - `bakeToWorldSpace` is set to False it will include its full hierarchy. + `bake_to_world_space` is set to False it will include its full hierarchy. """ @@ -27,7 +27,7 @@ class ExtractCameraAlembic(colorbleed.api.Extractor): instance.data.get("endFrame", 1)] handles = instance.data.get("handles", 0) step = instance.data.get("step", 1.0) - bake_to_worldspace = instance.data("bakeToWorldSpace", True) + bake_to_worldspace = instance.data("bake_to_world_space", True) # get cameras members = instance.data['setMembers'] @@ -66,8 +66,8 @@ class ExtractCameraAlembic(colorbleed.api.Extractor): job_str += ' -file "{0}"'.format(path) - with context.evaluation("off"): - with context.no_refresh(): + with lib.evaluation("off"): + with lib.no_refresh(): cmds.AbcExport(j=job_str, verbose=False) if "files" not in instance.data: diff --git a/colorbleed/plugins/maya/publish/extract_camera_mayaAscii.py b/colorbleed/plugins/maya/publish/extract_camera_mayaAscii.py index c29aaed4fe..bd0eeba05f 100644 --- a/colorbleed/plugins/maya/publish/extract_camera_mayaAscii.py +++ b/colorbleed/plugins/maya/publish/extract_camera_mayaAscii.py @@ -1,13 +1,11 @@ import os -from itertools import izip_longest from maya import cmds import avalon.maya import colorbleed.api - -import cb.utils.maya.context as context -from cb.utils.maya.animation import bakeToWorldSpace +from colorbleed.lib import grouper +from colorbleed.maya import lib def massage_ma_file(path): @@ -36,18 +34,6 @@ def massage_ma_file(path): f.close() -def grouper(iterable, n, fillvalue=None): - """Collect data into fixed-length chunks or blocks - - Examples: - grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx - - """ - - args = [iter(iterable)] * n - return izip_longest(fillvalue=fillvalue, *args) - - def unlock(plug): """Unlocks attribute and disconnects inputs for a plug. @@ -87,7 +73,7 @@ class ExtractCameraMayaAscii(colorbleed.api.Extractor): will be published. The cameras gets baked to world space by default. Only when the instance's - `bakeToWorldSpace` is set to False it will include its full hierarchy. + `bake_to_world_space` is set to False it will include its full hierarchy. Note: The extracted Maya ascii file gets "massaged" removing the uuid values @@ -106,12 +92,12 @@ class ExtractCameraMayaAscii(colorbleed.api.Extractor): instance.data.get("endFrame", 1)] handles = instance.data.get("handles", 0) step = instance.data.get("step", 1.0) - bake_to_worldspace = instance.data("bakeToWorldSpace", True) + bake_to_worldspace = instance.data("bake_to_world_space", True) # TODO: Implement a bake to non-world space # Currently it will always bake the resulting camera to world-space # and it does not allow to include the parent hierarchy, even though - # with `bakeToWorldSpace` set to False it should include its hierarchy + # with `bake_to_world_space` set to False it should include its hierarchy # to be correct with the family implementation. if not bake_to_worldspace: self.log.warning("Camera (Maya Ascii) export only supports world" @@ -140,11 +126,13 @@ class ExtractCameraMayaAscii(colorbleed.api.Extractor): # Perform extraction self.log.info("Performing camera bakes for: {0}".format(transform)) with avalon.maya.maintained_selection(): - with context.evaluation("off"): - with context.no_refresh(): - baked = bakeToWorldSpace(transform, - frameRange=range_with_handles, - step=step) + with lib.evaluation("off"): + with lib.no_refresh(): + baked = lib.bake_to_worldspace( + transform, + frameRange=range_with_handles, + step=step + ) baked_shapes = cmds.ls(baked, type="camera", dag=True, diff --git a/colorbleed/plugins/maya/publish/extract_look.py b/colorbleed/plugins/maya/publish/extract_look.py index 7e8fc2b436..c359fc10b5 100644 --- a/colorbleed/plugins/maya/publish/extract_look.py +++ b/colorbleed/plugins/maya/publish/extract_look.py @@ -6,10 +6,9 @@ from maya import cmds import pyblish.api import avalon.maya -import colorbleed.api -import colorbleed.maya.lib as maya -from cb.utils.maya import context +import colorbleed.api +import colorbleed.maya.lib as lib class ExtractLook(colorbleed.api.Extractor): @@ -63,10 +62,10 @@ class ExtractLook(colorbleed.api.Extractor): # Extract in correct render layer layer = instance.data.get("renderlayer", "defaultRenderLayer") - with context.renderlayer(layer): + with lib.renderlayer(layer): # TODO: Ensure membership edits don't become renderlayer overrides - with context.empty_sets(sets, force=True): - with maya.attribute_values(remap): + with lib.empty_sets(sets, force=True): + with lib.attribute_values(remap): with avalon.maya.maintained_selection(): cmds.select(sets, noExpand=True) cmds.file(maya_path, diff --git a/colorbleed/plugins/maya/publish/extract_model.py b/colorbleed/plugins/maya/publish/extract_model.py index e1be53d59a..466ee73174 100644 --- a/colorbleed/plugins/maya/publish/extract_model.py +++ b/colorbleed/plugins/maya/publish/extract_model.py @@ -4,8 +4,7 @@ from maya import cmds import avalon.maya import colorbleed.api - -from cb.utils.maya import context +import colorbleed.maya.lib as lib class ExtractModel(colorbleed.api.Extractor): @@ -47,15 +46,15 @@ class ExtractModel(colorbleed.api.Extractor): noIntermediate=True, long=True) - with context.no_display_layers(instance): - with context.displaySmoothness(members, - divisionsU=0, - divisionsV=0, - pointsWire=4, - pointsShaded=1, - polygonObject=1): - with context.shader(members, - shadingEngine="initialShadingGroup"): + 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"): with avalon.maya.maintained_selection(): cmds.select(members, noExpand=True) cmds.file(path, diff --git a/colorbleed/plugins/maya/publish/validate_look_sets.py b/colorbleed/plugins/maya/publish/validate_look_sets.py index 31884ab48f..aeec58674a 100644 --- a/colorbleed/plugins/maya/publish/validate_look_sets.py +++ b/colorbleed/plugins/maya/publish/validate_look_sets.py @@ -4,8 +4,6 @@ from colorbleed.maya import lib import pyblish.api import colorbleed.api -from cb.utils.maya import context - class ValidateLookSets(pyblish.api.InstancePlugin): """Validate if any sets are missing from the instance and look data @@ -57,7 +55,7 @@ class ValidateLookSets(pyblish.api.InstancePlugin): invalid = [] renderlayer = instance.data.get("renderlayer", "defaultRenderLayer") - with context.renderlayer(renderlayer): + with lib.renderlayer(renderlayer): for node in instance: # get the connected objectSets of the node sets = lib.get_related_sets(node) diff --git a/colorbleed/plugins/maya/publish/validate_rig_controllers.py b/colorbleed/plugins/maya/publish/validate_rig_controllers.py index 6117f46597..ecb3b687d1 100644 --- a/colorbleed/plugins/maya/publish/validate_rig_controllers.py +++ b/colorbleed/plugins/maya/publish/validate_rig_controllers.py @@ -1,10 +1,10 @@ from maya import cmds import pyblish.api -import colorbleed.api -from cb.utils.maya.context import undo_chunk +import colorbleed.api import colorbleed.maya.action +from colorbleed.maya.lib import undo_chunk class ValidateRigControllers(pyblish.api.InstancePlugin): diff --git a/colorbleed/plugins/maya/publish/validate_rig_controllers_arnold_attributes.py b/colorbleed/plugins/maya/publish/validate_rig_controllers_arnold_attributes.py index 6655e59a67..43bfde918f 100644 --- a/colorbleed/plugins/maya/publish/validate_rig_controllers_arnold_attributes.py +++ b/colorbleed/plugins/maya/publish/validate_rig_controllers_arnold_attributes.py @@ -2,8 +2,8 @@ from maya import cmds import pyblish.api import colorbleed.api -from cb.utils.maya.context import undo_chunk +import colorbleed.maya.lib as lib import colorbleed.maya.action @@ -83,7 +83,7 @@ class ValidateRigControllersArnoldAttributes(pyblish.api.InstancePlugin): def repair(cls, instance): invalid = cls.get_invalid(instance) - with undo_chunk(): + with lib.undo_chunk(): for node in invalid: for attribute in cls.attributes: if cmds.attributeQuery(attribute, node=node, exists=True):