Anyway, here is a quick gif video of an example comparing two setups, the green is an action driven by a pose through an SDK - and the red is an action driven by a pose through the PSR.
The SDK setup is a simple, the joint's rotateZ attribute from 0-90 will drive the translateY attribute of the sphere from 0-1. Very limited without adding more animations.
Here is the simple graph setup for the PSR to drive the movement of the sphere. To summarize, you are utilizing the PSR's controlled rotation values and remapping those values to different values that are sent over to the sphere (in this instance a single rotation of the joint drives the translate y of the sphere).
And here is the script I was working with...
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] ) |
No comments:
Post a Comment