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 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 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 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 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 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 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 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 apply(self, nodes, attributes=None, **kwargs): self.load() # do a version check - if older version clip is being used - perhaps we can write conversion functionality? try: if self._so.version != VER: printWarningStr("the anim clip version being loaded is old. YMMV!") except KeyError: printErrorStr("this is an old VER 1 pose clip - I don't know how to load them anymore...") return # generate the name mapping slamApply = kwargs.pop("slam", False) if slamApply: mapping = mappingUtils.matchNames(cmd.ls(typ="transform"), nodes) else: mapping = mappingUtils.matchNames(self._nodes, nodes) self._clip.applyToNodes(nodes)
def apply( self, nodes, attributes=None, **kwargs ): self.load() #do a version check - if older version clip is being used - perhaps we can write conversion functionality? try: if self._so.version != VER: printWarningStr( "the anim clip version being loaded is old. YMMV!" ) except KeyError: printErrorStr( "this is an old VER 1 pose clip - I don't know how to load them anymore..." ) return #generate the name mapping slamApply = kwargs.pop( 'slam', False ) if slamApply: mapping = mappingUtils.matchNames( cmd.ls( typ='transform' ), nodes ) else: mapping = mappingUtils.matchNames( self._nodes, nodes ) self._clip.applyToNodes( nodes )
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 __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 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 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 loadWeights( objects, filepath=None, usePosition=True, tolerance=TOL, axisMult=None, swapParity=True, averageVerts=True, doPreview=False, meshNameRemapDict=None, jointNameRemapDict=None ): ''' loads weights back on to a model given a file ''' #nothing to do... if not objects: print 'No objects given...' return if filepath is None: filepath = getDefaultPath() if not filepath.exists(): print 'File does not exist %s' % filepath return start = time.clock() #setup the mappings VertSkinWeight.MESH_NAME_REMAP_DICT = meshNameRemapDict VertSkinWeight.JOINT_NAME_REMAP_DICT = jointNameRemapDict #cache heavily access method objects as locals... skinPercent = cmd.skinPercent progressWindow = cmd.progressWindow xform = cmd.xform #now get a list of all weight files that are listed on the given objects - and #then load them one by one and apply them to the appropriate objects objItemsDict = {} for obj in objects: items = [] #this holds the vert list passed in IF any if obj.find('.') != -1: items = [obj] obj = obj.split('.')[0] try: objItemsDict[obj].extend( items ) except KeyError: objItemsDict[obj] = items numItems = len(objItemsDict) curItem = 1 progressWindow(e=True, title='loading weights from file %d items' % numItems) #load the data from the file miscData, joints, jointHierarchies, weightData = Path( filepath ).unpickle() #build the search tree tree = BinarySearchTree( weightData ) findMethod = tree.getWithin findMethodKw = { 'tolerance': tolerance } if averageVerts: findMethod = tree.getWithinRatio findMethodKw = { 'ratio': tolerance } #see if the file versions match if miscData[ presets.kEXPORT_DICT_TOOL_VER ] != TOOL_VERSION: printWarningStr( "WARNING: the file being loaded was stored from an older version (%d) of the tool - please re-generate the file. Current version is %d." % (miscData[ presets.kEXPORT_DICT_TOOL_VER ], TOOL_VERSION) ) #the miscData contains a dictionary with a bunch of data stored from when the weights was saved - do some #sanity checking to make sure we're not loading weights from some completely different source curFile = cmd.file(q=True, sn=True) origFile = miscData['scene'] if curFile != origFile: printWarningStr( 'the file these weights were saved in a different file from the current: "%s"' % origFile ) #remap joint names in the saved file to joint names that are in the scene - they may be namespace differences... missingJoints = set() for n, j in joints.iteritems(): if not cmd.objExists(j): #see if the joint with the same leaf name exists in the scene idxA = j.rfind(':') idxB = j.rfind('|') idx = max(idxA, idxB) if idx != -1: leafName = j[idx + 1:] if objExists( leafName ): joints[n] = leafName else: search = cmd.ls('%s*' % leafName, r=True, type='joint') if search: joints[n] = search[0] print '%s remapped to %s' % (j, search[0]) #now that we've remapped joint names, we go through the joints again and remap missing joints to their nearest parent #joint in the scene - NOTE: this needs to be done after the name remap so that parent joint names have also been remapped for n, j in joints.iteritems(): if not cmd.objExists(j): dealtWith = False for jp in jointHierarchies[n]: if cmd.objExists( jp ): joints[n] = jp dealtWith = True break if dealtWith: print '%s remapped to %s' % (j, jp) continue missingJoints.add(n) #now remove them from the list [ joints.pop(n) for n in missingJoints ] #axisMults can be used to alter the positions of verts saved in the weightData array - this is mainly useful for applying #weights to a mirrored version of a mesh - so weights can be stored on meshA, meshA duplicated to meshB, and then the #saved weights can be applied to meshB by specifying an axisMult=(-1,1,1) OR axisMult=(-1,) if axisMult is not None: for data in weightData: for n, mult in enumerate(axisMult): data[n] *= mult #we need to re-sort the weightData as the multiplication could have potentially reversed things... i could probably #be a bit smarter about when to re-order, but its not a huge hit... so, meh weightData = sortByIdx(weightData) #using axisMult for mirroring also often means you want to swap parity tokens on joint names - if so, do that now. #parity needs to be swapped in both joints and jointHierarchies if swapParity: for joint, target in joints.iteritems(): joints[joint] = str( names.Name(target).swap_parity() ) for joint, parents in jointHierarchies.iteritems(): jointHierarchies[joint] = [str( names.Name(p).swap_parity() ) for p in parents] for geo, items in objItemsDict.iteritems(): #if the geo is None, then check for data in the verts arg - the user may just want weights #loaded on a specific list of verts - we can get the geo name from those verts skinCluster = '' verts = cmd.ls(cmd.polyListComponentConversion(items if items else geo, toVertex=True), fl=True) #do we have a skinCluster on the geo already? if not, build one skinCluster = cmd.ls(cmd.listHistory(geo), type='skinCluster') if not skinCluster: skinCluster = cmd.skinCluster(geo,joints.values())[0] verts = cmd.ls(cmd.polyListComponentConversion(geo, toVertex=True), fl=True) else: skinCluster = skinCluster[0] #if we're using position, the restore weights path is quite different vertJointWeightData = [] if usePosition: progressWindow( e=True, status='searching by position: %s (%d/%d)' % (geo, curItem, numItems), maxValue=len( verts ) ) vCount = -1 for vert in verts: vCount += 1 pos = Vector( xform(vert, q=True, ws=True, t=True) ) foundVerts = findMethod( pos, **findMethodKw ) #accumulate found verts jointWeightDict = {} for v in foundVerts: for joint, weight in zip( v.joints, v.weights ): actualJoint = joints[ joint ] weight += jointWeightDict.get( actualJoint, 0 ) jointWeightDict[ actualJoint ] = weight #normalize the weights weightSum = float( sum( jointWeightDict.values() ) ) if weightSum != 1: for joint, weight in jointWeightDict.iteritems(): jointWeightDict[ joint ] = weight / weightSum #append the data vertJointWeightData.append( (vert, jointWeightDict.items()) ) #deal with the progress window - this isn't done EVERY vert because its kinda slow... if vCount % 50 == 0: progressWindow( e=True, progress=vCount ) #bail if we've been asked to cancel if progressWindow( q=True, isCancelled=True ): progressWindow( ep=True ) return progressWindow( e=True, status='maya is setting skin weights...' ) setSkinWeights( skinCluster, vertJointWeightData ) #otherwise simply restore by id else: progressWindow( e=True, status='searching by vert name: %s (%d/%d)' % (geo, curItem, numItems), maxValue=len( verts ) ) #rearrange the weightData structure so its ordered by vertex name weightDataById = {} [ weightDataById.setdefault(i.getVertName(), (i.joints, i.weights)) for i in weightData ] for vert in verts: #progressWindow(edit=True, progress=cur / num * 100.0) #if progressWindow(q=True, isCancelled=True): #progressWindow(ep=True) #return #cur += 1 try: jointList, weightList = weightDataById[vert] except KeyError: #in this case, the vert doesn't exist in teh file... print '### no point found for %s' % vert continue else: jointList = [ joints[ j ] for j in jointList ] jointsAndWeights = zip(jointList, weightList) skinPercent(skinCluster, vert, tv=jointsAndWeights) #remove unused influences from the skin cluster cmd.skinCluster( skinCluster, edit=True, removeUnusedInfluence=True ) curItem += 1 end = time.clock() print 'time for weight load %.02f secs' % (end-start)
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 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 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 saveWeights( geos, filepath=None ): start = time.clock() miscData = {} #if filepath is None, then generate a default filepath based on the location of the file if filepath is None: filepath = getDefaultPath() else: filepath = Path(filepath) geoAndData = {} skinPercent = cmd.skinPercent xform = cmd.xform #define teh data we're gathering masterJointList = [] weightData = [] #data gathering time! rigidBindObjects = [] for geo in geos: skinClusters = cmd.ls( cmd.listHistory( geo ), type='skinCluster' ) if len( skinClusters ) > 1: printWarningStr( "more than one skinCluster found on %s" % geo ) continue #so the geo isn't skinned in the traditional way - check to see if it is parented to a joint. if so, #stuff it into the rigid bind list to be dealt with outside this loop, and continue if not skinClusters: dealtWith = False for p in iterParents( geo ): if cmd.nodeType( p ) == 'joint': rigidBindObjects.append( (geo, p) ) masterJointList.append( p ) masterJointList = removeDupes( masterJointList ) dealtWith = True break if not dealtWith: msg = "cannot find a skinCluster for %s" % geo printWarningStr( msg ) continue skinCluster = skinClusters[ 0 ] masterJointList += cmd.skinCluster( skinCluster, q=True, inf=True ) masterJointList = removeDupes( masterJointList ) verts = cmd.ls(cmd.polyListComponentConversion(geo, toVertex=True), fl=True) for idx, vert in enumerate(verts): jointList = skinPercent(skinCluster, vert, ib=1e-4, q=True, transform=None) weightList = skinPercent(skinCluster, vert, ib=1e-4, q=True, value=True) if jointList is None: raise SkinWeightException("I can't find any joints - sorry. do you have any post skin cluster history???") pos = xform(vert, q=True, ws=True, t=True) vertData = VertSkinWeight( pos ) vertData.populate( geo, idx, [ masterJointList.index( j ) for j in jointList ], weightList ) weightData.append( vertData ) #deal with rigid bind objects for geo, j in rigidBindObjects: verts = cmd.ls( cmd.polyListComponentConversion(geo, toVertex=True), fl=True ) for idx, vert in enumerate( verts ): jIdx = masterJointList.index( j ) pos = xform( vert, q=True, ws=True, t=True ) vertData = VertSkinWeight( pos ) vertData.populate( geo, idx, [jIdx], [1] ) weightData.append( vertData ) #sort the weightData by ascending x values so we can search faster weightData.sort() #turn the masterJointList into a dict keyed by index joints = {} for n, j in enumerate( masterJointList ): joints[ n ] = j #generate joint hierarchy data - so if joints are missing on load we can find the best match jointHierarchies = {} for n, j in joints.iteritems(): jointHierarchies[ n ] = getAllParents( j ) toWrite = miscData, joints, jointHierarchies, weightData filepath = Path( filepath ) filepath.pickle( toWrite, False ) print 'Weights Successfully Saved to %s: time taken %.02f seconds' % (filepath, time.clock()-start) return filepath
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 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 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 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 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)