om2-skinTo


Disclaimer:
All code here is provided as is without support.
Use at your own risk! These posts assume you have some knowledge of import/running python script in maya. If Gifs/Images are not displaying in Chrome try a different browser.

"""2018 James Dunlop"""
import maya.api.OpenMaya as om2
import maya.cmds as cmds
import logging
import time
logging.basicConfig()
logger = logging.getLogger(__name__)
def skinTo(maxInfluences=4, byUVSpace=False, uvSpace=['map1', 'map1'], surfaceAssociation="closestComponent"):
"""
Supports: kMesh and kNurbsCurve
Use this to copy the skin weights from one mesh to another.
Note: You don't need to have an existing skinCluster on the mesh you want to transfer to. It will bind for you.
@:param maxInfluences: is the number of max influences on the source
@:param byUVSpace: if you want to xFer using UV maps or not
@:param uvSpace: The uvSpace flag indicates that the weight transfer should occur in UV space, based on the source
and destination UV sets specified.
@:param surfaceAssociation: The surfaceAssociation flag controls how the weights are transferred between the
surfaces: "closestPoint", "rayCast", or "closestComponent". The default is closestComponent.
:return:
"""
start = time.time()
mySel = om2.MSelectionList()
for eachMesh in cmds.ls(sl=True, long=True):
mySel.add(eachMesh)
## Bail out if bad selection
if mySel.length() < 2:
logger.warning("You must select a source mesh then ## of destination meshes!")
return
# Find the skinCluster and the influences we need to bind.
sourceMObj = mySel.getDependNode(0)
sourceMObjH = om2.MObjectHandle(sourceMObj)
sourceMFnDep = om2.MFnDependencyNode(sourceMObj)
sourceSkCls = _findSkinCluster(sourceMObjH)
if sourceSkCls is None:
logger.warning("No valid skinCluster could be found on {}".format(sourceMFnDep.name()))
return
bindInfluences = _findInfluences(sourceSkCls)
## Now bind each mesh to these influences
for x in range(mySel.length()):
## Skip source mesh
if x == 0:
continue
destMObj = mySel.getDependNode(x)
destMObjH = om2.MObjectHandle(destMObj)
destMFnDep = om2.MFnDependencyNode(destMObj)
skCls = _findSkinCluster(destMObjH)
if skCls is not None:
logger.warning("Found a skinCluster on {}. Skipping bind!".format(destMFnDep.name()))
else:
## Here we have to use CMDS to create the darn skinCluster and xfer!
## Not happy mixing cmds and om2 but short of writing a full om2 bind I'm sticking to this for now.
cmds.skinCluster(bindInfluences + [destMFnDep.name()], name="{}_skCls".format(destMFnDep.name()),
before=True, maximumInfluences=maxInfluences)
## Now copy the weights over
srcSkCls_MFnDep = om2.MFnDependencyNode(sourceSkCls.object())
if byUVSpace:
cmds.copySkinWeights(sourceSkin=srcSkCls_MFnDep.name(),
destinationSkin="{}_skCls".format(destMFnDep.name()),
sa=surfaceAssociation,
noMirror=True,
ia=["closestJoint", "label", ],
uvSpace=uvSpace)
else:
cmds.copySkinWeights(sourceSkin=srcSkCls_MFnDep.name(),
destinationSkin="{}_skCls".format(destMFnDep.name()),
sa=surfaceAssociation,
noMirror=True,
ia=["closestJoint", "label", ])
logger.info("SkinTo complete! Time taken: {}secs".format(start-time.time()))
#######################################################################################################################
### UTILS
def iterForSkinCluster(node):
"""
:param node: MObject for the source connection
:return: MObject
"""
if node.apiType() == om2.MFn.kSkinClusterFilter:
return om2.MObjectHandle(node)
iterDg = om2.MItDependencyGraph(node,
om2.MItDependencyGraph.kDownstream,
om2.MItDependencyGraph.kPlugLevel)
while not iterDg.isDone():
currentItem = iterDg.currentNode() # use currentItem for 2017 and below
if currentItem.hasFn(om2.MFn.kSkinClusterFilter):
return om2.MObjectHandle(currentItem)
iterDg.next()
def _findSkinCluster(mesh=None):
"""
Returns a skinCluster attached to the kMesh or kNurbsCurve
@:param mesh: MObjectHandle. Using the handles here may be playing it a little too safe. But meh.
:return: MObject
"""
if not mesh.isValid():
logger.warning("Destination is no longer valid!")
return
dagPath = om2.MDagPath()
geo = dagPath.getAPathTo(mesh.object())
## Does it have a valid number of shapes?
if geo.numberOfShapesDirectlyBelow() != 0:
## Fetch the shape of the geo now.
shapeMobj = geo.extendToShape().node()
mFn_shape = om2.MFnDependencyNode(shapeMobj)
apiType = shapeMobj.apiType()
if apiType == om2.MFn.kMesh:
## Look at the inMesh attr for the source
inMesh_attr = mFn_shape.attribute('inMesh')
elif apiType == om2.MFn.kNurbsCurve:
inMesh_attr = mFn_shape.attribute('create')
else:
logger.warning("This type of om2.MFn node is not supported! int: {}".format(apiType))
return
inMesh_plug = om2.MPlug(shapeMobj, inMesh_attr)
getSource = inMesh_plug.source().node()
## Now use the iterForSkinCluster() function to find the skinCluster in the connected network.
skinClusterNode_MObjH = _iterForSkinCluster(getSource)
if skinClusterNode_MObjH is not None:
return skinClusterNode_MObjH
else:
return None
return None
def _findInfluences(skinClusterMobjH=None):
"""
Returns all the valid influences from the .matrix attribute on the skinCluster node.
@:param mesh: MObjectHandle for the skinCluster. Using the handles here may be playing it a little too safe. But meh.
:return: MObject
"""
if not skinClusterMobjH.isValid():
logger.warning("Skincluster is no longer valid! Did it get deleted?")
return
skClsMFnDep = om2.MFnDependencyNode(skinClusterMobjH.object())
mtxAttr = skClsMFnDep.attribute("matrix")
matrixPlug = om2.MPlug(skinClusterMobjH.object(), mtxAttr)
## Get a list of all the valid connected indices in the matrix array now.
indices = matrixPlug.getExistingArrayAttributeIndices()
influences = []
for idx in indices:
influences.append(om2.MFnDependencyNode(matrixPlug.elementByLogicalIndex(idx).source().node()).absoluteName())
return influences
view raw om2_skinTo.py hosted with ❤ by GitHub