Source code for artv2_components.joint_mover

# -*- coding: utf-8 -*-
"""
This module contains a class that represents a joint mover object. A joint mover object is meant to be instantiated in
the __init__ of the base_component class. Nothing should inherit from the JointMover class.

The main methods a JointMover handles are: adding the joint mover file, renaming movers, setting up the hierarchy,
locking or unlocking movers, and retrieving mover-related data.

"""

import os

import pymel.core as pm

import artv2_utilities.general_utilities as utils
import artv2_utilities.component_utilities as component_utils
import artv2_utilities.error_utilities as errors
import artv2_utilities.joint_mover_utilities as dev_utils
from aim_helper import AimHelper


[docs]class JointMover: """ Class for creating and working with a joint mover rig, which is a simplified rig used to aid in the creation of a skeleton, keeping it clean and consistent. """ def __init__(self, path, metanode): """ :param path: Path to joint mover file. :param metanode: This is the network node from a component, like a leg or torso. It contains all of the metadata """ self._path = path self._metanode = pm.PyNode(metanode) self.aim_helper = AimHelper(self._metanode) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def add_joint_mover(self): """ Adds the joint mover maya ascii file to the maya scene. """ tools_directory = utils.return_settings()[0] joint_mover_path = utils.path_join(tools_directory, self._path) if not os.path.exists(joint_mover_path): errors.raise_error("The specified joint mover file does not exist: {0}".format(joint_mover_path)) return else: nodes = utils.import_file(joint_mover_path, return_nodes=True) joints = utils.filter_nodes(nodes, filter_by="joint") root_joint = utils.get_root_joint_of_component(joints) for joint in utils.convert_to_pynodes(joints): joint.overrideEnabled.set(1) joint.overrideDisplayType.set(2) self.create_joint_mover_controls(root_joint)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def create_joint_mover_controls(self, root_joint): """ Creates the joint mover controls on the joints that come in from the ascii file. :param root_joint: Root joint of the hierarchy from the joint mover ascii file. """ created_movers = dev_utils.create_movers_from_joints(self.metanode.componentName.get(), root_joint) dev_utils.mark_joint_movers(created_movers, self.metanode.componentName.get()) dev_utils.drive_joints_with_movers(created_movers, self.metanode.componentName.get()) self._connect_movers_to_metanode(created_movers) self.rename_movers("", "") movers = self.get_movers() for each in [movers.get("global"), movers.get("offset")]: for mover in each: component_utils.setup_global_scale(mover) self._lock_joint_movers(lock_children=True) containers = self.get_containers() pm.lockNode(containers[2], lock=True, lockUnpublished=True) pm.lockNode(containers[1], lock=True, lockUnpublished=False) pm.lockNode(containers[0], lock=True, lockUnpublished=False) pynode = pm.PyNode(self.metanode.mover_grp.connections()[0]) mover = component_utils.get_top_level_mover(pynode) pm.select(mover.fullPath()) pm.setToolTo("moveSuperContext")
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def get_containers(self): """ Get and return the containers that our joint mover controls and utility nodes are contained in. :return: PyNodes of containers that our joint mover controls and utility nodes reside in. """ guide_container = pm.container(q=True, findContainer=[self.return_top_mover_grp()]) top_container = pm.container(q=True, findContainer=[guide_container]) utils_container = top_container.utils_container.connections()[0] return [top_container, guide_container, utils_container]
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def get_created_joints(self): """ Finds and returns the joints created by this JointMover. :return: string array of joint names """ movers = self.get_movers() offset_movers = movers.get("offset") joints = [] for offset in offset_movers: if offset.build_joint.get() is True: joints.append(offset.created_joint.get()) return joints
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def get_driven_joints(self): """ Return the joint mover joints currently driven by the movers (these are not the same as the created joints). :return: List of PyNodes representing the joint mover joints. """ movers = self.get_movers() offset_movers = movers.get("offset") joints = [] for offset in offset_movers: joints.append(offset.connected_joint.connections()[0]) return joints
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def get_mover_values(self): """ Get the translation, rotation, and scale values of the joint mover controls. :return: Returns a dictionary of the joint mover transformation values. The dictionary is returned as: key = mover name, value = dictionary of attributes as keys, and their values as the values. For example: {calf_mover: {translateX: 5.00, translateY: 0.00, translateZ: 10.00}} """ mover_values = {} mover_data = self.get_movers() for mover_type in mover_data: movers = mover_data.get(mover_type) for mover in movers: attrs = mover.listAttr(keyable=True) attr_data = {} for attr in attrs: attr_data[attr.attrName()] = attr.get() mover_values[mover.name()] = attr_data return mover_values
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def get_movers(self): """ Finds and returns the joint mover control objects created by the JointMover. :return: Dictionary of joint movers with the keys: global and offset. """ global_movers, offset_movers, geo_movers, lra_movers = [], [], [], [] for attr in [["global_movers", global_movers], ["offset_movers", offset_movers]]: if self.metanode.hasAttr(attr[0]): connections = self.metanode.attr(attr[0]).connections() if connections is not None: attr[1].extend(connections) movers = {"global": global_movers, "offset": offset_movers} return movers
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def _lock_joint_movers(self, lock=True, lock_children=False): """ Method for locking down the joint mover nodes so they cannot be deleted (or unlocking them). :param lock: Whether to lock or unlock the nodes. :param lock_children: Whether to lock all children of the nodes, whether they are movers or not. :type lock: bool :type lock_children: bool """ joint_movers = self.get_movers() for mover_type in joint_movers: movers = joint_movers.get(mover_type) container = pm.container(q=True, findContainer=[mover for mover in movers]) if container is not None: container.unlock() for mover in movers: pm.lockNode(mover, lock=lock) if lock_children: pm.select(mover, hi=True) children = pm.ls(sl=True) for child in children: pm.lockNode(child, lock=lock) if container is not None: container.lock()
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def remove_movers(self): """ Remove the generated joint mover controls from the driven joints (as well as any associated containers, nodes). """ self._lock_joint_movers(lock=False, lock_children=True) mover_data = self.get_movers() containers = self.get_containers() for container in containers: container.unlock() utility_nodes = containers[2].getNodeList() for node in utility_nodes: node.unlock() pm.delete(node) for mover_type in mover_data: movers = mover_data.get(mover_type) for mover in movers: if pm.objExists(mover): pm.delete(mover) top_grp = self.return_top_mover_grp() top_grp.unlock() pm.delete(top_grp) joints = containers[1].getNodeList() containers[1].removeNode(joints) for joint in joints: original_name = joint.attr("otherType").get() utils.rename_object(joint, original_name) for container in containers: if pm.objExists(container): pm.delete(container)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def rename_movers(self, old_prefix, old_suffix): """ Renames joint movers with the new component prefix and/or suffix. :param str old_prefix: The old prefix to search/replace :param str old_suffix: The old suffix to search/replace """ prefix = self.metanode.prefix.get() suffix = self.metanode.suffix.get() mover_grp = self.metanode.mover_grp.connections()[0] pm.select(mover_grp, hi=True) selection = pm.ls(sl=True, long=True) rename_nodes = self._find_nodes_to_rename(selection, old_prefix, old_suffix) rename_data = [] for each in rename_nodes: if each[0].hasAttr("created_joint"): original_name = each[0].created_joint.get() current_name = utils.strip_name(original_name, old_prefix, None, old_suffix) utils.set_attribute(each[0], "created_joint", prefix + current_name + suffix, "string") rename_data.append([original_name, prefix + current_name + suffix]) guide_joint = each[0].connected_joint.connections()[0] utils.rename_object(guide_joint, self.metanode.componentName.get() + "_" + current_name) utils.rename_object(each[0], each[1]) self._rename_dependent_attrs(rename_data) pm.select(clear=True)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def return_top_mover_grp(self): """ Finds the top-most global mover group and returns it. :return: string name of the top-most global mover group. """ top_mover_grp = self.metanode.mover_grp.connections()[0] if not pm.objExists(top_mover_grp): errors.raise_error("{0} does not exist! This should not be the case!".format(top_mover_grp)) return return top_mover_grp
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def set_mover_parent(self, parent): """ Parents the top-most group in the joint mover file under the given parent joint's global mover. :param str parent: Name of parent joint. """ parent_mover = component_utils.find_associated_mover_from_joint(parent) top_mover = self.return_top_mover_grp() parent_constraint = top_mover.translateX.connections(type="parentConstraint", exactType=True) scale_constraint = top_mover.scaleX.connections(type="scaleConstraint", exactType=True) if parent_constraint: if pm.objExists(parent_constraint[0]): parent_constraint[0].unlock() pm.delete(parent_constraint[0]) if scale_constraint: if pm.objExists(scale_constraint[0]): scale_constraint[0].unlock() pm.delete(scale_constraint[0]) if pm.objExists(parent_mover): new_parent_constraint = pm.parentConstraint(parent_mover, top_mover, mo=True) new_scale_constraint = pm.scaleConstraint(parent_mover, top_mover) new_parent_constraint.lock() new_scale_constraint.lock() else: errors.raise_error("Could not parent {0} to {1}. {1} does not exist.".format(top_mover, parent_mover)) return
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def toggle_build_joint(self, joint, group, build=True): """ Sets the metadata on the joint mover controls to either build or not build the given joint. :param str joint: The joint whose build state is being toggled. :param str group: The name of the joint mover control group that includes that joint. :param bool build: Boolean value of whether to toggle the attribute on or off. """ movers = self.get_movers() offset_movers = movers["offset"] alert_user = False issues = component_utils.check_for_children(joint) if len(issues) > 0: message = "Some components have had their parent set to the root joint due to dependencies.\n" details = "" for each in issues: inst = component_utils.get_component_instance(each) inst.parent = "root" details += inst.prefix + inst.base_name + inst.suffix + "\n" alert_user = [message, details] self._lock_joint_movers(False, True) for offset in offset_movers: if offset.created_joint.get() == joint: offset.build_joint.set(build) driven_jnt = offset.connected_joint.connections()[0] utils.set_attribute(driven_jnt, "visibility", build) if not pm.objExists(group): errors.raise_error("ART_Component._toggle_build_joint: {0} does not exist".format(group)) return group.unlock() group.visibility.set(lock=False) group.visibility.set(build, lock=True) group.lock() self._lock_joint_movers(lock_children=True) if alert_user is not False: errors.inform_user(*alert_user)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def _find_nodes_to_rename(self, nodes, old_prefix, old_suffix): """ Given a list of nodes, find only the transforms and rename them. :param nodes: A list of PyNodes :param old_prefix: The old prefix. :param old_suffix: The old suffix. :return: Return a list of nodes(PyNodes) to rename, with their new name (str): [[node1, newName], [node2, newName]] """ unique_name = self.metanode.componentName.get() base_name = self.metanode.baseName.get() rename_nodes = [] for node in nodes: types = pm.nodeType(node.name(), inherited=True) if "transform" in types: if base_name in node.name(): tail = utils.strip_name(node.name(), old_prefix, base_name, old_suffix) new_name = unique_name + tail rename_nodes.append([node, new_name]) return rename_nodes
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def _connect_movers_to_metanode(self, nodes): """ Connect the joint mover controls to the owning component's metanode. :param nodes: a list of joint mover controls (PyNodes) """ for key in nodes: mover_nodes = nodes.get(key) for node in mover_nodes: if node.hasAttr("isMoverGrp"): self.metanode.mover_grp.connect(node.owning_component) if node.hasAttr("isMover"): mover_type = node.moverType.get() mover_types = {"global": "global_movers", "offset": "offset_movers"} for each in mover_types: if mover_type == each: self.metanode.attr(mover_types.get(each)).connect(node.owning_component)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] @staticmethod def set_mover_values(value_data): """ Sets values on the joint mover controls. :param value_data: The values to set on the controls. It should be formatted like the dictionary generated by the get_mover_values method. """ for mover in value_data: mover_node = pm.PyNode(mover) attribute_data = value_data.get(mover) for attribute in attribute_data: attribute_value = attribute_data.get(attribute) if pm.objExists(mover_node): if mover_node.hasAttr(attribute): mover_node.attr(attribute).set(attribute_value)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] @staticmethod def _rename_dependent_attrs(renamed_nodes): """ Renames the parentComponentBone attribute on any components that had a bone that was renamed. :param renamed_nodes: List of PyNodes and their new names. Used to check if the parentComponentBone attribute value is in that list, and if so, will then rename the attribute value for parentComponentBone. """ components = component_utils.return_all_components() for component in components: parent_joint_name = component.parentComponentBone.get() for each in renamed_nodes: if each[0] == parent_joint_name: utils.set_attribute(component, "parentComponentBone", each[1], "string")
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # @property def metanode(self): """ This property holds the metanode that is passed in from our component using the joint mover. :return: Returns the metanode from our component as a pynode. """ return self._metanode @metanode.setter def metanode(self, new_value): self._metanode = pm.PyNode(new_value)