def switchToFk( control, handle=None, attrName='ikBlend', onValue=1, offValue=0, key=False, joints=None ): if handle is None: handle = control if handle is None or not objExists( handle ): printWarningStr( "no ikHandle specified" ) return #if we weren't passed in joints - discover them now if joints is None: joints = getJointsFromIkHandle( handle ) #make sure ik is on before querying rotations setAttr( '%s.%s' % (control, attrName), onValue ) rots = [] for j in joints: rot = getAttr( "%s.r" % j )[0] rots.append( rot ) #now turn ik off and set rotations for the joints setAttr( '%s.%s' % (control, attrName), offValue ) for j, rot in zip( joints, rots ): for ax, r in zip( ('x', 'y', 'z'), rot ): if getAttr( '%s.r%s' % (j, ax), se=True ): setAttr( '%s.r%s' % (j, ax), r ) alignFast( joints[2], handle ) if key: setKeyframe( joints ) setKeyframe( '%s.%s' % (control, attrName) )
def switchAnimationToFk(control, handle=None, attrName='ikBlend', onValue=1, offValue=0, key=True, startFrame=None, endFrame=None): #grab the key times for keys set on the t or r channels on the ik control - these are the frames we want to switch to fk on keyTimes = keyframe(control, q=True, at=('t', 'r'), tc=True) if not keyTimes: switchToFk(control, handle, attrName, offValue, key) printWarningStr("No keys found on the ik control - nothing to do!") return #remove duplicate key times and sort them keyTimes = removeDupes(keyTimes) keyTimes.sort() cropValues(keyTimes, startFrame, endFrame) joints = getJointsFromIkHandle(handle) for time in keyTimes: currentTime(time, e=True) switchToFk(control, handle, attrName, onValue, offValue, key, joints) select(joints[-1])
def switchAnimationToIk( control, poleControl=None, handle=None, attrName='ikBlend', onValue=1, key=True, startFrame=None, endFrame=None ): #get the joints the ik control drives - we need these to get keyframes from so we know which frames to trace the ik control on joints = getJointsFromIkHandle( handle ) if not joints: printWarningStr( "Cannot find the fk controls for the given ik control" ) return #grab the key times for keys set on the t or r channels on the ik control - these are the frames we want to switch to fk on keyTimes = keyframe( joints, q=True, at=('t', 'r'), tc=True ) if not keyTimes: switchToIk( ikControl, poleControl, handle, attrName, onValue, key ) printWarningStr( "No keys found on the fk controls - nothing to do!" ) return #remove duplicate key times and sort them keyTimes = removeDupes( keyTimes ) keyTimes.sort() cropValues( keyTimes, startFrame, endFrame ) #clear out the keys for the ik control cutKey( control, poleControl, t=(keyTimes[0], keyTimes[-1]), cl=True ) startFrame = keyTimes[0] currentTime( startFrame, e=True ) setKeyframe( control, poleControl, t=(startFrame,) ) for time in keyTimes: currentTime( time, e=True ) switchToIk( control, poleControl, handle, attrName, onValue, key, joints, _isBatchMode=True ) setKeyframe( control, t=keyTimes, at=attrName, v=onValue ) select( control )
def on_filterChanged( self, *args ): self.UI_files.setFilter( self.UI_filter.getValue() ) if self._filterChangeCB: try: self._filterChangeCB() except: printWarningStr( "The filter change callback %s failed!" % self._filterChangeCB )
def changeParent( parent=0, objs=None ): if objs is None: objs = ls( sl=True, type='transform' ) or [] #only bother with objects that have a "parent" attribute and whose parent attribute value is not the same as the one we've been passed objsToActOn = [] for obj in objs: if objExists( '%s.parent' % obj ): objsToActOn.append( obj ) objs = objsToActOn #if there are no objects, bail if not objs: printWarningStr( "There are no objects to work on - aborting!" ) return #store the initial time so we can restore it at the end initialTime = currentTime( q=True ) #first we need to make sure that any frame with a rotation key needs to have a key on ALL rotation axes - so make this happen keyTimes = keyframe( objs, q=True, at=('t', 'r'), tc=True ) if not keyTimes: printWarningStr( "No keys found on the objects - nothing to do!" ) return #remove duplicate key times and sort them keyTimes = removeDupes( keyTimes ) keyTimes.sort() #store the objects that each have keys at each key time in a dict so we don't have to query maya again. maya queries are slower than accessing python data structures timeObjs = {} for time in keyTimes: currentTime( time, e=True ) timeObjs[ time ] = objsWithKeysAtThisTime = [] for obj in objs: keyOnCurrentTime = keyframe( obj, q=True, t=(time,), at=('parent', 't', 'r'), kc=True ) if keyOnCurrentTime: setKeyframe( obj, at=('parent', 't', 'r') ) objsWithKeysAtThisTime.append( obj ) #now that we've secured the translation/rotation poses with keys on all axes, change the parent on each keyframe for time, objsWithKeysAtThisTime in timeObjs.iteritems(): currentTime( time, e=True ) for obj in objsWithKeysAtThisTime: pos = xform( obj, q=True, rp=True, ws=True ) rot = xform( obj, q=True, ro=True, ws=True ) #change the parent and move/rotate back to the original world space pose setAttr( '%s.parent' % obj, parent ) move( pos[0], pos[1], pos[2], obj, ws=True, rpr=True ) rotate( rot[0], rot[1], rot[2], obj, ws=True ) #lock in the pose setKeyframe( obj, at=('parent', 't', 'r') ) #finally restore the initial time currentTime( initialTime, e=True )
def getControlsFromObjs( control ): ''' attempts to retrieve the pole vector control, the ik handle and all fk controls given an ik rig control. The information is returned in a 3 tuple containing: ikHandle, poleControl, fkControls ''' errorValue = None, None, None, None try: part = rigPrimitives.RigPart.InitFromItem( control ) return part.getControl( 'control' ), part.getIkHandle(), part.getControl( 'poleControl' ), part.getFkControls() except rigPrimitives.RigPartError: pass #so if the control we've been given isn't a rig primitive, lets try to extract whatever information we can from right click commands - if any exist trigger = Trigger( ikControl ) switchCmdStr = None for n, cmdName, cmdStr in trigger.iterMenus(): if cmdName.lower() == _IK_CMD_NAME: switchCmdStr = trigger.resolve( cmdStr ) break if switchCmdStr is None: printWarningStr( "Cannot find the %s command - aborting!" % _IK_CMD_NAME ) return errorValue #extract the control handle from the switch command - it may or may not exist, depending on which rexStr = re.compile( '-ikHandle \%([a-ZA-Z0-9_:|]+)', re.IGNORECASE | re.MULTILINE ) match = rexStr.search( switchCmdStr ) if not match: if match.groups()[0]: control = match.groups()[0] #extract the ik handle from the switch command rexStr = re.compile( '-ikHandle \%([a-ZA-Z0-9_:|]+)', re.IGNORECASE | re.MULTILINE ) match = rexStr.search( switchCmdStr ) if not match: printWarningStr( "Could not determine the ik handle from the given control" ) return errorValue handle = match.groups()[0] if handle is None: printWarningStr( "Could not find the ik handle at the given connect index!" ) return errorValue #now extract the pole control from the switch command rexStr = re.compile( '-pole \%([a-ZA-Z0-9_:|]+)', re.IGNORECASE | re.MULTILINE ) match = rexStr.search( switchCmdStr ) if not match: printWarningStr( "Could not determine the pole vector control from the given control" ) return errorValue poleControl = match.groups()[0] if poleControl is None: printWarningStr( "Could not find the ik handle at the given connect index!" ) return errorValue return control, poleControl, handle, getJointsFromIkHandle( handle )
def getJointsFromIkHandle( handle ): #get the joints the ik control drives - we need these to get keyframes from so we know which frames to trace the ik control on joints = ikHandle( handle, q=True, jl=True ) effector = ikHandle( handle, q=True, ee=True ) cons = listConnections( '%s.tx' % effector, d=False ) if not cons: printWarningStr( "Could not find the end effector control!" ) return joints.append( cons[0] ) return joints
def getJointsFromIkHandle(handle): #get the joints the ik control drives - we need these to get keyframes from so we know which frames to trace the ik control on joints = ikHandle(handle, q=True, jl=True) effector = ikHandle(handle, q=True, ee=True) cons = listConnections('%s.tx' % effector, d=False) if not cons: printWarningStr("Could not find the end effector control!") return joints.append(cons[0]) return joints
def propagateWeightChangesToModel_confirm(): ''' simply wraps the propagateWeightChangesToModel function with a confirmation dialog ''' allMeshNodes = ls( type='mesh' ) allSkinnedMeshes = [ mesh for mesh in allMeshNodes if mel.findRelatedSkinCluster( mesh ) ] if not allSkinnedMeshes: printWarningStr( "No skinned meshes can be found in the scene! Aborting!" ) return BUTTONS = OK, CANCEL = 'Ok', 'Cancel' ret = confirmDialog( m='Are you sure you want to push skinning changes to the model?', t='Are you sure?', b=BUTTONS, db=CANCEL ) if ret == OK: propagateWeightChangesToModel( allSkinnedMeshes )
def switchAnimationToIk(control, poleControl=None, handle=None, attrName='ikBlend', onValue=1, key=True, startFrame=None, endFrame=None): #get the joints the ik control drives - we need these to get keyframes from so we know which frames to trace the ik control on joints = getJointsFromIkHandle(handle) if not joints: printWarningStr("Cannot find the fk controls for the given ik control") return #grab the key times for keys set on the t or r channels on the ik control - these are the frames we want to switch to fk on keyTimes = keyframe(joints, q=True, at=('t', 'r'), tc=True) if not keyTimes: switchToIk(ikControl, poleControl, handle, attrName, onValue, key) printWarningStr("No keys found on the fk controls - nothing to do!") return #remove duplicate key times and sort them keyTimes = removeDupes(keyTimes) keyTimes.sort() cropValues(keyTimes, startFrame, endFrame) #clear out the keys for the ik control cutKey(control, poleControl, t=(keyTimes[0], keyTimes[-1]), cl=True) startFrame = keyTimes[0] currentTime(startFrame, e=True) setKeyframe(control, poleControl, t=(startFrame, )) for time in keyTimes: currentTime(time, e=True) switchToIk(control, poleControl, handle, attrName, onValue, key, joints, _isBatchMode=True) setKeyframe(control, t=keyTimes, at=attrName, v=onValue) select(control)
def trace( self ): if not self._tracePairs: printWarningStr( "No objects to trace!" ) return #wrap the following in a try so we can ensure cleanup gets called always try: #make sure the objects in the transform list are sorted properly self._sortTransformNodes() #run the pre-trace method on all tracePair instances for tracePair in self._tracePairs: tracePair.preTrace() #early out if there are no keyframes keyTimes = self.getKeyTimes() if not keyTimes: printWarningStr( "No keys to trace!" ) return #match rotation orders if required transformTracePairs = list( self.getTransformNodePairs() ) if self._matchRotationOrder: for tracePair in transformTracePairs: src, tgt = tracePair.getSrcTgt() if getAttr( '%s.ro' % tgt, se=True ): setAttr( '%s.ro' % tgt, getAttr( '%s.ro' % src ) ) #clear out existing animation for the duration of the trace on target nodes for tracePair in self._tracePairs: src, tgt = tracePair.getSrcTgt() cutKey( tgt, t=(keyTimes[0], keyTimes[-1]), clear=True ) #execute the traceFrame method for each tracePair instance for keyTime in keyTimes: currentTime( keyTime ) for tracePair in self._tracePairs: tracePair.traceFrame( keyTime ) #make sure the postTrace method gets executed once the trace finishes finally: for tracePair in self._tracePairs: tracePair.postTrace()
def __init__( self, partContainer, skeletonPart=None ): if partContainer is not None: assert isRigPartContainer( partContainer ), "Must pass a valid rig part container! (received %s - a %s)" % (partContainer, nodeType( partContainer )) self._container = partContainer self._skeletonPart = skeletonPart self._worldPart = None self._worldControl = None self._partsNode = None self._qss = None self._idx = None if partContainer: if skeletonPart is None: try: self.getSkeletonPart() #this isn't fatal, although its not good except RigPartError, x: printWarningStr( str( x ) )
def switchAnimationToFk( control, handle=None, attrName='ikBlend', onValue=1, offValue=0, key=True, startFrame=None, endFrame=None ): #grab the key times for keys set on the t or r channels on the ik control - these are the frames we want to switch to fk on keyTimes = keyframe( control, q=True, at=('t', 'r'), tc=True ) if not keyTimes: switchToFk( control, handle, attrName, offValue, key ) printWarningStr( "No keys found on the ik control - nothing to do!" ) return #remove duplicate key times and sort them keyTimes = removeDupes( keyTimes ) keyTimes.sort() cropValues( keyTimes, startFrame, endFrame ) joints = getJointsFromIkHandle( handle ) for time in keyTimes: currentTime( time, e=True ) switchToFk( control, handle, attrName, onValue, offValue, key, joints ) select( joints[-1] )
def propagateWeightChangesToModel_confirm(): ''' simply wraps the propagateWeightChangesToModel function with a confirmation dialog ''' allMeshNodes = ls(type='mesh') allSkinnedMeshes = [ mesh for mesh in allMeshNodes if mel.findRelatedSkinCluster(mesh) ] if not allSkinnedMeshes: printWarningStr( "No skinned meshes can be found in the scene! Aborting!") return BUTTONS = OK, CANCEL = 'Ok', 'Cancel' ret = confirmDialog( m='Are you sure you want to push skinning changes to the model?', t='Are you sure?', b=BUTTONS, db=CANCEL) if ret == OK: propagateWeightChangesToModel(allSkinnedMeshes)
def __call__(self): if not self.ENABLED: return #if autokey is turned on, bail - this + autokey = potentially weird behaviour #NOTE: the tool will turn autokey off automatically when loaded - so if its on, its because the user has turned it back on. Also #worth noting - this tool will restore the initial autokey state when closed/turned off... if autoKeyframe(q=True): printWarningStr( "Autokey is enabled - This tool doesn't play nice with autokey! Please turn it off!" ) return #if there are no entries in here - bail, they've already been handled if not PRE_ATTR_VALUES: return #put the following into a single undo chunk try: undoInfo(openChunk=True) for attrpath, preValue in PRE_ATTR_VALUES.iteritems(): curValue = getAttr(attrpath) valueDelta = curValue - preValue #if there was no delta, keep loopin if not valueDelta: continue #if there are no keyframes on this attribute - keep loopin if not keyframe(attrpath, q=True, kc=True): continue keyframe(attrpath, e=True, t=(), vc=valueDelta, relative=True) PRE_ATTR_VALUES.clear() finally: undoInfo(closeChunk=True) #setup an idle event to re-populate the PRE_ATTR_VALUES dict when everything has finished processing scriptJob(runOnce=True, idleEvent=self.ui.on_selectionChange)
def changeRo( objs=None, ro=XYZ ): if ro not in ROT_ORDER_STRS: raise TypeError( "need to specify a valid rotation order - one of: %s" % ' '.join( ROT_ORDER_STRS ) ) if objs is None: objs = ls( sl=True, type='transform' ) roIdx = list( ROT_ORDER_STRS ).index( ro ) #filter out objects that don't have all 3 rotation axes settable and while we're at it store the rotation orders for each object #in a dict - since accessing a python dict is WAY faster than doing a getAttr for each frame in the loop below RO_DICT = {} objsWithAllChannelsSettable = [] for obj in objs: if not getAttr( '%s.r' % obj, se=True ): printWarningStr( "Not all rotation axes on the object %s are settable - skipping!" % obj ) continue objRo = getAttr( '%s.ro' % obj ) #if the rotation order of this object is the same as what we're changing it to - skip the object entirely if objRo == roIdx: printWarningStr( "The object %s already has the rotation order %s - skipping!" % (obj, ro) ) continue RO_DICT[ obj ] = objRo objsWithAllChannelsSettable.append( obj ) #early out if we have no objects to work on objs = objsWithAllChannelsSettable if not objs: printWarningStr( "No objects to act on - exiting" ) return #cache the conversion method convertToMethod = MATRIX_ROTATION_ORDER_CONVERSIONS_TO[ roIdx ] #construct a key server object to march over keys and objects keyServer = KeyServer( objs, True ) for time in keyServer: for obj in keyServer.getNodesAtTime(): setKeyframe( obj, at='r' ) #now that we're secured the rotation poses with keys on all axes, fix up each rotation value to use the desired rotation order for time in keyServer: for obj in keyServer.getNodesAtTime(): currentRoIdx = RO_DICT[ obj ] rot = getAttr( '%s.r' % obj )[0] rotMatrix = MATRIX_ROTATION_ORDER_CONVERSIONS_FROM[ currentRoIdx ]( degrees=True, *rot ) newRot = convertToMethod( rotMatrix, True ) setAttr( '%s.r' % obj, *newRot ) setKeyframe( obj, at='r' ) #now change the rotation order to what it should be for obj in objs: setAttr( '%s.ro' % obj, roIdx )
def __call__( self ): if not self.ENABLED: return #if autokey is turned on, bail - this + autokey = potentially weird behaviour #NOTE: the tool will turn autokey off automatically when loaded - so if its on, its because the user has turned it back on. Also #worth noting - this tool will restore the initial autokey state when closed/turned off... if autoKeyframe( q=True ): printWarningStr( "Autokey is enabled - This tool doesn't play nice with autokey! Please turn it off!" ) return #if there are no entries in here - bail, they've already been handled if not PRE_ATTR_VALUES: return #put the following into a single undo chunk try: undoInfo( openChunk=True ) for attrpath, preValue in PRE_ATTR_VALUES.iteritems(): curValue = getAttr( attrpath ) valueDelta = curValue - preValue #if there was no delta, keep loopin if not valueDelta: continue #if there are no keyframes on this attribute - keep loopin if not keyframe( attrpath, q=True, kc=True ): continue keyframe( attrpath, e=True, t=(), vc=valueDelta, relative=True ) PRE_ATTR_VALUES.clear() finally: undoInfo( closeChunk=True ) #setup an idle event to re-populate the PRE_ATTR_VALUES dict when everything has finished processing scriptJob( runOnce=True, idleEvent=self.ui.on_selectionChange )
def switchToIk(control, poleControl=None, handle=None, attrName='ikBlend', onValue=1, key=False, joints=None, _isBatchMode=False): if handle is None: handle = control if handle is None or not objExists(handle): printWarningStr("no ikHandle specified") return #if we weren't passed in joints - discover them now if joints is None: joints = getJointsFromIkHandle(handle) alignFast(control, joints[2]) if poleControl: if objExists(poleControl): pos = findPolePosition(joints[2], joints[1], joints[0]) move(pos[0], pos[1], pos[2], poleControl, a=True, ws=True, rpr=True) setKeyframe(poleControl) setAttr('%s.%s' % (control, attrName), onValue) if key: setKeyframe(control, at=('t', 'r')) if not _isBatchMode: setKeyframe(control, at=attrName)
def switchToFk(control, handle=None, attrName='ikBlend', onValue=1, offValue=0, key=False, joints=None): if handle is None: handle = control if handle is None or not objExists(handle): printWarningStr("no ikHandle specified") return #if we weren't passed in joints - discover them now if joints is None: joints = getJointsFromIkHandle(handle) #make sure ik is on before querying rotations setAttr('%s.%s' % (control, attrName), onValue) rots = [] for j in joints: rot = getAttr("%s.r" % j)[0] rots.append(rot) #now turn ik off and set rotations for the joints setAttr('%s.%s' % (control, attrName), offValue) for j, rot in zip(joints, rots): for ax, r in zip(('x', 'y', 'z'), rot): if getAttr('%s.r%s' % (j, ax), se=True): setAttr('%s.r%s' % (j, ax), r) alignFast(joints[2], handle) if key: setKeyframe(joints) setKeyframe('%s.%s' % (control, attrName))
def switchToIk( control, poleControl=None, handle=None, attrName='ikBlend', onValue=1, key=False, joints=None, _isBatchMode=False ): if handle is None: handle = control if handle is None or not objExists( handle ): printWarningStr( "no ikHandle specified" ) return #if we weren't passed in joints - discover them now if joints is None: joints = getJointsFromIkHandle( handle ) alignFast( control, joints[2] ) if poleControl: if objExists( poleControl ): pos = findPolePosition( joints[2], joints[1], joints[0] ) move( pos[0], pos[1], pos[2], poleControl, a=True, ws=True, rpr=True ) setKeyframe( poleControl ) setAttr( '%s.%s' % (control, attrName), onValue ) if key: setKeyframe( control, at=('t', 'r') ) if not _isBatchMode: setKeyframe( control, at=attrName )
def changeRo( objs=None, ro=XYZ ): if ro not in ROTATION_ORDER_STRS: raise TypeError( "need to specify a valid rotation order - one of: %s" % ' '.join( ROTATION_ORDER_STRS ) ) if objs is None: objs = ls( sl=True, type='transform' ) roIdx = list( ROTATION_ORDER_STRS ).index( ro ) #filter out objects that don't have all 3 rotation axes settable and while we're at it store the rotation orders for each object #in a dict - since accessing a python dict is WAY faster than doing a getAttr for each frame in the loop below RO_DICT = {} objsWithAllChannelsSettable = [] for obj in objs: if not getAttr( '%s.r' % obj, se=True ): printWarningStr( "Not all rotation axes on the object %s are settable - skipping!" % obj ) continue objRo = getAttr( '%s.ro' % obj ) #if the rotation order of this object is the same as what we're changing it to - skip the object entirely if objRo == roIdx: printWarningStr( "The object %s already has the rotation order %s - skipping!" % (obj, ro) ) continue RO_DICT[ obj ] = objRo objsWithAllChannelsSettable.append( obj ) #early out if we have no objects to work on objs = objsWithAllChannelsSettable if not objs: printWarningStr( "No objects to act on - exiting" ) return #first we need to make sure that any frame with a rotation key needs to have a key on ALL rotation axes - so make this happen keyTimes = keyframe( objs, q=True, at='r', tc=True ) if not keyTimes: printWarningStr( "No keys found on the objects - nothing to do!" ) return #remove duplicate key times and sort them keyTimes = removeDupes( keyTimes ) keyTimes.sort() #cache out the conversion method convertToMethod = MATRIX_ROTATION_ORDER_CONVERSIONS_TO[ roIdx ] #store the objects that each have keys at each key time in a dict so we don't have to query maya again. maya queries are slower than accessing python data structures timeObjs = {} for time in keyTimes: currentTime( time, e=True ) timeObjs[ time ] = objsWithKeysAtThisTime = [] for obj in objs: keyOnCurrentTime = keyframe( obj, q=True, t=(time,), kc=True ) if keyOnCurrentTime: setKeyframe( obj, at='r' ) objsWithKeysAtThisTime.append( obj ) #now that we're secured the rotation poses with keys on all axes, fix up each rotation value to use the desired rotation order for time, objsWithKeysAtThisTime in timeObjs.iteritems(): currentTime( time, e=True ) for obj in objsWithKeysAtThisTime: currentRoIdx = RO_DICT[ obj ] rot = getAttr( '%s.r' % obj )[0] rotMatrix = MATRIX_ROTATION_ORDER_CONVERSIONS_FROM[ currentRoIdx ]( degrees=True, *rot ) newRot = convertToMethod( rotMatrix, True ) setAttr( '%s.r' % obj, *newRot ) setKeyframe( obj, at='r' ) #now change the rotation order to what it should be for obj in objs: setAttr( '%s.ro' % obj, roIdx )
def getControlsFromObjs(control): ''' attempts to retrieve the pole vector control, the ik handle and all fk controls given an ik rig control. The information is returned in a 3 tuple containing: ikHandle, poleControl, fkControls ''' errorValue = None, None, None, None try: part = rigPrimitives.RigPart.InitFromItem(control) return part.getControl('control'), part.getIkHandle(), part.getControl( 'poleControl'), part.getFkControls() except rigPrimitives.RigPartError: pass #so if the control we've been given isn't a rig primitive, lets try to extract whatever information we can from right click commands - if any exist trigger = Trigger(ikControl) switchCmdStr = None for n, cmdName, cmdStr in trigger.iterMenus(): if cmdName.lower() == _IK_CMD_NAME: switchCmdStr = trigger.resolve(cmdStr) break if switchCmdStr is None: printWarningStr("Cannot find the %s command - aborting!" % _IK_CMD_NAME) return errorValue #extract the control handle from the switch command - it may or may not exist, depending on which rexStr = re.compile('-ikHandle \%([a-ZA-Z0-9_:|]+)', re.IGNORECASE | re.MULTILINE) match = rexStr.search(switchCmdStr) if not match: if match.groups()[0]: control = match.groups()[0] #extract the ik handle from the switch command rexStr = re.compile('-ikHandle \%([a-ZA-Z0-9_:|]+)', re.IGNORECASE | re.MULTILINE) match = rexStr.search(switchCmdStr) if not match: printWarningStr( "Could not determine the ik handle from the given control") return errorValue handle = match.groups()[0] if handle is None: printWarningStr( "Could not find the ik handle at the given connect index!") return errorValue #now extract the pole control from the switch command rexStr = re.compile('-pole \%([a-ZA-Z0-9_:|]+)', re.IGNORECASE | re.MULTILINE) match = rexStr.search(switchCmdStr) if not match: printWarningStr( "Could not determine the pole vector control from the given control" ) return errorValue poleControl = match.groups()[0] if poleControl is None: printWarningStr( "Could not find the ik handle at the given connect index!") return errorValue return control, poleControl, handle, getJointsFromIkHandle(handle)
def changeRo(objs=None, ro=XYZ): if ro not in ROTATION_ORDER_STRS: raise TypeError("need to specify a valid rotation order - one of: %s" % ' '.join(ROTATION_ORDER_STRS)) if objs is None: objs = ls(sl=True, type='transform') roIdx = list(ROTATION_ORDER_STRS).index(ro) #filter out objects that don't have all 3 rotation axes settable and while we're at it store the rotation orders for each object #in a dict - since accessing a python dict is WAY faster than doing a getAttr for each frame in the loop below RO_DICT = {} objsWithAllChannelsSettable = [] for obj in objs: if not getAttr('%s.r' % obj, se=True): printWarningStr( "Not all rotation axes on the object %s are settable - skipping!" % obj) continue objRo = getAttr('%s.ro' % obj) #if the rotation order of this object is the same as what we're changing it to - skip the object entirely if objRo == roIdx: printWarningStr( "The object %s already has the rotation order %s - skipping!" % (obj, ro)) continue RO_DICT[obj] = objRo objsWithAllChannelsSettable.append(obj) #early out if we have no objects to work on objs = objsWithAllChannelsSettable if not objs: printWarningStr("No objects to act on - exiting") return #first we need to make sure that any frame with a rotation key needs to have a key on ALL rotation axes - so make this happen keyTimes = keyframe(objs, q=True, at='r', tc=True) if not keyTimes: printWarningStr("No keys found on the objects - nothing to do!") return #remove duplicate key times and sort them keyTimes = removeDupes(keyTimes) keyTimes.sort() #cache out the conversion method convertToMethod = MATRIX_ROTATION_ORDER_CONVERSIONS_TO[roIdx] #store the objects that each have keys at each key time in a dict so we don't have to query maya again. maya queries are slower than accessing python data structures timeObjs = {} for time in keyTimes: currentTime(time, e=True) timeObjs[time] = objsWithKeysAtThisTime = [] for obj in objs: keyOnCurrentTime = keyframe(obj, q=True, t=(time, ), kc=True) if keyOnCurrentTime: setKeyframe(obj, at='r') objsWithKeysAtThisTime.append(obj) #now that we're secured the rotation poses with keys on all axes, fix up each rotation value to use the desired rotation order for time, objsWithKeysAtThisTime in timeObjs.iteritems(): currentTime(time, e=True) for obj in objsWithKeysAtThisTime: currentRoIdx = RO_DICT[obj] rot = getAttr('%s.r' % obj)[0] rotMatrix = MATRIX_ROTATION_ORDER_CONVERSIONS_FROM[currentRoIdx]( degrees=True, *rot) newRot = convertToMethod(rotMatrix, True) setAttr('%s.r' % obj, *newRot) setKeyframe(obj, at='r') #now change the rotation order to what it should be for obj in objs: setAttr('%s.ro' % obj, roIdx)
def doCreate(): positions = [] for obj in objs: positions.append( xform( obj, q=True, ws=True, rp=True ) ) #the objs may not be in the same hierarchy, so create a proxy chain that IS in a heirarchy proxyJoints = [] for obj in objs: select( cl=True ) j = createNode( 'joint' ) j = rename( j, '%s_dynChainProxy#' % obj.split( ':' )[-1].split( '|' )[-1] ) if proxyJoints: parent( j, proxyJoints[-1] ) delete( parentConstraint( obj, j ) ) proxyJoints.append( j ) #constrain the original to the proxy parentConstraint( j, obj ) #hook up the proxy root to a special message attribute so we can easily find the proxy chain again for things like baking etc... connectAttr( '%s.message' % proxyJoints[0], '%s.proxyRoot' % setNode ) #build a linear curve linearCurve = curve( d=1, p=positions ) linearCurveShape = listRelatives( linearCurve, s=True, pa=True )[0] select( linearCurve ) maya.mel.eval( 'makeCurvesDynamicHairs 1 0 1;' ) #find the dynamic curve shape cons = listConnections( '%s.worldSpace' % linearCurveShape, s=False ) if not cons: printWarningStr( "Cannot find follicle" ) return follicleShape = cons[0] cons = listConnections( '%s.outHair' % follicleShape, s=False ) if not cons: printWarningStr( "Cannot find hair system!" ) return hairSystemNode = cons[0] setAttr( '%s.startFrame' % hairSystemNode, playbackOptions( q=True, min=True ) ) cons = listConnections( '%s.outCurve' % follicleShape, s=False ) if not cons: printWarningStr( "Cannot find out curve!" ) return dynamicCurve = cons[0] dynamicCurveParent = listRelatives( dynamicCurve, p=True, pa=True ) #grab the dynamic curve's shape select( dynamicCurve ) maya.mel.eval( 'displayHairCurves "current" 1;' ) follicle = listRelatives( linearCurve, p=True, pa=True )[0] objParent = listRelatives( objs[0], p=True, pa=True ) if objParent: objParent = objParent[0] parent( follicle, objParent ) parent( proxyJoints[0], objParent ) setAttr( '%s.overrideDynamics' % follicle, 1 ) setAttr( '%s.pointLock' % follicle, 1 ) #hook up all the attributes connectAttr( '%s.stiffness' % setNode, '%s.stiffness' % follicle ) connectAttr( '%s.lengthFlex' % setNode, '%s.lengthFlex' % follicle ) connectAttr( '%s.damping' % setNode, '%s.damp' % follicle ) connectAttr( '%s.drag' % setNode, '%s.drag' % hairSystemNode ) connectAttr( '%s.friction' % setNode, '%s.friction' % hairSystemNode ) connectAttr( '%s.gravity' % setNode, '%s.gravity' % hairSystemNode ) connectAttr( '%s.turbStrength' % setNode, '%s.turbulenceStrength' % hairSystemNode ) connectAttr( '%s.turbFreq' % setNode, '%s.turbulenceFrequency' % hairSystemNode ) connectAttr( '%s.turbSpeed' % setNode, '%s.turbulenceSpeed' % hairSystemNode ) splineIkHandle = ikHandle( sj=proxyJoints[0], ee=proxyJoints[-1], curve=dynamicCurve, sol='ikSplineSolver', ccv=False )[0] #for some reason the dynamic curve gets re-parented by the ikHandle command (weird) so set the parent back to what it was originally parent( dynamicCurve, dynamicCurveParent )
def construct( self ): ''' builds the actual dynamic hair network ''' setNode = self._node objs = self.getObjs() #before we do anything, check to see whether the selected objects have any incoming connections warnAboutDisconnections = False for obj in objs: #check the object for incoming connections - if it has any, remove them for chan in ('t', 'r'): for ax in Axis.BASE_AXES: cons = listConnections( '%s.%s%s' % (obj, chan, ax), d=False ) if cons: warnAboutDisconnections = True if objectType( cons[0], isAType='animCurve' ): delete( cons[0] ) else: raise TypeError( "The object %s has non anim curve incoming connections - aborting! Please remove connections manually before proceeding" % obj ) if warnAboutDisconnections: printWarningStr( "Some of the objects had incoming connections (probably from animation). These connections have been broken! undo if you want them back" ) #wrap the creation of the nodes in a function - below this we execute this function via a wrapper which returns a list of new nodes created #this is done so we can easily capture the nodes created and store them in the set that describes this dynamic chain def doCreate(): positions = [] for obj in objs: positions.append( xform( obj, q=True, ws=True, rp=True ) ) #the objs may not be in the same hierarchy, so create a proxy chain that IS in a heirarchy proxyJoints = [] for obj in objs: select( cl=True ) j = createNode( 'joint' ) j = rename( j, '%s_dynChainProxy#' % obj.split( ':' )[-1].split( '|' )[-1] ) if proxyJoints: parent( j, proxyJoints[-1] ) delete( parentConstraint( obj, j ) ) proxyJoints.append( j ) #constrain the original to the proxy parentConstraint( j, obj ) #hook up the proxy root to a special message attribute so we can easily find the proxy chain again for things like baking etc... connectAttr( '%s.message' % proxyJoints[0], '%s.proxyRoot' % setNode ) #build a linear curve linearCurve = curve( d=1, p=positions ) linearCurveShape = listRelatives( linearCurve, s=True, pa=True )[0] select( linearCurve ) maya.mel.eval( 'makeCurvesDynamicHairs 1 0 1;' ) #find the dynamic curve shape cons = listConnections( '%s.worldSpace' % linearCurveShape, s=False ) if not cons: printWarningStr( "Cannot find follicle" ) return follicleShape = cons[0] cons = listConnections( '%s.outHair' % follicleShape, s=False ) if not cons: printWarningStr( "Cannot find hair system!" ) return hairSystemNode = cons[0] setAttr( '%s.startFrame' % hairSystemNode, playbackOptions( q=True, min=True ) ) cons = listConnections( '%s.outCurve' % follicleShape, s=False ) if not cons: printWarningStr( "Cannot find out curve!" ) return dynamicCurve = cons[0] dynamicCurveParent = listRelatives( dynamicCurve, p=True, pa=True ) #grab the dynamic curve's shape select( dynamicCurve ) maya.mel.eval( 'displayHairCurves "current" 1;' ) follicle = listRelatives( linearCurve, p=True, pa=True )[0] objParent = listRelatives( objs[0], p=True, pa=True ) if objParent: objParent = objParent[0] parent( follicle, objParent ) parent( proxyJoints[0], objParent ) setAttr( '%s.overrideDynamics' % follicle, 1 ) setAttr( '%s.pointLock' % follicle, 1 ) #hook up all the attributes connectAttr( '%s.stiffness' % setNode, '%s.stiffness' % follicle ) connectAttr( '%s.lengthFlex' % setNode, '%s.lengthFlex' % follicle ) connectAttr( '%s.damping' % setNode, '%s.damp' % follicle ) connectAttr( '%s.drag' % setNode, '%s.drag' % hairSystemNode ) connectAttr( '%s.friction' % setNode, '%s.friction' % hairSystemNode ) connectAttr( '%s.gravity' % setNode, '%s.gravity' % hairSystemNode ) connectAttr( '%s.turbStrength' % setNode, '%s.turbulenceStrength' % hairSystemNode ) connectAttr( '%s.turbFreq' % setNode, '%s.turbulenceFrequency' % hairSystemNode ) connectAttr( '%s.turbSpeed' % setNode, '%s.turbulenceSpeed' % hairSystemNode ) splineIkHandle = ikHandle( sj=proxyJoints[0], ee=proxyJoints[-1], curve=dynamicCurve, sol='ikSplineSolver', ccv=False )[0] #for some reason the dynamic curve gets re-parented by the ikHandle command (weird) so set the parent back to what it was originally parent( dynamicCurve, dynamicCurveParent ) newNodes, returnValue = getNodesCreatedBy( doCreate ) #stuff the nodes created into the set that describes this dynamic chain - just add transform nodes... for aNode in newNodes: if objectType( aNode, isAType='transform' ): sets( aNode, e=True, add=setNode )
def propagateWeightChangesToModel(meshes): ''' Given a list of meshes to act on, this function will store the skin weights, remove any edits from the skin clusters that affect them, open the scene file the meshes come from and apply the weights to the geometry in that scene. This makes it possible to fix skinning problems while animating with minimal workflow changes ''' curFile = Path(file(q=True, sn=True)) referencedMeshes = getRefFilepathDictForNodes(meshes) if not curFile.name(): printWarningStr( "The current scene isn't saved - please save the current scene first before proceeding!" ) return for refFilepath, refNodeMeshDict in referencedMeshes.iteritems(): referencesToUnload = [] #make sure we don't visit any of the meshes more than once meshesToUpdateWeightsOn = [] meshesToUpdateWeightsOn_withNS = [] for refNode, refMeshes in refNodeMeshDict.iteritems(): #get the maya filepath for the reference (with the "copy number") mayaFilepathForRef = referenceQuery(refNode, f=True) #get the namespace for this reference refNodeNamespace = file(mayaFilepathForRef, q=True, namespace=True) #check to see if there are any meshes in this reference that we need to store weights for for mesh_withNS in refMeshes: mesh = stripNamespaceFromNamePath(mesh_withNS, refNodeNamespace) if mesh in meshesToUpdateWeightsOn: continue meshesToUpdateWeightsOn.append(mesh) meshesToUpdateWeightsOn_withNS.append( (mesh_withNS, refNodeNamespace)) #append the file to the list of reference files that we need to unload referencesToUnload.append(mayaFilepathForRef) #get a list of skin cluster nodes - its actually the skin cluster nodes we want to remove edits from... nodesToCleanRefEditsFrom = [] for m, ns in meshesToUpdateWeightsOn_withNS: nodesToCleanRefEditsFrom.append(mel.findRelatedSkinCluster(m)) #now we want to store out the weighting from the referenced meshes weights = [] for mesh, meshNamespace in meshesToUpdateWeightsOn_withNS: weights.append(storeWeightsById(mesh, meshNamespace)) #also lets remove any ref edits from the mesh and all of its shape nodes - this isn't strictly nessecary, but I can't think of a reason to make edits to these nodes outside of their native file nodesToCleanRefEditsFrom.append(mesh) nodesToCleanRefEditsFrom += listRelatives(mesh, s=True, pa=True) or [] #remove the skinweights reference edits from the meshes in the current scene for f in referencesToUnload: file(f, unloadReference=True) #remove ref edits from the shape node as well - this isn't strictly nessecary but there probably shouldn't be changes to the shape node anyway for node in nodesToCleanRefEditsFrom: referenceEdit(node, removeEdits=True, successfulEdits=True, failedEdits=True) #re-load references for f in referencesToUnload: file(f, loadReference=True) #save this scene now that we've removed ref edits ensureCurrentFileIsCheckedOut() file(save=True, f=True) #load up the referenced file and apply the weighting to the meshes in that scene file(refFilepath, open=True, f=True) for mesh, weightData in zip(meshesToUpdateWeightsOn, weights): #if there is no weight data to store - keep loopin... if not weightData: continue skinCluster = mel.findRelatedSkinCluster(mesh) if not skinCluster: printWarningStr( "Couldn't find a skin cluster driving %s - skipping this mesh" % mesh) continue skinWeights.setSkinWeights(skinCluster, weightData) #save the referenced scene now that we've applied the weights to it ensureCurrentFileIsCheckedOut() file(save=True, f=True) #reload the original file file(curFile, o=True, f=True)
def propagateWeightChangesToModel( meshes ): ''' Given a list of meshes to act on, this function will store the skin weights, remove any edits from the skin clusters that affect them, open the scene file the meshes come from and apply the weights to the geometry in that scene. This makes it possible to fix skinning problems while animating with minimal workflow changes ''' curFile = Path( file( q=True, sn=True ) ) referencedMeshes = getRefFilepathDictForNodes( meshes ) if not curFile.name(): printWarningStr( "The current scene isn't saved - please save the current scene first before proceeding!" ) return for refFilepath, refNodeMeshDict in referencedMeshes.iteritems(): referencesToUnload = [] #make sure we don't visit any of the meshes more than once meshesToUpdateWeightsOn = [] meshesToUpdateWeightsOn_withNS = [] for refNode, refMeshes in refNodeMeshDict.iteritems(): #get the maya filepath for the reference (with the "copy number") mayaFilepathForRef = referenceQuery( refNode, f=True ) #get the namespace for this reference refNodeNamespace = file( mayaFilepathForRef, q=True, namespace=True ) #check to see if there are any meshes in this reference that we need to store weights for for mesh_withNS in refMeshes: mesh = stripNamespaceFromNamePath( mesh_withNS, refNodeNamespace ) if mesh in meshesToUpdateWeightsOn: continue meshesToUpdateWeightsOn.append( mesh ) meshesToUpdateWeightsOn_withNS.append( (mesh_withNS, refNodeNamespace) ) #append the file to the list of reference files that we need to unload referencesToUnload.append( mayaFilepathForRef ) #get a list of skin cluster nodes - its actually the skin cluster nodes we want to remove edits from... nodesToCleanRefEditsFrom = [] for m, ns in meshesToUpdateWeightsOn_withNS: nodesToCleanRefEditsFrom.append( mel.findRelatedSkinCluster( m ) ) #now we want to store out the weighting from the referenced meshes weights = [] for mesh, meshNamespace in meshesToUpdateWeightsOn_withNS: weights.append( storeWeightsById( mesh, meshNamespace ) ) #also lets remove any ref edits from the mesh and all of its shape nodes - this isn't strictly nessecary, but I can't think of a reason to make edits to these nodes outside of their native file nodesToCleanRefEditsFrom.append( mesh ) nodesToCleanRefEditsFrom += listRelatives( mesh, s=True, pa=True ) or [] #remove the skinweights reference edits from the meshes in the current scene for f in referencesToUnload: file( f, unloadReference=True ) #remove ref edits from the shape node as well - this isn't strictly nessecary but there probably shouldn't be changes to the shape node anyway for node in nodesToCleanRefEditsFrom: referenceEdit( node, removeEdits=True, successfulEdits=True, failedEdits=True ) #re-load references for f in referencesToUnload: file( f, loadReference=True ) #save this scene now that we've removed ref edits ensureCurrentFileIsCheckedOut() file( save=True, f=True ) #load up the referenced file and apply the weighting to the meshes in that scene file( refFilepath, open=True, f=True ) for mesh, weightData in zip( meshesToUpdateWeightsOn, weights ): #if there is no weight data to store - keep loopin... if not weightData: continue skinCluster = mel.findRelatedSkinCluster( mesh ) if not skinCluster: printWarningStr( "Couldn't find a skin cluster driving %s - skipping this mesh" % mesh ) continue skinWeights.setSkinWeights( skinCluster, weightData ) #save the referenced scene now that we've applied the weights to it ensureCurrentFileIsCheckedOut() file( save=True, f=True ) #reload the original file file( curFile, o=True, f=True )
def changeRo(objs=None, ro=XYZ): if ro not in ROT_ORDER_STRS: raise TypeError("need to specify a valid rotation order - one of: %s" % ' '.join(ROT_ORDER_STRS)) if objs is None: objs = ls(sl=True, type='transform') roIdx = list(ROT_ORDER_STRS).index(ro) #filter out objects that don't have all 3 rotation axes settable and while we're at it store the rotation orders for each object #in a dict - since accessing a python dict is WAY faster than doing a getAttr for each frame in the loop below RO_DICT = {} objsWithAllChannelsSettable = [] for obj in objs: if not getAttr('%s.r' % obj, se=True): printWarningStr( "Not all rotation axes on the object %s are settable - skipping!" % obj) continue objRo = getAttr('%s.ro' % obj) #if the rotation order of this object is the same as what we're changing it to - skip the object entirely if objRo == roIdx: printWarningStr( "The object %s already has the rotation order %s - skipping!" % (obj, ro)) continue RO_DICT[obj] = objRo objsWithAllChannelsSettable.append(obj) #early out if we have no objects to work on objs = objsWithAllChannelsSettable if not objs: printWarningStr("No objects to act on - exiting") return #cache the conversion method convertToMethod = MATRIX_ROTATION_ORDER_CONVERSIONS_TO[roIdx] #construct a key server object to march over keys and objects keyServer = KeyServer(objs, True) for time in keyServer: for obj in keyServer.getNodesAtTime(): setKeyframe(obj, at='r') #now that we're secured the rotation poses with keys on all axes, fix up each rotation value to use the desired rotation order for time in keyServer: for obj in keyServer.getNodesAtTime(): currentRoIdx = RO_DICT[obj] rot = getAttr('%s.r' % obj)[0] rotMatrix = MATRIX_ROTATION_ORDER_CONVERSIONS_FROM[currentRoIdx]( degrees=True, *rot) newRot = convertToMethod(rotMatrix, True) setAttr('%s.r' % obj, *newRot) setKeyframe(obj, at='r') #now change the rotation order to what it should be for obj in objs: setAttr('%s.ro' % obj, roIdx)
def appendPair( self, src, tgt ): if not objExists( src ) or not objExists( tgt ): printWarningStr( "Either the src or tgt nodes don't exist!" ) return self._tracePairs.append( TracePair( src, tgt ) )