James Dunlop Homepage



om2-skinTo

INFO: Used to transfer a skin to another mesh(es)

USAGE: select the source, then destination mesh(es) or curve(s) and run skinTo()

LAST UPDATED: 02-02-2018

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

  • Can be found here: https://jamesbdunlop.github.io/jbd_om2/

    """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)
    bind = True
    if skCls is not None:
    logger.warning("Found a skinCluster on {}. Skipping bind!".format(destMFnDep.name()))
    bind = False

    if bind:
    ## Here we have to use CMDS to create the darn skinCluster and xfer!
    ## Note 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):
    """
    Protected function to source the skinCluster form the inMesh connection to the shapeNode
    This means if any blendshape colorSets etc etc it won't fail!
    @:param node: MObject for the source connection
    """

    skNode = None
    if node.apiType() == om2.MFn.kSkinClusterFilter:
    skNode = om2.MObjectHandle(node)
    else:
    mFn = om2.MFnDependencyNode(node)
    inputGeo_attr = mFn.attribute('inputGeometry')
    if om2.MObjectHandle(inputGeo_attr).isValid():
    inputGeo_plug = om2.MPlug(node, inputGeo_attr)
    newSource = inputGeo_plug.source().node()
    skNode = _iterForSkinCluster(newSource)
    return skNode

    return skNode

    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