1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
| # Maya imports
import pymel.core as pm
import maya.OpenMaya as om
def simple_pose_reader( root_joint ):
"""
Function to create a simple pose reader with "bend", "twist", "side" attributes
on the selected joint to use to drive other systems
Args:
root_joint (pynode) : a pynode object representing the main joint used for the pose reading
Returns:
None
"""
def first_or_default( sequence, default=None ):
"""
Function to return the first item in a list or a default value
Args:
sequence (list) : a list of items to parse
default (object) : a default value to return back if nothing was parsed from sequence
Returns:
The first object within the sequence or a default value
"""
for item in sequence:
return item
return default
def get_bone_draw_axis( joint, default=om.MVector(0,1,0) ):
"""
Function to return the first item in a list or a default value
Args:
joint (pynode) : the pynode that represents the joint to determine the draw_axis of
default (object) : a default value to return back if the draw_axis was not determined
Returns:
A MVector type that represents the normalized direction of the joint draw axis
"""
try:
child = first_or_default( joint.getChildren( type='joint' ) )
except:
raise ValueError( joint + " does not have any children" )
# Get the local position of the child joint ( localspace is offset from the parent space )
pos = [ value for value in child.getTranslation( localSpace=True ) ]
# Check which axis is greater than the others, this will determine the draw_axis vector
# X Axis
if abs( pos[0] ) > abs( pos[1] ) and abs( pos[0] ) > abs( pos[2] ):
if pos[0] > 0.0:
return om.MVector( 1, 0, 0 )
return om.MVector( -1, 0, 0 )
# Y Axis
elif abs( pos[1] ) > abs( pos[0] ) and abs( pos[1] ) > abs( pos[2] ):
if pos[1] > 0.0:
return om.MVector( 0, 1, 0 )
return om.MVector( 0, -1, 0 )
# Z Axis
elif abs( pos[2] ) > abs( pos[0] ) and abs( pos[2] ) > abs( pos[1] ):
if pos[2] > 0.0:
return om.MVector( 0, 0, 1 )
return om.MVector( 0, 0, -1 )
return default
def snap_to_transform( snap_transform, snap_to_transform ):
"""
Function to snap a transform to another transform based on position and orientation
Args:
snap_transform (pynode) : the object to snap
snap_to_transform (pynode) : the object to snap to
Returns:
None
"""
snap_transform.setTranslation( snap_to_transform.getTranslation( worldSpace=True ),
worldSpace=True )
snap_transform.setRotation( snap_to_transform.getRotation( worldSpace=True ),
worldSpace=True )
# ensure pynode
root_joint = pm.PyNode( root_joint )
setup_dict = { 'root_joint' : root_joint,
'child_joint' : first_or_default( root_joint.getChildren( type='joint' ) ),
'parent' : root_joint.getParent() }
# create pose reader attributes on root_joint
for attr in [ 'Bend', 'Twist', 'Side' ]:
setup_dict['root_joint'].addAttr( 'psr_' + attr.lower(),
attributeType='float',
niceName='PSR ' + attr,
keyable=True )
attr_name_list = [ setup_dict['root_joint'].name(), '.psr_', attr.lower() ]
setup_dict[ 'attr_psr_' + attr.lower() ] = pm.PyNode( ''.join( attr_name_list ) )
# create organizing groups
setup_dict[ 'psr_main_grp' ] = pm.group( name=setup_dict['root_joint'].name() + '_psrMain_GRP',
empty=True )
setup_dict[ 'psr_target_grp' ] = pm.group( name=setup_dict['root_joint'].name() + '_psrTarget_GRP',
empty=True )
setup_dict[ 'psr_twist_grp' ] = pm.group( name=setup_dict['root_joint'].name() + '_psrTwist_GRP',
empty=True )
# create locators
for loc in [ 'psrMain', 'psrMainTarget', 'psrMainUp', 'psrTwist', 'psrTwistTarget', 'psrTwistUp' ]:
loc_name_list = [ setup_dict['root_joint'].name(), '_', loc, '_LOC' ]
setup_dict[ loc ] = pm.spaceLocator( name= ''.join( loc_name_list ) )
setup_dict[ loc ].setParent( setup_dict['psr_main_grp'] )
# target locators parent under the target group, which is driven by the root_joint
# the main grp is parented under the root_joint parent to maintain aiming without
# taking in extra transforms from the root_joint or it's children
[ setup_dict[ item ].setParent( setup_dict[ 'psr_target_grp' ] ) for item in [ 'psrMainTarget', 'psrTwistTarget' ] ]
[ setup_dict[ item ].setParent( setup_dict[ 'psr_main_grp' ] ) for item in [ 'psr_target_grp', 'psr_twist_grp' ] ]
[ setup_dict[ item ].setParent( setup_dict[ 'psr_twist_grp' ] ) for item in [ 'psrTwist', 'psrTwistUp' ] ]
if setup_dict[ 'parent' ]:
setup_dict[ 'psr_main_grp' ].setParent( setup_dict[ 'parent' ] )
# align main group to the selected root joint
snap_to_transform( setup_dict[ 'psr_main_grp' ], setup_dict[ 'root_joint' ] )
draw_axis = get_bone_draw_axis( setup_dict['root_joint'] )
child_trans = setup_dict[ 'child_joint' ].getAttr( 't' )
child_offset = om.MVector( child_trans[0], child_trans[1], child_trans[2] )
# X draw axis
if draw_axis == om.MVector( 1, 0, 0 ) or draw_axis == om.MVector( -1, 0, 0 ):
main_up_offset = om.MVector( child_offset.x, child_offset.x, 0 )
main_up_vector = om.MVector( 0, -1, 0 )
twist_target_offset = om.MVector( 0, child_offset.x, 0 )
setup_dict[ 'twist_driver_rot' ] = [ '.rotateY', '.rotateX', '.rotateZ' ]
# Y draw axis
elif draw_axis == om.MVector( 0, 1, 0 ) or draw_axis == om.MVector( 0, -1, 0 ):
main_up_offset = om.MVector( 0, child_offset.y, child_offset.y )
main_up_vector = om.MVector( 0, 0, -1 )
twist_target_offset = om.MVector( 0, child_offset.y, 0 )
setup_dict[ 'twist_driver_rot' ] = [ '.rotateZ', '.rotateY', '.rotateX' ]
# Z draw axis
else:
main_up_offset = om.MVector( child_offset.z, 0, child_offset.z )
main_up_vector = om.MVector( -1, 0, 0 )
twist_target_offset = om.MVector( 0, 0, child_offset.z )
setup_dict[ 'twist_driver_rot' ] = [ '.rotateX', '.rotateY', '.rotateY' ]
setup_dict[ 'psrMainTarget' ].setTranslation( child_offset * 0.5,
localSpace=True,
relative=True )
for loc in ['psrMain', 'psrTwistUp']:
setup_dict[ loc ].setTranslation( child_offset * -1.0,
localSpace=True,
relative=True )
setup_dict[ 'psrTwistTarget' ].setTranslation( twist_target_offset * -1.0,
localSpace=True,
relative=True )
setup_dict[ 'psrMainUp' ].setTranslation( main_up_offset * -1.0,
localSpace=True,
relative=True )
setup_dict[ 'psrMainAC' ] = pm.aimConstraint( setup_dict[ 'psrMainTarget' ],
setup_dict[ 'psrMain' ],
maintainOffset=True,
aimVector=[ draw_axis.x, draw_axis.y, draw_axis.z ],
upVector=[ main_up_vector.x, main_up_vector.y, main_up_vector.z ],
worldUpType='objectrotation',
worldUpObject=setup_dict[ 'psrMainUp' ].name(),
weight=1.0 )
setup_dict[ 'psrTwistAC' ] = pm.aimConstraint( setup_dict[ 'psrTwistTarget' ],
setup_dict[ 'psrTwist' ],
maintainOffset=True,
aimVector=[ main_up_vector.x, main_up_vector.y, main_up_vector.z ],
upVector=[ draw_axis.x * -1, draw_axis.y * -1, draw_axis.z * -1 ],
worldUpType='objectrotation',
worldUpObject=setup_dict[ 'psrTwistUp' ].name(),
weight=1.0 )
setup_dict[ 'psr_target_grp' ].setParent( setup_dict[ 'root_joint' ] )
# the bend and side rotations of main psr locator drives the twist psr grp
# this allows the child twist locator (under the twist psr grp) to maintain
# an accurate twist rotation only
pm.connectAttr( setup_dict['psrMain'] + setup_dict['twist_driver_rot'][0],
setup_dict[ 'psr_twist_grp'] + setup_dict['twist_driver_rot'][0],
force=True )
pm.connectAttr( setup_dict['psrMain'] + setup_dict['twist_driver_rot'][2],
setup_dict[ 'psr_twist_grp'] + setup_dict['twist_driver_rot'][2],
force=True )
# connect final calculations to the custom attributes on the root_joint
# these can be used for various setups that are driven from the pose reader
pm.connectAttr( setup_dict['psrMain'] + setup_dict['twist_driver_rot'][0],
setup_dict[ 'attr_psr_bend' ],
force=True )
pm.connectAttr( setup_dict['psrMain'] + setup_dict['twist_driver_rot'][2],
setup_dict[ 'attr_psr_side' ],
force=True )
pm.connectAttr( setup_dict['psrTwist'] + setup_dict['twist_driver_rot'][1],
setup_dict[ 'attr_psr_twist' ],
force=True )
selection = pm.ls( selection=True )
if selection:
simple_pose_reader( selection[0] )
|