14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135 | class ValidateModelContent(plugin.MayaInstancePlugin,
OptionalPyblishPluginMixin):
"""Adheres to the content of 'model' product type
See `get_description` for more details.
"""
order = ValidateContentsOrder
families = ["model"]
label = "Model Content"
actions = [ayon_maya.api.action.SelectInvalidAction]
validate_top_group = True
optional = False
allowed = ('mesh', 'transform', 'nurbsCurve', 'nurbsSurface', 'locator')
@classmethod
def get_invalid(cls, instance):
content_instance = instance.data.get("setMembers", None)
if not content_instance:
cls.log.error("Model instance has no nodes. "
"It is not allowed to be empty")
return [instance.data["instance_node"]]
# All children will be included in the extracted export so we also
# validate *all* descendents of the set members and we skip any
# intermediate shapes
descendants = cmds.listRelatives(content_instance,
allDescendents=True,
fullPath=True) or []
descendants = cmds.ls(descendants, noIntermediate=True, long=True)
content_instance = list(set(content_instance + descendants))
# Ensure only valid node types
nodes = cmds.ls(content_instance, long=True)
valid = cmds.ls(content_instance, long=True, type=cls.allowed)
invalid = set(nodes) - set(valid)
if invalid:
# List as bullet points
invalid_bullets = "\n".join(f"- {node}" for node in invalid)
cls.log.error(
"These nodes are not allowed:\n{}\n\n"
"The valid node types are: {}".format(
invalid_bullets, ", ".join(cls.allowed))
)
return list(invalid)
if not valid:
cls.log.error(
"No valid nodes in the model instance.\n"
"The valid node types are: {}".format(", ".join(cls.allowed))
)
return [instance.data["instance_node"]]
# Ensure it has shapes
shapes = cmds.ls(valid, long=True, shapes=True)
if not shapes:
cls.log.error("No shapes in the model instance")
return [instance.data["instance_node"]]
# Ensure single top group
top_parents = {"|" + x.split("|", 2)[1] for x in content_instance}
if cls.validate_top_group and len(top_parents) != 1:
cls.log.error(
"A model instance must have exactly one top group. "
"Found top groups: {}".format(", ".join(top_parents))
)
return list(top_parents)
def _is_visible(node):
"""Return whether node is visible"""
return lib.is_visible(node,
displayLayer=False,
intermediateObject=True,
parentHidden=True,
visibility=True)
# The roots must be visible (the assemblies)
for parent in top_parents:
if not _is_visible(parent):
cls.log.error("Invisible parent (root node) is not "
"allowed: {0}".format(parent))
invalid.add(parent)
# Ensure at least one shape is visible
if not any(_is_visible(shape) for shape in shapes):
cls.log.error("No visible shapes in the model instance")
invalid.update(shapes)
return list(invalid)
def process(self, instance):
if not self.is_active(instance.data):
return
invalid = self.get_invalid(instance)
if invalid:
raise PublishValidationError(
title="Model content is invalid",
message="Model content is invalid. See log for more details.",
description=self.get_description()
)
@classmethod
def get_description(cls):
return inspect.cleandoc(f"""
### Model content is invalid
Your model instance does not adhere to the rules of a
model product type:
- Must have at least one visible shape in it, like a mesh.
- Must have one root node. When exporting multiple meshes they
must be inside a group.
- May only contain the following node types:
{", ".join(cls.allowed)}
""")
|