improved collectors

This commit is contained in:
aardschok 2017-07-25 13:37:03 +02:00
parent e0fa549f0b
commit 9a70383051
3 changed files with 233 additions and 25 deletions

View file

@ -3,7 +3,7 @@ from maya import cmds
import pyblish.api
class CollectMindbenderInstances(pyblish.api.ContextPlugin):
class CollectInstances(pyblish.api.ContextPlugin):
"""Gather instances by objectSet and pre-defined attribute
This collector takes into account assets that are associated with
@ -38,14 +38,24 @@ class CollectMindbenderInstances(pyblish.api.ContextPlugin):
"""
label = "Collect Mindbender Instances"
label = "Collect Instances"
order = pyblish.api.CollectorOrder
hosts = ["maya"]
isntance_order = ["colorbleed.model",
"colorbleed.rig",
"colorbleed.animation",
"colorbleed.camera",
"colorbleed.texture",
"colorbleed.lookdev",
"colorbleed.historyLookdev",
"colorbleed.group"]
def process(self, context):
objectset = cmds.ls("*.id", long=True, type="objectSet",
recursive=True, objectsOnly=True)
for objset in objectset:
self.log.info("Creating instance for {}".format(objset))
members = cmds.sets(objset, query=True)
if members is None:
@ -61,7 +71,8 @@ class CollectMindbenderInstances(pyblish.api.ContextPlugin):
# The developer is responsible for specifying
# the family of each instance.
has_family = cmds.attributeQuery("family", node=objset,
has_family = cmds.attributeQuery("family",
node=objset,
exists=True)
assert has_family, "\"%s\" was missing a family" % objset
@ -70,7 +81,7 @@ class CollectMindbenderInstances(pyblish.api.ContextPlugin):
# Apply each user defined attribute as data
for attr in cmds.listAttr(objset, userDefined=True) or list():
try:
value = cmds.getAttr("{}.{}".format(objset, attr))
value = cmds.getAttr("%s.%s" % (objset, attr))
except Exception:
# Some attributes cannot be read directly,
# such as mesh and color attributes. These
@ -82,9 +93,10 @@ class CollectMindbenderInstances(pyblish.api.ContextPlugin):
# Collect members
members = cmds.ls(members, long=True) or []
children = cmds.listRelatives(members,
allDescendents=True,
fullPath=True)
fullPath=True) or []
parents = self.get_all_parents(members)
members_hierarchy = list(set(members + children + parents))
@ -99,6 +111,10 @@ class CollectMindbenderInstances(pyblish.api.ContextPlugin):
# user interface interested in visualising it.
self.log.info("Found: \"%s\" " % instance.data["name"])
context[:] = sorted(context)
return context
def get_all_parents(self, nodes):
"""Get all parents by using string operations (optimization)
@ -108,6 +124,7 @@ class CollectMindbenderInstances(pyblish.api.ContextPlugin):
Returns:
list
"""
parents = []
for node in nodes:
splitted = node.split("|")

View file

@ -1,9 +1,9 @@
from maya import cmds
from cb.utils.maya import context
import cbra.utils.maya.node_uuid as id_utils
import pyblish.api
from cb.utils.maya import context, shaders
import cbra.utils.maya.node_uuid as id_utils
SHAPE_ATTRS = ["castsShadows",
"receiveShadows",
@ -37,8 +37,8 @@ def get_look_attrs(node):
valid = [attr for attr in attrs if attr in SHAPE_ATTRS]
result.extend(valid)
if "mbID" in result:
result.remove("mbID")
if "cbId" in result:
result.remove("cbId")
return result
@ -62,7 +62,7 @@ class CollectLook(pyblish.api.InstancePlugin):
"""
order = pyblish.api.CollectorOrder + 0.4
families = ["colorbleed.look"]
families = ["colorbleed.lookdev"]
label = "Collect Look"
hosts = ["maya"]
@ -120,6 +120,9 @@ class CollectLook(pyblish.api.InstancePlugin):
instance.data["lookData"] = {"attributes": attributes,
"relationships": sets.values(),
"sets": looksets}
# Collect textures
resources = [self.collect_resources(n) for n in cmds.ls(type="file")]
instance.data["resources"] = resources
# Log a warning when no relevant sets were retrieved for the look.
if not instance.data["lookData"]["sets"]:
@ -157,7 +160,8 @@ class CollectLook(pyblish.api.InstancePlugin):
for objset in related_sets:
if objset in sets:
continue
unique_id = cmds.getAttr("%s.mbID" % objset)
unique_id = cmds.getAttr("%s.cbId" % objset)
sets[objset] = {"name": objset,
"uuid": unique_id,
"members": list()}
@ -253,11 +257,24 @@ class CollectLook(pyblish.api.InstancePlugin):
if member in [m["name"] for m in objset_members]:
return
# check node type, if mesh get parent! makes assigning shaders easier
if cmds.nodeType(node) == "mesh":
parent = cmds.listRelatives(node, parent=True, fullPath=True)
# a mesh NEEDS to have a parent in Maya logic, no reason for
# assertions or extra checking
parent = parent[0]
if cmds.attributeQuery("cbId", node=parent, exists=True):
node = parent
else:
self.log.error("Transform group of mesh '{}' has no attribute "
"'cbId', this is manditory")
return
if verbose:
self.log.debug("Such as %s.." % member)
member_data = {"name": node,
"uuid": cmds.getAttr("{}.mbID".format(node, ))}
"uuid": cmds.getAttr("{}.cbId".format(node))}
# Include components information when components are assigned
if components:
@ -304,3 +321,46 @@ class CollectLook(pyblish.api.InstancePlugin):
attributes.append(data)
return attributes
def collect_resources(self, node, verbose=False):
"""Collect the link to the file(s) used (resource)
Args:
node (str): name of the node
verbose (bool): enable debug information
Returns:
dict
"""
attribute = "{}.fileTextureName".format(node)
source = cmds.getAttr(attribute)
# Get the computed file path (e.g. the one with the <UDIM> pattern
# in it) So we can reassign it this computed file path whenever
# we need to.
computed_attribute = "{}.computedFileTextureNamePattern".format(node)
computed_source = cmds.getAttr(computed_attribute)
if source != computed_source:
if verbose:
self.log.debug("File node computed pattern differs from "
"original pattern: {0} "
"({1} -> {2})".format(node,
source,
computed_source))
# We replace backslashes with forward slashes because V-Ray
# can't handle the UDIM files with the backslashes in the
# paths as the computed patterns
source = computed_source.replace("\\", "/")
files = shaders.get_file_node_files(node)
if not files:
self.log.error("File node does not have a texture set: "
"{0}".format(node))
return
# Define the resource
return {"node": node,
"attribute": attribute,
"source": source, # required for resources
"files": files} # required for resources

View file

@ -1,7 +1,6 @@
from maya import cmds
import pyblish.api
import cb.utils.maya.shaders as shaders
TAGS = ["maya", "attribute", "look"]
@ -72,23 +71,37 @@ class CollectLookTextures(pyblish.api.InstancePlugin):
"""
order = pyblish.api.CollectorOrder + 0.498
label = 'Textures'
order = pyblish.api.CollectorOrder + 0.35
label = 'Collect Look Textures'
families = ["colorbleed.texture"]
actions = [SelectTextureNodesAction]
IGNORE = ["out_SET", "controls_SET", "_INST"]
def process(self, instance):
verbose = instance.data.get("verbose", False)
# Get textures from sets
sets = instance.data["lookData"]["sets"]
if not sets:
raise RuntimeError("No look sets found for the nodes in the "
"instance. %s" % sets)
# Get all texture nodes from the shader networks
sets = self.gather_sets(instance)
instance_members = {str(i) for i in cmds.ls(instance, long=True,
absoluteName=True)}
self.log.info("Gathering set relations..")
for objset in sets:
self.log.debug("From %s.." % objset)
content = cmds.sets(objset, query=True)
objset_members = sets[objset]["members"]
for member in cmds.ls(content, long=True, absoluteName=True):
member_data = self.collect_member_data(member,
objset_members,
instance_members,
verbose)
if not member_data:
continue
# Get the file nodes
history = cmds.listHistory(sets) or []
history = cmds.listHistory(sets.keys()) or []
files = cmds.ls(history, type="file")
files = list(set(files))
@ -99,9 +112,44 @@ class CollectLookTextures(pyblish.api.InstancePlugin):
continue
resources.append(resource)
# Store resources
instance.data['resources'] = resources
def gather_sets(self, instance):
"""Gather all objectSets which are of importance for publishing
It checks if all nodes in the instance are related to any objectSet
which need to be
Args:
instance (list): all nodes to be published
Returns:
dict
"""
# Get view sets (so we can ignore those sets later)
sets = dict()
view_sets = set()
for panel in cmds.getPanel(type="modelPanel"):
view_set = cmds.modelEditor(panel, query=True,
viewObjects=True)
if view_set:
view_sets.add(view_set)
for node in instance:
related_sets = self.get_related_sets(node, view_sets)
if not related_sets:
continue
for objset in related_sets:
if objset in sets:
continue
unique_id = cmds.getAttr("%s.cbId" % objset)
sets[objset] = {"name": objset,
"uuid": unique_id,
"members": list()}
return sets
def collect_resources(self, node, verbose=False):
"""Collect the link to the file(s) used (resource)
Args:
@ -112,6 +160,8 @@ class CollectLookTextures(pyblish.api.InstancePlugin):
dict
"""
# assure node includes full path
node = cmds.ls(node, long=True)[0]
attribute = "{}.fileTextureName".format(node)
source = cmds.getAttr(attribute)
@ -146,8 +196,89 @@ class CollectLookTextures(pyblish.api.InstancePlugin):
"node": node,
"attribute": attribute,
"source": source, # required for resources
"files": files, # required for resources
"subfolder": "textures", # optional for resources
}
"files": files} # required for resources
return resource
def collect_member_data(self, member, objset_members, instance_members,
verbose=False):
"""Get all information of the node
Args:
member (str): the name of the node to check
objset_members (list): the objectSet members
instance_members (set): the collected instance members
verbose (bool): get debug information
Returns:
dict
"""
node, components = (member.rsplit(".", 1) + [None])[:2]
# Only include valid members of the instance
if node not in instance_members:
if verbose:
self.log.info("Skipping member %s" % member)
return
if member in [m["name"] for m in objset_members]:
return
if verbose:
self.log.debug("Such as %s.." % member)
member_data = {"name": node,
"uuid": cmds.getAttr("{}.cbId".format(node, ))}
# Include components information when components are assigned
if components:
member_data["components"] = components
return member_data
def get_related_sets(self, node, view_sets):
"""Get the sets which do not belong to any specific group
Filters out based on:
- id attribute is NOT `pyblish.avalon.container`
- shapes and deformer shapes (alembic creates meshShapeDeformed)
- set name ends with any from a predefined list
- set in not in viewport set (isolate selected for example)
Args:
node (str): name of the current not to check
"""
ignored = ["pyblish.avalon.instance", "pyblish.avalon.container"]
related_sets = cmds.listSets(object=node, extendToShape=False)
if not related_sets:
return []
# Ignore containers
sets = [s for s in related_sets if
not cmds.attributeQuery("id", node=s, exists=True) or
not cmds.getAttr("%s.id" % s) in ignored]
# Exclude deformer sets
# Autodesk documentation on listSets command:
# type(uint) : Returns all sets in the scene of the given
# >>> type:
# >>> 1 - all rendering sets
# >>> 2 - all deformer sets
deformer_sets = cmds.listSets(object=node, extendToShape=False,
type=2) or []
deformer_sets = set(deformer_sets) # optimize lookup
sets = [s for s in sets if s not in deformer_sets]
# Ignore specifically named sets
sets = [s for s in sets if not any(s.endswith(x) for x in self.IGNORE)]
# Ignore viewport filter view sets (from isolate select and
# viewports)
sets = [s for s in sets if s not in view_sets]
self.log.info("Found sets %s for %s" % (related_sets, node))
return sets