Source code for artv2_components.base_component

# -*- coding: utf-8 -*-
"""
The base_component provides the base class, ART_Component, from which all components are derived.

"""

import inspect
import os
from abc import abstractmethod, ABCMeta

import pymel.core as pm

import artv2_utilities.error_utilities as errors
import artv2_utilities.general_utilities as utils
import artv2_utilities.component_utilities as component_utils
from aim_helper import AimHelper
from joint_mover import JointMover


# noinspection PyPep8Naming
[docs]class ART_Component(object): """ Base class for defining a component (arm, leg, torso, single joint, etc) in ARTv2. All components should inherit from this class. When creating a new component, here is a list of common attributes and functions you will want or need to override. +------------+------------------------+----------------------------------------------------------------------------+ | Type | Name | Description | +============+========================+============================================================================+ | attribute | nice_name | The string nice name of the component (as it will appear in UIs) | +------------+------------------------+----------------------------------------------------------------------------+ | attribute | base_name | The string base name of the component. This is a simple string used to | | | | identify the component type and append onto nodes created by the component.| | | | For example, on a leg component, the base_name is simply: "leg" | +------------+------------------------+----------------------------------------------------------------------------+ | attribute | has_sides | Bool for whether or not the component supports different sides,like a leg | | | | or arm do. | +------------+------------------------+----------------------------------------------------------------------------+ | attribute | joint_mover_file | The relative path from the ARTv2 directory to the joint mover file of the | | | | component. For example, self.joint_mover_file = | | | | "resources/rigging_guides/root.ma" | +------------+------------------------+----------------------------------------------------------------------------+ | function | _add_metadata | This method adds attributes you want to keep track of that are unique to | | | | your component. Make sure that you still call on the base class's | | | | _add_metadata method to setup the required attributes. | +------------+------------------------+----------------------------------------------------------------------------+ """ __metaclass__ = ABCMeta nice_name = "" base_name = None has_sides = False up = pm.upAxis(q=True, ax=True) joint_mover_file = "replace_me_with_valid_path.ma" @abstractmethod def __init__(self, prefix="", suffix="", network_node=None): if network_node is not None: network_node = pm.PyNode(network_node) prefix = component_utils.validate_prefix(network_node, prefix) suffix = component_utils.validate_suffix(network_node, suffix) if network_node is None: self._unique_name = prefix + self.base_name + suffix self._create_component(prefix, suffix) self.joint_mover = JointMover(self.joint_mover_file, self.network_node) self.joint_mover.add_joint_mover() else: if pm.objExists(network_node): network_node = pm.PyNode(network_node) prefix = network_node.prefix.get() suffix = network_node.suffix.get() self._unique_name = prefix + self.base_name + suffix self.joint_mover = JointMover(self.joint_mover_file, network_node) self.prefix = prefix self.suffix = suffix # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def _create_component(self, prefix, suffix): """ Method for adding the component to the Maya scene. Calls on various functions to accomplish. """ character_node = component_utils.get_rig_asset_node() network_node = self._create_network_node() self._add_metadata(network_node, prefix, suffix) character_node.components.connect(network_node.asset)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] @abstractmethod def _add_metadata(self, network_node, prefix, suffix): """ Method for adding metadata attributes to the network node that are important for the component. The ones created by the base class are necessary for all components. :param network_node: component's network node name to add attributes to. :param str prefix: the component's (optional) prefix. :param str suffix: the component's (optional) suffix. :type network_node: PyNode """ network_node.unlock() network_node.addAttr("asset", at="message") network_node.addAttr("modulePath", dt="string", keyable=False) module_path = os.path.splitext(inspect.getfile(self.__class__).split("artv2_source")[1])[0] module_path = module_path.replace("\\", ".") module_path = module_path.partition(".")[2] network_node.modulePath.set(module_path, type="string", lock=True) network_node.addAttr("className", dt="string", keyable=False) network_node.className.set(self.__class__.__name__, type="string", lock=True) network_node.addAttr("componentName", dt="string", keyable=False) network_node.componentName.set(self._unique_name, type="string", lock=True) network_node.addAttr("baseName", dt="string", keyable=False) network_node.baseName.set(self.base_name, type="string", lock=True) network_node.addAttr("prefix", dt="string", keyable=False) network_node.prefix.set(prefix, type="string", lock=True) network_node.addAttr("suffix", dt="string", keyable=False) network_node.suffix.set(suffix, type="string", lock=True) network_node.addAttr("parentComponentBone", dt="string", keyable=False) network_node.addAttr("mirrorComponent", dt="string", keyable=False) network_node.addAttr("pinned", at="bool", keyable=False) network_node.pinned.set(False, lock=True) network_node.addAttr("isAiming", at="bool", keyable=False) network_node.isAiming.set(False, lock=True) network_node.addAttr("aimModeConstraints", at="message") network_node.addAttr("global_movers", at="message") network_node.addAttr("offset_movers", at="message") network_node.addAttr("mover_grp", at="message") network_node.lock()
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs] def _create_network_node(self): """ Creates the metanode that will store metadata for the component. :return: Returns the metanode. :rtype: PyNode """ existing_nodes = component_utils.return_all_components() for node in existing_nodes: if node.componentName.get() == self._unique_name: errors.raise_error("This component instance has already been created! Aborting.") return network_node = pm.shadingNode("network", name=self._unique_name + "_metadata", asUtility=True) network_node.lock() return network_node
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def change_num_joints(self, new_value, attr_min, attr_max, attr_name, base_string): """ Handles logic for when changing number of joints on a component. For example, changing the number of calf twists or index fingers, etc. :param new_value: The new number for the property (num_calf_twists = 2) :param attr_min: The minimum amount for that property. :param attr_max: The maximum amount for that property. :param attr_name: The name of the attribute on the network node that represents that property. :param base_string: The string to search for when looking for joints or other nodes. """ if (attr_min - 1) <= new_value <= attr_max: utils.set_attribute(self.network_node, attr_name, new_value) for i in range(attr_min, attr_max): joint = self.prefix + base_string + str(i + 1) + self.suffix group = pm.PyNode(self._unique_name + "_" + base_string + str(i + 1) + "_mover_grp") self.joint_mover.toggle_build_joint(joint, group, False) if i + 1 <= new_value: self.joint_mover.toggle_build_joint(joint, group) else: errors.raise_error("Invalid number for property: {0}. " "Integer must be between {1} and {2}.".format(attr_name, attr_min, attr_max)) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # @property def parent(self): """ This property holds the joint name of the parent of this component. """ return self.network_node.parentComponentBone.get() @parent.setter def parent(self, new_parent): all_joints = component_utils.get_all_created_joints() if new_parent in all_joints: utils.set_attribute(self.network_node, "parentComponentBone", new_parent, "string") self.joint_mover.set_mover_parent(new_parent) else: errors.raise_error("Invalid parent: {0}! No component creates such a joint.".format(new_parent)) return # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # @property def prefix(self): """ This property holds the component's prefix (optional) :return: Returns the component's prefix. :rtype: str """ return self.network_node.prefix.get() @prefix.setter def prefix(self, new_prefix): valid_prefix = component_utils.validate_prefix(self.network_node, new_prefix) if valid_prefix is not False: old_prefix = self.prefix suffix = self.suffix utils.set_attribute(self.network_node, "prefix", valid_prefix, "string") utils.set_attribute(self.network_node, "componentName", valid_prefix + self.base_name + suffix, "string") self._unique_name = valid_prefix + self.base_name + suffix pynode = pm.PyNode(self.network_node) utils.rename_object(pynode, self._unique_name + "_metadata") containers = self.joint_mover.get_containers() utils.rename_object(containers[0], self._unique_name + "_component") utils.rename_object(containers[1], self._unique_name + "_mover_guides") utils.rename_object(containers[2], self._unique_name + "_mover_utilities") self.joint_mover.metanode = pm.PyNode(self._unique_name + "_metadata") self.joint_mover.rename_movers(old_prefix, self.suffix) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # @property def suffix(self): """ This property holds the component's suffix (optional) :return: Returns the component's suffix. :rtype: str """ return self.network_node.suffix.get() @suffix.setter def suffix(self, new_suffix): valid_suffix = component_utils.validate_suffix(self.network_node, new_suffix) if valid_suffix is not False: old_suffix = self.suffix prefix = self.prefix utils.set_attribute(self.network_node, "suffix", valid_suffix, "string") utils.set_attribute(self.network_node, "componentName", prefix + self.base_name + valid_suffix, "string") self._unique_name = prefix + self.base_name + valid_suffix pynode = pm.PyNode(self.network_node) utils.rename_object(pynode, self._unique_name + "_metadata") containers = self.joint_mover.get_containers() utils.rename_object(containers[0], self._unique_name + "_component") utils.rename_object(containers[1], self._unique_name + "_mover_guides") utils.rename_object(containers[2], self._unique_name + "_mover_utilities") self.joint_mover.metanode = pm.PyNode(self._unique_name + "_metadata") self.joint_mover.rename_movers(self.prefix, old_suffix) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # @property def network_node(self): """ This property holds the network node that contains this component's metadata. """ return_node = None components = component_utils.return_all_components() for component in components: if component.componentName.get() == self._unique_name: return_node = component break if return_node is None: errors.raise_error("ART_Component.network_node: No network node found.") return return pm.PyNode(return_node)