def getSDKCurvesDrivenByObj(obj): curves = [] attrs = listAttr(obj, k=True, v=True, s=True, m=True) or [] for attr in attrs: curves += getSDKCurves(obj, attr) return removeDupes(curves)
def writeSetAttrCmd( trigger, connectIdxs=None ): cmdToks = [] assert isinstance( trigger, Trigger ) if connectIdxs is None: connectIdxs = [ idx for _,idx in trigger.connects() ] #make sure the zeroth connect isn't in the list and remove duplicates connectIdxs = removeDupes( connectIdxs ) if 0 in connectIdxs: connectIdxs.remove( 0 ) for connectIdx in connectIdxs: obj = trigger[connectIdx] attrs = cmd.listAttr( obj, k=True, s=True, v=True, m=True ) or [] objTrigger = Trigger( obj ) for a in attrs: attrPath = '%s.%s' % (obj, a) attrType = cmd.getAttr( attrPath, type=True ) attrValue = cmd.getAttr( attrPath ) if attrType.startswith( 'double' ): cmdToks.append( "setAttr %%%d.%s %0.5f;" % (connectIdx, a, attrValue) ) else: cmdToks.append( "setAttr %%%d.%s %d;" % (connectIdx, a, attrValue) ) return '\n'.join( cmdToks )
def getControlShapeFiles(): dir = CONTROL_DIRECTORY if isinstance(dir, basestring): dir = Path(dir) if not isinstance(dir, Path) or not dir.exists(): dir = Path(__file__).up() shapes = [] if dir.exists(): shapes = [f for f in dir.files() if f.hasExtension('shape')] if not shapes: searchPaths = map(Path, sys.path) searchPaths += map(Path, os.environ.get('MAYA_SCRIPT_PATH', '').split(';')) searchPaths = removeDupes(searchPaths) for d in searchPaths: try: shapes += [f for f in d.files() if f.hasExtension('shape')] except WindowsError: continue return shapes
def getSDKCurvesDrivenByObj( obj ): curves = [] attrs = listAttr( obj, k=True, v=True, s=True, m=True ) or [] for attr in attrs: curves += getSDKCurves( obj, attr ) return removeDupes( curves )
def autoSkinToVolumeMesh( mesh, skeletonMeshRoot ): ''' given a mesh and the root node for a hierarchy mesh volumes, this function will create a skeleton with the same hierarchy and skin the mesh to this skeleton using the mesh volumes to determine skin weights ''' #grab a list of meshes under the hierarchy - we need to grab this geo, parent it to a skeleton and transfer defacto weighting to the given mesh volumes = listRelatives( skeletonMeshRoot, ad=True, type='mesh', pa=True ) #now generate the skeleton transforms = removeDupes( listRelatives( volumes, p=True, type='transform', pa=True ) or [] ) jointRemap = {} for t in transforms: select( cl=True ) jName = '%s_joint' % t if objExists( jName ): jName += '#' j = joint( n=jName ) jointRemap[ t ] = j #now do parenting for t, j in jointRemap.iteritems(): tParent = listRelatives( t, p=True, pa=True ) if tParent: tParent = tParent[0] jParent = jointRemap.get( tParent, None ) else: jParent = None if jParent is not None: parent( j, jParent ) #now do positioning for t in apiExtensions.sortByHierarchy( transforms ): j = jointRemap[ t ] pos = xform( t, q=True, ws=True, rp=True ) move( pos[0], pos[1], pos[2], j, ws=True, rpr=True ) #duplicate the geometry and parent the geo to the joints in the skeleton we just created - store the duplicates so we can delete them later dupes = [] for t, j in jointRemap.iteritems(): dupe = apiExtensions.asMObject( duplicate( t, returnRootsOnly=True, renameChildren=True )[0] ) children = listRelatives( dupe, type='transform', pa=True ) or [] if children: delete( children ) parent( dupe, j ) dupes.append( dupe ) f = saveWeights( map( str, dupes ) ) loadWeights( [mesh], f, usePosition=True, tolerance=0.5, averageVerts=True, jointNameRemapDict=jointRemap ) delete( dupes ) return jointRemap
def on_storeB( self, *a ): kw = {} if self.UI_file.getValue(): kw[ 'filepath' ] = Path( self.UI_file.getValue() ) joints = cmd.ls( type='joint', r=True ) jointMeshes = removeDupes( cmd.listRelatives( joints, ad=True, pa=True, type='mesh' ) ) skinWeights.saveWeights( jointMeshes, **kw )
def getAutoOffsetAmount( placeObject, joints=None, axis=AX_Z, threshold=0.65 ): ''' returns a value reflecting the distance from the placeObject to the edge of the bounding box containing the verts skinned to the joints in the joints list the axis controls what edge of the bounding box is used if joints is None, [placeObject] is used ''' if joints is None: joints = [ placeObject ] else: joints = removeDupes( [ placeObject ] + joints ) #make sure the placeObject is the first item in the joints list, otherwise the bounds won't be transformed to the correct space #get the bounds of the geo skinned to the hand and use it to determine default placement of the slider control bounds = rigUtils.getJointBounds( joints ) offsetAmount = abs( bounds[ axis.isNegative() ][ axis % 3 ] ) #print bounds[ 0 ].x, bounds[ 0 ].y, bounds[ 0 ].z, bounds[ 1 ].x, bounds[ 1 ].y, bounds[ 1 ].z return offsetAmount
def getControlShapeFiles(): dir = CONTROL_DIRECTORY if isinstance( dir, basestring ): dir = Path( dir ) if not isinstance( dir, Path ) or not dir.exists(): dir = Path( __file__ ).up() shapes = [] if dir.exists(): shapes = [ f for f in dir.files() if f.hasExtension( 'shape' ) ] if not shapes: searchPaths = map( Path, sys.path ) searchPaths += map( Path, os.environ.get( 'MAYA_SCRIPT_PATH', '' ).split( ';' ) ) searchPaths = removeDupes( searchPaths ) for d in searchPaths: try: shapes += [ f for f in d.files() if f.hasExtension( 'shape' ) ] except WindowsError: continue return shapes
def buildMenuItems( parent, obj ): ''' build the menuItems in the dagProcMenu - it is possible to set a "kill menu" attribute on an object now that will stop the dagMenu building after the objMenu items have been added ''' defaultCmdName = "<empty cmd>" menusFromConnects = False killState = False objs = [ obj ] + (listRelatives( obj, pa=True, s=True ) or []) #the showMenusFromConnects attribute determines whether the object in question should show right click menus from any items connected to this one via triggered connects if objExists( '%s.showMenusFromConnects' % obj ): menusFromConnects = getAttr( '%s.showMenusFromConnects' % obj ) if menusFromConnects: connects = Trigger( obj ).connects() for connectObj, connectIdx in connects: objs.append( connectObj ) objs = removeDupes( objs ) #now get a list of objs that have menus - if there are more than one, build section labels, otherwise skip labels objsWithMenus = [] for obj in objs: obj = Trigger( obj ) if obj.menus(): objsWithMenus.append( obj ) doLabels = len( objsWithMenus ) > 1 setParent( parent, m=True ) for obj in objsWithMenus: #if ANY of the objs have the kill state set, turn it on if getKillState( obj ): killState = True tgts, names = spaceSwitching.getSpaceTargetsNames( obj ) names = [ 'parent to %s' % name for name in names ] if objExists( '%s.parent' % obj ): curIdx = getAttr( '%s.parent' % obj ) else: curIdx = None if doLabels: menuItem( l='---%s Menus---' % str( obj ).split( '|' )[-1].split( ':' )[-1], en=False ) for idx, cmdName, cmdStr in obj.menus( True ): #we need to construct the menu item using mel - because the tool was originally mel and all existing obj menu commands are written in mel #so you have to construct the menu item in mel otherwise its assumed the command is python... menuCmdToks = [ 'menuItem -l "%s"' % (cmdName or defaultCmdName) ] #so if the menu name starts with "parent to " then it assumed to be a menu item built by zooSpaceSwitching if cmdStr.startswith( "^parent to " ): if curIdx is not None: if idx == curIdx: menuCmdToks( '-cb 1' ) if cmdStr: menuCmdToks.append( '-c "%s"' % encodeString(cmdStr) ) mel.eval( ' '.join( menuCmdToks ) ) #should we die after menu build? if not killState: menuItem( d=True ) menuItem( d=True ) return killState
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
from melUtils import mel, printErrorStr SPACE_WORLD = rigUtils.SPACE_WORLD SPACE_LOCAL = rigUtils.SPACE_LOCAL SPACE_OBJECT = rigUtils.SPACE_OBJECT Axis = rigUtils.Axis CONTROL_DIRECTORY = None if CONTROL_DIRECTORY is None: #try to determine the directory that contains the control macros dirsToSearch = [ Path(__file__).up() ] + sys.path + os.environ['MAYA_SCRIPT_PATH'].split(os.pathsep) dirsToSearch = map(Path, dirsToSearch) dirsToSearch = removeDupes(dirsToSearch) for dirToSearch in dirsToSearch: if not dirToSearch.isDir(): continue foundControlDir = False for f in dirToSearch.files(recursive=True): if f.hasExtension('shape'): if f.name().startswith('control'): CONTROL_DIRECTORY = f.up() foundControlDir = True break if foundControlDir: break
def create(trigger, control=None, linear=True, optimize=True, preserve=False, defaultValue=0, maxValue=10): ''' converts poses stored as triggered menu commands on trigger to set driven keys on the given control. If control is None then the trigger itself is used. ''' if not isinstance(trigger, triggered.Trigger): trigger = triggered.Trigger(trigger) if control is None: control = trigger cmdData = trigger.menus() #gather data about the cmds first menuIdxs = [] menuNames = {} connectsUsedByCmds = {} cmdStrs = {} resolvedCmdStrs = {} sliderNames = {} sliderBreakdownValues = {} for menuIdx, menuName, cmdStr in cmdData: menuIdxs.append(menuIdx) menuNames[menuIdx] = menuName cmdStrs[menuIdx] = cmdStr connectsUsedByCmds[menuIdx] = [ trigger[connectIdx] for connectIdx in trigger.getConnectsUsedByCmd(cmdStr) ] resolvedCmdStrs[menuIdx] = trigger.resolve(cmdStr) #determine the slider name from the menu name sliderName = menuNames[menuIdx] while sliderName.startswith('_'): sliderName = sliderName[1:] underscoreIdx = sliderName.rfind('_') if underscoreIdx != -1: try: sliderBreakdownValues[menuIdx] = float( sliderName[underscoreIdx + 1:]) except ValueError: pass sliderName = sliderName[:underscoreIdx] else: sliderBreakdownValues[menuIdx] = maxValue sliderNames[menuIdx] = sliderName def setToDefaultPose(): melUtils.mel.eval(resolvedCmdStrs[menuIdxs[0]]) setToDefaultPose() #get a list of all target objects, so we know what objects to build SDK curves for allUsedConnectIdxs = [] for connectsUsed in connectsUsedByCmds.values(): allUsedConnectIdxs += connectsUsed allUsedConnects = removeDupes(allUsedConnectIdxs) #the user may want to preserve existing sdk data on the target objects... if not preserve: for connect in allUsedConnects: deleteSliders(connect) #delete any existing sliders for menuIdx in menuIdxs: sliderName = sliderNames[menuIdx] attrpath = '%s.%s' % (control, sliderName) if objExists(attrpath): deleteAttr(attrpath) #now build the sliders for menuIdx in menuIdxs: sliderName = sliderNames[menuIdx] breakdownVal = sliderBreakdownValues[menuIdx] #create the attribute if it doesn't already exist attrpath = '%s.%s' % (control, sliderName) if not objExists(attrpath): addAttr(control, ln=sliderName, at='double', min=0, max=0, dv=0) setAttr(attrpath, k=True) #now that the attribute is created, see what its limits are, and whether the current limit values should push #those limits further curMin = addAttr(attrpath, q=True, min=True) curMax = addAttr(attrpath, q=True, max=True) if breakdownVal < curMin: addAttr(attrpath, e=True, min=breakdownVal) elif breakdownVal > curMax: addAttr(attrpath, e=True, max=breakdownVal) #now build the SDKs, we know the attributes exist, so we don't need to worry about them sliders = [] sliderKeyCounts = [] for menuIdx in menuIdxs[1:]: attrpath = '%s.%s' % (control, sliderNames[menuIdx]) breakdownVal = sliderBreakdownValues[menuIdx] #return to default pose setAttr(attrpath, defaultValue) setToDefaultPose() for connect in connectsUsedByCmds[menuIdx]: setDrivenKeyframe(connect, cd=attrpath) #go into the pose and set its SDK key setAttr(attrpath, breakdownVal) melUtils.mel.eval(resolvedCmdStrs[menuIdx]) for connect in connectsUsedByCmds[menuIdx]: setDrivenKeyframe(connect, cd=attrpath) setAttr(attrpath, defaultValue) for connect in allUsedConnectIdxs: setCurveInfinityToLinear(connect) if optimize: deleteStaticChannels(connect) if linear: setTangentsTo(connect, 'linear')
def create( trigger, control=None, linear=True, optimize=True, preserve=False, defaultValue=0, maxValue=10 ): ''' converts poses stored as triggered menu commands on trigger to set driven keys on the given control. If control is None then the trigger itself is used. ''' if not isinstance( trigger, triggered.Trigger ): trigger = triggered.Trigger( trigger ) if control is None: control = trigger cmdData = trigger.menus() #gather data about the cmds first menuIdxs = [] menuNames = {} connectsUsedByCmds = {} cmdStrs = {} resolvedCmdStrs = {} sliderNames = {} sliderBreakdownValues = {} for menuIdx, menuName, cmdStr in cmdData: menuIdxs.append( menuIdx ) menuNames[menuIdx] = menuName cmdStrs[menuIdx] = cmdStr connectsUsedByCmds[menuIdx] = [ trigger[connectIdx] for connectIdx in trigger.getConnectsUsedByCmd( cmdStr ) ] resolvedCmdStrs[menuIdx] = trigger.resolve( cmdStr ) #determine the slider name from the menu name sliderName = menuNames[menuIdx] while sliderName.startswith( '_' ): sliderName = sliderName[1:] underscoreIdx = sliderName.rfind( '_' ) if underscoreIdx != -1: try: sliderBreakdownValues[menuIdx] = float( sliderName[underscoreIdx+1:] ) except ValueError: pass sliderName = sliderName[:underscoreIdx] else: sliderBreakdownValues[menuIdx] = maxValue sliderNames[menuIdx] = sliderName def setToDefaultPose(): melUtils.mel.eval( resolvedCmdStrs[ menuIdxs[0] ] ) setToDefaultPose() #get a list of all target objects, so we know what objects to build SDK curves for allUsedConnectIdxs = [] for connectsUsed in connectsUsedByCmds.values(): allUsedConnectIdxs += connectsUsed allUsedConnects = removeDupes( allUsedConnectIdxs ) #the user may want to preserve existing sdk data on the target objects... if not preserve: for connect in allUsedConnects: deleteSliders( connect ) #delete any existing sliders for menuIdx in menuIdxs: sliderName = sliderNames[menuIdx] attrpath = '%s.%s' % (control, sliderName) if objExists( attrpath ): deleteAttr( attrpath ) #now build the sliders for menuIdx in menuIdxs: sliderName = sliderNames[menuIdx] breakdownVal = sliderBreakdownValues[menuIdx] #create the attribute if it doesn't already exist attrpath = '%s.%s' % (control, sliderName) if not objExists( attrpath ): addAttr( control, ln=sliderName, at='double', min=0, max=0, dv=0 ) setAttr( attrpath, k=True ) #now that the attribute is created, see what its limits are, and whether the current limit values should push #those limits further curMin = addAttr( attrpath, q=True, min=True ) curMax = addAttr( attrpath, q=True, max=True ) if breakdownVal < curMin: addAttr( attrpath, e=True, min=breakdownVal ) elif breakdownVal > curMax: addAttr( attrpath, e=True, max=breakdownVal ) #now build the SDKs, we know the attributes exist, so we don't need to worry about them sliders = [] sliderKeyCounts = [] for menuIdx in menuIdxs[1:]: attrpath = '%s.%s' % (control, sliderNames[menuIdx]) breakdownVal = sliderBreakdownValues[menuIdx] #return to default pose setAttr( attrpath, defaultValue ) setToDefaultPose() for connect in connectsUsedByCmds[menuIdx]: setDrivenKeyframe( connect, cd=attrpath ) #go into the pose and set its SDK key setAttr( attrpath, breakdownVal ) melUtils.mel.eval( resolvedCmdStrs[menuIdx] ) for connect in connectsUsedByCmds[menuIdx]: setDrivenKeyframe( connect, cd=attrpath ) setAttr( attrpath, defaultValue ) for connect in allUsedConnectIdxs: setCurveInfinityToLinear( connect ) if optimize: deleteStaticChannels( connect ) if linear: setTangentsTo( connect, 'linear' )
from apiExtensions import asMObject, MObject from melUtils import mel, printErrorStr SPACE_WORLD = rigUtils.SPACE_WORLD SPACE_LOCAL = rigUtils.SPACE_LOCAL SPACE_OBJECT = rigUtils.SPACE_OBJECT Axis = rigUtils.Axis CONTROL_DIRECTORY = None if CONTROL_DIRECTORY is None: #try to determine the directory that contains the control macros dirsToSearch = [ Path( __file__ ).up() ] + sys.path + os.environ[ 'MAYA_SCRIPT_PATH' ].split( os.pathsep ) dirsToSearch = map( Path, dirsToSearch ) dirsToSearch = removeDupes( dirsToSearch ) for dirToSearch in dirsToSearch: if not dirToSearch.isDir(): continue foundControlDir = False for f in dirToSearch.files( recursive=True ): if f.hasExtension( 'shape' ): if f.name().startswith( 'control' ): CONTROL_DIRECTORY = f.up() foundControlDir = True break if foundControlDir: break