Merged in Aardschok/config (pull request #8)

Improvements and additions
This commit is contained in:
Wijnand Koreman 2017-08-10 12:38:51 +00:00 committed by Roy Nieterau
commit 6254b02df4
16 changed files with 292 additions and 187 deletions

View file

@ -97,11 +97,7 @@ def on_save(_):
avalon.logger.info("Running callback on save..")
types = ["objectSet", "file", "mesh", "nurbsCurve", "nurbsSurface"]
type_nodes = set(cmds.ls(type=types, long=True))
nodes = lib.filter_out_nodes(type_nodes,
defaults=True,
referenced_nodes=True)
nodes = lib.get_id_required_nodes(referenced_nodes=False)
# Lead with asset ID from the database
asset = os.environ["AVALON_ASSET"]
@ -111,5 +107,4 @@ def on_save(_):
# generate the ids
for node in nodes:
print node
_set_uuid(str(asset_id["_id"]), node)

View file

@ -595,55 +595,45 @@ def maya_temp_folder():
return tmp_dir
def remap_resource_nodes(resources, folder=None):
log.info("Updating resource nodes ...")
for resource in resources:
source = resource["source"]
if folder:
fname = os.path.basename(source)
fpath = os.path.join(folder, fname)
else:
fpath = source
node_attr = resource["attribute"]
cmds.setAttr(node_attr, fpath, type="string")
log.info("Saving file ...")
cmds.file(save=True, type="mayaAscii")
def filter_out_nodes(nodes, defaults=False, referenced_nodes=False):
def get_id_required_nodes(referenced_nodes=False):
"""Filter out any node which are locked (reference) or readOnly
Args:
nodes (set): nodes to filter
locked (bool): set True to filter out lockedNodes
readonly (bool): set True to filter out readOnly
referenced_nodes (bool): set True to filter out reference nodes
Returns:
nodes (list): list of filtered nodes
nodes (set): list of filtered nodes
"""
# establish set of nodes to ignore
# `readOnly` flag is obsolete as of Maya 2016 therefor we explicitly remove
# default nodes and reference nodes
camera_shapes = ["frontShape", "sideShape", "topShape", "perspShape"]
ignore = set()
if referenced_nodes:
ignore |= set(cmds.ls(long=True, referencedNodes=referenced_nodes))
if not referenced_nodes:
ignore |= set(cmds.ls(long=True, referencedNodes=True))
if defaults:
ignore |= set(cmds.ls(long=True, defaultNodes=defaults))
# list all defaultNodes to filter out from the rest
ignore |= set(cmds.ls(long=True, defaultNodes=True))
ignore |= set(cmds.ls(camera_shapes, long=True))
# establish set of nodes to ignore
types = ["objectSet", "file", "mesh", "nurbsCurve", "nurbsSurface"]
# We *always* ignore intermediate shapes, so we filter them out
# directly
nodes = cmds.ls(type=types, long=True, noIntermediate=True)
# The items which need to pass the id to their parent
# Add the collected transform to the nodes
dag = cmds.ls(list(nodes),
type="dagNode",
long=True) # query only dag nodes
dag = cmds.ls(nodes, type="dagNode", long=True) # query only dag nodes
transforms = cmds.listRelatives(dag,
parent=True,
fullPath=True) or []
nodes = set(nodes)
nodes |= set(transforms)
nodes -= ignore # Remove the ignored nodes
return nodes
@ -663,14 +653,10 @@ def get_id(node):
if node is None:
return
try:
attr = "{}.cbId".format(node)
attribute_value = cmds.getAttr(attr)
except Exception as e:
log.debug(e)
if not cmds.attributeQuery("cbId", node=node, exists=True):
return
return attribute_value
return cmds.getAttr("{}.cbId".format(node))
def get_representation_file(representation, template=TEMPLATE):
@ -910,7 +896,7 @@ def apply_shaders(relationships, shadernodes, nodes):
"""
attributes = relationships.get("attributes", [])
shader_sets = relationships.get("sets", [])
shader_data = relationships.get("relationships", {})
shading_engines = cmds.ls(shadernodes, type="objectSet", long=True)
assert len(shading_engines) > 0, ("Error in retrieving objectSets "
@ -927,10 +913,10 @@ def apply_shaders(relationships, shadernodes, nodes):
# endregion
# region assign
for shader_set in shader_sets:
for data in shader_data.values():
# collect all unique IDs of the set members
shader_uuid = shader_set["uuid"]
member_uuids = [member["uuid"] for member in shader_set["members"]]
shader_uuid = data["uuid"]
member_uuids = [member["uuid"] for member in data["members"]]
filtered_nodes = list()
for uuid in member_uuids:

View file

@ -1,7 +1,5 @@
import tempfile
import pyblish.api
import avalon.maya
ValidatePipelineOrder = pyblish.api.ValidatorOrder + 0.05
ValidateContentsOrder = pyblish.api.ValidatorOrder + 0.1

View file

@ -67,7 +67,7 @@ class CollectLook(pyblish.api.InstancePlugin):
hosts = ["maya"]
# Ignore specifically named sets (check with endswith)
IGNORE = ["out_SET", "controls_SET", "_INST"]
IGNORE = ["out_SET", "controls_SET", "_INST", "_CON"]
def process(self, instance):
"""Collect the Look in the instance with the correct layer settings"""
@ -120,19 +120,20 @@ class CollectLook(pyblish.api.InstancePlugin):
# Store data on the instance
instance.data["lookData"] = {"attributes": attributes,
"relationships": sets.values(),
"sets": looksets}
"relationships": sets}
# Collect file nodes used by shading engines
history = cmds.listHistory(looksets)
files = cmds.ls(history, type="file", long=True)
# Collect file nodes used by shading engines (if we have any)
files = list()
if looksets:
history = cmds.listHistory(looksets)
files = cmds.ls(history, type="file", long=True)
# Collect textures
resources = [self.collect_resource(n) for n in files]
instance.data["resources"] = resources
# Log a warning when no relevant sets were retrieved for the look.
if not instance.data["lookData"]["sets"]:
if not instance.data["lookData"]["relationships"]:
self.log.warning("No sets found for the nodes in the instance: "
"%s" % instance[:])
@ -168,8 +169,7 @@ class CollectLook(pyblish.api.InstancePlugin):
if objset in sets:
continue
sets[objset] = {"name": objset,
"uuid": lib.get_id(objset),
sets[objset] = {"uuid": lib.get_id(objset),
"members": list()}
return sets

View file

@ -41,7 +41,7 @@ class ExtractLook(colorbleed.api.Extractor):
# exported file by accident
self.log.info("Extract sets (Maya ASCII) ...")
lookdata = instance.data["lookData"]
sets = lookdata["sets"]
sets = lookdata["relationships"].keys()
resources = instance.data["resources"]
remap = {}
@ -72,7 +72,7 @@ class ExtractLook(colorbleed.api.Extractor):
# Write the JSON data
self.log.info("Extract json..")
data = {"attributes": lookdata["attributes"],
"sets": lookdata["relationships"]}
"relationships": lookdata["relationships"]}
with open(json_path, "w") as f:
json.dump(data, f)

View file

@ -1,5 +1,8 @@
import maya.cmds as cmds
import pyblish.api
import colorbleed.api
import colorbleed.maya.lib as lib
class ValidateLookContents(pyblish.api.InstancePlugin):
@ -13,32 +16,32 @@ class ValidateLookContents(pyblish.api.InstancePlugin):
order = colorbleed.api.ValidateContentsOrder
families = ['colorbleed.lookdev']
hosts = ['maya']
label = 'Look Contents'
label = 'Look Data Contents'
actions = [colorbleed.api.SelectInvalidAction]
invalid = []
errors = []
def process(self, instance):
"""Process all the nodes in the instance"""
if not instance[:]:
raise RuntimeError("Instance is empty")
self.get_invalid(instance)
if self.errors:
error_string = "\n".join(self.errors)
raise RuntimeError("Invalid look content. "
"Errors : {}".format(error_string))
invalid = self.get_invalid(instance)
if invalid:
raise RuntimeError("'{}' has invalid look "
"content".format(instance.name))
@classmethod
def get_invalid(cls, instance):
"""Get all invalid nodes"""
invalid_attr = list(cls.validate_lookdata_attributes(instance))
invalid_rels = list(cls.validate_relationships(instance))
cls.log.info("Validating look content for "
"'{}'".format(instance.name))
invalid = invalid_attr + invalid_rels
instance_items = cls.validate_instance_items(instance)
attributes = list(cls.validate_lookdata_attributes(instance))
relationships = list(cls.validate_relationship_ids(instance))
invalid = instance_items + attributes + relationships
return invalid
@ -53,30 +56,50 @@ class ValidateLookContents(pyblish.api.InstancePlugin):
invalid = set()
attributes = ["sets", "relationships", "attributes"]
attributes = ["relationships", "attributes"]
lookdata = instance.data["lookData"]
for attr in attributes:
if attr not in lookdata:
cls.errors.append("Look Data has no attribute "
"'{}'".format(attr))
cls.log.error("Look Data has no attribute "
"'{}'".format(attr))
invalid.add(instance.name)
# Validate at least one single relationship is collected
if not lookdata["relationships"]:
cls.log.error("Look '{}' has no "
"`relationship`".format(instance.name))
invalid.add(instance.name)
return invalid
@classmethod
def validate_relationships(cls, instance):
def validate_relationship_ids(cls, instance):
"""Validate and update lookData relationships"""
invalid = set()
relationships = instance.data["lookData"]["relationships"]
for relationship in relationships:
look_name = relationship["name"]
for key, value in relationship.items():
if value is None:
cls.errors.append("{} has invalid attribite "
"'{}'".format(look_name, key))
invalid.add(look_name)
for objectset, members in relationships.items():
uuid = members["uuid"]
if not uuid:
look_name = objectset
cls.log.error("{} has invalid ID ".format(look_name))
invalid.add(look_name)
return invalid
@classmethod
def validate_instance_items(cls, instance):
required_nodes = lib.get_id_required_nodes(referenced_nodes=False)
invalid = [node for node in instance if node in required_nodes
and not lib.get_id(node)]
if invalid:
nr_of_invalid = len(invalid)
cls.log.error("Found {} nodes without ID: {}".format(nr_of_invalid,
invalid))
return invalid

View file

@ -1,3 +1,5 @@
from maya import cmds
import pyblish.api
import colorbleed.api
@ -20,11 +22,10 @@ class ValidateLookDefaultShadersConnections(pyblish.api.InstancePlugin):
label = 'Look Default Shader Connections'
# The default connections to check
DEFAULTS = [
("initialShadingGroup.surfaceShader", "lambert1"),
("initialParticleSE.surfaceShader", "lambert1"),
("initialParticleSE.volumeShader", "particleCloud1")
]
DEFAULTS = [("initialShadingGroup.surfaceShader", "lambert1"),
("initialParticleSE.surfaceShader", "lambert1"),
("initialParticleSE.volumeShader", "particleCloud1")
]
def process(self, instance):
@ -33,19 +34,15 @@ class ValidateLookDefaultShadersConnections(pyblish.api.InstancePlugin):
# the family is not present in an instance.
key = "__validate_look_default_shaders_connections_checked"
context = instance.context
is_run = context.data.get(key,
False)
is_run = context.data.get(key, False)
if is_run:
return
else:
context.data[key] = True
# Process as usual
from maya import cmds
invalid = list()
for plug, input_node in self.DEFAULTS:
inputs = cmds.listConnections(plug,
source=True,
destination=False) or None

View file

@ -3,7 +3,7 @@ import colorbleed.api
import colorbleed.maya.lib as lib
class ValidateLookMembersHaveId(pyblish.api.InstancePlugin):
class ValidateLookMembers(pyblish.api.InstancePlugin):
"""Validate look members have colorbleed id attributes
Looks up the contents of the look to see if all its members have
@ -18,35 +18,27 @@ class ValidateLookMembersHaveId(pyblish.api.InstancePlugin):
order = colorbleed.api.ValidatePipelineOrder
families = ['colorbleed.lookdev']
hosts = ['maya']
label = 'Look Members Have ID Attribute'
label = 'Look Members'
actions = [colorbleed.api.SelectInvalidAction,
colorbleed.api.GenerateUUIDsOnInvalidAction]
def process(self, instance):
"""Process all meshes"""
invalid = self.get_invalid(instance)
if invalid:
raise RuntimeError("Members found without "
"asset IDs: {0}".format(invalid))
invalid_ids = self.get_invalid(instance)
if invalid_ids:
raise RuntimeError("Found invalid nodes.\nNo ID : "
"{}".format(invalid_ids))
@classmethod
def get_invalid(cls, instance):
# Get all members from the sets
members = []
relations = instance.data["lookData"]["relationships"]
for relation in relations:
members = [member['name'] for member in relation['members']]
members.extend(members)
members = set()
relationships = instance.data["lookData"]["relationships"]
for relation in relationships.values():
members.update([member['name'] for member in relation['members']])
# Get all sets
members = list(set(members))
# Ensure all nodes have a cbId
invalid = list()
for node in members:
if not lib.get_id(node):
invalid.append(node)
invalid = [m for m in members if not lib.get_id(m)]
return invalid

View file

@ -4,15 +4,7 @@ from maya import cmds
import pyblish.api
import colorbleed.api
def get_unique_id(node):
attr = 'cbId'
unique_id = None
has_attribute = cmds.attributeQuery(attr, node=node, exists=True)
if has_attribute:
unique_id = cmds.getAttr("{}.{}".format(node, attr))
return unique_id
import colorbleed.maya.lib as lib
class ValidateNonDuplicateRelationshipMembers(pyblish.api.InstancePlugin):
@ -43,26 +35,22 @@ class ValidateNonDuplicateRelationshipMembers(pyblish.api.InstancePlugin):
# Get all members from the sets
members = []
relationships = instance.data["lookData"]["relationships"]
for sg in relationships:
sg_members = [member['name'] for member in sg['members']]
members.extend(sg_members)
for relationship in relationships.values():
members.extend([i['name'] for i in relationship['members']])
# Ensure we don't have components but the objects
members = cmds.ls(members, objectsOnly=True, long=True)
members = list(set(members))
members = set(cmds.ls(members, objectsOnly=True, long=True))
members = list(members)
# Group members per id
id_nodes = defaultdict(set)
for node in members:
node_id = get_unique_id(node)
node_id = lib.get_id(node)
if not node_id:
continue
id_nodes[node_id].add(node)
invalid = list()
for nodes in id_nodes.itervalues():
if len(nodes) > 1:
invalid.extend(nodes)
invalid = [n for n in id_nodes.itervalues() if len(n) > 1]
return invalid

View file

@ -37,16 +37,15 @@ class ValidateLookNoDefaultShaders(pyblish.api.InstancePlugin):
"initialParticleSE",
"particleCloud1"]
setmembers = instance.data["setMembers"]
members = cmds.listRelatives(setmembers,
members = cmds.listRelatives(instance,
allDescendents=True,
type="shape")
shapes=True,
noIntermediate=True) or []
for member in members:
# get connection
# listConnections returns a list or None
shading_engine = cmds.listConnections(member, type="shadingEngine")
shading_engine = cmds.listConnections(member, type="objectSet")
if not shading_engine:
cls.log.error("Detected shape without shading engine : "
"'{}'".format(member))
@ -58,6 +57,7 @@ class ValidateLookNoDefaultShaders(pyblish.api.InstancePlugin):
if shading_engine in disallowed:
cls.log.error("Member connected to a disallows objectSet: "
"'{}'".format(member))
invalid.append(member)
else:
continue
@ -66,7 +66,6 @@ class ValidateLookNoDefaultShaders(pyblish.api.InstancePlugin):
def process(self, instance):
"""Process all the nodes in the instance"""
# sets = self.get_invalid_sets(instance)
sets = self.get_invalid(instance)
if sets:
raise RuntimeError("Invalid shaders found: {0}".format(sets))
invalid = self.get_invalid(instance)
if invalid:
raise RuntimeError("Invalid shaders found: {0}".format(invalid))

View file

@ -1,9 +1,9 @@
import pprint
from collections import defaultdict
import maya.cmds as cmds
import pyblish.api
import colorbleed.api
import colorbleed.maya.lib as lib
class ValidateLookNodeUniqueIds(pyblish.api.InstancePlugin):
@ -18,26 +18,44 @@ class ValidateLookNodeUniqueIds(pyblish.api.InstancePlugin):
actions = [colorbleed.api.SelectInvalidAction,
colorbleed.api.RepairAction]
@staticmethod
def get_invalid(instance):
@classmethod
def get_invalid(cls, instance):
nodes = instance.data["lookData"]["sets"]
invalid = []
uuids_dict = defaultdict(list)
# Ensure all nodes have a cbId
id_sets = defaultdict(list)
invalid = list()
for node in nodes:
unique_id = None
if cmds.attributeQuery("cbId", node=node, exists=True):
unique_id = cmds.getAttr("{}.cbId".format(node))
if not unique_id:
continue
relationships = instance.data["lookData"]["relationships"]
pprint.pprint(relationships)
for objectset, relationship in relationships.items():
cls.log.info("Validating lookData for '%s'" % objectset)
# check if node has UUID and this matches with found node
for member in relationship["members"]:
node = member["name"]
member_uuid = member["uuid"]
uuid_query = lib.get_id(node)
id_sets[unique_id].append(node)
if not member_uuid:
cls.log.error("No UUID found for '{}'".format(node))
invalid.append(node)
continue
for unique_id, nodes in id_sets.iteritems():
if len(nodes) > 1:
invalid.extend(nodes)
if uuid_query != member_uuid:
cls.log.error("UUID in lookData does not match with "
"queried UUID of '{}'".format(node))
invalid.append(node)
continue
# check if the uuid has already passed through the check
# if so it means its a duplicate.
uuids_dict[objectset].append(uuid_query)
for objectset, member_uuids in uuids_dict.items():
stored = len(member_uuids)
unique = len(set(member_uuids))
if unique != stored:
rel_members = relationships[objectset]["members"]
invalid.extend([i["name"] for i in rel_members if
i["uuid"] not in unique])
return invalid
@ -46,5 +64,7 @@ class ValidateLookNodeUniqueIds(pyblish.api.InstancePlugin):
invalid = self.get_invalid(instance)
if invalid:
raise RuntimeError("Nodes found without "
"asset IDs: {0}".format(invalid))
for item in invalid:
self.log.error("Invalid node : %s" % item)
raise RuntimeError("Nodes found without unique "
"IDs, see records")

View file

@ -0,0 +1,61 @@
from maya import cmds
import pyblish.api
import colorbleed.api
class ValidateSingleShader(pyblish.api.InstancePlugin):
"""Validate default shaders in the scene have their default connections.
For example the lambert1 could potentially be disconnected from the
initialShadingGroup. As such it's not lambert1 that will be identified
as the default shader which can have unpredictable results.
To fix the default connections need to be made again. See the logs for
more details on which connections are missing.
"""
order = colorbleed.api.ValidateContentsOrder
families = ['colorbleed.lookdev']
hosts = ['maya']
label = 'Look Single Shader Per Shape'
# The default connections to check
def process(self, instance):
invalid = self.get_invalid(instance)
if invalid:
raise RuntimeError("Found shapes which have multiple "
"shadingEngines connected."
"\n{}".format(invalid))
@classmethod
def get_invalid(cls, instance):
invalid = []
shape_types = ["numrbsCurve", "nurbsSurface", "mesh"]
# Get all shapes from the instance
shapes = set()
for node in instance[:]:
nodetype = cmds.nodeType(node)
if nodetype in shape_types:
shapes.add(node)
elif nodetype == "transform":
shape = cmds.listRelatives(node, children=True,
type="shape", fullPath=True)
if not shape:
continue
shapes.add(shape[0])
# Check the number of connected shadingEngines per shape
for shape in shapes:
shading_engines = cmds.listConnections(shape,
destination=True,
type="shadingEngine") or []
if len(shading_engines) > 1:
invalid.append(shape)
return invalid

View file

@ -20,11 +20,16 @@ class ValidateNoAnimation(pyblish.api.Validator):
optional = True
actions = [colorbleed.api.SelectInvalidAction]
def process(self, instance):
invalid = self.get_invalid(instance)
if invalid:
raise RuntimeError("Keyframes found: {0}".format(invalid))
@staticmethod
def get_invalid(instance):
nodes = instance[:]
if not nodes:
return []
@ -33,10 +38,3 @@ class ValidateNoAnimation(pyblish.api.Validator):
return list(set(cmds.listConnections(curves)))
return []
def process(self, instance):
invalid = self.get_invalid(instance)
if invalid:
raise RuntimeError("Keyframes found: {0}".format(invalid))

View file

@ -1,5 +1,3 @@
import maya.cmds as cmds
import pyblish.api
import colorbleed.api
@ -12,7 +10,7 @@ class ValidateNodeIDs(pyblish.api.InstancePlugin):
"""
order = colorbleed.api.ValidatePipelineOrder
label = 'Node Ids (ID)'
label = 'Instance Nodes Have ID'
hosts = ['maya']
families = ["colorbleed.model",
"colorbleed.lookdev",
@ -33,19 +31,11 @@ class ValidateNodeIDs(pyblish.api.InstancePlugin):
@classmethod
def get_invalid(cls, instance):
"""Return the member nodes that are invalid"""
invalid = list()
# TODO: Implement check on only nodes like on_save callback.
instance_shape = cmds.ls(instance, type="shape")
# We do want to check the referenced nodes as we it might be
# We do want to check the referenced nodes as it might be
# part of the end product
nodes = lib.filter_out_nodes(set(instance_shape), defaults=True)
for node in nodes:
if not lib.get_id(node):
invalid.append(node)
id_nodes = lib.get_id_required_nodes(referenced_nodes=True)
invalid = [n for n in instance[:] if n in id_nodes
and not lib.get_id(n)]
return invalid

View file

@ -0,0 +1,59 @@
import pyblish.api
import colorbleed.api
import avalon.io as io
from colorbleed.maya import lib
class ValidateNodeIDsRelated(pyblish.api.InstancePlugin):
"""Validate nodes have a related Colorbleed Id to the instance asset
"""
order = colorbleed.api.ValidatePipelineOrder
label = 'Node Ids Related (ID)'
hosts = ['maya']
families = ["colorbleed.model",
"colorbleed.lookdev",
"colorbleed.rig"]
actions = [colorbleed.api.SelectInvalidAction,
colorbleed.api.GenerateUUIDsOnInvalidAction]
def process(self, instance):
"""Process all nodes in instance (including hierarchy)"""
# Ensure all nodes have a cbId
invalid = self.get_invalid(instance)
if invalid:
raise RuntimeError("Nodes IDs found that are not related to asset "
"'{}' : {}".format(instance.data['asset'],
invalid))
@classmethod
def get_invalid(cls, instance):
"""Return the member nodes that are invalid"""
invalid = list()
asset = instance.data['asset']
asset_data = io.find_one({"name": asset,
"type": "asset"},
projection={"_id": True})
asset_id = str(asset_data['_id'])
# We do want to check the referenced nodes as we it might be
# part of the end product
for node in instance:
_id = lib.get_id(node)
if not _id:
continue
node_asset_id = _id.split(":", 1)[0]
if node_asset_id != asset_id:
invalid.append(node)
return invalid

View file

@ -100,7 +100,6 @@ class ValidateRigContents(pyblish.api.InstancePlugin):
"""
errors = []
for node in nodes:
print node
if node not in hierarchy:
errors.append(node)
return errors