merging with upstream

This commit is contained in:
aardschok 2017-06-28 13:41:18 +02:00
commit 524d80ebf2
32 changed files with 107 additions and 173 deletions

View file

@ -22,4 +22,3 @@ class CollectAlembicCBAttrs(pyblish.api.InstancePlugin):
# Ensure UVs are written
instance.data['uvWrite'] = True

View file

@ -1,6 +1,8 @@
import pyblish.api
import maya.cmds as cmds
import pyblish.api
class CollectInstancerCaches(pyblish.api.InstancePlugin):
"""For an Instancer collect the history.
@ -31,10 +33,10 @@ class CollectInstancerCaches(pyblish.api.InstancePlugin):
# correctly.
if not files:
errors = True
self.log.error("Cache has no files: {0}".format(cache))
self.log.error("Cache has no files: %s" % cache)
continue
source = files[0] # The first file is the .xml file
source = files[0] # The first file is the .xml file
# TODO: Filter the files to only contain the required frame range.

View file

@ -31,14 +31,14 @@ class CollectInstancerHistory(pyblish.api.InstancePlugin):
export.extend(particles)
if particles:
self.log.info("Particles: {0}".format(particles))
particles_history = cmds.listHistory(particles) or []
self.log.debug("Particle history: {0}".format(particles_history))
nucleus = cmds.ls(particles_history, long=True, type="nucleus")
self.log.info("Collected nucleus: {0}".format(nucleus))
export.extend(nucleus)
caches = cmds.ls(particles_history, long=True, type="cacheFile")
self.log.info("Collected caches: {0}".format(caches))
export.extend(caches)

View file

@ -150,5 +150,3 @@ class CollectLookTextures(pyblish.api.InstancePlugin):
}
return resource

View file

@ -86,7 +86,8 @@ class ExtractInstancerMayaAscii(colorbleed.api.Extractor):
hosts = ["maya"]
families = ["colorbleed.instancer"]
# TODO: Find other solution than expanding vars to fix lack of support of cacheFile
# TODO: Find other solution than expanding vars to fix lack of support
# TODO: of cacheFile
def process(self, instance):
@ -94,6 +95,35 @@ class ExtractInstancerMayaAscii(colorbleed.api.Extractor):
# Set up cacheFile path remapping.
resources = instance.data.get("resources", [])
attr_remap, cache_remap = self.process_resources(resources)
# Define extract output file path
dir_path = self.staging_dir(instance)
filename = "{0}.ma".format(instance.name)
path = os.path.join(dir_path, filename)
# Perform extraction
self.log.info("Performing extraction..")
with avalon.maya.maintained_selection():
with cache_file_paths(cache_remap):
with context.attribute_values(attr_remap):
cmds.select(export, noExpand=True)
cmds.file(path,
force=True,
typ="mayaAscii",
exportSelected=True,
preserveReferences=False,
constructionHistory=False,
channels=True, # allow animation
constraints=False,
shader=False,
expressions=False)
self.log.info("Extracted instance '{0}' to: {1}".format(
instance.name, path))
def process_resources(self, resources):
attr_remap = dict()
cache_remap = dict()
for resource in resources:
@ -113,32 +143,10 @@ class ExtractInstancerMayaAscii(colorbleed.api.Extractor):
folder += "/"
# Set path and name
attr_remap["{0}.cacheName".format(node)] = os.path.expandvars(fname)
attr_remap["{0}.cacheName".format(node)] = os.path.expandvars(
fname)
cache_remap[node] = os.path.expandvars(folder)
self.log.info("Mapping {0} to {1}".format(node, destination))
# Define extract output file path
dir_path = self.staging_dir(instance)
filename = "{0}.ma".format(instance.name)
path = os.path.join(dir_path, filename)
# Perform extraction
self.log.info("Performing extraction..")
with avalon.maya.maintained_selection():
with cache_file_paths(cache_remap):
with context.attribute_values(attr_remap):
cmds.select(export, noExpand=True)
cmds.file(path,
force=True,
typ="mayaAscii",
exportSelected=True,
preserveReferences=False,
constructionHistory=False,
channels=True, # allow animation
constraints=False,
shader=False,
expressions=False)
self.log.info("Extracted instance '{0}' to: {1}".format(
instance.name, path))
return attr_remap, cache_remap

View file

@ -41,11 +41,12 @@ class ExtractLook(colorbleed.api.Extractor):
# Define the texture file node remapping
resource_remap = dict()
required = ["maya", "attribute", "look"] # required tags to be a look resource
# required tags to be a look resource
required_tags = ["maya", "attribute", "look"]
resources = instance.data.get("resources", [])
for resource in resources:
resource_tags = resource.get("tags", [])
if all(tag in resource_tags for tag in required):
if all(tag in resource_tags for tag in required_tags):
node = resource['node']
destination = resource['destination']
resource_remap["{}.fileTextureName".format(node)] = destination

View file

@ -37,11 +37,12 @@ class ExtractFurYeti(colorbleed.api.Extractor):
# Remap cache files names and ensure fileMode is set to load from cache
resource_remap = dict()
required = ["maya", "yeti", "attribute"] # required tags to be a yeti resource
# required tags to be a yeti resource
required_tags = ["maya", "yeti", "attribute"]
resources = instance.data.get("resources", [])
for resource in resources:
resource_tags = resource.get("tags", [])
if all(tag in resource_tags for tag in required):
if all(tag in resource_tags for tag in required_tags):
attribute = resource['attribute']
destination = resource['destination']
resource_remap[attribute] = destination

View file

@ -61,4 +61,3 @@ class ValidateCameraContents(pyblish.api.InstancePlugin):
if invalid:
raise RuntimeError("Invalid camera contents: "
"{0}".format(invalid))

View file

@ -43,7 +43,8 @@ class ValidateFrameRange(pyblish.api.InstancePlugin):
self.log.info("Comparing start (%s) and end (%s)" % (start, end))
if start > end:
raise RuntimeError("The start frame is a higher value "
"than the end frame: {0}>{1}".format(start, end))
"than the end frame: "
"{0}>{1}".format(start, end))
if handles is not None:
if handles < 0.0:

View file

@ -9,22 +9,21 @@ import colorbleed.api
def get_gpu_cache_subnodes(cache):
"""Return the amount of subnodes in the cache
This uses `maya.cmds.gpuCache(showStats=True)` and parses
the resulting stats for the amount of internal sub nodes.
Args:
cache (str): gpuCache node name.
Returns:
int: Amount of subnodes in loaded gpuCache
Raises:
TypeError: when `cache` is not a gpuCache object type.
RuntimeError: when `maya.cmds.gpuCache(showStats=True)`
does not return stats from which we can parse the
amount of subnodes.
"""
# Ensure gpuCache
@ -65,10 +64,9 @@ def get_empty_gpu_caches(caches):
class ValidateGPUCacheNotEmpty(pyblish.api.InstancePlugin):
"""Validates that gpuCaches have at least one visible shape in them.
This is tested using the `maya.cmds.gpuCache(cache, showStats=True)`
command.
"""
order = colorbleed.api.ValidateContentsOrder
@ -78,14 +76,12 @@ class ValidateGPUCacheNotEmpty(pyblish.api.InstancePlugin):
@classmethod
def get_invalid(cls, instance):
caches = cmds.ls(instance, type="gpuCache", long=True)
invalid = get_empty_gpu_caches(caches)
return invalid
def process(self, instance):
invalid = self.get_invalid(instance)
if invalid:
raise RuntimeError("Invalid nodes found: {0}".format(invalid))

View file

@ -71,4 +71,3 @@ class ValidateInstancerContent(pyblish.api.InstancePlugin):
return False
return True

View file

@ -165,4 +165,4 @@ class ValidateInstancerFrameRanges(pyblish.api.InstancePlugin):
if invalid:
self.log.error("Invalid nodes: {0}".format(invalid))
raise RuntimeError("Invalid particle caches in instance. "
"See logs for details.")
"See logs for details.")

View file

@ -4,6 +4,7 @@ import colorbleed.api
import avalon.maya
import cb.utils.maya.dag as dag
import cbra.utils.maya.layout as layout
class ValidateLayoutContent(pyblish.api.InstancePlugin):
@ -30,15 +31,15 @@ class ValidateLayoutContent(pyblish.api.InstancePlugin):
placeholder = instance.data.get("placeholder", False)
# Ensure any meshes or gpuCaches in instance
if not cmds.ls(instance, type=("mesh", "gpuCache", "nurbsCurve"), long=True):
raise RuntimeError("Layout has no mesh, gpuCache or nurbsCurve children: "
"{0}".format(instance))
if not cmds.ls(instance, type=("mesh", "gpuCache", "nurbsCurve"),
long=True):
raise RuntimeError(
"Layout has no mesh, gpuCache or nurbsCurve children: "
"{0}".format(instance))
# Ensure at least any extract nodes readily available after filtering
with avalon.maya.maintained_selection():
import cbra.utils.maya.layout as layout
nodes = instance.data['setMembers']
cmds.select(nodes, r=1, hierarchy=True)
hierarchy = cmds.ls(sl=True, long=True)
@ -84,4 +85,3 @@ class ValidateLayoutContent(pyblish.api.InstancePlugin):
raise RuntimeError("No extract shape is visible. "
"Layout requires at least one "
"shape to be visible.")

View file

@ -42,4 +42,3 @@ class ValidateLayoutNodeIds(pyblish.api.InstancePlugin):
if invalid:
raise RuntimeError("Transforms (non-referenced) found in layout "
"without asset IDs: {0}".format(invalid))

View file

@ -17,9 +17,8 @@ _ATTRS = ['tx', 'ty', 'tz',
def is_identity(node, tolerance=1e-30):
mat = cmds.xform(node, query=True, matrix=True, objectSpace=True)
if not all(abs(x-y) < tolerance for x, y in zip(_IDENTITY, mat)):
if not all(abs(x - y) < tolerance for x, y in zip(_IDENTITY, mat)):
return False
return True
@ -49,12 +48,12 @@ class ValidateLayoutParentNoTransforms(pyblish.api.InstancePlugin):
@staticmethod
def get_invalid(instance):
invalid = []
# Get highest in hierarchy
nodes = instance.data["setMembers"]
highest = getHighestInHierarchy(nodes)
invalid = []
for node in highest:
for parent in iterParents(node):
if not is_identity(parent) or is_animated(parent):
@ -70,4 +69,3 @@ class ValidateLayoutParentNoTransforms(pyblish.api.InstancePlugin):
if invalid:
raise RuntimeError("Transforms (non-referenced) found in layout "
"without asset IDs: {0}".format(invalid))

View file

@ -62,4 +62,3 @@ class ValidateLayoutUniqueNodeIds(pyblish.api.InstancePlugin):
if invalid:
raise RuntimeError("Transforms found with non-unique "
"asset IDs: {0}".format(invalid))

View file

@ -52,4 +52,3 @@ class ValidateLookMembersNodeIds(pyblish.api.InstancePlugin):
if invalid:
raise RuntimeError("Members found without "
"asset IDs: {0}".format(invalid))

View file

@ -66,4 +66,3 @@ class ValidateLookMembersUnique(pyblish.api.InstancePlugin):
if invalid:
raise RuntimeError("Members found without "
"asset IDs: {0}".format(invalid))

View file

@ -65,12 +65,11 @@ class ValidateMeshHasUVs(pyblish.api.InstancePlugin):
vertex = cmds.polyEvaluate(node, vertex=True)
if uv < vertex:
# Workaround:
# Maya can have instanced UVs in a single mesh, for example
# imported from an Alembic. With instanced UVs the UV count from
# `maya.cmds.polyEvaluate(uv=True)` will only result in the unique
# UV count instead of for all vertices.
# imported from an Alembic. With instanced UVs the UV count
# from `maya.cmds.polyEvaluate(uv=True)` will only result in
# the unique UV count instead of for all vertices.
#
# Note: Maya can save instanced UVs to `mayaAscii` but cannot
# load this as instanced. So saving, opening and saving
@ -90,7 +89,6 @@ class ValidateMeshHasUVs(pyblish.api.InstancePlugin):
def process(self, instance):
invalid = self.get_invalid(instance)
if invalid:
raise RuntimeError("Meshes found in instance without "
"valid UVs: {0}".format(invalid))

View file

@ -6,7 +6,7 @@ import colorbleed.api
class ValidateMeshLaminaFaces(pyblish.api.InstancePlugin):
"""Validate meshes don't have lamina faces.
Lamina faces share all of their edges.
"""
@ -22,7 +22,10 @@ class ValidateMeshLaminaFaces(pyblish.api.InstancePlugin):
@staticmethod
def get_invalid(instance):
meshes = cmds.ls(instance, type='mesh', long=True)
return [mesh for mesh in meshes if cmds.polyInfo(mesh, laminaFaces=True)]
invalid = [mesh for mesh in meshes if
cmds.polyInfo(mesh, laminaFaces=True)]
return invalid
def process(self, instance):
"""Process all the nodes in the instance 'objectSet'"""

View file

@ -29,9 +29,8 @@ class ValidateMeshNonZeroEdgeLength(pyblish.api.InstancePlugin):
@classmethod
def get_invalid(cls, instance):
"""Return the invalid edges.
Also see: http://help.autodesk.com/view/MAYAUL/2015/ENU/?guid=Mesh__Cleanup
"""
meshes = cmds.ls(instance, type='mesh', long=True)
@ -43,7 +42,7 @@ class ValidateMeshNonZeroEdgeLength(pyblish.api.InstancePlugin):
# Filter by constraint on edge length
invalid = polyConstraint(edges,
t=0x8000, # type=edge
t=0x8000, # type=edge
length=1,
lengthbound=(0, cls.__tolerance))

View file

@ -24,12 +24,12 @@ class ValidateMeshSingleUVSet(pyblish.api.InstancePlugin):
invalid = []
for mesh in meshes:
uvSets = cmds.polyUVSet(mesh,
query=True,
uvSets = cmds.polyUVSet(mesh,
query=True,
allUVSets=True) or []
# ensure unique (sometimes maya will list 'map1' twice)
uvSets = set(uvSets)
uvSets = set(uvSets)
if len(uvSets) != 1:
invalid.append(mesh)
@ -112,7 +112,8 @@ class ValidateMeshSingleUVSet(pyblish.api.InstancePlugin):
indices = cmds.getAttr('{0}.uvSet'.format(mesh),
multiIndices=True)
if not indices:
cls.log.warning("No uv set found indices for: {0}".format(mesh))
cls.log.warning(
"No uv set found indices for: {0}".format(mesh))
return
# Delete from end to avoid shifting indices

View file

@ -39,21 +39,21 @@ def len_flattened(components):
class ValidateMeshVerticesHaveEdges(pyblish.api.InstancePlugin):
"""Validate meshes have only vertices that are connected by to edges.
Maya can have invalid geometry with vertices that have no edges or
faces connected to them.
In Maya 2016 EXT 2 and later there's a command to fix this:
`maya.cmds.polyClean(mesh, cleanVertices=True)
In older versions of Maya it works to select the invalid vertices
and merge the components.
To find these invalid vertices select all vertices of the mesh
that are visible in the viewport (drag to select), afterwards
invert your selection (Ctrl + Shift + I). The remaining selection
contains the invalid vertices.
"""
order = colorbleed.api.ValidateMeshOrder

View file

@ -4,65 +4,7 @@ from maya import cmds
import pyblish.api
import colorbleed.api
def is_visible(node,
displayLayer=True,
intermediateObject=True,
parentHidden=True,
visibility=True):
"""Is `node` visible?
Returns whether a node is hidden by one of the following methods:
- The node exists (always checked)
- The node must be a dagNode (always checked)
- The node's visibility is off.
- The node is set as intermediate Object.
- The node is in a disabled displayLayer.
- Whether any of its parent nodes is hidden.
Roughly based on: http://ewertb.soundlinker.com/mel/mel.098.php
Returns:
bool: Whether the node is visible in the scene
"""
# Only existing objects can be visible
if not cmds.objExists(node):
return False
# Only dagNodes can be visible
if not cmds.objectType(node, isAType='dagNode'):
return False
if visibility:
if not cmds.getAttr('{0}.visibility'.format(node)):
return False
if intermediateObject and cmds.objectType(node, isAType='shape'):
if cmds.getAttr('{0}.intermediateObject'.format(node)):
return False
if displayLayer:
# Display layers set overrideEnabled and overrideVisibility on members
if cmds.attributeQuery('overrideEnabled', node=node, exists=True):
if cmds.getAttr('{0}.overrideEnabled'.format(node)) and \
cmds.getAttr('{0}.overrideVisibility'.format(node)):
return False
if parentHidden:
parents = cmds.listRelatives(node, parent=True, fullPath=True)
if parents:
parent = parents[0]
if not is_visible(parent,
displayLayer=displayLayer,
intermediateObject=False,
parentHidden=parentHidden,
visibility=visibility):
return False
return True
import colorbleed.maya.lib as lib
class ValidateModelContent(pyblish.api.InstancePlugin):
@ -114,11 +56,11 @@ class ValidateModelContent(pyblish.api.InstancePlugin):
def _is_visible(node):
"""Return whether node is visible"""
return is_visible(node,
displayLayer=False,
intermediateObject=True,
parentHidden=True,
visibility=True)
return lib.is_visible(node,
displayLayer=False,
intermediateObject=True,
parentHidden=True,
visibility=True)
# The roots must be visible (the assemblies)
for assembly in assemblies:
@ -141,4 +83,3 @@ class ValidateModelContent(pyblish.api.InstancePlugin):
if invalid:
raise RuntimeError("Model content is invalid. See log.")

View file

@ -1,6 +1,7 @@
from maya import cmds
import pyblish.api
import colorbleed.api
from maya import cmds
class ValidateNamespaceEmpty(pyblish.api.ContextPlugin):
@ -8,7 +9,7 @@ class ValidateNamespaceEmpty(pyblish.api.ContextPlugin):
This is a scene wide validation that filters out "UI" and "shared"
namespaces that exist by default in Maya and are mostly hidden.
A namespace that has other namespaces in it is *not* considered empty.
Only those that have no children namespaces or nodes is considered empty.
@ -35,5 +36,4 @@ class ValidateNamespaceEmpty(pyblish.api.ContextPlugin):
if not namespace_content:
invalid.append(namespace)
assert not invalid, (
"Empty namespaces found: {0}".format(invalid))
assert not invalid, ("Empty namespaces found: {0}".format(invalid))

View file

@ -60,7 +60,6 @@ class ValidateNoNullTransforms(pyblish.api.InstancePlugin):
def process(self, instance):
"""Process all the transform nodes in the instance """
invalid = self.get_invalid(instance)
if invalid:
raise ValueError("Empty transforms found: {0}".format(invalid))
@ -73,6 +72,5 @@ class ValidateNoNullTransforms(pyblish.api.InstancePlugin):
"""
invalid = cls.get_invalid(instance)
if invalid:
cmds.delete(invalid)

View file

@ -12,12 +12,12 @@ class ValidateNoVRayMesh(pyblish.api.InstancePlugin):
def process(self, instance):
shapes = cmds.ls(instance,
shapes=True,
type="mesh")
shapes=True,
type="mesh")
inputs = cmds.listConnections(shapes,
destination=False,
source=True) or []
destination=False,
source=True) or []
vray_meshes = cmds.ls(inputs, type='VRayMesh')
if vray_meshes:
raise RuntimeError("Meshes that are VRayMeshes shouldn't "

View file

@ -1,7 +1,7 @@
import pyblish.api
import colorbleed.api
from maya import cmds
import pyblish.api
import colorbleed.api
class RepairFailedEditsAction(pyblish.api.Action):
@ -10,7 +10,6 @@ class RepairFailedEditsAction(pyblish.api.Action):
icon = "wrench" # Icon from Awesome Icon
def process(self, context, plugin):
from maya import cmds
self.log.info("Finding bad nodes..")
# Get the errored instances
@ -108,7 +107,6 @@ class ValidateReferencesNoFailedEdits(pyblish.api.InstancePlugin):
"""Process all the nodes in the instance"""
invalid = self.get_invalid(instance)
if invalid:
raise ValueError("Reference nodes found with failed "
"reference edits: {0}".format(invalid))

View file

@ -3,6 +3,7 @@ import maya.cmds as cmds
import pyblish.api
import colorbleed.api
import cbra.lib
import cbra.utils.maya.node_uuid as id_utils
@ -56,8 +57,6 @@ class ValidateRigPointcacheRelatedNodeIds(pyblish.api.InstancePlugin):
@classmethod
def get_invalid(cls, instance):
import cbra.lib
# Get a full context from the instance context
context = instance.data['instanceContext']
item_path = context['itemPath']
@ -101,8 +100,6 @@ class ValidateRigPointcacheRelatedNodeIds(pyblish.api.InstancePlugin):
# Ensure all nodes have a cbId
invalid = self.get_invalid(instance)
if invalid:
raise RuntimeError("Nodes found with non-related "
"asset IDs: {0}".format(invalid))

View file

@ -39,7 +39,8 @@ class ValidateSceneSetWorkspace(pyblish.api.ContextPlugin):
scene_name = cmds.file(query=True, sceneName=True)
if not scene_name:
raise RuntimeError("Scene hasn't been saved. Workspace can't be validated.")
raise RuntimeError("Scene hasn't been saved. Workspace can't be "
"validated.")
root_dir = cmds.workspace(query=True, rootDirectory=True)

View file

@ -33,13 +33,13 @@ class ValidateTransfers(pyblish.api.InstancePlugin):
for destination, sources in collected.items():
if len(sources) > 1:
if verbose:
self.log.error("Non-unique file transfer for resources: {0} "
"(sources: {1})".format(destination, sources))
self.log.error("Non-unique file transfer for resources: "
"{0} (sources: {1})".format(destination,
sources))
invalid = True
invalid_destinations.append(destination)
if invalid:
if not verbose:
# If not verbose then still log the resource destination as
# opposed to every individual file transfer