mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 12:54:40 +01:00
Merge branch 'master' of https://bitbucket.org/colorbleed/config
This commit is contained in:
commit
06a7864a6c
12 changed files with 319 additions and 175 deletions
|
|
@ -12,6 +12,8 @@ from .plugin import (
|
|||
|
||||
# temporary fix, might
|
||||
from .action import (
|
||||
|
||||
get_errored_instances_from_context,
|
||||
SelectInvalidAction,
|
||||
GenerateUUIDsOnInvalidAction,
|
||||
RepairAction,
|
||||
|
|
@ -24,7 +26,8 @@ all = [
|
|||
"ValidateContentsOrder",
|
||||
"ValidateSceneOrder",
|
||||
"ValidateMeshOrder",
|
||||
|
||||
# action
|
||||
"get_errored_instances_from_context",
|
||||
"SelectInvalidAction",
|
||||
"GenerateUUIDsOnInvalidAction",
|
||||
"RepairAction"
|
||||
|
|
|
|||
|
|
@ -242,3 +242,63 @@ def collect_animation_data():
|
|||
|
||||
def get_current_renderlayer():
|
||||
return cmds.editRenderLayerGlobals(query=True, currentRenderLayer=True)
|
||||
|
||||
|
||||
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):
|
||||
override_enabled = cmds.getAttr('{}.overrideEnabled'.format(node))
|
||||
override_visibility = cmds.getAttr('{}.overrideVisibility'.format(node))
|
||||
if override_enabled and override_visibility:
|
||||
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
|
||||
|
|
@ -7,6 +7,11 @@ class AbcLoader(api.Loader):
|
|||
families = ["colorbleed.animation", "colorbleed.camera"]
|
||||
representations = ["abc"]
|
||||
|
||||
label = "Reference animation"
|
||||
order = -10
|
||||
icon = "code-fork"
|
||||
color = "orange"
|
||||
|
||||
def process(self, name, namespace, context):
|
||||
from maya import cmds
|
||||
|
||||
|
|
@ -31,6 +36,10 @@ class CurvesLoader(api.Loader):
|
|||
families = ["colorbleed.animation"]
|
||||
representations = ["curves"]
|
||||
|
||||
label = "Import curves"
|
||||
order = -1
|
||||
icon = "question"
|
||||
|
||||
def process(self, name, namespace, context):
|
||||
from maya import cmds
|
||||
from avalon import maya
|
||||
|
|
|
|||
|
|
@ -8,6 +8,11 @@ class HistoryLookLoader(api.Loader):
|
|||
families = ["colorbleed.historyLookdev"]
|
||||
representations = ["ma"]
|
||||
|
||||
label = "Reference look history"
|
||||
order = -10
|
||||
icon = "code-fork"
|
||||
color = "orange"
|
||||
|
||||
def process(self, name, namespace, context):
|
||||
from avalon import maya
|
||||
with maya.maintained_selection():
|
||||
|
|
|
|||
|
|
@ -11,6 +11,11 @@ class LookLoader(api.Loader):
|
|||
families = ["colorbleed.lookdev"]
|
||||
representations = ["ma"]
|
||||
|
||||
label = "Reference look"
|
||||
order = -10
|
||||
icon = "code-fork"
|
||||
color = "orange"
|
||||
|
||||
def process(self, name, namespace, context):
|
||||
from avalon import maya
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -5,15 +5,16 @@ from avalon import maya
|
|||
|
||||
|
||||
class ModelLoader(api.Loader):
|
||||
"""Load models
|
||||
|
||||
Stores the imported asset in a container named after the asset.
|
||||
|
||||
"""
|
||||
"""Load the model"""
|
||||
|
||||
families = ["colorbleed.model"]
|
||||
representations = ["ma"]
|
||||
|
||||
label = "Reference model"
|
||||
order = -10
|
||||
icon = "code-fork"
|
||||
color = "orange"
|
||||
|
||||
def process(self, name, namespace, context):
|
||||
|
||||
with maya.maintained_selection():
|
||||
|
|
@ -31,3 +32,46 @@ class ModelLoader(api.Loader):
|
|||
cmds.sets(meshes, forceElement="initialShadingGroup")
|
||||
|
||||
self[:] = nodes
|
||||
|
||||
|
||||
class ModelGPUCacheLoader(api.Loader):
|
||||
"""Import a GPU Cache"""
|
||||
|
||||
families = ["colorbleed.model"]
|
||||
representations = ["abc"]
|
||||
|
||||
label = "Import GPU Cache"
|
||||
order = -1
|
||||
icon = "download"
|
||||
|
||||
def process(self, name, namespace, context):
|
||||
|
||||
from maya import cmds
|
||||
|
||||
# todo: This will likely not be entirely safe with "containerize"
|
||||
# also this cannot work in the manager because it only works
|
||||
# on references at the moment!
|
||||
# especially in cases of duplicating the gpu cache node this will
|
||||
# mess up the "containered" workflow in the avalon core for maya
|
||||
print("WARNING: Importing gpuCaches isn't fully tested yet")
|
||||
|
||||
path = self.fname
|
||||
|
||||
cmds.loadPlugin("gpuCache", quiet=True)
|
||||
|
||||
# Create transform with shape
|
||||
transform = cmds.createNode("transform",
|
||||
name=name)
|
||||
cache = cmds.createNode("gpuCache",
|
||||
parent=transform,
|
||||
name="{0}Shape".format(name))
|
||||
|
||||
# Set the cache filepath
|
||||
cmds.setAttr(cache + '.cacheFileName', path, type="string")
|
||||
cmds.setAttr(cache + '.cacheGeomPath', "|", type="string") # root
|
||||
|
||||
# Select the transform
|
||||
cmds.select(transform, r=1)
|
||||
|
||||
# Store the created nodes
|
||||
self[:] = [transform, cache]
|
||||
|
|
|
|||
|
|
@ -12,6 +12,11 @@ class RigLoader(api.Loader):
|
|||
families = ["colorbleed.rig"]
|
||||
representations = ["ma"]
|
||||
|
||||
label = "Reference rig"
|
||||
order = -10
|
||||
icon = "code-fork"
|
||||
color = "orange"
|
||||
|
||||
def process(self, name, namespace, context):
|
||||
nodes = cmds.file(self.fname,
|
||||
namespace=namespace,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import pyblish.api
|
||||
import maya.cmds as cmds
|
||||
import cb.utils.maya.dag as dag
|
||||
|
||||
import pyblish.api
|
||||
import colorbleed.maya.lib as lib
|
||||
|
||||
|
||||
class ValidateInstancerContent(pyblish.api.InstancePlugin):
|
||||
|
|
@ -15,7 +16,7 @@ class ValidateInstancerContent(pyblish.api.InstancePlugin):
|
|||
|
||||
def process(self, instance):
|
||||
|
||||
invalid = False
|
||||
error = False
|
||||
members = instance.data['setMembers']
|
||||
export_members = instance.data['exactExportMembers']
|
||||
|
||||
|
|
@ -23,42 +24,22 @@ class ValidateInstancerContent(pyblish.api.InstancePlugin):
|
|||
|
||||
if not len(members) == len(cmds.ls(members, type="instancer")):
|
||||
self.log.error("Instancer can only contain instancers")
|
||||
invalid = True
|
||||
error = True
|
||||
|
||||
# TODO: Implement better check for particles are cached
|
||||
if not cmds.ls(export_members, type="nucleus"):
|
||||
self.log.error("Instancer must have a connected nucleus")
|
||||
invalid = True
|
||||
error = True
|
||||
|
||||
if not cmds.ls(export_members, type="cacheFile"):
|
||||
self.log.error("Instancer must be cached")
|
||||
invalid = True
|
||||
error = True
|
||||
|
||||
# Ensure all instanced geometry is hidden
|
||||
shapes = cmds.ls(export_members,
|
||||
dag=True, shapes=True,
|
||||
noIntermediate=True)
|
||||
meshes = cmds.ls(shapes, type="mesh")
|
||||
|
||||
def invalidate(node):
|
||||
"""Whether mesh is in a valid state
|
||||
|
||||
Arguments:
|
||||
node (str): The node to check
|
||||
|
||||
Returns:
|
||||
bool: Whether it is in a valid state.
|
||||
|
||||
"""
|
||||
return dag.is_visible(node,
|
||||
displayLayer=False,
|
||||
intermediateObject=False)
|
||||
|
||||
visible = [node for node in meshes if invalidate(node)]
|
||||
if visible:
|
||||
hidden = self.check_geometry_hidden(export_members)
|
||||
if not hidden:
|
||||
error = True
|
||||
self.log.error("Instancer input geometry must be hidden "
|
||||
"the scene. Invalid: {0}".format(visible))
|
||||
invalid = True
|
||||
"the scene. Invalid: {0}".format(hidden))
|
||||
|
||||
# Ensure all in one group
|
||||
parents = cmds.listRelatives(members,
|
||||
|
|
@ -68,7 +49,26 @@ class ValidateInstancerContent(pyblish.api.InstancePlugin):
|
|||
if len(roots) > 1:
|
||||
self.log.error("Instancer should all be contained in a single "
|
||||
"group. Current roots: {0}".format(roots))
|
||||
invalid = True
|
||||
error = True
|
||||
|
||||
if invalid:
|
||||
if error:
|
||||
raise RuntimeError("Instancer Content is invalid. See log.")
|
||||
|
||||
def check_geometry_hidden(self, export_members):
|
||||
|
||||
# Ensure all instanced geometry is hidden
|
||||
shapes = cmds.ls(export_members,
|
||||
dag=True,
|
||||
shapes=True,
|
||||
noIntermediate=True)
|
||||
meshes = cmds.ls(shapes, type="mesh")
|
||||
|
||||
visible = [node for node in meshes
|
||||
if lib.is_visible(node,
|
||||
displayLayer=False,
|
||||
intermediateObject=False)]
|
||||
if visible:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import os
|
||||
import re
|
||||
import pyblish.api
|
||||
|
||||
VERBOSE = False
|
||||
|
|
@ -10,6 +12,27 @@ def is_cache_resource(resource):
|
|||
return required.issubset(tags)
|
||||
|
||||
|
||||
def valdidate_files(files):
|
||||
for f in files:
|
||||
assert os.path.exists(f)
|
||||
assert f.endswith(".mcx") or f.endswith(".mcc")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def filter_ticks(files):
|
||||
tick_files = set()
|
||||
ticks = set()
|
||||
for path in files:
|
||||
match = re.match(".+Tick([0-9]+).mcx$", os.path.basename(path))
|
||||
if match:
|
||||
tick_files.add(path)
|
||||
num = match.group(1)
|
||||
ticks.add(int(num))
|
||||
|
||||
return tick_files, ticks
|
||||
|
||||
|
||||
class ValidateInstancerFrameRanges(pyblish.api.InstancePlugin):
|
||||
"""Validates all instancer particle systems are cached correctly.
|
||||
|
||||
|
|
@ -26,7 +49,6 @@ class ValidateInstancerFrameRanges(pyblish.api.InstancePlugin):
|
|||
@classmethod
|
||||
def get_invalid(cls, instance):
|
||||
|
||||
import os
|
||||
import pyseq
|
||||
|
||||
start_frame = instance.data.get("startFrame", 0)
|
||||
|
|
@ -42,7 +64,6 @@ class ValidateInstancerFrameRanges(pyblish.api.InstancePlugin):
|
|||
|
||||
node = resource['node']
|
||||
all_files = resource['files'][:]
|
||||
|
||||
all_lookup = set(all_files)
|
||||
|
||||
# The first file is usually the .xml description file.
|
||||
|
|
@ -54,28 +75,21 @@ class ValidateInstancerFrameRanges(pyblish.api.InstancePlugin):
|
|||
|
||||
# Ensure all files exist (including ticks)
|
||||
# The remainder file paths should be the .mcx or .mcc files
|
||||
for f in all_files:
|
||||
assert os.path.exists(f)
|
||||
assert f.endswith(".mcx") or f.endswith(".mcc")
|
||||
valdidate_files(all_files)
|
||||
|
||||
# Maya particle caches support substeps by saving out additional files
|
||||
# that end with a Tick60.mcx, Tick120.mcx, etc. suffix. To avoid `pyseq`
|
||||
# getting confused we filter those out and then for each file (except
|
||||
# the last frame) check that at least all ticks exist.
|
||||
tick_files = set()
|
||||
ticks = set()
|
||||
for path in all_files:
|
||||
import re
|
||||
match = re.match(".+Tick([0-9]+).mcx$", os.path.basename(path))
|
||||
# Maya particle caches support substeps by saving out additional
|
||||
# files that end with a Tick60.mcx, Tick120.mcx, etc. suffix.
|
||||
# To avoid `pyseq` getting confused we filter those out and then
|
||||
# for each file (except the last frame) check that at least all
|
||||
# ticks exist.
|
||||
|
||||
if match:
|
||||
tick_files.add(path)
|
||||
num = match.group(1)
|
||||
ticks.add(int(num))
|
||||
tick_files, ticks = filter_ticks(all_files)
|
||||
if tick_files:
|
||||
files = [f for f in all_files if f not in tick_files]
|
||||
else:
|
||||
files = all_files
|
||||
|
||||
files = [f for f in all_files if f not in tick_files] if tick_files else all_files
|
||||
sequences = pyseq.get_sequences(files)
|
||||
|
||||
if len(sequences) != 1:
|
||||
invalid.append(node)
|
||||
cls.log.warning("More than one sequence found? "
|
||||
|
|
@ -112,7 +126,8 @@ class ValidateInstancerFrameRanges(pyblish.api.InstancePlugin):
|
|||
# for the frames required by the time range.
|
||||
if ticks:
|
||||
ticks = list(sorted(ticks))
|
||||
cls.log.info("Found ticks: {0} (substeps: {1})".format(ticks, len(ticks)))
|
||||
cls.log.info("Found ticks: {0} "
|
||||
"(substeps: {1})".format(ticks, len(ticks)))
|
||||
|
||||
# Check all frames except the last since we don't
|
||||
# require subframes after our time range.
|
||||
|
|
@ -123,7 +138,8 @@ class ValidateInstancerFrameRanges(pyblish.api.InstancePlugin):
|
|||
frame = item.frame
|
||||
if not frame:
|
||||
invalid.append(node)
|
||||
cls.log.error("Path is not a frame in sequence: {0}".format(item))
|
||||
cls.log.error("Path is not a frame in sequence: "
|
||||
"{0}".format(item))
|
||||
continue
|
||||
|
||||
# Not required for our time range
|
||||
|
|
@ -137,7 +153,8 @@ class ValidateInstancerFrameRanges(pyblish.api.InstancePlugin):
|
|||
if tick_file not in all_lookup:
|
||||
invalid.append(node)
|
||||
cls.log.warning("Tick file found that is not "
|
||||
"in cache query filenames: {0}".format(tick_file))
|
||||
"in cache query filenames: "
|
||||
"{0}".format(tick_file))
|
||||
|
||||
return invalid
|
||||
|
||||
|
|
@ -148,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.")
|
||||
|
|
@ -2,66 +2,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):
|
||||
override_enabled = cmds.getAttr('{}.overrideEnabled'.format(node))
|
||||
override_visibility = cmds.getAttr('{}.overrideVisibility'.format(node))
|
||||
if override_enabled and override_visibility:
|
||||
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 ValidateJointsHidden(pyblish.api.InstancePlugin):
|
||||
|
|
@ -87,7 +28,7 @@ class ValidateJointsHidden(pyblish.api.InstancePlugin):
|
|||
@staticmethod
|
||||
def get_invalid(instance):
|
||||
joints = cmds.ls(instance, type='joint', long=True)
|
||||
return [j for j in joints if is_visible(j, displayLayer=True)]
|
||||
return [j for j in joints if lib.is_visible(j, displayLayer=True)]
|
||||
|
||||
def process(self, instance):
|
||||
"""Process all the nodes in the instance 'objectSet'"""
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ from maya import cmds
|
|||
|
||||
import pyblish.api
|
||||
import colorbleed.api
|
||||
from colorbleed.api import get_errored_instances_from_context
|
||||
|
||||
from cbra.utils.maya.node_uuid import get_id, add_ids
|
||||
|
||||
|
|
@ -71,10 +70,10 @@ class CopyUUIDsFromHistory(pyblish.api.Action):
|
|||
|
||||
# Get the errored instances
|
||||
self.log.info("Finding failed instances..")
|
||||
errored_instances = get_errored_instances_from_context(context)
|
||||
errored = colorbleed.api.get_errored_instances_from_context(context)
|
||||
|
||||
# Apply pyblish.logic to get the instances for the plug-in
|
||||
instances = pyblish.api.instances_by_plugin(errored_instances, plugin)
|
||||
instances = pyblish.api.instances_by_plugin(errored, plugin)
|
||||
|
||||
ids_map = dict()
|
||||
for instance in instances:
|
||||
|
|
|
|||
|
|
@ -22,93 +22,149 @@ class ValidateRigContents(pyblish.api.InstancePlugin):
|
|||
accepted_controllers = ["transform"]
|
||||
ignore_nodes = []
|
||||
|
||||
invalid_hierarchy = []
|
||||
invalid_controls = []
|
||||
invalid_geometry = []
|
||||
|
||||
def process(self, instance):
|
||||
|
||||
error = False
|
||||
|
||||
objectsets = ("controls_SET", "out_SET")
|
||||
missing = [obj for obj in objectsets if obj not in instance]
|
||||
|
||||
assert not missing, ("%s is missing %s" % (instance, missing))
|
||||
|
||||
# Ensure there are at least some transforms or dag nodes
|
||||
# in the rig instance
|
||||
set_members = self.check_set_members(instance)
|
||||
|
||||
self.log.info("Evaluating contents of object sets..")
|
||||
|
||||
# Ensure contents in sets and retrieve long path for all objects
|
||||
output_content = cmds.sets("out_SET", query=True) or []
|
||||
assert output_content, "Must have members in rig out_SET"
|
||||
|
||||
controls_content = cmds.set("controls_SET", query=True) or []
|
||||
assert controls_content, "Must have members in rig controls_SET"
|
||||
|
||||
root_node = cmds.ls(set_members, assemblies=True)
|
||||
hierarchy = cmds.listRelatives(root_node, allDescendents=True,
|
||||
fullPath=True)
|
||||
|
||||
self.invalid_geometry = self.validate_geometry(output_content,
|
||||
hierarchy)
|
||||
self.invalid_controls = self.validate_controls(controls_content,
|
||||
hierarchy)
|
||||
|
||||
if self.invalid_hierachy:
|
||||
self.log.error("Found nodes which reside outside of root group "
|
||||
"while they are set up for publishing."
|
||||
"\n%s" % self.invalid_hierachy)
|
||||
error = True
|
||||
|
||||
if self.not_transforms:
|
||||
self.log.error("Only transforms can be part of the controls_SET."
|
||||
"\n%s" % self.not_transforms)
|
||||
error = True
|
||||
|
||||
if self.invalid_geometry:
|
||||
self.log.error("Only meshes can be part of the out_SET\n%s"
|
||||
% self.invalid_geometry)
|
||||
error = True
|
||||
|
||||
if error:
|
||||
raise RuntimeError("Invalid rig content. See log for details.")
|
||||
|
||||
def check_set_members(self, instance):
|
||||
"""Check if the instance has any dagNodes
|
||||
Args:
|
||||
instance: the instance which needs to be published
|
||||
Returns:
|
||||
set_members (list): all dagNodes from instance
|
||||
"""
|
||||
|
||||
set_members = instance.data['setMembers']
|
||||
if not cmds.ls(set_members, type="dagNode", long=True):
|
||||
raise RuntimeError("No dag nodes in the pointcache instance. "
|
||||
"(Empty instance?)")
|
||||
return set_members
|
||||
|
||||
self.log.info("Evaluating contents of object sets..")
|
||||
def validate_hierarchy(self, hierarchy, nodes):
|
||||
"""Collect all nodes which are NOT within the hierarchy
|
||||
Args:
|
||||
hierarchy (list): nodes within the root node
|
||||
nodes (list): nodes to check
|
||||
|
||||
not_meshes = list()
|
||||
not_transforms = list()
|
||||
invalid_hierachy = list()
|
||||
Returns:
|
||||
errors (list): list of nodes
|
||||
"""
|
||||
errors = []
|
||||
for node in nodes:
|
||||
if node not in hierarchy:
|
||||
errors.append(node)
|
||||
return errors
|
||||
|
||||
error = False
|
||||
def validate_geometry(self, set_members, hierarchy):
|
||||
"""Check if the out set passes the validations
|
||||
|
||||
# Ensure contents in sets and retrieve long path for all objects
|
||||
out_members = cmds.sets("out_SET", query=True) or []
|
||||
assert out_members, "Must have members in rig out_SET"
|
||||
out_members = cmds.ls(out_members, long=True)
|
||||
Checks if all its set members are within the hierarchy of the root
|
||||
Checks if the node types of the set members valid
|
||||
|
||||
controls_members = cmds.sets("controls_SET", query=True) or []
|
||||
controls_members = cmds.ls(controls_members, long=True)
|
||||
assert controls_members, "Must have controls in rig control_SET"
|
||||
Args:
|
||||
set_members: list of nodes of the controls_set
|
||||
hierarchy: list of nodes which reside under the root node
|
||||
|
||||
root_node = cmds.ls(set_members, assemblies=True)
|
||||
root_content = cmds.listRelatives(root_node,
|
||||
allDescendents=True,
|
||||
fullPath=True)
|
||||
Returns:
|
||||
errors (list)
|
||||
"""
|
||||
|
||||
errors = []
|
||||
# Validate the contents further
|
||||
shapes = cmds.listRelatives(out_members,
|
||||
shapes = cmds.listRelatives(set_members,
|
||||
allDescendents=True,
|
||||
shapes=True,
|
||||
fullPath=True) or []
|
||||
|
||||
# The user can add the shape node to the out_set, this will result
|
||||
# in none when querying allDescendents
|
||||
out_shapes = out_members + shapes
|
||||
all_shapes = set_members + shapes
|
||||
|
||||
# geometry
|
||||
for shape in out_shapes:
|
||||
invalid_shapes = self.validate_hierarchy(hierarchy, all_shapes)
|
||||
self.invalid_hierachy.extend(invalid_shapes)
|
||||
for shape in all_shapes:
|
||||
nodetype = cmds.nodeType(shape)
|
||||
if nodetype in self.ignore_nodes:
|
||||
continue
|
||||
|
||||
if nodetype not in self.accepted_output:
|
||||
not_meshes.append(shape)
|
||||
errors.append(shape)
|
||||
|
||||
# check if controllers are in the root group
|
||||
if shape not in root_content:
|
||||
invalid_hierachy.append(shape)
|
||||
return errors
|
||||
|
||||
# curves
|
||||
for node in controls_members:
|
||||
def validate_controls(self, set_members, hierarchy):
|
||||
"""Check if the controller set passes the validations
|
||||
|
||||
Checks if all its set members are within the hierarchy of the root
|
||||
Checks if the node types of the set members valid
|
||||
|
||||
Args:
|
||||
set_members: list of nodes of the controls_set
|
||||
hierarchy: list of nodes which reside under the root node
|
||||
|
||||
Returns:
|
||||
errors (list)
|
||||
"""
|
||||
|
||||
errors = []
|
||||
invalid_controllers = self.validate_hierarchy(hierarchy, set_members)
|
||||
self.invalid_hierachy.extend(invalid_controllers)
|
||||
for node in set_members:
|
||||
nodetype = cmds.nodeType(node)
|
||||
if nodetype in self.ignore_nodes:
|
||||
continue
|
||||
|
||||
if nodetype not in self.accepted_controllers:
|
||||
not_transforms.append(node)
|
||||
errors.append(node)
|
||||
|
||||
# check if controllers are in the root group
|
||||
if node not in root_content:
|
||||
invalid_hierachy.append(node)
|
||||
|
||||
if invalid_hierachy:
|
||||
self.log.error("Found nodes which reside outside of root group "
|
||||
"while they are set up for publishing."
|
||||
"\n%s" % invalid_hierachy)
|
||||
error = True
|
||||
|
||||
if not_transforms:
|
||||
self.log.error("Only transforms can be part of the controls_SET."
|
||||
"\n%s" % not_transforms)
|
||||
error = True
|
||||
|
||||
if not_meshes:
|
||||
self.log.error("Only meshes can be part of the out_SET\n%s"
|
||||
% not_meshes)
|
||||
error = True
|
||||
|
||||
if error:
|
||||
raise RuntimeError("Invalid rig content. See log for details.")
|
||||
return errors
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue