Created
May 17, 2023 07:42
-
-
Save JustinPedersen/4f4d73eee27a3cea7612275a5376ad96 to your computer and use it in GitHub Desktop.
Automated Rig Checks V1.0.0
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
""" | |
import pymel.core as pm | |
from pprint import pprint | |
def line_list(in_list): | |
""" Convert a list into formatted lines for error output. """ | |
str_list = [x.name() if isinstance(x, pm.PyNode) else x for x in in_list] | |
return '\n\t {}'.format('\n\t '.join(str_list)) | |
def get_namespaces(): | |
""" Returns a list of namespaces present in the scene. """ | |
result = list(set([node.namespace() for node in pm.ls() if node.namespace()])) | |
if result: | |
return False, f'The following namespaces are present in the scene: {line_list(result)}' | |
return True, 'All namespaces removed from scene' | |
def get_ctl_transforms(round_precision=4): | |
""" | |
Finds all transforms in the scene that have '_ctl' in their name and have non-default | |
values on their translate, rotate, or scale attributes. | |
:param round_precision: decimal places to round values to. | |
""" | |
ctl_transforms = [] | |
for node in pm.ls('*_ctl', type='transform'): | |
for channel, expected_value in zip(['translate', 'rotate', 'scale'], [0.0, 0.0, 1.0]): | |
for axis in ['X', 'Y', 'Z']: | |
attr = f'{channel}{axis}' | |
if node.attr(attr).isKeyable(): | |
if round(node.attr(attr).get(), round_precision) != expected_value: | |
ctl_transforms.append(node) | |
break | |
if ctl_transforms: | |
return False, f'The following controls have values on them: {line_list(ctl_transforms)}' | |
return True, 'All controls are clean.' | |
def get_visible_meshes_without_skin(): | |
""" Returns a list of every visible mesh in the scene that does not have a skin cluster attached to it. """ | |
visible_meshes_without_skin = [] | |
for mesh in pm.ls(type='mesh', visible=True): | |
if not mesh.isIntermediate(): | |
if not mesh.listHistory(type='skinCluster'): | |
visible_meshes_without_skin.append(mesh.getParent()) | |
if visible_meshes_without_skin: | |
return False, f"The following meshes are visible and aren't skinned: {line_list(visible_meshes_without_skin)}" | |
return True, 'All meshes that are visible are skinned.' | |
def get_nodes_with_pasted_name(): | |
""" Returns a list of all nodes in the scene with 'pasted__' in their name. """ | |
nodes_with_pasted_names = pm.ls('*pasted__*') | |
if nodes_with_pasted_names: | |
return False, f'The following nodes have been pasted into the scene: {line_list(nodes_with_pasted_names)}' | |
return True, 'There are no pasted nodes in the scene' | |
def check_unused_influences(): | |
""" | |
Checks every skin cluster in the scene and returns a list of joints that | |
have zero influence on the skin cluster. | |
""" | |
unused_influences = list() | |
for skin_cluster in pm.ls(type='skinCluster'): | |
skin_geo = skin_cluster.getGeometry()[0] | |
weight_lookup = dict(zip(skin_cluster.getInfluence(), | |
zip(*skin_cluster.getWeights(skin_geo)))) | |
for influence, weight_list in weight_lookup.items(): | |
if not all(weight_list): | |
unused_influences.append(skin_geo.getParent()) | |
break | |
result = list(set(unused_influences)) | |
if result: | |
return False, f'The following meshes have unused skinning influences: {line_list(result)}' | |
return True, 'All skin clusters have been optimized.' | |
def list_non_deformer_history(): | |
""" Lists the non-deformer history on each mesh in the scene that has a skin cluster. """ | |
result = [] | |
ignore_list = [pm.nt.Joint, pm.nt.Mesh, pm.nt.SkinCluster, pm.nt.BlendShape, pm.nt.NonLinear, pm.nt.DeformBend, | |
pm.nt.DeformFlare, pm.nt.DeformSine, pm.nt.DeformSquash, pm.nt.DeformTwist, pm.nt.DeformWave, | |
pm.nt.Transform] | |
for skin_cluster in pm.ls(type='skinCluster'): | |
mesh = skin_cluster.getGeometry()[0] | |
history = pm.listHistory(mesh, interestLevel=2) or [] | |
result.extend([mesh.getParent() for h in history if type(h) not in ignore_list]) | |
result = list(set(result)) | |
if result: | |
return False, f'The following meshes have non deformer history on them {line_list(result)}' | |
else: | |
return True, 'All skinned meshes have only deformer history.' | |
def check_skinned_mesh_display_layers(): | |
""" Checks if every skinned mesh in the scene belongs to a display layer. """ | |
skinned_meshes = [x.getGeometry()[0].getParent() for x in pm.ls(type='skinCluster')] | |
skinned_meshes_without_display_layers = [] | |
for mesh in skinned_meshes: | |
if not pm.listConnections(mesh, type='displayLayer'): | |
skinned_meshes_without_display_layers.append(mesh) | |
if skinned_meshes_without_display_layers: | |
return False, f"The following skinned meshes do not belong to any display layer: " \ | |
f"{line_list(skinned_meshes_without_display_layers)}" | |
else: | |
return True, "All skinned meshes belong to a display layer." | |
def check_extra_cameras(): | |
""" Check the scene for extra cameras """ | |
extra_cameras = [] | |
for cam in pm.ls(type='camera'): | |
if cam.name() not in ['frontShape', 'perspShape', 'sideShape', 'topShape']: | |
extra_cameras.append(cam.getParent()) | |
if extra_cameras: | |
return False, f'There are additional cameras in the scene that need to be removed: {line_list(extra_cameras)}' | |
return True, 'There are no additional cameras in the scene.' | |
def check_lights(): | |
""" Check the scene for lights that shouldn't be there """ | |
lights = pm.ls(type='light') | |
if lights: | |
return False, f'There are lights in the scene that need to be removed: {line_list(lights)}' | |
return True, 'There are no lights in the scene.' | |
def check_image_planes(): | |
""" Check the scene for image planes that shouldn't be there """ | |
image_panes = pm.ls(type='imagePlane') | |
if image_panes: | |
return False, f'There are Image Planes in the scene that need to be removed: {line_list(image_panes)}' | |
return True, 'There are no Image Planes in the scene.' | |
def check_keyframes(): | |
""" Check that there are no keyframes in the scene """ | |
node_types = ['animCurveTA', 'animCurveTL', 'animCurveTU'] | |
keyed_controls = [] | |
for node_type in node_types: | |
for keyframe_nodes in pm.ls(type=node_type): | |
keyed_controls.extend(pm.listConnections(keyframe_nodes.output, destination=True)) | |
result = list(set(keyed_controls)) | |
if result: | |
return False, f'There are keyframes on the following nodes: {line_list(result)}' | |
else: | |
return True, 'There are no keyframes in the scene.' | |
def check_guide_in_scene(): | |
""" Check that there is no mGear guide in the scene """ | |
guides = pm.ls('guide') | |
if guides: | |
return False, f'There are guides in the scene that need to be deleted: {line_list(guides)}' | |
else: | |
return True, 'There are no guides in the scene.' | |
def check_ng_skin_nodes(): | |
""" Check that all the NG skin tools nodes have been removed from the scene """ | |
ng_nodes = pm.ls(type='ngst2SkinLayerData') | |
if ng_nodes: | |
return False, f'There are NG Skin Nodes in the scene that need to be deleted: {line_list(ng_nodes)}' | |
else: | |
return True, 'There are no NG SKin Nodes in the scene.' | |
def run_checks(checks): | |
""" | |
Run all the given checks and print the feedback from each method. | |
""" | |
check_results = [] | |
passed_checks = 0 | |
for check in checks: | |
status, message = check() | |
check_results.append(f'PASS: {message}\n' if status else f'FAIL: {message}\n') | |
if status: | |
passed_checks += 1 | |
# Print a header | |
print('=' * 50) | |
print(f'CHECKS PASSED: {passed_checks}/{len(checks)}') | |
print('=' * 50 + '\n') | |
# Print out the results | |
[print(cr) for cr in check_results] | |
if __name__ == '__main__': | |
check_methods = [ | |
get_namespaces, | |
get_ctl_transforms, | |
get_visible_meshes_without_skin, | |
get_nodes_with_pasted_name, | |
check_unused_influences, | |
list_non_deformer_history, | |
check_skinned_mesh_display_layers, | |
check_extra_cameras, | |
check_lights, | |
check_image_planes, | |
check_keyframes, | |
check_guide_in_scene, | |
check_ng_skin_nodes | |
] | |
run_checks(check_methods) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment