"""
Created 2020
@author Michelle Swann
This module contains definitions for rig class objects that themselves contain methods for generating and managing rig
related data
"""
import maya.cmds as cmds
import os
from Rig.Utils import Ctrl_Utils
from UI.Utils import UI_Utils
class Rig(object):
"""
This class is intended to create a class object to contain and manage all of the data stored in the rigConfig file
"""
def __init__(self, **kwargs):
"""
Sources data for self.__dict__ from the given rigConfig file or, if None, from kwargs
kwargs:
name: str(), given rig name (character name, prop name, etc..) should be formatted like so:
'PROJECT_Asset'
moduleDict: dict(), entry for modules' data if any exists
skinWeights: str(), file path to skin weight data if any
proxy: str(), file path to proxy geo if any
"""
self.CurrentFile = cmds.file(q=True, sn=True)
self.RigConfigPath = self.getRigConfigPath(**kwargs)
if UI_Utils.checkFile(self.RigConfigPath):
self.readWriteRigConfig(mode='r')
else:
self.Name = kwargs.get('name', None)
self.Modules = kwargs.get('moduleDict', dict())
self.SkinWeights = kwargs.get('skinWeights', None)
self.Proxy = kwargs.get('proxy', None)
hierarchy = DefaultHierarchy(name=self.Name)
self.__dict__['Hierarchy'] = hierarchy.__dict__
self.readWriteRigConfig(mode='w')
def manageRig(self, **kwargs):
"""
Updates the class object's rigConfig file with its current dictionary
kwargs: Used to update the class object's dictionary ( self.__dict__[kwarg] = kwargs[kwarg] )
"""
for kwarg in kwargs:
self.__dict__[kwarg] = kwargs[kwarg]
self.readWriteRigConfig(mode='w')
def getRigConfigPath(self, **kwargs):
"""
If a configPath is not given the user will be prompted to choose a location to save one to
kwargs:
configPath: str(), existing path or None if one is not given
returns:
configPath: str(), file path to which the rigConfig file can be written or read from
"""
configPath = kwargs.get('configPath', None)
if configPath is None:
if self.CurrentFile == '':
result = str(cmds.fileDialog2(ff='*.yaml', fm=3, ds=2,
cap='Select existing config file or target directory')[0])
configPath = os.path.join(result, 'rigConfig.yaml').replace('\\', '/')
else:
configPath = os.path.join('/'.join(self.CurrentFile.split('/')[0:-1]), 'rigConfig.yaml').replace('\\',
'/')
return configPath
def readWriteRigConfig(self, mode):
"""
Reads or writes to/from the class object's rigConfig file, either overwriting it's own dictionary with the
loaded data or writing its dictionary data to the file
Args:
mode: read or write
"""
if mode == 'w':
UI_Utils.readWriteYamlFile(mode=mode, filePath=self.RigConfigPath, data=self.__dict__)
if mode == 'r':
self.__dict__ = UI_Utils.readWriteYamlFile(mode=mode, filePath=self.RigConfigPath)
def manageModule(self, **kwargs):
"""
Contains all rig module management functions from an overall rig perspective
kwargs:
add: bool(), if true, used to add the given module's data entry to the rigConfig file
remove: bool(), if true, used to remove the given module's entry from the rigConfig file
edit: bool(), if true, used to update the given module's data entry in the rigConfig file
module: str(), python module path to the file containing the moduleType/className class definition
moduleType: str(), also className such as JntSingle, JntArray, or JntChain
name: str(), name of module to be managed
(other): additional kwargs are passed to the module instance during entry creation or edits
"""
add = kwargs.get('add', False)
remove = kwargs.get('remove', False)
edit = kwargs.get('edit', False)
module = kwargs.get('module', None)
moduleType = kwargs.get('moduleType', None)
className = moduleType
name = kwargs.get('name', None)
if add:
Instance = getattr(module, className)(**kwargs)
name = Instance.Name
if name not in self.Modules:
self.Modules[name] = Instance.__dict__
self.readWriteRigConfig(mode='w')
else:
cmds.error('{0} already in Modules'.format(Instance.Name))
if remove:
self.Modules.pop(name, None)
self.readWriteRigConfig(mode='w')
if edit:
Instance = getattr(module, className)(**kwargs)
name = Instance.Name
self.Modules[name] = Instance.__dict__
self.readWriteRigConfig(mode='w')
def build(self, **kwargs):
"""
Contains options for building rigs or layouts. This function is undoable.
kwargs:
mode: 'All' or 'Selected', determines which modules are to be built
layout: if true the given layout(s) will be built
rig: if true the given rig(s) will be built
name: if mode is 'Selected' a module name will need to be specified
"""
cmds.undoInfo(openChunk=True)
mode = kwargs.get('mode', 'Selected')
layout = kwargs.get('layout', False)
rig = kwargs.get('rig', False)
if mode == 'Selected':
name = kwargs.get('name', None)
if name is not None:
data = self.Modules[name]
moduleType = data['ModuleType']
module = data['Module']
className = moduleType
Instance = getattr(module, className)(data=data)
Instance.Module = module
options = data['Options']
if layout:
Instance.buildLayout(options=options)
elif rig:
Instance.updateLayoutData()
self.manageModule(edit=True, module=module, moduleType=moduleType, data=Instance.__dict__)
Instance.buildRig(options=options)
self.Modules[name] = Instance.__dict__
elif mode == 'All':
if self.Modules:
for name in self.Modules:
data = self.Modules[name]
moduleType = data['ModuleType']
module = data['Module']
className = moduleType
Instance = getattr(module, className)(data=data)
Instance.Module = module
options = data['Options']
if layout:
Instance.buildLayout(options=options)
elif rig:
Instance.updateLayoutData()
self.manageModule(edit=True, module=module, moduleType=moduleType, data=Instance.__dict__)
Instance.buildRig(options=options)
self.Modules[name] = Instance.__dict__
self.readWriteRigConfig(mode='w')
cmds.undoInfo(closeChunk=True)
class RigModule(object):
"""
This class is intended to create a class object to contain and manage rig module data
"""
def __init__(self, **kwargs):
"""
Sources data for self.__dict__ from given data or, if None, from kwargs
kwargs:
data: dict(), existing data input
module: str(), python module path to the file containing the moduleType/className class definition
moduleType: str(), also className, such as JntSingle, JntArray, or JntChain
side: str(), rig side ('L', 'R', 'C', 'N')
name: str(), user defined module name, moduleType if None
"""
data = kwargs.get('data', None)
if data:
self.__dict__ = data
elif not data:
self.ModuleType = kwargs.get('moduleType', None) # rig module type
self.Module = kwargs.get('module', None) # rig module
self.Side = kwargs.get('side', 'N') # module side
self.Name = kwargs.get('name', self.ModuleType) # module name
master = RigMaster('{0}_{1}_01_Master'.format(self.Name, self.Side))
self.Master = master.__dict__ # master ctrl name
modIO = '{0}_{1}_01_IO'.format(self.Name, self.Side)
modIn = '{0}_{1}_01_Input'.format(self.Name, self.Side)
modOut = '{0}_{1}_01_Output'.format(self.Name, self.Side)
self.IO = {'modIO': {'Name': modIO,
'Attributes': dict()},
'modIn': {'Name': modIn,
'Attributes': dict()},
'modOut': {'Name': modOut,
'Attributes': dict()}} # module IO group names
self.JointsGroup = RigTransform(name='{0}_{1}_01_Joints'.format(self.Name, self.Side),
parent=self.Master['Name']).__dict__ # module joint group info dict
self.ControlsGroup = RigTransform(name='{0}_{1}_01_Controls'.format(self.Name, self.Side),
parent=self.Master['Name']).__dict__ # module controls group info dict
self.KinematicsGroup = RigTransform(name='{0}_{1}_01_Kinematics'.format(self.Name, self.Side),
parent=self.Master['Name']).__dict__ # module kn joint group info dict
self.GeometryGroup = RigTransform(name='{0}_{1}_01_Geometry'.format(self.Name, self.Side),
parent=self.Master['Name']).__dict__ # module geo group info dict
self.LayoutGroup = RigTransform(name='{0}_{1}_01_Layout'.format(self.Name, self.Side),
parent=self.Master['Name']).__dict__ # module layout group info dict
self.JointCount = kwargs.get('jointCount', 1) # number of joints in the module
self.Vector = kwargs.get('vector', None) # direction the module is built
self.MirrorDict = kwargs.get('mirrorDict') # map for mirroring control attrs
self.Controls = kwargs.get('controls') # controls and their attributes
self.Constraints = kwargs.get('constraints') # constraints and their attributes
self.Options = kwargs.get('options', None)
self.Layout = self.generateLayoutData()
def buildLayout(self):
"""
Builds a master ctrl locator and all layout joints stored in self.Layout
"""
print('building layout')
master = RigMaster(name=self.Master['Name'], data=self.Master)
self.createMasterControl(name=self.Master['Name'], position=(0, 0, 0), orientation=(0, 0, 0), side=self.Side)
master.SetAttributeValues()
layoutGroup = RigTransform(name=self.LayoutGroup['Name'], data=self.LayoutGroup)
cmds.group(n=layoutGroup.Name, em=True)
cmds.parent(layoutGroup.Name, layoutGroup.Parent)
layoutGroup.SetAttributeValues()
# generate layout joints
for i in range(0, self.JointCount):
cmds.select(cl=True)
jnt = RigLayoutJoint(name=self.Layout['jntIndexDict'][i]['Name'], data=self.Layout['jntIndexDict'][i])
cmds.joint(n=jnt.Name)
jnt.SetAttributeValues()
cmds.parent(jnt.Name, jnt.Parent)
self.updateLayoutData()
def buildRig(self):
"""
Builds this rig module's kinematic systems and controls
"""
print('building rig')
# update layout data with current positions and rotations
self.updateLayoutData()
def updateLayoutData(self):
"""
Overwrites Attributes data in self.Layout and self.Master
"""
print('updating layout data')
for i in range(0, self.JointCount):
data = self.Layout['jntIndexDict'][i]
jnt = RigLayoutJoint(name=data['Name'], data=data)
jnt.RefreshAttributeValues()
master = RigMaster(name=self.Master['Name'], data=self.Master)
master.RefreshAttributeValues()
def createMasterControl(self, **kwargs):
"""
Creates a master ctrl locator
kwargs:
position: tuple(), a given position to place the master ctrl, (0, 0, 0) if None
orientation: tuple(), a given orientation for the master ctrl, (0, 0, 0) if None
"""
print('creating master ctrl')
name = self.Master['Name']
position = kwargs.get('position', (0, 0, 0))
orientation = kwargs.get('orientation', (0, 0, 0))
if name is None:
cmds.error('module is missing a name')
else:
masterCtrl = Ctrl_Utils.createCtrl(shape='master',
name=name,
color='darkGreen')[0]
cmds.setAttr('{0}.translate'.format(masterCtrl), position[0], position[1], position[2])
cmds.setAttr('{0}.rotate'.format(masterCtrl), orientation[0], orientation[1], orientation[2])
cmds.setAttr('{0}.moduleType'.format(masterCtrl), self.ModuleType, type='string')
cmds.select(masterCtrl)
return masterCtrl
def createIO(self, input, output):
"""
Creates IO groups and adds IO attrs
args:
input: dict(), input attrs, their types, values, parents, etc..
output: dict(), output attrs, their types, values, parents, etc..
"""
print('creating IO')
modIO = self.IO['modIO']['Name']
modIn = self.IO['modIn']['Name']
modOut = self.IO['modOut']['Name']
cmds.group(n=modIO, em=True)
cmds.group(n=modIn, em=True)
cmds.group(n=modOut, em=True)
cmds.parent(modIO, self.Master['Name'])
cmds.parent(modIn, modIO)
cmds.parent(modOut, modIO)
for grp in [input, output]:
self.addIOAttr(attrs=grp, side=self.Side)
def addIOAttr(self, attrs, side):
"""
Adds IO attrs to the IO groups
args:
attrs: dict(), attrs, their types, values, parents, etc..
side: str(), rig side ('L', 'R', 'C', 'N')
"""
attrDict = {}
for attr in attrs:
typ = attrs[attr]['Type']
parent = attrs[attr]['Parent']
value = attrs[attr]['Value']
nullTgt = attrs[attr]['Null Target']
mode = attrs[attr]['Mode']
IOGrp = self.IO['modIO']['Name']
grpDict = {'In': self.IO['modIn']['Name'],
'Out': self.IO['modOut']['Name']}
grp = grpDict[mode]
# add nulls
if nullTgt is not None:
null = cmds.group(n='{0}{1}_{2}_01_Null'.format(self.Name, ''.join(attr.split('_')), side), em=True)
cmds.parent(null, grp)
else:
null = None
# add attr
if parent is not None:
if value is not None and 'Matrix' not in typ:
cmds.addAttr(grp, ln=attr, at=typ, p=parent, k=True, h=False, dv=value)
cmds.addAttr(IOGrp, ln=attr, at=typ, p=parent, k=True, h=False, dv=value)
else:
cmds.addAttr(grp, ln=attr, at=typ, p=parent, k=True, h=False)
cmds.addAttr(IOGrp, ln=attr, at=typ, p=parent, k=True, h=False)
else:
if value is not None and 'Matrix' not in typ:
cmds.addAttr(grp, ln=attr, at=typ, k=True, h=False, dv=value)
cmds.addAttr(IOGrp, ln=attr, at=typ, k=True, h=False, dv=value)
else:
cmds.addAttr(grp, ln=attr, at=typ, k=True, h=False)
cmds.addAttr(IOGrp, ln=attr, at=typ, k=True, h=False)
# add decomposeMatrix node if necessary and set values if values exist
if typ == 'fltMatrix':
if value is not None:
cmds.setAttr('{0}.{1}'.format(grp, attr), value, typ='matrix')
mat = cmds.createNode('decomposeMatrix',
n='{0}_{1}_decomposeMatrix'.format(''.join(grp.split('_')),
''.join(attr.split('_'))))
else:
if value is not None:
cmds.setAttr('{0}.{1}'.format(grp, attr), value)
mat = None
# connect attrs
if mode == 'In':
cmds.connectAttr('{0}.{1}'.format(IOGrp, attr), '{0}.{1}'.format(grp, attr), f=True)
if mat is not None:
cmds.connectAttr('{0}.{1}'.format(grp, attr), '{0}.inputMatrix'.format(mat), f=True)
cmds.connectAttr('{0}.outputTranslate'.format(mat), '{0}.translate'.format(null), f=True)
cmds.connectAttr('{0}.outputRotate'.format(mat), '{0}.rotate'.format(null), f=True)
cmds.connectAttr('{0}.outputScale'.format(mat), '{0}.scale'.format(null), f=True)
cmds.connectAttr('{0}.outputShear'.format(mat), '{0}.shear'.format(null), f=True)
cmds.connectAttr('{0}.outputQuat'.format(mat), '{0}.rotateQuaternion'.format(null), f=True)
if mode == 'Out':
cmds.connectAttr('{0}.{1}'.format(grp, attr), '{0}.{1}'.format(IOGrp, attr), f=True)
if mat is not None:
cmds.connectAttr('{0}.worldMatrix[0]'.format(null), '{0}.{1}'.format(grp, attr), f=True)
cmds.connectAttr('{0}.outputTranslate'.format(mat), '{0}.translate'.format(null), f=True)
cmds.connectAttr('{0}.outputRotate'.format(mat), '{0}.rotate'.format(null), f=True)
cmds.connectAttr('{0}.outputScale'.format(mat), '{0}.scale'.format(null), f=True)
cmds.connectAttr('{0}.outputShear'.format(mat), '{0}.shear'.format(null), f=True)
cmds.connectAttr('{0}.outputQuat'.format(mat), '{0}.rotateQuaternion'.format(null), f=True)
if nullTgt is not None:
cmds.connectAttr('{0}.worldMatrix[0]'.format(nullTgt), '{0}.inputMatrix'.format(mat), f=True)
attrDict[attr] = [null, mat]
self.IO['mod{0}'.format(mode)]['Attributes'] = attrDict
return attrDict
def createKinematicJoints(self):
"""
Creates this rig module's kinematic system(s) joints
"""
print('creating kinematic joints')
jointsGroup = RigTransform(name=self.JointsGroup['Name'], data=self.JointsGroup)
cmds.group(n=jointsGroup.Name, em=True)
cmds.parent(jointsGroup.Name, jointsGroup.Parent)
jointsGroup.SetAttributeValues()
for i in range(0, self.JointCount):
cmds.select(cl=True)
layoutJnt = self.Layout['jntIndexDict'][i]['Name']
jnt = layoutJnt.replace('_LayoutJnt', '_Kn')
parent = jointsGroup.Name
cmds.joint(n=jnt)
cmds.parent(jnt, parent)
cmds.delete(cmds.parentConstraint(layoutJnt, jnt))
def createControls(self):
"""
Creates this rig module's kinematic system(s) controls
"""
print('creating controls')
colorDict = {'L': 'blue',
'R': 'red',
'C': 'yellow',
'N': None,
None: None}
controlsGroup = RigTransform(name=self.ControlsGroup['Name'], data=self.ControlsGroup)
cmds.group(n=controlsGroup.Name, em=True)
cmds.parent(controlsGroup.Name, controlsGroup.Parent)
controlsGroup.SetAttributeValues()
ctrlIndexDict = {}
for i in range(0, self.JointCount):
jnt = self.Layout['jntIndexDict'][i]['Name'].replace('_LayoutJnt', '_Kn')
ctrlInstance = RigControl(name=jnt.replace('_Kn', '_Ctrl'), color=colorDict[self.Side],
buffer=True, parent=controlsGroup.Name)
ctrlIndexDict[ctrlInstance.Name] = ctrlInstance.__dict__
ctrlInstance.create(snapTo=jnt)
cmds.parentConstraint(ctrlInstance.Name, jnt, mo=True)
self.Controls = ctrlIndexDict
def hideMaster(self):
"""
Hides this rig module's master ctrl locator and associated annotations
"""
print('hiding master')
for item in cmds.listRelatives(self.Master['Name']):
if cmds.nodeType(item) == 'annotationShape' or cmds.nodeType(item) == 'locator':
cmds.setAttr('{0}.visibility'.format(item), 0)
def finalize(self):
"""
Performs actions to finalize this rig module's rig build
"""
print('finalizing')
# hide masterCtrl shapes
self.hideMaster()
# hide layout
print('hiding layout group')
cmds.setAttr('{0}.visibility'.format(self.LayoutGroup['Name']), 0)
def generateLayoutData(self):
"""
Generates this rig module's layout data including master controls, layout joints and their positions and
orientations
"""
print('generating layout data')
class RigObject(object):
"""
This is the base class from which other rig objects are derived
"""
def __init__(self, name, **kwargs):
"""
Sources data for self.__dict__ from given data or, if None, from kwargs
args:
name: str(), this object's name
kwargs:
data: dict(), existing data input
parent: str(), this object's parent
"""
self.Name = name # object name
data = kwargs.get('data', None)
if not data:
self.Parent = kwargs.get('parent', None) # object parent
self.Attributes = dict() # object attributes and their associated properties
else:
self.__dict__ = data
def RefreshAttributeValues(self):
"""
Overwrites data in self.Attributes by querying those values from the associated Maya object
"""
axes = ['X', 'Y', 'Z']
axisVal = float()
for attr in self.Attributes.keys():
if 'translate' in attr:
value = cmds.xform(self.Name, t=True, q=True, ws=True)
for i in range(0, 3):
axis = axes[i]
if attr[-1] == axis:
axisVal = value[i]
self.Attributes[attr]['value'] = axisVal
elif 'rotate' in attr:
value = cmds.xform(self.Name, ro=True, q=True, ws=True)
for i in range(0, 3):
axis = axes[i]
if attr[-1] == axis:
axisVal = value[i]
self.Attributes[attr]['value'] = axisVal
elif 'scale' in attr:
value = cmds.xform(self.Name, s=True, q=True, ws=True)
for i in range(0, 3):
axis = axes[i]
if attr[-1] == axis:
axisVal = value[i]
self.Attributes[attr]['value'] = axisVal
else:
self.Attributes[attr]['value'] = cmds.getAttr('{0}.{1}'.format(self.Name, attr))
self.Attributes[attr]['locked'] = cmds.getAttr('{0}.{1}'.format(self.Name, attr), l=True)
self.Attributes[attr]['hidden'] = cmds.getAttr('{0}.{1}'.format(self.Name, attr), cb=True)
self.Attributes[attr]['input'] = cmds.connectionInfo('{0}.{1}'.format(self.Name, attr), sfd=True)
self.Attributes[attr]['output'] = cmds.connectionInfo('{0}.{1}'.format(self.Name, attr), dfs=True)
def SetAttributeValues(self):
"""
Sets and makes connections to and from attributes in self.Attributes for the associated Maya object
"""
for attr in self.Attributes.keys():
Hidden = self.Attributes[attr]['hidden']
Input = self.Attributes[attr]['input']
Locked = self.Attributes[attr]['locked']
Output = self.Attributes[attr]['output']
Value = self.Attributes[attr]['value']
try:
cmds.setAttr('{0}.{1}'.format(self.Name, attr), Value, cb=Hidden, l=Locked)
except RuntimeError as err:
cmds.warning('failed to set {0}.{1}\n{2}'.format(self.Name, attr, err))
if Input:
try:
cmds.connectAttr(Input, '{0}.{1}'.format(self.Name, attr), f=True)
except RuntimeError as err:
cmds.warning('failed to connect input attr {0} to {1}.{2}\n{3}'.format(Input, self.Name, attr, err))
if Output:
for tgt in Output:
try:
cmds.connectAttr('{0}.{1}'.format(self.Name, attr), tgt, f=True)
except RuntimeError as err:
cmds.warning('failed to connect output attr {0} to {1}.{2}\n{3}'.format(tgt, self.Name, attr,
err))
def RefreshParent(self):
"""
Overwrites the object's parent in self.Parent by querying the associated Maya object
"""
self.Parent = cmds.listRelatives(self.Name, p=True)[0]
class RigTransform(RigObject):
"""
Expanded RigObject. Contains new variable Type and additional Attributes entries
"""
def __init__(self, name, **kwargs):
"""
Sources data for self.__dict__ from given data or, if None, from kwargs
args:
name: str(), this object's name
kwargs:
data: dict(), existing data input
(other): additional kwargs are passed to the parent instance
"""
RigObject.__init__(self, name, **kwargs)
data = kwargs.get('data', None)
if not data:
self.Type = 'transform'
self.Attributes['translateX'] = {'value': 0,
'locked': False,
'hidden': False,
'input': None,
'output': None}
self.Attributes['translateY'] = {'value': 0,
'locked': False,
'hidden': False,
'input': None,
'output': None}
self.Attributes['translateZ'] = {'value': 0,
'locked': False,
'hidden': False,
'input': None,
'output': None}
self.Attributes['rotateX'] = {'value': 0,
'locked': False,
'hidden': False,
'input': None,
'output': None}
self.Attributes['rotateY'] = {'value': 0,
'locked': False,
'hidden': False,
'input': None,
'output': None}
self.Attributes['rotateZ'] = {'value': 0,
'locked': False,
'hidden': False,
'input': None,
'output': None}
self.Attributes['scaleX'] = {'value': 1,
'locked': False,
'hidden': False,
'input': None,
'output': None}
self.Attributes['scaleY'] = {'value': 1,
'locked': False,
'hidden': False,
'input': None,
'output': None}
self.Attributes['scaleZ'] = {'value': 1,
'locked': False,
'hidden': False,
'input': None,
'output': None}
self.Attributes['visibility'] = {'value': 1,
'locked': False,
'hidden': False,
'input': None,
'output': None}
else:
self.__dict__ = data
class RigJoint(RigTransform):
"""
Expanded RigTransform. Contains additional Attributes entries
"""
def __init__(self, name, **kwargs):
"""
Sources data for self.__dict__ from given data or, if None, from kwargs
args:
name: str(), this object's name
kwargs:
data: dict(), existing data input
(other): additional kwargs are passed to the parent instance
"""
RigTransform.__init__(self, name, **kwargs)
data = kwargs.get('data', None)
if not data:
self.Type = 'joint'
self.Attributes['radius'] = {'value': 1,
'locked': False,
'hidden': False,
'input': None,
'output': None}
self.Attributes['segmentScaleCompensate'] = {'value': 0,
'locked': False,
'hidden': True,
'input': None,
'output': None}
self.Attributes['jointOrientX'] = {'value': 0,
'locked': False,
'hidden': True,
'input': None,
'output': None}
self.Attributes['jointOrientY'] = {'value': 0,
'locked': False,
'hidden': True,
'input': None,
'output': None}
self.Attributes['jointOrientZ'] = {'value': 0,
'locked': False,
'hidden': True,
'input': None,
'output': None}
self.Attributes['overrideEnabled'] = {'value': 0,
'locked': False,
'hidden': True,
'input': None,
'output': None}
self.Attributes['overrideColor'] = {'value': 0,
'locked': False,
'hidden': True,
'input': None,
'output': None}
self.Attributes['overrideDisplayType'] = {'value': 0,
'locked': False,
'hidden': True,
'input': None,
'output': None}
else:
self.__dict__ = data
class RigKinematicJoint(RigJoint):
"""
Expanded RigJoint. Contains additional Attributes entries
"""
def __init__(self, name, **kwargs):
"""
Sources data for self.__dict__ from given data or, if None, from kwargs
args:
name: str(), this object's name
kwargs:
data: dict(), existing data input
(other): additional kwargs are passed to the parent instance
"""
RigJoint.__init__(self, name, **kwargs)
data = kwargs.get('data', None)
if not data:
self.Attributes['overrideEnabled'] = {'value': 1,
'locked': False,
'hidden': True,
'input': None,
'output': None}
self.Attributes['overrideColor'] = {'value': 0,
'locked': False,
'hidden': True,
'input': None,
'output': None}
self.Attributes['overrideDisplayType'] = {'value': 2,
'locked': False,
'hidden': True,
'input': None,
'output': None}
else:
self.__dict__ = data
class RigLayoutJoint(RigJoint):
"""
Expanded RigJoint. Contains additional Attributes entries
"""
def __init__(self, name, **kwargs):
"""
Sources data for self.__dict__ from given data or, if None, from kwargs
args:
name: str(), this object's name
kwargs:
data: dict(), existing data input
(other): additional kwargs are passed to the parent instance
"""
RigJoint.__init__(self, name, **kwargs)
data = kwargs.get('data', None)
if not data:
self.Attributes['overrideEnabled'] = {'value': 1,
'locked': False,
'hidden': True,
'input': None,
'output': None}
self.Attributes['overrideColor'] = {'value': 18,
'locked': False,
'hidden': True,
'input': None,
'output': None}
self.Attributes['overrideDisplayType'] = {'value': 0,
'locked': False,
'hidden': True,
'input': None,
'output': None}
else:
self.__dict__ = data
class RigBindJoint(RigJoint):
"""
Expanded RigJoint. Contains additional Attributes entries
"""
def __init__(self, name, **kwargs):
"""
Sources data for self.__dict__ from given data or, if None, from kwargs
args:
name: str(), this object's name
kwargs:
data: dict(), existing data input
(other): additional kwargs are passed to the parent instance
"""
RigJoint.__init__(self, name, **kwargs)
data = kwargs.get('data', None)
if not data:
self.Attributes['overrideEnabled'] = {'value': 1,
'locked': False,
'hidden': True,
'input': None,
'output': None}
self.Attributes['overrideColor'] = {'value': 13,
'locked': False,
'hidden': True,
'input': None,
'output': None}
self.Attributes['overrideDisplayType'] = {'value': 2,
'locked': False,
'hidden': True,
'input': None,
'output': None}
else:
self.__dict__ = data
class RigControl(RigTransform):
"""
Expanded RigTransform. Contains additional Attributes entries, control shape and color definitions, and functions
for building those shapes and managing their CV data
"""
#todo fix naming on shape nodes
colorDict = {
'blue': 6,
'lightBlue': 18,
'darkBlue': 5,
'red': 13,
'lightRed': 20,
'darkRed': 4,
'yellow': 17,
'lightYellow': 23,
'darkYellow': 22,
'green': 14,
'lightGreen': 19,
'darkGreen': 7,
'black': 1,
'white': 16
}
shapeDict = {
'cube': {
'type': 'curve',
'shapeCount': 1,
'defaultShapeCVDict': {
0: {'degree': 1,
0: (-0.5, -0.5, -0.5),
1: (-0.5, -0.5, 0.5),
2: (0.5, -0.5, 0.5),
3: (0.5, -0.5, -0.5),
4: (-0.5, -0.5, -0.5),
5: (-0.5, 0.5, -0.5),
6: (0.5, 0.5, -0.5),
7: (0.5, -0.5, -0.5),
8: (0.5, 0.5, -0.5),
9: (0.5, 0.5, 0.5),
10: (0.5, -0.5, 0.5),
11: (0.5, 0.5, 0.5),
12: (-0.5, 0.5, 0.5),
13: (-0.5, -0.5, 0.5),
14: (-0.5, 0.5, 0.5),
15: (-0.5, 0.5, -0.5)
}
}
},
'circle': {
'type': 'curve',
'shapeCount': 1,
'defaultShapeCVDict': {
0: {'degree': 1,
0: [0.783612, 0.0, -0.783612],
1: [0.0, 0.0, -1.108194],
2: [-0.783612, 0.0, -0.783612],
3: [-1.108194, 0.0, -0.0],
4: [-0.783612, -0.0, 0.783612],
5: [-0.0, -0.0, 1.108194],
6: [0.783612, -0.0, 0.783612],
7: [1.108194, -0.0, 0.0],
8: [1.108194, -0.0, 0.0],
9: [1.108194, -0.0, 0.0],
10: [0.783612, 0.0, -0.783612]
}
}
},
'sphere': {
'type': 'curve',
'shapeCount': 3,
'defaultShapeCVDict': {
0: {'degree': 3,
0: [0.0, 0.783612, -0.783612],
1: [0.0, 1.108194, -0.0],
2: [-0.0, 0.783612, 0.783612],
3: [-0.0, 0.0, 1.108194],
4: [-0.0, -0.783612, 0.783612],
5: [-0.0, -1.108194, 0.0],
6: [0.0, -0.783612, -0.783612],
7: [0.0, -0.0, -1.108194],
8: [0.0, -0.0, -1.108194],
9: [0.0, -0.0, -1.108194],
10: [0.0, -0.0, -1.108194]
},
1: {'degree': 3,
0: [0.783612, 0.0, -0.783612],
1: [0.0, 0.0, -1.108194],
2: [-0.783612, 0.0, -0.783612],
3: [-1.108194, 0.0, -0.0],
4: [-0.783612, -0.0, 0.783612],
5: [-0.0, -0.0, 1.108194],
6: [0.783612, -0.0, 0.783612],
7: [1.108194, -0.0, 0.0],
8: [1.108194, -0.0, 0.0],
9: [1.108194, -0.0, 0.0],
10: [1.108194, -0.0, 0.0]
},
2: {'degree': 3,
0: [0.783612, 0.783612, 0.0],
1: [0.0, 1.108194, 0.0],
2: [-0.783612, 0.783612, 0.0],
3: [-1.108194, 0.0, 0.0],
4: [-0.783612, -0.783612, 0.0],
5: [-0.0, -1.108194, 0.0],
6: [0.783612, -0.783612, 0.0],
7: [1.108194, -0.0, 0.0],
8: [1.108194, -0.0, 0.0],
9: [1.108194, -0.0, 0.0],
10: [1.108194, -0.0, 0.0]
}
}
},
'hemisphere': {
'type': 'curve',
'shapeCount': 3,
'defaultShapeCVDict': {
0: {'degree': 3,
0: [0.0, 0.783612, -0.783612],
1: [0.0, 1.108194, -0.0],
2: [-0.0, 0.783612, 0.783612],
3: [-0.0, 0.0, 1.108194],
4: [-0.0, 2e-06, 0.783612],
5: [-0.0, -2e-06, 0.0],
6: [0.0, 2e-06, -0.783612],
7: [0.0, -0.0, -1.108194],
8: [0.0, -0.0, -1.108194],
9: [0.0, -0.0, -1.108194],
10: [0.0, -0.0, -1.108194]
},
1: {'degree': 3,
0: [0.783612, 0.0, -0.783612],
1: [0.0, 0.0, -1.108194],
2: [-0.783612, 0.0, -0.783612],
3: [-1.108194, 0.0, -0.0],
4: [-0.783612, -0.0, 0.783612],
5: [-0.0, -0.0, 1.108194],
6: [0.783612, -0.0, 0.783612],
7: [1.108194, -0.0, 0.0],
8: [1.108194, -0.0, 0.0],
9: [1.108194, -0.0, 0.0],
10: [1.108194, -0.0, 0.0]
},
2: {'degree': 3,
0: [0.783612, 0.783612, 0.0],
1: [0.0, 1.108194, 0.0],
2: [-0.783612, 0.783612, 0.0],
3: [-1.108194, 0.0, 0.0],
4: [-0.783612, 2e-06, 0.0],
5: [-0.0, -2e-06, 0.0],
6: [0.783612, 2e-06, 0.0],
7: [1.108194, -0.0, 0.0],
8: [1.108194, -0.0, 0.0],
9: [1.108194, -0.0, 0.0],
10: [1.108194, -0.0, 0.0]
}
}
},
'square': {
'type': 'curve',
'shapeCount': 1,
'defaultShapeCVDict': {
0: {'degree': 1,
0: (-0.5, 0, -0.5),
1: (-0.5, 0, 0.5),
2: (0.5, 0, 0.5),
3: (0.5, 0, -0.5)
}
}
},
'master': {
'type': 'locator',
'shapeCount': 1
}
}
def __init__(self, name, **kwargs):
"""
Sources data for self.__dict__ from given data or, if None, from kwargs
args:
name: str(), this object's name
kwargs:
data: dict(), existing data input
shape: str(), which shape this object will build
color: str(), the color of the shapes this object will build
buffer: bool(), if True, builds a buffer control group over the associated Maya object
input: int(), defines the number of additional buffer/input groups that will be built over the
associated Maya object
(other): additional kwargs are passed to the parent instance
"""
RigTransform.__init__(self, name, **kwargs)
data = kwargs.get('data', None)
if not data:
self.Type = 'control'
self.Shape = kwargs.get('shape', 'circle')
self.Color = kwargs.get('color', None)
self.Buffer = kwargs.get('buffer', None)
self.InputLayerCount = kwargs.get('input', 0)
self.InputLayerDict = dict()
self.ShapeCVDict = self.shapeDict[self.Shape]['defaultShapeCVDict']
self.ShapeCount = self.shapeDict[self.Shape]['shapeCount']
self.Attributes['useOutlinerColor'] = {'value': 0,
'locked': False,
'hidden': True,
'input': None,
'output': None}
self.Attributes['outlinerColorR'] = {'value': 0,
'locked': False,
'hidden': True,
'input': None,
'output': None}
self.Attributes['outlinerColorG'] = {'value': 0,
'locked': False,
'hidden': True,
'input': None,
'output': None}
self.Attributes['outlinerColorB'] = {'value': 0,
'locked': False,
'hidden': True,
'input': None,
'output': None}
self.Attributes['overrideEnabled'] = {'value': 0,
'locked': False,
'hidden': True,
'input': None,
'output': None}
self.Attributes['overrideColor'] = {'value': 0,
'locked': False,
'hidden': True,
'input': None,
'output': None}
self.Attributes['overrideDisplayType'] = {'value': 0,
'locked': False,
'hidden': True,
'input': None,
'output': None}
else:
self.__dict__ = data
def manageShapeCVData(self, **kwargs):
"""
Reads or writes/overwrites self.ShapeCVDict data. This dictionary stores linear degree ctrl shape cv positional
data
kwargs:
mode: str(), Read ('r') or write ('w'), if None defaults to read
"""
mode = kwargs.get('mode', 'r')
for s in range(0, self.ShapeCount+1):
cvDict = self.ShapeCVDict[s]
cvCount = len([key for key in cvDict.keys() if key != 'degree'])
if mode == 'w':
for i in range(1, cvCount+1):
cv = '{0}Shape{1}.cv[{2}]'.format(self.Name, s, i)
position = [round(x, 6) for x in cmds.xform(cv, q=True, t=True, ws=True)]
cvDict[i] = position
self.ShapeCVDict[s] = cvDict
if mode == 'r':
for i in range(0, cvCount+1):
cv = '{0}Shape{1}.cv[{2}]'.format(self.Name, s, i)
position = cvDict[i]
cmds.xform(cv, t=position, ws=True)
def create(self, **kwargs):
"""
Creates the associated Maya object control and any control groups
kwargs:
snapTo: str(), a target to snap the new control or control groups to
position: tuple(), a target position to set the new control or control groups to
orientation: tuple(), a target orientation to set the new control or control groups to
"""
snapTo = kwargs.get('snapTo', None)
position = kwargs.get('position', None)
orientation = kwargs.get('orientation', None)
if self.Shape == 'master':
ctrl = cmds.spaceLocator(n=self.Name)[0]
else:
ctrl = cmds.group(n=self.Name, em=True)
for s in range(0, self.ShapeCount):
degree = self.ShapeCVDict[s]['degree']
cvCount = len(self.ShapeCVDict[s].keys())
shapeName = '{0}Shape{1}'.format(self.Name, s)
tempCrv = cmds.curve(n='temp', d=degree, p=[self.ShapeCVDict[s][cv] for cv in range(0, cvCount-1)])
tempShape = cmds.listRelatives(tempCrv)[0]
cmds.rename(tempShape, shapeName)
cmds.parent(shapeName, ctrl, s=True, r=True)
cmds.delete(tempCrv)
if self.Buffer is not None:
suffix = 'Grp'
self.groupSpecial(suffix=suffix)
grpInstance = RigTransform(name=ctrl+suffix)
if self.Parent is not None:
cmds.parent(grpInstance.Name, self.Parent)
grpInstance.RefreshParent()
grpInstance.RefreshAttributeValues()
self.Buffer = grpInstance.__dict__
self.Parent = grpInstance.Name
if snapTo is not None:
cmds.delete(cmds.parentConstraint(snapTo, grpInstance.Name))
if position is not None:
cmds.xform(grpInstance.Name, t=position, ws=True)
if orientation is not None:
cmds.xform(grpInstance.Name, ro=orientation, ws=True)
if self.InputLayerCount is not 0 and self.Buffer is not None:
for i in range(0, self.InputLayerCount):
suffix = 'Input{0}'.format(i)
self.groupSpecial(suffix=suffix)
grpInstance = RigTransform(name=ctrl+suffix)
if self.Parent is not None:
cmds.parent(grpInstance.Name, self.Parent)
grpInstance.RefreshParent()
grpInstance.RefreshAttributeValues()
self.InputLayerDict[i] = grpInstance.__dict__
self.Parent = grpInstance.Name
if snapTo is not None:
cmds.delete(cmds.parentConstraint(snapTo, grpInstance.Name))
if position is not None:
cmds.xform(grpInstance.Name, t=position, ws=True)
if orientation is not None:
cmds.xform(grpInstance.Name, ro=orientation, ws=True)
cmds.bakePartialHistory(ctrl, ppt=True)
if self.Color is not None:
if self.Shape == 'master':
for item in cmds.listRelatives(ctrl):
cmds.setAttr('{0}.overrideEnabled'.format(item), 1)
cmds.setAttr('{0}.overrideColor'.format(item), self.Color)
else:
cmds.setAttr('{0}.overrideEnabled'.format(ctrl), 1)
cmds.setAttr('{0}.overrideColor'.format(ctrl), self.Color)
cmds.setAttr('{0}.visibility'.format(ctrl), l=True, k=False)
def groupSpecial(self, suffix):
"""
Creates a group over the associated Maya object and matches it's position and orientation
args:
suffix: The suffix to append to the end of the new group's name
"""
grpParent = cmds.listRelatives(self.Name, p=True)
grp = cmds.group(n=self.Name + suffix, em=True)
cmds.delete(cmds.parentConstraint(self.Name, grp))
if grpParent is not None:
cmds.parent(grp, grpParent)
cmds.parent(self.Name, grp)
class RigMaster(RigControl):
"""
Extended RigControl. Contains additional Attributes entries
"""
def __init__(self, name, **kwargs):
"""
Sources data for self.__dict__ from given data or, if None, from kwargs
args:
name: str(), this object's name
kwargs:
data: dict(), existing data input
(other): additional kwargs are passed to the parent instance
"""
RigControl.__init__(self, name, **kwargs)
data = kwargs.get('data', None)
if not data:
self.Type = 'master'
self.Shape = 'master'
self.Attributes['built'] = {'value': False,
'locked': False,
'hidden': False,
'input': None,
'output': None}
self.Attributes['moduleType'] = {'value': None,
'locked': False,
'hidden': False,
'input': None,
'output': None}
else:
self.__dict__ = data
class DefaultHierarchy:
"""
A class for constructing default hierarchy information
"""
def __init__(self, **kwargs):
"""
Sources data for self.__dict__ from given data or, if None, from kwargs
kwargs:
name: str(), the rig's name
data: dict(), existing data input
"""
data = kwargs.get('data', None)
name = kwargs.get('name', None)
if not data:
self.World = RigTransform(name='{0}_N_01_World'.format(name),
parent=None).__dict__
self.PlacementCtrl = RigControl(name='{0}_N_01_PlacementCtrl'.format(name),
parent='{0}_N_01_Controls'.format(name)).__dict__
self.GeometryGroup = RigTransform(name='{0}_N_01_Geometry'.format(name),
parent='{0}_N_01_World'.format(name)).__dict__
self.KinematicsGroup = RigTransform(name='{0}_N_01_Kinematics'.format(name),
parent='{0}_N_01_World'.format(name)).__dict__
self.ControlsGroup = RigTransform(name='{0}_N_01_Controls'.format(name),
parent='{0}_N_01_World'.format(name)).__dict__
self.JointsGroup = RigTransform(name='{0}_N_01_Joints'.format(name),
parent='{0}_N_01_World'.format(name)).__dict__
self.LayoutGroup = RigTransform(name='{0}_N_01_Layout'.format(name),
parent='{0}_N_01_World'.format(name)).__dict__
self.BonesGroup = RigTransform(name='{0}_N_01_Bones'.format(name),
parent='{0}_N_01_World'.format(name)).__dict__
self.JointsRoot = RigKinematicJoint(name='{0}Root_N_01_Kn'.format(name),
parent='{0}_N_01_Joints'.format(name)).__dict__
self.LayoutRoot = RigLayoutJoint(name='{0}Root_N_01_LayoutJnt'.format(name),
parent='{0}_N_01_Layout'.format(name)).__dict__
self.BonesRoot = RigBindJoint(name='{0}Root_N_01_Bn'.format(name),
parent='{0}_N_01_Bones'.format(name)).__dict__
else:
self.__dict__ = data
def create(self):
"""
Creates the associated Maya objects
"""
hierarchyItems = [x for x in self.__dict__.keys()]
while len(hierarchyItems) > 0:
item = hierarchyItems[0]
name = self.__dict__[item]['Name']
parent = self.__dict__[item]['Parent']
typ = self.__dict__[item]['Type']
cmds.select(cl=True)
if parent is None:
if typ == 'transform':
cmds.group(n=name, em=True)
if parent is not None:
cmds.parent(name, parent)
if typ == 'joint':
cmds.joint(n=name)
cmds.parent(name, parent)
if typ == 'control':
shape = self.__dict__[item]['Shape']
color = self.__dict__[item]['Color']
(ctrl, ctrlGrp) = Ctrl_Utils.createCtrl(name=name, shape=shape, color=color)
cmds.parent(ctrlGrp, parent)
hierarchyItems.pop(0)
elif cmds.objExists(parent):
if typ == 'transform':
cmds.group(n=name, em=True)
cmds.parent(name, parent)
if typ == 'joint':
cmds.joint(n=name)
cmds.parent(name, parent)
if typ == 'control':
shape = self.__dict__[item]['Shape']
color = self.__dict__[item]['Color']
(ctrl, ctrlGrp) = Ctrl_Utils.createCtrl(name=name, shape=shape, color=color)
cmds.parent(ctrlGrp, parent)
hierarchyItems.pop(0)
else:
hierarchyItems.pop(0)
hierarchyItems.insert(-1, item)