Example #1
0
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 packageScripts( scriptFilesToPackage, destPackageFilepath, dependencyTree ):
	'''
	will package all given files and import dependencies into a single zip file
	'''
	destPackageFilepath = Path( destPackageFilepath ).setExtension( 'zip' )
	if destPackageFilepath.exists:
		destPackageFilepath.delete()

	filesToPackage = map( Path, scriptFilesToPackage )
	for f in scriptFilesToPackage:
		filesToPackage += dependencyTree.findDependencies( f, None, False )

	if not filesToPackage:
		return None

	#remove any duplicate files...
	filesToPackage = removeDupes( filesToPackage )

	#this is a little hacky - but we don't want to re-distribute wingdbstub so lets check to see if its in the list of files
	for f in filesToPackage:
		if f.name() == 'wingdbstub':
			filesToPackage.remove( f )
			break

	#now build the zip file
	import zipfile
	with zipfile.ZipFile( str( destPackageFilepath ), 'w' ) as thePackage:
		for f in filesToPackage:
			thePackage.write( str( f ), str( makeScriptPathRelative( f ) ) )

	return destPackageFilepath
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 api.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 )
Example #5
0
def buildPackage(dependencyTree=None):
    if not _PYTHON_TOOLS_TO_PACKAGE.exists():
        raise ValueError("Cannot find %s file!" %
                         _PYTHON_TOOLS_TO_PACKAGE.name())

    modulesToPackage = []
    for toolName in _PYTHON_TOOLS_TO_PACKAGE.read():
        if toolName:
            if toolName.startswith('#'):
                continue
            elif toolName.startswith('//'):
                continue

            modulesToPackage.append(toolName)

    cleanPackageDir()

    if dependencyTree is None:
        dependencyTree = generateDepTree()

    filesToPackage = []
    for moduleName in modulesToPackage:
        moduleScriptPath = dependencyTree.moduleNameToScript(moduleName)
        filesToPackage += dependencyTree.findDependencies(
            moduleScriptPath, None, False)

    if not filesToPackage:
        return None

    #remove any duplicate files...
    filesToPackage = removeDupes(filesToPackage)

    #this is a little hacky - but we don't want to re-distribute wingdbstub so lets check to see if its in the list of files
    for f in filesToPackage:
        if f.name() == 'wingdbstub':
            filesToPackage.remove(f)
            break

    print >> Good, "Found dependencies - %d files" % len(filesToPackage)
    for f in filesToPackage:
        relativePath = makeScriptPathRelative(f)
        packagedPath = _PACKAGE_DIR / relativePath
        if len(relativePath) > 1:
            packagedPath.up().create()

        f.copy(packagedPath)
        print 'copying ---->   %s' % f

    #now zip up the files into a package
    cmdStr = '7z a -r ..\\%s\\zooToolBoxPy.7z ..\\%s\\' % (_PACKAGE_DIR_NAME,
                                                           _PACKAGE_DIR_NAME)
    os.system(cmdStr)

    #now write a simple mel script to load the toolbox UI
    cmdStr = """global proc zooToolBox() { return; }"""
    bootstrapMelScript = _PACKAGE_DIR / 'zooToolBox.mel'
    bootstrapMelScript.write(cmdStr)
Example #6
0
    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 buildPackage( dependencyTree=None ):
	if not _PYTHON_TOOLS_TO_PACKAGE.exists:
		raise ValueError( "Cannot find %s file!" % _PYTHON_TOOLS_TO_PACKAGE.name() )

	modulesToPackage = []
	for toolName in _PYTHON_TOOLS_TO_PACKAGE.read():
		if toolName:
			if toolName.startswith( '#' ):
				continue
			elif toolName.startswith( '//' ):
				continue

			modulesToPackage.append( toolName )

	cleanPackageDir()

	if dependencyTree is None:
		dependencyTree = generateDepTree()

	filesToPackage = []
	for moduleName in modulesToPackage:
		moduleScriptPath = dependencyTree.moduleNameToScript( moduleName )
		filesToPackage += dependencyTree.findDependencies( moduleScriptPath, None, False )

	if not filesToPackage:
		return None

	#remove any duplicate files...
	filesToPackage = removeDupes( filesToPackage )

	#this is a little hacky - but we don't want to re-distribute wingdbstub so lets check to see if its in the list of files
	for f in filesToPackage:
		if f.name() == 'wingdbstub':
			filesToPackage.remove( f )
			break

	print >> Good, "Found dependencies - %d files" % len( filesToPackage )
	for f in filesToPackage:
		relativePath = makeScriptPathRelative( f )
		packagedPath = _PACKAGE_DIR / relativePath
		if len( relativePath ) > 1:
			packagedPath.up().create()

		f.copy( packagedPath )
		print 'copying ---->   %s' % f

	#now zip up the files into a package
	cmdStr = '7z a -r ..\\%s\\zooToolBoxPy.7z ..\\%s\\' % (_PACKAGE_DIR_NAME, _PACKAGE_DIR_NAME)
	os.system( cmdStr )

	#now write a simple mel script to load the toolbox UI
	cmdStr = """global proc zooToolBox() { return; }"""
	bootstrapMelScript = _PACKAGE_DIR / 'zooToolBox.mel'
	bootstrapMelScript.write( cmdStr )
Example #8
0
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
Example #9
0
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
Example #10
0
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
Example #11
0
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 api.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
Example #12
0
def saveWeights(geos, filepath=None):
    start = time.clock()
    miscData = api.writeExportDict(TOOL_NAME, TOOL_VERSION)

    #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:
            api.melWarning("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
                api.melWarning(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)
    melPrint('Weights Successfully Saved to %s: time taken %.02f seconds' %
             (filepath, time.clock() - start))

    return filepath
Example #13
0
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)
Example #14
0
from apiExtensions import asMObject, MObject
from common import 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 resetAttrs( obj, skipVisibility=True ):
	'''
	simply resets all keyable attributes on a given object to its default value
	great for running on a large selection such as all character controls...
	'''

	#obj = apiExtensions.asMObject( obj )
	attrs = listAttr( obj, k=True, s=True, m=True ) or []
	if skipVisibility:
		if 'visibility' in attrs:
			attrs.remove( 'visibility' )

	#if the transform is a joint, see if its part of a bindpose, and if so, restore to
	#the bindpose, not zero, as this is generally the preferred behaviour
	poses = listConnections( obj, s=False, type='dagPose' )
	bindPoses = []

	if poses:
		poses = removeDupes( poses )
		for pose in poses:
			if getAttr( '%s.bindPose' % pose ):
				bindPoses.append( pose )

	numBindPoses = len( bindPoses )

	if numBindPoses == 1:
		dagPose( obj, r=True, bp=True )

	#in this case we want to throw a list of bindposes to the user and let them pick which bindpose to go to
	elif numBindPoses > 1:
		dagPose( obj, r=True, name=bindPoses[ 0 ] )

	#otherwise just reset attribute values
	else:
		if not attrs:
			return

		selAttrs = channelBox( 'mainChannelBox', q=True, sma=True ) or channelBox( 'mainChannelBox', q=True, sha=True )

		for attr in attrs:

			#if there are selected attributes AND the current attribute isn't in the list of selected attributes, skip it...
			if selAttrs:
				attrShortName = attributeQuery( attr, n=obj, shortName=True )
				if attrShortName not in selAttrs:
					continue

			default = 0

			try:
				default = attributeQuery( attr, n=obj, listDefault=True )[ 0 ]
			except RuntimeError: pass

			attrpath = '%s.%s' % (obj, attr)
			if not getAttr( attrpath, settable=True ):
				continue

			#need to catch because maya will let the default value lie outside an attribute's
			#valid range (ie maya will let you creat an attrib with a default of 0, min 5, max 10)
			try:
				setAttr( attrpath, default )
			except RuntimeError: pass
Example #16
0
def resetAttrs(obj, skipVisibility=True):
    '''
	simply resets all keyable attributes on a given object to its default value
	great for running on a large selection such as all character controls...
	'''

    #obj = apiExtensions.asMObject( obj )
    attrs = listAttr(obj, k=True, s=True, m=True) or []
    if skipVisibility:
        if 'visibility' in attrs:
            attrs.remove('visibility')

    #if the transform is a joint, see if its part of a bindpose, and if so, restore to
    #the bindpose, not zero, as this is generally the preferred behaviour
    poses = listConnections(obj, s=False, type='dagPose')
    bindPoses = []

    if poses:
        poses = removeDupes(poses)
        for pose in poses:
            if getAttr('%s.bindPose' % pose):
                bindPoses.append(pose)

    numBindPoses = len(bindPoses)

    if numBindPoses == 1:
        dagPose(obj, r=True, bp=True)

    #in this case we want to throw a list of bindposes to the user and let them pick which bindpose to go to
    elif numBindPoses > 1:
        dagPose(obj, r=True, name=bindPoses[0])

    #otherwise just reset attribute values
    else:
        if not attrs:
            return

        selAttrs = channelBox('mainChannelBox', q=True,
                              sma=True) or channelBox(
                                  'mainChannelBox', q=True, sha=True)

        for attr in attrs:

            #if there are selected attributes AND the current attribute isn't in the list of selected attributes, skip it...
            if selAttrs:
                attrShortName = attributeQuery(attr, n=obj, shortName=True)
                if attrShortName not in selAttrs:
                    continue

            default = 0

            try:
                default = attributeQuery(attr, n=obj, listDefault=True)[0]
            except RuntimeError:
                pass

            attrpath = '%s.%s' % (obj, attr)
            if not getAttr(attrpath, settable=True):
                continue

            #need to catch because maya will let the default value lie outside an attribute's
            #valid range (ie maya will let you creat an attrib with a default of 0, min 5, max 10)
            try:
                setAttr(attrpath, default)
            except RuntimeError:
                pass
Example #17
0
from common import 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 saveWeights( geos, filepath=None ):
	start = time.clock()
	miscData = api.writeExportDict(TOOL_NAME, TOOL_VERSION)

	#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:
			api.melWarning("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
				api.melWarning(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 )
	melPrint( 'Weights Successfully Saved to %s: time taken %.02f seconds' % (filepath, time.clock()-start) )

	return filepath
Example #19
0
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 )