def get_skin_clusters(nodes):
"""Get the skinClusters attached to the specified node and all nodes in descendents.
:param nodes: List of dag nodes.
@return A list of the skinClusters in the hierarchy of the specified root node.
"""
if isinstance(nodes, basestring):
nodes = [nodes, ]
all_skins = []
for node in nodes:
relatives = cmds.listRelatives(node, ad=True, path=True) or []
relatives.insert(0, node)
relatives = [shortcuts.get_shape(node) for node in relatives]
for relative in relatives:
history = cmds.listHistory(relative, pruneDagObjects=True, il=2) or []
skins = [x for x in history if cmds.nodeType(x) == 'skinCluster']
if skins:
all_skins.append(skins[0])
return list(set(all_skins))
python类skinCluster()的实例源码
def copy_skin(source, destination):
'''Duplicate the selected mesh with skinning'''
joints = utils.get_history(source, 'joint')
history = utils.get_history(destination, 'skinCluster')
if history:
skincluster = history[0]
for joint in joints:
try:
cmds.skinCluster(skincluster, edit=True, lw=True, ai=joint)
except RuntimeError as e:
if 'is already attached' not in str(e):
raise
else:
skincluster = cmds.skinCluster(*(joints + [destination]), tsb=True)
cmds.copySkinWeights(
source, destination,
noMirror=True,
surfaceAssociation='closestPoint',
influenceAssociation='closestJoint'
)
def copySkinInfluences(source, dest):
sourceSkin = getSkinCluster(source)
if not sourceSkin:
return False
joints = mc.skinCluster(sourceSkin, query=True, influence=True)
destSkin = getSkinCluster(dest)
if not destSkin:
destSkin = mc.skinCluster(joints, dest, toSelectedBones=True)[0]
else:
destJoints = mc.skinCluster(destSkin, query=True, influence=True)
for joint in [x for x in joints if x not in destJoints]:
mc.skinCluster(destSkin, edit=True, addInfluence=joint, lockWeights=False, weight=0)
return destSkin
def copySkinComponents(source, destinationVerts):
if not mc.listRelatives(source, shapes=True):
raise RuntimeError('Source object must be geometry.')
sourceSkin = getSkinCluster(source)
if not sourceSkin:
raise RuntimeError("Source mesh doesn't have a skinCluster to copy from.")
destMesh = mc.ls(destinationVerts[0], o=True)[0]
destMesh = mc.listRelatives(destMesh, parent=True)[0]
destSkin = copySkinInfluences(source, destMesh)
tempSet = mc.sets(destinationVerts)
mc.select(source, tempSet)
mc.copySkinWeights(noMirror=True,
surfaceAssociation='closestPoint',
influenceAssociation='closestJoint',
normalize=True)
mc.delete(tempSet)
mc.select(destinationVerts)
def export_skin(file_path=None, shapes=None):
"""Exports the skinClusters of the given shapes to disk in a pickled list of skinCluster data.
:param file_path: Path to export the data.
:param shapes: Optional list of dag nodes to export skins from. All descendent nodes will be searched for
skinClusters also.
"""
if shapes is None:
shapes = cmds.ls(sl=True) or []
# If no shapes were selected, export all skins
skins = get_skin_clusters(shapes) if shapes else cmds.ls(type='skinCluster')
if not skins:
raise RuntimeError('No skins to export.')
if file_path is None:
file_path = cmds.fileDialog2(dialogStyle=2, fileMode=0, fileFilter='Skin Files (*{0})'.format(EXTENSION))
if file_path:
file_path = file_path[0]
if not file_path:
return
if not file_path.endswith(EXTENSION):
file_path += EXTENSION
all_data = []
for skin in skins:
skin = SkinCluster(skin)
data = skin.gather_data()
all_data.append(data)
logging.info('Exporting skinCluster %s on %s (%d influences, %d vertices)',
skin.node, skin.shape, len(data['weights'].keys()), len(data['blendWeights']))
fh = open(file_path, 'wb')
json.dump(all_data, fh)
fh.close()
def __init__(self, skin_cluster):
"""Constructor"""
self.node = skin_cluster
self.shape = cmds.listRelatives(cmds.deformer(skin_cluster, q=True, g=True)[0], parent=True, path=True)[0]
# Get the skinCluster MObject
self.mobject = shortcuts.get_mobject(self.node)
self.fn = OpenMayaAnim.MFnSkinCluster(self.mobject)
self.data = {
'weights': {},
'blendWeights': [],
'name': self.node,
'shape': self.shape
}
def gather_data(self):
"""Gather all the skinCluster data into a dictionary so it can be serialized.
:return: The data dictionary containing all the skinCluster data.
"""
dag_path, components = self.__get_geometry_components()
self.gather_influence_weights(dag_path, components)
self.gather_blend_weights(dag_path, components)
for attr in SkinCluster.attributes:
self.data[attr] = cmds.getAttr('%s.%s' % (self.node, attr))
return self.data
def setUp(self):
self.joint1 = cmds.joint(p=(-0.5, -0.5, 0))
self.joint2 = cmds.joint(p=(0, 0.0, 0))
self.joint3 = cmds.joint(p=(0.5, 0.5, 0))
self.shape = cmds.polyCube()[0]
cmds.delete(self.shape, ch=True)
self.skin = cmds.skinCluster(self.joint1, self.joint2, self.joint3, self.shape)[0]
self.expected = {
'bindMethod': 1,
'blendWeights': [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
'dropoffRate': 4.0,
'heatmapFalloff': 0.0,
'maintainMaxInfluences': False,
'maxInfluences': 2,
'name': u'skinCluster1',
'normalizeWeights': 1,
'shape': u'pCube1',
'skinningMethod': 0,
'useComponents': False,
'weightDistribution': 0,
'weights': {
u'joint1': [0.9, 0.5, 0.5, 0.0, 0.5, 0.0, 0.9, 0.5],
u'joint2': [0.10000000000000002,
0.5,
0.5,
0.5,
0.5,
0.5,
0.10000000000000002,
0.5],
u'joint3': [0.0, 0.0, 0.0, 0.5, 0.0, 0.5, 0.0, 0.0]}
}
def dm2skin_getLargestInfluenceOnVert(vertex, skinCluster=None):
"""Given a vertex returns the largest influence in the provided
skin cluster that acts upon it."""
if not skinCluster:
return False
vertInfs = cmds.skinCluster(skinCluster, q=True, inf=True)
vertVals = cmds.skinPercent(skinCluster, vertex, q=True, value=True)
return vertInfs[vertVals.index(max(vertVals))]
def dm2skin_getNeighbouringJoints(joint, vertexString=None, cluster=None, influences=3):
"""This gets a list of nearby joints in the skin cluster to joint up to
the number of influences. These will be the ones we use in our minimization
later"""
if not cmds.objExists(joint):
return False
if influences < 3:
return False
if not cluster:
return False
clusterJoints = cmds.skinCluster(cluster, q=True, inf=True)
pos1 = cmds.xform(vertexString, q=True, ws=True, t=True)
parentJoint = cmds.listRelatives(joint, parent=True)
subtract = 1
# add the main joint
resultList = [joint]
# i've found it works best to always include the parent
if parentJoint and parentJoint in clusterJoints:
resultList.insert(0, parentJoint[0])
subtract = 2
# for the rest of the available influences get a list of nearby joints in space
measureList = []
for measureJnt in clusterJoints:
if measureJnt not in resultList:
jntPos2 = cmds.xform(measureJnt, q=True, ws=True, t=True)
#this just gets the length of the vector between the two joints
dist = math.sqrt(reduce(lambda x, y: x + y, [math.pow(jntPos2[i] - pos1[i], 2) for i in range(len(pos1))]))
measureList.append((measureJnt, dist))
# sort the list in ascending order so we get the closest joints first
measureList.sort(key=lambda dist: dist[1])
ascendingList = [entry[0] for entry in measureList[0:influences - subtract]]
return resultList + ascendingList
def dm2skin_getMaxInfluences(mesh):
"""Finds a skin cluster on the given mesh and returns
the number of influences it is set to have."""
skinClusterStr = 'findRelatedSkinCluster("' + mesh + '")'
skinCluster = mel.eval(skinClusterStr)
if not skinCluster:
return 0
allInfluences = cmds.skinCluster(skinCluster, q=True, inf=True)
return len(allInfluences)
def copySkinning(*args):
"""select the orig bound mesh, then the new unbound target mesh and run"""
sel = cmds.ls(sl=True)
orig = sel[0]
target = sel[1]
#get orig obj joints
try:
jnts = cmds.skinCluster(orig, q=True, influence = True)
except:
cmds.warning("couldn't get skin weights from {}".format(orig))
#bind the target with the jnts
try:
targetClus = cmds.skinCluster(jnts, target, bindMethod=0, skinMethod=0, normalizeWeights=1, maximumInfluences = 3, obeyMaxInfluences=False, tsb=True)[0]
print targetClus
except:
cmds.warning("couln't bind to {}".format(target))
#get skin clusters
origClus = mel.eval("findRelatedSkinCluster " + orig)
#copy skin weights from orig to target
try:
cmds.copySkinWeights(ss=origClus, ds=targetClus, noMirror=True, sa="closestPoint", ia="closestJoint")
except:
cmds.warning("couldn't copy skin weights from {0} to {1}".format(orig, target))
def new_joint_bind_at_center(tform, *args):
"""
create a new joint at the boudnign box center of pts and bind all pts to 1
:param tform - string - the geo to bind
:param args:
:return: string - skinCluster
"""
cmds.select(cl=True)
jnt = cmds.joint(name="{0}_base_JNT".format(tform))
center = bounding_box_center(tform)
cmds.xform(jnt, ws=True, t=center)
skinCl = cmds.skinCluster(jnt, tform, normalizeWeights=1)[0]
return(jnt, skinCl)
def main():
sel = mc.ls(sl=True)
for each in sel:
shapes = mc.listRelatives(each, shapes=True)
for shape in shapes:
#get skin cluster
history = mc.listHistory(shape, groupLevels=True, pruneDagObjects=True)
skins = mc.ls(history, type='skinCluster')
for skin in skins:
joints = mc.skinCluster(skin, query=True, influence=True)
mc.setAttr(skin+'.envelope', 0)
mc.skinCluster(skin, edit=True, unbindKeepHistory=True)
#delete bindPose
dagPose = mc.dagPose(each, query=True, bindPose=True)
if dagPose:
mc.delete(dagPose)
dagPose = mc.listConnections(skin+'.bindPose', d=False, type='dagPose')
if dagPose:
mc.delete(dagPose)
mc.skinCluster(joints, shape, toSelectedBones=True)
mc.setAttr(skin+'.envelope', 1)
if sel:
mc.select(sel)
def getSkinCluster(mesh):
shapes = mc.listRelatives(mesh, shapes=True, path=True)
for shape in shapes:
history = mc.listHistory(shape, groupLevels=True, pruneDagObjects=True)
if not history:
continue
skins = mc.ls(history, type='skinCluster')
if skins:
return skins[0]
return None
def softSelectionSkinWeights(*args):
model = mc.ls(sl=True, o=True)
joints = mc.ls(model, type='joint')
mc.select(joints, deselect=True)
weights = getSoftSelectionWeights()
if not model or not joints or not weights:
raise RuntimeError('Select vertices followed by a joint')
if len(joints) > 1:
raise RuntimeError('Only one joint can be selected at a time')
joint = joints[0]
skin = utl.getSkinCluster(model[0])
if not skin:
raise RuntimeError('Mesh must have an existing skinCluster')
influences = mc.skinCluster(skin, query=True, influence=True)
if joint not in influences:
mc.skinCluster(skin, edit=True, addInfluence=joint, lockWeights=False, weight=0)
for influence in influences:
mc.skinCluster(skin, edit=True, influence=influence, lockWeights=False)
for vertex, weight in weights.items():
mc.skinPercent(skin, vertex, transformValue=(joint, weight))
mc.select(joint)
# ______________________
# - -/__ Revision History __/- - - - - - - - - - - - - - - - - - - - - - - -
#
# Revision 1: 2016-12-31 : Initial publish
def create_spine(start_joint, end_joint, lower_control, upper_control, name='spine'):
spline_chain, original_chain = shortcuts.duplicate_chain(start_joint, end_joint, prefix='ikSpine_')
# Create the spline ik
ikh, effector, curve = cmds.ikHandle(
name='{0}_ikh'.format(name), solver='ikSplineSolver',
startJoint=spline_chain[0], endEffector=spline_chain[-1], parentCurve=False,
simplifyCurve=False)
effector = cmds.rename(effector, '{0}_eff'.format(name))
curve = cmds.rename(curve, '{0}_crv'.format(name))
# Create the joints to skin the curve
curve_start_joint = cmds.duplicate(start_joint, parentOnly=True, name='{0}CurveStart_jnt'.format(name))
cmds.parent(curve_start_joint, lower_control)
curve_end_joint = cmds.duplicate(end_joint, parentOnly=True, name='{0}CurveEnd_jnt'.format(name))
cmds.parent(curve_end_joint, upper_control)
# Skin curve
cmds.skinCluster(curve_start_joint, curve_end_joint, curve, name='{0}_scl'.format(name), tsb=True)
# Create stretch network
curve_info = cmds.arclen(curve, constructionHistory=True)
mdn = cmds.createNode('multiplyDivide', name='{0}Stretch_mdn'.format(name))
cmds.connectAttr('{0}.arcLength'.format(curve_info), '{0}.input1X'.format(mdn))
cmds.setAttr('{0}.input2X'.format(mdn), cmds.getAttr('{0}.arcLength'.format(curve_info)))
cmds.setAttr('{0}.operation'.format(mdn), 2) # Divide
# Connect to joints
for joint in spline_chain[1:]:
tx = cmds.getAttr('{0}.translateX'.format(joint))
mdl = cmds.createNode('multDoubleLinear', name='{0}Stretch_mdl'.format(joint))
cmds.setAttr('{0}.input1'.format(mdl), tx)
cmds.connectAttr('{0}.outputX'.format(mdn), '{0}.input2'.format(mdl))
cmds.connectAttr('{0}.output'.format(mdl), '{0}.translateX'.format(joint))
# Setup advanced twist
cmds.setAttr('{0}.dTwistControlEnable'.format(ikh), True)
cmds.setAttr('{0}.dWorldUpType'.format(ikh), 4) # Object up
cmds.setAttr('{0}.dWorldUpAxis'.format(ikh), 0) # Positive Y Up
cmds.setAttr('{0}.dWorldUpVectorX'.format(ikh), 0)
cmds.setAttr('{0}.dWorldUpVectorY'.format(ikh), 1)
cmds.setAttr('{0}.dWorldUpVectorZ'.format(ikh), 0)
cmds.setAttr('{0}.dWorldUpVectorEndX'.format(ikh), 0)
cmds.setAttr('{0}.dWorldUpVectorEndY'.format(ikh), 1)
cmds.setAttr('{0}.dWorldUpVectorEndZ'.format(ikh), 0)
cmds.connectAttr('{0}.worldMatrix[0]'.format(lower_control), '{0}.dWorldUpMatrix'.format(ikh))
cmds.connectAttr('{0}.worldMatrix[0]'.format(upper_control), '{0}.dWorldUpMatrixEnd'.format(ikh))
# Constrain original chain back to spline chain
for ik_joint, joint in zip(spline_chain, original_chain):
if joint == end_joint:
cmds.pointConstraint(ik_joint, joint, mo=True)
cmds.orientConstraint(upper_control, joint, mo=True)
else:
cmds.parentConstraint(ik_joint, joint)
def create_joint(cvs, wts):
tform = cvs[0].partition(".")[0]
curve = cmds.listRelatives(tform, f=True, s=True)[0]
ps = []
center = []
for cv in cvs:
ps.append(cmds.pointPosition(cv))
center = [sum(y)/len(y) for y in zip(*ps)]
#create joint at location
# ----------- should get closest point on surface
cmds.select(cl=True)
jnt = cmds.joint()
cmds.xform(jnt, ws=True, t=center)
#---------------- orient the joint along the curve?
#---------------- here create the ctrl set up for the joint
ctrl = rig.createControl(name="{0}Ctrl".format(jnt), type="sphere", color="red")
grp = cmds.group(name="{0}Grp".format(ctrl), em=True)
cmds.parent(ctrl, grp)
cmds.xform(grp, ws=True, t=center)
cmds.parent(jnt, ctrl)
# scale the control
comps = cmds.ls("{0}.cv[*]".format(ctrl))
cmds.select(comps, r=True)
cmds.scale(.2,.2,.2)
#add influence to skin Cluster
cmds.select(tform, r=True)
cmds.skinCluster(e=True, ai=jnt, wt=0)
cmds.setAttr("{0}.v".format(jnt), 0)
#apply weights to that joint
cls = mel.eval("findRelatedSkinCluster " + tform)
for v in range(len(cvs)):
cmds.skinPercent(cls, cvs[v], transformValue=[jnt, wts[v]])
return(jnt, ctrl, grp)
def soft_selection_to_joint(*args):
"""
takes a soft selection of verts and creates a joint to bind & wieght them in that proportion
:param args:
:return: string - name of the soft sel joint we've created
"""
# TODO - check for selection of verts!
selVtx = cmds.ls(sl=True, fl=True) # to get center for joint
vtxs, wts = rig.get_soft_selection() # to get weights for jnt
tform = vtxs[0].partition(".")[0]
mesh = cmds.listRelatives(tform, s=True)[0]
ptOnSurface = cmds.checkBoxGrp(widgets["jntCPOMCBG"], q=True, v1=True)
auto = cmds.checkBoxGrp(widgets["jntAutoCBG"], q=True, v1=True)
jntName = cmds.textFieldGrp(widgets["jntNameTFG"], q=True, tx=True)
rotOnSurface = cmds.checkBoxGrp(widgets["jntRotCBG"], q=True, v1=True)
cls = mel.eval("findRelatedSkinCluster " + tform)
if not cls:
if auto:
baseJnt, cls = rig.new_joint_bind_at_center(tform)
else:
cmds.warning("There isn't an initial bind on this geometry. Either create one or check 'auto'")
return()
center = rig.average_point_positions(selVtx)
rot = (0,0,0)
if ptOnSurface:
center = rig.closest_pt_on_mesh_position(center, mesh)
if rotOnSurface:
rot = rig.closest_pt_on_mesh_rotation(center, mesh)
cmds.select(cl=True)
jnt = cmds.joint(name = jntName)
cmds.xform(jnt, ws=True, t=center)
cmds.xform(jnt, ws=True, ro=rot)
# add influence to skin Cluster
cmds.select(tform, r=True)
cmds.skinCluster(e=True, ai=jnt, wt=0)
# apply weights to that joint
for v in range(len(vtxs)):
cmds.skinPercent(cls, vtxs[v], transformValue=[jnt, wts[v]])
newName = rig.increment_name(jntName)
cmds.textFieldGrp(widgets["jntNameTFG"], tx=newName, e=True)
return(jnt)
def createCenterOfMass(*args):
'''
Create a center of mass node, and constrain it to the
character based on the selected root node.
'''
sel = mc.ls(sl=True)
if not len(sel) == 1:
raise RuntimeError('Please select the root control of your puppet.')
print 'Create Center Of Mass Node'
print '--------------------------'
meshes = meshesFromReference(sel[0]) or meshesFromHistory(sel[0])
if not meshes:
raise RuntimeError('Could not determine geomerty from selected control. Make sure geo is visible.')
mc.select(meshes)
mc.refresh()
print 'Discovered Meshes:'
for mesh in meshes:
print '\t',mesh
skinnedMeshes = []
for mesh in meshes:
if utl.getSkinCluster(mesh):
skinnedMeshes.append(mesh)
continue
hist = mc.listHistory(mesh, breadthFirst=True)
skins = mc.ls(hist, type='skinCluster')
if not skins:
warnings.warn('Could not find a skinned mesh affecting {}'.format(mesh))
continue
outGeo = mc.listConnections(skins[0]+'.outputGeometry[0]', source=False)
outGeo = mc.ls(outGeo, type=['mesh','transform'])
if not outGeo:
warnings.warn('Could not find a skinned mesh affecting {}'.format(mesh))
continue
skinnedMeshes.append(outGeo[0])
if not skinnedMeshes:
raise RuntimeError('Could not determine skinned geometry from selected control. This tool will only work if geo is skinned.')
locator = centerOfMassLocator(skinnedMeshes)
mc.addAttr(locator, longName=COM_ATTR, attributeType='message')
mc.connectAttr('.'.join((sel[0],'message')), '.'.join((locator,COM_ATTR)))
mc.select(sel)
return locator