# -*- coding: utf-8 -*- """ :author: Jeremy Ernst :description: This module contains the class for defining a rig asset (character, weapon, etc). The methods in this class are for operating on an asset-level, such as getting all components of an asset, getting all controls of an asset, etc. """ import json import pymel.core as pm import artv2.utilities.component_utilities as component_utils import artv2.utilities.error_utilities as errors import artv2.utilities.general_utilities as utils import artv2.tools.system.logger.output_logger as logger import artv2.components.base_components.pose as pose class RigAsset(object): """ The rig asset class defines the top-level representation of a rig and holds data like which components make up that rig, what the asset name is, etc. """ def __init__(self, network_node=None): self.logger = logger.OutputLogger(self.__class__.__name__) if network_node is None: self._unique_name = "asset" self._create_asset() else: network_node = pm.PyNode(network_node) self._unique_name = network_node.attr("name").get() # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def _create_asset(self): """ Creates a network node and adds metadata to the network node to define the rig asset. """ self.logger.info("{0}._create_asset: Creating asset network node.".format(self.__class__.__name__)) network_node = self._create_network_node() self.logger.info("{0}._create_asset: Adding metadata to network node.".format(self.__class__.__name__)) self._add_metadata(network_node) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def _create_network_node(self): """ Creates the metanode to store metadata. :return: Returns the metanode :rtype: PyNode """ network_nodes = pm.ls(type="network") for node in network_nodes: if pm.objExists(node + ".components"): self.logger.warning("{0}._create_network_node: As asset node already exists. Cannot create new asset." "".format(self.__class__.__name__)) errors.raise_error("An asset node already exists. Cannot create new asset.") network_node = pm.shadingNode("network", name=self._unique_name + "_metadata", asUtility=True) network_node.lock() return network_node # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def _add_metadata(self, network_node): """ Adds attributes for tracking metadata to the asset's network node. :param network_node: The metanode to add attributes onto. :type network_node: PyNode """ network_node.unlock() network_node.addAttr("components", at="message") network_node.addAttr("name", dt="string", keyable=False) network_node.attr("name").set(self._unique_name, type="string") network_node.addAttr("has_skeleton", at="bool", dv=False, keyable=False) network_node.has_skeleton.set(lock=True) network_node.addAttr("skeleton_hierarchy", at="message") network_node.addAttr("bone_count_target", dv=120, keyable=False) network_node.bone_count_target.set(lock=True) network_node.addAttr("global_mover_visibility", at="bool", dv=True, keyable=False) network_node.global_mover_visibility.set(lock=True) network_node.addAttr("offset_mover_visibility", at="bool", dv=False, keyable=False) network_node.offset_mover_visibility.set(lock=True) network_node.addAttr("lra_visibility", at="bool", dv=False, keyable=False) network_node.lra_visibility.set(lock=True) network_node.lock() # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def save_dag_pose(self, name): """ Saves a new pose for the component movers and skeleton (basically storing dag poses). Stores the pose data on the network node for this asset. param name: The name of the pose. """ self.logger.info("{0}.save_dag_pose: Saving pose with the following name: {1}".format(self.__class__.__name__, name)) pose_inst = pose.Pose(self.get_movers()) pose_data = pose_inst.create_pose(name) if not self.network_node.hasAttr("dag_poses"): self.network_node.unlock() self.network_node.addAttr("dag_poses", dt="string") self.network_node.lock() data = {name: pose_data} data_string = json.dumps(data) utils.set_attribute(self.network_node, "dag_poses", data_string, attr_type="string") else: existing_data = json.loads(self.network_node.dag_poses.get()) existing_data[name] = pose_data data_string = json.dumps(existing_data) utils.set_attribute(self.network_node, "dag_poses", data_string, attr_type="string") del pose_inst # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def load_dag_pose(self, name): """ Loads a pose on the component movers or skeleton with the given name (if it exists). :param name: The name of the pose to load. """ self.logger.info("{0}.load_dag_pose: Loading pose {1}".format(self.__class__.__name__, name)) pose_data = json.loads(self.network_node.dag_poses.get()) pose_inst = pose.Pose(self.get_movers()) if self.network_node.has_skeleton.get(): component_utils.constrain_joints_to_movers() if name in pose_data.keys(): data = pose_data.get(name) pose_inst.load_pose(data) if self.network_node.has_skeleton.get(): component_utils.unconstrain_joints_to_movers() del pose_inst # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def remove_dag_pose(self, name): """ Removes a bind pose with the given name (if it exists). :param name: Name of the pose to remove. """ self.logger.info("{0}.remove_dag_pose: Removing pose {1}".format(self.__class__.__name__, name)) pose_data = json.loads(self.network_node.dag_poses.get()) if name in pose_data.keys(): pose_data.pop(name, None) data_string = json.dumps(pose_data) utils.set_attribute(self.network_node, "dag_poses", data_string, attr_type="string") # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def get_movers(self): """ Get all joint movers of this asset (returns movers from every component associated with this asset.) :return: Returns a list of pynodes that are the joint mover controls from every component in this asset. """ movers = [] components = self.network_node.components.connections() for component in components: movers.extend(component.global_movers.connections()) movers.extend(component.offset_movers.connections()) return movers # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # @property def name(self): """ This property holds the asset name. This could be the name of the character, for example. """ return self.network_node.attr("name").get() @name.setter def name(self, new_name): self.logger.info("{0}.name: Setting asset name to {1}".format(self.__class__.__name__, new_name)) if component_utils.validate_asset_name(new_name) is True: self.network_node.attr("name").set(new_name, type="string") self._unique_name = new_name pynode = pm.PyNode(self.network_node) utils.rename_object(pynode, self._unique_name + "_metadata") else: self.logger.warning("{0}.name: Invalid name. Unable to set.".format(self.__class__.__name__)) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # @property def network_node(self): """ This property holds the network node that contains the asset's metadata. """ return_node = None assets = component_utils.get_all_rig_asset_nodes() for asset in assets: if asset.attr("name").get() == self._unique_name: return_node = asset break return return_node